forked from rrcarlosr/Jetpack
298 lines
7.2 KiB
C
298 lines
7.2 KiB
C
/*
|
|
* 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>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/tty.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
|
|
/**
|
|
* 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 /* CONFIG_OF */
|
|
|
|
int sg_nents(struct scatterlist *sg)
|
|
{
|
|
int nents;
|
|
for (nents = 0; sg; sg = sg_next(sg))
|
|
nents++;
|
|
return nents;
|
|
}
|
|
EXPORT_SYMBOL_GPL(sg_nents);
|
|
|
|
int tty_port_install(struct tty_port *port, struct tty_driver *driver,
|
|
struct tty_struct *tty)
|
|
{
|
|
tty->port = port;
|
|
return tty_standard_install(driver, tty);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tty_port_install);
|