Commit 969337a4 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'net-ipq4019-rate'



Christian Marangi says:

====================
net: mdio-ipq4019: fix wrong default MDC rate

This was a long journey to arrive and discover this problem.

To not waste too much char, there is a race problem with PHY and driver
probe. This was observed with Aquantia PHY firmware loading.

With some hacks the race problem was workarounded but an interesting
thing was notice. It took more than a minute for the firmware to load
via MDIO.

This was strange as the same operation was done by UBoot in at max 5
second and the same data was loaded.

A similar problem was observed on a mtk board that also had an
Aquantia PHY where the load was very slow. It was notice that the cause
was the MDIO bus running at a very low speed and the firmware
was missing a property (present in mtk sdk) that set the right frequency
to the MDIO bus.

It was fun to find that THE VERY SAME PROBLEM is present on IPQ in a
different form. The MDIO apply internally a division to the feed clock
resulting in the bus running at 390KHz instead of 6.25Mhz.

Searching around the web for some documentation and some include and
analyzing the uboot codeflow resulted in the divider being set wrongly
at /256 instead of /16 as the value was actually never set.
Applying the value restore the original load time for the Aquantia PHY.

This series mainly handle this by adding support for the "clock-frequency"
property.

Changes v3:
- Add Reviewed-by tag
- Fix english grammar error in comment
- Drop DTS patch
Changes v2:
- Use DIV_ROUND_UP
- Introduce logic to chose a default value for 802.3 spec 2.5MHz
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 747056a9 bdce82e9
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -44,6 +44,21 @@ properties:
    items:
      - const: gcc_mdio_ahb_clk

  clock-frequency:
    description:
      The MDIO bus clock that must be output by the MDIO bus hardware, if
      absent, the default hardware values are used.

      MDC rate is feed by an external clock (fixed 100MHz) and is divider
      internally. The default divider is /256 resulting in the default rate
      applied of 390KHz.

      To follow 802.3 standard that instruct up to 2.5MHz by default, if
      this property is not declared and the divider is set to /256, by
      default 1.5625Mhz is select.
    enum: [ 390625, 781250, 1562500, 3125000, 6250000, 12500000 ]
    default: 1562500

required:
  - compatible
  - reg
+103 −6
Original line number Diff line number Diff line
@@ -14,6 +14,20 @@
#include <linux/clk.h>

#define MDIO_MODE_REG				0x40
#define   MDIO_MODE_MDC_MODE			BIT(12)
/* 0 = Clause 22, 1 = Clause 45 */
#define   MDIO_MODE_C45				BIT(8)
#define   MDIO_MODE_DIV_MASK			GENMASK(7, 0)
#define     MDIO_MODE_DIV(x)			FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)
#define     MDIO_MODE_DIV_1			0x0
#define     MDIO_MODE_DIV_2			0x1
#define     MDIO_MODE_DIV_4			0x3
#define     MDIO_MODE_DIV_8			0x7
#define     MDIO_MODE_DIV_16			0xf
#define     MDIO_MODE_DIV_32			0x1f
#define     MDIO_MODE_DIV_64			0x3f
#define     MDIO_MODE_DIV_128			0x7f
#define     MDIO_MODE_DIV_256			0xff
#define MDIO_ADDR_REG				0x44
#define MDIO_DATA_WRITE_REG			0x48
#define MDIO_DATA_READ_REG			0x4c
@@ -26,9 +40,6 @@
#define MDIO_CMD_ACCESS_CODE_C45_WRITE	1
#define MDIO_CMD_ACCESS_CODE_C45_READ	2

/* 0 = Clause 22, 1 = Clause 45 */
#define MDIO_MODE_C45				BIT(8)

#define IPQ4019_MDIO_TIMEOUT	10000
#define IPQ4019_MDIO_SLEEP		10

@@ -41,6 +52,7 @@ struct ipq4019_mdio_data {
	void __iomem	*membase;
	void __iomem *eth_ldo_rdy;
	struct clk *mdio_clk;
	unsigned int mdc_rate;
};

static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
@@ -203,6 +215,38 @@ static int ipq4019_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
	return 0;
}

static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)
{
	unsigned long ahb_rate;
	int div;
	u32 val;

	/* If we don't have a clock for AHB use the fixed value */
	ahb_rate = IPQ_MDIO_CLK_RATE;
	if (priv->mdio_clk)
		ahb_rate = clk_get_rate(priv->mdio_clk);

	/* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)
	 * While supported, internal documentation doesn't
	 * assure correct functionality of the MDIO bus
	 * with divider of 1, 2 or 4.
	 */
	for (div = 8; div <= 256; div *= 2) {
		/* The requested rate is supported by the div */
		if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {
			val = readl(priv->membase + MDIO_MODE_REG);
			val &= ~MDIO_MODE_DIV_MASK;
			val |= MDIO_MODE_DIV(div);
			writel(val, priv->membase + MDIO_MODE_REG);

			return 0;
		}
	}

	/* The requested rate is not supported */
	return -EINVAL;
}

static int ipq_mdio_reset(struct mii_bus *bus)
{
	struct ipq4019_mdio_data *priv = bus->priv;
@@ -225,10 +269,58 @@ static int ipq_mdio_reset(struct mii_bus *bus)
		return ret;

	ret = clk_prepare_enable(priv->mdio_clk);
	if (ret == 0)
	if (ret)
		return ret;

	mdelay(10);

	return ret;
	/* Restore MDC rate */
	return ipq4019_mdio_set_div(priv);
}

static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,
					 struct ipq4019_mdio_data *priv)
{
	unsigned long ahb_rate;
	int div;
	u32 val;

	/* MDC rate defined in DT, we don't have to decide a default value */
	if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
				  &priv->mdc_rate))
		return;

	/* If we don't have a clock for AHB use the fixed value */
	ahb_rate = IPQ_MDIO_CLK_RATE;
	if (priv->mdio_clk)
		ahb_rate = clk_get_rate(priv->mdio_clk);

	/* Check what is the current div set */
	val = readl(priv->membase + MDIO_MODE_REG);
	div = FIELD_GET(MDIO_MODE_DIV_MASK, val);

	/* div is not set to the default value of /256
	 * Probably someone changed that (bootloader, other drivers)
	 * Keep this and don't overwrite it.
	 */
	if (div != MDIO_MODE_DIV_256) {
		priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);
		return;
	}

	/* If div is /256 assume nobody have set this value and
	 * try to find one MDC rate that is close the 802.3 spec of
	 * 2.5MHz
	 */
	for (div = 256; div >= 8; div /= 2) {
		/* Stop as soon as we found a divider that
		 * reached the closest value to 2.5MHz
		 */
		if (DIV_ROUND_UP(ahb_rate, div) > 2500000)
			break;

		priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);
	}
}

static int ipq4019_mdio_probe(struct platform_device *pdev)
@@ -252,6 +344,11 @@ static int ipq4019_mdio_probe(struct platform_device *pdev)
	if (IS_ERR(priv->mdio_clk))
		return PTR_ERR(priv->mdio_clk);

	ipq4019_mdio_select_mdc_rate(pdev, priv);
	ret = ipq4019_mdio_set_div(priv);
	if (ret)
		return ret;

	/* The platform resource is provided on the chipset IPQ5018 */
	/* This resource is optional */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);