Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next v7 1/2] dt-bindings: net: add DT bindings for Socionext UniPhier AVE
From: Kunihiko Hayashi @ 2017-12-21 12:45 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171221113254.GB9930@lunn.ch>

Hello Andrew,

On Thu, 21 Dec 2017 12:32:54 +0100 Andrew Lunn <andrew@lunn.ch> wrote:

> > +Optional properties:
> > + - resets: A phandle to the reset control for the MAC
> > + - local-mac-address: See ethernet.txt in the same directory.
> > +
> > +Required subnode:
> > + - mdio: Device tree subnode with the following required properties:
> > +
> > +Example:
> 
> It sounds like there should be some properties before the Example.

Indeed, this is my carelessness.

> 
>    Andrew
> 
> > +
> > +	ether: ethernet at 65000000 {
> > +		compatible = "socionext,uniphier-ld20-ave4";
> > +		reg = <0x65000000 0x8500>;
> > +		interrupts = <0 66 4>;
> > +		phy-mode = "rgmii";
> > +		phy-handle = <&ethphy>;
> > +		clocks = <&sys_clk 6>;
> > +		resets = <&sys_rst 6>;
> > +		local-mac-address = [00 00 00 00 00 00];
> 
> Typically you would put a blank line here, before the mdio node.

Okay, I'll put it.

> > +		mdio {
> > +			#address-cells = <1>;
> > +			#size-cells = <0>;
> > +			ethphy: ethphy at 1 {
> > +				reg = <1>;
> > +			};
> > +		};
> > +	};
> 
>   Andrew

Thank you,

---
Best Regards,
Kunihiko Hayashi

^ permalink raw reply

* [PATCH v3 4/5] ARM: davinci: convert to common clock framework
From: Sekhar Nori @ 2017-12-21 12:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4d7e4751-490b-5dfc-ef75-195fe41719b7@lechnology.com>

On Wednesday 20 December 2017 04:42 AM, David Lechner wrote:
> On 12/19/2017 07:47 AM, Sekhar Nori wrote:
>> Hi David,
>>
>> On Saturday 09 December 2017 07:45 AM, David Lechner wrote:
>>> This converts the clocks in mach-davinci to the common clock framework.
>>>
>>> Most of the patch just involves renaming struct clk to struct
>>> davinci_clk.
>>> There is also a struct clk_hw added to provide the bridge between the
>>> existing clock implementation and the common clock framework.
>>>
>>> The clk_get_parent and clk_set_parent callbacks are dropped because all
>>> clocks currently (effectivly) have a single parent, in which case the
>>> common clock framework does not want you to implement these functions
>>> yourself.
>>>
>>> clk_unregister() is dropped because it is not used anywhere in
>>> mach-davinci.
>>>
>>> EXPORT_SYMBOL() is removed from functions not used outside of
>>> mach-davinci.
>>>
>>> Fixed checkpatch.pl warning about bare use of unsigned in dump_clock().
>>>
>>> Signed-off-by: David Lechner <david@lechnology.com>
>>
>> The cleanups leading upto this patch look fine, but I am not sure about
>> this patch itself. Ideally, we should have moved to drivers/clk also.
>> And shared code with keystone too since the PSC and PLL implementations
>> of the two architectures are quite similar.
>>
>> I could think of this as an intermediate step while we get there, but I
>> am afraid of the churn that would cause. For example, if we reuse
>> keystone driver, we will be using clk_psc and we can get rid of the
>> davinci_clk that this patch introduces.
>>
>> So unless there is big roadblock to moving to drivers/clk, we should
>> probably do that in one shot.
>>
> 
> Yes, this is intended as an intermediate step. My thinking was that it
> would be better to do one thing at a time, so if something breaks, it
> will be easier to find the problem than if we try to do everything at
> once. But, you are right, there will be quite a few extra intermediate
> steps required, for example, I have realized that in order to use the
> davinci_clk concurrently with other clocks in the common clock
> framework, at least the parent pointer and children linked list will
> have to be removed, introducing another intermediate step.

Thats right, we have two clock lists now, one within mach-davinci and
another in drivers/clk.

> While avoiding the davinci_clk step would be ideal, there is quite a bit
> that needs to be done first before this will be possible. And the
> downside I see to doing it this way is that no one will be able to test
> any of the preparatory patches because they depend on the common clock
> framework. We won't even be able to compile them until all the pieces
> are in place and we can enable the common clock kernel configuration
> option.

Thats right, the whole conversion needs to be one big series. I dont
think we can queue just the preparatory patches without the actual
conversion as the preparation might change with the conversion.

Its a big change no doubt, we have to time it right and make sure it
gets tested in linux-next for quite a while before getting merged.

> Here is a list of some of the issues I know about so far preventing me
> from doing everything at once at this point in time:
> 
> * Reentrancy of clk_enable() on non-SMP systems is broken in the common
> clock framework [1][2], but this reentrancy is needed for the DA8xx USB
> PHY PLL clock [3].

I see. I will look at those threads in detail, but looks like you have
the attention of clock framework maintainers so hopefully that will be
solved soon.

> * drivers/remoteproc/da8xx_remoteproc.c calls
> davinci_clk_reset_{assert,deassert}() in arch/arm/mach-davinci/clock.c.
> This needs to be moved to the PSC driver in drivers/clk. The reset
> framework in drivers/reset looks ideal for handling this, but it is
> currently device tree only and it looks like we need it to work on
> non-device tree boards. So, our options are: to move this hack to the
> PSC driver in drivers/clk/ *or* to update the reset framework to work
> with non-device tree boards *or* convert all of the mach-davinci boards
> to device tree only.

The second option "update the reset framework to work with non-device
tree boards" is what we should aim for. The last option is not happening
anytime soon ;)

> 
> * The device tree bindings for "ti,keystone,psc-clock" are not the best
> and I am really not keen on reusing them. What makes sense to me would
> be to define a device tree node that represents the entire PSC IP block
> with child nodes for each clock output. Something like this:
> 
> power-controller at 2350000 {
> ????compatible = "ti,davinci-psc";
> ????reg = <0x2350000 0xb00>;
> 
> ????...
> 
> ????clkhyperlink0: clkhyperlink0 {
> ??????? #clock-cells = <0>;
> ??????? clock-output-names = "hyperlink-0";
> ??????? clocks = <&chipclk12>;
> ??????? power-domain = <5>;
> ??????? module-domain = <12>;
> ????};
> 
> ????...
> };
> 
> But the keystone bindings assign the entire PSC register space to each
> clock individually (twice). And the really ugly part is that the module
> domain and the power domain numbers of each PSC "clock" is embedded in
> the register address. So, if you have reg = <0x02350030 0xb00>,
> <0x02350014 0x400>; what this really means is that the clock driver will
> iomap 0x02350000 (notice the 00 instead of the 30 and 14 at the end of
> each register and that the base 0x02350000 is essentially listed twice
> with two different sizes) and it means that this particular clock has a

Okay, I do see the unnecessary ioremap going on and the wasted virtual
memory space. But I dont quite see that the same base (0x02350000) is
being remapped again and again. From whatever I can see in
of_psc_clk_init(), it just maps the addresses for "control" and "domain"
provided in device tree (keystone-clocks.dtsi)

> module domain of 12 (0x30 / 4 = 12, 4 comes from the fact these are
> 32-bit registers) and a power domain of 5 (0x14 / 4 = 5). You can find
> the numbers 12 and 5 easily in the SRM, but the numbers used in the
> device tree bindings are pretty meaningless. Furthermore, the driver
> itself stores the base address in a global variable which won't work for
> DA8XX since it has two PSCs instead of one. So, for this case, I'm

Yeah, I see this issue.

> really tempted to just write a new driver with better device tree
> bindings rather than trying to fix up the keystone driver to make it
> compatible with davinci and better device tree bindings while still
> maintaining backwards compatibility with wacky device tree bindings.

I think thats fine to do as well. We can also look at reusing some code
(for example functions like psc_config() while using different
bindings). I dont see any backward compatibility need here since DA850
is a new device altogether. We cannot change existing keystone bindings,
but can definitely reuse existing keystone code where it suits while
creating new bindings.

We may decide that keeping davinci PSC code handling separate is easier
overall (considering we have to support non-device-tree-mode too, which
keystone does not). This is something that will have to be looked into
along with CCF maintainers.

> * The keystone PLL driver and device tree bindings are in better shape,
> but they cannot be used as-is with davinci family processors since the
> registers are laid out differently. I have a work-in-progress patch for
> this that I started quite a while back [4][5].

Okay, I hadnt done a detailed comparison, but from the patches you have,
it does look bad. I guess we are looking at drivers/clock/davinci here.
And we can use the analysis you have to justify it.

> * All of the keystone clock drivers are currently device tree only, so
> they will need to be modified *or* as mentioned above all mach-davinci
> boards could be converted to device tree only.

The later is not going to happen soon, so it will have to be the former.

> 
> [1]: https://patchwork.kernel.org/patch/10108437/
> [2]: https://patchwork.kernel.org/patch/10115483/
> [3]: https://patchwork.kernel.org/patch/9449741/
> [4]:
> https://github.com/dlech/ev3dev-kernel/commit/5e7bf2070aa03283b605d7b86864758d24f83aa8
> 
> [5]:
> https://github.com/dlech/ev3dev-kernel/commit/1e34686ff57a673c8655eaedacec4d65d48f93b3
> 
> 
> 
> ---
> 
> Also, a bit more background on my motivation here. Really, all I want is
> to be able to use the "pwm-clock" device tree bindings to supply a clock
> to a Bluetooth chip. But the driver for it requires the common clock
> framework.

Okay.

> 
> This is just a spare time project for me, which is why I have just done
> the bare minimum to get CONFIG_COMMON_CLK enabled rather than trying to
> fix all of the hard problems to do thing the "right way". As you have
> seen above, there is still quite a bit of work remaining to be done,
> especially when doing this just for fun. It is probably just wishful
> thinking, but I kind of hoped that if I was able to get things this far,
> maybe some other interested parties might be able to help with one of
> the various pieces.

All I can say is that issue has been noted within TI and I believe
(hope) that help is on the way!

If we do go the completely separate clock driver route, I think even
migrating outside of mach-davinci should not be that tough.

> 
> One way or another, I suppose I will get my Bluetooth clock eventually. :-)

Sure! Thanks for all your work on this platform so far!

Regards,
Sekhar

^ permalink raw reply

* [PATCH 4/4] ARM: dts: vf610-zii-dev-rev-b: add interrupts for 88e1545 PHY
From: Linus Walleij @ 2017-12-21 12:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <E1eRnWr-0006VS-1m@rmk-PC.armlinux.org.uk>

On Thu, Dec 21, 2017 at 12:12 AM, Russell King
<rmk+kernel@armlinux.org.uk> wrote:

> The 88e1545 PHY has its interrupts wired to the VF610, so we might as
> well use them.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---
> This is certainly not correct, as all PHYs on this device share the
> same interrupt line, but we can't specify the pinmux settings
> individually on each PHY.  How should this be handled?

I do not know the details of the Marvell switch.

Sorry for any possible misunderstandings below.

What I did with the Realtek switch I was playing around
with was to create a separate irqchip, also in the device tree,
embedded inside the DSA switch, then referenced the
IRQs from that chip as 0, 1 .. n.

The patches are here:
DTS:
https://marc.info/?l=linux-netdev&m=150992420713391&w=2
Driver:
https://marc.info/?l=linux-netdev&m=150992421113393&w=2

Note that this RFC is wrong: it assigns the IRQs to ports
instead of PHYs, but the idea with an IRQchip inside the
DSA is pretty solid IMO. (I will rewrite it using your method
of a separate mdio bus node and phy-handle references.)

Anyway I was inspired to this model from certain PCI bridges that
contain an IRQ demuxer and thus instantiate an irqchip for
this, that is then part of the bridge itself.

Then for the pin control, I guess the irqchip inside the bridge
should be the entity taking the IRQ from the GPIO-backed
irq controller and also the pin control handle.  As pin control
handles are tied to Linux devices, that requires it
to be a device proper though. I don't know if it's possible
to properly spawn a device for this irqchip from the switch,
but I guess it is what I would try.

I hope this helps.

Yours,
Linus Walleij

^ permalink raw reply

* Applied "spi: pxa2xx: avoid redundant gpio_to_desc(desc_to_gpio()) round-trip" to the spi tree
From: Mark Brown @ 2017-12-21 12:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171221003731.21633-1-linux@rasmusvillemoes.dk>

The patch

   spi: pxa2xx: avoid redundant gpio_to_desc(desc_to_gpio()) round-trip

has been applied to the spi tree at

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 221886646f75964ca31cf60f1811b2c9c4e965a5 Mon Sep 17 00:00:00 2001
From: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Date: Thu, 21 Dec 2017 01:37:31 +0100
Subject: [PATCH] spi: pxa2xx: avoid redundant gpio_to_desc(desc_to_gpio())
 round-trip

gpio_free(gpio) simply does gpiod_free(gpio_to_desc(gpio)), so it's
simpler and cleaner to use gpiod_free directly.

Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Signed-off-by: Mark Brown <broonie@kernel.org>
---
 drivers/spi/spi-pxa2xx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index 4cb515a3104c..c209dc1047b5 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1237,7 +1237,7 @@ static int setup_cs(struct spi_device *spi, struct chip_data *chip,
 	 * different chip_info, release previously requested GPIO
 	 */
 	if (chip->gpiod_cs) {
-		gpio_free(desc_to_gpio(chip->gpiod_cs));
+		gpiod_free(chip->gpiod_cs);
 		chip->gpiod_cs = NULL;
 	}
 
@@ -1417,7 +1417,7 @@ static void cleanup(struct spi_device *spi)
 
 	if (drv_data->ssp_type != CE4100_SSP && !drv_data->cs_gpiods &&
 	    chip->gpiod_cs)
-		gpio_free(desc_to_gpio(chip->gpiod_cs));
+		gpiod_free(chip->gpiod_cs);
 
 	kfree(chip);
 }
-- 
2.15.0

^ permalink raw reply related

* [PATCH 2/2] mmc: sunxi: Add runtime_pm support
From: Ulf Hansson @ 2017-12-21 12:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <412579f183c42f6fe2ea04f294dfb788be5e4875.1513766964.git-series.maxime.ripard@free-electrons.com>

On 20 December 2017 at 11:50, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> So far, even if our card was not in use, we didn't shut down our main
> clock, which meant that it was still output on the MMC bus.
>
> While this obviously means that we could save some power there, it also
> created issues when it comes with EMC control since we'll have a perfect
> peak at the card clock rate.
>
> Let's implement runtime_pm with autosuspend so that we will shut down the
> clock when it's not been in use for quite some time.
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  drivers/mmc/host/sunxi-mmc.c | 90 ++++++++++++++++++++++++-------------
>  1 file changed, 60 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
> index 3ce46ebd3488..c6a0bd0e0476 100644
> --- a/drivers/mmc/host/sunxi-mmc.c
> +++ b/drivers/mmc/host/sunxi-mmc.c
> @@ -35,6 +35,7 @@
>  #include <linux/of_gpio.h>
>  #include <linux/of_platform.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/reset.h>
>  #include <linux/scatterlist.h>
> @@ -1217,29 +1218,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
>                 return ret;
>         }
>
> -       ret = clk_prepare_enable(host->clk_mmc);
> -       if (ret) {
> -               dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret);
> -               goto error_disable_clk_ahb;
> -       }
> -
> -       ret = clk_prepare_enable(host->clk_output);
> -       if (ret) {
> -               dev_err(&pdev->dev, "Enable output clk err %d\n", ret);
> -               goto error_disable_clk_mmc;
> -       }
> -
> -       ret = clk_prepare_enable(host->clk_sample);
> -       if (ret) {
> -               dev_err(&pdev->dev, "Enable sample clk err %d\n", ret);
> -               goto error_disable_clk_output;
> -       }
> -

Actually, I think you should keep all the above. Perhaps you may want
to move it to a separate helper function though, which the
->runtime_resume() callbacks can invoke as well.

More reasons to why, see the comment in the ->probe() function.

>         if (!IS_ERR(host->reset)) {
>                 ret = reset_control_reset(host->reset);
>                 if (ret) {
>                         dev_err(&pdev->dev, "reset err %d\n", ret);
> -                       goto error_disable_clk_sample;
> +                       goto error_disable_clk_ahb;
>                 }
>         }
>
> @@ -1258,12 +1241,6 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
>  error_assert_reset:
>         if (!IS_ERR(host->reset))
>                 reset_control_assert(host->reset);
> -error_disable_clk_sample:
> -       clk_disable_unprepare(host->clk_sample);
> -error_disable_clk_output:
> -       clk_disable_unprepare(host->clk_output);
> -error_disable_clk_mmc:
> -       clk_disable_unprepare(host->clk_mmc);
>  error_disable_clk_ahb:
>         clk_disable_unprepare(host->clk_ahb);
>         return ret;
> @@ -1280,6 +1257,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
>                 dev_err(&pdev->dev, "mmc alloc host failed\n");
>                 return -ENOMEM;
>         }
> +       platform_set_drvdata(pdev, mmc);
>
>         host = mmc_priv(mmc);
>         host->mmc = mmc;
> @@ -1340,12 +1318,16 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
>         if (ret)
>                 goto error_free_dma;
>
> +       pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
> +       pm_runtime_use_autosuspend(&pdev->dev);
> +       pm_runtime_enable(&pdev->dev);
> +

This means in case you don't have CONFIG_PM set when compiling this
driver, the clocks will never be enabled using runtime PM.

I think it's good practice to deal with this, therefore I think you
should enable the clocks as before, and instead indicate that the
device is already runtime resumed.

In other words, before you call pm_runtime_enable(), invoke
pm_runtime_set_active().

>         ret = mmc_add_host(mmc);
>         if (ret)
>                 goto error_free_dma;
>
>         dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
> -       platform_set_drvdata(pdev, mmc);
> +
>         return 0;
>
>  error_free_dma:
> @@ -1361,27 +1343,75 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
>         struct sunxi_mmc_host *host = mmc_priv(mmc);
>
>         mmc_remove_host(mmc);
> +       pm_runtime_force_suspend(&pdev->dev);

Do you need the clocks to be enabled, while calling disable_irq() and
sunxi_mmc_reset_host()?

In such case you need to call pm_runtime_get_sync() here. Then move
pm_runtime_force_suspend() a few lines below, and later call
pm_runtime_put_noidle().

>         disable_irq(host->irq);
>         sunxi_mmc_reset_host(host);
>
>         if (!IS_ERR(host->reset))
>                 reset_control_assert(host->reset);
>
> -       clk_disable_unprepare(host->clk_sample);
> +       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> +       mmc_free_host(mmc);
> +
> +       return 0;
> +}
> +
> +static int sunxi_mmc_runtime_resume(struct device *dev)
> +{
> +       struct mmc_host *mmc = dev_get_drvdata(dev);
> +       struct sunxi_mmc_host *host = mmc_priv(mmc);
> +       int ret;
> +
> +       ret = clk_prepare_enable(host->clk_mmc);
> +       if (ret) {
> +               dev_err(dev, "Enable mmc clk err %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = clk_prepare_enable(host->clk_output);
> +       if (ret) {
> +               dev_err(dev, "Enable output clk err %d\n", ret);
> +               goto error_disable_clk_mmc;
> +       }
> +
> +       ret = clk_prepare_enable(host->clk_sample);
> +       if (ret) {
> +               dev_err(dev, "Enable sample clk err %d\n", ret);
> +               goto error_disable_clk_output;
> +       }
> +
> +       return 0;
> +
> +error_disable_clk_output:
>         clk_disable_unprepare(host->clk_output);
> +error_disable_clk_mmc:
>         clk_disable_unprepare(host->clk_mmc);
> -       clk_disable_unprepare(host->clk_ahb);
> +       return ret;
> +}
>
> -       dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
> -       mmc_free_host(mmc);
> +static int sunxi_mmc_runtime_suspend(struct device *dev)
> +{
> +       struct mmc_host *mmc = dev_get_drvdata(dev);
> +       struct sunxi_mmc_host *host = mmc_priv(mmc);
> +
> +       clk_disable_unprepare(host->clk_sample);
> +       clk_disable_unprepare(host->clk_output);
> +       clk_disable_unprepare(host->clk_mmc);
>
>         return 0;
>  }
>
> +static const struct dev_pm_ops sunxi_mmc_pm_ops = {
> +       SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend,
> +                          sunxi_mmc_runtime_resume,
> +                          NULL)
> +};
> +
>  static struct platform_driver sunxi_mmc_driver = {
>         .driver = {
>                 .name   = "sunxi-mmc",
>                 .of_match_table = of_match_ptr(sunxi_mmc_of_match),
> +               .pm = &sunxi_mmc_pm_ops,
>         },
>         .probe          = sunxi_mmc_probe,
>         .remove         = sunxi_mmc_remove,
> --
> git-series 0.9.1

Otherwise this looks good to me!

BTW, isn't system wide suspend/resume() also important to save power
for? That's rather simple to implement, after you have enabled runtime
PM support.

Kind regards
Uffe

^ permalink raw reply

* [PATCH 3/4] ARM: dts: vf610-zii-dev-rev-b: add PHYs for switch2
From: Linus Walleij @ 2017-12-21 12:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <E1eRnWl-0006VL-TL@rmk-PC.armlinux.org.uk>

On Thu, Dec 21, 2017 at 12:11 AM, Russell King
<rmk+kernel@armlinux.org.uk> wrote:

> Switch 2 has an 88e1545 PHY behind it, which is a quad PHY.  Only the
> first three PHYs are used, the remaining PHY is unused.  When we wire
> up the SFF sockets in a later commit, the omission of this causes the
> fourth PHY to be used for port 3.  Specifying the PHYs in DT avoids
> the auto-probing of the bus, and discovery of this PHY.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Ah this is elegant and what Andrew requested me to do for
another switch as well. Makes perfect sense. FWIW:

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH 0/3] [v11] pinctrl: qcom: add support for sparse GPIOs
From: Linus Walleij @ 2017-12-21 12:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513797033-9494-1-git-send-email-timur@codeaurora.org>

Hi Timur,

thank you for your perseverance. I am sorry that I am sometimes not
fast to respond :(

On Wed, Dec 20, 2017 at 8:10 PM, Timur Tabi <timur@codeaurora.org> wrote:

> Patch 1 reverts an old patch that triggers a get_direction of every
> pin upon init, without attempting to request the pins first.  The
> direction is already being queried when the pin is requested.
>
> Patch 2 adds support to pinctrl-msm for "unavailable" GPIOs.

I have applied both of these to the pinctrl "devel" branch so we
can see if all is fine.

They have Stephen's ACK so I am happy with them, I am just
still slightly worried about possible regressions because of
patch 1.

> Patch 3 extends that support to pinctrl-qdf2xxx.  A recent ACPI change
> on QDF2400 platforms blocks access to most pins, so the driver can only
> register a subset.

I see this one is still under discussion.

If nothing drastic happens with patch 1/2 in linux-next
it should be fine if you just resend this single patch in subsequent
submissions.

I think it may be worthwhile to keep Andrew Cooks in the loop for
future submissions as he's trying to solve similar problems for
AMD.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH] of: usb: Fix definition of of_usb_get_dr_mode_by_phy
From: Suzuki K Poulose @ 2017-12-21 12:06 UTC (permalink / raw)
  To: linux-arm-kernel

Declaration of of_usb_get_dr_mode_by_phy only depends on CONFIG_OF
and not on CONFIG_USB_SUPPORT, which actually defines it. This can
break the build like below, if USB_SUPPORT is not selected :

 drivers/phy/renesas/phy-rcar-gen3-usb2.o: In function `rcar_gen3_phy_usb2_probe':
 drivers/phy/renesas/phy-rcar-gen3-usb2.c:444: undefined reference to `of_usb_get_dr_mode_by_phy'
 drivers/phy/renesas/phy-rcar-gen3-usb2.c:444:(.text+0x2d4): relocation
  truncated to fit: R_AARCH64_CALL26 against undefined symbol `of_usb_get_dr_mode_by_phy'
 drivers/phy/renesas/phy-rcar-gen3-usb2.c:444: undefined reference to `of_usb_get_dr_mode_by_phy'
 drivers/phy/renesas/phy-rcar-gen3-usb2.c:444:(.text+0x3bc): relocation truncated to
  fit: R_AARCH64_CALL26 against undefined symbol `of_usb_get_dr_mode_by_phy'
 make: *** [Makefile:993: vmlinux] Error 1

Fixes: commit 98bfb39466954c69d2 ("usb: of: add an api to get dr_mode by the phy node")
Cc: linux-renesas-soc at vger.kernel.org
Cc: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb at vger.kernel.org
Cc: Bin Liu <b-liu@ti.com>
Cc: Felipe Balbi <balbi@ti.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
 include/linux/usb/of.h | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h
index 6cbe7a5c2b57..181245e5fe36 100644
--- a/include/linux/usb/of.h
+++ b/include/linux/usb/of.h
@@ -13,7 +13,6 @@
 #include <linux/usb/phy.h>
 
 #if IS_ENABLED(CONFIG_OF)
-enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0);
 bool of_usb_host_tpl_support(struct device_node *np);
 int of_usb_update_otg_caps(struct device_node *np,
 			struct usb_otg_caps *otg_caps);
@@ -21,11 +20,6 @@ struct device_node *usb_of_get_child_node(struct device_node *parent,
 			int portnum);
 struct device *usb_of_get_companion_dev(struct device *dev);
 #else
-static inline enum usb_dr_mode
-of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
-{
-	return USB_DR_MODE_UNKNOWN;
-}
 static inline bool of_usb_host_tpl_support(struct device_node *np)
 {
 	return false;
@@ -48,7 +42,13 @@ static inline struct device *usb_of_get_companion_dev(struct device *dev)
 
 #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_USB_SUPPORT)
 enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np);
+enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0);
 #else
+static inline enum usb_dr_mode
+of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
+{
+	return USB_DR_MODE_UNKNOWN;
+}
 static inline enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
 {
 	return USBPHY_INTERFACE_MODE_UNKNOWN;
-- 
2.13.6

^ permalink raw reply related

* arm64 crashkernel fails to boot on acpi-only machines due to ACPI regions being no longer mapped as NOMAP
From: Bhupesh Sharma @ 2017-12-21 12:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171221103440.GJ28046@linaro.org>

Hello Akashi,

On Thu, Dec 21, 2017 at 4:04 PM, AKASHI Takahiro
<takahiro.akashi@linaro.org> wrote:
> Bhupesh,
>
> Can you test the patch attached below, please?
>
> It is intended to retain already-reserved regions (ACPI reclaim memory
> in this case) in system ram (i.e. memblock.memory) without explicitly
> exporting them via usable-memory-range.
> (I still have to figure out what the side-effect of this patch is.)
>
> Thanks,
> -Takahiro AKASHI
>
> On Thu, Dec 21, 2017 at 01:30:43AM +0530, Bhupesh Sharma wrote:
>> On Tue, Dec 19, 2017 at 6:39 PM, Ard Biesheuvel
>> <ard.biesheuvel@linaro.org> wrote:
>> > On 19 December 2017 at 07:09, AKASHI Takahiro
>> > <takahiro.akashi@linaro.org> wrote:
>> >> On Mon, Dec 18, 2017 at 01:40:09PM +0800, Dave Young wrote:
>> >>> On 12/15/17 at 05:59pm, AKASHI Takahiro wrote:
>> >>> > On Wed, Dec 13, 2017 at 12:17:22PM +0000, Ard Biesheuvel wrote:
>> >>> > > On 13 December 2017 at 12:16, AKASHI Takahiro
>> >>> > > <takahiro.akashi@linaro.org> wrote:
>> >>> > > > On Wed, Dec 13, 2017 at 10:49:27AM +0000, Ard Biesheuvel wrote:
>> >>> > > >> On 13 December 2017 at 10:26, AKASHI Takahiro
>> >>> > > >> <takahiro.akashi@linaro.org> wrote:
>> >>> > > >> > Bhupesh, Ard,
>> >>> > > >> >
>> >>> > > >> > On Wed, Dec 13, 2017 at 03:21:59AM +0530, Bhupesh Sharma wrote:
>> >>> > > >> >> Hi Ard, Akashi
>> >>> > > >> >>
>> >>> > > >> > (snip)
>> >>> > > >> >
>> >>> > > >> >> Looking deeper into the issue, since the arm64 kexec-tools uses the
>> >>> > > >> >> 'linux,usable-memory-range' dt property to allow crash dump kernel to
>> >>> > > >> >> identify its own usable memory and exclude, at its boot time, any
>> >>> > > >> >> other memory areas that are part of the panicked kernel's memory.
>> >>> > > >> >> (see https://www.kernel.org/doc/Documentation/devicetree/bindings/chosen.txt
>> >>> > > >> >> , for details)
>> >>> > > >> >
>> >>> > > >> > Right.
>> >>> > > >> >
>> >>> > > >> >> 1). Now when 'kexec -p' is executed, this node is patched up only
>> >>> > > >> >> with the crashkernel memory range:
>> >>> > > >> >>
>> >>> > > >> >>                 /* add linux,usable-memory-range */
>> >>> > > >> >>                 nodeoffset = fdt_path_offset(new_buf, "/chosen");
>> >>> > > >> >>                 result = fdt_setprop_range(new_buf, nodeoffset,
>> >>> > > >> >>                                 PROP_USABLE_MEM_RANGE, &crash_reserved_mem,
>> >>> > > >> >>                                 address_cells, size_cells);
>> >>> > > >> >>
>> >>> > > >> >> (see https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/tree/kexec/arch/arm64/kexec-arm64.c#n465
>> >>> > > >> >> , for details)
>> >>> > > >> >>
>> >>> > > >> >> 2). This excludes the ACPI reclaim regions irrespective of whether
>> >>> > > >> >> they are marked as System RAM or as RESERVED. As,
>> >>> > > >> >> 'linux,usable-memory-range' dt node is patched up only with
>> >>> > > >> >> 'crash_reserved_mem' and not 'system_memory_ranges'
>> >>> > > >> >>
>> >>> > > >> >> 3). As a result when the crashkernel boots up it doesn't find this
>> >>> > > >> >> ACPI memory and crashes while trying to access the same:
>> >>> > > >> >>
>> >>> > > >> >> # kexec -p /boot/vmlinuz-`uname -r` --initrd=/boot/initramfs-`uname
>> >>> > > >> >> -r`.img --reuse-cmdline -d
>> >>> > > >> >>
>> >>> > > >> >> [snip..]
>> >>> > > >> >>
>> >>> > > >> >> Reserved memory range
>> >>> > > >> >> 000000000e800000-000000002e7fffff (0)
>> >>> > > >> >>
>> >>> > > >> >> Coredump memory ranges
>> >>> > > >> >> 0000000000000000-000000000e7fffff (0)
>> >>> > > >> >> 000000002e800000-000000003961ffff (0)
>> >>> > > >> >> 0000000039d40000-000000003ed2ffff (0)
>> >>> > > >> >> 000000003ed60000-000000003fbfffff (0)
>> >>> > > >> >> 0000001040000000-0000001ffbffffff (0)
>> >>> > > >> >> 0000002000000000-0000002ffbffffff (0)
>> >>> > > >> >> 0000009000000000-0000009ffbffffff (0)
>> >>> > > >> >> 000000a000000000-000000affbffffff (0)
>> >>> > > >> >>
>> >>> > > >> >> 4). So if we revert Ard's patch or just comment the fixing up of the
>> >>> > > >> >> memory cap'ing passed to the crash kernel inside
>> >>> > > >> >> 'arch/arm64/mm/init.c' (see below):
>> >>> > > >> >>
>> >>> > > >> >> static void __init fdt_enforce_memory_region(void)
>> >>> > > >> >> {
>> >>> > > >> >>         struct memblock_region reg = {
>> >>> > > >> >>                 .size = 0,
>> >>> > > >> >>         };
>> >>> > > >> >>
>> >>> > > >> >>         of_scan_flat_dt(early_init_dt_scan_usablemem, &reg);
>> >>> > > >> >>
>> >>> > > >> >>         if (reg.size)
>> >>> > > >> >>                 //memblock_cap_memory_range(reg.base, reg.size); /*
>> >>> > > >> >> comment this out */
>> >>> > > >> >> }
>> >>> > > >> >
>> >>> > > >> > Please just don't do that. It can cause a fatal damage on
>> >>> > > >> > memory contents of the *crashed* kernel.
>> >>> > > >> >
>> >>> > > >> >> 5). Both the above temporary solutions fix the problem.
>> >>> > > >> >>
>> >>> > > >> >> 6). However exposing all System RAM regions to the crashkernel is not
>> >>> > > >> >> advisable and may cause the crashkernel or some crashkernel drivers to
>> >>> > > >> >> fail.
>> >>> > > >> >>
>> >>> > > >> >> 6a). I am trying an approach now, where the ACPI reclaim regions are
>> >>> > > >> >> added to '/proc/iomem' separately as ACPI reclaim regions by the
>> >>> > > >> >> kernel code and on the other hand the user-space 'kexec-tools' will
>> >>> > > >> >> pick up the ACPI reclaim regions from '/proc/iomem' and add it to the
>> >>> > > >> >> dt node 'linux,usable-memory-range'
>> >>> > > >> >
>> >>> > > >> > I still don't understand why we need to carry over the information
>> >>> > > >> > about "ACPI Reclaim memory" to crash dump kernel. In my understandings,
>> >>> > > >> > such regions are free to be reused by the kernel after some point of
>> >>> > > >> > initialization. Why does crash dump kernel need to know about them?
>> >>> > > >> >
>> >>> > > >>
>> >>> > > >> Not really. According to the UEFI spec, they can be reclaimed after
>> >>> > > >> the OS has initialized, i.e., when it has consumed the ACPI tables and
>> >>> > > >> no longer needs them. Of course, in order to be able to boot a kexec
>> >>> > > >> kernel, those regions needs to be preserved, which is why they are
>> >>> > > >> memblock_reserve()'d now.
>> >>> > > >
>> >>> > > > For my better understandings, who is actually accessing such regions
>> >>> > > > during boot time, uefi itself or efistub?
>> >>> > > >
>> >>> > >
>> >>> > > No, only the kernel. This is where the ACPI tables are stored. For
>> >>> > > instance, on QEMU we have
>> >>> > >
>> >>> > >  ACPI: RSDP 0x0000000078980000 000024 (v02 BOCHS )
>> >>> > >  ACPI: XSDT 0x0000000078970000 000054 (v01 BOCHS  BXPCFACP 00000001
>> >>> > >   01000013)
>> >>> > >  ACPI: FACP 0x0000000078930000 00010C (v05 BOCHS  BXPCFACP 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: DSDT 0x0000000078940000 0011DA (v02 BOCHS  BXPCDSDT 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: APIC 0x0000000078920000 000140 (v03 BOCHS  BXPCAPIC 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: GTDT 0x0000000078910000 000060 (v02 BOCHS  BXPCGTDT 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: MCFG 0x0000000078900000 00003C (v01 BOCHS  BXPCMCFG 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: SPCR 0x00000000788F0000 000050 (v02 BOCHS  BXPCSPCR 00000001
>> >>> > > BXPC 00000001)
>> >>> > >  ACPI: IORT 0x00000000788E0000 00007C (v00 BOCHS  BXPCIORT 00000001
>> >>> > > BXPC 00000001)
>> >>> > >
>> >>> > > covered by
>> >>> > >
>> >>> > >  efi:   0x0000788e0000-0x00007894ffff [ACPI Reclaim Memory ...]
>> >>> > >  ...
>> >>> > >  efi:   0x000078970000-0x00007898ffff [ACPI Reclaim Memory ...]
>> >>> >
>> >>> > OK. I mistakenly understood those regions could be freed after exiting
>> >>> > UEFI boot services.
>> >>> >
>> >>> > >
>> >>> > > >> So it seems that kexec does not honour the memblock_reserve() table
>> >>> > > >> when booting the next kernel.
>> >>> > > >
>> >>> > > > not really.
>> >>> > > >
>> >>> > > >> > (In other words, can or should we skip some part of ACPI-related init code
>> >>> > > >> > on crash dump kernel?)
>> >>> > > >> >
>> >>> > > >>
>> >>> > > >> I don't think so. And the change to the handling of ACPI reclaim
>> >>> > > >> regions only revealed the bug, not created it (given that other
>> >>> > > >> memblock_reserve regions may be affected as well)
>> >>> > > >
>> >>> > > > As whether we should honor such reserved regions over kexec'ing
>> >>> > > > depends on each one's specific nature, we will have to take care one-by-one.
>> >>> > > > As a matter of fact, no information about "reserved" memblocks is
>> >>> > > > exposed to user space (via proc/iomem).
>> >>> > > >
>> >>> > >
>> >>> > > That is why I suggested (somewhere in this thread?) to not expose them
>> >>> > > as 'System RAM'. Do you think that could solve this?
>> >>> >
>> >>> > Memblock-reserv'ing them is necessary to prevent their corruption and
>> >>> > marking them under another name in /proc/iomem would also be good in order
>> >>> > not to allocate them as part of crash kernel's memory.
>> >>> >
>> >>> > But I'm not still convinced that we should export them in useable-
>> >>> > memory-range to crash dump kernel. They will be accessed through
>> >>> > acpi_os_map_memory() and so won't be required to be part of system ram
>> >>> > (or memblocks), I guess.
>> >>> >     -> Bhupesh?
>> >>>
>> >>> I forgot how arm64 kernel retrieve the memory ranges and initialize
>> >>> them.  If no "e820" like interfaces shouldn't kernel reinitialize all
>> >>> the memory according to the efi memmap?  For kdump kernel anything other
>> >>> than usable memory (which is from the dt node instead) should be
>> >>> reinitialized according to efi passed info, no?
>> >>
>> >> All the regions exported in efi memmap will be added to memblock.memory
>> >> in (u)efi_init() and then trimmed down to the exact range specified as
>> >> usable-memory-range by fdt_enforce_memory_region().
>> >>
>> >> Now I noticed that the current fdt_enforce_memory_region() may not work well
>> >> with multiple entries in usable-memory-range.
>> >>
>> >
>> > In any case, the root of the problem is that memory regions lose their
>> > 'memory' annotation due to the way the memory map is mangled before
>> > being supplied to the kexec kernel.
>> >
>> > Would it be possible to classify all memory that we want to hide from
>> > the kexec kernel as NOMAP instead? That way, it will not be mapped
>> > implicitly, but will still be mapped cacheable by acpi_os_ioremap(),
>> > so this seems to be the most appropriate way to deal with the host
>> > kernel's memory contents.
>>
>> Hmm. wouldn't appending the acpi reclaim regions to
>> 'linux,usable-memory-range' in the dtb being passed to the crashkernel
>> be better? Because its indirectly achieving a similar objective
>> (although may be a subset of all System RAM regions on the primary
>> kernel's memory).
>>
>> I am not aware of the background about the current kexec-tools
>> implementation where we add only the crashkernel range to the dtb
>> being passed to the crashkernel.
>>
>> Probably Akashi can answer better, as to how we arrived at this design
>> approach and why we didn't want to expose all System RAM regions (i.e.
>> ! NOMPAP regions) to the crashkernel.
>>
>> I am suspecting that some issues were seen/meet when the System RAM (!
>> NOMAP regions) were exposed to the crashkernel, and that's why we
>> finalized on this design approach, but this is something which is just
>> my guess.
>>
>> Regards,
>> Bhupesh
>>
>> >>> >
>> >>> > Just FYI, on x86, ACPI tables seems to be exposed to crash dump kernel
>> >>> > via a kernel command line parameter, "memmap=".
>> >>>
>> >>> memmap= is only used in old kexec-tools, now we are passing them via
>> >>> e820 table.
>> >>
>> >> Thanks. I remember that you have explained it before.
>> >>
>> >> -Takahiro AKASHI
>> >>
>> >>> [snip]
>> >>>
>> >>> Thanks
>> >>> Dave
>
> ===8<==
> From 74e2451fea83d546feae76160ba7de426913fe03 Mon Sep 17 00:00:00 2001
> From: AKASHI Takahiro <takahiro.akashi@linaro.org>
> Date: Thu, 21 Dec 2017 19:14:23 +0900
> Subject: [PATCH] arm64: kdump: mark unusable memory as NOMAP
>
> ---
>  arch/arm64/mm/init.c | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
> index 00e7b900ca41..8175db94257b 100644
> --- a/arch/arm64/mm/init.c
> +++ b/arch/arm64/mm/init.c
> @@ -352,11 +352,17 @@ static void __init fdt_enforce_memory_region(void)
>         struct memblock_region reg = {
>                 .size = 0,
>         };
> +       u64 idx;
> +       phys_addr_t start, end;
>
>         of_scan_flat_dt(early_init_dt_scan_usablemem, &reg);
>
> -       if (reg.size)
> -               memblock_cap_memory_range(reg.base, reg.size);
> +       if (reg.size) {
> +               for_each_free_mem_range(idx, NUMA_NO_NODE, MEMBLOCK_NONE,
> +                                       &start, &end, NULL)
> +                       memblock_mark_nomap(start, end - start);
> +               memblock_clear_nomap(reg.base, reg.size);
> +       }
>  }
>
>  void __init arm64_memblock_init(void)
> --
> 2.15.1
>

Thanks for the patch. After applying this on top of
4.15.0-rc4-next-20171220, there seems to be a improvement and the
crashkernel boot no longer hangs while trying to access the acpi
tables.

However I notice a minor issue. Please see the log below for
reference, the following message keeps spamming the console but I see
the crashkernel boot proceed further.:

[    0.000000] ACPI: NUMA: SRAT: PXM 3 -> MPIDR 0x70303 -> Node 3
[    0.000000] ACPI: SRAT: Node 0 PXM 0 [mem 0x00000000-0x3fffffff]
[    0.000000] ACPI: SRAT: Node 1 PXM 1 [mem 0x2000000000-0x2fffffffff]
[    0.000000] ACPI: SRAT: Node 0 PXM 0 [mem 0x1000000000-0x1fffffffff]
[    0.000000] ACPI: SRAT: Node 3 PXM 3 [mem 0xa000000000-0xafffffffff]
[    0.000000] ACPI: SRAT: Node 2 PXM 2 [mem 0x9000000000-0x9fffffffff]
[    0.000000] NUMA: NODE_DATA [mem 0x1ffbffe200-0x1ffbffffff]
[    0.000000] NUMA: NODE_DATA [mem 0x1ffbffc400-0x1ffbffe1ff]
[    0.000000] NUMA: NODE_DATA(1) on node 0
[    0.000000] NUMA: NODE_DATA [mem 0x1ffbffa600-0x1ffbffc3ff]
[    0.000000] NUMA: NODE_DATA(2) on node 0
[    0.000000] NUMA: NODE_DATA [mem 0x1ffbff8800-0x1ffbffa5ff]
[    0.000000] NUMA: NODE_DATA(3) on node 0
[    0.000000] [ffff7fe008000000-ffff7fe00800ffff] potential offnode
page_structs
[    0.000000] [ffff7fe008010000-ffff7fe00801ffff] potential offnode
page_structs
[    0.000000] [ffff7fe008020000-ffff7fe00802ffff] potential offnode
page_structs
[    0.000000] [ffff7fe008030000-ffff7fe00803ffff] potential offnode
page_structs
[    0.000000] [ffff7fe008040000-ffff7fe00804ffff] potential offnode
page_structs
[    0.000000] [ffff7fe008050000-ffff7fe00805ffff] potential offnode
page_structs

[snip..]
[    0.000000] [ffff7fe0081f0000-ffff7fe0081fffff] potential offnode
page_structs

This WARNING message seems to come from vmemmap_verify() inside
'mm/sparse-vmemmap.c'

Regards,
Bhupesh

^ permalink raw reply

* [PATCH v2] MAINTAINERS: Add self as extended maintainer for a slew of files
From: Linus Walleij @ 2017-12-21 11:56 UTC (permalink / raw)
  To: linux-arm-kernel

Take over sole maintenance of Nomadik, U300 and Ux500. Since all are
Device Tree converted and using standard format drivers this is not
burdensome. Alessandro is not working on this platform any more.
Let's use one single git tree for all of them and combine the
MAINTAINERS entries into one.

Suggested-by: Krzysztof Kozlowski <krzk@kernel.org>
Acked-by: Alessandro Rubini <rubini@unipv.it>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v1->v2:
- Combine the existing U300 and Ux500 entries into this entry
  covering all ST-Ericsson/Nomadik families.
- Add Alessandro's ACK.

ARM SoC folks: please apply this directly where appropriate.
---
 MAINTAINERS | 63 ++++++++++++++++++++++++++-----------------------------------
 1 file changed, 27 insertions(+), 36 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index aa71ab52fd76..0d6b84abad1d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1634,14 +1634,38 @@ ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT
 M:	Michael Petchkovsky <mkpetch@internode.on.net>
 S:	Maintained
 
-ARM/NOMADIK ARCHITECTURE
-M:	Alessandro Rubini <rubini@unipv.it>
+ARM/NOMADIK/U300/Ux500 ARCHITECTURES
 M:	Linus Walleij <linus.walleij@linaro.org>
 L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 F:	arch/arm/mach-nomadik/
-F:	drivers/pinctrl/nomadik/
+F:	arch/arm/mach-u300/
+F:	arch/arm/mach-ux500/
+F:	arch/arm/boot/dts/ste-*
+F:	drivers/clk/clk-nomadik.c
+F:	drivers/clk/clk-u300.c
+F:	drivers/clocksource/clksrc-dbx500-prcmu.c
+F:	drivers/clocksource/timer-u300.c
+F:	drivers/dma/coh901318*
+F:	drivers/dma/ste_dma40*
+F:	drivers/hwspinlock/u8500_hsem.c
 F:	drivers/i2c/busses/i2c-nomadik.c
+F:	drivers/i2c/busses/i2c-stu300.c
+F:	drivers/mfd/ab3100*
+F:	drivers/mfd/ab8500*
+F:	drivers/mfd/abx500*
+F:	drivers/mfd/dbx500*
+F:	drivers/mfd/db8500*
+F:	drivers/pinctrl/nomadik/
+F:	drivers/pinctrl/pinctrl-coh901*
+F:	drivers/pinctrl/pinctrl-u300.c
+F:	drivers/rtc/rtc-ab3100.c
+F:	drivers/rtc/rtc-ab8500.c
+F:	drivers/rtc/rtc-coh901331.c
+F:	drivers/rtc/rtc-pl031.c
+F:	drivers/watchdog/coh901327_wdt.c
+F:	Documentation/devicetree/bindings/arm/ste-*
+F:	Documentation/devicetree/bindings/arm/ux500/
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git
 
 ARM/NUVOTON W90X900 ARM ARCHITECTURE
@@ -2021,21 +2045,6 @@ M:	Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
 M:	Dirk Opfer <dirk@opfer-online.de>
 S:	Maintained
 
-ARM/U300 MACHINE SUPPORT
-M:	Linus Walleij <linus.walleij@linaro.org>
-L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
-S:	Supported
-F:	arch/arm/mach-u300/
-F:	drivers/clocksource/timer-u300.c
-F:	drivers/i2c/busses/i2c-stu300.c
-F:	drivers/rtc/rtc-coh901331.c
-F:	drivers/watchdog/coh901327_wdt.c
-F:	drivers/dma/coh901318*
-F:	drivers/mfd/ab3100*
-F:	drivers/rtc/rtc-ab3100.c
-F:	drivers/rtc/rtc-coh901331.c
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
-
 ARM/UNIPHIER ARCHITECTURE
 M:	Masahiro Yamada <yamada.masahiro@socionext.com>
 L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
@@ -2057,24 +2066,6 @@ F:	drivers/reset/reset-uniphier.c
 F:	drivers/tty/serial/8250/8250_uniphier.c
 N:	uniphier
 
-ARM/Ux500 ARM ARCHITECTURE
-M:	Linus Walleij <linus.walleij@linaro.org>
-L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
-S:	Maintained
-F:	arch/arm/mach-ux500/
-F:	drivers/clocksource/clksrc-dbx500-prcmu.c
-F:	drivers/dma/ste_dma40*
-F:	drivers/hwspinlock/u8500_hsem.c
-F:	drivers/mfd/abx500*
-F:	drivers/mfd/ab8500*
-F:	drivers/mfd/dbx500*
-F:	drivers/mfd/db8500*
-F:	drivers/pinctrl/nomadik/pinctrl-ab*
-F:	drivers/pinctrl/nomadik/pinctrl-nomadik*
-F:	drivers/rtc/rtc-ab8500.c
-F:	drivers/rtc/rtc-pl031.c
-T:	git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
-
 ARM/Ux500 CLOCK FRAMEWORK SUPPORT
 M:	Ulf Hansson <ulf.hansson@linaro.org>
 L:	linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
-- 
2.14.3

^ permalink raw reply related

* [PATCH v5 15/15] devicetree: bindings: Document qcom,pvs
From: Sricharan R @ 2017-12-21 11:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171220211817.mpkkj747blr4qkqm@rob-hp-laptop>

Hi Rob,

On 12/21/2017 2:48 AM, Rob Herring wrote:
> On Wed, Dec 20, 2017 at 11:55:33AM +0530, Sricharan R wrote:
>> Hi Viresh,
>>
>> On 12/20/2017 8:56 AM, Viresh Kumar wrote:
>>> On 19-12-17, 21:25, Sricharan R wrote:
>>>> +	cpu at 0 {
>>>> +		compatible = "qcom,krait";
>>>> +		enable-method = "qcom,kpss-acc-v1";
>>>> +		device_type = "cpu";
>>>> +		reg = <0>;
>>>> +		qcom,acc = <&acc0>;
>>>> +		qcom,saw = <&saw0>;
>>>> +		clocks = <&kraitcc 0>;
>>>> +		clock-names = "cpu";
>>>> +		cpu-supply = <&smb208_s2a>;
>>>> +		operating-points-v2 = <&cpu_opp_table>;
>>>> +	};
>>>> +
>>>> +	qcom,pvs {
>>>> +		qcom,pvs-format-a;
>>>> +	};
>>>
>>> Not sure what Rob is going to say on that :)
>>>
>>
>>  Yes. Would be good to know the best way.
> 
> Seems like this should be a property of an efuse node either implied by 
> the compatible or a separate property. What determines format A vs. B?
> 

 Yes, this efuse registers are part of the eeprom (qfprom) tied to the soc.
 So this property (details like bitfields and register offsets that it represents)
 can be put soc specific and nvmem apis can be used to read
 the registers. Does something like below look ok ?

 qcom,pvs {
	compatible = "qcom,pvs-ipq8064";
	nvmem-cells = <&pvs_efuse>;
 }

 qfprom: qfprom at 700000 {
 	compatible      = "qcom,qfprom";
 	reg             = <0x00700000 0x1000>;
 	#address-cells  = <1>;
 	#size-cells     = <1>;
 	ranges;
 	pvs_efuse: pvs {
 	reg = <0xc0 0x8>;
 	};
 };


Regards,
 Sricharan
 
 

-- 
"QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

^ permalink raw reply

* [GIT PULL v2] arm: Updates for soc driver for v4.15-next
From: Matthias Brugger @ 2017-12-21 11:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Arnd,
Hi Olof,

Please take the following patches into account.

Thanks a lot,
Matthias

---
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:

  Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/linux.git
tags/v4.15-next-soc

for you to fetch changes up to fdac5f4d4857d488fcd77308fec95b44196fdfb1:

  Merge remote-tracking branch 'mainline-kernel/v4.15-next/pdids' into
v4.15-next/soc (2017-12-21 11:49:33 +0100)

----------------------------------------------------------------
- change kconfig entry for armv7 SoCs to be more generic
- add support for mt2701 scpsys driver
  binding documentation
  extend driver to allow the bus protection to overwrite the register

----------------------------------------------------------------
Matthias Brugger (1):
      Merge remote-tracking branch 'mainline-kernel/v4.15-next/pdids' into
v4.15-next/soc

Sean Wang (1):
      ARM: mediatek: use more generic prompts for SoCs with ARMv7

weiyi.lu at mediatek.com (4):
      dt-bindings: soc: add MT2712 power dt-bindings
      soc: mediatek: extend bus protection API
      soc: mediatek: add dependent clock jpgdec/audio for scpsys
      soc: mediatek: add MT2712 scpsys support

 .../devicetree/bindings/soc/mediatek/scpsys.txt    |   3 +
 arch/arm/mach-mediatek/Kconfig                     |   2 +-
 drivers/soc/mediatek/mtk-infracfg.c                |  26 +++-
 drivers/soc/mediatek/mtk-scpsys.c                  | 140 ++++++++++++++++++---
 include/dt-bindings/power/mt2712-power.h           |  26 ++++
 include/linux/soc/mediatek/infracfg.h              |   7 +-
 6 files changed, 181 insertions(+), 23 deletions(-)
 create mode 100644 include/dt-bindings/power/mt2712-power.h

^ permalink raw reply

* [GIT PULL v2] arm64: Updates of aarch64 DTS for v4.15-next
From: Matthias Brugger @ 2017-12-21 11:47 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Anrd, hi Olof,

Please have a look on the following patches.

Thanks a lot,
Matthias

---
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:

  Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/linux.git
tags/v4.15-next-dts64

for you to fetch changes up to ca977a4cae1655d0dac37a9d85455a3a3afe55eb:

  arm64: dts: Add power controller device node of MT2712 (2017-12-21 11:35:18 +0100)

----------------------------------------------------------------
- mt8173 add cpufreq related nodes
  supply nodes
  frequency/voltage operation table

- mt2712 add cpufreq related nodes
  fixed regulator
  supply nodes
  frequency/voltage operation table
- mt2712 add clock contoller nodes
- mt2712 add scpsys node

----------------------------------------------------------------
Andrew-sh Cheng (2):
      arm64: dts: mediatek: add mt8173 cpufreq related device nodes
      arm64: dts: mediatek: add mt2712 cpufreq related device nodes

Matthias Brugger (1):
      Merge remote-tracking branch 'mainline-kernel/v4.15-next/pdids' into
v4.15-next/dts64

weiyi.lu at mediatek.com (3):
      dt-bindings: soc: add MT2712 power dt-bindings
      arm64: dts: mt2712: Add clock controller device nodes
      arm64: dts: Add power controller device node of MT2712

 .../devicetree/bindings/soc/mediatek/scpsys.txt    |   3 +
 arch/arm64/boot/dts/mediatek/mt2712-evb.dts        |  27 +++
 arch/arm64/boot/dts/mediatek/mt2712e.dtsi          | 188 +++++++++++++++++++++
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts        |  18 ++
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |  90 ++++++++++
 include/dt-bindings/power/mt2712-power.h           |  26 +++
 6 files changed, 352 insertions(+)
 create mode 100644 include/dt-bindings/power/mt2712-power.h

^ permalink raw reply

* [GIT PULL v2] arm: Updates of armv7 DTS for v4.15-next
From: Matthias Brugger @ 2017-12-21 11:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Olof, hi Arnd,

Please have a look on the following patches.

Thanks a lot,
Matthias

---
The following changes since commit 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323:

  Linux 4.15-rc1 (2017-11-26 16:01:47 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/linux.git
tags/v4.15-next-dts32

for you to fetch changes up to a227cf4dfd74a873857c9cc017100168d01539ed:

  dt-bindings: ARM: Mediatek: Fix ethsys documentation (2017-12-20 18:10:12 +0100)

----------------------------------------------------------------
- add reset cells mt2701 and mt7623 ethsys
- update mmc nodes for mt7623
- mt7623 change mmc card detection pin to active low
- mt7623 set unit address to lower case

----------------------------------------------------------------
Mathieu Malaterre (1):
      arm: mt7: dts: Remove leading 0x and 0s from bindings notation

Matthias Brugger (3):
      arm: dts: mt7623: Update ethsys binding
      arm: dts: mt2701: Add reset-cells
      dt-bindings: ARM: Mediatek: Fix ethsys documentation

Sean Wang (2):
      arm: dts: mt7623: update mmc related nodes with the appropriate fallback
      arm: dts: mt7623: fix card detection issue on bananapi-r2

 Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt | 1 +
 arch/arm/boot/dts/mt2701.dtsi                                      | 2 ++
 arch/arm/boot/dts/mt7623.dtsi                                      | 5 +++--
 arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts                      | 2 +-
 arch/arm/boot/dts/mt7623n-rfb-nand.dts                             | 2 +-
 5 files changed, 8 insertions(+), 4 deletions(-)

^ permalink raw reply

* [PATCH] MAINTAINERS: Add self as extended maintainer for a slew of files
From: Linus Walleij @ 2017-12-21 11:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAK8P3a2LFGyTfdQ2AYd=jeu4vCbu5oVqQJhVjFwwc-zChLyj=A@mail.gmail.com>

On Wed, Dec 20, 2017 at 3:27 PM, Arnd Bergmann <arnd@arndb.de> wrote:
> On Wed, Dec 20, 2017 at 12:30 PM, Linus Walleij
> <linus.walleij@linaro.org> wrote:
>> Take over sole maintenance of Nomadik, U300 and Ux500. Since all are
>> Device Tree converted and using standard format drivers this is not
>> burdensome. Alessandro is not working on this platform any more.
>>
>> Suggested-by: Krzysztof Kozlowski <krzk@kernel.org>
>> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
>> ---
>> ARM SoC folks: please apply this directly where appropriate.
>
> I'm sure you are doing this right, but it would be good out of principle
> to have Alessandro on Cc and ask for an Ack from him.

Yeah sorry about that, I missed it. Luckily Alessandro came
back and ACKed it.

>> -ARM/NOMADIK ARCHITECTURE
>> -M:     Alessandro Rubini <rubini@unipv.it>
>> +ARM/NOMADIK/U300/Ux500 ARCHITECTURE
>>  M:     Linus Walleij <linus.walleij@linaro.org>
>>  L:     linux-arm-kernel at lists.infradead.org (moderated for non-subscribers)
>>  S:     Maintained
>>  F:     arch/arm/mach-nomadik/
>> +F:     arch/arm/mach-u300/
>> +F:     arch/arm/mach-ux500/
>> +F:     arch/arm/boot/dts/ste-*
>>  F:     drivers/pinctrl/nomadik/
>>  F:     drivers/i2c/busses/i2c-nomadik.c
>> +F:     Documentation/devicetree/bindings/arm/ste-*
>> +F:     Documentation/devicetree/bindings/arm/ux500/
>>  T:     git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git
>
> Shouldn't you remove the existing Ux500 and merge the remaining
> contents in here?

Indeed. I will resend it with Alessandros ACK and that fixed.

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH v3 2/2] PCI: mediatek: Fixup class type for MT7622
From: honghui.zhang at mediatek.com @ 2017-12-21 11:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513856325-28813-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang@mediatek.com>

The host bridge of MT7622 has hardware code the class code to an
arbitrary, meaningless value, fix that.

Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
---
 drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
index fc29a9a..dcafeac 100644
--- a/drivers/pci/host/pcie-mediatek.c
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -1175,3 +1175,15 @@ static struct platform_driver mtk_pcie_driver = {
 	},
 };
 builtin_platform_driver(mtk_pcie_driver);
+
+/* The host bridge of MT7622 advertises the wrong device class. */
+static void mtk_fixup_class(struct pci_dev *dev)
+{
+	dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+
+/*
+ * The HW default value of vendor id and device id for mt7622 are 0x0e8d,
+ * 0x3258, which are arbitrary, meaningless values.
+ */
+DECLARE_PCI_FIXUP_EARLY(0x0e8d, 0x3258, mtk_fixup_class);
-- 
2.6.4

^ permalink raw reply related

* [PATCH v3 1/2] PCI: mediatek: Clear IRQ status after IRQ dispatched to avoid reentry
From: honghui.zhang at mediatek.com @ 2017-12-21 11:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513856325-28813-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang@mediatek.com>

There maybe a same IRQ reentry scenario after IRQ received in current
IRQ handle flow:
	EP device		PCIe host driver	EP driver
1. issue an IRQ
			2. received IRQ
			3. clear IRQ status
			4. dispatch IRQ
						5. clear IRQ source
The IRQ status was not successfully cleared at step 2 since the IRQ
source was not cleared yet. So the PCIe host driver may receive the
same IRQ after step 5. Then there's an IRQ reentry occurred.
Even worse, if the reentry IRQ was not an IRQ that EP driver expected,
it may not handle the IRQ. Then we may run into the infinite loop from
step 2 to step 4.
Clear the IRQ status after IRQ have been dispatched to avoid the IRQ
reentry.
This patch also fix another INTx IRQ issue by initialize the iterate
before the loop. If an INTx IRQ re-occurred while we are dispatching
the INTx IRQ, then iterate may start from PCI_NUM_INTX + INTX_SHIFT
instead of INTX_SHIFT for the second time entering the
for_each_set_bit_from() loop.

Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
Acked-by: Ryder Lee <ryder.lee@mediatek.com>
---
 drivers/pci/host/pcie-mediatek.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
index db93efd..fc29a9a 100644
--- a/drivers/pci/host/pcie-mediatek.c
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -601,15 +601,16 @@ static irqreturn_t mtk_pcie_intr_handler(int irq, void *data)
 	struct mtk_pcie_port *port = (struct mtk_pcie_port *)data;
 	unsigned long status;
 	u32 virq;
-	u32 bit = INTX_SHIFT;
+	u32 bit;
 
 	while ((status = readl(port->base + PCIE_INT_STATUS)) & INTX_MASK) {
+		bit = INTX_SHIFT;
 		for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) {
-			/* Clear the INTx */
-			writel(1 << bit, port->base + PCIE_INT_STATUS);
 			virq = irq_find_mapping(port->irq_domain,
 						bit - INTX_SHIFT);
 			generic_handle_irq(virq);
+			/* Clear the INTx */
+			writel(1 << bit, port->base + PCIE_INT_STATUS);
 		}
 	}
 
@@ -619,10 +620,10 @@ static irqreturn_t mtk_pcie_intr_handler(int irq, void *data)
 
 			while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
 				for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
-					/* Clear the MSI */
-					writel(1 << bit, port->base + PCIE_IMSI_STATUS);
 					virq = irq_find_mapping(port->msi_domain, bit);
 					generic_handle_irq(virq);
+					/* Clear the MSI */
+					writel(1 << bit, port->base + PCIE_IMSI_STATUS);
 				}
 			}
 			/* Clear MSI interrupt status */
-- 
2.6.4

^ permalink raw reply related

* [PATCH v3 0/2] PCI: mediatek: Fixups for the IRQ handle routine and MT7622's class code
From: honghui.zhang at mediatek.com @ 2017-12-21 11:38 UTC (permalink / raw)
  To: linux-arm-kernel

From: Honghui Zhang <honghui.zhang@mediatek.com>

Two fixups for mediatek's host bridge:
The first patch fixup the IRQ handle routine to avoid IRQ reentry which
may exist for both MT2712 and MT7622.
The second patch fixup class type for MT7622.

Change since v2:
 - Move the initialize of the iterate before the loop to fix an
   INTx IRQ issue in the first patch

Change since v1:
 - Add the second patch.
 - Make the first patch's commit message more standard.

Honghui Zhang (2):
  PCI: mediatek: Clear IRQ status after IRQ dispatched to avoid reentry
  PCI: mediatek: Fixup class type for MT7622

 drivers/pci/host/pcie-mediatek.c | 23 ++++++++++++++++++-----
 1 file changed, 18 insertions(+), 5 deletions(-)

-- 
2.6.4

^ permalink raw reply

* [PATCH] KVM: arm/arm64: don't set vtimer->cnt_ctl in kvm_arch_timer_handler
From: Christoffer Dall @ 2017-12-21 11:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <b38aaf58-ea00-f5ef-a307-56cdfd84bd64@gmail.com>

On Thu, Dec 21, 2017 at 05:16:48PM +0800, Jia He wrote:
> 
> Sorry for the late, I ever thought you would send out v2 with isb().
> It seems not.
> 

I did:

https://lists.cs.columbia.edu/pipermail/kvmarm/2017-December/029078.html

> 
> On 12/15/2017 6:04 PM, Christoffer Dall Wrote:
> >On Fri, Dec 15, 2017 at 10:27:02AM +0800, Jia He wrote:
> >
> >[...]
> [...]
> >
> >Meanwhile, I think I thought of a cleaner way to do this.  Could you
> >test the following two patches on your platform as well?
> >
> >>From 3a594a3aa222bd64a86f6c6afcb209c9be20d5c5 Mon Sep 17 00:00:00 2001
> >From: Christoffer Dall <christoffer.dall@linaro.org>
> >Date: Thu, 14 Dec 2017 19:54:50 +0100
> >Subject: [PATCH 1/2] KVM: arm/arm64: Properly handle arch-timer IRQs after
> >  vtimer_save_state
> >
> >The recent timer rework was assuming that once the timer was disabled,
> >we should no longer see any interrupts from the timer.  This assumption
> >turns out to not be true, and instead we have to handle the case when
> >the timer ISR runs even after the timer has been disabled.
> >
> >This requires a couple of changes:
> >
> >First, we should never overwrite the cached guest state of the timer
> >control register when the ISR runs, because KVM may have disabled its
> >timers when doing vcpu_put(), even though the guest still had the timer
> >enabled.
> >
> >Second, we shouldn't assume that the timer is actually firing just
> >because we see an ISR, but we should check the ISTATUS field of the
> >timer control register to understand if the hardware timer is really
> >firing or not.
> >
> >Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
> 
> Signed-off-by: Jia He <jia.he@hxt-semitech.com>
> 

Did you write parts of this patch and thus I should have had your
signed-off-by ?

Or did you mean to provide another tag.

Anyway, these patches have been pulled already, so I hope we can live
with the way they are.

> >---
> >  virt/kvm/arm/arch_timer.c | 19 ++++++++++++-------
> >  1 file changed, 12 insertions(+), 7 deletions(-)
> >
> >diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> >index aa9adfafe12b..792bcf6277b6 100644
> >--- a/virt/kvm/arm/arch_timer.c
> >+++ b/virt/kvm/arm/arch_timer.c
> >@@ -92,16 +92,21 @@ static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
> >  {
> >  	struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
> >  	struct arch_timer_context *vtimer;
> >+	u32 cnt_ctl;
> >-	if (!vcpu) {
> >-		pr_warn_once("Spurious arch timer IRQ on non-VCPU thread\n");
> >-		return IRQ_NONE;
> >-	}
> >-	vtimer = vcpu_vtimer(vcpu);
> >+	/*
> >+	 * We may see a timer interrupt after vcpu_put() has been called which
> >+	 * sets the CPU's vcpu pointer to NULL, because even though the timer
> >+	 * has been disabled in vtimer_save_state(), the singal may not have
> >+	 * been retired from the interrupt controller yet.
> >+	 */
> >+	if (!vcpu)
> >+		return IRQ_HANDLED;
> >+	vtimer = vcpu_vtimer(vcpu);
> >  	if (!vtimer->irq.level) {
> >-		vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl);
> >-		if (kvm_timer_irq_can_fire(vtimer))
> >+		cnt_ctl = read_sysreg_el0(cntv_ctl);
> >+		if (cnt_ctl & ARCH_TIMER_CTRL_IT_STAT)
> >  			kvm_timer_update_irq(vcpu, true, vtimer);
> >  	}
> >
> >
> >>From ed96302b47d209024814df116994f64dc8f07c96 Mon Sep 17 00:00:00 2001
> >From: Christoffer Dall <christoffer.dall@linaro.org>
> >Date: Fri, 15 Dec 2017 00:30:12 +0100
> >Subject: [PATCH 2/2] KVM: arm/arm64: Fix timer enable flow
> >
> >When enabling the timer on the first run, we fail to ever restore the
> >state and mark it as loaded.  That means, that in the initial entry to
> >the VCPU ioctl, unless we exit to userspace for some reason such as a
> >pending signal, if the guest programs a timer and blocks, we will wait
> >forever, because we never read back the hardware state (the loaded flag
> >is not set), and so we think the timer is disabled, and we never
> >schedule a background soft timer.
> >
> >The end result?  The VCPU blocks forever, and the only solution is to
> >kill the thread.
> >
> >Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
> >---
> >  virt/kvm/arm/arch_timer.c | 5 +----
> >  1 file changed, 1 insertion(+), 4 deletions(-)
> >
> >diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
> >index 792bcf6277b6..8869658e6983 100644
> >--- a/virt/kvm/arm/arch_timer.c
> >+++ b/virt/kvm/arm/arch_timer.c
> >@@ -843,10 +843,7 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
> >  no_vgic:
> >  	preempt_disable();
> >  	timer->enabled = 1;
> >-	if (!irqchip_in_kernel(vcpu->kvm))
> >-		kvm_timer_vcpu_load_user(vcpu);
> >-	else
> >-		kvm_timer_vcpu_load_vgic(vcpu);
> >+	kvm_timer_vcpu_load(vcpu);
> >  	preempt_enable();
> >  	return 0;
> >
> >
> >Thanks,
> >-Christoffer
> >
> I have tested your 2 patches in my QDF2400 server for 10 times, the
> guest can be boot up without any issues.
> Without this patch, the guest will always hang in booting stages.
> 
Thanks for this, it's comforting to know.

Thanks,
-Christoffer

^ permalink raw reply

* [PATCH net-next v7 1/2] dt-bindings: net: add DT bindings for Socionext UniPhier AVE
From: Andrew Lunn @ 2017-12-21 11:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513854776-4149-2-git-send-email-hayashi.kunihiko@socionext.com>

> +Optional properties:
> + - resets: A phandle to the reset control for the MAC
> + - local-mac-address: See ethernet.txt in the same directory.
> +
> +Required subnode:
> + - mdio: Device tree subnode with the following required properties:
> +
> +Example:

It sounds like there should be some properties before the Example.

   Andrew

> +
> +	ether: ethernet at 65000000 {
> +		compatible = "socionext,uniphier-ld20-ave4";
> +		reg = <0x65000000 0x8500>;
> +		interrupts = <0 66 4>;
> +		phy-mode = "rgmii";
> +		phy-handle = <&ethphy>;
> +		clocks = <&sys_clk 6>;
> +		resets = <&sys_rst 6>;
> +		local-mac-address = [00 00 00 00 00 00];

Typically you would put a blank line here, before the mdio node.

> +		mdio {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			ethphy: ethphy at 1 {
> +				reg = <1>;
> +			};
> +		};
> +	};

  Andrew

^ permalink raw reply

* [PATCH 0/2] arm64: dts: renesas: Remove renesas,no-ether-link property
From: Simon Horman @ 2017-12-21 11:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513776132-22700-1-git-send-email-vladimir_zapolskiy@mentor.com>

On Wed, Dec 20, 2017 at 03:22:10PM +0200, Vladimir Zapolskiy wrote:
> The present change is a bug fix for AVB link iteratively up/down.

If this is a bug please consider including Fixes tags in the patches.

> Steps to reproduce:
> - start AVB TX stream (Using aplay via MSE),
> - disconnect+reconnect the eth cable,
> - after a reconnection the eth connection goes iteratively up/down
>   without user interaction,
> - this may heal after some seconds or even stay for minutes.
> 
> As the documentation specifies, the "renesas,no-ether-link" option
> should be used when a board does not provide a proper AVB_LINK signal.
> There is no need for this option enabled on RCAR H3/M3 Salvator-X/XS
> and ULCB starter kits since the AVB_LINK is correctly handled by HW.
> 
> Choosing to keep or remove the "renesas,no-ether-link" option will
> have impact on the code flow in the following ways:
> - keeping this option enabled may lead to unexpected behavior since
>   the RX & TX are enabled/disabled directly from adjust_link function
>   without any HW interrogation,
> - removing this option, the RX & TX will only be enabled/disabled after
>   HW interrogation. The HW check is made through the LMON pin in PSR
>   register which specifies AVB_LINK signal value (0 - at low level;
>   1 - at high level).
> 
> In conclusion, the change is also a safety improvement because it
> removes the "renesas,no-ether-link" option leading to a proper way
> of detecting the link state based on HW interrogation and not on
> software heuristic.
> 
> Note that DTS files for V3M Starter Kit, Draak and Eagle boards
> contain the same property, the files are untouched due to unavailable
> schematics to verify if the fix applies to these boards as well.
> 
> Bogdan Mirea (2):
>   arm64: dts: renesas: salvator-x: Remove renesas,no-ether-link property
>   arm64: dts: renesas: ulcb: Remove renesas,no-ether-link property
> 
>  arch/arm64/boot/dts/renesas/salvator-common.dtsi | 1 -
>  arch/arm64/boot/dts/renesas/ulcb.dtsi            | 1 -
>  2 files changed, 2 deletions(-)
> 
> -- 
> 2.8.1
> 

^ permalink raw reply

* [PATCH] drm/sun4i: hdmi: Fix sun4i_tmds_determine_rate
From: Jonathan Liu @ 2017-12-21 11:24 UTC (permalink / raw)
  To: linux-arm-kernel

There are several issues in sun4i_tmds_determine_rate:
- doesn't check if the best match was already set before comparing it
  with the enumerated parameters which could result in integer divide
  by zero
- doesn't consider rate halving when determining closest match if it
  can't match the requested rate exactly
- sets best_div to i which corresponds to rate halving when it should be
  set to j which corresponds to the divider

Fix these issues.

Fixes: 9c5681011a0c ("drm/sun4i: Add HDMI support")
Signed-off-by: Jonathan Liu <net147@gmail.com>
---
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
index dc332ea56f6c..3ecffa52c814 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -102,10 +102,13 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw,
 					goto out;
 				}
 
-				if (abs(rate - rounded / i) <
-				    abs(rate - best_parent / best_div)) {
+				if (!best_parent ||
+				    abs(rate - rounded / i / j) <
+				    abs(rate - best_parent / best_half /
+					best_div)) {
 					best_parent = rounded;
-					best_div = i;
+					best_half = i;
+					best_div = j;
 				}
 			}
 		}
-- 
2.15.0

^ permalink raw reply related

* [PATCH 09/10] arm64: allow ID map to be extended to 52 bits
From: Suzuki K Poulose @ 2017-12-21 11:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513184845-8711-10-git-send-email-kristina.martsenko@arm.com>

On 13/12/17 17:07, Kristina Martsenko wrote:
> Currently, when using VA_BITS < 48, if the ID map text happens to be
> placed in physical memory above VA_BITS, we increase the VA size (up to
> 48) and create a new table level, in order to map in the ID map text.
> This is okay because the system always supports 48 bits of VA.
> 
> This patch extends the code such that if the system supports 52 bits of
> VA, and the ID map text is placed that high up, then we increase the VA
> size accordingly, up to 52.
> 
> One difference from the current implementation is that so far the
> condition of VA_BITS < 48 has meant that the top level table is always
> "full", with the maximum number of entries, and an extra table level is
> always needed. Now, when VA_BITS = 48 (and using 64k pages), the top
> level table is not full, and we simply need to increase the number of
> entries in it, instead of creating a new table level.
> 
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> ---
>   arch/arm/include/asm/kvm_mmu.h       |  5 +++
>   arch/arm64/include/asm/assembler.h   |  2 -
>   arch/arm64/include/asm/kvm_mmu.h     |  7 +++-
>   arch/arm64/include/asm/mmu_context.h | 14 ++++++-
>   arch/arm64/kernel/head.S             | 76 +++++++++++++++++++++---------------
>   arch/arm64/kvm/hyp-init.S            | 17 ++++----
>   arch/arm64/mm/mmu.c                  |  1 +
>   virt/kvm/arm/mmu.c                   | 12 +++---
>   8 files changed, 83 insertions(+), 51 deletions(-)
> 
> diff --git a/arch/arm/include/asm/kvm_mmu.h b/arch/arm/include/asm/kvm_mmu.h
> index 8dbec683638b..8c5643e2eea4 100644
> --- a/arch/arm/include/asm/kvm_mmu.h
> +++ b/arch/arm/include/asm/kvm_mmu.h
> @@ -211,6 +211,11 @@ static inline bool __kvm_cpu_uses_extended_idmap(void)
>   	return false;
>   }
>   
> +static inline unsigned long __kvm_idmap_ptrs_per_pgd(void)
> +{
> +	return PTRS_PER_PGD;
> +}
> +
>   static inline void __kvm_extend_hypmap(pgd_t *boot_hyp_pgd,
>   				       pgd_t *hyp_pgd,
>   				       pgd_t *merged_hyp_pgd,
> diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
> index 2058fd864bfb..11719b11f4a7 100644
> --- a/arch/arm64/include/asm/assembler.h
> +++ b/arch/arm64/include/asm/assembler.h
> @@ -344,10 +344,8 @@ alternative_endif
>    * tcr_set_idmap_t0sz - update TCR.T0SZ so that we can load the ID map
>    */
>   	.macro	tcr_set_idmap_t0sz, valreg, tmpreg
> -#ifndef CONFIG_ARM64_VA_BITS_48
>   	ldr_l	\tmpreg, idmap_t0sz
>   	bfi	\valreg, \tmpreg, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
> -#endif
>   	.endm
>   
>   /*
> diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
> index b3f7b68b042d..8d663ca0d50c 100644
> --- a/arch/arm64/include/asm/kvm_mmu.h
> +++ b/arch/arm64/include/asm/kvm_mmu.h
> @@ -273,7 +273,12 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled);
>   
>   static inline bool __kvm_cpu_uses_extended_idmap(void)
>   {
> -	return __cpu_uses_extended_idmap();
> +	return __cpu_uses_extended_idmap_table();
> +}
> +
> +static inline unsigned long __kvm_idmap_ptrs_per_pgd(void)
> +{
> +	return idmap_ptrs_per_pgd;
>   }
>   
>   /*
> diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
> index accc2ff32a0e..043eed856b55 100644
> --- a/arch/arm64/include/asm/mmu_context.h
> +++ b/arch/arm64/include/asm/mmu_context.h
> @@ -63,11 +63,21 @@ static inline void cpu_set_reserved_ttbr0(void)
>    * physical memory, in which case it will be smaller.
>    */
>   extern u64 idmap_t0sz;
> +extern u64 idmap_ptrs_per_pgd;
>   
>   static inline bool __cpu_uses_extended_idmap(void)
>   {
> -	return (!IS_ENABLED(CONFIG_ARM64_VA_BITS_48) &&
> -		unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS)));
> +	return unlikely(idmap_t0sz != TCR_T0SZ(VA_BITS));
> +}
> +
> +/*
> + * True if the extended ID map requires an extra level of translation table
> + * to be configured.
> + */
> +static inline bool __cpu_uses_extended_idmap_table(void)
> +{
> +	return __cpu_uses_extended_idmap() &&
> +		(idmap_ptrs_per_pgd == PTRS_PER_PGD);
>   }
>   

Kristina,

I feel this is a bit confusing, as we use idmap_ptrs_per_pgd value to convey two
different information.

1) Whether we use an extended level of table
2) Number of ptrs in the host VA PGD level for idmap

I think we are able to figure out if the idmap uses an extended table using the idmap_t0sz alone :

static inline bool __cpu_uses_extended_idmap_level(void)
{
	return ARM64_HW_PGTABLE_LEVELS((64 - idmap_t0sz)) > CONFIG_PGTABLE_LEVELS;
}

Also, the name could be changed to __cpu_uses_extended_idmap_level() to imply we are talking
about an additional level of table, rather than extending the table, which could either mean
extending the table to include more pgd ptrs or an additional level.


Rest looks good to me.

Suzuki

^ permalink raw reply

* [PATCH] arm64: defconfig: enable ARM_ARMADA_37XX_CPUFREQ
From: Gregory CLEMENT @ 2017-12-21 11:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171215153532.29924-1-gregory.clement@free-electrons.com>

Hi,
 
 On ven., d?c. 15 2017, Gregory CLEMENT <gregory.clement@free-electrons.com> wrote:

> Add the cpu frequency scaling support for Armada 37xx by default, this
> should allow a better coverage in kernel continuous integration tests.
>
> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>

Applied on mvebu/arm64

Gregory

> ---
>  arch/arm64/configs/defconfig | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
> index 6356c6da34ea..a4222de46500 100644
> --- a/arch/arm64/configs/defconfig
> +++ b/arch/arm64/configs/defconfig
> @@ -99,6 +99,7 @@ CONFIG_WQ_POWER_EFFICIENT_DEFAULT=y
>  CONFIG_ARM_CPUIDLE=y
>  CONFIG_CPU_FREQ=y
>  CONFIG_CPUFREQ_DT=y
> +CONFIG_ARM_ARMADA_37XX_CPUFREQ=y
>  CONFIG_ARM_BIG_LITTLE_CPUFREQ=y
>  CONFIG_ARM_SCPI_CPUFREQ=y
>  CONFIG_ACPI_CPPC_CPUFREQ=m
> -- 
> 2.15.1
>

-- 
Gregory Clement, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* [PATCH net-next v7 2/2] net: ethernet: socionext: add AVE ethernet driver
From: Kunihiko Hayashi @ 2017-12-21 11:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513854776-4149-1-git-send-email-hayashi.kunihiko@socionext.com>

The UniPhier platform from Socionext provides the AVE ethernet
controller that includes MAC and MDIO bus supporting RGMII/RMII
modes. The controller is named AVE.

Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/Kconfig             |    1 +
 drivers/net/ethernet/Makefile            |    1 +
 drivers/net/ethernet/socionext/Kconfig   |   22 +
 drivers/net/ethernet/socionext/Makefile  |    5 +
 drivers/net/ethernet/socionext/sni_ave.c | 1736 ++++++++++++++++++++++++++++++
 5 files changed, 1765 insertions(+)
 create mode 100644 drivers/net/ethernet/socionext/Kconfig
 create mode 100644 drivers/net/ethernet/socionext/Makefile
 create mode 100644 drivers/net/ethernet/socionext/sni_ave.c

diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index c604213..d50519e 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -170,6 +170,7 @@ source "drivers/net/ethernet/sis/Kconfig"
 source "drivers/net/ethernet/sfc/Kconfig"
 source "drivers/net/ethernet/sgi/Kconfig"
 source "drivers/net/ethernet/smsc/Kconfig"
+source "drivers/net/ethernet/socionext/Kconfig"
 source "drivers/net/ethernet/stmicro/Kconfig"
 source "drivers/net/ethernet/sun/Kconfig"
 source "drivers/net/ethernet/tehuti/Kconfig"
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 39f62733..6cf5ade 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_SFC) += sfc/
 obj-$(CONFIG_SFC_FALCON) += sfc/falcon/
 obj-$(CONFIG_NET_VENDOR_SGI) += sgi/
 obj-$(CONFIG_NET_VENDOR_SMSC) += smsc/
+obj-$(CONFIG_NET_VENDOR_SOCIONEXT) += socionext/
 obj-$(CONFIG_NET_VENDOR_STMICRO) += stmicro/
 obj-$(CONFIG_NET_VENDOR_SUN) += sun/
 obj-$(CONFIG_NET_VENDOR_TEHUTI) += tehuti/
diff --git a/drivers/net/ethernet/socionext/Kconfig b/drivers/net/ethernet/socionext/Kconfig
new file mode 100644
index 0000000..3a1829e
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Kconfig
@@ -0,0 +1,22 @@
+config NET_VENDOR_SOCIONEXT
+	bool "Socionext ethernet drivers"
+	default y
+	---help---
+	  Option to select ethernet drivers for Socionext platforms.
+
+	  Note that the answer to this question doesn't directly affect the
+	  kernel: saying N will just cause the configurator to skip all
+	  the questions about Socionext devices. If you say Y, you will be asked
+	  for your specific card in the following questions.
+
+if NET_VENDOR_SOCIONEXT
+
+config SNI_AVE
+	tristate "Socionext AVE ethernet support"
+	depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
+	select PHYLIB
+	---help---
+	  Driver for gigabit ethernet MACs, called AVE, in the
+	  Socionext UniPhier family.
+
+endif #NET_VENDOR_SOCIONEXT
diff --git a/drivers/net/ethernet/socionext/Makefile b/drivers/net/ethernet/socionext/Makefile
new file mode 100644
index 0000000..ab83df6
--- /dev/null
+++ b/drivers/net/ethernet/socionext/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for all ethernet ip drivers on Socionext platforms
+#
+obj-$(CONFIG_SNI_AVE) += sni_ave.o
diff --git a/drivers/net/ethernet/socionext/sni_ave.c b/drivers/net/ethernet/socionext/sni_ave.c
new file mode 100644
index 0000000..0925675
--- /dev/null
+++ b/drivers/net/ethernet/socionext/sni_ave.c
@@ -0,0 +1,1736 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * sni_ave.c - Socionext UniPhier AVE ethernet driver
+ * Copyright 2014 Panasonic Corporation
+ * Copyright 2015-2017 Socionext Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/etherdevice.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/mii.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_net.h>
+#include <linux/of_mdio.h>
+#include <linux/of_platform.h>
+#include <linux/phy.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <linux/u64_stats_sync.h>
+
+/* General Register Group */
+#define AVE_IDR			0x000	/* ID */
+#define AVE_VR			0x004	/* Version */
+#define AVE_GRR			0x008	/* Global Reset */
+#define AVE_CFGR		0x00c	/* Configuration */
+
+/* Interrupt Register Group */
+#define AVE_GIMR		0x100	/* Global Interrupt Mask */
+#define AVE_GISR		0x104	/* Global Interrupt Status */
+
+/* MAC Register Group */
+#define AVE_TXCR		0x200	/* TX Setup */
+#define AVE_RXCR		0x204	/* RX Setup */
+#define AVE_RXMAC1R		0x208	/* MAC address (lower) */
+#define AVE_RXMAC2R		0x20c	/* MAC address (upper) */
+#define AVE_MDIOCTR		0x214	/* MDIO Control */
+#define AVE_MDIOAR		0x218	/* MDIO Address */
+#define AVE_MDIOWDR		0x21c	/* MDIO Data */
+#define AVE_MDIOSR		0x220	/* MDIO Status */
+#define AVE_MDIORDR		0x224	/* MDIO Rd Data */
+
+/* Descriptor Control Register Group */
+#define AVE_DESCC		0x300	/* Descriptor Control */
+#define AVE_TXDC		0x304	/* TX Descriptor Configuration */
+#define AVE_RXDC0		0x308	/* RX Descriptor Ring0 Configuration */
+#define AVE_IIRQC		0x34c	/* Interval IRQ Control */
+
+/* Packet Filter Register Group */
+#define AVE_PKTF_BASE		0x800	/* PF Base Address */
+#define AVE_PFMBYTE_BASE	0xd00	/* PF Mask Byte Base Address */
+#define AVE_PFMBIT_BASE		0xe00	/* PF Mask Bit Base Address */
+#define AVE_PFSEL_BASE		0xf00	/* PF Selector Base Address */
+#define AVE_PFEN		0xffc	/* Packet Filter Enable */
+#define AVE_PKTF(ent)		(AVE_PKTF_BASE + (ent) * 0x40)
+#define AVE_PFMBYTE(ent)	(AVE_PFMBYTE_BASE + (ent) * 8)
+#define AVE_PFMBIT(ent)		(AVE_PFMBIT_BASE + (ent) * 4)
+#define AVE_PFSEL(ent)		(AVE_PFSEL_BASE + (ent) * 4)
+
+/* 64bit descriptor memory */
+#define AVE_DESC_SIZE_64	12	/* Descriptor Size */
+
+#define AVE_TXDM_64		0x1000	/* Tx Descriptor Memory */
+#define AVE_RXDM_64		0x1c00	/* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_64	0x0ba0	/* Tx Descriptor Memory Size 3KB */
+#define AVE_RXDM_SIZE_64	0x6000	/* Rx Descriptor Memory Size 24KB */
+
+/* 32bit descriptor memory */
+#define AVE_DESC_SIZE_32	8	/* Descriptor Size */
+
+#define AVE_TXDM_32		0x1000	/* Tx Descriptor Memory */
+#define AVE_RXDM_32		0x1800	/* Rx Descriptor Memory */
+
+#define AVE_TXDM_SIZE_32	0x07c0	/* Tx Descriptor Memory Size 2KB */
+#define AVE_RXDM_SIZE_32	0x4000	/* Rx Descriptor Memory Size 16KB */
+
+/* RMII Bridge Register Group */
+#define AVE_RSTCTRL		0x8028	/* Reset control */
+#define AVE_RSTCTRL_RMIIRST	BIT(16)
+#define AVE_LINKSEL		0x8034	/* Link speed setting */
+#define AVE_LINKSEL_100M	BIT(0)
+
+/* AVE_GRR */
+#define AVE_GRR_RXFFR		BIT(5)	/* Reset RxFIFO */
+#define AVE_GRR_PHYRST		BIT(4)	/* Reset external PHY */
+#define AVE_GRR_GRST		BIT(0)	/* Reset all MAC */
+
+/* AVE_CFGR */
+#define AVE_CFGR_FLE		BIT(31)	/* Filter Function */
+#define AVE_CFGR_CHE		BIT(30)	/* Checksum Function */
+#define AVE_CFGR_MII		BIT(27)	/* Func mode (1:MII/RMII, 0:RGMII) */
+#define AVE_CFGR_IPFCEN		BIT(24)	/* IP fragment sum Enable */
+
+/* AVE_GISR (common with GIMR) */
+#define AVE_GI_PHY		BIT(24)	/* PHY interrupt */
+#define AVE_GI_TX		BIT(16)	/* Tx complete */
+#define AVE_GI_RXERR		BIT(8)	/* Receive frame more than max size */
+#define AVE_GI_RXOVF		BIT(7)	/* Overflow at the RxFIFO */
+#define AVE_GI_RXDROP		BIT(6)	/* Drop packet */
+#define AVE_GI_RXIINT		BIT(5)	/* Interval interrupt */
+
+/* AVE_TXCR */
+#define AVE_TXCR_FLOCTR		BIT(18)	/* Flow control */
+#define AVE_TXCR_TXSPD_1G	BIT(17)
+#define AVE_TXCR_TXSPD_100	BIT(16)
+
+/* AVE_RXCR */
+#define AVE_RXCR_RXEN		BIT(30)	/* Rx enable */
+#define AVE_RXCR_FDUPEN		BIT(22)	/* Interface mode */
+#define AVE_RXCR_FLOCTR		BIT(21)	/* Flow control */
+#define AVE_RXCR_AFEN		BIT(19)	/* MAC address filter */
+#define AVE_RXCR_DRPEN		BIT(18)	/* Drop pause frame */
+#define AVE_RXCR_MPSIZ_MASK	GENMASK(10, 0)
+
+/* AVE_MDIOCTR */
+#define AVE_MDIOCTR_RREQ	BIT(3)	/* Read request */
+#define AVE_MDIOCTR_WREQ	BIT(2)	/* Write request */
+
+/* AVE_MDIOSR */
+#define AVE_MDIOSR_STS		BIT(0)	/* access status */
+
+/* AVE_DESCC */
+#define AVE_DESCC_STATUS_MASK	GENMASK(31, 16)
+#define AVE_DESCC_RD0		BIT(8)	/* Enable Rx descriptor Ring0 */
+#define AVE_DESCC_RDSTP		BIT(4)	/* Pause Rx descriptor */
+#define AVE_DESCC_TD		BIT(0)	/* Enable Tx descriptor */
+
+/* AVE_TXDC */
+#define AVE_TXDC_SIZE		GENMASK(27, 16)	/* Size of Tx descriptor */
+#define AVE_TXDC_ADDR		GENMASK(11, 0)	/* Start address */
+#define AVE_TXDC_ADDR_START	0
+
+/* AVE_RXDC0 */
+#define AVE_RXDC0_SIZE		GENMASK(30, 16)	/* Size of Rx descriptor */
+#define AVE_RXDC0_ADDR		GENMASK(14, 0)	/* Start address */
+#define AVE_RXDC0_ADDR_START	0
+
+/* AVE_IIRQC */
+#define AVE_IIRQC_EN0		BIT(27)	/* Enable interval interrupt Ring0 */
+#define AVE_IIRQC_BSCK		GENMASK(15, 0)	/* Interval count unit */
+
+/* Command status for descriptor */
+#define AVE_STS_OWN		BIT(31)	/* Descriptor ownership */
+#define AVE_STS_INTR		BIT(29)	/* Request for interrupt */
+#define AVE_STS_OK		BIT(27)	/* Normal transmit */
+/* TX */
+#define AVE_STS_NOCSUM		BIT(28)	/* No use HW checksum */
+#define AVE_STS_1ST		BIT(26)	/* Head of buffer chain */
+#define AVE_STS_LAST		BIT(25)	/* Tail of buffer chain */
+#define AVE_STS_OWC		BIT(21)	/* Out of window,Late Collision */
+#define AVE_STS_EC		BIT(20)	/* Excess collision occurred */
+#define AVE_STS_PKTLEN_TX_MASK	GENMASK(15, 0)
+/* RX */
+#define AVE_STS_CSSV		BIT(21)	/* Checksum check performed */
+#define AVE_STS_CSER		BIT(20)	/* Checksum error detected */
+#define AVE_STS_PKTLEN_RX_MASK	GENMASK(10, 0)
+
+/* Packet filter */
+#define AVE_PFMBYTE_MASK0	(GENMASK(31, 8) | GENMASK(5, 0))
+#define AVE_PFMBYTE_MASK1	GENMASK(25, 0)
+#define AVE_PFMBIT_MASK		GENMASK(15, 0)
+
+#define AVE_PF_SIZE		17	/* Number of all packet filter */
+#define AVE_PF_MULTICAST_SIZE	7	/* Number of multicast filter */
+
+#define AVE_PFNUM_FILTER	0	/* No.0 */
+#define AVE_PFNUM_UNICAST	1	/* No.1 */
+#define AVE_PFNUM_BROADCAST	2	/* No.2 */
+#define AVE_PFNUM_MULTICAST	11	/* No.11-17 */
+
+/* NETIF Message control */
+#define AVE_DEFAULT_MSG_ENABLE	(NETIF_MSG_DRV    |	\
+				 NETIF_MSG_PROBE  |	\
+				 NETIF_MSG_LINK   |	\
+				 NETIF_MSG_TIMER  |	\
+				 NETIF_MSG_IFDOWN |	\
+				 NETIF_MSG_IFUP   |	\
+				 NETIF_MSG_RX_ERR |	\
+				 NETIF_MSG_TX_ERR)
+
+/* Parameter for descriptor */
+#define AVE_NR_TXDESC		32	/* Tx descriptor */
+#define AVE_NR_RXDESC		64	/* Rx descriptor */
+
+#define AVE_DESC_OFS_CMDSTS	0
+#define AVE_DESC_OFS_ADDRL	4
+#define AVE_DESC_OFS_ADDRU	8
+
+/* Parameter for ethernet frame */
+#define AVE_MAX_ETHFRAME	1518
+
+/* Parameter for interrupt */
+#define AVE_INTM_COUNT		20
+#define AVE_FORCE_TXINTCNT	1
+
+#define IS_DESC_64BIT(p)	((p)->data->is_desc_64bit)
+
+enum desc_id {
+	AVE_DESCID_RX,
+	AVE_DESCID_TX,
+};
+
+enum desc_state {
+	AVE_DESC_RX_PERMIT,
+	AVE_DESC_RX_SUSPEND,
+	AVE_DESC_START,
+	AVE_DESC_STOP,
+};
+
+struct ave_desc {
+	struct sk_buff	*skbs;
+	dma_addr_t	skbs_dma;
+	size_t		skbs_dmalen;
+};
+
+struct ave_desc_info {
+	u32	ndesc;		/* number of descriptor */
+	u32	daddr;		/* start address of descriptor */
+	u32	proc_idx;	/* index of processing packet */
+	u32	done_idx;	/* index of processed packet */
+	struct ave_desc *desc;	/* skb info related descriptor */
+};
+
+struct ave_soc_data {
+	bool	is_desc_64bit;
+};
+
+struct ave_stats {
+	struct	u64_stats_sync	syncp;
+	u64	packets;
+	u64	bytes;
+	u64	errors;
+	u64	dropped;
+	u64	collisions;
+	u64	fifo_errors;
+};
+
+struct ave_private {
+	void __iomem            *base;
+	int                     irq;
+	int			phy_id;
+	unsigned int		desc_size;
+	u32			msg_enable;
+	struct clk		*clk;
+	struct reset_control	*rst;
+	phy_interface_t		phy_mode;
+	struct phy_device	*phydev;
+	struct mii_bus		*mdio;
+
+	/* stats */
+	struct ave_stats	stats_rx;
+	struct ave_stats	stats_tx;
+
+	/* NAPI support */
+	struct net_device	*ndev;
+	struct napi_struct	napi_rx;
+	struct napi_struct	napi_tx;
+
+	/* descriptor */
+	struct ave_desc_info	rx;
+	struct ave_desc_info	tx;
+
+	/* flow control */
+	int pause_auto;
+	int pause_rx;
+	int pause_tx;
+
+	const struct ave_soc_data *data;
+};
+
+static u32 ave_desc_read(struct net_device *ndev, enum desc_id id, int entry,
+			 int offset)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 addr;
+
+	addr = ((id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr)
+		+ entry * priv->desc_size + offset;
+
+	return readl(priv->base + addr);
+}
+
+static u32 ave_desc_read_cmdsts(struct net_device *ndev, enum desc_id id,
+				int entry)
+{
+	return ave_desc_read(ndev, id, entry, AVE_DESC_OFS_CMDSTS);
+}
+
+static void ave_desc_write(struct net_device *ndev, enum desc_id id,
+			   int entry, int offset, u32 val)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 addr;
+
+	addr = ((id == AVE_DESCID_TX) ? priv->tx.daddr : priv->rx.daddr)
+		+ entry * priv->desc_size + offset;
+
+	writel(val, priv->base + addr);
+}
+
+static void ave_desc_write_cmdsts(struct net_device *ndev, enum desc_id id,
+				  int entry, u32 val)
+{
+	ave_desc_write(ndev, id, entry, AVE_DESC_OFS_CMDSTS, val);
+}
+
+static void ave_desc_write_addr(struct net_device *ndev, enum desc_id id,
+				int entry, dma_addr_t paddr)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	ave_desc_write(ndev, id, entry, AVE_DESC_OFS_ADDRL,
+		       lower_32_bits(paddr));
+	if (IS_DESC_64BIT(priv))
+		ave_desc_write(ndev, id,
+			       entry, AVE_DESC_OFS_ADDRU,
+			       upper_32_bits(paddr));
+}
+
+static u32 ave_irq_disable_all(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 ret;
+
+	ret = readl(priv->base + AVE_GIMR);
+	writel(0, priv->base + AVE_GIMR);
+
+	return ret;
+}
+
+static void ave_irq_restore(struct net_device *ndev, u32 val)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	writel(val, priv->base + AVE_GIMR);
+}
+
+static void ave_irq_enable(struct net_device *ndev, u32 bitflag)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	writel(readl(priv->base + AVE_GIMR) | bitflag, priv->base + AVE_GIMR);
+	writel(bitflag, priv->base + AVE_GISR);
+}
+
+static void ave_hw_write_macaddr(struct net_device *ndev,
+				 const unsigned char *mac_addr,
+				 int reg1, int reg2)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	writel(mac_addr[0] | mac_addr[1] << 8 |
+	       mac_addr[2] << 16 | mac_addr[3] << 24, priv->base + reg1);
+	writel(mac_addr[4] | mac_addr[5] << 8, priv->base + reg2);
+}
+
+static void ave_hw_read_version(struct net_device *ndev, char *buf, int len)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 major, minor, vr;
+
+	vr = readl(priv->base + AVE_VR);
+	major = (vr & GENMASK(15, 8)) >> 8;
+	minor = (vr & GENMASK(7, 0));
+	snprintf(buf, len, "v%u.%u", major, minor);
+}
+
+static void ave_ethtool_get_drvinfo(struct net_device *ndev,
+				    struct ethtool_drvinfo *info)
+{
+	struct device *dev = ndev->dev.parent;
+
+	strlcpy(info->driver, dev->driver->name, sizeof(info->driver));
+	strlcpy(info->bus_info, dev_name(dev), sizeof(info->bus_info));
+	ave_hw_read_version(ndev, info->fw_version, sizeof(info->fw_version));
+}
+
+static u32 ave_ethtool_get_msglevel(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	return priv->msg_enable;
+}
+
+static void ave_ethtool_set_msglevel(struct net_device *ndev, u32 val)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	priv->msg_enable = val;
+}
+
+static void ave_ethtool_get_wol(struct net_device *ndev,
+				struct ethtool_wolinfo *wol)
+{
+	wol->supported = 0;
+	wol->wolopts   = 0;
+
+	if (ndev->phydev)
+		phy_ethtool_get_wol(ndev->phydev, wol);
+}
+
+static int ave_ethtool_set_wol(struct net_device *ndev,
+			       struct ethtool_wolinfo *wol)
+{
+	int ret;
+
+	if (!ndev->phydev ||
+	    (wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE)))
+		return -EOPNOTSUPP;
+
+	ret = phy_ethtool_set_wol(ndev->phydev, wol);
+	if (!ret)
+		device_set_wakeup_enable(&ndev->dev, !!wol->wolopts);
+
+	return ret;
+}
+
+static void ave_ethtool_get_pauseparam(struct net_device *ndev,
+				       struct ethtool_pauseparam *pause)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	pause->autoneg  = priv->pause_auto;
+	pause->rx_pause = priv->pause_rx;
+	pause->tx_pause = priv->pause_tx;
+}
+
+static int ave_ethtool_set_pauseparam(struct net_device *ndev,
+				      struct ethtool_pauseparam *pause)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+
+	if (!phydev)
+		return -EINVAL;
+
+	priv->pause_auto = pause->autoneg;
+	priv->pause_rx   = pause->rx_pause;
+	priv->pause_tx   = pause->tx_pause;
+
+	phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
+	if (pause->rx_pause)
+		phydev->advertising |= ADVERTISED_Pause | ADVERTISED_Asym_Pause;
+	if (pause->tx_pause)
+		phydev->advertising ^= ADVERTISED_Asym_Pause;
+
+	if (pause->autoneg) {
+		if (netif_running(ndev))
+			phy_start_aneg(phydev);
+	}
+
+	return 0;
+}
+
+static const struct ethtool_ops ave_ethtool_ops = {
+	.get_link_ksettings	= phy_ethtool_get_link_ksettings,
+	.set_link_ksettings	= phy_ethtool_set_link_ksettings,
+	.get_drvinfo		= ave_ethtool_get_drvinfo,
+	.nway_reset		= phy_ethtool_nway_reset,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= ave_ethtool_get_msglevel,
+	.set_msglevel		= ave_ethtool_set_msglevel,
+	.get_wol		= ave_ethtool_get_wol,
+	.set_wol		= ave_ethtool_set_wol,
+	.get_pauseparam         = ave_ethtool_get_pauseparam,
+	.set_pauseparam         = ave_ethtool_set_pauseparam,
+};
+
+static int ave_mdiobus_read(struct mii_bus *bus, int phyid, int regnum)
+{
+	struct net_device *ndev = bus->priv;
+	struct ave_private *priv;
+	u32 mdioctl, mdiosr;
+	int ret;
+
+	priv = netdev_priv(ndev);
+
+	/* write address */
+	writel((phyid << 8) | regnum, priv->base + AVE_MDIOAR);
+
+	/* read request */
+	mdioctl = readl(priv->base + AVE_MDIOCTR);
+	writel((mdioctl | AVE_MDIOCTR_RREQ) & ~AVE_MDIOCTR_WREQ,
+	       priv->base + AVE_MDIOCTR);
+
+	ret = readl_poll_timeout(priv->base + AVE_MDIOSR, mdiosr,
+				 !(mdiosr & AVE_MDIOSR_STS), 20, 2000);
+	if (ret) {
+		netdev_err(ndev, "failed to read (phy:%d reg:%x)\n",
+			   phyid, regnum);
+		return ret;
+	}
+
+	return readl(priv->base + AVE_MDIORDR) & GENMASK(15, 0);
+}
+
+static int ave_mdiobus_write(struct mii_bus *bus, int phyid, int regnum,
+			     u16 val)
+{
+	struct net_device *ndev = bus->priv;
+	struct ave_private *priv;
+	u32 mdioctl, mdiosr;
+	int ret;
+
+	priv = netdev_priv(ndev);
+
+	/* write address */
+	writel((phyid << 8) | regnum, priv->base + AVE_MDIOAR);
+
+	/* write data */
+	writel(val, priv->base + AVE_MDIOWDR);
+
+	/* write request */
+	mdioctl = readl(priv->base + AVE_MDIOCTR);
+	writel((mdioctl | AVE_MDIOCTR_WREQ) & ~AVE_MDIOCTR_RREQ,
+	       priv->base + AVE_MDIOCTR);
+
+	ret = readl_poll_timeout(priv->base + AVE_MDIOSR, mdiosr,
+				 !(mdiosr & AVE_MDIOSR_STS), 20, 2000);
+	if (ret)
+		netdev_err(ndev, "failed to write (phy:%d reg:%x)\n",
+			   phyid, regnum);
+
+	return ret;
+}
+
+static int ave_dma_map(struct net_device *ndev, struct ave_desc *desc,
+		       void *ptr, size_t len, enum dma_data_direction dir,
+		       dma_addr_t *paddr)
+{
+	dma_addr_t map_addr;
+
+	map_addr = dma_map_single(ndev->dev.parent, ptr, len, dir);
+	if (unlikely(dma_mapping_error(ndev->dev.parent, map_addr)))
+		return -ENOMEM;
+
+	desc->skbs_dma = map_addr;
+	desc->skbs_dmalen = len;
+	*paddr = map_addr;
+
+	return 0;
+}
+
+static void ave_dma_unmap(struct net_device *ndev, struct ave_desc *desc,
+			  enum dma_data_direction dir)
+{
+	if (!desc->skbs_dma)
+		return;
+
+	dma_unmap_single(ndev->dev.parent,
+			 desc->skbs_dma, desc->skbs_dmalen, dir);
+	desc->skbs_dma = 0;
+}
+
+/* Prepare Rx descriptor and memory */
+static int ave_rxdesc_prepare(struct net_device *ndev, int entry)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	struct sk_buff *skb;
+	dma_addr_t paddr;
+	int ret;
+
+	skb = priv->rx.desc[entry].skbs;
+	if (!skb) {
+		skb = netdev_alloc_skb_ip_align(ndev,
+						AVE_MAX_ETHFRAME);
+		if (!skb) {
+			netdev_err(ndev, "can't allocate skb for Rx\n");
+			return -ENOMEM;
+		}
+	}
+
+	/* set disable to cmdsts */
+	ave_desc_write_cmdsts(ndev, AVE_DESCID_RX, entry,
+			      AVE_STS_INTR | AVE_STS_OWN);
+
+	/* map Rx buffer
+	 * Rx buffer set to the Rx descriptor has two restrictions:
+	 * - Rx buffer address is 4 byte aligned.
+	 * - Rx buffer begins with 2 byte headroom, and data will be put from
+	 *   (buffer + 2).
+	 * To satisfy this, specify the address to put back the buffer
+	 * pointer advanced by NET_IP_ALIGN by netdev_alloc_skb_ip_align(),
+	 * and expand the map size by NET_IP_ALIGN.
+	 */
+	ret = ave_dma_map(ndev, &priv->rx.desc[entry],
+			  skb->data - NET_IP_ALIGN,
+			  AVE_MAX_ETHFRAME + NET_IP_ALIGN,
+			  DMA_FROM_DEVICE, &paddr);
+	if (ret) {
+		netdev_err(ndev, "can't map skb for Rx\n");
+		dev_kfree_skb_any(skb);
+		return ret;
+	}
+	priv->rx.desc[entry].skbs = skb;
+
+	/* set buffer pointer */
+	ave_desc_write_addr(ndev, AVE_DESCID_RX, entry, paddr);
+
+	/* set enable to cmdsts */
+	ave_desc_write_cmdsts(ndev, AVE_DESCID_RX, entry,
+			      AVE_STS_INTR | AVE_MAX_ETHFRAME);
+
+	return ret;
+}
+
+/* Switch state of descriptor */
+static int ave_desc_switch(struct net_device *ndev, enum desc_state state)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	int ret = 0;
+	u32 val;
+
+	switch (state) {
+	case AVE_DESC_START:
+		writel(AVE_DESCC_TD | AVE_DESCC_RD0, priv->base + AVE_DESCC);
+		break;
+
+	case AVE_DESC_STOP:
+		writel(0, priv->base + AVE_DESCC);
+		if (readl_poll_timeout(priv->base + AVE_DESCC, val, !val,
+				       150, 15000)) {
+			netdev_err(ndev, "can't stop descriptor\n");
+			ret = -EBUSY;
+		}
+		break;
+
+	case AVE_DESC_RX_SUSPEND:
+		val = readl(priv->base + AVE_DESCC);
+		val |= AVE_DESCC_RDSTP;
+		val &= ~AVE_DESCC_STATUS_MASK;
+		writel(val, priv->base + AVE_DESCC);
+		if (readl_poll_timeout(priv->base + AVE_DESCC, val,
+				       val & (AVE_DESCC_RDSTP << 16),
+				       150, 150000)) {
+			netdev_err(ndev, "can't suspend descriptor\n");
+			ret = -EBUSY;
+		}
+		break;
+
+	case AVE_DESC_RX_PERMIT:
+		val = readl(priv->base + AVE_DESCC);
+		val &= ~AVE_DESCC_RDSTP;
+		val &= ~AVE_DESCC_STATUS_MASK;
+		writel(val, priv->base + AVE_DESCC);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int ave_tx_complete(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 proc_idx, done_idx, ndesc, cmdsts;
+	unsigned int nr_freebuf = 0;
+	unsigned int tx_packets = 0;
+	unsigned int tx_bytes = 0;
+
+	proc_idx = priv->tx.proc_idx;
+	done_idx = priv->tx.done_idx;
+	ndesc    = priv->tx.ndesc;
+
+	/* free pre-stored skb from done_idx to proc_idx */
+	while (proc_idx != done_idx) {
+		cmdsts = ave_desc_read_cmdsts(ndev, AVE_DESCID_TX, done_idx);
+
+		/* do nothing if owner is HW (==1 for Tx) */
+		if (cmdsts & AVE_STS_OWN)
+			break;
+
+		/* check Tx status and updates statistics */
+		if (cmdsts & AVE_STS_OK) {
+			tx_bytes += cmdsts & AVE_STS_PKTLEN_TX_MASK;
+			/* success */
+			if (cmdsts & AVE_STS_LAST)
+				tx_packets++;
+		} else {
+			/* error */
+			if (cmdsts & AVE_STS_LAST) {
+				priv->stats_tx.errors++;
+				if (cmdsts & (AVE_STS_OWC | AVE_STS_EC))
+					priv->stats_tx.collisions++;
+			}
+		}
+
+		/* release skb */
+		if (priv->tx.desc[done_idx].skbs) {
+			ave_dma_unmap(ndev, &priv->tx.desc[done_idx],
+				      DMA_TO_DEVICE);
+			dev_consume_skb_any(priv->tx.desc[done_idx].skbs);
+			priv->tx.desc[done_idx].skbs = NULL;
+			nr_freebuf++;
+		}
+		done_idx = (done_idx + 1) % ndesc;
+	}
+
+	priv->tx.done_idx = done_idx;
+
+	/* update stats */
+	u64_stats_update_begin(&priv->stats_tx.syncp);
+	priv->stats_tx.packets += tx_packets;
+	priv->stats_tx.bytes   += tx_bytes;
+	u64_stats_update_end(&priv->stats_tx.syncp);
+
+	/* wake queue for freeing buffer */
+	if (unlikely(netif_queue_stopped(ndev)) && nr_freebuf)
+		netif_wake_queue(ndev);
+
+	return nr_freebuf;
+}
+
+static int ave_rx_receive(struct net_device *ndev, int num)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	unsigned int rx_packets = 0;
+	unsigned int rx_bytes = 0;
+	u32 proc_idx, done_idx;
+	struct sk_buff *skb;
+	unsigned int pktlen;
+	int restpkt, npkts;
+	u32 ndesc, cmdsts;
+
+	proc_idx = priv->rx.proc_idx;
+	done_idx = priv->rx.done_idx;
+	ndesc    = priv->rx.ndesc;
+	restpkt  = ((proc_idx + ndesc - 1) - done_idx) % ndesc;
+
+	for (npkts = 0; npkts < num; npkts++) {
+		/* we can't receive more packet, so fill desc quickly */
+		if (--restpkt < 0)
+			break;
+
+		cmdsts = ave_desc_read_cmdsts(ndev, AVE_DESCID_RX, proc_idx);
+
+		/* do nothing if owner is HW (==0 for Rx) */
+		if (!(cmdsts & AVE_STS_OWN))
+			break;
+
+		if (!(cmdsts & AVE_STS_OK)) {
+			priv->stats_rx.errors++;
+			proc_idx = (proc_idx + 1) % ndesc;
+			continue;
+		}
+
+		pktlen = cmdsts & AVE_STS_PKTLEN_RX_MASK;
+
+		/* get skbuff for rx */
+		skb = priv->rx.desc[proc_idx].skbs;
+		priv->rx.desc[proc_idx].skbs = NULL;
+
+		ave_dma_unmap(ndev, &priv->rx.desc[proc_idx], DMA_FROM_DEVICE);
+
+		skb->dev = ndev;
+		skb_put(skb, pktlen);
+		skb->protocol = eth_type_trans(skb, ndev);
+
+		if ((cmdsts & AVE_STS_CSSV) && (!(cmdsts & AVE_STS_CSER)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+		rx_packets++;
+		rx_bytes += pktlen;
+
+		netif_receive_skb(skb);
+
+		proc_idx = (proc_idx + 1) % ndesc;
+	}
+
+	priv->rx.proc_idx = proc_idx;
+
+	/* update stats */
+	u64_stats_update_begin(&priv->stats_rx.syncp);
+	priv->stats_rx.packets += rx_packets;
+	priv->stats_rx.bytes   += rx_bytes;
+	u64_stats_update_end(&priv->stats_rx.syncp);
+
+	/* refill the Rx buffers */
+	while (proc_idx != done_idx) {
+		if (ave_rxdesc_prepare(ndev, done_idx))
+			break;
+		done_idx = (done_idx + 1) % ndesc;
+	}
+
+	priv->rx.done_idx = done_idx;
+
+	return npkts;
+}
+
+static int ave_napi_poll_rx(struct napi_struct *napi, int budget)
+{
+	struct ave_private *priv;
+	struct net_device *ndev;
+	int num;
+
+	priv = container_of(napi, struct ave_private, napi_rx);
+	ndev = priv->ndev;
+
+	num = ave_rx_receive(ndev, budget);
+	if (num < budget) {
+		napi_complete_done(napi, num);
+
+		/* enable Rx interrupt when NAPI finishes */
+		ave_irq_enable(ndev, AVE_GI_RXIINT);
+	}
+
+	return num;
+}
+
+static int ave_napi_poll_tx(struct napi_struct *napi, int budget)
+{
+	struct ave_private *priv;
+	struct net_device *ndev;
+	int num;
+
+	priv = container_of(napi, struct ave_private, napi_tx);
+	ndev = priv->ndev;
+
+	num = ave_tx_complete(ndev);
+	napi_complete(napi);
+
+	/* enable Tx interrupt when NAPI finishes */
+	ave_irq_enable(ndev, AVE_GI_TX);
+
+	return num;
+}
+
+static void ave_global_reset(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 val;
+
+	/* set config register */
+	val = AVE_CFGR_FLE | AVE_CFGR_IPFCEN | AVE_CFGR_CHE;
+	if (!phy_interface_mode_is_rgmii(priv->phy_mode))
+		val |= AVE_CFGR_MII;
+	writel(val, priv->base + AVE_CFGR);
+
+	/* reset RMII register */
+	val = readl(priv->base + AVE_RSTCTRL);
+	val &= ~AVE_RSTCTRL_RMIIRST;
+	writel(val, priv->base + AVE_RSTCTRL);
+
+	/* assert reset */
+	writel(AVE_GRR_GRST | AVE_GRR_PHYRST, priv->base + AVE_GRR);
+	msleep(20);
+
+	/* 1st, negate PHY reset only */
+	writel(AVE_GRR_GRST, priv->base + AVE_GRR);
+	msleep(40);
+
+	/* negate reset */
+	writel(0, priv->base + AVE_GRR);
+	msleep(40);
+
+	/* negate RMII register */
+	val = readl(priv->base + AVE_RSTCTRL);
+	val |= AVE_RSTCTRL_RMIIRST;
+	writel(val, priv->base + AVE_RSTCTRL);
+
+	ave_irq_disable_all(ndev);
+}
+
+static void ave_rxfifo_reset(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 rxcr_org;
+
+	/* save and disable MAC receive op */
+	rxcr_org = readl(priv->base + AVE_RXCR);
+	writel(rxcr_org & (~AVE_RXCR_RXEN), priv->base + AVE_RXCR);
+
+	/* suspend Rx descriptor */
+	ave_desc_switch(ndev, AVE_DESC_RX_SUSPEND);
+
+	/* receive all packets before descriptor starts */
+	ave_rx_receive(ndev, priv->rx.ndesc);
+
+	/* assert reset */
+	writel(AVE_GRR_RXFFR, priv->base + AVE_GRR);
+	usleep_range(40, 50);
+
+	/* negate reset */
+	writel(0, priv->base + AVE_GRR);
+	usleep_range(10, 20);
+
+	/* negate interrupt status */
+	writel(AVE_GI_RXOVF, priv->base + AVE_GISR);
+
+	/* permit descriptor */
+	ave_desc_switch(ndev, AVE_DESC_RX_PERMIT);
+
+	/* restore MAC reccieve op */
+	writel(rxcr_org, priv->base + AVE_RXCR);
+}
+
+static irqreturn_t ave_irq_handler(int irq, void *netdev)
+{
+	struct net_device *ndev = (struct net_device *)netdev;
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 gimr_val, gisr_val;
+
+	gimr_val = ave_irq_disable_all(ndev);
+
+	/* get interrupt status */
+	gisr_val = readl(priv->base + AVE_GISR);
+
+	/* PHY */
+	if (gisr_val & AVE_GI_PHY)
+		writel(AVE_GI_PHY, priv->base + AVE_GISR);
+
+	/* check exceeding packet */
+	if (gisr_val & AVE_GI_RXERR) {
+		writel(AVE_GI_RXERR, priv->base + AVE_GISR);
+		netdev_err(ndev, "receive a packet exceeding frame buffer\n");
+	}
+
+	gisr_val &= gimr_val;
+	if (!gisr_val)
+		goto exit_isr;
+
+	/* RxFIFO overflow */
+	if (gisr_val & AVE_GI_RXOVF) {
+		priv->stats_rx.fifo_errors++;
+		ave_rxfifo_reset(ndev);
+		goto exit_isr;
+	}
+
+	/* Rx drop */
+	if (gisr_val & AVE_GI_RXDROP) {
+		priv->stats_rx.dropped++;
+		writel(AVE_GI_RXDROP, priv->base + AVE_GISR);
+	}
+
+	/* Rx interval */
+	if (gisr_val & AVE_GI_RXIINT) {
+		napi_schedule(&priv->napi_rx);
+		/* still force to disable Rx interrupt until NAPI finishes */
+		gimr_val &= ~AVE_GI_RXIINT;
+	}
+
+	/* Tx completed */
+	if (gisr_val & AVE_GI_TX) {
+		napi_schedule(&priv->napi_tx);
+		/* still force to disable Tx interrupt until NAPI finishes */
+		gimr_val &= ~AVE_GI_TX;
+	}
+
+exit_isr:
+	ave_irq_restore(ndev, gimr_val);
+
+	return IRQ_HANDLED;
+}
+
+static int ave_pfsel_start(struct net_device *ndev, unsigned int entry)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 val;
+
+	if (WARN_ON(entry > AVE_PF_SIZE))
+		return -EINVAL;
+
+	val = readl(priv->base + AVE_PFEN);
+	writel(val | BIT(entry), priv->base + AVE_PFEN);
+
+	return 0;
+}
+
+static int ave_pfsel_stop(struct net_device *ndev, unsigned int entry)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 val;
+
+	if (WARN_ON(entry > AVE_PF_SIZE))
+		return -EINVAL;
+
+	val = readl(priv->base + AVE_PFEN);
+	writel(val & ~BIT(entry), priv->base + AVE_PFEN);
+
+	return 0;
+}
+
+static int ave_pfsel_set_macaddr(struct net_device *ndev,
+				 unsigned int entry,
+				 const unsigned char *mac_addr,
+				 unsigned int set_size)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	if (WARN_ON(entry > AVE_PF_SIZE))
+		return -EINVAL;
+	if (WARN_ON(set_size > 6))
+		return -EINVAL;
+
+	ave_pfsel_stop(ndev, entry);
+
+	/* set MAC address for the filter */
+	ave_hw_write_macaddr(ndev, mac_addr,
+			     AVE_PKTF(entry), AVE_PKTF(entry) + 4);
+
+	/* set byte mask */
+	writel(GENMASK(31, set_size) & AVE_PFMBYTE_MASK0,
+	       priv->base + AVE_PFMBYTE(entry));
+	writel(AVE_PFMBYTE_MASK1, priv->base + AVE_PFMBYTE(entry) + 4);
+
+	/* set bit mask filter */
+	writel(AVE_PFMBIT_MASK, priv->base + AVE_PFMBIT(entry));
+
+	/* set selector to ring 0 */
+	writel(0, priv->base + AVE_PFSEL(entry));
+
+	/* restart filter */
+	ave_pfsel_start(ndev, entry);
+
+	return 0;
+}
+
+static void ave_pfsel_set_promisc(struct net_device *ndev,
+				  unsigned int entry, u32 rxring)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	if (WARN_ON(entry > AVE_PF_SIZE))
+		return;
+
+	ave_pfsel_stop(ndev, entry);
+
+	/* set byte mask */
+	writel(AVE_PFMBYTE_MASK0, priv->base + AVE_PFMBYTE(entry));
+	writel(AVE_PFMBYTE_MASK1, priv->base + AVE_PFMBYTE(entry) + 4);
+
+	/* set bit mask filter */
+	writel(AVE_PFMBIT_MASK, priv->base + AVE_PFMBIT(entry));
+
+	/* set selector to rxring */
+	writel(rxring, priv->base + AVE_PFSEL(entry));
+
+	ave_pfsel_start(ndev, entry);
+}
+
+static void ave_pfsel_init(struct net_device *ndev)
+{
+	unsigned char bcast_mac[ETH_ALEN];
+	int i;
+
+	eth_broadcast_addr(bcast_mac);
+
+	for (i = 0; i < AVE_PF_SIZE; i++)
+		ave_pfsel_stop(ndev, i);
+
+	/* promiscious entry, select ring 0 */
+	ave_pfsel_set_promisc(ndev, AVE_PFNUM_FILTER, 0);
+
+	/* unicast entry */
+	ave_pfsel_set_macaddr(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+
+	/* broadcast entry */
+	ave_pfsel_set_macaddr(ndev, AVE_PFNUM_BROADCAST, bcast_mac, 6);
+}
+
+static void ave_phy_adjust_link(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	struct phy_device *phydev = ndev->phydev;
+	u32 val, txcr, rxcr, rxcr_org;
+	u16 rmt_adv = 0, lcl_adv = 0;
+	u8 cap;
+
+	/* set RGMII speed */
+	val = readl(priv->base + AVE_TXCR);
+	val &= ~(AVE_TXCR_TXSPD_100 | AVE_TXCR_TXSPD_1G);
+
+	if (phy_interface_is_rgmii(phydev) && phydev->speed == SPEED_1000)
+		val |= AVE_TXCR_TXSPD_1G;
+	else if (phydev->speed == SPEED_100)
+		val |= AVE_TXCR_TXSPD_100;
+
+	writel(val, priv->base + AVE_TXCR);
+
+	/* set RMII speed (100M/10M only) */
+	if (!phy_interface_is_rgmii(phydev)) {
+		val = readl(priv->base + AVE_LINKSEL);
+		if (phydev->speed == SPEED_10)
+			val &= ~AVE_LINKSEL_100M;
+		else
+			val |= AVE_LINKSEL_100M;
+		writel(val, priv->base + AVE_LINKSEL);
+	}
+
+	/* check current RXCR/TXCR */
+	rxcr = readl(priv->base + AVE_RXCR);
+	txcr = readl(priv->base + AVE_TXCR);
+	rxcr_org = rxcr;
+
+	if (phydev->duplex) {
+		rxcr |= AVE_RXCR_FDUPEN;
+
+		if (phydev->pause)
+			rmt_adv |= LPA_PAUSE_CAP;
+		if (phydev->asym_pause)
+			rmt_adv |= LPA_PAUSE_ASYM;
+		if (phydev->advertising & ADVERTISED_Pause)
+			lcl_adv |= ADVERTISE_PAUSE_CAP;
+		if (phydev->advertising & ADVERTISED_Asym_Pause)
+			lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+		cap = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+		if (cap & FLOW_CTRL_TX)
+			txcr |= AVE_TXCR_FLOCTR;
+		else
+			txcr &= ~AVE_TXCR_FLOCTR;
+		if (cap & FLOW_CTRL_RX)
+			rxcr |= AVE_RXCR_FLOCTR;
+		else
+			rxcr &= ~AVE_RXCR_FLOCTR;
+	} else {
+		rxcr &= ~AVE_RXCR_FDUPEN;
+		rxcr &= ~AVE_RXCR_FLOCTR;
+		txcr &= ~AVE_TXCR_FLOCTR;
+	}
+
+	if (rxcr_org != rxcr) {
+		/* disable Rx mac */
+		writel(rxcr & ~AVE_RXCR_RXEN, priv->base + AVE_RXCR);
+		/* change and enable TX/Rx mac */
+		writel(txcr, priv->base + AVE_TXCR);
+		writel(rxcr, priv->base + AVE_RXCR);
+	}
+
+	phy_print_status(phydev);
+}
+
+static void ave_macaddr_init(struct net_device *ndev)
+{
+	ave_hw_write_macaddr(ndev, ndev->dev_addr, AVE_RXMAC1R, AVE_RXMAC2R);
+
+	/* pfsel unicast entry */
+	ave_pfsel_set_macaddr(ndev, AVE_PFNUM_UNICAST, ndev->dev_addr, 6);
+}
+
+static int ave_init(struct net_device *ndev)
+{
+	struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
+	struct ave_private *priv = netdev_priv(ndev);
+	struct device *dev = ndev->dev.parent;
+	struct device_node *np = dev->of_node;
+	struct device_node *mdio_np;
+	struct phy_device *phydev;
+	int ret;
+
+	/* enable clk because of hw access until ndo_open */
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "can't enable clock\n");
+		return ret;
+	}
+	ret = reset_control_deassert(priv->rst);
+	if (ret) {
+		dev_err(dev, "can't deassert reset\n");
+		goto out_clk_disable;
+	}
+
+	ave_global_reset(ndev);
+
+	mdio_np = of_get_child_by_name(np, "mdio");
+	if (!mdio_np) {
+		dev_err(dev, "mdio node not found\n");
+		ret = -EINVAL;
+		goto out_reset_assert;
+	}
+	ret = of_mdiobus_register(priv->mdio, mdio_np);
+	of_node_put(mdio_np);
+	if (ret) {
+		dev_err(dev, "failed to register mdiobus\n");
+		goto out_reset_assert;
+	}
+
+	phydev = of_phy_get_and_connect(ndev, np, ave_phy_adjust_link);
+	if (!phydev) {
+		dev_err(dev, "could not attach to PHY\n");
+		ret = -ENODEV;
+		goto out_mdio_unregister;
+	}
+
+	priv->phydev = phydev;
+
+	phy_ethtool_get_wol(phydev, &wol);
+	device_set_wakeup_capable(&ndev->dev, !!wol.supported);
+
+	if (!phy_interface_is_rgmii(phydev)) {
+		phydev->supported &= ~PHY_GBIT_FEATURES;
+		phydev->supported |= PHY_BASIC_FEATURES;
+	}
+	phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+
+	phy_attached_info(phydev);
+
+	return 0;
+
+out_mdio_unregister:
+	mdiobus_unregister(priv->mdio);
+out_reset_assert:
+	reset_control_assert(priv->rst);
+out_clk_disable:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+static void ave_uninit(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+
+	phy_disconnect(priv->phydev);
+	mdiobus_unregister(priv->mdio);
+
+	/* disable clk because of hw access after ndo_stop */
+	reset_control_assert(priv->rst);
+	clk_disable_unprepare(priv->clk);
+}
+
+static int ave_open(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	int entry;
+	int ret;
+	u32 val;
+
+	ret = request_irq(priv->irq, ave_irq_handler, IRQF_SHARED, ndev->name,
+			  ndev);
+	if (ret)
+		return ret;
+
+	priv->tx.desc = kcalloc(priv->tx.ndesc, sizeof(*priv->tx.desc),
+				GFP_KERNEL);
+	if (!priv->tx.desc) {
+		ret = -ENOMEM;
+		goto out_free_irq;
+	}
+
+	priv->rx.desc = kcalloc(priv->rx.ndesc, sizeof(*priv->rx.desc),
+				GFP_KERNEL);
+	if (!priv->rx.desc) {
+		kfree(priv->tx.desc);
+		ret = -ENOMEM;
+		goto out_free_irq;
+	}
+
+	/* initialize Tx work and descriptor */
+	priv->tx.proc_idx = 0;
+	priv->tx.done_idx = 0;
+	for (entry = 0; entry < priv->tx.ndesc; entry++) {
+		ave_desc_write_cmdsts(ndev, AVE_DESCID_TX, entry, 0);
+		ave_desc_write_addr(ndev, AVE_DESCID_TX, entry, 0);
+	}
+	writel(AVE_TXDC_ADDR_START
+		| (((priv->tx.ndesc * priv->desc_size) << 16) & AVE_TXDC_SIZE),
+		priv->base + AVE_TXDC);
+
+	/* initialize Rx work and descriptor */
+	priv->rx.proc_idx = 0;
+	priv->rx.done_idx = 0;
+	for (entry = 0; entry < priv->rx.ndesc; entry++) {
+		if (ave_rxdesc_prepare(ndev, entry))
+			break;
+	}
+	writel(AVE_RXDC0_ADDR_START
+	       | (((priv->rx.ndesc * priv->desc_size) << 16) & AVE_RXDC0_SIZE),
+	       priv->base + AVE_RXDC0);
+
+	ave_desc_switch(ndev, AVE_DESC_START);
+
+	ave_pfsel_init(ndev);
+	ave_macaddr_init(ndev);
+
+	/* set Rx configuration */
+	/* full duplex, enable pause drop, enalbe flow control */
+	val = AVE_RXCR_RXEN | AVE_RXCR_FDUPEN | AVE_RXCR_DRPEN |
+		AVE_RXCR_FLOCTR | (AVE_MAX_ETHFRAME & AVE_RXCR_MPSIZ_MASK);
+	writel(val, priv->base + AVE_RXCR);
+
+	/* set Tx configuration */
+	/* enable flow control, disable loopback */
+	writel(AVE_TXCR_FLOCTR, priv->base + AVE_TXCR);
+
+	/* enable timer, clear EN,INTM, and mask interval unit(BSCK) */
+	val = readl(priv->base + AVE_IIRQC) & AVE_IIRQC_BSCK;
+	val |= AVE_IIRQC_EN0 | (AVE_INTM_COUNT << 16);
+	writel(val, priv->base + AVE_IIRQC);
+
+	val = AVE_GI_RXIINT | AVE_GI_RXOVF | AVE_GI_TX;
+	ave_irq_restore(ndev, val);
+
+	napi_enable(&priv->napi_rx);
+	napi_enable(&priv->napi_tx);
+
+	phy_start(ndev->phydev);
+	phy_start_aneg(ndev->phydev);
+	netif_start_queue(ndev);
+
+	return 0;
+
+out_free_irq:
+	disable_irq(priv->irq);
+	free_irq(priv->irq, ndev);
+
+	return ret;
+}
+
+static int ave_stop(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	int entry;
+
+	ave_irq_disable_all(ndev);
+	disable_irq(priv->irq);
+	free_irq(priv->irq, ndev);
+
+	netif_tx_disable(ndev);
+	phy_stop(ndev->phydev);
+	napi_disable(&priv->napi_tx);
+	napi_disable(&priv->napi_rx);
+
+	ave_desc_switch(ndev, AVE_DESC_STOP);
+
+	/* free Tx buffer */
+	for (entry = 0; entry < priv->tx.ndesc; entry++) {
+		if (!priv->tx.desc[entry].skbs)
+			continue;
+
+		ave_dma_unmap(ndev, &priv->tx.desc[entry], DMA_TO_DEVICE);
+		dev_kfree_skb_any(priv->tx.desc[entry].skbs);
+		priv->tx.desc[entry].skbs = NULL;
+	}
+	priv->tx.proc_idx = 0;
+	priv->tx.done_idx = 0;
+
+	/* free Rx buffer */
+	for (entry = 0; entry < priv->rx.ndesc; entry++) {
+		if (!priv->rx.desc[entry].skbs)
+			continue;
+
+		ave_dma_unmap(ndev, &priv->rx.desc[entry], DMA_FROM_DEVICE);
+		dev_kfree_skb_any(priv->rx.desc[entry].skbs);
+		priv->rx.desc[entry].skbs = NULL;
+	}
+	priv->rx.proc_idx = 0;
+	priv->rx.done_idx = 0;
+
+	kfree(priv->tx.desc);
+	kfree(priv->rx.desc);
+
+	return 0;
+}
+
+static int ave_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	u32 proc_idx, done_idx, ndesc, cmdsts;
+	int ret, freepkt;
+	dma_addr_t paddr;
+
+	proc_idx = priv->tx.proc_idx;
+	done_idx = priv->tx.done_idx;
+	ndesc = priv->tx.ndesc;
+	freepkt = ((done_idx + ndesc - 1) - proc_idx) % ndesc;
+
+	/* stop queue when not enough entry */
+	if (unlikely(freepkt < 1)) {
+		netif_stop_queue(ndev);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* add padding for short packet */
+	if (skb_put_padto(skb, ETH_ZLEN)) {
+		priv->stats_tx.dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	/* map Tx buffer
+	 * Tx buffer set to the Tx descriptor doesn't have any restriction.
+	 */
+	ret = ave_dma_map(ndev, &priv->tx.desc[proc_idx],
+			  skb->data, skb->len, DMA_TO_DEVICE, &paddr);
+	if (ret) {
+		dev_kfree_skb_any(skb);
+		priv->stats_tx.dropped++;
+		return NETDEV_TX_OK;
+	}
+
+	priv->tx.desc[proc_idx].skbs = skb;
+
+	ave_desc_write_addr(ndev, AVE_DESCID_TX, proc_idx, paddr);
+
+	cmdsts = AVE_STS_OWN | AVE_STS_1ST | AVE_STS_LAST
+		| (skb->len & AVE_STS_PKTLEN_TX_MASK);
+
+	/* set interrupt per AVE_FORCE_TXINTCNT or when queue is stopped */
+	if (!(proc_idx % AVE_FORCE_TXINTCNT) || netif_queue_stopped(ndev))
+		cmdsts |= AVE_STS_INTR;
+
+	/* disable checksum calculation when skb doesn't calurate checksum */
+	if (skb->ip_summed == CHECKSUM_NONE ||
+	    skb->ip_summed == CHECKSUM_UNNECESSARY)
+		cmdsts |= AVE_STS_NOCSUM;
+
+	ave_desc_write_cmdsts(ndev, AVE_DESCID_TX, proc_idx, cmdsts);
+
+	priv->tx.proc_idx = (proc_idx + 1) % ndesc;
+
+	return NETDEV_TX_OK;
+}
+
+static int ave_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
+{
+	return phy_mii_ioctl(ndev->phydev, ifr, cmd);
+}
+
+static const u8 v4multi_macadr[] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 v6multi_macadr[] = { 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static void ave_set_rx_mode(struct net_device *ndev)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	struct netdev_hw_addr *hw_adr;
+	int count, mc_cnt;
+	u32 val;
+
+	/* MAC addr filter enable for promiscious mode */
+	mc_cnt = netdev_mc_count(ndev);
+	val = readl(priv->base + AVE_RXCR);
+	if (ndev->flags & IFF_PROMISC || !mc_cnt)
+		val &= ~AVE_RXCR_AFEN;
+	else
+		val |= AVE_RXCR_AFEN;
+	writel(val, priv->base + AVE_RXCR);
+
+	/* set all multicast address */
+	if ((ndev->flags & IFF_ALLMULTI) || mc_cnt > AVE_PF_MULTICAST_SIZE) {
+		ave_pfsel_set_macaddr(ndev, AVE_PFNUM_MULTICAST,
+				      v4multi_macadr, 1);
+		ave_pfsel_set_macaddr(ndev, AVE_PFNUM_MULTICAST + 1,
+				      v6multi_macadr, 1);
+	} else {
+		/* stop all multicast filter */
+		for (count = 0; count < AVE_PF_MULTICAST_SIZE; count++)
+			ave_pfsel_stop(ndev, AVE_PFNUM_MULTICAST + count);
+
+		/* set multicast addresses */
+		count = 0;
+		netdev_for_each_mc_addr(hw_adr, ndev) {
+			if (count == mc_cnt)
+				break;
+			ave_pfsel_set_macaddr(ndev, AVE_PFNUM_MULTICAST + count,
+					      hw_adr->addr, 6);
+			count++;
+		}
+	}
+}
+
+static void ave_get_stats64(struct net_device *ndev,
+			    struct rtnl_link_stats64 *stats)
+{
+	struct ave_private *priv = netdev_priv(ndev);
+	unsigned int start;
+
+	do {
+		start = u64_stats_fetch_begin_irq(&priv->stats_rx.syncp);
+		stats->rx_packets = priv->stats_rx.packets;
+		stats->rx_bytes	  = priv->stats_rx.bytes;
+	} while (u64_stats_fetch_retry_irq(&priv->stats_rx.syncp, start));
+
+	do {
+		start = u64_stats_fetch_begin_irq(&priv->stats_tx.syncp);
+		stats->tx_packets = priv->stats_tx.packets;
+		stats->tx_bytes	  = priv->stats_tx.bytes;
+	} while (u64_stats_fetch_retry_irq(&priv->stats_tx.syncp, start));
+
+	stats->rx_errors      = priv->stats_rx.errors;
+	stats->tx_errors      = priv->stats_tx.errors;
+	stats->rx_dropped     = priv->stats_rx.dropped;
+	stats->tx_dropped     = priv->stats_tx.dropped;
+	stats->rx_fifo_errors = priv->stats_rx.fifo_errors;
+	stats->collisions     = priv->stats_tx.collisions;
+}
+
+static int ave_set_mac_address(struct net_device *ndev, void *p)
+{
+	int ret = eth_mac_addr(ndev, p);
+
+	if (ret)
+		return ret;
+
+	ave_macaddr_init(ndev);
+
+	return 0;
+}
+
+static const struct net_device_ops ave_netdev_ops = {
+	.ndo_init		= ave_init,
+	.ndo_uninit		= ave_uninit,
+	.ndo_open		= ave_open,
+	.ndo_stop		= ave_stop,
+	.ndo_start_xmit		= ave_start_xmit,
+	.ndo_do_ioctl		= ave_ioctl,
+	.ndo_set_rx_mode	= ave_set_rx_mode,
+	.ndo_get_stats64	= ave_get_stats64,
+	.ndo_set_mac_address	= ave_set_mac_address,
+};
+
+static int ave_probe(struct platform_device *pdev)
+{
+	const struct ave_soc_data *data;
+	struct device *dev = &pdev->dev;
+	char buf[ETHTOOL_FWVERS_LEN];
+	phy_interface_t phy_mode;
+	struct ave_private *priv;
+	struct net_device *ndev;
+	struct device_node *np;
+	struct resource	*res;
+	const void *mac_addr;
+	void __iomem *base;
+	u64 dma_mask;
+	int irq, ret;
+	u32 ave_id;
+
+	data = of_device_get_match_data(dev);
+	if (WARN_ON(!data))
+		return -EINVAL;
+
+	np = dev->of_node;
+	phy_mode = of_get_phy_mode(np);
+	if (phy_mode < 0) {
+		dev_err(dev, "phy-mode not found\n");
+		return -EINVAL;
+	}
+	if ((!phy_interface_mode_is_rgmii(phy_mode)) &&
+	    phy_mode != PHY_INTERFACE_MODE_RMII &&
+	    phy_mode != PHY_INTERFACE_MODE_MII) {
+		dev_err(dev, "phy-mode is invalid\n");
+		return -EINVAL;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "IRQ not found\n");
+		return irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	ndev = alloc_etherdev(sizeof(struct ave_private));
+	if (!ndev) {
+		dev_err(dev, "can't allocate ethernet device\n");
+		return -ENOMEM;
+	}
+
+	ndev->netdev_ops = &ave_netdev_ops;
+	ndev->ethtool_ops = &ave_ethtool_ops;
+	SET_NETDEV_DEV(ndev, dev);
+
+	ndev->features    |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+	ndev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_RXCSUM);
+
+	ndev->max_mtu = AVE_MAX_ETHFRAME - (ETH_HLEN + ETH_FCS_LEN);
+
+	mac_addr = of_get_mac_address(np);
+	if (mac_addr)
+		ether_addr_copy(ndev->dev_addr, mac_addr);
+
+	/* if the mac address is invalid, use random mac address */
+	if (!is_valid_ether_addr(ndev->dev_addr)) {
+		eth_hw_addr_random(ndev);
+		dev_warn(dev, "Using random MAC address: %pM\n",
+			 ndev->dev_addr);
+	}
+
+	priv = netdev_priv(ndev);
+	priv->base = base;
+	priv->irq = irq;
+	priv->ndev = ndev;
+	priv->msg_enable = netif_msg_init(-1, AVE_DEFAULT_MSG_ENABLE);
+	priv->phy_mode = phy_mode;
+	priv->data = data;
+
+	if (IS_DESC_64BIT(priv)) {
+		priv->desc_size = AVE_DESC_SIZE_64;
+		priv->tx.daddr  = AVE_TXDM_64;
+		priv->rx.daddr  = AVE_RXDM_64;
+		dma_mask = DMA_BIT_MASK(64);
+	} else {
+		priv->desc_size = AVE_DESC_SIZE_32;
+		priv->tx.daddr  = AVE_TXDM_32;
+		priv->rx.daddr  = AVE_RXDM_32;
+		dma_mask = DMA_BIT_MASK(32);
+	}
+	ret = dma_set_mask(dev, dma_mask);
+	if (ret)
+		goto out_free_netdev;
+
+	priv->tx.ndesc = AVE_NR_TXDESC;
+	priv->rx.ndesc = AVE_NR_RXDESC;
+
+	u64_stats_init(&priv->stats_tx.syncp);
+	u64_stats_init(&priv->stats_rx.syncp);
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk)) {
+		ret = PTR_ERR(priv->clk);
+		goto out_free_netdev;
+	}
+
+	priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
+	if (IS_ERR(priv->rst)) {
+		ret = PTR_ERR(priv->rst);
+		goto out_free_netdev;
+	}
+
+	priv->mdio = devm_mdiobus_alloc(dev);
+	if (!priv->mdio) {
+		ret = -ENOMEM;
+		goto out_free_netdev;
+	}
+	priv->mdio->priv = ndev;
+	priv->mdio->parent = dev;
+	priv->mdio->read = ave_mdiobus_read;
+	priv->mdio->write = ave_mdiobus_write;
+	priv->mdio->name = "uniphier-mdio";
+	snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%x",
+		 pdev->name, pdev->id);
+
+	/* Register as a NAPI supported driver */
+	netif_napi_add(ndev, &priv->napi_rx, ave_napi_poll_rx, priv->rx.ndesc);
+	netif_tx_napi_add(ndev, &priv->napi_tx, ave_napi_poll_tx,
+			  priv->tx.ndesc);
+
+	platform_set_drvdata(pdev, ndev);
+
+	ret = register_netdev(ndev);
+	if (ret) {
+		dev_err(dev, "failed to register netdevice\n");
+		goto out_del_napi;
+	}
+
+	/* get ID and version */
+	ave_id = readl(priv->base + AVE_IDR);
+	ave_hw_read_version(ndev, buf, sizeof(buf));
+
+	dev_info(dev, "Socionext %c%c%c%c Ethernet IP %s (irq=%d, phy=%s)\n",
+		 (ave_id >> 24) & 0xff, (ave_id >> 16) & 0xff,
+		 (ave_id >> 8) & 0xff, (ave_id >> 0) & 0xff,
+		 buf, priv->irq, phy_modes(phy_mode));
+
+	return 0;
+
+out_del_napi:
+	netif_napi_del(&priv->napi_rx);
+	netif_napi_del(&priv->napi_tx);
+out_free_netdev:
+	free_netdev(ndev);
+
+	return ret;
+}
+
+static int ave_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ave_private *priv = netdev_priv(ndev);
+
+	unregister_netdev(ndev);
+	netif_napi_del(&priv->napi_rx);
+	netif_napi_del(&priv->napi_tx);
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static const struct ave_soc_data ave_pro4_data = {
+	.is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_pxs2_data = {
+	.is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld11_data = {
+	.is_desc_64bit = false,
+};
+
+static const struct ave_soc_data ave_ld20_data = {
+	.is_desc_64bit = true,
+};
+
+static const struct of_device_id of_ave_match[] = {
+	{
+		.compatible = "socionext,uniphier-pro4-ave4",
+		.data = &ave_pro4_data,
+	},
+	{
+		.compatible = "socionext,uniphier-pxs2-ave4",
+		.data = &ave_pxs2_data,
+	},
+	{
+		.compatible = "socionext,uniphier-ld11-ave4",
+		.data = &ave_ld11_data,
+	},
+	{
+		.compatible = "socionext,uniphier-ld20-ave4",
+		.data = &ave_ld20_data,
+	},
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_ave_match);
+
+static struct platform_driver ave_driver = {
+	.probe  = ave_probe,
+	.remove = ave_remove,
+	.driver	= {
+		.name = "ave",
+		.of_match_table	= of_ave_match,
+	},
+};
+module_platform_driver(ave_driver);
+
+MODULE_DESCRIPTION("Socionext UniPhier AVE ethernet driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox