Skip to content

Commit 7f71f22

Browse files
anssihgregkh
authored andcommitted
net: xilinx_emaclite: fix freezes due to unordered I/O
[ Upstream commit acf138f1b00bdd1b7cd9894562ed0c2a1670888e ] The xilinx_emaclite uses __raw_writel and __raw_readl for register accesses. Those functions do not imply any kind of memory barriers and they may be reordered. The driver does not seem to take that into account, though, and the driver does not satisfy the ordering requirements of the hardware. For clear examples, see xemaclite_mdio_write() and xemaclite_mdio_read() which try to set MDIO address before initiating the transaction. I'm seeing system freezes with the driver with GCC 5.4 and current Linux kernels on Zynq-7000 SoC immediately when trying to use the interface. In commit 123c140 ("net: emaclite: Do not use microblaze and ppc IO functions") the driver was switched from non-generic in_be32/out_be32 (memory barriers, big endian) to __raw_readl/__raw_writel (no memory barriers, native endian), so apparently the device follows system endianness and the driver was originally written with the assumption of memory barriers. Rather than try to hunt for each case of missing barrier, just switch the driver to use iowrite32/ioread32/iowrite32be/ioread32be depending on endianness instead. Tested on little-endian Zynq-7000 ARM SoC FPGA. Signed-off-by: Anssi Hannula <anssi.hannula@bitwise.fi> Fixes: 123c140 ("net: emaclite: Do not use microblaze and ppc IO functions") Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Sasha Levin <alexander.levin@verizon.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2ba464a commit 7f71f22

1 file changed

Lines changed: 62 additions & 54 deletions

File tree

drivers/net/ethernet/xilinx/xilinx_emaclite.c

Lines changed: 62 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@
100100
/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */
101101
#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT)
102102

103+
#ifdef __BIG_ENDIAN
104+
#define xemaclite_readl ioread32be
105+
#define xemaclite_writel iowrite32be
106+
#else
107+
#define xemaclite_readl ioread32
108+
#define xemaclite_writel iowrite32
109+
#endif
110+
103111
/**
104112
* struct net_local - Our private per device data
105113
* @ndev: instance of the network device
@@ -158,15 +166,15 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata)
158166
u32 reg_data;
159167

160168
/* Enable the Tx interrupts for the first Buffer */
161-
reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
162-
__raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
163-
drvdata->base_addr + XEL_TSR_OFFSET);
169+
reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
170+
xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK,
171+
drvdata->base_addr + XEL_TSR_OFFSET);
164172

165173
/* Enable the Rx interrupts for the first buffer */
166-
__raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
174+
xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET);
167175

168176
/* Enable the Global Interrupt Enable */
169-
__raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
177+
xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
170178
}
171179

172180
/**
@@ -181,17 +189,17 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata)
181189
u32 reg_data;
182190

183191
/* Disable the Global Interrupt Enable */
184-
__raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
192+
xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET);
185193

186194
/* Disable the Tx interrupts for the first buffer */
187-
reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET);
188-
__raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
189-
drvdata->base_addr + XEL_TSR_OFFSET);
195+
reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET);
196+
xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK),
197+
drvdata->base_addr + XEL_TSR_OFFSET);
190198

191199
/* Disable the Rx interrupts for the first buffer */
192-
reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET);
193-
__raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
194-
drvdata->base_addr + XEL_RSR_OFFSET);
200+
reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET);
201+
xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK),
202+
drvdata->base_addr + XEL_RSR_OFFSET);
195203
}
196204

197205
/**
@@ -323,7 +331,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
323331
byte_count = ETH_FRAME_LEN;
324332

325333
/* Check if the expected buffer is available */
326-
reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
334+
reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
327335
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
328336
XEL_TSR_XMIT_ACTIVE_MASK)) == 0) {
329337

@@ -336,7 +344,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
336344

337345
addr = (void __iomem __force *)((u32 __force)addr ^
338346
XEL_BUFFER_OFFSET);
339-
reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
347+
reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
340348

341349
if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK |
342350
XEL_TSR_XMIT_ACTIVE_MASK)) != 0)
@@ -347,16 +355,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data,
347355
/* Write the frame to the buffer */
348356
xemaclite_aligned_write(data, (u32 __force *) addr, byte_count);
349357

350-
__raw_writel((byte_count & XEL_TPLR_LENGTH_MASK),
351-
addr + XEL_TPLR_OFFSET);
358+
xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK),
359+
addr + XEL_TPLR_OFFSET);
352360

353361
/* Update the Tx Status Register to indicate that there is a
354362
* frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which
355363
* is used by the interrupt handler to check whether a frame
356364
* has been transmitted */
357-
reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
365+
reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
358366
reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK);
359-
__raw_writel(reg_data, addr + XEL_TSR_OFFSET);
367+
xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET);
360368

361369
return 0;
362370
}
@@ -381,7 +389,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
381389
addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use);
382390

383391
/* Verify which buffer has valid data */
384-
reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
392+
reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
385393

386394
if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) {
387395
if (drvdata->rx_ping_pong != 0)
@@ -398,14 +406,14 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
398406
return 0; /* No data was available */
399407

400408
/* Verify that buffer has valid data */
401-
reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
409+
reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
402410
if ((reg_data & XEL_RSR_RECV_DONE_MASK) !=
403411
XEL_RSR_RECV_DONE_MASK)
404412
return 0; /* No data was available */
405413
}
406414

407415
/* Get the protocol type of the ethernet frame that arrived */
408-
proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET +
416+
proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET +
409417
XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) &
410418
XEL_RPLR_LENGTH_MASK);
411419

@@ -414,7 +422,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
414422
if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) {
415423

416424
if (proto_type == ETH_P_IP) {
417-
length = ((ntohl(__raw_readl(addr +
425+
length = ((ntohl(xemaclite_readl(addr +
418426
XEL_HEADER_IP_LENGTH_OFFSET +
419427
XEL_RXBUFF_OFFSET)) >>
420428
XEL_HEADER_SHIFT) &
@@ -436,9 +444,9 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data)
436444
data, length);
437445

438446
/* Acknowledge the frame */
439-
reg_data = __raw_readl(addr + XEL_RSR_OFFSET);
447+
reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET);
440448
reg_data &= ~XEL_RSR_RECV_DONE_MASK;
441-
__raw_writel(reg_data, addr + XEL_RSR_OFFSET);
449+
xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET);
442450

443451
return length;
444452
}
@@ -465,14 +473,14 @@ static void xemaclite_update_address(struct net_local *drvdata,
465473

466474
xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN);
467475

468-
__raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
476+
xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET);
469477

470478
/* Update the MAC address in the EmacLite */
471-
reg_data = __raw_readl(addr + XEL_TSR_OFFSET);
472-
__raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
479+
reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET);
480+
xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET);
473481

474482
/* Wait for EmacLite to finish with the MAC address update */
475-
while ((__raw_readl(addr + XEL_TSR_OFFSET) &
483+
while ((xemaclite_readl(addr + XEL_TSR_OFFSET) &
476484
XEL_TSR_PROG_MAC_ADDR) != 0)
477485
;
478486
}
@@ -642,32 +650,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id)
642650
u32 tx_status;
643651

644652
/* Check if there is Rx Data available */
645-
if ((__raw_readl(base_addr + XEL_RSR_OFFSET) &
653+
if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) &
646654
XEL_RSR_RECV_DONE_MASK) ||
647-
(__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
655+
(xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET)
648656
& XEL_RSR_RECV_DONE_MASK))
649657

650658
xemaclite_rx_handler(dev);
651659

652660
/* Check if the Transmission for the first buffer is completed */
653-
tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET);
661+
tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET);
654662
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
655663
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
656664

657665
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
658-
__raw_writel(tx_status, base_addr + XEL_TSR_OFFSET);
666+
xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET);
659667

660668
tx_complete = true;
661669
}
662670

663671
/* Check if the Transmission for the second buffer is completed */
664-
tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
672+
tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
665673
if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) &&
666674
(tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) {
667675

668676
tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK;
669-
__raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
670-
XEL_TSR_OFFSET);
677+
xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET +
678+
XEL_TSR_OFFSET);
671679

672680
tx_complete = true;
673681
}
@@ -700,7 +708,7 @@ static int xemaclite_mdio_wait(struct net_local *lp)
700708
/* wait for the MDIO interface to not be busy or timeout
701709
after some time.
702710
*/
703-
while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
711+
while (xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) &
704712
XEL_MDIOCTRL_MDIOSTS_MASK) {
705713
if (time_before_eq(end, jiffies)) {
706714
WARN_ON(1);
@@ -736,17 +744,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg)
736744
* MDIO Address register. Set the Status bit in the MDIO Control
737745
* register to start a MDIO read transaction.
738746
*/
739-
ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
740-
__raw_writel(XEL_MDIOADDR_OP_MASK |
741-
((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
742-
lp->base_addr + XEL_MDIOADDR_OFFSET);
743-
__raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
744-
lp->base_addr + XEL_MDIOCTRL_OFFSET);
747+
ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
748+
xemaclite_writel(XEL_MDIOADDR_OP_MASK |
749+
((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
750+
lp->base_addr + XEL_MDIOADDR_OFFSET);
751+
xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
752+
lp->base_addr + XEL_MDIOCTRL_OFFSET);
745753

746754
if (xemaclite_mdio_wait(lp))
747755
return -ETIMEDOUT;
748756

749-
rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET);
757+
rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET);
750758

751759
dev_dbg(&lp->ndev->dev,
752760
"xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n",
@@ -783,13 +791,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg,
783791
* Data register. Finally, set the Status bit in the MDIO Control
784792
* register to start a MDIO write transaction.
785793
*/
786-
ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
787-
__raw_writel(~XEL_MDIOADDR_OP_MASK &
788-
((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
789-
lp->base_addr + XEL_MDIOADDR_OFFSET);
790-
__raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
791-
__raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
792-
lp->base_addr + XEL_MDIOCTRL_OFFSET);
794+
ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET);
795+
xemaclite_writel(~XEL_MDIOADDR_OP_MASK &
796+
((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg),
797+
lp->base_addr + XEL_MDIOADDR_OFFSET);
798+
xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET);
799+
xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK,
800+
lp->base_addr + XEL_MDIOCTRL_OFFSET);
793801

794802
return 0;
795803
}
@@ -836,8 +844,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev)
836844
/* Enable the MDIO bus by asserting the enable bit in MDIO Control
837845
* register.
838846
*/
839-
__raw_writel(XEL_MDIOCTRL_MDIOEN_MASK,
840-
lp->base_addr + XEL_MDIOCTRL_OFFSET);
847+
xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK,
848+
lp->base_addr + XEL_MDIOCTRL_OFFSET);
841849

842850
bus = mdiobus_alloc();
843851
if (!bus) {
@@ -1141,8 +1149,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev)
11411149
dev_warn(dev, "No MAC address found\n");
11421150

11431151
/* Clear the Tx CSR's in case this is a restart */
1144-
__raw_writel(0, lp->base_addr + XEL_TSR_OFFSET);
1145-
__raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
1152+
xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET);
1153+
xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET);
11461154

11471155
/* Set the MAC address in the EmacLite device */
11481156
xemaclite_update_address(lp, ndev->dev_addr);

0 commit comments

Comments
 (0)