All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Lunn <andrew@lunn.ch>
To: John Crispin <john@phrozen.org>
Cc: "David S. Miller" <davem@davemloft.net>,
	Florian Fainelli <f.fainelli@gmail.com>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	qsdk-review@qca.qualcomm.com
Subject: Re: [PATCH 3/3] net-next: dsa: add new driver for qca8xxx family
Date: Mon, 12 Sep 2016 15:16:20 +0200	[thread overview]
Message-ID: <20160912131620.GB17533@lunn.ch> (raw)
In-Reply-To: <1473669337-21221-4-git-send-email-john@phrozen.org>

> +static u32
> +qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum)
> +{
> +	u16 lo, hi;
> +
> +	lo = bus->read(bus, phy_id, regnum);
> +	hi = bus->read(bus, phy_id, regnum + 1);
> +
> +	return (hi << 16) | lo;
> +}
> +
> +static void
> +qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
> +{
> +	u16 lo, hi;
> +
> +	lo = val & 0xffff;
> +	hi = (u16)(val >> 16);
> +
> +	bus->write(bus, phy_id, regnum, lo);
> +	bus->write(bus, phy_id, regnum + 1, hi);
> +}

Hi John

Thanks for picking up this driver and continuing its journey towards
mainline.

bus->read() and bus->write() can return an error code. There is a lot
of error handling in the mv88e6xxx driver looking at the error code
from these two functions. And it very rarely happens. So it seems
overkill to me. However, i have had errors, when bringing up a new
board and the device tree is wrong somehow.

But ignoring potential error altogether does not seem wise. I think at
minimum you should look at the return code in these functions and do a
rate limited dev_err().

> +
> +static void
> +qca8k_set_page(struct mii_bus *bus, u16 page)
> +{
> +	if (page == qca8k_current_page)
> +		return;
> +
> +	bus->write(bus, 0x18, 0, page);
> +	udelay(5);

Is that delay in the data sheet? What about other accesses, like
reading the stats which is going to generate a lot of back to back
reads?

> +	qca8k_current_page = page;
> +}
> +
> +static u32
> +qca8k_read(struct qca8k_priv *priv, u32 reg)
> +{
> +	u16 r1, r2, page;
> +	u32 val;
> +
> +	qca8k_split_addr(reg, &r1, &r2, &page);
> +
> +	mutex_lock(&priv->bus->mdio_lock);
> +
> +	qca8k_set_page(priv->bus, page);
> +	val = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
> +
> +	mutex_unlock(&priv->bus->mdio_lock);
> +
> +	return val;
> +}
> +
> +static void
> +qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val)
> +{
> +	u16 r1, r2, page;
> +
> +	qca8k_split_addr(reg, &r1, &r2, &page);
> +
> +	mutex_lock(&priv->bus->mdio_lock);
> +
> +	qca8k_set_page(priv->bus, page);
> +	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, val);
> +
> +	mutex_unlock(&priv->bus->mdio_lock);
> +}
> +
> +static u32
> +qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 val)
> +{
> +	u16 r1, r2, page;
> +	u32 ret;
> +
> +	qca8k_split_addr(reg, &r1, &r2, &page);
> +
> +	mutex_lock(&priv->bus->mdio_lock);
> +
> +	qca8k_set_page(priv->bus, page);
> +	ret = qca8k_mii_read32(priv->bus, 0x10 | r2, r1);
> +	ret &= ~mask;
> +	ret |= val;
> +	qca8k_mii_write32(priv->bus, 0x10 | r2, r1, ret);
> +
> +	mutex_unlock(&priv->bus->mdio_lock);
> +
> +	return ret;
> +}
> +
> +static inline void
> +qca8k_reg_set(struct qca8k_priv *priv, u32 reg, u32 val)
> +{
> +	qca8k_rmw(priv, reg, 0, val);
> +}
> +
> +static inline void
> +qca8k_reg_clear(struct qca8k_priv *priv, u32 reg, u32 val)
> +{
> +	qca8k_rmw(priv, reg, val, 0);
> +}

Drop the inline please.

> +static u16
> +qca8k_phy_mmd_read(struct qca8k_priv *priv, int phy_addr, u16 addr, u16 reg)
> +{
> +	u16 data;
> +
> +	mutex_lock(&priv->bus->mdio_lock);
> +
> +	priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr);
> +	priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_DATA, reg);
> +	priv->bus->write(priv->bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
> +	data = priv->bus->read(priv->bus, phy_addr, MII_ATH_MMD_DATA);
> +
> +	mutex_unlock(&priv->bus->mdio_lock);

Have you run lockdep on this? The mv88e6xxx has something similar, and
were getying false positive splats. We needed to use
mdiobus_write_nested(). Since you are using bus->write directly, not
using the wrapper, maybe this is not an issue. But i'm wondering if
ignoring the wrapped is the right thing to do, with nested MDIO
busses. Something i need to think about.

> +static int
> +qca8k_fdb_busy_wait(struct qca8k_priv *priv)
> +{
> +	unsigned long timeout;
> +
> +	timeout = jiffies + msecs_to_jiffies(20);
> +
> +	/* loop until the busy flag has cleared */
> +	do {
> +		u32 reg = qca8k_read(priv, QCA8K_REG_ATU_FUNC);
> +		int busy = reg & QCA8K_ATU_FUNC_BUSY;
> +
> +		if (!busy)
> +			break;
> +	} while (!time_after_eq(jiffies, timeout));
> +
> +	return time_after_eq(jiffies, timeout);
> +}

No sleep? You busy loop for 20ms?

> +
> +static void
> +qca8k_fdb_read(struct qca8k_priv *priv, struct qca8k_fdb *fdb)
> +{
> +	u32 reg[4];
> +	int i;
> +
> +	/* load the ARL table into an array */
> +	for (i = 0; i < 4; i++)
> +		reg[i] = qca8k_read(priv, QCA8K_REG_ATU_DATA0 + (i * 4));
> +
> +	/* vid - 83:72 */
> +	fdb->vid = (reg[2] >> QCA8K_ATU_VID_S) & QCA8K_ATU_VID_M;
> +	/* aging - 67:64 */
> +	fdb->aging = reg[2] & QCA8K_ATU_STATUS_M;
> +	/* portmask - 54:48 */
> +	fdb->port_mask = (reg[1] >> QCA8K_ATU_PORT_S) & QCA8K_ATU_PORT_M;
> +	/* mac - 47:0 */
> +	fdb->mac[0] = (reg[1] >> QCA8K_ATU_ADDR0_S) & 0xff;
> +	fdb->mac[1] = reg[1] & 0xff;
> +	fdb->mac[2] = (reg[0] >> QCA8K_ATU_ADDR2_S) & 0xff;
> +	fdb->mac[3] = (reg[0] >> QCA8K_ATU_ADDR3_S) & 0xff;
> +	fdb->mac[4] = (reg[0] >> QCA8K_ATU_ADDR4_S) & 0xff;
> +	fdb->mac[5] = reg[0] & 0xff;
> +}
> +
> +static void
> +qca8k_fdb_write(struct qca8k_priv *priv, u16 vid, u8 port_mask, const u8 *mac,
> +		u8 aging)
> +{
> +	u32 reg[3] = { 0 };
> +	int i;
> +
> +	/* vid - 83:72 */
> +	reg[2] = (vid & QCA8K_ATU_VID_M) << QCA8K_ATU_VID_S;
> +	/* aging - 67:64 */
> +	reg[2] |= aging & QCA8K_ATU_STATUS_M;
> +	/* portmask - 54:48 */
> +	reg[1] = (port_mask & QCA8K_ATU_PORT_M) << QCA8K_ATU_PORT_S;
> +	/* mac - 47:0 */
> +	reg[1] |= mac[0] << QCA8K_ATU_ADDR0_S;
> +	reg[1] |= mac[1];
> +	reg[0] |= mac[2] << QCA8K_ATU_ADDR2_S;
> +	reg[0] |= mac[3] << QCA8K_ATU_ADDR3_S;
> +	reg[0] |= mac[4] << QCA8K_ATU_ADDR4_S;
> +	reg[0] |= mac[5];
> +
> +	/* load the array into the ARL table */
> +	for (i = 0; i < 3; i++)
> +		qca8k_write(priv, QCA8K_REG_ATU_DATA0 + (i * 4), reg[i]);
> +}
> +
> +static int
> +qca8k_fdb_access(struct qca8k_priv *priv, enum qca8k_fdb_cmd cmd, int port)
> +{
> +	u32 reg;
> +
> +	/* Set the command and FDB index */
> +	reg = QCA8K_ATU_FUNC_BUSY;
> +	reg |= cmd;
> +	if (port >= 0) {
> +		reg |= QCA8K_ATU_FUNC_PORT_EN;
> +		reg |= (port && QCA8K_ATU_FUNC_PORT_M) << QCA8K_ATU_FUNC_PORT_S;
> +	}
> +
> +	/* Write the function register triggering the table access */
> +	qca8k_write(priv, QCA8K_REG_ATU_FUNC, reg);
> +
> +	/* wait for completion */
> +	if (qca8k_fdb_busy_wait(priv))
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static int
> +qca8k_fdb_next(struct qca8k_priv *priv, struct qca8k_fdb *fdb, int port)
> +{
> +	int ret;
> +
> +	qca8k_fdb_write(priv, fdb->vid, fdb->port_mask, fdb->mac, fdb->aging);
> +	ret = qca8k_fdb_access(priv, QCA8K_FDB_NEXT, port);
> +	if (ret >= 0)
> +		qca8k_fdb_read(priv, fdb);
> +
> +	return ret;
> +}

So a big picture question. How does locking work?

qca8k_fdb_write() will take and release the MDIO bus
lock. qca8k_fdb_access() will also multiple times take and release the
lock and then qca8k_fdb_read() will take and release the lock a few
times. So it seems like there are multiple opportunities for a race
condition, multiple parallel calls to qca8k_fdb_next() for different
ports. Is this addresses somewhere?

I'm out of time for the moment. More comments later.

    Andrew

  reply	other threads:[~2016-09-12 13:16 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-12  8:35 [PATCH 0/3] net-next: dsa: add QCA8K support John Crispin
2016-09-12  8:35 ` [PATCH 1/3] Documentation: devicetree: add qca8k binding John Crispin
2016-09-12 11:53   ` Sergei Shtylyov
2016-09-12  8:35 ` [PATCH 2/3] net-next: dsa: add Qualcomm tag RX/TX handler John Crispin
2016-09-12 12:26   ` Andrew Lunn
2016-09-12  8:35 ` [PATCH 3/3] net-next: dsa: add new driver for qca8xxx family John Crispin
2016-09-12 13:16   ` Andrew Lunn [this message]
2016-09-12 22:52   ` Andrew Lunn
2016-09-13  0:40   ` Andrew Lunn
2016-09-13  8:04     ` John Crispin
2016-09-13 15:59       ` Andrew Lunn
2016-09-13 17:09         ` Florian Fainelli
2016-09-13 18:07           ` John Crispin
2016-09-13 18:11             ` Florian Fainelli
2016-09-13  1:23   ` Andrew Lunn
2016-09-13  9:40     ` John Crispin
2016-09-13 13:14       ` Andrew Lunn
2016-09-13 17:11         ` Vivien Didelot
2016-09-13 19:07           ` Andrew Lunn
2016-09-13 19:10             ` John Crispin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160912131620.GB17533@lunn.ch \
    --to=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=f.fainelli@gmail.com \
    --cc=john@phrozen.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=qsdk-review@qca.qualcomm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.