[U-Boot] [PATCH v2 1/2] TI: netdev: add driver for cpsw ethernet device

Kumar Nath, Chandan chandan.nath at ti.com
Mon Nov 28 11:33:55 CET 2011


Andy,

> -----Original Message-----
> From: Andy Fleming [mailto:afleming at gmail.com]
> Sent: Friday, November 11, 2011 4:14 AM
> To: Kumar Nath, Chandan
> Cc: u-boot at lists.denx.de; Chemparathy, Cyril
> Subject: Re: [U-Boot] [PATCH v2 1/2] TI: netdev: add driver for cpsw
> ethernet device
> 
> On Thu, Nov 10, 2011 at 6:40 AM, Chandan Nath <chandan.nath at ti.com>
> wrote:
> > From: Cyril Chemparathy <cyril at ti.com>
> >
> > CPSW is an on-chip ethernet switch that is found on various SoCs from
> Texas
> > Instruments.  This patch adds a simple driver (based on the Linux
> driver) for
> > this hardware module.
> 
> 
> Which Linux driver is this based on?

This is based on linux CPSW driver, which is yet to be up streamed.

> 
> 
> >
> > Signed-off-by: Cyril Chemparathy <cyril at ti.com>
> > ---
> > Changes since v1:
> >  - Code cleanup and removal of cpsw_eth_set_mac_addr function
> >
> >  drivers/net/Makefile |    1 +
> >  drivers/net/cpsw.c   |  862
> ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  include/netdev.h     |   29 ++
> >  3 files changed, 892 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/net/cpsw.c
> >
> > +
> > +#define BIT(x)                 (1 << (x))
> > +#define BITMASK(bits)          (BIT(bits) - 1)
> > +#define PHY_REG_MASK           0x1f
> > +#define PHY_ID_MASK            0x1f
> 
> These 4 defines seem very generic

I did not find any generic code for these definitions in arch/arm
and so keeping these definitions here.

> 
> 
> > +struct cpsw_priv {
> > +       struct eth_device               *dev;
> > +       struct cpsw_platform_data       data;
> > +       int                             host_port;
> > +
> > +       struct cpsw_regs                *regs;
> > +       void                            *dma_regs;
> > +       struct cpsw_host_regs           *host_port_regs;
> > +       void                            *ale_regs;
> > +
> > +       struct cpdma_desc               descs[NUM_DESCS];
> > +       struct cpdma_desc               *desc_free;
> > +       struct cpdma_chan               rx_chan, tx_chan;
> > +
> > +       struct cpsw_slave               *slaves;
> > +#define for_each_slave(priv, func, arg...)                     \
> > +       do {                                                    \
> > +               int idx;                                        \
> > +               for (idx = 0; idx < (priv)->data.slaves; idx++) \
> > +                       (func)((priv)->slaves + idx, ##arg);    \
> > +       } while (0)
> > +};
> 
> 
> It seems a bit awkward to me to put a complex macro like this inside
> the structure definition. After further review, I think it should also
> be defined differently, more like the list macros:
> 
> #define for_each_slave(slave, priv) \
>         for (slave = (priv)->slaves; slave != (priv)->slaves +
> (priv)->data.slaves; slave++)
> 
> It makes the code more maintainable, as the macro no longer contains
> the implicit assumption that "func" has a slave pointer as its first
> argument.
> 

This complex macro will be separated out and will be defined like list macros as you have
mentioned above.

> 
> > +
> > +static inline int cpsw_ale_get_field(u32 *ale_entry, u32 start, u32
> bits)
> > +{
> > +       int idx;
> > +
> > +       idx    = start / 32;
> > +       start -= idx * 32;
> > +       idx    = 2 - idx; /* flip */
> > +       return (ale_entry[idx] >> start) & BITMASK(bits);
> > +}
> > +
> > +static inline void cpsw_ale_set_field(u32 *ale_entry, u32 start, u32
> bits,
> > +                                     u32 value)
> > +{
> > +       int idx;
> > +
> > +       value &= BITMASK(bits);
> > +       idx    = start / 32;
> > +       start -= idx * 32;
> > +       idx    = 2 - idx; /* flip */
> > +       ale_entry[idx] &= ~(BITMASK(bits) << start);
> > +       ale_entry[idx] |=  (value << start);
> > +}
> 
> 
> These two functions seem both very generic, and also over-worked.
> 

Should this be changed to a different implementation?

> 
> > +
> > +#define DEFINE_ALE_FIELD(name, start, bits)
>    \
> > +static inline int cpsw_ale_get_##name(u32 *ale_entry)
>    \
> > +{
>    \
> > +       return cpsw_ale_get_field(ale_entry, start, bits);
>    \
> > +}
>    \
> > +static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value)
>    \
> > +{
>    \
> > +       cpsw_ale_set_field(ale_entry, start, bits, value);
>    \
> > +}
> > +
> > +DEFINE_ALE_FIELD(entry_type,           60,     2)
> > +DEFINE_ALE_FIELD(mcast_state,          62,     2)
> > +DEFINE_ALE_FIELD(port_mask,            64,     3)
> > +DEFINE_ALE_FIELD(ucast_type,           66,     2)
> > +DEFINE_ALE_FIELD(port_num,             64,     2)
> > +DEFINE_ALE_FIELD(blocked,              63,     1)
> > +DEFINE_ALE_FIELD(secure,               62,     1)
> > +DEFINE_ALE_FIELD(mcast,                        47,     1)
> > +
> > +/* The MAC address field in the ALE entry cannot be macroized as
> above */
> > +static inline void cpsw_ale_get_addr(u32 *ale_entry, u8 *addr)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < 6; i++)
> > +               addr[i] = cpsw_ale_get_field(ale_entry, 40 - 8*i, 8);
> > +}
> > +
> > +static inline void cpsw_ale_set_addr(u32 *ale_entry, u8 *addr)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < 6; i++)
> > +               cpsw_ale_set_field(ale_entry, 40 - 8*i, 8, addr[i]);
> > +}
> > +
> > +static int cpsw_ale_read(struct cpsw_priv *priv, int idx, u32
> *ale_entry)
> > +{
> > +       int i;
> > +
> > +       __raw_writel(idx, priv->ale_regs + ALE_TABLE_CONTROL);
> > +
> > +       for (i = 0; i < ALE_ENTRY_WORDS; i++)
> > +               ale_entry[i] = __raw_readl(priv->ale_regs + ALE_TABLE
> + 4 * i);
> > +
> > +       return idx;
> > +}
> > +
> > +static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32
> *ale_entry)
> > +{
> > +       int i;
> > +
> > +       for (i = 0; i < ALE_ENTRY_WORDS; i++)
> > +               __raw_writel(ale_entry[i], priv->ale_regs + ALE_TABLE
> + 4 * i);
> > +
> > +       __raw_writel(idx | ALE_TABLE_WRITE, priv->ale_regs +
> ALE_TABLE_CONTROL);
> > +
> > +       return idx;
> > +}
> 
> 
> I don't know how your device works, but it seems odd to me to have to
> read out a large table, and modify it, and then write it back out.
> 
> I'm also highly suspicious of the overworked bitfield math. Are
> standard C bitfields not usable for some reason? It borders on code
> obfuscation, IMO. I've spent far too much time, already, just trying
> to figure out what that code was doing, and why.
> 

I will think of some standard C bitfields to make it simpler.

> > +static inline void cpsw_ale_port_state(struct cpsw_priv *priv, int
> port,
> > +                                      int val)
> > +{
> > +       int offset = ALE_PORTCTL + 4 * port;
> > +       u32 tmp, mask = 0x3;
> > +
> > +       tmp  = __raw_readl(priv->ale_regs + offset);
> > +       tmp &= ~mask;
> > +       tmp |= val & 0x3;
> 
> All of this can probably be done with more generic setbits functions,
> but at least use "mask" consistently for both the clearing and the
> masking of "val".
> 

Ok, mask will be used consistently.

> > +       __raw_writel(tmp, priv->ale_regs + offset);
> > +}
> > +
> > +static struct cpsw_mdio_regs *mdio_regs;
> 
> 
> Global variables like this shouldn't be necessary. Especially with the
> new PHY API
>

This global variable is required to access mdio registers across different functions.
 
> 	
> 
> > +
> > +static int cpsw_mdio_read(char *devname, unsigned char phy_id,
> > +                         unsigned char phy_reg, unsigned short
> *data)
> > +{
> 
> This is the legacy interface for PHY interactions. You should look
> into using the new "PHYLIB" code.
> 

Sure, I will be using new "PHYLIB" code instead of this legacy code.

> 
> > +       u32 reg;
> > +
> > +       if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
> > +               return -EINVAL;
> > +
> > +       wait_for_user_access();
> > +       reg = (USERACCESS_GO | USERACCESS_READ | (phy_reg << 21) |
> > +              (phy_id << 16));
> > +       __raw_writel(reg, &mdio_regs->user[0].access);
> > +       reg = wait_for_user_access();
> > +
> > +       *data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -
> 1;
> > +       return (reg & USERACCESS_ACK) ? 0 : -EIO;
> > +}
> > +
> > +static int cpsw_mdio_write(char *devname, unsigned char phy_id,
> > +                          unsigned char phy_reg, unsigned short
> data)
> 
> Same comment as above.
> 

Yes, I will use "PHYLIB" code instead of legacy code.

> 
> > +static void cpsw_mdio_init(char *name, u32 mdio_base, u32 div)
> > +{
> > +       mdio_regs = (struct cpsw_mdio_regs *)mdio_base;
> > +
> > +       /* set enable and clock divider */
> > +       __raw_writel(div | CONTROL_ENABLE, &mdio_regs->control);
> > +
> > +       /*
> > +        * wait for scan logic to settle:
> > +        * the scan time consists of (a) a large fixed component, and
> (b) a
> > +        * small component that varies with the mii bus frequency.
>  These
> > +        * were estimated using measurements at 1.1 and 2.2 MHz on
> tnetv107x
> > +        * silicon.  Since the effect of (b) was found to be largely
> > +        * negligible, we keep things simple here.
> > +        */
> > +       udelay(1000);
> 
> This seems sketchy. Is there no way to *know* the bus is ready, other
> than to wait?
> 
> > +
> > +       miiphy_register(name, cpsw_mdio_read, cpsw_mdio_write);
> 
> 
> This is the legacy API.
> 

PHYLIB code will be used instead of this.

> 
> > +}
> > +
> > +static inline void soft_reset(void *reg)
> > +{
> > +       __raw_writel(1, reg);
> > +       while (__raw_readl(reg) & 1)
> > +               ;
> > +}
> 
> 
> ???
> 
> Can reg be *any* reg? Use a better name. Also, name "1" something
> sensible.
> 
> Perhaps a cleaner implementation (which might be usable in other
> people's code) would be something like:
> 
> /* Set a self-clearing bit in a register, and wait for it to clear */
> static inline void setbit_and_wait_for_clear32(void *addr, u32 mask)
> {
>    __raw_writel(val, addr);
>   while (__raw_readl(addr) & val)
>     ;
> }
> 
> 

Ok, I will implement this in a cleaner manner.

> > +static void cpsw_slave_update_link(struct cpsw_slave *slave,
> > +                                  struct cpsw_priv *priv, int *link)
> > +{
> > +       char *name = priv->dev->name;
> > +       int phy_id = slave->data->phy_id;
> > +       int speed, duplex;
> > +       unsigned short reg;
> > +       u32 mac_control = 0;
> > +
> > +       if (miiphy_read(name, phy_id, MII_BMSR, &reg))
> > +               return; /* could not read, assume no link */
> 
> 
> Please use phy_read(), with the new API.
>

Ok, I will use phy_read(), instead of miiphy_read.
 
> 
> > +
> > +       if (reg & BMSR_LSTATUS) { /* link up */
> > +               speed = miiphy_speed(name, phy_id);
> > +               duplex = miiphy_duplex(name, phy_id);
> > +
> > +               *link = 1;
> > +               mac_control = priv->data.mac_control;
> > +               if (speed == 1000)
> > +                       mac_control |= BIT(7);  /* GIGABITEN    */
> > +               if (duplex == FULL)
> > +                       mac_control |= BIT(0);  /* FULLDUPLEXEN */
> 
> Why not:
>     mac_control |= GIGABITEN;
> 
> etc
> 

Sure, I will make this change.

> 
> 
> > +static int cpsw_update_link(struct cpsw_priv *priv)
> > +{
> > +       int link = 0;
> > +       for_each_slave(priv, cpsw_slave_update_link, priv, &link);
> 
> As I mentioned, there are a lot of similarly-named macros in Linux and
> U-boot. Usually they act exactly like for-loops, where you provide an
> iterator, and then use it in each iteration:
> 
> struct cpsw_slave *slave;
> 
> for_each_slave(slave, priv)
>   cpsw_slave_update_link(slave, priv, &link);
> 
> I think this is much easier to understand and maintain.
> 
> > +       return link;
> 
> Also, link looks useless. It will always be the value in the last
> slave. Is that what you meant to do?
> 
> 

Ok, I will use this as per your comment.
for_each_slave(slave, priv)
	cpsw_slave_update_link(slave, priv, &link);

Also, I am returning link which returns the value of last slave.

> > +}
> > +
> > +static void cpsw_slave_init(struct cpsw_slave *slave, struct
> cpsw_priv *priv)
> > +{
> 
> [...]
> 
> > +
> > +       priv->data.phy_init(priv->dev->name, slave->data->phy_id);
> 
> What does this do? Probably needs to be converted to the newer API.
> 

I will remove this and new PHYLIB APIs will be used instead.

> 
> > +}
> > +
> > +static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv)
> > +{
> > +       struct cpdma_desc *desc = priv->desc_free;
> > +
> > +       if (desc)
> > +               priv->desc_free = desc_read_ptr(desc, hw_next);
> > +       return desc;
> > +}
> > +
> > +static void cpdma_desc_free(struct cpsw_priv *priv, struct
> cpdma_desc *desc)
> > +{
> > +       if (desc) {
> > +               desc_write(desc, hw_next, priv->desc_free);
> > +               priv->desc_free = desc;
> > +       }
> > +}
> > +
> > +static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan
> *chan,
> > +                       volatile void *buffer, int len)
> > +{
> > +       struct cpdma_desc *desc, *prev;
> > +       u32 mode;
> > +
> > +       desc = cpdma_desc_alloc(priv);
> > +       if (!desc)
> > +               return -ENOMEM;
> > +
> > +       if (len < PKT_MIN)
> > +               len = PKT_MIN;
> > +
> > +       mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP;
> > +
> > +       desc_write(desc, hw_next,   0);
> > +       desc_write(desc, hw_buffer, buffer);
> > +       desc_write(desc, hw_len,    len);
> > +       desc_write(desc, hw_mode,   mode | len);
> > +       desc_write(desc, sw_buffer, buffer);
> > +       desc_write(desc, sw_len,    len);
> > +
> > +       if (!chan->head) {
> > +               /* simple case - first packet enqueued */
> > +               chan->head = desc;
> > +               chan->tail = desc;
> > +               chan_write(chan, hdp, desc);
> > +               goto done;
> > +       }
> > +
> > +       /* not the first packet - enqueue at the tail */
> > +       prev = chan->tail;
> > +       desc_write(prev, hw_next, desc);
> > +       chan->tail = desc;
> > +
> > +       /* next check if EOQ has been triggered already */
> > +       if (desc_read(prev, hw_mode) & CPDMA_DESC_EOQ)
> > +               chan_write(chan, hdp, desc);
> > +
> > +done:
> > +       if (chan->rxfree)
> > +               chan_write(chan, rxfree, 1);
> > +       return 0;
> > +}
> > +
> > +static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan
> *chan,
> > +                        void **buffer, int *len)
> > +{
> > +       struct cpdma_desc *desc = chan->head;
> > +       u32 status;
> > +
> > +       if (!desc)
> > +               return -ENOENT;
> > +
> > +       status  = desc_read(desc, hw_mode);
> > +
> > +       if (len)
> > +               *len = status & 0x7ff;
> > +
> > +       if (buffer)
> > +               *buffer = desc_read_ptr(desc, sw_buffer);
> > +
> > +       if (status & CPDMA_DESC_OWNER)
> > +               return -EBUSY;
> > +
> > +       chan->head = desc_read_ptr(desc, hw_next);
> > +       chan_write(chan, cp, desc);
> > +
> > +       cpdma_desc_free(priv, desc);
> > +       return 0;
> > +}
> > +
> > +static int cpsw_init(struct eth_device *dev, bd_t *bis)
> > +{
> > +       struct cpsw_priv        *priv = dev->priv;
> > +       int i, ret;
> > +
> > +       priv->data.control(1);
> 
> 1 what? The "control" function probably needs a better name. And maybe
> so does "1".
> 

This is not required and will be removed from the code.

> > +
> > +       /* soft reset the controller and initialize priv */
> > +       soft_reset(&priv->regs->soft_reset);
> > +
> 
> See comment about soft_reset, above.
> 

Ok, I will address this as above.

> > +
> > +       cpsw_ale_port_state(priv, priv->host_port,
> ALE_PORT_STATE_FORWARD);
> > +
> > +       cpsw_ale_add_ucast(priv, priv->dev->enetaddr, priv-
> >host_port,
> > +                          ALE_SECURE);
> > +       cpsw_ale_add_mcast(priv, NetBcastAddr, 1 << priv->host_port);
> > +
> > +       for_each_slave(priv, cpsw_slave_init, priv);
> 
> Same comment as before about the for-loop construct.
> 

Ok, will address in similar way as above.

> [...]
> 
> > +       eth_register(dev);
> > +
> > +       cpsw_mdio_init(dev->name, data->mdio_base, data->mdio_div);
> 
> I bet you only need one MDIO bus, and the new PHY API makes that
> straightforward.
>

Yes, I will use new PHYLIB APIs.
 
> > +
> > +       return 1;
> > +}
> > diff --git a/include/netdev.h b/include/netdev.h
> > index 96c7b9b..740213e 100644
> > --- a/include/netdev.h
> > +++ b/include/netdev.h
> > @@ -185,4 +185,33 @@ struct mv88e61xx_config {
> >  int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig);
> >  #endif /* CONFIG_MV88E61XX_SWITCH */
> >
> > +#ifdef CONFIG_DRIVER_TI_CPSW
> > +
> > +struct cpsw_slave_data {
> > +       u32             slave_reg_ofs;
> > +       u32             sliver_reg_ofs;
> > +       int             phy_id;
> > +};
> > +
> > +struct cpsw_platform_data {
> > +       u32     mdio_base;
> > +       u32     cpsw_base;
> > +       int     mdio_div;
> > +       int     channels;       /* number of cpdma channels
> (symmetric) */
> > +       u32     cpdma_reg_ofs;  /* cpdma register offset
>    */
> > +       int     slaves;         /* number of slave cpgmac ports
>   */
> > +       u32     ale_reg_ofs;    /* address lookup engine reg offset
>   */
> > +       int     ale_entries;    /* ale table size
>   */
> > +       u32     host_port_reg_ofs;      /* cpdma host port registers
>    */
> > +       u32     hw_stats_reg_ofs;       /* cpsw hw stats counters
>   */
> > +       u32     mac_control;
> > +       struct cpsw_slave_data  *slave_data;
> > +       void    (*control)(int enabled);
> > +       void    (*phy_init)(char *name, int addr);
> > +};
> > +
> > +int cpsw_register(struct cpsw_platform_data *data);
> > +
> > +#endif /* CONFIG_DRIVER_TI_CPSW */
> 
> Hmmm... nothing here seems like it should be in a generic header like
> "netdev.h". Only cpsw_register(), and probably not that, either. I see
> there's some Marvell stuff in there, too, which is suspect, but let's
> not propagate that issue. There's a cpu_eth_init() and a
> board_eth_init(). Surely one of those can call cpsw_register(), and
> then you can put the cpsw-specific structures in something like
> include/cpsw.h.
> 

Sure, I will remove this code from netdev.h and will keep in separate cpsw header, i.e. cpsw.h

> Andy


More information about the U-Boot mailing list