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

Commit 88de1b2f authored by Alex Dubov's avatar Alex Dubov Committed by Pierre Ossman
Browse files

tifm_7xx1: fix adapter resume function



Fixes to the adapter resume function to correctly handle all possible cases:
1. Card is removed during suspend
2. Card is inserted during suspend into previously empty socket
3. Card is replaced during suspend by same or different media type card.

Signed-off-by: default avatarAlex Dubov <oakad@yahoo.com>
Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 2428a8fe
Loading
Loading
Loading
Loading
+31 −27
Original line number Diff line number Diff line
@@ -220,7 +220,8 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state)
static int tifm_7xx1_resume(struct pci_dev *dev)
{
	struct tifm_adapter *fm = pci_get_drvdata(dev);
	int cnt, rc;
	int rc;
	unsigned int good_sockets = 0, bad_sockets = 0;
	unsigned long flags;
	unsigned char new_ids[fm->num_sockets];
	DECLARE_COMPLETION_ONSTACK(finish_resume);
@@ -234,46 +235,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev)

	dev_dbg(&dev->dev, "resuming host\n");

	for (cnt = 0; cnt < fm->num_sockets; cnt++)
		new_ids[cnt] = tifm_7xx1_toggle_sock_power(
					tifm_7xx1_sock_addr(fm->addr, cnt));
	for (rc = 0; rc < fm->num_sockets; rc++)
		new_ids[rc] = tifm_7xx1_toggle_sock_power(
					tifm_7xx1_sock_addr(fm->addr, rc));
	spin_lock_irqsave(&fm->lock, flags);
	fm->socket_change_set = 0;
	for (cnt = 0; cnt < fm->num_sockets; cnt++) {
		if (fm->sockets[cnt]) {
			if (fm->sockets[cnt]->type == new_ids[cnt])
				fm->socket_change_set |= 1 << cnt;

			fm->sockets[cnt]->type = new_ids[cnt];
	for (rc = 0; rc < fm->num_sockets; rc++) {
		if (fm->sockets[rc]) {
			if (fm->sockets[rc]->type == new_ids[rc])
				good_sockets |= 1 << rc;
			else
				bad_sockets |= 1 << rc;
		}
	}

	writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	if (!fm->socket_change_set) {
		spin_unlock_irqrestore(&fm->lock, flags);
		return 0;
	} else {
	dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n",
		good_sockets, bad_sockets);

	fm->socket_change_set = 0;
	if (good_sockets) {
		fm->finish_me = &finish_resume;
		spin_unlock_irqrestore(&fm->lock, flags);
		rc = wait_for_completion_timeout(&finish_resume, HZ);
		dev_dbg(&dev->dev, "wait returned %d\n", rc);
		writel(TIFM_IRQ_FIFOMASK(good_sockets)
		       | TIFM_IRQ_CARDMASK(good_sockets),
		       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
		writel(TIFM_IRQ_FIFOMASK(good_sockets)
		       | TIFM_IRQ_CARDMASK(good_sockets),
		       fm->addr + FM_SET_INTERRUPT_ENABLE);
		spin_lock_irqsave(&fm->lock, flags);
		fm->finish_me = NULL;
		fm->socket_change_set ^= good_sockets & fm->socket_change_set;
	}

	wait_for_completion_timeout(&finish_resume, HZ);
	fm->socket_change_set |= bad_sockets;
	if (fm->socket_change_set)
		tifm_queue_work(&fm->media_switcher);

	spin_lock_irqsave(&fm->lock, flags);
	fm->finish_me = NULL;
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
	writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set)
	       | TIFM_IRQ_CARDMASK(fm->socket_change_set),
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	spin_unlock_irqrestore(&fm->lock, flags);
	writel(TIFM_IRQ_ENABLE,
	       fm->addr + FM_SET_INTERRUPT_ENABLE);
	fm->socket_change_set = 0;

	spin_unlock_irqrestore(&fm->lock, flags);
	return 0;
}