sh: pci: clock framework support for SH7786 PCIe.
This gets each port handling its MSTP bit, as well as moving the PHY clock management in to the clock framework. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Родитель
cecf48e23f
Коммит
c524ebf5a6
|
@ -13,11 +13,15 @@
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/sh_clk.h>
|
||||||
#include "pcie-sh7786.h"
|
#include "pcie-sh7786.h"
|
||||||
#include <asm/sizes.h>
|
#include <asm/sizes.h>
|
||||||
|
#include <asm/clock.h>
|
||||||
|
|
||||||
struct sh7786_pcie_port {
|
struct sh7786_pcie_port {
|
||||||
struct pci_channel *hose;
|
struct pci_channel *hose;
|
||||||
|
struct clk *fclk, phy_clk;
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
int endpoint;
|
int endpoint;
|
||||||
int link;
|
int link;
|
||||||
|
@ -121,6 +125,10 @@ static struct pci_channel sh7786_pci_channels[] = {
|
||||||
DEFINE_CONTROLLER(0xfcc00000, 2),
|
DEFINE_CONTROLLER(0xfcc00000, 2),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct clk fixed_pciexclkp = {
|
||||||
|
.rate = 100000000, /* 100 MHz reference clock */
|
||||||
|
};
|
||||||
|
|
||||||
static void __devinit sh7786_pci_fixup(struct pci_dev *dev)
|
static void __devinit sh7786_pci_fixup(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -139,7 +147,7 @@ static void __devinit sh7786_pci_fixup(struct pci_dev *dev)
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786,
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786,
|
||||||
sh7786_pci_fixup);
|
sh7786_pci_fixup);
|
||||||
|
|
||||||
static int phy_wait_for_ack(struct pci_channel *chan)
|
static int __init phy_wait_for_ack(struct pci_channel *chan)
|
||||||
{
|
{
|
||||||
unsigned int timeout = 100;
|
unsigned int timeout = 100;
|
||||||
|
|
||||||
|
@ -153,7 +161,7 @@ static int phy_wait_for_ack(struct pci_channel *chan)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
|
static int __init pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
|
||||||
{
|
{
|
||||||
unsigned int timeout = 100;
|
unsigned int timeout = 100;
|
||||||
|
|
||||||
|
@ -167,8 +175,8 @@ static int pci_wait_for_irq(struct pci_channel *chan, unsigned int mask)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void phy_write_reg(struct pci_channel *chan, unsigned int addr,
|
static void __init phy_write_reg(struct pci_channel *chan, unsigned int addr,
|
||||||
unsigned int lane, unsigned int data)
|
unsigned int lane, unsigned int data)
|
||||||
{
|
{
|
||||||
unsigned long phyaddr;
|
unsigned long phyaddr;
|
||||||
|
|
||||||
|
@ -188,15 +196,67 @@ static void phy_write_reg(struct pci_channel *chan, unsigned int addr,
|
||||||
phy_wait_for_ack(chan);
|
phy_wait_for_ack(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int phy_init(struct pci_channel *chan)
|
static int __init pcie_clk_init(struct sh7786_pcie_port *port)
|
||||||
{
|
{
|
||||||
unsigned long ctrl;
|
struct pci_channel *chan = port->hose;
|
||||||
|
struct clk *clk;
|
||||||
|
char fclk_name[16];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First register the fixed clock
|
||||||
|
*/
|
||||||
|
ret = clk_register(&fixed_pciexclkp);
|
||||||
|
if (unlikely(ret != 0))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grab the port's function clock, which the PHY clock depends
|
||||||
|
* on. clock lookups don't help us much at this point, since no
|
||||||
|
* dev_id is available this early. Lame.
|
||||||
|
*/
|
||||||
|
snprintf(fclk_name, sizeof(fclk_name), "pcie%d_fck", port->index);
|
||||||
|
|
||||||
|
port->fclk = clk_get(NULL, fclk_name);
|
||||||
|
if (IS_ERR(port->fclk)) {
|
||||||
|
ret = PTR_ERR(port->fclk);
|
||||||
|
goto err_fclk;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_enable(port->fclk);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* And now, set up the PHY clock
|
||||||
|
*/
|
||||||
|
clk = &port->phy_clk;
|
||||||
|
|
||||||
|
memset(clk, 0, sizeof(struct clk));
|
||||||
|
|
||||||
|
clk->parent = &fixed_pciexclkp;
|
||||||
|
clk->enable_reg = (void __iomem *)(chan->reg_base + SH4A_PCIEPHYCTLR);
|
||||||
|
clk->enable_bit = BITS_CKE;
|
||||||
|
|
||||||
|
ret = sh_clk_mstp32_register(clk, 1);
|
||||||
|
if (unlikely(ret < 0))
|
||||||
|
goto err_phy;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_phy:
|
||||||
|
clk_disable(port->fclk);
|
||||||
|
clk_put(port->fclk);
|
||||||
|
err_fclk:
|
||||||
|
clk_unregister(&fixed_pciexclkp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init phy_init(struct sh7786_pcie_port *port)
|
||||||
|
{
|
||||||
|
struct pci_channel *chan = port->hose;
|
||||||
unsigned int timeout = 100;
|
unsigned int timeout = 100;
|
||||||
|
|
||||||
/* Enable clock */
|
clk_enable(&port->phy_clk);
|
||||||
ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR);
|
|
||||||
ctrl |= (1 << BITS_CKE);
|
|
||||||
pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR);
|
|
||||||
|
|
||||||
/* Initialize the phy */
|
/* Initialize the phy */
|
||||||
phy_write_reg(chan, 0x60, 0xf, 0x004b008b);
|
phy_write_reg(chan, 0x60, 0xf, 0x004b008b);
|
||||||
|
@ -212,9 +272,7 @@ static int phy_init(struct pci_channel *chan)
|
||||||
phy_write_reg(chan, 0x67, 0x1, 0x00000400);
|
phy_write_reg(chan, 0x67, 0x1, 0x00000400);
|
||||||
|
|
||||||
/* Disable clock */
|
/* Disable clock */
|
||||||
ctrl = pci_read_reg(chan, SH4A_PCIEPHYCTLR);
|
clk_disable(&port->phy_clk);
|
||||||
ctrl &= ~(1 << BITS_CKE);
|
|
||||||
pci_write_reg(chan, ctrl, SH4A_PCIEPHYCTLR);
|
|
||||||
|
|
||||||
while (timeout--) {
|
while (timeout--) {
|
||||||
if (pci_read_reg(chan, SH4A_PCIEPHYSR))
|
if (pci_read_reg(chan, SH4A_PCIEPHYSR))
|
||||||
|
@ -226,7 +284,7 @@ static int phy_init(struct pci_channel *chan)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pcie_reset(struct sh7786_pcie_port *port)
|
static void __init pcie_reset(struct sh7786_pcie_port *port)
|
||||||
{
|
{
|
||||||
struct pci_channel *chan = port->hose;
|
struct pci_channel *chan = port->hose;
|
||||||
|
|
||||||
|
@ -236,7 +294,7 @@ static void pcie_reset(struct sh7786_pcie_port *port)
|
||||||
pci_write_reg(chan, 0, SH4A_PCIETXVC0SR);
|
pci_write_reg(chan, 0, SH4A_PCIETXVC0SR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pcie_init(struct sh7786_pcie_port *port)
|
static int __init pcie_init(struct sh7786_pcie_port *port)
|
||||||
{
|
{
|
||||||
struct pci_channel *chan = port->hose;
|
struct pci_channel *chan = port->hose;
|
||||||
unsigned int data;
|
unsigned int data;
|
||||||
|
@ -411,26 +469,33 @@ int __init pcibios_map_platform_irq(struct pci_dev *pdev, u8 slot, u8 pin)
|
||||||
return 71;
|
return 71;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh7786_pcie_core_init(void)
|
static int __init sh7786_pcie_core_init(void)
|
||||||
{
|
{
|
||||||
/* Return the number of ports */
|
/* Return the number of ports */
|
||||||
return test_mode_pin(MODE_PIN12) ? 3 : 2;
|
return test_mode_pin(MODE_PIN12) ? 3 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __devinit sh7786_pcie_init_hw(struct sh7786_pcie_port *port)
|
static int __init sh7786_pcie_init_hw(struct sh7786_pcie_port *port)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = phy_init(port->hose);
|
|
||||||
if (unlikely(ret < 0))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if we are configured in endpoint or root complex mode,
|
* Check if we are configured in endpoint or root complex mode,
|
||||||
* this is a fixed pin setting that applies to all PCIe ports.
|
* this is a fixed pin setting that applies to all PCIe ports.
|
||||||
*/
|
*/
|
||||||
port->endpoint = test_mode_pin(MODE_PIN11);
|
port->endpoint = test_mode_pin(MODE_PIN11);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setup clocks, needed both for PHY and PCIe registers.
|
||||||
|
*/
|
||||||
|
ret = pcie_clk_init(port);
|
||||||
|
if (unlikely(ret < 0))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = phy_init(port);
|
||||||
|
if (unlikely(ret < 0))
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = pcie_init(port);
|
ret = pcie_init(port);
|
||||||
if (unlikely(ret < 0))
|
if (unlikely(ret < 0))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче