On Wed, May 14, 2014 at 8:18 AM, Dean Nelson <dnelson@xxxxxxxxxx> wrote: > On 05/05/2014 04:47 PM, Iyappan Subramanian wrote: >> >> This patch adds network driver for APM X-Gene SoC ethernet. >> >> Signed-off-by: Iyappan Subramanian <isubramanian@xxxxxxx> >> Signed-off-by: Ravi Patel <rapatel@xxxxxxx> >> Signed-off-by: Keyur Chudgar <kchudgar@xxxxxxx> >> --- >> drivers/net/ethernet/Kconfig | 1 + >> drivers/net/ethernet/Makefile | 1 + >> drivers/net/ethernet/apm/Kconfig | 1 + >> drivers/net/ethernet/apm/Makefile | 5 + >> drivers/net/ethernet/apm/xgene/Kconfig | 9 + >> drivers/net/ethernet/apm/xgene/Makefile | 6 + >> drivers/net/ethernet/apm/xgene/xgene_enet_hw.c | 807 >> +++++++++++++++++++ >> drivers/net/ethernet/apm/xgene/xgene_enet_hw.h | 353 ++++++++ >> drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 936 >> ++++++++++++++++++++++ >> drivers/net/ethernet/apm/xgene/xgene_enet_main.h | 131 +++ >> 10 files changed, 2250 insertions(+) >> create mode 100644 drivers/net/ethernet/apm/Kconfig >> create mode 100644 drivers/net/ethernet/apm/Makefile >> create mode 100644 drivers/net/ethernet/apm/xgene/Kconfig >> create mode 100644 drivers/net/ethernet/apm/xgene/Makefile >> create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_hw.c >> create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_hw.h >> create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.c >> create mode 100644 drivers/net/ethernet/apm/xgene/xgene_enet_main.h > > > Below, you'll find some comments from my review (as far as I've > gotten)... > > There's an inter-related piece centered on ring->id and RING_BUFNUM(), > with comments scattered throughout. You may have to read all the > comments related to it before what I'm trying to convey makes sense. > If indeed anything of what I'm trying to say can make any sense at > all. :-) For I might be missing the obvious. > > I'll continue my review as time permits, but thought it best to send > what I've seen so far for your consideration. > > Dean Thanks Dean. I really appreciate the in-depth review around RING_OWNER and RING_BUFNUM :-) X-Gene hardware uses RING_OWNER and RING_BUFNUM to uniquely identify each ring. ( ring_owner is 4 bits and bufnum is 6 bits value). That is why you see (ring_owner << 6 | bufnum) code. > > > <snip> > >> diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c >> b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c >> new file mode 100644 >> index 0000000..421a841 >> --- /dev/null >> +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c > > > <snip> > >> + >> +struct xgene_enet_desc_ring *xgene_enet_setup_ring( >> + struct xgene_enet_desc_ring *ring) >> +{ >> + u32 size = ring->size; >> + u32 i, data; >> + u64 *desc; >> + >> + xgene_enet_clr_ring_state(ring); >> + xgene_enet_set_ring_state(ring); >> + xgene_enet_set_ring_id(ring); >> + >> + ring->slots = IS_FP(ring->id) ? size / 16 : size / 32; >> + >> + if (IS_FP(ring->id) || RING_OWNER(ring) != RING_OWNER_CPU) >> + goto out; > > > Since we will bail out here, if (ring->id & 0x20) is true... > > >> + >> + for (i = 0; i < ring->slots; i++) { >> + desc = (u64 *)&ring->desc[i]; >> + desc[EMPTY_SLOT_INDEX] = EMPTY_SLOT; >> + } >> + >> + xgene_enet_ring_rd32(ring, CSR_RING_NE_INT_MODE, &data); >> + data |= (1 << (31 - RING_BUFNUM(ring))); > > > Then RING_BUFNUM(ring) should always be 0 here, since I don't see > the 'bufnum' portion of ring->id being anything other than 0x20 or 0. > So why bother? There is 1 Tx ring, 1 Bufpool ring, 1 Rx ring used in the code that I submitted. But the plan is to add more rings. Regular bufnum starts at 0 and bufpool bufnum start at 0x20. > > > >> + xgene_enet_ring_wr32(ring, CSR_RING_NE_INT_MODE, data); >> + >> +out: >> + return ring; >> +} >> + >> +void xgene_enet_clear_ring(struct xgene_enet_desc_ring *ring) >> +{ >> + u32 data; >> + >> + if (IS_FP(ring->id) || RING_OWNER(ring) != RING_OWNER_CPU) >> + goto out; > > > And again, since we will bail out here, if (ring->id & 0x20) is true... > > >> + >> + xgene_enet_ring_rd32(ring, CSR_RING_NE_INT_MODE, &data); >> + data &= ~(u32) (1 << (31 - RING_BUFNUM(ring))); > > > Then RING_BUFNUM(ring) should always be 0 here, since I don't see > the 'bufnum' portion of ring->id being anything other than 0x20 or 0. > So why bother? > > > >> + xgene_enet_ring_wr32(ring, CSR_RING_NE_INT_MODE, data); >> + >> +out: >> + xgene_enet_clr_desc_ring_id(ring); >> + xgene_enet_clr_ring_state(ring); >> +} >> + > > > > <snip> > >> + >> +static int xgene_enet_phy_connect(struct net_device *ndev) >> +{ >> + struct xgene_enet_pdata *pdata = netdev_priv(ndev); >> + struct device_node *phy_np; >> + struct phy_device *phy_dev; > > > Initialize phy_dev to NULL here, to assist the addition of a 'goto' > below. I changed the code as per your suggestion and try to use goto only when common code needs to handled. So initializing phy_dev is not required. > > >> + int ret = 0; >> + >> + struct device *dev = &pdata->pdev->dev; >> + >> + phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0); >> + > > > Please remove the preceding blank line. > > > >> + if (!phy_np) { >> + netdev_dbg(ndev, "No phy-handle found\n"); >> + ret = -ENODEV; > > > The following line should be added here... > > goto out; >> >> + } >> + >> + phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link, >> >> + 0, pdata->phy_mode); >> + if (!phy_dev) { >> + netdev_err(ndev, "Could not connect to PHY\n"); >> + ret = -ENODEV; >> + goto out; >> + } >> + >> +out: >> + pdata->phy_link = 0; >> + pdata->phy_speed = 0; >> + pdata->phy_dev = phy_dev; >> + >> + return ret; >> +} >> + > > > > <snip> > >> diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h >> b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h >> new file mode 100644 >> index 0000000..a4c0a14 >> --- /dev/null >> +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.h > > > <snip> > >> + >> +static inline u32 get_bits(u32 val, u32 start, u32 end) >> +{ >> + return (val & GENMASK(end, start)) >> start; >> +} >> + >> +#define CSR_RING_ID 0x00000008 >> +#define OVERWRITE BIT(31) >> +#define IS_BUFFER_POOL BIT(20) >> +#define PREFETCH_BUF_EN BIT(21) >> +#define CSR_RING_ID_BUF 0x0000000c >> +#define CSR_RING_NE_INT_MODE 0x0000017c >> +#define CSR_RING_CONFIG 0x0000006c >> +#define CSR_RING_WR_BASE 0x00000070 >> +#define NUM_RING_CONFIG 5 >> +#define BUFPOOL_MODE 3 >> +#define RM3 3 >> +#define INC_DEC_CMD_ADDR 0x2c >> +#define IS_FP(x) ((x & 0x0020) ? 1 : 0) > > > IS_FP() is only ever called with 'ring->id' as the argument 'x'. > > And this macro should really be defined as... > > #define IS_FP(x) (((x) & 0x0020) ? 1 : 0) > > with a parentheses around the argument x. (And this holds true for > all your macros defined here, they should have parentheses around each > of their arguments in the body of the macro.) > > I will add paranthesis around each of the arguments. I changed IS_FP to function. > >> +#define UDP_HDR_SIZE 2 >> + >> +#define CREATE_MASK(pos, len) GENMASK(pos+len-1, pos) >> +#define CREATE_MASK_ULL(pos, len) GENMASK_ULL(pos+len-1, pos) > > > Add parentheses around args in the above two macros. > > > >> + >> +/* Empty slot soft signature */ >> +#define EMPTY_SLOT_INDEX 1 >> +#define EMPTY_SLOT ~0ULL >> + >> +#define RING_BUFNUM(q) (q->id & 0x003F) >> +#define RING_OWNER(q) ((q->id & 0x03C0) >> 6) > > > Add parentheses around args... > > #define RING_BUFNUM(q) ((q)->id & 0x003F) > #define RING_OWNER(q) (((q)->id & 0x3C0) >> 6) > > > Taking IS_FP(), together with RING_BUFNUM() and RING_OWNER(), I gather > that ring->id is... > > 0x03C0 owner > 0x0020 buf_pool flag > 0x001F bufnum > > But I don't see bufnum ever being set to anything other than 0. > Wherever RING_BUFNUM() is called, either a check for 0x0020 being set > precedes it (and if true returns), or 0x0020 is subtracted from it. So > that bit can't be playing a part in what one might consider the bufnum > to be. Is this correct? Or am I missing something (perhaps the obvious)? Subtraction of 0x20 is required for the bufpool bufnum, since the hardware expects in that format. > > >> +#define BUF_LEN_CODE_2K 0x5000 >> + > > > <snip> > >> diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c >> b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c >> new file mode 100644 >> index 0000000..0feb571 >> --- /dev/null >> +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c > > > > <snip> > >> + >> +static int xgene_enet_create_desc_rings(struct net_device *ndev) >> +{ >> + struct xgene_enet_pdata *pdata = netdev_priv(ndev); >> + struct device *dev = &pdata->pdev->dev; >> + struct xgene_enet_desc_ring *rx_ring, *tx_ring, *cp_ring; >> + struct xgene_enet_desc_ring *buf_pool = NULL; >> + u32 ring_num = 0; >> + u32 ring_id; >> + int ret = 0; >> + >> + /* allocate rx descriptor ring */ >> + ring_id = (RING_OWNER_CPU << 6) | RING_BUFNUM_REGULAR; > > > Here we see what will become the value of ring->id being set up > RING_BUFNUM_REGULAR is 0. I don't see a non-zero bufnum being set > here (or anywhere else). > > And this should be made into a macro and defined along side of > RING_BUFNUM() and RING_OWNER(). Perhaps something like... > > #define SET_RING_ID(owner, bufnum) ((owner) << 6) | (bufnum)) > > or some such. And you may consider changing these to functions for > the advantage that has over macros. The compiler can inline them. I added set_ring_id function. > > > >> + rx_ring = xgene_enet_create_desc_ring(ndev, ring_num++, >> + RING_CFGSIZE_16KB, ring_id); >> + if (IS_ERR_OR_NULL(rx_ring)) { >> + ret = PTR_ERR(rx_ring); >> + goto err; >> + } >> + >> + /* allocate buffer pool for receiving packets */ >> + ring_id = (RING_OWNER_ETH0 << 6) | RING_BUFNUM_BUFPOOL; > > > And again here, RING_BUFNUM_BUFPOOL is 0x20. But that's just a flag > that indicates that this ring is a buf_pool. I don't see a non-zero > bufnum being set here (or anywhere else). > > And a macro like 'SET_RING_ID()' as mentioned above, should be used > here. > > > >> + buf_pool = xgene_enet_create_desc_ring(ndev, ring_num++, >> + RING_CFGSIZE_2KB, ring_id); >> + if (IS_ERR_OR_NULL(buf_pool)) { >> + ret = PTR_ERR(buf_pool); >> + goto err; >> + } >> + >> + rx_ring->nbufpool = NUM_BUFPOOL; >> + rx_ring->buf_pool = buf_pool; >> + rx_ring->irq = pdata->rx_irq; >> + buf_pool->rx_skb = devm_kcalloc(dev, buf_pool->slots, >> + sizeof(struct sk_buff *), >> GFP_KERNEL); >> + if (!buf_pool->rx_skb) { >> + ret = -ENOMEM; >> + goto err; >> + } >> + >> + buf_pool->dst_ring_num = xgene_enet_dst_ring_num(buf_pool); >> + rx_ring->buf_pool = buf_pool; >> + pdata->rx_ring = rx_ring; >> + >> + /* allocate tx descriptor ring */ >> + ring_id = (RING_OWNER_ETH0 << 6) | RING_BUFNUM_REGULAR; > > > And again here. same story as above. > > > >> + tx_ring = xgene_enet_create_desc_ring(ndev, ring_num++, >> + RING_CFGSIZE_16KB, ring_id); >> + if (IS_ERR_OR_NULL(tx_ring)) { >> + ret = PTR_ERR(tx_ring); >> + goto err; >> + } >> + pdata->tx_ring = tx_ring; >> + >> + cp_ring = pdata->rx_ring; >> + cp_ring->cp_skb = devm_kcalloc(dev, tx_ring->slots, >> + sizeof(struct sk_buff *), >> GFP_KERNEL); >> + if (!cp_ring->cp_skb) { >> + ret = -ENOMEM; >> + goto err; >> + } >> + pdata->tx_ring->cp_ring = cp_ring; >> + pdata->tx_ring->dst_ring_num = xgene_enet_dst_ring_num(cp_ring); >> + >> + pdata->tx_qcnt_hi = pdata->tx_ring->slots / 2; >> + pdata->cp_qcnt_hi = pdata->rx_ring->slots / 2; >> + pdata->cp_qcnt_low = pdata->cp_qcnt_hi / 2; >> + >> + return 0; >> + >> +err: >> + xgene_enet_delete_desc_rings(pdata); >> + return ret; >> +} >> + > > > <snip> > >> + >> +static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) >> +{ >> + struct platform_device *pdev; >> + struct net_device *ndev; >> + struct device *dev; >> + struct resource *res; >> + void *base_addr; >> + const char *mac; >> + int ret = 0; >> + >> + pdev = pdata->pdev; >> + dev = &pdev->dev; >> + ndev = pdata->ndev; >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) { >> + dev_err(dev, "Resource IORESOURCE_MEM 0 not defined\n"); >> + ret = -ENODEV; >> + goto out; >> + } >> + pdata->base_addr = devm_ioremap_resource(dev, res); >> + if (IS_ERR(pdata->base_addr)) { >> + dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); >> + return PTR_ERR(pdata->base_addr); >> + } >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); >> + if (!res) { >> + dev_err(dev, "Resource IORESOURCE_MEM 1 not defined\n"); >> + ret = -ENODEV; >> + goto out; >> + } >> + pdata->ring_csr_addr = devm_ioremap_resource(dev, res); >> + if (IS_ERR(pdata->ring_csr_addr)) { >> + dev_err(dev, "Unable to retrieve ENET Ring CSR region\n"); >> + return PTR_ERR(pdata->ring_csr_addr); >> + } >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); >> + if (!res) { >> + dev_err(dev, "Resource IORESOURCE_MEM 2 not defined\n"); >> + ret = -ENODEV; >> + goto out; >> + } >> + pdata->ring_cmd_addr = devm_ioremap_resource(dev, res); >> + if (IS_ERR(pdata->ring_cmd_addr)) { >> + dev_err(dev, "Unable to retrieve ENET Ring command >> region\n"); >> + return PTR_ERR(pdata->ring_cmd_addr); >> + } >> + >> + ret = platform_get_irq(pdev, 0); >> + if (ret <= 0) { > > > If you return 0 as an error return value from this function, the caller > will have no idea anything was amiss. > > > >> + dev_err(dev, "Unable to get ENET Rx IRQ\n"); > > > So you need to at least convert the 0 to a sensible error return, > leaving the others as is... > > ret = ret ? : -ENXIO; > > or just reset them all.. > > ret = -ENXIO; > > You can chose the error return value that makes the most sense to you. > I've seen others use: -ENXIO, -EINVAL, and -ENODEV. Great catch. I have taken care of 0 case. > > > >> + goto out; >> + } >> + pdata->rx_irq = ret; >> + >> + mac = of_get_mac_address(dev->of_node); >> + if (mac) >> + memcpy(ndev->dev_addr, mac, ndev->addr_len); >> + else >> + eth_hw_addr_random(ndev); >> + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); >> + >> + pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node); >> + if (pdata->phy_mode < 0) { >> + dev_err(dev, "Incorrect phy-connection-type in DTS\n"); >> + ret = -EINVAL; >> + goto out; >> + } >> + >> + pdata->clk = devm_clk_get(&pdev->dev, NULL); >> + ret = IS_ERR(pdata->clk); > > > IS_ERR() does not yield a proper return error value. For that one needs > to use PTR_ERR(). So remove the preceding line, and change the following > line... > >> + if (ret) { > > > to... > > if (IS_ERR(pdata->clk)) { > > >> + dev_err(&pdev->dev, "can't get clock\n"); > > > And add the following line here... > > ret = PTR_ERR(info->clk); I modified the code as per your suggestion. > >> + goto out; >> + } >> + >> + base_addr = pdata->base_addr; >> + pdata->eth_csr_addr = base_addr + BLOCK_ETH_CSR_OFFSET; >> + pdata->eth_ring_if_addr = base_addr + BLOCK_ETH_RING_IF_OFFSET; >> + pdata->eth_diag_csr_addr = base_addr + BLOCK_ETH_DIAG_CSR_OFFSET; >> + pdata->mcx_mac_addr = base_addr + BLOCK_ETH_MAC_OFFSET; >> + pdata->mcx_stats_addr = base_addr + BLOCK_ETH_STATS_OFFSET; >> + pdata->mcx_mac_csr_addr = base_addr + BLOCK_ETH_MAC_CSR_OFFSET; >> + pdata->rx_buff_cnt = NUM_PKT_BUF; >> +out: >> + return ret; > > > The mixture of 'goto' and 'return' usage in this function is confusing. > I'd think it best if they were all the same. Because of the following, > which is stated in Documentation/CodingStyle (chapter 7)... > > The goto statement comes in handy when a function exits from multiple > locations and some common work such as cleanup has to be done. If there > is no > cleanup needed then just return directly. > > And since you don't have any common work being done, my vote is with > using returns and not gotos. > > I'd suggest you consider replacing gotos by returns in all functions > which simply return without having any common work to be done. I modified the code as per the coding guidelines. > > > >> +} >> + >> +static int xgene_enet_init_hw(struct xgene_enet_pdata *pdata) >> +{ >> + struct net_device *ndev = pdata->ndev; >> + struct xgene_enet_desc_ring *buf_pool; >> + int ret = 0; >> + >> + xgene_enet_reset(pdata); >> + >> + xgene_gmac_tx_disable(pdata); >> + xgene_gmac_rx_disable(pdata); >> + >> + ret = xgene_enet_create_desc_rings(ndev); >> + if (ret) { >> + netdev_err(ndev, "Error in ring configuration\n"); >> + goto out; >> + } >> + >> + /* setup buffer pool */ >> + buf_pool = pdata->rx_ring->buf_pool; >> + xgene_enet_init_bufpool(buf_pool); >> + ret = xgene_enet_refill_bufpool(buf_pool, pdata->rx_buff_cnt); >> + if (ret) >> + goto out; >> + >> + xgene_enet_cle_bypass(pdata, >> xgene_enet_dst_ring_num(pdata->rx_ring), >> + RING_BUFNUM(buf_pool) - 0x20, 0); > > > Subtracting an unidentified number (0x20) from RING_BUFNUM(buf_pool) > doesn't seem to me to be the best approach here. If I'm not mistaken, > it appears the 0x20 is a flag set in ring->id to indicate that this is > a buf_pool. And here you're trying to grab just the non-flag portion > of ring->id's 0x003f, which amounts to 0x001f. > > So maybe given the other things I've mentioned about RING_BUFNUM() in > this review, if it is still needed, change it to be... > > #define RING_BUFNUM(q) ((q)->id & 0x001F) > > Or since the 3rd argument to xgene_enet_cle_bypass() is called fpsel, > you might create a new macro called GET_FPSEL(), and make it like the > one just mentioned. I moved the complexity inside the function. > > But again, as mentioned elsewhere, the value will always be zero for > the driver as it is now. So is there a point to this? > > > >> + xgene_gmac_init(pdata, SPEED_1000); >> +out: >> + return ret; >> +} > > > <snip> -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html