Skip to content

Commit bf2c6b9

Browse files
mitadavem330
authored andcommitted
net: w5100: enable to support sleepable register access interface
SPI transfer routines are callable only from contexts that can sleep. This adds ability to tell the core driver that the interface mode cannot access w5100 register on atomic contexts. In this case, workqueue and threaded irq are required. This also corrects timeout period waiting for command register to be automatically cleared because the latency of the register access with SPI transfer can be interfered by other contexts. Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> Cc: Mike Sinkovsky <msink@permonline.ru> Cc: David S. Miller <davem@davemloft.net> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 850576c commit bf2c6b9

File tree

2 files changed

+153
-38
lines changed

2 files changed

+153
-38
lines changed

drivers/net/ethernet/wiznet/w5100.c

Lines changed: 152 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ struct w5100_priv {
9696
struct net_device *ndev;
9797
bool promisc;
9898
u32 msg_enable;
99+
100+
struct workqueue_struct *xfer_wq;
101+
struct work_struct rx_work;
102+
struct sk_buff *tx_skb;
103+
struct work_struct tx_work;
104+
struct work_struct setrx_work;
105+
struct work_struct restart_work;
99106
};
100107

101108
/************************************************************************
@@ -502,10 +509,12 @@ static int w5100_reset(struct w5100_priv *priv)
502509

503510
static int w5100_command(struct w5100_priv *priv, u16 cmd)
504511
{
505-
unsigned long timeout = jiffies + msecs_to_jiffies(100);
512+
unsigned long timeout;
506513

507514
w5100_write(priv, W5100_S0_CR, cmd);
508515

516+
timeout = jiffies + msecs_to_jiffies(100);
517+
509518
while (w5100_read(priv, W5100_S0_CR) != 0) {
510519
if (time_after(jiffies, timeout))
511520
return -EIO;
@@ -605,7 +614,7 @@ static void w5100_get_regs(struct net_device *ndev,
605614
w5100_readbulk(priv, W5100_S0_REGS, buf, W5100_S0_REGS_LEN);
606615
}
607616

608-
static void w5100_tx_timeout(struct net_device *ndev)
617+
static void w5100_restart(struct net_device *ndev)
609618
{
610619
struct w5100_priv *priv = netdev_priv(ndev);
611620

@@ -617,12 +626,28 @@ static void w5100_tx_timeout(struct net_device *ndev)
617626
netif_wake_queue(ndev);
618627
}
619628

620-
static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
629+
static void w5100_restart_work(struct work_struct *work)
630+
{
631+
struct w5100_priv *priv = container_of(work, struct w5100_priv,
632+
restart_work);
633+
634+
w5100_restart(priv->ndev);
635+
}
636+
637+
static void w5100_tx_timeout(struct net_device *ndev)
621638
{
622639
struct w5100_priv *priv = netdev_priv(ndev);
623-
u16 offset;
624640

625-
netif_stop_queue(ndev);
641+
if (priv->ops->may_sleep)
642+
schedule_work(&priv->restart_work);
643+
else
644+
w5100_restart(ndev);
645+
}
646+
647+
static void w5100_tx_skb(struct net_device *ndev, struct sk_buff *skb)
648+
{
649+
struct w5100_priv *priv = netdev_priv(ndev);
650+
u16 offset;
626651

627652
offset = w5100_read16(priv, W5100_S0_TX_WR);
628653
w5100_writebuf(priv, offset, skb->data, skb->len);
@@ -632,47 +657,98 @@ static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
632657
dev_kfree_skb(skb);
633658

634659
w5100_command(priv, S0_CR_SEND);
660+
}
661+
662+
static void w5100_tx_work(struct work_struct *work)
663+
{
664+
struct w5100_priv *priv = container_of(work, struct w5100_priv,
665+
tx_work);
666+
struct sk_buff *skb = priv->tx_skb;
667+
668+
priv->tx_skb = NULL;
669+
670+
if (WARN_ON(!skb))
671+
return;
672+
w5100_tx_skb(priv->ndev, skb);
673+
}
674+
675+
static int w5100_start_tx(struct sk_buff *skb, struct net_device *ndev)
676+
{
677+
struct w5100_priv *priv = netdev_priv(ndev);
678+
679+
netif_stop_queue(ndev);
680+
681+
if (priv->ops->may_sleep) {
682+
WARN_ON(priv->tx_skb);
683+
priv->tx_skb = skb;
684+
queue_work(priv->xfer_wq, &priv->tx_work);
685+
} else {
686+
w5100_tx_skb(ndev, skb);
687+
}
635688

636689
return NETDEV_TX_OK;
637690
}
638691

639-
static int w5100_napi_poll(struct napi_struct *napi, int budget)
692+
static struct sk_buff *w5100_rx_skb(struct net_device *ndev)
640693
{
641-
struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
642-
struct net_device *ndev = priv->ndev;
694+
struct w5100_priv *priv = netdev_priv(ndev);
643695
struct sk_buff *skb;
644-
int rx_count;
645696
u16 rx_len;
646697
u16 offset;
647698
u8 header[2];
699+
u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR);
648700

649-
for (rx_count = 0; rx_count < budget; rx_count++) {
650-
u16 rx_buf_len = w5100_read16(priv, W5100_S0_RX_RSR);
651-
if (rx_buf_len == 0)
652-
break;
701+
if (rx_buf_len == 0)
702+
return NULL;
653703

654-
offset = w5100_read16(priv, W5100_S0_RX_RD);
655-
w5100_readbuf(priv, offset, header, 2);
656-
rx_len = get_unaligned_be16(header) - 2;
657-
658-
skb = netdev_alloc_skb_ip_align(ndev, rx_len);
659-
if (unlikely(!skb)) {
660-
w5100_write16(priv, W5100_S0_RX_RD,
661-
offset + rx_buf_len);
662-
w5100_command(priv, S0_CR_RECV);
663-
ndev->stats.rx_dropped++;
664-
return -ENOMEM;
665-
}
704+
offset = w5100_read16(priv, W5100_S0_RX_RD);
705+
w5100_readbuf(priv, offset, header, 2);
706+
rx_len = get_unaligned_be16(header) - 2;
666707

667-
skb_put(skb, rx_len);
668-
w5100_readbuf(priv, offset + 2, skb->data, rx_len);
669-
w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
708+
skb = netdev_alloc_skb_ip_align(ndev, rx_len);
709+
if (unlikely(!skb)) {
710+
w5100_write16(priv, W5100_S0_RX_RD, offset + rx_buf_len);
670711
w5100_command(priv, S0_CR_RECV);
671-
skb->protocol = eth_type_trans(skb, ndev);
712+
ndev->stats.rx_dropped++;
713+
return NULL;
714+
}
715+
716+
skb_put(skb, rx_len);
717+
w5100_readbuf(priv, offset + 2, skb->data, rx_len);
718+
w5100_write16(priv, W5100_S0_RX_RD, offset + 2 + rx_len);
719+
w5100_command(priv, S0_CR_RECV);
720+
skb->protocol = eth_type_trans(skb, ndev);
721+
722+
ndev->stats.rx_packets++;
723+
ndev->stats.rx_bytes += rx_len;
724+
725+
return skb;
726+
}
727+
728+
static void w5100_rx_work(struct work_struct *work)
729+
{
730+
struct w5100_priv *priv = container_of(work, struct w5100_priv,
731+
rx_work);
732+
struct sk_buff *skb;
733+
734+
while ((skb = w5100_rx_skb(priv->ndev)))
735+
netif_rx_ni(skb);
736+
737+
w5100_write(priv, W5100_IMR, IR_S0);
738+
}
739+
740+
static int w5100_napi_poll(struct napi_struct *napi, int budget)
741+
{
742+
struct w5100_priv *priv = container_of(napi, struct w5100_priv, napi);
743+
int rx_count;
744+
745+
for (rx_count = 0; rx_count < budget; rx_count++) {
746+
struct sk_buff *skb = w5100_rx_skb(priv->ndev);
672747

673-
netif_receive_skb(skb);
674-
ndev->stats.rx_packets++;
675-
ndev->stats.rx_bytes += rx_len;
748+
if (skb)
749+
netif_receive_skb(skb);
750+
else
751+
break;
676752
}
677753

678754
if (rx_count < budget) {
@@ -699,10 +775,12 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
699775
}
700776

701777
if (ir & S0_IR_RECV) {
702-
if (napi_schedule_prep(&priv->napi)) {
703-
w5100_write(priv, W5100_IMR, 0);
778+
w5100_write(priv, W5100_IMR, 0);
779+
780+
if (priv->ops->may_sleep)
781+
queue_work(priv->xfer_wq, &priv->rx_work);
782+
else if (napi_schedule_prep(&priv->napi))
704783
__napi_schedule(&priv->napi);
705-
}
706784
}
707785

708786
return IRQ_HANDLED;
@@ -726,14 +804,26 @@ static irqreturn_t w5100_detect_link(int irq, void *ndev_instance)
726804
return IRQ_HANDLED;
727805
}
728806

807+
static void w5100_setrx_work(struct work_struct *work)
808+
{
809+
struct w5100_priv *priv = container_of(work, struct w5100_priv,
810+
setrx_work);
811+
812+
w5100_hw_start(priv);
813+
}
814+
729815
static void w5100_set_rx_mode(struct net_device *ndev)
730816
{
731817
struct w5100_priv *priv = netdev_priv(ndev);
732818
bool set_promisc = (ndev->flags & IFF_PROMISC) != 0;
733819

734820
if (priv->promisc != set_promisc) {
735821
priv->promisc = set_promisc;
736-
w5100_hw_start(priv);
822+
823+
if (priv->ops->may_sleep)
824+
schedule_work(&priv->setrx_work);
825+
else
826+
w5100_hw_start(priv);
737827
}
738828
}
739829

@@ -872,6 +962,17 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops,
872962
if (err < 0)
873963
goto err_register;
874964

965+
priv->xfer_wq = create_workqueue(netdev_name(ndev));
966+
if (!priv->xfer_wq) {
967+
err = -ENOMEM;
968+
goto err_wq;
969+
}
970+
971+
INIT_WORK(&priv->rx_work, w5100_rx_work);
972+
INIT_WORK(&priv->tx_work, w5100_tx_work);
973+
INIT_WORK(&priv->setrx_work, w5100_setrx_work);
974+
INIT_WORK(&priv->restart_work, w5100_restart_work);
975+
875976
if (mac_addr)
876977
memcpy(ndev->dev_addr, mac_addr, ETH_ALEN);
877978
else
@@ -889,8 +990,14 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops,
889990
goto err_hw;
890991
}
891992

892-
err = request_irq(priv->irq, w5100_interrupt, IRQF_TRIGGER_LOW,
893-
netdev_name(ndev), ndev);
993+
if (ops->may_sleep) {
994+
err = request_threaded_irq(priv->irq, NULL, w5100_interrupt,
995+
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
996+
netdev_name(ndev), ndev);
997+
} else {
998+
err = request_irq(priv->irq, w5100_interrupt,
999+
IRQF_TRIGGER_LOW, netdev_name(ndev), ndev);
1000+
}
8941001
if (err)
8951002
goto err_hw;
8961003

@@ -915,6 +1022,8 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops,
9151022
err_gpio:
9161023
free_irq(priv->irq, ndev);
9171024
err_hw:
1025+
destroy_workqueue(priv->xfer_wq);
1026+
err_wq:
9181027
unregister_netdev(ndev);
9191028
err_register:
9201029
free_netdev(ndev);
@@ -932,6 +1041,11 @@ int w5100_remove(struct device *dev)
9321041
if (gpio_is_valid(priv->link_gpio))
9331042
free_irq(priv->link_irq, ndev);
9341043

1044+
flush_work(&priv->setrx_work);
1045+
flush_work(&priv->restart_work);
1046+
flush_workqueue(priv->xfer_wq);
1047+
destroy_workqueue(priv->xfer_wq);
1048+
9351049
unregister_netdev(ndev);
9361050
free_netdev(ndev);
9371051
return 0;

drivers/net/ethernet/wiznet/w5100.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
struct w5100_ops {
11+
bool may_sleep;
1112
int (*read)(struct net_device *ndev, u16 addr);
1213
int (*write)(struct net_device *ndev, u16 addr, u8 data);
1314
int (*read16)(struct net_device *ndev, u16 addr);

0 commit comments

Comments
 (0)