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

Commit 2aef469a authored by Kristian Høgsberg's avatar Kristian Høgsberg Committed by Stefan Richter
Browse files

firewire: Implement suspend/resume PCI driver hooks.



It's a low-impact design, that just makes suspend/resume look like
a bus reset to the upper level drivers, but it should be sufficient.

Signed-off-by: default avatarKristian Høgsberg <krh@redhat.com>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent b3274475
Loading
Loading
Loading
Loading
+0 −5
Original line number Original line Diff line number Diff line
@@ -407,11 +407,6 @@ fw_card_add(struct fw_card *card,
	card->link_speed = link_speed;
	card->link_speed = link_speed;
	card->guid = guid;
	card->guid = guid;


	/* Activate link_on bit and contender bit in our self ID packets.*/
	if (card->driver->update_phy_reg(card, 4, 0,
					 PHY_LINK_ACTIVE | PHY_CONTENDER) < 0)
		return -EIO;

	/*
	/*
	 * The subsystem grabs a reference when the card is added and
	 * The subsystem grabs a reference when the card is added and
	 * drops it when the driver calls fw_core_remove_card.
	 * drops it when the driver calls fw_core_remove_card.
+123 −64
Original line number Original line Diff line number Diff line
@@ -417,12 +417,21 @@ ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs)
	ctx->current_buffer = ab.next;
	ctx->current_buffer = ab.next;
	ctx->pointer = ctx->current_buffer->data;
	ctx->pointer = ctx->current_buffer->data;


	reg_write(ctx->ohci, COMMAND_PTR(ctx->regs),
	return 0;
		  le32_to_cpu(ab.descriptor.branch_address));
}

static void ar_context_run(struct ar_context *ctx)
{
	struct ar_buffer *ab = ctx->current_buffer;
	dma_addr_t ab_bus;
	size_t offset;

	offset = offsetof(struct ar_buffer, data);
	ab_bus = ab->descriptor.data_address - offset;

	reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ab_bus | 1);
	reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN);
	reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN);
	flush_writes(ctx->ohci);
	flush_writes(ctx->ohci);

	return 0;
}
}


static void context_tasklet(unsigned long data)
static void context_tasklet(unsigned long data)
@@ -1039,11 +1048,78 @@ static irqreturn_t irq_handler(int irq, void *data)
	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}


static int software_reset(struct fw_ohci *ohci)
{
	int i;

	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);

	for (i = 0; i < OHCI_LOOP_COUNT; i++) {
		if ((reg_read(ohci, OHCI1394_HCControlSet) &
		     OHCI1394_HCControl_softReset) == 0)
			return 0;
		msleep(1);
	}

	return -EBUSY;
}

static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
{
{
	struct fw_ohci *ohci = fw_ohci(card);
	struct fw_ohci *ohci = fw_ohci(card);
	struct pci_dev *dev = to_pci_dev(card->device);
	struct pci_dev *dev = to_pci_dev(card->device);


	if (software_reset(ohci)) {
		fw_error("Failed to reset ohci card.\n");
		return -EBUSY;
	}

	/*
	 * Now enable LPS, which we need in order to start accessing
	 * most of the registers.  In fact, on some cards (ALI M5251),
	 * accessing registers in the SClk domain without LPS enabled
	 * will lock up the machine.  Wait 50msec to make sure we have
	 * full link enabled.
	 */
	reg_write(ohci, OHCI1394_HCControlSet,
		  OHCI1394_HCControl_LPS |
		  OHCI1394_HCControl_postedWriteEnable);
	flush_writes(ohci);
	msleep(50);

	reg_write(ohci, OHCI1394_HCControlClear,
		  OHCI1394_HCControl_noByteSwapData);

	reg_write(ohci, OHCI1394_LinkControlSet,
		  OHCI1394_LinkControl_rcvSelfID |
		  OHCI1394_LinkControl_cycleTimerEnable |
		  OHCI1394_LinkControl_cycleMaster);

	reg_write(ohci, OHCI1394_ATRetries,
		  OHCI1394_MAX_AT_REQ_RETRIES |
		  (OHCI1394_MAX_AT_RESP_RETRIES << 4) |
		  (OHCI1394_MAX_PHYS_RESP_RETRIES << 8));

	ar_context_run(&ohci->ar_request_ctx);
	ar_context_run(&ohci->ar_response_ctx);

	reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
	reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000);
	reg_write(ohci, OHCI1394_IntEventClear, ~0);
	reg_write(ohci, OHCI1394_IntMaskClear, ~0);
	reg_write(ohci, OHCI1394_IntMaskSet,
		  OHCI1394_selfIDComplete |
		  OHCI1394_RQPkt | OHCI1394_RSPkt |
		  OHCI1394_reqTxComplete | OHCI1394_respTxComplete |
		  OHCI1394_isochRx | OHCI1394_isochTx |
		  OHCI1394_masterIntEnable |
		  OHCI1394_cycle64Seconds);

	/* Activate link_on bit and contender bit in our self ID packets.*/
	if (ohci_update_phy_reg(card, 4, 0,
				PHY_LINK_ACTIVE | PHY_CONTENDER) < 0)
		return -EIO;

	/*
	/*
	 * When the link is not yet enabled, the atomic config rom
	 * When the link is not yet enabled, the atomic config rom
	 * update mechanism described below in ohci_set_config_rom()
	 * update mechanism described below in ohci_set_config_rom()
@@ -1701,22 +1777,6 @@ static const struct fw_card_driver ohci_driver = {
	.stop_iso		= ohci_stop_iso,
	.stop_iso		= ohci_stop_iso,
};
};


static int software_reset(struct fw_ohci *ohci)
{
	int i;

	reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset);

	for (i = 0; i < OHCI_LOOP_COUNT; i++) {
		if ((reg_read(ohci, OHCI1394_HCControlSet) &
		     OHCI1394_HCControl_softReset) == 0)
			return 0;
		msleep(1);
	}

	return -EBUSY;
}

static int __devinit
static int __devinit
pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
{
{
@@ -1762,33 +1822,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
		goto fail_iomem;
		goto fail_iomem;
	}
	}


	if (software_reset(ohci)) {
		fw_error("Failed to reset ohci card.\n");
		err = -EBUSY;
		goto fail_registers;
	}

	/*
	 * Now enable LPS, which we need in order to start accessing
	 * most of the registers.  In fact, on some cards (ALI M5251),
	 * accessing registers in the SClk domain without LPS enabled
	 * will lock up the machine.  Wait 50msec to make sure we have
	 * full link enabled.
	 */
	reg_write(ohci, OHCI1394_HCControlSet,
		  OHCI1394_HCControl_LPS |
		  OHCI1394_HCControl_postedWriteEnable);
	flush_writes(ohci);
	msleep(50);

	reg_write(ohci, OHCI1394_HCControlClear,
		  OHCI1394_HCControl_noByteSwapData);

	reg_write(ohci, OHCI1394_LinkControlSet,
		  OHCI1394_LinkControl_rcvSelfID |
		  OHCI1394_LinkControl_cycleTimerEnable |
		  OHCI1394_LinkControl_cycleMaster);

	ar_context_init(&ohci->ar_request_ctx, ohci,
	ar_context_init(&ohci->ar_request_ctx, ohci,
			OHCI1394_AsReqRcvContextControlSet);
			OHCI1394_AsReqRcvContextControlSet);


@@ -1801,11 +1834,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
	context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE,
	context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE,
		     OHCI1394_AsRspTrContextControlSet, handle_at_packet);
		     OHCI1394_AsRspTrContextControlSet, handle_at_packet);


	reg_write(ohci, OHCI1394_ATRetries,
		  OHCI1394_MAX_AT_REQ_RETRIES |
		  (OHCI1394_MAX_AT_RESP_RETRIES << 4) |
		  (OHCI1394_MAX_PHYS_RESP_RETRIES << 8));

	reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0);
	reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0);
	ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
	ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet);
	reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0);
	reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0);
@@ -1835,18 +1863,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
		goto fail_registers;
		goto fail_registers;
	}
	}


	reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
	reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000);
	reg_write(ohci, OHCI1394_IntEventClear, ~0);
	reg_write(ohci, OHCI1394_IntMaskClear, ~0);
	reg_write(ohci, OHCI1394_IntMaskSet,
		  OHCI1394_selfIDComplete |
		  OHCI1394_RQPkt | OHCI1394_RSPkt |
		  OHCI1394_reqTxComplete | OHCI1394_respTxComplete |
		  OHCI1394_isochRx | OHCI1394_isochTx |
		  OHCI1394_masterIntEnable |
		  OHCI1394_cycle64Seconds);

	bus_options = reg_read(ohci, OHCI1394_BusOptions);
	bus_options = reg_read(ohci, OHCI1394_BusOptions);
	max_receive = (bus_options >> 12) & 0xf;
	max_receive = (bus_options >> 12) & 0xf;
	link_speed = bus_options & 0x7;
	link_speed = bus_options & 0x7;
@@ -1908,6 +1924,45 @@ static void pci_remove(struct pci_dev *dev)
	fw_notify("Removed fw-ohci device.\n");
	fw_notify("Removed fw-ohci device.\n");
}
}


#ifdef CONFIG_PM
static int pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
	struct fw_ohci *ohci = pci_get_drvdata(pdev);
	int err;

	software_reset(ohci);
	free_irq(pdev->irq, ohci);
	err = pci_save_state(pdev);
	if (err) {
		fw_error("pci_save_state failed with %d", err);
		return err;
	}
	err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
	if (err) {
		fw_error("pci_set_power_state failed with %d", err);
		return err;
	}

	return 0;
}

static int pci_resume(struct pci_dev *pdev)
{
	struct fw_ohci *ohci = pci_get_drvdata(pdev);
	int err;

	pci_set_power_state(pdev, PCI_D0);
	pci_restore_state(pdev);
	err = pci_enable_device(pdev);
	if (err) {
		fw_error("pci_enable_device failed with %d", err);
		return err;
	}

	return ohci_enable(&ohci->card, ohci->config_rom, CONFIG_ROM_SIZE);
}
#endif

static struct pci_device_id pci_table[] = {
static struct pci_device_id pci_table[] = {
	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) },
	{ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) },
	{ }
	{ }
@@ -1920,6 +1975,10 @@ static struct pci_driver fw_ohci_pci_driver = {
	.id_table	= pci_table,
	.id_table	= pci_table,
	.probe		= pci_probe,
	.probe		= pci_probe,
	.remove		= pci_remove,
	.remove		= pci_remove,
#ifdef CONFIG_PM
	.resume		= pci_resume,
	.suspend	= pci_suspend,
#endif
};
};


MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");
MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>");