diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-05-30 19:06:35 -0400 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-05-31 15:40:14 -0400 |
commit | 2aef469a35a273609beaa7094d5a07c1f6d75285 (patch) | |
tree | 3aad939f26bee97a7998b67a634478d081192cee /drivers/firewire/fw-ohci.c | |
parent | b32744751e751e0abb184cf3b7359c9387a4bdf8 (diff) |
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: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-ohci.c')
-rw-r--r-- | drivers/firewire/fw-ohci.c | 187 |
1 files changed, 123 insertions, 64 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 2e4cfa57126d..0d08bf9b78c2 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c | |||
@@ -417,12 +417,21 @@ ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs) | |||
417 | ctx->current_buffer = ab.next; | 417 | ctx->current_buffer = ab.next; |
418 | ctx->pointer = ctx->current_buffer->data; | 418 | ctx->pointer = ctx->current_buffer->data; |
419 | 419 | ||
420 | reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), | 420 | return 0; |
421 | le32_to_cpu(ab.descriptor.branch_address)); | 421 | } |
422 | |||
423 | static void ar_context_run(struct ar_context *ctx) | ||
424 | { | ||
425 | struct ar_buffer *ab = ctx->current_buffer; | ||
426 | dma_addr_t ab_bus; | ||
427 | size_t offset; | ||
428 | |||
429 | offset = offsetof(struct ar_buffer, data); | ||
430 | ab_bus = ab->descriptor.data_address - offset; | ||
431 | |||
432 | reg_write(ctx->ohci, COMMAND_PTR(ctx->regs), ab_bus | 1); | ||
422 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN); | 433 | reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_RUN); |
423 | flush_writes(ctx->ohci); | 434 | flush_writes(ctx->ohci); |
424 | |||
425 | return 0; | ||
426 | } | 435 | } |
427 | 436 | ||
428 | static void context_tasklet(unsigned long data) | 437 | static void context_tasklet(unsigned long data) |
@@ -1039,11 +1048,78 @@ static irqreturn_t irq_handler(int irq, void *data) | |||
1039 | return IRQ_HANDLED; | 1048 | return IRQ_HANDLED; |
1040 | } | 1049 | } |
1041 | 1050 | ||
1051 | static int software_reset(struct fw_ohci *ohci) | ||
1052 | { | ||
1053 | int i; | ||
1054 | |||
1055 | reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); | ||
1056 | |||
1057 | for (i = 0; i < OHCI_LOOP_COUNT; i++) { | ||
1058 | if ((reg_read(ohci, OHCI1394_HCControlSet) & | ||
1059 | OHCI1394_HCControl_softReset) == 0) | ||
1060 | return 0; | ||
1061 | msleep(1); | ||
1062 | } | ||
1063 | |||
1064 | return -EBUSY; | ||
1065 | } | ||
1066 | |||
1042 | static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) | 1067 | static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length) |
1043 | { | 1068 | { |
1044 | struct fw_ohci *ohci = fw_ohci(card); | 1069 | struct fw_ohci *ohci = fw_ohci(card); |
1045 | struct pci_dev *dev = to_pci_dev(card->device); | 1070 | struct pci_dev *dev = to_pci_dev(card->device); |
1046 | 1071 | ||
1072 | if (software_reset(ohci)) { | ||
1073 | fw_error("Failed to reset ohci card.\n"); | ||
1074 | return -EBUSY; | ||
1075 | } | ||
1076 | |||
1077 | /* | ||
1078 | * Now enable LPS, which we need in order to start accessing | ||
1079 | * most of the registers. In fact, on some cards (ALI M5251), | ||
1080 | * accessing registers in the SClk domain without LPS enabled | ||
1081 | * will lock up the machine. Wait 50msec to make sure we have | ||
1082 | * full link enabled. | ||
1083 | */ | ||
1084 | reg_write(ohci, OHCI1394_HCControlSet, | ||
1085 | OHCI1394_HCControl_LPS | | ||
1086 | OHCI1394_HCControl_postedWriteEnable); | ||
1087 | flush_writes(ohci); | ||
1088 | msleep(50); | ||
1089 | |||
1090 | reg_write(ohci, OHCI1394_HCControlClear, | ||
1091 | OHCI1394_HCControl_noByteSwapData); | ||
1092 | |||
1093 | reg_write(ohci, OHCI1394_LinkControlSet, | ||
1094 | OHCI1394_LinkControl_rcvSelfID | | ||
1095 | OHCI1394_LinkControl_cycleTimerEnable | | ||
1096 | OHCI1394_LinkControl_cycleMaster); | ||
1097 | |||
1098 | reg_write(ohci, OHCI1394_ATRetries, | ||
1099 | OHCI1394_MAX_AT_REQ_RETRIES | | ||
1100 | (OHCI1394_MAX_AT_RESP_RETRIES << 4) | | ||
1101 | (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); | ||
1102 | |||
1103 | ar_context_run(&ohci->ar_request_ctx); | ||
1104 | ar_context_run(&ohci->ar_response_ctx); | ||
1105 | |||
1106 | reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); | ||
1107 | reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); | ||
1108 | reg_write(ohci, OHCI1394_IntEventClear, ~0); | ||
1109 | reg_write(ohci, OHCI1394_IntMaskClear, ~0); | ||
1110 | reg_write(ohci, OHCI1394_IntMaskSet, | ||
1111 | OHCI1394_selfIDComplete | | ||
1112 | OHCI1394_RQPkt | OHCI1394_RSPkt | | ||
1113 | OHCI1394_reqTxComplete | OHCI1394_respTxComplete | | ||
1114 | OHCI1394_isochRx | OHCI1394_isochTx | | ||
1115 | OHCI1394_masterIntEnable | | ||
1116 | OHCI1394_cycle64Seconds); | ||
1117 | |||
1118 | /* Activate link_on bit and contender bit in our self ID packets.*/ | ||
1119 | if (ohci_update_phy_reg(card, 4, 0, | ||
1120 | PHY_LINK_ACTIVE | PHY_CONTENDER) < 0) | ||
1121 | return -EIO; | ||
1122 | |||
1047 | /* | 1123 | /* |
1048 | * When the link is not yet enabled, the atomic config rom | 1124 | * When the link is not yet enabled, the atomic config rom |
1049 | * update mechanism described below in ohci_set_config_rom() | 1125 | * update mechanism described below in ohci_set_config_rom() |
@@ -1701,22 +1777,6 @@ static const struct fw_card_driver ohci_driver = { | |||
1701 | .stop_iso = ohci_stop_iso, | 1777 | .stop_iso = ohci_stop_iso, |
1702 | }; | 1778 | }; |
1703 | 1779 | ||
1704 | static int software_reset(struct fw_ohci *ohci) | ||
1705 | { | ||
1706 | int i; | ||
1707 | |||
1708 | reg_write(ohci, OHCI1394_HCControlSet, OHCI1394_HCControl_softReset); | ||
1709 | |||
1710 | for (i = 0; i < OHCI_LOOP_COUNT; i++) { | ||
1711 | if ((reg_read(ohci, OHCI1394_HCControlSet) & | ||
1712 | OHCI1394_HCControl_softReset) == 0) | ||
1713 | return 0; | ||
1714 | msleep(1); | ||
1715 | } | ||
1716 | |||
1717 | return -EBUSY; | ||
1718 | } | ||
1719 | |||
1720 | static int __devinit | 1780 | static int __devinit |
1721 | pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) | 1781 | pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) |
1722 | { | 1782 | { |
@@ -1762,33 +1822,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) | |||
1762 | goto fail_iomem; | 1822 | goto fail_iomem; |
1763 | } | 1823 | } |
1764 | 1824 | ||
1765 | if (software_reset(ohci)) { | ||
1766 | fw_error("Failed to reset ohci card.\n"); | ||
1767 | err = -EBUSY; | ||
1768 | goto fail_registers; | ||
1769 | } | ||
1770 | |||
1771 | /* | ||
1772 | * Now enable LPS, which we need in order to start accessing | ||
1773 | * most of the registers. In fact, on some cards (ALI M5251), | ||
1774 | * accessing registers in the SClk domain without LPS enabled | ||
1775 | * will lock up the machine. Wait 50msec to make sure we have | ||
1776 | * full link enabled. | ||
1777 | */ | ||
1778 | reg_write(ohci, OHCI1394_HCControlSet, | ||
1779 | OHCI1394_HCControl_LPS | | ||
1780 | OHCI1394_HCControl_postedWriteEnable); | ||
1781 | flush_writes(ohci); | ||
1782 | msleep(50); | ||
1783 | |||
1784 | reg_write(ohci, OHCI1394_HCControlClear, | ||
1785 | OHCI1394_HCControl_noByteSwapData); | ||
1786 | |||
1787 | reg_write(ohci, OHCI1394_LinkControlSet, | ||
1788 | OHCI1394_LinkControl_rcvSelfID | | ||
1789 | OHCI1394_LinkControl_cycleTimerEnable | | ||
1790 | OHCI1394_LinkControl_cycleMaster); | ||
1791 | |||
1792 | ar_context_init(&ohci->ar_request_ctx, ohci, | 1825 | ar_context_init(&ohci->ar_request_ctx, ohci, |
1793 | OHCI1394_AsReqRcvContextControlSet); | 1826 | OHCI1394_AsReqRcvContextControlSet); |
1794 | 1827 | ||
@@ -1801,11 +1834,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) | |||
1801 | context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE, | 1834 | context_init(&ohci->at_response_ctx, ohci, AT_BUFFER_SIZE, |
1802 | OHCI1394_AsRspTrContextControlSet, handle_at_packet); | 1835 | OHCI1394_AsRspTrContextControlSet, handle_at_packet); |
1803 | 1836 | ||
1804 | reg_write(ohci, OHCI1394_ATRetries, | ||
1805 | OHCI1394_MAX_AT_REQ_RETRIES | | ||
1806 | (OHCI1394_MAX_AT_RESP_RETRIES << 4) | | ||
1807 | (OHCI1394_MAX_PHYS_RESP_RETRIES << 8)); | ||
1808 | |||
1809 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); | 1837 | reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, ~0); |
1810 | ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); | 1838 | ohci->it_context_mask = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); |
1811 | reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0); | 1839 | reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, ~0); |
@@ -1835,18 +1863,6 @@ pci_probe(struct pci_dev *dev, const struct pci_device_id *ent) | |||
1835 | goto fail_registers; | 1863 | goto fail_registers; |
1836 | } | 1864 | } |
1837 | 1865 | ||
1838 | reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus); | ||
1839 | reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000); | ||
1840 | reg_write(ohci, OHCI1394_IntEventClear, ~0); | ||
1841 | reg_write(ohci, OHCI1394_IntMaskClear, ~0); | ||
1842 | reg_write(ohci, OHCI1394_IntMaskSet, | ||
1843 | OHCI1394_selfIDComplete | | ||
1844 | OHCI1394_RQPkt | OHCI1394_RSPkt | | ||
1845 | OHCI1394_reqTxComplete | OHCI1394_respTxComplete | | ||
1846 | OHCI1394_isochRx | OHCI1394_isochTx | | ||
1847 | OHCI1394_masterIntEnable | | ||
1848 | OHCI1394_cycle64Seconds); | ||
1849 | |||
1850 | bus_options = reg_read(ohci, OHCI1394_BusOptions); | 1866 | bus_options = reg_read(ohci, OHCI1394_BusOptions); |
1851 | max_receive = (bus_options >> 12) & 0xf; | 1867 | max_receive = (bus_options >> 12) & 0xf; |
1852 | link_speed = bus_options & 0x7; | 1868 | link_speed = bus_options & 0x7; |
@@ -1908,6 +1924,45 @@ static void pci_remove(struct pci_dev *dev) | |||
1908 | fw_notify("Removed fw-ohci device.\n"); | 1924 | fw_notify("Removed fw-ohci device.\n"); |
1909 | } | 1925 | } |
1910 | 1926 | ||
1927 | #ifdef CONFIG_PM | ||
1928 | static int pci_suspend(struct pci_dev *pdev, pm_message_t state) | ||
1929 | { | ||
1930 | struct fw_ohci *ohci = pci_get_drvdata(pdev); | ||
1931 | int err; | ||
1932 | |||
1933 | software_reset(ohci); | ||
1934 | free_irq(pdev->irq, ohci); | ||
1935 | err = pci_save_state(pdev); | ||
1936 | if (err) { | ||
1937 | fw_error("pci_save_state failed with %d", err); | ||
1938 | return err; | ||
1939 | } | ||
1940 | err = pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
1941 | if (err) { | ||
1942 | fw_error("pci_set_power_state failed with %d", err); | ||
1943 | return err; | ||
1944 | } | ||
1945 | |||
1946 | return 0; | ||
1947 | } | ||
1948 | |||
1949 | static int pci_resume(struct pci_dev *pdev) | ||
1950 | { | ||
1951 | struct fw_ohci *ohci = pci_get_drvdata(pdev); | ||
1952 | int err; | ||
1953 | |||
1954 | pci_set_power_state(pdev, PCI_D0); | ||
1955 | pci_restore_state(pdev); | ||
1956 | err = pci_enable_device(pdev); | ||
1957 | if (err) { | ||
1958 | fw_error("pci_enable_device failed with %d", err); | ||
1959 | return err; | ||
1960 | } | ||
1961 | |||
1962 | return ohci_enable(&ohci->card, ohci->config_rom, CONFIG_ROM_SIZE); | ||
1963 | } | ||
1964 | #endif | ||
1965 | |||
1911 | static struct pci_device_id pci_table[] = { | 1966 | static struct pci_device_id pci_table[] = { |
1912 | { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) }, | 1967 | { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_FIREWIRE_OHCI, ~0) }, |
1913 | { } | 1968 | { } |
@@ -1920,6 +1975,10 @@ static struct pci_driver fw_ohci_pci_driver = { | |||
1920 | .id_table = pci_table, | 1975 | .id_table = pci_table, |
1921 | .probe = pci_probe, | 1976 | .probe = pci_probe, |
1922 | .remove = pci_remove, | 1977 | .remove = pci_remove, |
1978 | #ifdef CONFIG_PM | ||
1979 | .resume = pci_resume, | ||
1980 | .suspend = pci_suspend, | ||
1981 | #endif | ||
1923 | }; | 1982 | }; |
1924 | 1983 | ||
1925 | MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); | 1984 | MODULE_AUTHOR("Kristian Hoegsberg <krh@bitplanet.net>"); |