Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 687b13e9 authored by Daniel Martensson's avatar Daniel Martensson Committed by David S. Miller
Browse files

caif-hsi: Making read and writes asynchronous.



Some platforms do not allow to put HSI block into low-power
mode when FIFO is not empty. The patch flushes (by reading)
FIFO at wake down sequence. Asynchronous read and write is
implemented for that. As a side effect this will also greatly
improve performance.

Signed-off-by: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 73033c98
Loading
Loading
Loading
Loading
+127 −136
Original line number Original line Diff line number Diff line
@@ -145,7 +145,7 @@ static int cfhsi_flush_fifo(struct cfhsi *cfhsi)
		}
		}


		ret = 5 * HZ;
		ret = 5 * HZ;
		wait_event_interruptible_timeout(cfhsi->flush_fifo_wait,
		ret = wait_event_interruptible_timeout(cfhsi->flush_fifo_wait,
			 !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret);
			 !test_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits), ret);


		if (ret < 0) {
		if (ret < 0) {
@@ -272,16 +272,13 @@ static int cfhsi_tx_frm(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
	return CFHSI_DESC_SZ + pld_len;
	return CFHSI_DESC_SZ + pld_len;
}
}


static void cfhsi_tx_done_work(struct work_struct *work)
static void cfhsi_tx_done(struct cfhsi *cfhsi)
{
{
	struct cfhsi *cfhsi = NULL;
	struct cfhsi_desc *desc = NULL;
	struct cfhsi_desc *desc = NULL;
	int len = 0;
	int len = 0;
	int res;
	int res;


	cfhsi = container_of(work, struct cfhsi, tx_done_work);
	dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
		__func__);


	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
		return;
		return;
@@ -343,11 +340,11 @@ static void cfhsi_tx_done_cb(struct cfhsi_drv *drv)


	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
		return;
		return;

	cfhsi_tx_done(cfhsi);
	queue_work(cfhsi->wq, &cfhsi->tx_done_work);
}
}


static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
				bool *dump)
{
{
	int xfer_sz = 0;
	int xfer_sz = 0;
	int nfrms = 0;
	int nfrms = 0;
@@ -358,6 +355,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
			(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
			(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
			__func__);
			__func__);
		*dump = true;
		return 0;
		return 0;
	}
	}


@@ -365,7 +363,7 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
	if (desc->offset) {
	if (desc->offset) {
		struct sk_buff *skb;
		struct sk_buff *skb;
		u8 *dst = NULL;
		u8 *dst = NULL;
		int len = 0, retries = 0;
		int len = 0;
		pfrm = ((u8 *)desc) + desc->offset;
		pfrm = ((u8 *)desc) + desc->offset;


		/* Remove offset padding. */
		/* Remove offset padding. */
@@ -378,24 +376,11 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)




		/* Allocate SKB (OK even in IRQ context). */
		/* Allocate SKB (OK even in IRQ context). */
		skb = alloc_skb(len + 1, GFP_KERNEL);
		skb = alloc_skb(len + 1, GFP_ATOMIC);
		while (!skb) {
		if (!skb) {
			retries++;
			dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
			schedule_timeout(1);
			skb = alloc_skb(len + 1, GFP_KERNEL);
			if (skb) {
				printk(KERN_WARNING "%s: slept for %u "
						"before getting memory\n",
						__func__, retries);
				break;
			}
			if (retries > HZ) {
				printk(KERN_ERR "%s: slept for 1HZ and "
						"did not get memory\n",
				__func__);
				__func__);
				cfhsi->ndev->stats.rx_dropped++;
			return -ENOMEM;
				goto drop_frame;
			}
		}
		}
		caif_assert(skb != NULL);
		caif_assert(skb != NULL);


@@ -421,7 +406,6 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
		cfhsi->ndev->stats.rx_bytes += len;
		cfhsi->ndev->stats.rx_bytes += len;
	}
	}


drop_frame:
	/* Calculate transfer length. */
	/* Calculate transfer length. */
	plen = desc->cffrm_len;
	plen = desc->cffrm_len;
	while (nfrms < CFHSI_MAX_PKTS && *plen) {
	while (nfrms < CFHSI_MAX_PKTS && *plen) {
@@ -439,12 +423,13 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
				"%s: Invalid payload len: %d, ignored.\n",
				"%s: Invalid payload len: %d, ignored.\n",
			__func__, xfer_sz);
			__func__, xfer_sz);
		xfer_sz = 0;
		xfer_sz = 0;
		*dump = true;
	}
	}

	return xfer_sz;
	return xfer_sz;
}
}


static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi,
				bool *dump)
{
{
	int rx_sz = 0;
	int rx_sz = 0;
	int nfrms = 0;
	int nfrms = 0;
@@ -456,21 +441,33 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
			(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
			(desc->offset > CFHSI_MAX_EMB_FRM_SZ))) {
		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
		dev_err(&cfhsi->ndev->dev, "%s: Invalid descriptor.\n",
			__func__);
			__func__);
		*dump = true;
		return -EINVAL;
		return -EINVAL;
	}
	}


	/* Set frame pointer to start of payload. */
	/* Set frame pointer to start of payload. */
	pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
	pfrm = desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ;
	plen = desc->cffrm_len;
	plen = desc->cffrm_len;

	/* Skip already processed frames. */
	while (nfrms < cfhsi->rx_state.nfrms) {
		pfrm += *plen;
		rx_sz += *plen;
		plen++;
		nfrms++;
	}

	/* Parse payload. */
	while (nfrms < CFHSI_MAX_PKTS && *plen) {
	while (nfrms < CFHSI_MAX_PKTS && *plen) {
		struct sk_buff *skb;
		struct sk_buff *skb;
		u8 *dst = NULL;
		u8 *dst = NULL;
		u8 *pcffrm = NULL;
		u8 *pcffrm = NULL;
		int len = 0, retries = 0;
		int len = 0;


		if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) {
		if (WARN_ON(desc->cffrm_len[nfrms] > CFHSI_MAX_PAYLOAD_SZ)) {
			dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n",
			dev_err(&cfhsi->ndev->dev, "%s: Invalid payload.\n",
				__func__);
				__func__);
			*dump = true;
			return -EINVAL;
			return -EINVAL;
		}
		}


@@ -483,24 +480,12 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
		len += 2;	/* Add FCS fields. */
		len += 2;	/* Add FCS fields. */


		/* Allocate SKB (OK even in IRQ context). */
		/* Allocate SKB (OK even in IRQ context). */
		skb = alloc_skb(len + 1, GFP_KERNEL);
		skb = alloc_skb(len + 1, GFP_ATOMIC);
		while (!skb) {
		if (!skb) {
			retries++;
			dev_err(&cfhsi->ndev->dev, "%s: Out of memory !\n",
			schedule_timeout(1);
			skb = alloc_skb(len + 1, GFP_KERNEL);
			if (skb) {
				printk(KERN_WARNING "%s: slept for %u "
						"before getting memory\n",
						__func__, retries);
				break;
			}
			if (retries > HZ) {
				printk(KERN_ERR "%s: slept for 1HZ "
						"and did not get memory\n",
				__func__);
				__func__);
				cfhsi->ndev->stats.rx_dropped++;
			cfhsi->rx_state.nfrms = nfrms;
				goto drop_frame;
			return -ENOMEM;
			}
		}
		}
		caif_assert(skb != NULL);
		caif_assert(skb != NULL);


@@ -524,7 +509,6 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
		cfhsi->ndev->stats.rx_packets++;
		cfhsi->ndev->stats.rx_packets++;
		cfhsi->ndev->stats.rx_bytes += len;
		cfhsi->ndev->stats.rx_bytes += len;


drop_frame:
		pfrm += *plen;
		pfrm += *plen;
		rx_sz += *plen;
		rx_sz += *plen;
		plen++;
		plen++;
@@ -534,18 +518,16 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
	return rx_sz;
	return rx_sz;
}
}


static void cfhsi_rx_done_work(struct work_struct *work)
static void cfhsi_rx_done(struct cfhsi *cfhsi)
{
{
	int res;
	int res;
	int desc_pld_len = 0;
	int desc_pld_len = 0;
	struct cfhsi *cfhsi = NULL;
	struct cfhsi_desc *desc = NULL;
	struct cfhsi_desc *desc = NULL;
	bool dump = false;


	cfhsi = container_of(work, struct cfhsi, rx_done_work);
	desc = (struct cfhsi_desc *)cfhsi->rx_buf;
	desc = (struct cfhsi_desc *)cfhsi->rx_buf;


	dev_dbg(&cfhsi->ndev->dev, "%s: Kick timer if pending.\n",
	dev_dbg(&cfhsi->ndev->dev, "%s\n", __func__);
		__func__);


	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
		return;
		return;
@@ -555,21 +537,33 @@ static void cfhsi_rx_done_work(struct work_struct *work)
	mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
	mod_timer_pending(&cfhsi->timer, jiffies + CFHSI_INACTIVITY_TOUT);
	spin_unlock_bh(&cfhsi->lock);
	spin_unlock_bh(&cfhsi->lock);


	if (cfhsi->rx_state == CFHSI_RX_STATE_DESC) {
	if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
		desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
		desc_pld_len = cfhsi_rx_desc(desc, cfhsi, &dump);
		if (desc_pld_len == -ENOMEM)
			goto restart;
	} else {
	} else {
		int pld_len;
		int pld_len;


		pld_len = cfhsi_rx_pld(desc, cfhsi);
		if (!cfhsi->rx_state.piggy_desc) {
			pld_len = cfhsi_rx_pld(desc, cfhsi, &dump);
			if (pld_len == -ENOMEM)
				goto restart;
			cfhsi->rx_state.pld_len = pld_len;
		} else {
			pld_len = cfhsi->rx_state.pld_len;
		}


		if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
		if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
			struct cfhsi_desc *piggy_desc;
			struct cfhsi_desc *piggy_desc;
			piggy_desc = (struct cfhsi_desc *)
			piggy_desc = (struct cfhsi_desc *)
				(desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
				(desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
						pld_len);
						pld_len);
			cfhsi->rx_state.piggy_desc = true;


			/* Extract piggy-backed descriptor. */
			/* Extract piggy-backed descriptor. */
			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
			desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi, &dump);
			if (desc_pld_len == -ENOMEM)
				goto restart;


			/*
			/*
			 * Copy needed information from the piggy-backed
			 * Copy needed information from the piggy-backed
@@ -580,16 +574,24 @@ static void cfhsi_rx_done_work(struct work_struct *work)
		}
		}
	}
	}


	if (unlikely(dump)) {
		size_t rx_offset = cfhsi->rx_ptr - cfhsi->rx_buf;
		dev_err(&cfhsi->ndev->dev, "%s: RX offset: %u.\n",
			__func__, (unsigned) rx_offset);
		print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE,
				cfhsi->rx_buf, cfhsi->rx_len + rx_offset);
	}

	memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
	if (desc_pld_len) {
	if (desc_pld_len) {
		cfhsi->rx_state = CFHSI_RX_STATE_PAYLOAD;
		cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD;
		cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
		cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
		cfhsi->rx_len = desc_pld_len;
		cfhsi->rx_len = desc_pld_len;
	} else {
	} else {
		cfhsi->rx_state = CFHSI_RX_STATE_DESC;
		cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
		cfhsi->rx_ptr = cfhsi->rx_buf;
		cfhsi->rx_ptr = cfhsi->rx_buf;
		cfhsi->rx_len = CFHSI_DESC_SZ;
		cfhsi->rx_len = CFHSI_DESC_SZ;
	}
	}
	clear_bit(CFHSI_PENDING_RX, &cfhsi->bits);


	if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
	if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
		/* Set up new transfer. */
		/* Set up new transfer. */
@@ -604,6 +606,26 @@ static void cfhsi_rx_done_work(struct work_struct *work)
			cfhsi->ndev->stats.rx_dropped++;
			cfhsi->ndev->stats.rx_dropped++;
		}
		}
	}
	}
	return;

restart:
	if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) {
		dev_err(&cfhsi->ndev->dev, "%s: No memory available "
			"in %d iterations.\n",
			__func__, CFHSI_MAX_RX_RETRIES);
		BUG();
	}
	mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
}

static void cfhsi_rx_slowpath(unsigned long arg)
{
	struct cfhsi *cfhsi = (struct cfhsi *)arg;

	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
		__func__);

	cfhsi_rx_done(cfhsi);
}
}


static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
@@ -617,12 +639,10 @@ static void cfhsi_rx_done_cb(struct cfhsi_drv *drv)
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
		return;
		return;


	set_bit(CFHSI_PENDING_RX, &cfhsi->bits);

	if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits))
	if (test_and_clear_bit(CFHSI_FLUSH_FIFO, &cfhsi->bits))
		wake_up_interruptible(&cfhsi->flush_fifo_wait);
		wake_up_interruptible(&cfhsi->flush_fifo_wait);
	else
	else
		queue_work(cfhsi->wq, &cfhsi->rx_done_work);
		cfhsi_rx_done(cfhsi);
}
}


static void cfhsi_wake_up(struct work_struct *work)
static void cfhsi_wake_up(struct work_struct *work)
@@ -651,9 +671,9 @@ static void cfhsi_wake_up(struct work_struct *work)
		__func__);
		__func__);


	/* Wait for acknowledge. */
	/* Wait for acknowledge. */
	ret = CFHSI_WAKEUP_TOUT;
	ret = CFHSI_WAKE_TOUT;
	wait_event_interruptible_timeout(cfhsi->wake_up_wait,
	ret = wait_event_interruptible_timeout(cfhsi->wake_up_wait,
					test_bit(CFHSI_WAKE_UP_ACK,
					test_and_clear_bit(CFHSI_WAKE_UP_ACK,
							&cfhsi->bits), ret);
							&cfhsi->bits), ret);
	if (unlikely(ret < 0)) {
	if (unlikely(ret < 0)) {
		/* Interrupted by signal. */
		/* Interrupted by signal. */
@@ -678,16 +698,11 @@ static void cfhsi_wake_up(struct work_struct *work)
	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);


	/* Resume read operation. */
	/* Resume read operation. */
	if (!test_bit(CFHSI_PENDING_RX, &cfhsi->bits)) {
	dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", __func__);
		dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n",
	res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len, cfhsi->dev);
			__func__);

		res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr,
	if (WARN_ON(res < 0))
				cfhsi->rx_len, cfhsi->dev);
		dev_err(&cfhsi->ndev->dev, "%s: RX err %d.\n", __func__, res);
		if (WARN_ON(res < 0)) {
			dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n",
				__func__, res);
		}
	}


	/* Clear power up acknowledment. */
	/* Clear power up acknowledment. */
	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
@@ -726,50 +741,29 @@ static void cfhsi_wake_up(struct work_struct *work)
				"%s: Failed to create HSI frame: %d.\n",
				"%s: Failed to create HSI frame: %d.\n",
				__func__, len);
				__func__, len);
	}
	}

}
}


static void cfhsi_wake_down(struct work_struct *work)
static void cfhsi_wake_down(struct work_struct *work)
{
{
	long ret;
	long ret;
	struct cfhsi *cfhsi = NULL;
	struct cfhsi *cfhsi = NULL;
	size_t fifo_occupancy;
	size_t fifo_occupancy = 0;
	int retry = CFHSI_WAKE_TOUT;


	cfhsi = container_of(work, struct cfhsi, wake_down_work);
	cfhsi = container_of(work, struct cfhsi, wake_down_work);
	dev_dbg(&cfhsi->ndev->dev, "%s.\n",
	dev_dbg(&cfhsi->ndev->dev, "%s.\n", __func__);
		__func__);


	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
	if (test_bit(CFHSI_SHUTDOWN, &cfhsi->bits))
		return;
		return;


	/* Check if there is something in FIFO. */
	if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
							&fifo_occupancy)))
		fifo_occupancy = 0;

	if (fifo_occupancy) {
		dev_dbg(&cfhsi->ndev->dev,
				"%s: %u words in RX FIFO, restart timer.\n",
				__func__, (unsigned) fifo_occupancy);
		spin_lock_bh(&cfhsi->lock);
		mod_timer(&cfhsi->timer,
				jiffies + CFHSI_INACTIVITY_TOUT);
		spin_unlock_bh(&cfhsi->lock);
		return;
	}

	/* Cancel pending RX requests */
	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);

	/* Deactivate wake line. */
	/* Deactivate wake line. */
	cfhsi->dev->cfhsi_wake_down(cfhsi->dev);
	cfhsi->dev->cfhsi_wake_down(cfhsi->dev);


	/* Wait for acknowledge. */
	/* Wait for acknowledge. */
	ret = CFHSI_WAKEUP_TOUT;
	ret = CFHSI_WAKE_TOUT;
	ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait,
	ret = wait_event_interruptible_timeout(cfhsi->wake_down_wait,
					test_bit(CFHSI_WAKE_DOWN_ACK,
					test_and_clear_bit(CFHSI_WAKE_DOWN_ACK,
							&cfhsi->bits),
							&cfhsi->bits), ret);
					ret);
	if (ret < 0) {
	if (ret < 0) {
		/* Interrupted by signal. */
		/* Interrupted by signal. */
		dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
		dev_info(&cfhsi->ndev->dev, "%s: Signalled: %ld.\n",
@@ -777,28 +771,31 @@ static void cfhsi_wake_down(struct work_struct *work)
		return;
		return;
	} else if (!ret) {
	} else if (!ret) {
		/* Timeout */
		/* Timeout */
		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n",
		dev_err(&cfhsi->ndev->dev, "%s: Timeout.\n", __func__);
			__func__);
	}
	}


	/* Clear power down acknowledment. */
	/* Check FIFO occupancy. */
	clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
	while (retry) {
		WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
							&fifo_occupancy));

		if (!fifo_occupancy)
			break;

		set_current_state(TASK_INTERRUPTIBLE);
		schedule_timeout(1);
		retry--;
	}

	if (!retry)
		dev_err(&cfhsi->ndev->dev, "%s: FIFO Timeout.\n", __func__);

	/* Clear AWAKE condition. */
	clear_bit(CFHSI_AWAKE, &cfhsi->bits);
	clear_bit(CFHSI_AWAKE, &cfhsi->bits);


	/* Check if there is something in FIFO. */
	/* Cancel pending RX requests. */
	if (WARN_ON(cfhsi->dev->cfhsi_fifo_occupancy(cfhsi->dev,
	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
							&fifo_occupancy)))
		fifo_occupancy = 0;


	if (fifo_occupancy) {
		dev_dbg(&cfhsi->ndev->dev,
				"%s: %u words in RX FIFO, wakeup forced.\n",
				__func__, (unsigned) fifo_occupancy);
		if (!test_and_set_bit(CFHSI_WAKE_UP, &cfhsi->bits))
			queue_work(cfhsi->wq, &cfhsi->wake_up_work);
	} else
		dev_dbg(&cfhsi->ndev->dev, "%s: Done.\n",
			__func__);
}
}


static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
static void cfhsi_wake_up_cb(struct cfhsi_drv *drv)
@@ -874,11 +871,7 @@ static int cfhsi_xmit(struct sk_buff *skb, struct net_device *dev)
	}
	}


	/* Delete inactivity timer if started. */
	/* Delete inactivity timer if started. */
#ifdef CONFIG_SMP
	timer_active = del_timer_sync(&cfhsi->timer);
	timer_active = del_timer_sync(&cfhsi->timer);
#else
	timer_active = del_timer(&cfhsi->timer);
#endif /* CONFIG_SMP */


	spin_unlock_bh(&cfhsi->lock);
	spin_unlock_bh(&cfhsi->lock);


@@ -962,7 +955,7 @@ int cfhsi_probe(struct platform_device *pdev)


	/* Initialize state vaiables. */
	/* Initialize state vaiables. */
	cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
	cfhsi->tx_state = CFHSI_TX_STATE_IDLE;
	cfhsi->rx_state = CFHSI_RX_STATE_DESC;
	cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;


	/* Set flow info */
	/* Set flow info */
	cfhsi->flow_off_sent = 0;
	cfhsi->flow_off_sent = 0;
@@ -1012,15 +1005,12 @@ int cfhsi_probe(struct platform_device *pdev)
	/* Initialize the work queues. */
	/* Initialize the work queues. */
	INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
	INIT_WORK(&cfhsi->wake_up_work, cfhsi_wake_up);
	INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
	INIT_WORK(&cfhsi->wake_down_work, cfhsi_wake_down);
	INIT_WORK(&cfhsi->rx_done_work, cfhsi_rx_done_work);
	INIT_WORK(&cfhsi->tx_done_work, cfhsi_tx_done_work);


	/* Clear all bit fields. */
	/* Clear all bit fields. */
	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_UP_ACK, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_DOWN_ACK, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
	clear_bit(CFHSI_WAKE_UP, &cfhsi->bits);
	clear_bit(CFHSI_AWAKE, &cfhsi->bits);
	clear_bit(CFHSI_AWAKE, &cfhsi->bits);
	clear_bit(CFHSI_PENDING_RX, &cfhsi->bits);


	/* Create work thread. */
	/* Create work thread. */
	cfhsi->wq = create_singlethread_workqueue(pdev->name);
	cfhsi->wq = create_singlethread_workqueue(pdev->name);
@@ -1040,6 +1030,10 @@ int cfhsi_probe(struct platform_device *pdev)
	init_timer(&cfhsi->timer);
	init_timer(&cfhsi->timer);
	cfhsi->timer.data = (unsigned long)cfhsi;
	cfhsi->timer.data = (unsigned long)cfhsi;
	cfhsi->timer.function = cfhsi_inactivity_tout;
	cfhsi->timer.function = cfhsi_inactivity_tout;
	/* Setup the slowpath RX timer. */
	init_timer(&cfhsi->rx_slowpath_timer);
	cfhsi->rx_slowpath_timer.data = (unsigned long)cfhsi;
	cfhsi->rx_slowpath_timer.function = cfhsi_rx_slowpath;


	/* Add CAIF HSI device to list. */
	/* Add CAIF HSI device to list. */
	spin_lock(&cfhsi_list_lock);
	spin_lock(&cfhsi_list_lock);
@@ -1110,12 +1104,9 @@ static void cfhsi_shutdown(struct cfhsi *cfhsi, bool remove_platform_dev)
	/* Flush workqueue */
	/* Flush workqueue */
	flush_workqueue(cfhsi->wq);
	flush_workqueue(cfhsi->wq);


	/* Delete timer if pending */
	/* Delete timers if pending */
#ifdef CONFIG_SMP
	del_timer_sync(&cfhsi->timer);
	del_timer_sync(&cfhsi->timer);
#else
	del_timer_sync(&cfhsi->rx_slowpath_timer);
	del_timer(&cfhsi->timer);
#endif /* CONFIG_SMP */


	/* Cancel pending RX request (if any) */
	/* Cancel pending RX request (if any) */
	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
	cfhsi->dev->cfhsi_rx_cancel(cfhsi->dev);
+20 −9
Original line number Original line Diff line number Diff line
@@ -75,18 +75,21 @@ struct cfhsi_desc {
#define CFHSI_WAKE_UP_ACK			1
#define CFHSI_WAKE_UP_ACK			1
#define CFHSI_WAKE_DOWN_ACK			2
#define CFHSI_WAKE_DOWN_ACK			2
#define CFHSI_AWAKE				3
#define CFHSI_AWAKE				3
#define CFHSI_PENDING_RX			4
#define CFHSI_WAKELOCK_HELD			4
#define CFHSI_SHUTDOWN				6
#define CFHSI_SHUTDOWN				5
#define CFHSI_FLUSH_FIFO			7
#define CFHSI_FLUSH_FIFO			6


#ifndef CFHSI_INACTIVITY_TOUT
#ifndef CFHSI_INACTIVITY_TOUT
#define CFHSI_INACTIVITY_TOUT			(1 * HZ)
#define CFHSI_INACTIVITY_TOUT			(1 * HZ)
#endif /* CFHSI_INACTIVITY_TOUT */
#endif /* CFHSI_INACTIVITY_TOUT */


#ifndef CFHSI_WAKEUP_TOUT
#ifndef CFHSI_WAKE_TOUT
#define CFHSI_WAKEUP_TOUT			(3 * HZ)
#define CFHSI_WAKE_TOUT			(3 * HZ)
#endif /* CFHSI_WAKEUP_TOUT */
#endif /* CFHSI_WAKE_TOUT */


#ifndef CFHSI_MAX_RX_RETRIES
#define CFHSI_MAX_RX_RETRIES		(10 * HZ)
#endif


/* Structure implemented by the CAIF HSI driver. */
/* Structure implemented by the CAIF HSI driver. */
struct cfhsi_drv {
struct cfhsi_drv {
@@ -109,6 +112,15 @@ struct cfhsi_dev {
	struct cfhsi_drv *drv;
	struct cfhsi_drv *drv;
};
};


/* Structure holds status of received CAIF frames processing */
struct cfhsi_rx_state {
	int state;
	int nfrms;
	int pld_len;
	int retries;
	bool piggy_desc;
};

/* Structure implemented by CAIF HSI drivers. */
/* Structure implemented by CAIF HSI drivers. */
struct cfhsi {
struct cfhsi {
	struct caif_dev_common cfdev;
	struct caif_dev_common cfdev;
@@ -118,7 +130,7 @@ struct cfhsi {
	struct cfhsi_drv drv;
	struct cfhsi_drv drv;
	struct cfhsi_dev *dev;
	struct cfhsi_dev *dev;
	int tx_state;
	int tx_state;
	int rx_state;
	struct cfhsi_rx_state rx_state;
	int rx_len;
	int rx_len;
	u8 *rx_ptr;
	u8 *rx_ptr;
	u8 *tx_buf;
	u8 *tx_buf;
@@ -130,13 +142,12 @@ struct cfhsi {
	struct list_head list;
	struct list_head list;
	struct work_struct wake_up_work;
	struct work_struct wake_up_work;
	struct work_struct wake_down_work;
	struct work_struct wake_down_work;
	struct work_struct rx_done_work;
	struct work_struct tx_done_work;
	struct workqueue_struct *wq;
	struct workqueue_struct *wq;
	wait_queue_head_t wake_up_wait;
	wait_queue_head_t wake_up_wait;
	wait_queue_head_t wake_down_wait;
	wait_queue_head_t wake_down_wait;
	wait_queue_head_t flush_fifo_wait;
	wait_queue_head_t flush_fifo_wait;
	struct timer_list timer;
	struct timer_list timer;
	struct timer_list rx_slowpath_timer;
	unsigned long bits;
	unsigned long bits;
};
};