aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/ohci.c
diff options
context:
space:
mode:
authorStephan Gatzka <stephan@gatzka.org>2011-09-12 16:23:53 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2011-09-16 16:30:28 -0400
commit25935ebebd861182ac58ecea67718bb6a617c7cb (patch)
treedec9589910ff692c473fe1a1d6e9eb1d7c670ec6 /drivers/firewire/ohci.c
parent2d7a36e23300d268599f6eae4093643d22fbb356 (diff)
firewire: ohci: Add support for TSB41BA3D phy
This patch implements a work around for the Texas Instruments PHY TSB41BA3D. This phy has a bug at least in combination with the TI LLCs TSB82AA2B and TSB12LV26. The selfid coming from the locally connected phy is not propagated into the selfid buffer of the OHCI (see http://www.ti.com/litv/pdf/sllz059 for details). The main idea is to construct the selfid ourselves. Signed-off-by: Stephan Gatzka <stephan@gatzka.org> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/ohci.c')
-rw-r--r--drivers/firewire/ohci.c185
1 files changed, 183 insertions, 2 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index c026f46fc157..b983581cfe35 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -264,6 +264,8 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
264#define PCI_DEVICE_ID_AGERE_FW643 0x5901 264#define PCI_DEVICE_ID_AGERE_FW643 0x5901
265#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380 265#define PCI_DEVICE_ID_JMICRON_JMB38X_FW 0x2380
266#define PCI_DEVICE_ID_TI_TSB12LV22 0x8009 266#define PCI_DEVICE_ID_TI_TSB12LV22 0x8009
267#define PCI_DEVICE_ID_TI_TSB12LV26 0x8020
268#define PCI_DEVICE_ID_TI_TSB82AA2 0x8025
267#define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd 269#define PCI_VENDOR_ID_PINNACLE_SYSTEMS 0x11bd
268 270
269#define QUIRK_CYCLE_TIMER 1 271#define QUIRK_CYCLE_TIMER 1
@@ -271,6 +273,7 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
271#define QUIRK_BE_HEADERS 4 273#define QUIRK_BE_HEADERS 4
272#define QUIRK_NO_1394A 8 274#define QUIRK_NO_1394A 8
273#define QUIRK_NO_MSI 16 275#define QUIRK_NO_MSI 16
276#define QUIRK_TI_SLLZ059 32
274 277
275/* In case of multiple matches in ohci_quirks[], only the first one is used. */ 278/* In case of multiple matches in ohci_quirks[], only the first one is used. */
276static const struct { 279static const struct {
@@ -300,6 +303,12 @@ static const struct {
300 {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID, 303 {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV22, PCI_ANY_ID,
301 QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A}, 304 QUIRK_CYCLE_TIMER | QUIRK_RESET_PACKET | QUIRK_NO_1394A},
302 305
306 {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB12LV26, PCI_ANY_ID,
307 QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
308
309 {PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_TSB82AA2, PCI_ANY_ID,
310 QUIRK_RESET_PACKET | QUIRK_TI_SLLZ059},
311
303 {PCI_VENDOR_ID_TI, PCI_ANY_ID, PCI_ANY_ID, 312 {PCI_VENDOR_ID_TI, PCI_ANY_ID, PCI_ANY_ID,
304 QUIRK_RESET_PACKET}, 313 QUIRK_RESET_PACKET},
305 314
@@ -316,6 +325,7 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0"
316 ", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS) 325 ", AR/selfID endianess = " __stringify(QUIRK_BE_HEADERS)
317 ", no 1394a enhancements = " __stringify(QUIRK_NO_1394A) 326 ", no 1394a enhancements = " __stringify(QUIRK_NO_1394A)
318 ", disable MSI = " __stringify(QUIRK_NO_MSI) 327 ", disable MSI = " __stringify(QUIRK_NO_MSI)
328 ", workaround for TI SLLZ059 errata = " __stringify(QUIRK_TI_SLLZ059)
319 ")"); 329 ")");
320 330
321#define OHCI_PARAM_DEBUG_AT_AR 1 331#define OHCI_PARAM_DEBUG_AT_AR 1
@@ -1714,6 +1724,114 @@ static u32 update_bus_time(struct fw_ohci *ohci)
1714 return ohci->bus_time | cycle_time_seconds; 1724 return ohci->bus_time | cycle_time_seconds;
1715} 1725}
1716 1726
1727static int get_status_for_port(struct fw_ohci *ohci, int port_index)
1728{
1729 int reg;
1730
1731 mutex_lock(&ohci->phy_reg_mutex);
1732 reg = write_phy_reg(ohci, 7, port_index);
1733 mutex_unlock(&ohci->phy_reg_mutex);
1734 if (reg < 0)
1735 return reg;
1736
1737 mutex_lock(&ohci->phy_reg_mutex);
1738 reg = read_phy_reg(ohci, 8);
1739 mutex_unlock(&ohci->phy_reg_mutex);
1740 if (reg < 0)
1741 return reg;
1742
1743 switch (reg & 0x0f) {
1744 case 0x06:
1745 return 2; /* is child node (connected to parent node) */
1746 case 0x0e:
1747 return 3; /* is parent node (connected to child node) */
1748 }
1749 return 1; /* not connected */
1750}
1751
1752static int get_self_id_pos(struct fw_ohci *ohci, u32 self_id,
1753 int self_id_count)
1754{
1755 int i;
1756 u32 entry;
1757 for (i = 0; i < self_id_count; i++) {
1758 entry = ohci->self_id_buffer[i];
1759 if ((self_id & 0xff000000) == (entry & 0xff000000))
1760 return -1;
1761 if ((self_id & 0xff000000) < (entry & 0xff000000))
1762 return i;
1763 }
1764 return i;
1765}
1766
1767/*
1768 * This function implements a work around for the Texas Instruments PHY
1769 * TSB41BA3D. This phy has a bug at least in combination with the TI
1770 * LLCs TSB82AA2B and TSB12LV26. The selfid coming from the locally
1771 * connected phy is not propagated into the selfid buffer of the OHCI
1772 * (see http://www.ti.com/litv/pdf/sllz059 for details).
1773 * The main idea is to construct the selfid ourselves.
1774 */
1775
1776static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
1777{
1778 int reg;
1779 int i;
1780 int pos;
1781 int status;
1782 u32 self_id;
1783
1784/*
1785 * preset bits in self_id
1786 *
1787 * link active: 0b1
1788 * speed: 0b11
1789 * bridge: 0b00
1790 * contender: 0b1
1791 * initiated reset: 0b0
1792 * more packets: 0b0
1793 */
1794 self_id = 0x8040C800;
1795
1796 reg = reg_read(ohci, OHCI1394_NodeID);
1797 if (!(reg & OHCI1394_NodeID_idValid)) {
1798 fw_notify("node ID not valid, new bus reset in progress\n");
1799 return -EBUSY;
1800 }
1801 self_id |= ((reg & 0x3f) << 24); /* phy ID */
1802
1803 mutex_lock(&ohci->phy_reg_mutex);
1804 reg = read_phy_reg(ohci, 4);
1805 mutex_unlock(&ohci->phy_reg_mutex);
1806 if (reg < 0)
1807 return reg;
1808 self_id |= ((reg & 0x07) << 8); /* power class */
1809
1810 mutex_lock(&ohci->phy_reg_mutex);
1811 reg = read_phy_reg(ohci, 1);
1812 mutex_unlock(&ohci->phy_reg_mutex);
1813 if (reg < 0)
1814 return reg;
1815 self_id |= ((reg & 0x3f) << 16); /* gap count */
1816
1817 for (i = 0; i < 3; i++) {
1818 status = get_status_for_port(ohci, i);
1819 if (status < 0)
1820 return status;
1821 self_id |= ((status & 0x3) << (6 - (i * 2)));
1822 }
1823
1824 pos = get_self_id_pos(ohci, self_id, self_id_count);
1825 if (pos >= 0) {
1826 memmove(&(ohci->self_id_buffer[pos+1]),
1827 &(ohci->self_id_buffer[pos]),
1828 (self_id_count - pos) * sizeof(*ohci->self_id_buffer));
1829 ohci->self_id_buffer[pos] = self_id;
1830 self_id_count++;
1831 }
1832 return self_id_count;
1833}
1834
1717static void bus_reset_work(struct work_struct *work) 1835static void bus_reset_work(struct work_struct *work)
1718{ 1836{
1719 struct fw_ohci *ohci = 1837 struct fw_ohci *ohci =
@@ -1755,10 +1873,12 @@ static void bus_reset_work(struct work_struct *work)
1755 * bit extra to get the actual number of self IDs. 1873 * bit extra to get the actual number of self IDs.
1756 */ 1874 */
1757 self_id_count = (reg >> 3) & 0xff; 1875 self_id_count = (reg >> 3) & 0xff;
1758 if (self_id_count == 0 || self_id_count > 252) { 1876
1877 if (self_id_count > 252) {
1759 fw_notify("inconsistent self IDs\n"); 1878 fw_notify("inconsistent self IDs\n");
1760 return; 1879 return;
1761 } 1880 }
1881
1762 generation = (cond_le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff; 1882 generation = (cond_le32_to_cpu(ohci->self_id_cpu[0]) >> 16) & 0xff;
1763 rmb(); 1883 rmb();
1764 1884
@@ -1770,6 +1890,19 @@ static void bus_reset_work(struct work_struct *work)
1770 ohci->self_id_buffer[j] = 1890 ohci->self_id_buffer[j] =
1771 cond_le32_to_cpu(ohci->self_id_cpu[i]); 1891 cond_le32_to_cpu(ohci->self_id_cpu[i]);
1772 } 1892 }
1893
1894 if (ohci->quirks & QUIRK_TI_SLLZ059) {
1895 self_id_count = find_and_insert_self_id(ohci, self_id_count);
1896 if (self_id_count < 0) {
1897 fw_notify("could not construct local self IDs\n");
1898 return;
1899 }
1900 }
1901
1902 if (self_id_count == 0) {
1903 fw_notify("inconsistent self IDs\n");
1904 return;
1905 }
1773 rmb(); 1906 rmb();
1774 1907
1775 /* 1908 /*
@@ -2050,13 +2183,50 @@ static int configure_1394a_enhancements(struct fw_ohci *ohci)
2050 return 0; 2183 return 0;
2051} 2184}
2052 2185
2186#define TSB41BA3D_VID 0x00080028
2187#define TSB41BA3D_PID 0x00833005
2188
2189static int probe_tsb41ba3d(struct fw_ohci *ohci)
2190{
2191 int reg;
2192 int i;
2193 int vendor_id;
2194 int product_id;
2195
2196 reg = read_phy_reg(ohci, 2);
2197 if (reg < 0)
2198 return reg;
2199
2200 if ((reg & PHY_EXTENDED_REGISTERS) == PHY_EXTENDED_REGISTERS) {
2201 vendor_id = 0;
2202 for (i = 10; i < 13; i++) {
2203 reg = read_paged_phy_reg(ohci, 1, i);
2204 if (reg < 0)
2205 return reg;
2206 vendor_id = (vendor_id << 8) | reg;
2207 }
2208 product_id = 0;
2209 for (i = 13; i < 16; i++) {
2210 reg = read_paged_phy_reg(ohci, 1, i);
2211 if (reg < 0)
2212 return reg;
2213 product_id = (product_id << 8) | reg;
2214 }
2215
2216 if ((vendor_id == TSB41BA3D_VID) &&
2217 (product_id == TSB41BA3D_PID))
2218 return 1;
2219 }
2220 return 0;
2221}
2222
2053static int ohci_enable(struct fw_card *card, 2223static int ohci_enable(struct fw_card *card,
2054 const __be32 *config_rom, size_t length) 2224 const __be32 *config_rom, size_t length)
2055{ 2225{
2056 struct fw_ohci *ohci = fw_ohci(card); 2226 struct fw_ohci *ohci = fw_ohci(card);
2057 struct pci_dev *dev = to_pci_dev(card->device); 2227 struct pci_dev *dev = to_pci_dev(card->device);
2058 u32 lps, seconds, version, irqs; 2228 u32 lps, seconds, version, irqs;
2059 int i, ret; 2229 int i, ret, tsb41ba3d_found;
2060 2230
2061 if (software_reset(ohci)) { 2231 if (software_reset(ohci)) {
2062 fw_error("Failed to reset ohci card.\n"); 2232 fw_error("Failed to reset ohci card.\n");
@@ -2087,6 +2257,17 @@ static int ohci_enable(struct fw_card *card,
2087 return -EIO; 2257 return -EIO;
2088 } 2258 }
2089 2259
2260 if (ohci->quirks & QUIRK_TI_SLLZ059) {
2261 tsb41ba3d_found = probe_tsb41ba3d(ohci);
2262 if (tsb41ba3d_found < 0)
2263 return tsb41ba3d_found;
2264 if (!tsb41ba3d_found) {
2265 fw_notify("No TSB41BA3D found, "
2266 "resetting QUIRK_TI_SLLZ059\n");
2267 ohci->quirks &= ~QUIRK_TI_SLLZ059;
2268 }
2269 }
2270
2090 reg_write(ohci, OHCI1394_HCControlClear, 2271 reg_write(ohci, OHCI1394_HCControlClear,
2091 OHCI1394_HCControl_noByteSwapData); 2272 OHCI1394_HCControl_noByteSwapData);
2092 2273