1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

[lantiq] cleanup patches

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@32953 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
blogic
2012-08-03 08:53:02 +00:00
parent 6b899d5dea
commit cea2b4210d
209 changed files with 146978 additions and 82080 deletions

View File

@@ -0,0 +1,81 @@
/******************************************************************************
**
** FILE NAME : ifxmips_fixup_pcie.c
** PROJECT : IFX UEIP for VRX200
** MODULES : PCIe
**
** DATE : 02 Mar 2009
** AUTHOR : Lei Chuanhua
** DESCRIPTION : PCIe Root Complex Driver
** COPYRIGHT : Copyright (c) 2009
** Infineon Technologies AG
** Am Campeon 1-12, 85579 Neubiberg, Germany
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** HISTORY
** $Version $Date $Author $Comment
** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version
*******************************************************************************/
/*!
\file ifxmips_fixup_pcie.c
\ingroup IFX_PCIE
\brief PCIe Fixup functions source file
*/
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/pci_ids.h>
#include <lantiq_soc.h>
#include "pcie-lantiq.h"
#define PCI_VENDOR_ID_INFINEON 0x15D1
#define PCI_DEVICE_ID_INFINEON_DANUBE 0x000F
#define PCI_DEVICE_ID_INFINEON_PCIE 0x0011
#define PCI_VENDOR_ID_LANTIQ 0x1BEF
#define PCI_DEVICE_ID_LANTIQ_PCIE 0x0011
static void __devinit
ifx_pcie_fixup_resource(struct pci_dev *dev)
{
u32 reg;
IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev));
IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s: fixup host controller %s (%04x:%04x)\n",
__func__, pci_name(dev), dev->vendor, dev->device);
/* Setup COMMAND register */
reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* |
PCI_COMMAND_INTX_DISABLE */| PCI_COMMAND_SERR;
pci_write_config_word(dev, PCI_COMMAND, reg);
IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev));
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ifx_pcie_fixup_resource);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_VENDOR_ID_LANTIQ, ifx_pcie_fixup_resource);
static void __devinit
ifx_pcie_rc_class_early_fixup(struct pci_dev *dev)
{
IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev));
if (dev->devfn == PCI_DEVFN(0, 0) &&
(dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
dev->class = (PCI_CLASS_BRIDGE_PCI << 8) | (dev->class & 0xff);
printk(KERN_INFO "%s: fixed pcie host bridge to pci-pci bridge\n", __func__);
}
IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev));
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE,
ifx_pcie_rc_class_early_fixup);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_DEVICE_ID_LANTIQ_PCIE,
ifx_pcie_rc_class_early_fixup);

View File

@@ -0,0 +1,42 @@
/*
* 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.
*
* Copyright (C) 2012 John Crispin <blogic@openwrt.org>
*/
#include <linux/of_irq.h>
#include <linux/of_pci.h>
int (*ltqpci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin) = NULL;
int (*ltqpci_plat_arch_init)(struct pci_dev *dev) = NULL;
int (*ltqpci_plat_dev_init)(struct pci_dev *dev) = NULL;
int *ltq_pci_irq_map;
int pcibios_plat_dev_init(struct pci_dev *dev)
{
if (ltqpci_plat_arch_init)
return ltqpci_plat_arch_init(dev);
if (ltqpci_plat_dev_init)
return ltqpci_plat_dev_init(dev);
return 0;
}
int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (ltqpci_map_irq)
return ltqpci_map_irq(dev, slot, pin);
if (ltq_pci_irq_map[slot]) {
dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, ltq_pci_irq_map[slot]);
return ltq_pci_irq_map[slot];
}
printk(KERN_ERR "lq_pci: trying to map irq for unknown slot %d\n",
slot);
return 0;
}

View File

@@ -0,0 +1,399 @@
/******************************************************************************
**
** FILE NAME : ifxmips_pcie_msi.c
** PROJECT : IFX UEIP for VRX200
** MODULES : PCI MSI sub module
**
** DATE : 02 Mar 2009
** AUTHOR : Lei Chuanhua
** DESCRIPTION : PCIe MSI Driver
** COPYRIGHT : Copyright (c) 2009
** Infineon Technologies AG
** Am Campeon 1-12, 85579 Neubiberg, Germany
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** HISTORY
** $Date $Author $Comment
** 02 Mar,2009 Lei Chuanhua Initial version
*******************************************************************************/
/*!
\defgroup IFX_PCIE_MSI MSI OS APIs
\ingroup IFX_PCIE
\brief PCIe bus driver OS interface functions
*/
/*!
\file ifxmips_pcie_msi.c
\ingroup IFX_PCIE
\brief PCIe MSI OS interface file
*/
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/kernel_stat.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/module.h>
#include <asm/bootinfo.h>
#include <asm/irq.h>
#include <asm/traps.h>
#include "pcie-lantiq.h"
#define IFX_MSI_IRQ_NUM 16
#define SM(_v, _f) (((_v) << _f##_S) & (_f))
#define IFX_MSI_PIC_REG_BASE (KSEG1 | 0x1F700000)
#define IFX_PCIE_MSI_IR0 (INT_NUM_IM4_IRL0 + 27)
#define IFX_PCIE_MSI_IR1 (INT_NUM_IM4_IRL0 + 28)
#define IFX_PCIE_MSI_IR2 (INT_NUM_IM4_IRL0 + 29)
#define IFX_PCIE_MSI_IR3 (INT_NUM_IM0_IRL0 + 30)
#define IFX_MSI_PCI_INT_DISABLE 0x80000000
#define IFX_MSI_PIC_INT_LINE 0x30000000
#define IFX_MSI_PIC_MSG_ADDR 0x0FFF0000
#define IFX_MSI_PIC_MSG_DATA 0x0000FFFF
#define IFX_MSI_PIC_BIG_ENDIAN 1
#define IFX_MSI_PIC_INT_LINE_S 28
#define IFX_MSI_PIC_MSG_ADDR_S 16
#define IFX_MSI_PIC_MSG_DATA_S 0x0
enum {
IFX_PCIE_MSI_IDX0 = 0,
IFX_PCIE_MSI_IDX1,
IFX_PCIE_MSI_IDX2,
IFX_PCIE_MSI_IDX3,
};
typedef struct ifx_msi_irq_idx {
const int irq;
const int idx;
}ifx_msi_irq_idx_t;
struct ifx_msi_pic {
volatile u32 pic_table[IFX_MSI_IRQ_NUM];
volatile u32 pic_endian; /* 0x40 */
};
typedef struct ifx_msi_pic *ifx_msi_pic_t;
typedef struct ifx_msi_irq {
const volatile ifx_msi_pic_t msi_pic_p;
const u32 msi_phy_base;
const ifx_msi_irq_idx_t msi_irq_idx[IFX_MSI_IRQ_NUM];
/*
* Each bit in msi_free_irq_bitmask represents a MSI interrupt that is
* in use.
*/
u16 msi_free_irq_bitmask;
/*
* Each bit in msi_multiple_irq_bitmask tells that the device using
* this bit in msi_free_irq_bitmask is also using the next bit. This
* is used so we can disable all of the MSI interrupts when a device
* uses multiple.
*/
u16 msi_multiple_irq_bitmask;
}ifx_msi_irq_t;
static ifx_msi_irq_t msi_irqs[IFX_PCIE_CORE_NR] = {
{
.msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI_PIC_REG_BASE,
.msi_phy_base = PCIE_MSI_PHY_BASE,
.msi_irq_idx = {
{IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3},
},
.msi_free_irq_bitmask = 0,
.msi_multiple_irq_bitmask= 0,
},
#ifdef CONFIG_IFX_PCIE_2ND_CORE
{
.msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI1_PIC_REG_BASE,
.msi_phy_base = PCIE1_MSI_PHY_BASE,
.msi_irq_idx = {
{IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3},
{IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1},
{IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3},
},
.msi_free_irq_bitmask = 0,
.msi_multiple_irq_bitmask= 0,
},
#endif /* CONFIG_IFX_PCIE_2ND_CORE */
};
/*
* This lock controls updates to msi_free_irq_bitmask,
* msi_multiple_irq_bitmask and pic register settting
*/
static DEFINE_SPINLOCK(ifx_pcie_msi_lock);
void pcie_msi_pic_init(int pcie_port)
{
spin_lock(&ifx_pcie_msi_lock);
msi_irqs[pcie_port].msi_pic_p->pic_endian = IFX_MSI_PIC_BIG_ENDIAN;
spin_unlock(&ifx_pcie_msi_lock);
}
/**
* \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
* \brief Called when a driver request MSI interrupts instead of the
* legacy INT A-D. This routine will allocate multiple interrupts
* for MSI devices that support them. A device can override this by
* programming the MSI control bits [6:4] before calling
* pci_enable_msi().
*
* \param[in] pdev Device requesting MSI interrupts
* \param[in] desc MSI descriptor
*
* \return -EINVAL Invalid pcie root port or invalid msi bit
* \return 0 OK
* \ingroup IFX_PCIE_MSI
*/
int
arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
{
int irq, pos;
u16 control;
int irq_idx;
int irq_step;
int configured_private_bits;
int request_private_bits;
struct msi_msg msg;
u16 search_mask;
struct ifx_pci_controller *ctrl = pdev->bus->sysdata;
int pcie_port = ctrl->port;
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s %s enter\n", __func__, pci_name(pdev));
/* XXX, skip RC MSI itself */
if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s RC itself doesn't use MSI interrupt\n", __func__);
return -EINVAL;
}
/*
* Read the MSI config to figure out how many IRQs this device
* wants. Most devices only want 1, which will give
* configured_private_bits and request_private_bits equal 0.
*/
pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &control);
/*
* If the number of private bits has been configured then use
* that value instead of the requested number. This gives the
* driver the chance to override the number of interrupts
* before calling pci_enable_msi().
*/
configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4;
if (configured_private_bits == 0) {
/* Nothing is configured, so use the hardware requested size */
request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1;
}
else {
/*
* Use the number of configured bits, assuming the
* driver wanted to override the hardware request
* value.
*/
request_private_bits = configured_private_bits;
}
/*
* The PCI 2.3 spec mandates that there are at most 32
* interrupts. If this device asks for more, only give it one.
*/
if (request_private_bits > 5) {
request_private_bits = 0;
}
again:
/*
* The IRQs have to be aligned on a power of two based on the
* number being requested.
*/
irq_step = (1 << request_private_bits);
/* Mask with one bit for each IRQ */
search_mask = (1 << irq_step) - 1;
/*
* We're going to search msi_free_irq_bitmask_lock for zero
* bits. This represents an MSI interrupt number that isn't in
* use.
*/
spin_lock(&ifx_pcie_msi_lock);
for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos += irq_step) {
if ((msi_irqs[pcie_port].msi_free_irq_bitmask & (search_mask << pos)) == 0) {
msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos;
msi_irqs[pcie_port].msi_multiple_irq_bitmask |= (search_mask >> 1) << pos;
break;
}
}
spin_unlock(&ifx_pcie_msi_lock);
/* Make sure the search for available interrupts didn't fail */
if (pos >= IFX_MSI_IRQ_NUM) {
if (request_private_bits) {
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s: Unable to find %d free "
"interrupts, trying just one", __func__, 1 << request_private_bits);
request_private_bits = 0;
goto again;
}
else {
printk(KERN_ERR "%s: Unable to find a free MSI interrupt\n", __func__);
return -EINVAL;
}
}
irq = msi_irqs[pcie_port].msi_irq_idx[pos].irq;
irq_idx = msi_irqs[pcie_port].msi_irq_idx[pos].idx;
IFX_PCIE_PRINT(PCIE_MSG_MSI, "pos %d, irq %d irq_idx %d\n", pos, irq, irq_idx);
/*
* Initialize MSI. This has to match the memory-write endianess from the device
* Address bits [23:12]
*/
spin_lock(&ifx_pcie_msi_lock);
msi_irqs[pcie_port].msi_pic_p->pic_table[pos] = SM(irq_idx, IFX_MSI_PIC_INT_LINE) |
SM((msi_irqs[pcie_port].msi_phy_base >> 12), IFX_MSI_PIC_MSG_ADDR) |
SM((1 << pos), IFX_MSI_PIC_MSG_DATA);
/* Enable this entry */
msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~IFX_MSI_PCI_INT_DISABLE;
spin_unlock(&ifx_pcie_msi_lock);
IFX_PCIE_PRINT(PCIE_MSG_MSI, "pic_table[%d]: 0x%08x\n",
pos, msi_irqs[pcie_port].msi_pic_p->pic_table[pos]);
/* Update the number of IRQs the device has available to it */
control &= ~PCI_MSI_FLAGS_QSIZE;
control |= (request_private_bits << 4);
pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, control);
irq_set_msi_desc(irq, desc);
msg.address_hi = 0x0;
msg.address_lo = msi_irqs[pcie_port].msi_phy_base;
msg.data = SM((1 << pos), IFX_MSI_PIC_MSG_DATA);
IFX_PCIE_PRINT(PCIE_MSG_MSI, "msi_data: pos %d 0x%08x\n", pos, msg.data);
write_msi_msg(irq, &msg);
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__);
return 0;
}
static int
pcie_msi_irq_to_port(unsigned int irq, int *port)
{
int ret = 0;
if (irq == IFX_PCIE_MSI_IR0 || irq == IFX_PCIE_MSI_IR1 ||
irq == IFX_PCIE_MSI_IR2 || irq == IFX_PCIE_MSI_IR3) {
*port = IFX_PCIE_PORT0;
}
#ifdef CONFIG_IFX_PCIE_2ND_CORE
else if (irq == IFX_PCIE1_MSI_IR0 || irq == IFX_PCIE1_MSI_IR1 ||
irq == IFX_PCIE1_MSI_IR2 || irq == IFX_PCIE1_MSI_IR3) {
*port = IFX_PCIE_PORT1;
}
#endif /* CONFIG_IFX_PCIE_2ND_CORE */
else {
printk(KERN_ERR "%s: Attempted to teardown illegal "
"MSI interrupt (%d)\n", __func__, irq);
ret = -EINVAL;
}
return ret;
}
/**
* \fn void arch_teardown_msi_irq(unsigned int irq)
* \brief Called when a device no longer needs its MSI interrupts. All
* MSI interrupts for the device are freed.
*
* \param irq The devices first irq number. There may be multple in sequence.
* \return none
* \ingroup IFX_PCIE_MSI
*/
void
arch_teardown_msi_irq(unsigned int irq)
{
int pos;
int number_irqs;
u16 bitmask;
int pcie_port;
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s enter\n", __func__);
BUG_ON(irq > (INT_NUM_IM4_IRL0 + 31));
if (pcie_msi_irq_to_port(irq, &pcie_port) != 0) {
return;
}
/* Shift the mask to the correct bit location, not always correct
* Probally, the first match will be chosen.
*/
for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos++) {
if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq)
&& (msi_irqs[pcie_port].msi_free_irq_bitmask & ( 1 << pos))) {
break;
}
}
if (pos >= IFX_MSI_IRQ_NUM) {
printk(KERN_ERR "%s: Unable to find a matched MSI interrupt\n", __func__);
return;
}
spin_lock(&ifx_pcie_msi_lock);
/* Disable this entry */
msi_irqs[pcie_port].msi_pic_p->pic_table[pos] |= IFX_MSI_PCI_INT_DISABLE;
msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~(IFX_MSI_PIC_INT_LINE | IFX_MSI_PIC_MSG_ADDR | IFX_MSI_PIC_MSG_DATA);
spin_unlock(&ifx_pcie_msi_lock);
/*
* Count the number of IRQs we need to free by looking at the
* msi_multiple_irq_bitmask. Each bit set means that the next
* IRQ is also owned by this device.
*/
number_irqs = 0;
while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) &&
(msi_irqs[pcie_port].msi_multiple_irq_bitmask & (1 << (pos + number_irqs)))) {
number_irqs++;
}
number_irqs++;
/* Mask with one bit for each IRQ */
bitmask = (1 << number_irqs) - 1;
bitmask <<= pos;
if ((msi_irqs[pcie_port].msi_free_irq_bitmask & bitmask) != bitmask) {
printk(KERN_ERR "%s: Attempted to teardown MSI "
"interrupt (%d) not in use\n", __func__, irq);
return;
}
/* Checks are done, update the in use bitmask */
spin_lock(&ifx_pcie_msi_lock);
msi_irqs[pcie_port].msi_free_irq_bitmask &= ~bitmask;
msi_irqs[pcie_port].msi_multiple_irq_bitmask &= ~(bitmask >> 1);
spin_unlock(&ifx_pcie_msi_lock);
IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chuanhua.Lei@infineon.com");
MODULE_SUPPORTED_DEVICE("Infineon PCIe IP builtin MSI PIC module");
MODULE_DESCRIPTION("Infineon PCIe IP builtin MSI PIC driver");

View File

@@ -0,0 +1,408 @@
/******************************************************************************
**
** FILE NAME : ifxmips_pcie_phy.c
** PROJECT : IFX UEIP for VRX200
** MODULES : PCIe PHY sub module
**
** DATE : 14 May 2009
** AUTHOR : Lei Chuanhua
** DESCRIPTION : PCIe Root Complex Driver
** COPYRIGHT : Copyright (c) 2009
** Infineon Technologies AG
** Am Campeon 1-12, 85579 Neubiberg, Germany
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** HISTORY
** $Version $Date $Author $Comment
** 0.0.1 14 May,2009 Lei Chuanhua Initial version
*******************************************************************************/
/*!
\file ifxmips_pcie_phy.c
\ingroup IFX_PCIE
\brief PCIe PHY PLL register programming source file
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/paccess.h>
#include <linux/delay.h>
#include "pcie-lantiq.h"
/* PCIe PDI only supports 16 bit operation */
#define IFX_PCIE_PHY_REG_WRITE16(__addr, __data) \
((*(volatile u16 *) (__addr)) = (__data))
#define IFX_PCIE_PHY_REG_READ16(__addr) \
(*(volatile u16 *) (__addr))
#define IFX_PCIE_PHY_REG16(__addr) \
(*(volatile u16 *) (__addr))
#define IFX_PCIE_PHY_REG(__reg, __value, __mask) do { \
u16 read_data; \
u16 write_data; \
read_data = IFX_PCIE_PHY_REG_READ16((__reg)); \
write_data = (read_data & ((u16)~(__mask))) | (((u16)(__value)) & ((u16)(__mask)));\
IFX_PCIE_PHY_REG_WRITE16((__reg), write_data); \
} while (0)
#define IFX_PCIE_PLL_TIMEOUT 1000 /* Tunnable */
static void
pcie_phy_comm_setup(int pcie_port)
{
/* PLL Setting */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF);
/* increase the bias reference voltage */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF);
/* Endcnt */
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF);
/* force */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008);
/* predrv_ser_en */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF);
/* ctrl_lim */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF);
/* ctrl */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00);
/* predrv_ser_en */
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00);
/* RTERM*/
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF);
/* Improved 100MHz clock output */
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF);
/* Reduced CDR BW to avoid glitches */
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF);
}
#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE
static void
pcie_phy_36mhz_mode_setup(int pcie_port)
{
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port);
/* en_ext_mmd_div_ratio */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002);
/* ext_mmd_div_ratio*/
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070);
/* pll_ensdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200);
/* en_const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100);
/* mmd */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000);
/* lf_mode */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000);
/* const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF);
/* const sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF);
/* pllmod */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF);
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port);
}
#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */
#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE
static void
pcie_phy_36mhz_ssc_mode_setup(int pcie_port)
{
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port);
/* PLL Setting */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF);
/* Increase the bias reference voltage */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF);
/* Endcnt */
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF);
/* Force */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008);
/* Predrv_ser_en */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF);
/* ctrl_lim */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF);
/* ctrl */
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00);
/* predrv_ser_en */
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00);
/* RTERM*/
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF);
/* en_ext_mmd_div_ratio */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002);
/* ext_mmd_div_ratio*/
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070);
/* pll_ensdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0400, 0x0400);
/* en_const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200);
/* mmd */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000);
/* lf_mode */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000);
/* const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0100);
/* const sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF);
/* pllmod */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1c72, 0xFFFF);
/* improved 100MHz clock output */
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF);
/* reduced CDR BW to avoid glitches */
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF);
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port);
}
#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE */
#ifdef CONFIG_IFX_PCIE_PHY_25MHZ_MODE
static void
pcie_phy_25mhz_mode_setup(int pcie_port)
{
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port);
/* en_const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100);
/* pll_ensdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0200);
/* en_ext_mmd_div_ratio*/
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0002, 0x0002);
/* ext_mmd_div_ratio*/
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0040, 0x0070);
/* mmd */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x6000, 0xe000);
/* lf_mode */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x4000, 0x4000);
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port);
}
#endif /* CONFIG_IFX_PCIE_PHY_25MHZ_MODE */
#ifdef CONFIG_IFX_PCIE_PHY_100MHZ_MODE
static void
pcie_phy_100mhz_mode_setup(int pcie_port)
{
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port);
/* en_ext_mmd_div_ratio */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002);
/* ext_mmd_div_ratio*/
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070);
/* pll_ensdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200);
/* en_const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100);
/* mmd */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000);
/* lf_mode */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000);
/* const_sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF);
/* const sdm */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF);
/* pllmod */
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF);
IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port);
}
#endif /* CONFIG_IFX_PCIE_PHY_100MHZ_MODE */
static int
pcie_phy_wait_startup_ready(int pcie_port)
{
int i;
for (i = 0; i < IFX_PCIE_PLL_TIMEOUT; i++) {
if ((IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port)) & 0x0040) != 0) {
break;
}
udelay(10);
}
if (i >= IFX_PCIE_PLL_TIMEOUT) {
printk(KERN_ERR "%s PLL Link timeout\n", __func__);
return -1;
}
return 0;
}
static void
pcie_phy_load_enable(int pcie_port, int slice)
{
/* Set the load_en of tx/rx slice to '1' */
switch (slice) {
case 1:
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0010, 0x0010);
break;
case 2:
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0010, 0x0010);
break;
case 3:
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0002, 0x0002);
break;
}
}
static void
pcie_phy_load_disable(int pcie_port, int slice)
{
/* set the load_en of tx/rx slice to '0' */
switch (slice) {
case 1:
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0000, 0x0010);
break;
case 2:
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0000, 0x0010);
break;
case 3:
IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0000, 0x0002);
break;
}
}
static void pcie_phy_load_war(int pcie_port)
{
int slice;
for (slice = 1; slice < 4; slice++) {
pcie_phy_load_enable(pcie_port, slice);
udelay(1);
pcie_phy_load_disable(pcie_port, slice);
}
}
static void pcie_phy_tx2_modulation(int pcie_port)
{
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD1(pcie_port), 0x1FFE, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD2(pcie_port), 0xFFFE, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0601, 0xFFFF);
mdelay(1);
IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0001, 0xFFFF);
}
static void pcie_phy_tx1_modulation(int pcie_port)
{
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD1(pcie_port), 0x1FFE, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD2(pcie_port), 0xFFFE, 0xFFFF);
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0601, 0xFFFF);
mdelay(1);
IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0001, 0xFFFF);
}
static void pcie_phy_tx_modulation_war(int pcie_port)
{
int i;
#define PCIE_PHY_MODULATION_NUM 5
for (i = 0; i < PCIE_PHY_MODULATION_NUM; i++) {
pcie_phy_tx2_modulation(pcie_port);
pcie_phy_tx1_modulation(pcie_port);
}
#undef PCIE_PHY_MODULATION_NUM
}
void pcie_phy_clock_mode_setup(int pcie_port)
{
pcie_pdi_big_endian(pcie_port);
/* Enable PDI to access PCIe PHY register */
pcie_pdi_pmu_enable(pcie_port);
/* Configure PLL and PHY clock */
pcie_phy_comm_setup(pcie_port);
#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE
pcie_phy_36mhz_mode_setup(pcie_port);
#elif defined(CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE)
pcie_phy_36mhz_ssc_mode_setup(pcie_port);
#elif defined(CONFIG_IFX_PCIE_PHY_25MHZ_MODE)
pcie_phy_25mhz_mode_setup(pcie_port);
#elif defined (CONFIG_IFX_PCIE_PHY_100MHZ_MODE)
pcie_phy_100mhz_mode_setup(pcie_port);
#else
#error "PCIE PHY Clock Mode must be chosen first!!!!"
#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */
/* Enable PCIe PHY and make PLL setting take effect */
pcie_phy_pmu_enable(pcie_port);
/* Check if we are in startup_ready status */
pcie_phy_wait_startup_ready(pcie_port);
pcie_phy_load_war(pcie_port);
/* Apply TX modulation workarounds */
pcie_phy_tx_modulation_war(pcie_port);
#ifdef IFX_PCI_PHY_REG_DUMP
IFX_PCIE_PRINT(PCIE_MSG_PHY, "Modified PHY register dump\n");
pcie_phy_reg_dump(pcie_port);
#endif
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff