121a0ffd9SStephan Gerhold // SPDX-License-Identifier: GPL-2.0-only
221a0ffd9SStephan Gerhold /*
321a0ffd9SStephan Gerhold * Qualcomm BAM-DMUX WWAN network driver
421a0ffd9SStephan Gerhold * Copyright (c) 2020, Stephan Gerhold <stephan@gerhold.net>
521a0ffd9SStephan Gerhold */
621a0ffd9SStephan Gerhold
721a0ffd9SStephan Gerhold #include <linux/atomic.h>
821a0ffd9SStephan Gerhold #include <linux/bitops.h>
921a0ffd9SStephan Gerhold #include <linux/completion.h>
1021a0ffd9SStephan Gerhold #include <linux/dma-mapping.h>
1121a0ffd9SStephan Gerhold #include <linux/dmaengine.h>
1221a0ffd9SStephan Gerhold #include <linux/if_arp.h>
1321a0ffd9SStephan Gerhold #include <linux/interrupt.h>
1421a0ffd9SStephan Gerhold #include <linux/mod_devicetable.h>
1521a0ffd9SStephan Gerhold #include <linux/module.h>
1621a0ffd9SStephan Gerhold #include <linux/netdevice.h>
1721a0ffd9SStephan Gerhold #include <linux/platform_device.h>
1821a0ffd9SStephan Gerhold #include <linux/pm_runtime.h>
1921a0ffd9SStephan Gerhold #include <linux/soc/qcom/smem_state.h>
2021a0ffd9SStephan Gerhold #include <linux/spinlock.h>
2121a0ffd9SStephan Gerhold #include <linux/wait.h>
2221a0ffd9SStephan Gerhold #include <linux/workqueue.h>
2321a0ffd9SStephan Gerhold #include <net/pkt_sched.h>
2421a0ffd9SStephan Gerhold
2521a0ffd9SStephan Gerhold #define BAM_DMUX_BUFFER_SIZE SZ_2K
2621a0ffd9SStephan Gerhold #define BAM_DMUX_HDR_SIZE sizeof(struct bam_dmux_hdr)
2721a0ffd9SStephan Gerhold #define BAM_DMUX_MAX_DATA_SIZE (BAM_DMUX_BUFFER_SIZE - BAM_DMUX_HDR_SIZE)
2821a0ffd9SStephan Gerhold #define BAM_DMUX_NUM_SKB 32
2921a0ffd9SStephan Gerhold
3021a0ffd9SStephan Gerhold #define BAM_DMUX_HDR_MAGIC 0x33fc
3121a0ffd9SStephan Gerhold
3221a0ffd9SStephan Gerhold #define BAM_DMUX_AUTOSUSPEND_DELAY 1000
3321a0ffd9SStephan Gerhold #define BAM_DMUX_REMOTE_TIMEOUT msecs_to_jiffies(2000)
3421a0ffd9SStephan Gerhold
3521a0ffd9SStephan Gerhold enum {
3621a0ffd9SStephan Gerhold BAM_DMUX_CMD_DATA,
3721a0ffd9SStephan Gerhold BAM_DMUX_CMD_OPEN,
3821a0ffd9SStephan Gerhold BAM_DMUX_CMD_CLOSE,
3921a0ffd9SStephan Gerhold };
4021a0ffd9SStephan Gerhold
4121a0ffd9SStephan Gerhold enum {
4221a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_0,
4321a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_1,
4421a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_2,
4521a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_3,
4621a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_4,
4721a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_5,
4821a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_6,
4921a0ffd9SStephan Gerhold BAM_DMUX_CH_DATA_7,
5021a0ffd9SStephan Gerhold BAM_DMUX_NUM_CH
5121a0ffd9SStephan Gerhold };
5221a0ffd9SStephan Gerhold
5321a0ffd9SStephan Gerhold struct bam_dmux_hdr {
5421a0ffd9SStephan Gerhold u16 magic;
5521a0ffd9SStephan Gerhold u8 signal;
5621a0ffd9SStephan Gerhold u8 cmd;
5721a0ffd9SStephan Gerhold u8 pad;
5821a0ffd9SStephan Gerhold u8 ch;
5921a0ffd9SStephan Gerhold u16 len;
6021a0ffd9SStephan Gerhold };
6121a0ffd9SStephan Gerhold
6221a0ffd9SStephan Gerhold struct bam_dmux_skb_dma {
6321a0ffd9SStephan Gerhold struct bam_dmux *dmux;
6421a0ffd9SStephan Gerhold struct sk_buff *skb;
6521a0ffd9SStephan Gerhold dma_addr_t addr;
6621a0ffd9SStephan Gerhold };
6721a0ffd9SStephan Gerhold
6821a0ffd9SStephan Gerhold struct bam_dmux {
6921a0ffd9SStephan Gerhold struct device *dev;
7021a0ffd9SStephan Gerhold
7121a0ffd9SStephan Gerhold int pc_irq;
7221a0ffd9SStephan Gerhold bool pc_state, pc_ack_state;
7321a0ffd9SStephan Gerhold struct qcom_smem_state *pc, *pc_ack;
7421a0ffd9SStephan Gerhold u32 pc_mask, pc_ack_mask;
7521a0ffd9SStephan Gerhold wait_queue_head_t pc_wait;
7621a0ffd9SStephan Gerhold struct completion pc_ack_completion;
7721a0ffd9SStephan Gerhold
7821a0ffd9SStephan Gerhold struct dma_chan *rx, *tx;
7921a0ffd9SStephan Gerhold struct bam_dmux_skb_dma rx_skbs[BAM_DMUX_NUM_SKB];
8021a0ffd9SStephan Gerhold struct bam_dmux_skb_dma tx_skbs[BAM_DMUX_NUM_SKB];
8121a0ffd9SStephan Gerhold spinlock_t tx_lock; /* Protect tx_skbs, tx_next_skb */
8221a0ffd9SStephan Gerhold unsigned int tx_next_skb;
8321a0ffd9SStephan Gerhold atomic_long_t tx_deferred_skb;
8421a0ffd9SStephan Gerhold struct work_struct tx_wakeup_work;
8521a0ffd9SStephan Gerhold
8621a0ffd9SStephan Gerhold DECLARE_BITMAP(remote_channels, BAM_DMUX_NUM_CH);
8721a0ffd9SStephan Gerhold struct work_struct register_netdev_work;
8821a0ffd9SStephan Gerhold struct net_device *netdevs[BAM_DMUX_NUM_CH];
8921a0ffd9SStephan Gerhold };
9021a0ffd9SStephan Gerhold
9121a0ffd9SStephan Gerhold struct bam_dmux_netdev {
9221a0ffd9SStephan Gerhold struct bam_dmux *dmux;
9321a0ffd9SStephan Gerhold u8 ch;
9421a0ffd9SStephan Gerhold };
9521a0ffd9SStephan Gerhold
bam_dmux_pc_vote(struct bam_dmux * dmux,bool enable)9621a0ffd9SStephan Gerhold static void bam_dmux_pc_vote(struct bam_dmux *dmux, bool enable)
9721a0ffd9SStephan Gerhold {
9821a0ffd9SStephan Gerhold reinit_completion(&dmux->pc_ack_completion);
9921a0ffd9SStephan Gerhold qcom_smem_state_update_bits(dmux->pc, dmux->pc_mask,
10021a0ffd9SStephan Gerhold enable ? dmux->pc_mask : 0);
10121a0ffd9SStephan Gerhold }
10221a0ffd9SStephan Gerhold
bam_dmux_pc_ack(struct bam_dmux * dmux)10321a0ffd9SStephan Gerhold static void bam_dmux_pc_ack(struct bam_dmux *dmux)
10421a0ffd9SStephan Gerhold {
10521a0ffd9SStephan Gerhold qcom_smem_state_update_bits(dmux->pc_ack, dmux->pc_ack_mask,
10621a0ffd9SStephan Gerhold dmux->pc_ack_state ? 0 : dmux->pc_ack_mask);
10721a0ffd9SStephan Gerhold dmux->pc_ack_state = !dmux->pc_ack_state;
10821a0ffd9SStephan Gerhold }
10921a0ffd9SStephan Gerhold
bam_dmux_skb_dma_map(struct bam_dmux_skb_dma * skb_dma,enum dma_data_direction dir)11021a0ffd9SStephan Gerhold static bool bam_dmux_skb_dma_map(struct bam_dmux_skb_dma *skb_dma,
11121a0ffd9SStephan Gerhold enum dma_data_direction dir)
11221a0ffd9SStephan Gerhold {
11321a0ffd9SStephan Gerhold struct device *dev = skb_dma->dmux->dev;
11421a0ffd9SStephan Gerhold
11521a0ffd9SStephan Gerhold skb_dma->addr = dma_map_single(dev, skb_dma->skb->data, skb_dma->skb->len, dir);
11621a0ffd9SStephan Gerhold if (dma_mapping_error(dev, skb_dma->addr)) {
11721a0ffd9SStephan Gerhold dev_err(dev, "Failed to DMA map buffer\n");
11821a0ffd9SStephan Gerhold skb_dma->addr = 0;
11921a0ffd9SStephan Gerhold return false;
12021a0ffd9SStephan Gerhold }
12121a0ffd9SStephan Gerhold
12221a0ffd9SStephan Gerhold return true;
12321a0ffd9SStephan Gerhold }
12421a0ffd9SStephan Gerhold
bam_dmux_skb_dma_unmap(struct bam_dmux_skb_dma * skb_dma,enum dma_data_direction dir)12521a0ffd9SStephan Gerhold static void bam_dmux_skb_dma_unmap(struct bam_dmux_skb_dma *skb_dma,
12621a0ffd9SStephan Gerhold enum dma_data_direction dir)
12721a0ffd9SStephan Gerhold {
12821a0ffd9SStephan Gerhold dma_unmap_single(skb_dma->dmux->dev, skb_dma->addr, skb_dma->skb->len, dir);
12921a0ffd9SStephan Gerhold skb_dma->addr = 0;
13021a0ffd9SStephan Gerhold }
13121a0ffd9SStephan Gerhold
bam_dmux_tx_wake_queues(struct bam_dmux * dmux)13221a0ffd9SStephan Gerhold static void bam_dmux_tx_wake_queues(struct bam_dmux *dmux)
13321a0ffd9SStephan Gerhold {
13421a0ffd9SStephan Gerhold int i;
13521a0ffd9SStephan Gerhold
13621a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "wake queues\n");
13721a0ffd9SStephan Gerhold
13821a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_CH; ++i) {
13921a0ffd9SStephan Gerhold struct net_device *netdev = dmux->netdevs[i];
14021a0ffd9SStephan Gerhold
14121a0ffd9SStephan Gerhold if (netdev && netif_running(netdev))
14221a0ffd9SStephan Gerhold netif_wake_queue(netdev);
14321a0ffd9SStephan Gerhold }
14421a0ffd9SStephan Gerhold }
14521a0ffd9SStephan Gerhold
bam_dmux_tx_stop_queues(struct bam_dmux * dmux)14621a0ffd9SStephan Gerhold static void bam_dmux_tx_stop_queues(struct bam_dmux *dmux)
14721a0ffd9SStephan Gerhold {
14821a0ffd9SStephan Gerhold int i;
14921a0ffd9SStephan Gerhold
15021a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "stop queues\n");
15121a0ffd9SStephan Gerhold
15221a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_CH; ++i) {
15321a0ffd9SStephan Gerhold struct net_device *netdev = dmux->netdevs[i];
15421a0ffd9SStephan Gerhold
15521a0ffd9SStephan Gerhold if (netdev)
15621a0ffd9SStephan Gerhold netif_stop_queue(netdev);
15721a0ffd9SStephan Gerhold }
15821a0ffd9SStephan Gerhold }
15921a0ffd9SStephan Gerhold
bam_dmux_tx_done(struct bam_dmux_skb_dma * skb_dma)16021a0ffd9SStephan Gerhold static void bam_dmux_tx_done(struct bam_dmux_skb_dma *skb_dma)
16121a0ffd9SStephan Gerhold {
16221a0ffd9SStephan Gerhold struct bam_dmux *dmux = skb_dma->dmux;
16321a0ffd9SStephan Gerhold unsigned long flags;
16421a0ffd9SStephan Gerhold
16521a0ffd9SStephan Gerhold pm_runtime_mark_last_busy(dmux->dev);
16621a0ffd9SStephan Gerhold pm_runtime_put_autosuspend(dmux->dev);
16721a0ffd9SStephan Gerhold
16821a0ffd9SStephan Gerhold if (skb_dma->addr)
16921a0ffd9SStephan Gerhold bam_dmux_skb_dma_unmap(skb_dma, DMA_TO_DEVICE);
17021a0ffd9SStephan Gerhold
17121a0ffd9SStephan Gerhold spin_lock_irqsave(&dmux->tx_lock, flags);
17221a0ffd9SStephan Gerhold skb_dma->skb = NULL;
17321a0ffd9SStephan Gerhold if (skb_dma == &dmux->tx_skbs[dmux->tx_next_skb % BAM_DMUX_NUM_SKB])
17421a0ffd9SStephan Gerhold bam_dmux_tx_wake_queues(dmux);
17521a0ffd9SStephan Gerhold spin_unlock_irqrestore(&dmux->tx_lock, flags);
17621a0ffd9SStephan Gerhold }
17721a0ffd9SStephan Gerhold
bam_dmux_tx_callback(void * data)17821a0ffd9SStephan Gerhold static void bam_dmux_tx_callback(void *data)
17921a0ffd9SStephan Gerhold {
18021a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma = data;
18121a0ffd9SStephan Gerhold struct sk_buff *skb = skb_dma->skb;
18221a0ffd9SStephan Gerhold
18321a0ffd9SStephan Gerhold bam_dmux_tx_done(skb_dma);
18421a0ffd9SStephan Gerhold dev_consume_skb_any(skb);
18521a0ffd9SStephan Gerhold }
18621a0ffd9SStephan Gerhold
bam_dmux_skb_dma_submit_tx(struct bam_dmux_skb_dma * skb_dma)18721a0ffd9SStephan Gerhold static bool bam_dmux_skb_dma_submit_tx(struct bam_dmux_skb_dma *skb_dma)
18821a0ffd9SStephan Gerhold {
18921a0ffd9SStephan Gerhold struct bam_dmux *dmux = skb_dma->dmux;
19021a0ffd9SStephan Gerhold struct dma_async_tx_descriptor *desc;
19121a0ffd9SStephan Gerhold
19221a0ffd9SStephan Gerhold desc = dmaengine_prep_slave_single(dmux->tx, skb_dma->addr,
19321a0ffd9SStephan Gerhold skb_dma->skb->len, DMA_MEM_TO_DEV,
19421a0ffd9SStephan Gerhold DMA_PREP_INTERRUPT);
19521a0ffd9SStephan Gerhold if (!desc) {
19621a0ffd9SStephan Gerhold dev_err(dmux->dev, "Failed to prepare TX DMA buffer\n");
19721a0ffd9SStephan Gerhold return false;
19821a0ffd9SStephan Gerhold }
19921a0ffd9SStephan Gerhold
20021a0ffd9SStephan Gerhold desc->callback = bam_dmux_tx_callback;
20121a0ffd9SStephan Gerhold desc->callback_param = skb_dma;
20221a0ffd9SStephan Gerhold desc->cookie = dmaengine_submit(desc);
20321a0ffd9SStephan Gerhold return true;
20421a0ffd9SStephan Gerhold }
20521a0ffd9SStephan Gerhold
20621a0ffd9SStephan Gerhold static struct bam_dmux_skb_dma *
bam_dmux_tx_queue(struct bam_dmux * dmux,struct sk_buff * skb)20721a0ffd9SStephan Gerhold bam_dmux_tx_queue(struct bam_dmux *dmux, struct sk_buff *skb)
20821a0ffd9SStephan Gerhold {
20921a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma;
21021a0ffd9SStephan Gerhold unsigned long flags;
21121a0ffd9SStephan Gerhold
21221a0ffd9SStephan Gerhold spin_lock_irqsave(&dmux->tx_lock, flags);
21321a0ffd9SStephan Gerhold
21421a0ffd9SStephan Gerhold skb_dma = &dmux->tx_skbs[dmux->tx_next_skb % BAM_DMUX_NUM_SKB];
21521a0ffd9SStephan Gerhold if (skb_dma->skb) {
21621a0ffd9SStephan Gerhold bam_dmux_tx_stop_queues(dmux);
21721a0ffd9SStephan Gerhold spin_unlock_irqrestore(&dmux->tx_lock, flags);
21821a0ffd9SStephan Gerhold return NULL;
21921a0ffd9SStephan Gerhold }
22021a0ffd9SStephan Gerhold skb_dma->skb = skb;
22121a0ffd9SStephan Gerhold
22221a0ffd9SStephan Gerhold dmux->tx_next_skb++;
22321a0ffd9SStephan Gerhold if (dmux->tx_skbs[dmux->tx_next_skb % BAM_DMUX_NUM_SKB].skb)
22421a0ffd9SStephan Gerhold bam_dmux_tx_stop_queues(dmux);
22521a0ffd9SStephan Gerhold
22621a0ffd9SStephan Gerhold spin_unlock_irqrestore(&dmux->tx_lock, flags);
22721a0ffd9SStephan Gerhold return skb_dma;
22821a0ffd9SStephan Gerhold }
22921a0ffd9SStephan Gerhold
bam_dmux_send_cmd(struct bam_dmux_netdev * bndev,u8 cmd)23021a0ffd9SStephan Gerhold static int bam_dmux_send_cmd(struct bam_dmux_netdev *bndev, u8 cmd)
23121a0ffd9SStephan Gerhold {
23221a0ffd9SStephan Gerhold struct bam_dmux *dmux = bndev->dmux;
23321a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma;
23421a0ffd9SStephan Gerhold struct bam_dmux_hdr *hdr;
23521a0ffd9SStephan Gerhold struct sk_buff *skb;
23621a0ffd9SStephan Gerhold int ret;
23721a0ffd9SStephan Gerhold
23821a0ffd9SStephan Gerhold skb = alloc_skb(sizeof(*hdr), GFP_KERNEL);
23921a0ffd9SStephan Gerhold if (!skb)
24021a0ffd9SStephan Gerhold return -ENOMEM;
24121a0ffd9SStephan Gerhold
24221a0ffd9SStephan Gerhold hdr = skb_put_zero(skb, sizeof(*hdr));
24321a0ffd9SStephan Gerhold hdr->magic = BAM_DMUX_HDR_MAGIC;
24421a0ffd9SStephan Gerhold hdr->cmd = cmd;
24521a0ffd9SStephan Gerhold hdr->ch = bndev->ch;
24621a0ffd9SStephan Gerhold
24721a0ffd9SStephan Gerhold skb_dma = bam_dmux_tx_queue(dmux, skb);
24821a0ffd9SStephan Gerhold if (!skb_dma) {
24921a0ffd9SStephan Gerhold ret = -EAGAIN;
25021a0ffd9SStephan Gerhold goto free_skb;
25121a0ffd9SStephan Gerhold }
25221a0ffd9SStephan Gerhold
25321a0ffd9SStephan Gerhold ret = pm_runtime_get_sync(dmux->dev);
25421a0ffd9SStephan Gerhold if (ret < 0)
25521a0ffd9SStephan Gerhold goto tx_fail;
25621a0ffd9SStephan Gerhold
25721a0ffd9SStephan Gerhold if (!bam_dmux_skb_dma_map(skb_dma, DMA_TO_DEVICE)) {
25821a0ffd9SStephan Gerhold ret = -ENOMEM;
25921a0ffd9SStephan Gerhold goto tx_fail;
26021a0ffd9SStephan Gerhold }
26121a0ffd9SStephan Gerhold
26221a0ffd9SStephan Gerhold if (!bam_dmux_skb_dma_submit_tx(skb_dma)) {
26321a0ffd9SStephan Gerhold ret = -EIO;
26421a0ffd9SStephan Gerhold goto tx_fail;
26521a0ffd9SStephan Gerhold }
26621a0ffd9SStephan Gerhold
26721a0ffd9SStephan Gerhold dma_async_issue_pending(dmux->tx);
26821a0ffd9SStephan Gerhold return 0;
26921a0ffd9SStephan Gerhold
27021a0ffd9SStephan Gerhold tx_fail:
27121a0ffd9SStephan Gerhold bam_dmux_tx_done(skb_dma);
27221a0ffd9SStephan Gerhold free_skb:
27321a0ffd9SStephan Gerhold dev_kfree_skb(skb);
27421a0ffd9SStephan Gerhold return ret;
27521a0ffd9SStephan Gerhold }
27621a0ffd9SStephan Gerhold
bam_dmux_netdev_open(struct net_device * netdev)27721a0ffd9SStephan Gerhold static int bam_dmux_netdev_open(struct net_device *netdev)
27821a0ffd9SStephan Gerhold {
27921a0ffd9SStephan Gerhold struct bam_dmux_netdev *bndev = netdev_priv(netdev);
28021a0ffd9SStephan Gerhold int ret;
28121a0ffd9SStephan Gerhold
28221a0ffd9SStephan Gerhold ret = bam_dmux_send_cmd(bndev, BAM_DMUX_CMD_OPEN);
28321a0ffd9SStephan Gerhold if (ret)
28421a0ffd9SStephan Gerhold return ret;
28521a0ffd9SStephan Gerhold
28621a0ffd9SStephan Gerhold netif_start_queue(netdev);
28721a0ffd9SStephan Gerhold return 0;
28821a0ffd9SStephan Gerhold }
28921a0ffd9SStephan Gerhold
bam_dmux_netdev_stop(struct net_device * netdev)29021a0ffd9SStephan Gerhold static int bam_dmux_netdev_stop(struct net_device *netdev)
29121a0ffd9SStephan Gerhold {
29221a0ffd9SStephan Gerhold struct bam_dmux_netdev *bndev = netdev_priv(netdev);
29321a0ffd9SStephan Gerhold
29421a0ffd9SStephan Gerhold netif_stop_queue(netdev);
29521a0ffd9SStephan Gerhold bam_dmux_send_cmd(bndev, BAM_DMUX_CMD_CLOSE);
29621a0ffd9SStephan Gerhold return 0;
29721a0ffd9SStephan Gerhold }
29821a0ffd9SStephan Gerhold
needed_room(unsigned int avail,unsigned int needed)29921a0ffd9SStephan Gerhold static unsigned int needed_room(unsigned int avail, unsigned int needed)
30021a0ffd9SStephan Gerhold {
30121a0ffd9SStephan Gerhold if (avail >= needed)
30221a0ffd9SStephan Gerhold return 0;
30321a0ffd9SStephan Gerhold return needed - avail;
30421a0ffd9SStephan Gerhold }
30521a0ffd9SStephan Gerhold
bam_dmux_tx_prepare_skb(struct bam_dmux_netdev * bndev,struct sk_buff * skb)30621a0ffd9SStephan Gerhold static int bam_dmux_tx_prepare_skb(struct bam_dmux_netdev *bndev,
30721a0ffd9SStephan Gerhold struct sk_buff *skb)
30821a0ffd9SStephan Gerhold {
30921a0ffd9SStephan Gerhold unsigned int head = needed_room(skb_headroom(skb), BAM_DMUX_HDR_SIZE);
31021a0ffd9SStephan Gerhold unsigned int pad = sizeof(u32) - skb->len % sizeof(u32);
31121a0ffd9SStephan Gerhold unsigned int tail = needed_room(skb_tailroom(skb), pad);
31221a0ffd9SStephan Gerhold struct bam_dmux_hdr *hdr;
31321a0ffd9SStephan Gerhold int ret;
31421a0ffd9SStephan Gerhold
31521a0ffd9SStephan Gerhold if (head || tail || skb_cloned(skb)) {
31621a0ffd9SStephan Gerhold ret = pskb_expand_head(skb, head, tail, GFP_ATOMIC);
31721a0ffd9SStephan Gerhold if (ret)
31821a0ffd9SStephan Gerhold return ret;
31921a0ffd9SStephan Gerhold }
32021a0ffd9SStephan Gerhold
32121a0ffd9SStephan Gerhold hdr = skb_push(skb, sizeof(*hdr));
32221a0ffd9SStephan Gerhold hdr->magic = BAM_DMUX_HDR_MAGIC;
32321a0ffd9SStephan Gerhold hdr->signal = 0;
32421a0ffd9SStephan Gerhold hdr->cmd = BAM_DMUX_CMD_DATA;
32521a0ffd9SStephan Gerhold hdr->pad = pad;
32621a0ffd9SStephan Gerhold hdr->ch = bndev->ch;
32721a0ffd9SStephan Gerhold hdr->len = skb->len - sizeof(*hdr);
32821a0ffd9SStephan Gerhold if (pad)
32921a0ffd9SStephan Gerhold skb_put_zero(skb, pad);
33021a0ffd9SStephan Gerhold
33121a0ffd9SStephan Gerhold return 0;
33221a0ffd9SStephan Gerhold }
33321a0ffd9SStephan Gerhold
bam_dmux_netdev_start_xmit(struct sk_buff * skb,struct net_device * netdev)33421a0ffd9SStephan Gerhold static netdev_tx_t bam_dmux_netdev_start_xmit(struct sk_buff *skb,
33521a0ffd9SStephan Gerhold struct net_device *netdev)
33621a0ffd9SStephan Gerhold {
33721a0ffd9SStephan Gerhold struct bam_dmux_netdev *bndev = netdev_priv(netdev);
33821a0ffd9SStephan Gerhold struct bam_dmux *dmux = bndev->dmux;
33921a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma;
34021a0ffd9SStephan Gerhold int active, ret;
34121a0ffd9SStephan Gerhold
34221a0ffd9SStephan Gerhold skb_dma = bam_dmux_tx_queue(dmux, skb);
34321a0ffd9SStephan Gerhold if (!skb_dma)
34421a0ffd9SStephan Gerhold return NETDEV_TX_BUSY;
34521a0ffd9SStephan Gerhold
34621a0ffd9SStephan Gerhold active = pm_runtime_get(dmux->dev);
34721a0ffd9SStephan Gerhold if (active < 0 && active != -EINPROGRESS)
34821a0ffd9SStephan Gerhold goto drop;
34921a0ffd9SStephan Gerhold
35021a0ffd9SStephan Gerhold ret = bam_dmux_tx_prepare_skb(bndev, skb);
35121a0ffd9SStephan Gerhold if (ret)
35221a0ffd9SStephan Gerhold goto drop;
35321a0ffd9SStephan Gerhold
35421a0ffd9SStephan Gerhold if (!bam_dmux_skb_dma_map(skb_dma, DMA_TO_DEVICE))
35521a0ffd9SStephan Gerhold goto drop;
35621a0ffd9SStephan Gerhold
35721a0ffd9SStephan Gerhold if (active <= 0) {
35821a0ffd9SStephan Gerhold /* Cannot sleep here so mark skb for wakeup handler and return */
35921a0ffd9SStephan Gerhold if (!atomic_long_fetch_or(BIT(skb_dma - dmux->tx_skbs),
36021a0ffd9SStephan Gerhold &dmux->tx_deferred_skb))
36121a0ffd9SStephan Gerhold queue_pm_work(&dmux->tx_wakeup_work);
36221a0ffd9SStephan Gerhold return NETDEV_TX_OK;
36321a0ffd9SStephan Gerhold }
36421a0ffd9SStephan Gerhold
36521a0ffd9SStephan Gerhold if (!bam_dmux_skb_dma_submit_tx(skb_dma))
36621a0ffd9SStephan Gerhold goto drop;
36721a0ffd9SStephan Gerhold
36821a0ffd9SStephan Gerhold dma_async_issue_pending(dmux->tx);
36921a0ffd9SStephan Gerhold return NETDEV_TX_OK;
37021a0ffd9SStephan Gerhold
37121a0ffd9SStephan Gerhold drop:
37221a0ffd9SStephan Gerhold bam_dmux_tx_done(skb_dma);
37321a0ffd9SStephan Gerhold dev_kfree_skb_any(skb);
37421a0ffd9SStephan Gerhold return NETDEV_TX_OK;
37521a0ffd9SStephan Gerhold }
37621a0ffd9SStephan Gerhold
bam_dmux_tx_wakeup_work(struct work_struct * work)37721a0ffd9SStephan Gerhold static void bam_dmux_tx_wakeup_work(struct work_struct *work)
37821a0ffd9SStephan Gerhold {
37921a0ffd9SStephan Gerhold struct bam_dmux *dmux = container_of(work, struct bam_dmux, tx_wakeup_work);
38021a0ffd9SStephan Gerhold unsigned long pending;
38121a0ffd9SStephan Gerhold int ret, i;
38221a0ffd9SStephan Gerhold
38321a0ffd9SStephan Gerhold ret = pm_runtime_resume_and_get(dmux->dev);
38421a0ffd9SStephan Gerhold if (ret < 0) {
38521a0ffd9SStephan Gerhold dev_err(dmux->dev, "Failed to resume: %d\n", ret);
38621a0ffd9SStephan Gerhold return;
38721a0ffd9SStephan Gerhold }
38821a0ffd9SStephan Gerhold
38921a0ffd9SStephan Gerhold pending = atomic_long_xchg(&dmux->tx_deferred_skb, 0);
39021a0ffd9SStephan Gerhold if (!pending)
39121a0ffd9SStephan Gerhold goto out;
39221a0ffd9SStephan Gerhold
39321a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "pending skbs after wakeup: %#lx\n", pending);
39421a0ffd9SStephan Gerhold for_each_set_bit(i, &pending, BAM_DMUX_NUM_SKB) {
39521a0ffd9SStephan Gerhold bam_dmux_skb_dma_submit_tx(&dmux->tx_skbs[i]);
39621a0ffd9SStephan Gerhold }
39721a0ffd9SStephan Gerhold dma_async_issue_pending(dmux->tx);
39821a0ffd9SStephan Gerhold
39921a0ffd9SStephan Gerhold out:
40021a0ffd9SStephan Gerhold pm_runtime_mark_last_busy(dmux->dev);
40121a0ffd9SStephan Gerhold pm_runtime_put_autosuspend(dmux->dev);
40221a0ffd9SStephan Gerhold }
40321a0ffd9SStephan Gerhold
40421a0ffd9SStephan Gerhold static const struct net_device_ops bam_dmux_ops = {
40521a0ffd9SStephan Gerhold .ndo_open = bam_dmux_netdev_open,
40621a0ffd9SStephan Gerhold .ndo_stop = bam_dmux_netdev_stop,
40721a0ffd9SStephan Gerhold .ndo_start_xmit = bam_dmux_netdev_start_xmit,
40821a0ffd9SStephan Gerhold };
40921a0ffd9SStephan Gerhold
41021a0ffd9SStephan Gerhold static const struct device_type wwan_type = {
41121a0ffd9SStephan Gerhold .name = "wwan",
41221a0ffd9SStephan Gerhold };
41321a0ffd9SStephan Gerhold
bam_dmux_netdev_setup(struct net_device * dev)41421a0ffd9SStephan Gerhold static void bam_dmux_netdev_setup(struct net_device *dev)
41521a0ffd9SStephan Gerhold {
41621a0ffd9SStephan Gerhold dev->netdev_ops = &bam_dmux_ops;
41721a0ffd9SStephan Gerhold
41821a0ffd9SStephan Gerhold dev->type = ARPHRD_RAWIP;
41921a0ffd9SStephan Gerhold SET_NETDEV_DEVTYPE(dev, &wwan_type);
42021a0ffd9SStephan Gerhold dev->flags = IFF_POINTOPOINT | IFF_NOARP;
42121a0ffd9SStephan Gerhold
42221a0ffd9SStephan Gerhold dev->mtu = ETH_DATA_LEN;
42321a0ffd9SStephan Gerhold dev->max_mtu = BAM_DMUX_MAX_DATA_SIZE;
42421a0ffd9SStephan Gerhold dev->needed_headroom = sizeof(struct bam_dmux_hdr);
42521a0ffd9SStephan Gerhold dev->needed_tailroom = sizeof(u32); /* word-aligned */
42621a0ffd9SStephan Gerhold dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
42721a0ffd9SStephan Gerhold
42821a0ffd9SStephan Gerhold /* This perm addr will be used as interface identifier by IPv6 */
42921a0ffd9SStephan Gerhold dev->addr_assign_type = NET_ADDR_RANDOM;
43021a0ffd9SStephan Gerhold eth_random_addr(dev->perm_addr);
43121a0ffd9SStephan Gerhold }
43221a0ffd9SStephan Gerhold
bam_dmux_register_netdev_work(struct work_struct * work)43321a0ffd9SStephan Gerhold static void bam_dmux_register_netdev_work(struct work_struct *work)
43421a0ffd9SStephan Gerhold {
43521a0ffd9SStephan Gerhold struct bam_dmux *dmux = container_of(work, struct bam_dmux, register_netdev_work);
43621a0ffd9SStephan Gerhold struct bam_dmux_netdev *bndev;
43721a0ffd9SStephan Gerhold struct net_device *netdev;
43821a0ffd9SStephan Gerhold int ch, ret;
43921a0ffd9SStephan Gerhold
44021a0ffd9SStephan Gerhold for_each_set_bit(ch, dmux->remote_channels, BAM_DMUX_NUM_CH) {
44121a0ffd9SStephan Gerhold if (dmux->netdevs[ch])
44221a0ffd9SStephan Gerhold continue;
44321a0ffd9SStephan Gerhold
44421a0ffd9SStephan Gerhold netdev = alloc_netdev(sizeof(*bndev), "wwan%d", NET_NAME_ENUM,
44521a0ffd9SStephan Gerhold bam_dmux_netdev_setup);
44621a0ffd9SStephan Gerhold if (!netdev)
44721a0ffd9SStephan Gerhold return;
44821a0ffd9SStephan Gerhold
44921a0ffd9SStephan Gerhold SET_NETDEV_DEV(netdev, dmux->dev);
45021a0ffd9SStephan Gerhold netdev->dev_port = ch;
45121a0ffd9SStephan Gerhold
45221a0ffd9SStephan Gerhold bndev = netdev_priv(netdev);
45321a0ffd9SStephan Gerhold bndev->dmux = dmux;
45421a0ffd9SStephan Gerhold bndev->ch = ch;
45521a0ffd9SStephan Gerhold
45621a0ffd9SStephan Gerhold ret = register_netdev(netdev);
45721a0ffd9SStephan Gerhold if (ret) {
45821a0ffd9SStephan Gerhold dev_err(dmux->dev, "Failed to register netdev for channel %u: %d\n",
45921a0ffd9SStephan Gerhold ch, ret);
46021a0ffd9SStephan Gerhold free_netdev(netdev);
46121a0ffd9SStephan Gerhold return;
46221a0ffd9SStephan Gerhold }
46321a0ffd9SStephan Gerhold
46421a0ffd9SStephan Gerhold dmux->netdevs[ch] = netdev;
46521a0ffd9SStephan Gerhold }
46621a0ffd9SStephan Gerhold }
46721a0ffd9SStephan Gerhold
46821a0ffd9SStephan Gerhold static void bam_dmux_rx_callback(void *data);
46921a0ffd9SStephan Gerhold
bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma * skb_dma)47021a0ffd9SStephan Gerhold static bool bam_dmux_skb_dma_submit_rx(struct bam_dmux_skb_dma *skb_dma)
47121a0ffd9SStephan Gerhold {
47221a0ffd9SStephan Gerhold struct bam_dmux *dmux = skb_dma->dmux;
47321a0ffd9SStephan Gerhold struct dma_async_tx_descriptor *desc;
47421a0ffd9SStephan Gerhold
47521a0ffd9SStephan Gerhold desc = dmaengine_prep_slave_single(dmux->rx, skb_dma->addr,
47621a0ffd9SStephan Gerhold skb_dma->skb->len, DMA_DEV_TO_MEM,
47721a0ffd9SStephan Gerhold DMA_PREP_INTERRUPT);
47821a0ffd9SStephan Gerhold if (!desc) {
47921a0ffd9SStephan Gerhold dev_err(dmux->dev, "Failed to prepare RX DMA buffer\n");
48021a0ffd9SStephan Gerhold return false;
48121a0ffd9SStephan Gerhold }
48221a0ffd9SStephan Gerhold
48321a0ffd9SStephan Gerhold desc->callback = bam_dmux_rx_callback;
48421a0ffd9SStephan Gerhold desc->callback_param = skb_dma;
48521a0ffd9SStephan Gerhold desc->cookie = dmaengine_submit(desc);
48621a0ffd9SStephan Gerhold return true;
48721a0ffd9SStephan Gerhold }
48821a0ffd9SStephan Gerhold
bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma * skb_dma,gfp_t gfp)48921a0ffd9SStephan Gerhold static bool bam_dmux_skb_dma_queue_rx(struct bam_dmux_skb_dma *skb_dma, gfp_t gfp)
49021a0ffd9SStephan Gerhold {
49121a0ffd9SStephan Gerhold if (!skb_dma->skb) {
49221a0ffd9SStephan Gerhold skb_dma->skb = __netdev_alloc_skb(NULL, BAM_DMUX_BUFFER_SIZE, gfp);
49321a0ffd9SStephan Gerhold if (!skb_dma->skb)
49421a0ffd9SStephan Gerhold return false;
49521a0ffd9SStephan Gerhold skb_put(skb_dma->skb, BAM_DMUX_BUFFER_SIZE);
49621a0ffd9SStephan Gerhold }
49721a0ffd9SStephan Gerhold
49821a0ffd9SStephan Gerhold return bam_dmux_skb_dma_map(skb_dma, DMA_FROM_DEVICE) &&
49921a0ffd9SStephan Gerhold bam_dmux_skb_dma_submit_rx(skb_dma);
50021a0ffd9SStephan Gerhold }
50121a0ffd9SStephan Gerhold
bam_dmux_cmd_data(struct bam_dmux_skb_dma * skb_dma)50221a0ffd9SStephan Gerhold static void bam_dmux_cmd_data(struct bam_dmux_skb_dma *skb_dma)
50321a0ffd9SStephan Gerhold {
50421a0ffd9SStephan Gerhold struct bam_dmux *dmux = skb_dma->dmux;
50521a0ffd9SStephan Gerhold struct sk_buff *skb = skb_dma->skb;
50621a0ffd9SStephan Gerhold struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
50721a0ffd9SStephan Gerhold struct net_device *netdev = dmux->netdevs[hdr->ch];
50821a0ffd9SStephan Gerhold
50921a0ffd9SStephan Gerhold if (!netdev || !netif_running(netdev)) {
51021a0ffd9SStephan Gerhold dev_warn(dmux->dev, "Data for inactive channel %u\n", hdr->ch);
51121a0ffd9SStephan Gerhold return;
51221a0ffd9SStephan Gerhold }
51321a0ffd9SStephan Gerhold
51421a0ffd9SStephan Gerhold if (hdr->len > BAM_DMUX_MAX_DATA_SIZE) {
51521a0ffd9SStephan Gerhold dev_err(dmux->dev, "Data larger than buffer? (%u > %u)\n",
51621a0ffd9SStephan Gerhold hdr->len, (u16)BAM_DMUX_MAX_DATA_SIZE);
51721a0ffd9SStephan Gerhold return;
51821a0ffd9SStephan Gerhold }
51921a0ffd9SStephan Gerhold
52021a0ffd9SStephan Gerhold skb_dma->skb = NULL; /* Hand over to network stack */
52121a0ffd9SStephan Gerhold
52221a0ffd9SStephan Gerhold skb_pull(skb, sizeof(*hdr));
52321a0ffd9SStephan Gerhold skb_trim(skb, hdr->len);
52421a0ffd9SStephan Gerhold skb->dev = netdev;
52521a0ffd9SStephan Gerhold
52621a0ffd9SStephan Gerhold /* Only Raw-IP/QMAP is supported by this driver */
52721a0ffd9SStephan Gerhold switch (skb->data[0] & 0xf0) {
52821a0ffd9SStephan Gerhold case 0x40:
52921a0ffd9SStephan Gerhold skb->protocol = htons(ETH_P_IP);
53021a0ffd9SStephan Gerhold break;
53121a0ffd9SStephan Gerhold case 0x60:
53221a0ffd9SStephan Gerhold skb->protocol = htons(ETH_P_IPV6);
53321a0ffd9SStephan Gerhold break;
53421a0ffd9SStephan Gerhold default:
53521a0ffd9SStephan Gerhold skb->protocol = htons(ETH_P_MAP);
53621a0ffd9SStephan Gerhold break;
53721a0ffd9SStephan Gerhold }
53821a0ffd9SStephan Gerhold
53921a0ffd9SStephan Gerhold netif_receive_skb(skb);
54021a0ffd9SStephan Gerhold }
54121a0ffd9SStephan Gerhold
bam_dmux_cmd_open(struct bam_dmux * dmux,struct bam_dmux_hdr * hdr)54221a0ffd9SStephan Gerhold static void bam_dmux_cmd_open(struct bam_dmux *dmux, struct bam_dmux_hdr *hdr)
54321a0ffd9SStephan Gerhold {
54421a0ffd9SStephan Gerhold struct net_device *netdev = dmux->netdevs[hdr->ch];
54521a0ffd9SStephan Gerhold
54621a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "open channel: %u\n", hdr->ch);
54721a0ffd9SStephan Gerhold
54821a0ffd9SStephan Gerhold if (__test_and_set_bit(hdr->ch, dmux->remote_channels)) {
54921a0ffd9SStephan Gerhold dev_warn(dmux->dev, "Channel already open: %u\n", hdr->ch);
55021a0ffd9SStephan Gerhold return;
55121a0ffd9SStephan Gerhold }
55221a0ffd9SStephan Gerhold
55321a0ffd9SStephan Gerhold if (netdev) {
55421a0ffd9SStephan Gerhold netif_device_attach(netdev);
55521a0ffd9SStephan Gerhold } else {
55621a0ffd9SStephan Gerhold /* Cannot sleep here, schedule work to register the netdev */
55721a0ffd9SStephan Gerhold schedule_work(&dmux->register_netdev_work);
55821a0ffd9SStephan Gerhold }
55921a0ffd9SStephan Gerhold }
56021a0ffd9SStephan Gerhold
bam_dmux_cmd_close(struct bam_dmux * dmux,struct bam_dmux_hdr * hdr)56121a0ffd9SStephan Gerhold static void bam_dmux_cmd_close(struct bam_dmux *dmux, struct bam_dmux_hdr *hdr)
56221a0ffd9SStephan Gerhold {
56321a0ffd9SStephan Gerhold struct net_device *netdev = dmux->netdevs[hdr->ch];
56421a0ffd9SStephan Gerhold
56521a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "close channel: %u\n", hdr->ch);
56621a0ffd9SStephan Gerhold
56721a0ffd9SStephan Gerhold if (!__test_and_clear_bit(hdr->ch, dmux->remote_channels)) {
56821a0ffd9SStephan Gerhold dev_err(dmux->dev, "Channel not open: %u\n", hdr->ch);
56921a0ffd9SStephan Gerhold return;
57021a0ffd9SStephan Gerhold }
57121a0ffd9SStephan Gerhold
57221a0ffd9SStephan Gerhold if (netdev)
57321a0ffd9SStephan Gerhold netif_device_detach(netdev);
57421a0ffd9SStephan Gerhold }
57521a0ffd9SStephan Gerhold
bam_dmux_rx_callback(void * data)57621a0ffd9SStephan Gerhold static void bam_dmux_rx_callback(void *data)
57721a0ffd9SStephan Gerhold {
57821a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma = data;
57921a0ffd9SStephan Gerhold struct bam_dmux *dmux = skb_dma->dmux;
58021a0ffd9SStephan Gerhold struct sk_buff *skb = skb_dma->skb;
58121a0ffd9SStephan Gerhold struct bam_dmux_hdr *hdr = (struct bam_dmux_hdr *)skb->data;
58221a0ffd9SStephan Gerhold
58321a0ffd9SStephan Gerhold bam_dmux_skb_dma_unmap(skb_dma, DMA_FROM_DEVICE);
58421a0ffd9SStephan Gerhold
58521a0ffd9SStephan Gerhold if (hdr->magic != BAM_DMUX_HDR_MAGIC) {
58621a0ffd9SStephan Gerhold dev_err(dmux->dev, "Invalid magic in header: %#x\n", hdr->magic);
58721a0ffd9SStephan Gerhold goto out;
58821a0ffd9SStephan Gerhold }
58921a0ffd9SStephan Gerhold
59021a0ffd9SStephan Gerhold if (hdr->ch >= BAM_DMUX_NUM_CH) {
59121a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "Unsupported channel: %u\n", hdr->ch);
59221a0ffd9SStephan Gerhold goto out;
59321a0ffd9SStephan Gerhold }
59421a0ffd9SStephan Gerhold
59521a0ffd9SStephan Gerhold switch (hdr->cmd) {
59621a0ffd9SStephan Gerhold case BAM_DMUX_CMD_DATA:
59721a0ffd9SStephan Gerhold bam_dmux_cmd_data(skb_dma);
59821a0ffd9SStephan Gerhold break;
59921a0ffd9SStephan Gerhold case BAM_DMUX_CMD_OPEN:
60021a0ffd9SStephan Gerhold bam_dmux_cmd_open(dmux, hdr);
60121a0ffd9SStephan Gerhold break;
60221a0ffd9SStephan Gerhold case BAM_DMUX_CMD_CLOSE:
60321a0ffd9SStephan Gerhold bam_dmux_cmd_close(dmux, hdr);
60421a0ffd9SStephan Gerhold break;
60521a0ffd9SStephan Gerhold default:
60621a0ffd9SStephan Gerhold dev_err(dmux->dev, "Unsupported command %u on channel %u\n",
60721a0ffd9SStephan Gerhold hdr->cmd, hdr->ch);
60821a0ffd9SStephan Gerhold break;
60921a0ffd9SStephan Gerhold }
61021a0ffd9SStephan Gerhold
61121a0ffd9SStephan Gerhold out:
61221a0ffd9SStephan Gerhold if (bam_dmux_skb_dma_queue_rx(skb_dma, GFP_ATOMIC))
61321a0ffd9SStephan Gerhold dma_async_issue_pending(dmux->rx);
61421a0ffd9SStephan Gerhold }
61521a0ffd9SStephan Gerhold
bam_dmux_power_on(struct bam_dmux * dmux)61621a0ffd9SStephan Gerhold static bool bam_dmux_power_on(struct bam_dmux *dmux)
61721a0ffd9SStephan Gerhold {
61821a0ffd9SStephan Gerhold struct device *dev = dmux->dev;
61921a0ffd9SStephan Gerhold struct dma_slave_config dma_rx_conf = {
62021a0ffd9SStephan Gerhold .direction = DMA_DEV_TO_MEM,
62121a0ffd9SStephan Gerhold .src_maxburst = BAM_DMUX_BUFFER_SIZE,
62221a0ffd9SStephan Gerhold };
62321a0ffd9SStephan Gerhold int i;
62421a0ffd9SStephan Gerhold
62521a0ffd9SStephan Gerhold dmux->rx = dma_request_chan(dev, "rx");
62621a0ffd9SStephan Gerhold if (IS_ERR(dmux->rx)) {
62721a0ffd9SStephan Gerhold dev_err(dev, "Failed to request RX DMA channel: %pe\n", dmux->rx);
62821a0ffd9SStephan Gerhold dmux->rx = NULL;
62921a0ffd9SStephan Gerhold return false;
63021a0ffd9SStephan Gerhold }
63121a0ffd9SStephan Gerhold dmaengine_slave_config(dmux->rx, &dma_rx_conf);
63221a0ffd9SStephan Gerhold
63321a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
63421a0ffd9SStephan Gerhold if (!bam_dmux_skb_dma_queue_rx(&dmux->rx_skbs[i], GFP_KERNEL))
63521a0ffd9SStephan Gerhold return false;
63621a0ffd9SStephan Gerhold }
63721a0ffd9SStephan Gerhold dma_async_issue_pending(dmux->rx);
63821a0ffd9SStephan Gerhold
63921a0ffd9SStephan Gerhold return true;
64021a0ffd9SStephan Gerhold }
64121a0ffd9SStephan Gerhold
bam_dmux_free_skbs(struct bam_dmux_skb_dma skbs[],enum dma_data_direction dir)64221a0ffd9SStephan Gerhold static void bam_dmux_free_skbs(struct bam_dmux_skb_dma skbs[],
64321a0ffd9SStephan Gerhold enum dma_data_direction dir)
64421a0ffd9SStephan Gerhold {
64521a0ffd9SStephan Gerhold int i;
64621a0ffd9SStephan Gerhold
64721a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
64821a0ffd9SStephan Gerhold struct bam_dmux_skb_dma *skb_dma = &skbs[i];
64921a0ffd9SStephan Gerhold
65021a0ffd9SStephan Gerhold if (skb_dma->addr)
65121a0ffd9SStephan Gerhold bam_dmux_skb_dma_unmap(skb_dma, dir);
65221a0ffd9SStephan Gerhold if (skb_dma->skb) {
65321a0ffd9SStephan Gerhold dev_kfree_skb(skb_dma->skb);
65421a0ffd9SStephan Gerhold skb_dma->skb = NULL;
65521a0ffd9SStephan Gerhold }
65621a0ffd9SStephan Gerhold }
65721a0ffd9SStephan Gerhold }
65821a0ffd9SStephan Gerhold
bam_dmux_power_off(struct bam_dmux * dmux)65921a0ffd9SStephan Gerhold static void bam_dmux_power_off(struct bam_dmux *dmux)
66021a0ffd9SStephan Gerhold {
66121a0ffd9SStephan Gerhold if (dmux->tx) {
66221a0ffd9SStephan Gerhold dmaengine_terminate_sync(dmux->tx);
66321a0ffd9SStephan Gerhold dma_release_channel(dmux->tx);
66421a0ffd9SStephan Gerhold dmux->tx = NULL;
66521a0ffd9SStephan Gerhold }
66621a0ffd9SStephan Gerhold
66721a0ffd9SStephan Gerhold if (dmux->rx) {
66821a0ffd9SStephan Gerhold dmaengine_terminate_sync(dmux->rx);
66921a0ffd9SStephan Gerhold dma_release_channel(dmux->rx);
67021a0ffd9SStephan Gerhold dmux->rx = NULL;
67121a0ffd9SStephan Gerhold }
67221a0ffd9SStephan Gerhold
67321a0ffd9SStephan Gerhold bam_dmux_free_skbs(dmux->rx_skbs, DMA_FROM_DEVICE);
67421a0ffd9SStephan Gerhold }
67521a0ffd9SStephan Gerhold
bam_dmux_pc_irq(int irq,void * data)67621a0ffd9SStephan Gerhold static irqreturn_t bam_dmux_pc_irq(int irq, void *data)
67721a0ffd9SStephan Gerhold {
67821a0ffd9SStephan Gerhold struct bam_dmux *dmux = data;
67921a0ffd9SStephan Gerhold bool new_state = !dmux->pc_state;
68021a0ffd9SStephan Gerhold
68121a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "pc: %u\n", new_state);
68221a0ffd9SStephan Gerhold
68321a0ffd9SStephan Gerhold if (new_state) {
68421a0ffd9SStephan Gerhold if (bam_dmux_power_on(dmux))
68521a0ffd9SStephan Gerhold bam_dmux_pc_ack(dmux);
68621a0ffd9SStephan Gerhold else
68721a0ffd9SStephan Gerhold bam_dmux_power_off(dmux);
68821a0ffd9SStephan Gerhold } else {
68921a0ffd9SStephan Gerhold bam_dmux_power_off(dmux);
69021a0ffd9SStephan Gerhold bam_dmux_pc_ack(dmux);
69121a0ffd9SStephan Gerhold }
69221a0ffd9SStephan Gerhold
69321a0ffd9SStephan Gerhold dmux->pc_state = new_state;
69421a0ffd9SStephan Gerhold wake_up_all(&dmux->pc_wait);
69521a0ffd9SStephan Gerhold
69621a0ffd9SStephan Gerhold return IRQ_HANDLED;
69721a0ffd9SStephan Gerhold }
69821a0ffd9SStephan Gerhold
bam_dmux_pc_ack_irq(int irq,void * data)69921a0ffd9SStephan Gerhold static irqreturn_t bam_dmux_pc_ack_irq(int irq, void *data)
70021a0ffd9SStephan Gerhold {
70121a0ffd9SStephan Gerhold struct bam_dmux *dmux = data;
70221a0ffd9SStephan Gerhold
70321a0ffd9SStephan Gerhold dev_dbg(dmux->dev, "pc ack\n");
70421a0ffd9SStephan Gerhold complete_all(&dmux->pc_ack_completion);
70521a0ffd9SStephan Gerhold
70621a0ffd9SStephan Gerhold return IRQ_HANDLED;
70721a0ffd9SStephan Gerhold }
70821a0ffd9SStephan Gerhold
bam_dmux_runtime_suspend(struct device * dev)70921a0ffd9SStephan Gerhold static int bam_dmux_runtime_suspend(struct device *dev)
71021a0ffd9SStephan Gerhold {
71121a0ffd9SStephan Gerhold struct bam_dmux *dmux = dev_get_drvdata(dev);
71221a0ffd9SStephan Gerhold
71321a0ffd9SStephan Gerhold dev_dbg(dev, "runtime suspend\n");
71421a0ffd9SStephan Gerhold bam_dmux_pc_vote(dmux, false);
71521a0ffd9SStephan Gerhold
71621a0ffd9SStephan Gerhold return 0;
71721a0ffd9SStephan Gerhold }
71821a0ffd9SStephan Gerhold
bam_dmux_runtime_resume(struct device * dev)71921a0ffd9SStephan Gerhold static int __maybe_unused bam_dmux_runtime_resume(struct device *dev)
72021a0ffd9SStephan Gerhold {
72121a0ffd9SStephan Gerhold struct bam_dmux *dmux = dev_get_drvdata(dev);
72221a0ffd9SStephan Gerhold
72321a0ffd9SStephan Gerhold dev_dbg(dev, "runtime resume\n");
72421a0ffd9SStephan Gerhold
72521a0ffd9SStephan Gerhold /* Wait until previous power down was acked */
72621a0ffd9SStephan Gerhold if (!wait_for_completion_timeout(&dmux->pc_ack_completion,
72721a0ffd9SStephan Gerhold BAM_DMUX_REMOTE_TIMEOUT))
72821a0ffd9SStephan Gerhold return -ETIMEDOUT;
72921a0ffd9SStephan Gerhold
73021a0ffd9SStephan Gerhold /* Vote for power state */
73121a0ffd9SStephan Gerhold bam_dmux_pc_vote(dmux, true);
73221a0ffd9SStephan Gerhold
73321a0ffd9SStephan Gerhold /* Wait for ack */
73421a0ffd9SStephan Gerhold if (!wait_for_completion_timeout(&dmux->pc_ack_completion,
73521a0ffd9SStephan Gerhold BAM_DMUX_REMOTE_TIMEOUT)) {
73621a0ffd9SStephan Gerhold bam_dmux_pc_vote(dmux, false);
73721a0ffd9SStephan Gerhold return -ETIMEDOUT;
73821a0ffd9SStephan Gerhold }
73921a0ffd9SStephan Gerhold
74021a0ffd9SStephan Gerhold /* Wait until we're up */
74121a0ffd9SStephan Gerhold if (!wait_event_timeout(dmux->pc_wait, dmux->pc_state,
74221a0ffd9SStephan Gerhold BAM_DMUX_REMOTE_TIMEOUT)) {
74321a0ffd9SStephan Gerhold bam_dmux_pc_vote(dmux, false);
74421a0ffd9SStephan Gerhold return -ETIMEDOUT;
74521a0ffd9SStephan Gerhold }
74621a0ffd9SStephan Gerhold
74721a0ffd9SStephan Gerhold /* Ensure that we actually initialized successfully */
74821a0ffd9SStephan Gerhold if (!dmux->rx) {
74921a0ffd9SStephan Gerhold bam_dmux_pc_vote(dmux, false);
75021a0ffd9SStephan Gerhold return -ENXIO;
75121a0ffd9SStephan Gerhold }
75221a0ffd9SStephan Gerhold
75321a0ffd9SStephan Gerhold /* Request TX channel if necessary */
75421a0ffd9SStephan Gerhold if (dmux->tx)
75521a0ffd9SStephan Gerhold return 0;
75621a0ffd9SStephan Gerhold
75721a0ffd9SStephan Gerhold dmux->tx = dma_request_chan(dev, "tx");
7586b3c7455SYang Yingliang if (IS_ERR(dmux->tx)) {
75921a0ffd9SStephan Gerhold dev_err(dev, "Failed to request TX DMA channel: %pe\n", dmux->tx);
76021a0ffd9SStephan Gerhold dmux->tx = NULL;
76121a0ffd9SStephan Gerhold bam_dmux_runtime_suspend(dev);
76221a0ffd9SStephan Gerhold return -ENXIO;
76321a0ffd9SStephan Gerhold }
76421a0ffd9SStephan Gerhold
76521a0ffd9SStephan Gerhold return 0;
76621a0ffd9SStephan Gerhold }
76721a0ffd9SStephan Gerhold
bam_dmux_probe(struct platform_device * pdev)76821a0ffd9SStephan Gerhold static int bam_dmux_probe(struct platform_device *pdev)
76921a0ffd9SStephan Gerhold {
77021a0ffd9SStephan Gerhold struct device *dev = &pdev->dev;
77121a0ffd9SStephan Gerhold struct bam_dmux *dmux;
77221a0ffd9SStephan Gerhold int ret, pc_ack_irq, i;
77321a0ffd9SStephan Gerhold unsigned int bit;
77421a0ffd9SStephan Gerhold
77521a0ffd9SStephan Gerhold dmux = devm_kzalloc(dev, sizeof(*dmux), GFP_KERNEL);
77621a0ffd9SStephan Gerhold if (!dmux)
77721a0ffd9SStephan Gerhold return -ENOMEM;
77821a0ffd9SStephan Gerhold
77921a0ffd9SStephan Gerhold dmux->dev = dev;
78021a0ffd9SStephan Gerhold platform_set_drvdata(pdev, dmux);
78121a0ffd9SStephan Gerhold
78221a0ffd9SStephan Gerhold dmux->pc_irq = platform_get_irq_byname(pdev, "pc");
78321a0ffd9SStephan Gerhold if (dmux->pc_irq < 0)
78421a0ffd9SStephan Gerhold return dmux->pc_irq;
78521a0ffd9SStephan Gerhold
78621a0ffd9SStephan Gerhold pc_ack_irq = platform_get_irq_byname(pdev, "pc-ack");
78721a0ffd9SStephan Gerhold if (pc_ack_irq < 0)
78821a0ffd9SStephan Gerhold return pc_ack_irq;
78921a0ffd9SStephan Gerhold
79021a0ffd9SStephan Gerhold dmux->pc = devm_qcom_smem_state_get(dev, "pc", &bit);
79121a0ffd9SStephan Gerhold if (IS_ERR(dmux->pc))
79221a0ffd9SStephan Gerhold return dev_err_probe(dev, PTR_ERR(dmux->pc),
79321a0ffd9SStephan Gerhold "Failed to get pc state\n");
79421a0ffd9SStephan Gerhold dmux->pc_mask = BIT(bit);
79521a0ffd9SStephan Gerhold
79621a0ffd9SStephan Gerhold dmux->pc_ack = devm_qcom_smem_state_get(dev, "pc-ack", &bit);
79721a0ffd9SStephan Gerhold if (IS_ERR(dmux->pc_ack))
79821a0ffd9SStephan Gerhold return dev_err_probe(dev, PTR_ERR(dmux->pc_ack),
79921a0ffd9SStephan Gerhold "Failed to get pc-ack state\n");
80021a0ffd9SStephan Gerhold dmux->pc_ack_mask = BIT(bit);
80121a0ffd9SStephan Gerhold
80221a0ffd9SStephan Gerhold init_waitqueue_head(&dmux->pc_wait);
80321a0ffd9SStephan Gerhold init_completion(&dmux->pc_ack_completion);
80421a0ffd9SStephan Gerhold complete_all(&dmux->pc_ack_completion);
80521a0ffd9SStephan Gerhold
80621a0ffd9SStephan Gerhold spin_lock_init(&dmux->tx_lock);
80721a0ffd9SStephan Gerhold INIT_WORK(&dmux->tx_wakeup_work, bam_dmux_tx_wakeup_work);
80821a0ffd9SStephan Gerhold INIT_WORK(&dmux->register_netdev_work, bam_dmux_register_netdev_work);
80921a0ffd9SStephan Gerhold
81021a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_SKB; i++) {
81121a0ffd9SStephan Gerhold dmux->rx_skbs[i].dmux = dmux;
81221a0ffd9SStephan Gerhold dmux->tx_skbs[i].dmux = dmux;
81321a0ffd9SStephan Gerhold }
81421a0ffd9SStephan Gerhold
81521a0ffd9SStephan Gerhold /* Runtime PM manages our own power vote.
81621a0ffd9SStephan Gerhold * Note that the RX path may be active even if we are runtime suspended,
81721a0ffd9SStephan Gerhold * since it is controlled by the remote side.
81821a0ffd9SStephan Gerhold */
81921a0ffd9SStephan Gerhold pm_runtime_set_autosuspend_delay(dev, BAM_DMUX_AUTOSUSPEND_DELAY);
82021a0ffd9SStephan Gerhold pm_runtime_use_autosuspend(dev);
82121a0ffd9SStephan Gerhold pm_runtime_enable(dev);
82221a0ffd9SStephan Gerhold
82321a0ffd9SStephan Gerhold ret = devm_request_threaded_irq(dev, pc_ack_irq, NULL, bam_dmux_pc_ack_irq,
82421a0ffd9SStephan Gerhold IRQF_ONESHOT, NULL, dmux);
82521a0ffd9SStephan Gerhold if (ret)
826*271b4904SJinjie Ruan goto err_disable_pm;
82721a0ffd9SStephan Gerhold
82821a0ffd9SStephan Gerhold ret = devm_request_threaded_irq(dev, dmux->pc_irq, NULL, bam_dmux_pc_irq,
82921a0ffd9SStephan Gerhold IRQF_ONESHOT, NULL, dmux);
83021a0ffd9SStephan Gerhold if (ret)
831*271b4904SJinjie Ruan goto err_disable_pm;
83221a0ffd9SStephan Gerhold
83321a0ffd9SStephan Gerhold ret = irq_get_irqchip_state(dmux->pc_irq, IRQCHIP_STATE_LINE_LEVEL,
83421a0ffd9SStephan Gerhold &dmux->pc_state);
83521a0ffd9SStephan Gerhold if (ret)
836*271b4904SJinjie Ruan goto err_disable_pm;
83721a0ffd9SStephan Gerhold
83821a0ffd9SStephan Gerhold /* Check if remote finished initialization before us */
83921a0ffd9SStephan Gerhold if (dmux->pc_state) {
84021a0ffd9SStephan Gerhold if (bam_dmux_power_on(dmux))
84121a0ffd9SStephan Gerhold bam_dmux_pc_ack(dmux);
84221a0ffd9SStephan Gerhold else
84321a0ffd9SStephan Gerhold bam_dmux_power_off(dmux);
84421a0ffd9SStephan Gerhold }
84521a0ffd9SStephan Gerhold
84621a0ffd9SStephan Gerhold return 0;
847*271b4904SJinjie Ruan
848*271b4904SJinjie Ruan err_disable_pm:
849*271b4904SJinjie Ruan pm_runtime_disable(dev);
850*271b4904SJinjie Ruan pm_runtime_dont_use_autosuspend(dev);
851*271b4904SJinjie Ruan return ret;
85221a0ffd9SStephan Gerhold }
85321a0ffd9SStephan Gerhold
bam_dmux_remove(struct platform_device * pdev)85421a0ffd9SStephan Gerhold static int bam_dmux_remove(struct platform_device *pdev)
85521a0ffd9SStephan Gerhold {
85621a0ffd9SStephan Gerhold struct bam_dmux *dmux = platform_get_drvdata(pdev);
85721a0ffd9SStephan Gerhold struct device *dev = dmux->dev;
85821a0ffd9SStephan Gerhold LIST_HEAD(list);
85921a0ffd9SStephan Gerhold int i;
86021a0ffd9SStephan Gerhold
86121a0ffd9SStephan Gerhold /* Unregister network interfaces */
86221a0ffd9SStephan Gerhold cancel_work_sync(&dmux->register_netdev_work);
86321a0ffd9SStephan Gerhold rtnl_lock();
86421a0ffd9SStephan Gerhold for (i = 0; i < BAM_DMUX_NUM_CH; ++i)
86521a0ffd9SStephan Gerhold if (dmux->netdevs[i])
86621a0ffd9SStephan Gerhold unregister_netdevice_queue(dmux->netdevs[i], &list);
86721a0ffd9SStephan Gerhold unregister_netdevice_many(&list);
86821a0ffd9SStephan Gerhold rtnl_unlock();
86921a0ffd9SStephan Gerhold cancel_work_sync(&dmux->tx_wakeup_work);
87021a0ffd9SStephan Gerhold
87121a0ffd9SStephan Gerhold /* Drop our own power vote */
87221a0ffd9SStephan Gerhold pm_runtime_disable(dev);
87321a0ffd9SStephan Gerhold pm_runtime_dont_use_autosuspend(dev);
87421a0ffd9SStephan Gerhold bam_dmux_runtime_suspend(dev);
87521a0ffd9SStephan Gerhold pm_runtime_set_suspended(dev);
87621a0ffd9SStephan Gerhold
87721a0ffd9SStephan Gerhold /* Try to wait for remote side to drop power vote */
87821a0ffd9SStephan Gerhold if (!wait_event_timeout(dmux->pc_wait, !dmux->rx, BAM_DMUX_REMOTE_TIMEOUT))
87921a0ffd9SStephan Gerhold dev_err(dev, "Timed out waiting for remote side to suspend\n");
88021a0ffd9SStephan Gerhold
88121a0ffd9SStephan Gerhold /* Make sure everything is cleaned up before we return */
88221a0ffd9SStephan Gerhold disable_irq(dmux->pc_irq);
88321a0ffd9SStephan Gerhold bam_dmux_power_off(dmux);
88421a0ffd9SStephan Gerhold bam_dmux_free_skbs(dmux->tx_skbs, DMA_TO_DEVICE);
88521a0ffd9SStephan Gerhold
88621a0ffd9SStephan Gerhold return 0;
88721a0ffd9SStephan Gerhold }
88821a0ffd9SStephan Gerhold
88921a0ffd9SStephan Gerhold static const struct dev_pm_ops bam_dmux_pm_ops = {
89021a0ffd9SStephan Gerhold SET_RUNTIME_PM_OPS(bam_dmux_runtime_suspend, bam_dmux_runtime_resume, NULL)
89121a0ffd9SStephan Gerhold };
89221a0ffd9SStephan Gerhold
89321a0ffd9SStephan Gerhold static const struct of_device_id bam_dmux_of_match[] = {
89421a0ffd9SStephan Gerhold { .compatible = "qcom,bam-dmux" },
89521a0ffd9SStephan Gerhold { /* sentinel */ }
89621a0ffd9SStephan Gerhold };
89721a0ffd9SStephan Gerhold MODULE_DEVICE_TABLE(of, bam_dmux_of_match);
89821a0ffd9SStephan Gerhold
89921a0ffd9SStephan Gerhold static struct platform_driver bam_dmux_driver = {
90021a0ffd9SStephan Gerhold .probe = bam_dmux_probe,
90121a0ffd9SStephan Gerhold .remove = bam_dmux_remove,
90221a0ffd9SStephan Gerhold .driver = {
90321a0ffd9SStephan Gerhold .name = "bam-dmux",
90421a0ffd9SStephan Gerhold .pm = &bam_dmux_pm_ops,
90521a0ffd9SStephan Gerhold .of_match_table = bam_dmux_of_match,
90621a0ffd9SStephan Gerhold },
90721a0ffd9SStephan Gerhold };
90821a0ffd9SStephan Gerhold module_platform_driver(bam_dmux_driver);
90921a0ffd9SStephan Gerhold
91021a0ffd9SStephan Gerhold MODULE_LICENSE("GPL v2");
91121a0ffd9SStephan Gerhold MODULE_DESCRIPTION("Qualcomm BAM-DMUX WWAN Network Driver");
91221a0ffd9SStephan Gerhold MODULE_AUTHOR("Stephan Gerhold <stephan@gerhold.net>");
913