blob: f7e7848b22baeba555a18dc0621880c2eb93e550 [file] [log] [blame]
/*
* 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, &reg16);
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 */