| /* |
| * Copyright 2012 Luis R. Rodriguez <mcgrof@do-not-panic.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * Backport functionality introduced in Linux 3.7. |
| */ |
| |
| #include <linux/workqueue.h> |
| #include <linux/export.h> |
| #include <linux/pci.h> |
| #include <linux/pci_regs.h> |
| #include <linux/of.h> |
| |
| bool mod_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, |
| unsigned long delay) |
| { |
| cancel_delayed_work(dwork); |
| queue_delayed_work(wq, dwork, delay); |
| return false; |
| } |
| EXPORT_SYMBOL_GPL(mod_delayed_work); |
| |
| #ifdef CONFIG_PCI |
| /* |
| * Kernels >= 3.7 get their PCI-E Capabilities Register cached |
| * via the pci_dev->pcie_flags_reg so for older kernels we have |
| * no other option but to read this every single time we need |
| * it accessed. If we really cared to improve the efficiency |
| * of this we could try to find an unused u16 varible on the |
| * pci_dev but if we found it we likely would remove it from |
| * the kernel anyway right? Bite me. |
| */ |
| static inline u16 pcie_flags_reg(struct pci_dev *dev) |
| { |
| int pos; |
| u16 reg16; |
| |
| pos = pci_find_capability(dev, PCI_CAP_ID_EXP); |
| if (!pos) |
| return 0; |
| |
| pci_read_config_word(dev, pos + PCI_EXP_FLAGS, ®16); |
| |
| return reg16; |
| } |
| |
| #define pci_pcie_type LINUX_BACKPORT(pci_pcie_type) |
| static inline int pci_pcie_type(struct pci_dev *dev) |
| { |
| return (pcie_flags_reg(dev) & PCI_EXP_FLAGS_TYPE) >> 4; |
| } |
| |
| #define pcie_cap_version LINUX_BACKPORT(pcie_cap_version) |
| static inline int pcie_cap_version(struct pci_dev *dev) |
| { |
| return pcie_flags_reg(dev) & PCI_EXP_FLAGS_VERS; |
| } |
| |
| static inline bool pcie_cap_has_lnkctl(struct pci_dev *dev) |
| { |
| int type = pci_pcie_type(dev); |
| |
| return pcie_cap_version(dev) > 1 || |
| type == PCI_EXP_TYPE_ROOT_PORT || |
| type == PCI_EXP_TYPE_ENDPOINT || |
| type == PCI_EXP_TYPE_LEG_END; |
| } |
| |
| static inline bool pcie_cap_has_sltctl(struct pci_dev *dev) |
| { |
| int type = pci_pcie_type(dev); |
| |
| return pcie_cap_version(dev) > 1 || |
| type == PCI_EXP_TYPE_ROOT_PORT || |
| (type == PCI_EXP_TYPE_DOWNSTREAM && |
| pcie_flags_reg(dev) & PCI_EXP_FLAGS_SLOT); |
| } |
| |
| static inline bool pcie_cap_has_rtctl(struct pci_dev *dev) |
| { |
| int type = pci_pcie_type(dev); |
| |
| return pcie_cap_version(dev) > 1 || |
| type == PCI_EXP_TYPE_ROOT_PORT || |
| type == PCI_EXP_TYPE_RC_EC; |
| } |
| |
| static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) |
| { |
| if (!pci_is_pcie(dev)) |
| return false; |
| |
| switch (pos) { |
| case PCI_EXP_FLAGS_TYPE: |
| return true; |
| case PCI_EXP_DEVCAP: |
| case PCI_EXP_DEVCTL: |
| case PCI_EXP_DEVSTA: |
| return true; |
| case PCI_EXP_LNKCAP: |
| case PCI_EXP_LNKCTL: |
| case PCI_EXP_LNKSTA: |
| return pcie_cap_has_lnkctl(dev); |
| case PCI_EXP_SLTCAP: |
| case PCI_EXP_SLTCTL: |
| case PCI_EXP_SLTSTA: |
| return pcie_cap_has_sltctl(dev); |
| case PCI_EXP_RTCTL: |
| case PCI_EXP_RTCAP: |
| case PCI_EXP_RTSTA: |
| return pcie_cap_has_rtctl(dev); |
| case PCI_EXP_DEVCAP2: |
| case PCI_EXP_DEVCTL2: |
| case PCI_EXP_LNKCAP2: |
| case PCI_EXP_LNKCTL2: |
| case PCI_EXP_LNKSTA2: |
| return pcie_cap_version(dev) > 1; |
| default: |
| return false; |
| } |
| } |
| |
| /* |
| * Note that these accessor functions are only for the "PCI Express |
| * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the |
| * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.) |
| */ |
| int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val) |
| { |
| int ret; |
| |
| *val = 0; |
| if (pos & 1) |
| return -EINVAL; |
| |
| if (pcie_capability_reg_implemented(dev, pos)) { |
| ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val); |
| /* |
| * Reset *val to 0 if pci_read_config_word() fails, it may |
| * have been written as 0xFFFF if hardware error happens |
| * during pci_read_config_word(). |
| */ |
| if (ret) |
| *val = 0; |
| return ret; |
| } |
| |
| /* |
| * For Functions that do not implement the Slot Capabilities, |
| * Slot Status, and Slot Control registers, these spaces must |
| * be hardwired to 0b, with the exception of the Presence Detect |
| * State bit in the Slot Status register of Downstream Ports, |
| * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8) |
| */ |
| if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA && |
| pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { |
| *val = PCI_EXP_SLTSTA_PDS; |
| } |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_read_word); |
| |
| int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val) |
| { |
| int ret; |
| |
| *val = 0; |
| if (pos & 3) |
| return -EINVAL; |
| |
| if (pcie_capability_reg_implemented(dev, pos)) { |
| ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val); |
| /* |
| * Reset *val to 0 if pci_read_config_dword() fails, it may |
| * have been written as 0xFFFFFFFF if hardware error happens |
| * during pci_read_config_dword(). |
| */ |
| if (ret) |
| *val = 0; |
| return ret; |
| } |
| |
| if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL && |
| pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) { |
| *val = PCI_EXP_SLTSTA_PDS; |
| } |
| |
| return 0; |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_read_dword); |
| |
| int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val) |
| { |
| if (pos & 1) |
| return -EINVAL; |
| |
| if (!pcie_capability_reg_implemented(dev, pos)) |
| return 0; |
| |
| return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val); |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_write_word); |
| |
| int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val) |
| { |
| if (pos & 3) |
| return -EINVAL; |
| |
| if (!pcie_capability_reg_implemented(dev, pos)) |
| return 0; |
| |
| return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val); |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_write_dword); |
| |
| int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos, |
| u16 clear, u16 set) |
| { |
| int ret; |
| u16 val; |
| |
| ret = pcie_capability_read_word(dev, pos, &val); |
| if (!ret) { |
| val &= ~clear; |
| val |= set; |
| ret = pcie_capability_write_word(dev, pos, val); |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_clear_and_set_word); |
| |
| int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos, |
| u32 clear, u32 set) |
| { |
| int ret; |
| u32 val; |
| |
| ret = pcie_capability_read_dword(dev, pos, &val); |
| if (!ret) { |
| val &= ~clear; |
| val |= set; |
| ret = pcie_capability_write_dword(dev, pos, val); |
| } |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(pcie_capability_clear_and_set_dword); |
| #endif |
| |
| #ifdef CONFIG_OF |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) |
| /** |
| * of_get_child_by_name - Find the child node by name for a given parent |
| * @node: parent node |
| * @name: child name to look for. |
| * |
| * This function looks for child node for given matching name |
| * |
| * Returns a node pointer if found, with refcount incremented, use |
| * of_node_put() on it when done. |
| * Returns NULL if node is not found. |
| */ |
| struct device_node *of_get_child_by_name(const struct device_node *node, |
| const char *name) |
| { |
| struct device_node *child; |
| |
| for_each_child_of_node(node, child) |
| if (child->name && (of_node_cmp(child->name, name) == 0)) |
| break; |
| return child; |
| } |
| EXPORT_SYMBOL_GPL(of_get_child_by_name); |
| #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)) */ |
| #endif /* CONFIG_OF */ |