diff options
Diffstat (limited to 'drivers/bcma/driver_pci.c')
| -rw-r--r-- | drivers/bcma/driver_pci.c | 289 |
1 files changed, 193 insertions, 96 deletions
diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 4fde6254f04..50329d1057e 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -2,8 +2,9 @@ * Broadcom specific AMBA * PCI Core * - * Copyright 2005, Broadcom Corporation + * Copyright 2005, 2011, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch <m@bues.ch> + * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -16,120 +17,131 @@ * R/W ops. **************************************************/ -static u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address) +u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address) { - pcicore_write32(pc, 0x130, address); - pcicore_read32(pc, 0x130); - return pcicore_read32(pc, 0x134); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA); } -#if 0 static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data) { - pcicore_write32(pc, 0x130, address); - pcicore_read32(pc, 0x130); - pcicore_write32(pc, 0x134, data); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address); + pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR); + pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data); } -#endif -static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy) +static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; u32 v; int i; - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 28); /* Write Transaction */ - v |= (1 << 17); /* Turnaround */ - v |= (0x1F << 18); + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR << + BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + v |= BCMA_CORE_PCI_MDIODATA_TA; v |= (phy << 4); - pcicore_write32(pc, mdio_data, v); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); udelay(10); for (i = 0; i < 200; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; - msleep(1); + usleep_range(1000, 2000); } } -static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address) +static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; int max_retries = 10; u16 ret = 0; u32 v; int i; - v = 0x80; /* Enable Preamble Sequence */ - v |= 0x2; /* MDIO Clock Divisor */ - pcicore_write32(pc, mdio_control, v); + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 29); /* Read Transaction */ - v |= (1 << 17); /* Turnaround */ - if (pc->core->id.rev < 10) - v |= (u32)device << 22; - v |= (u32)address << 18; - pcicore_write32(pc, mdio_data, v); + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_READ; + v |= BCMA_CORE_PCI_MDIODATA_TA; + + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) { + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) { udelay(10); - ret = pcicore_read32(pc, mdio_data); + ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA); break; } - msleep(1); + usleep_range(1000, 2000); } - pcicore_write32(pc, mdio_control, 0); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); return ret; } -static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, +static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device, u8 address, u16 data) { - const u16 mdio_control = 0x128; - const u16 mdio_data = 0x12C; int max_retries = 10; u32 v; int i; - v = 0x80; /* Enable Preamble Sequence */ - v |= 0x2; /* MDIO Clock Divisor */ - pcicore_write32(pc, mdio_control, v); + /* enable mdio access to SERDES */ + v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN; + v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL; + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v); if (pc->core->id.rev >= 10) { max_retries = 200; bcma_pcie_mdio_set_phy(pc, device); + v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR << + BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF); + } else { + v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD); + v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD); } - v = (1 << 30); /* Start of Transaction */ - v |= (1 << 28); /* Write Transaction */ - v |= (1 << 17); /* Turnaround */ - if (pc->core->id.rev < 10) - v |= (u32)device << 22; - v |= (u32)address << 18; + v = BCMA_CORE_PCI_MDIODATA_START; + v |= BCMA_CORE_PCI_MDIODATA_WRITE; + v |= BCMA_CORE_PCI_MDIODATA_TA; v |= data; - pcicore_write32(pc, mdio_data, v); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v); /* Wait for the device to complete the transaction */ udelay(10); for (i = 0; i < max_retries; i++) { - v = pcicore_read32(pc, mdio_control); - if (v & 0x100 /* Trans complete */) + v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL); + if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) break; - msleep(1); + usleep_range(1000, 2000); } - pcicore_write32(pc, mdio_control, 0); + pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0); +} + +static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device, + u8 address, u16 data) +{ + bcma_pcie_mdio_write(pc, device, address, data); + return bcma_pcie_mdio_read(pc, device, address); } /************************************************** @@ -138,54 +150,75 @@ static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device, static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc) { - return (bcma_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80; + u32 tmp; + + tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG); + if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT) + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE | + BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY; + else + return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE; } static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) { - const u8 serdes_pll_device = 0x1D; - const u8 serdes_rx_device = 0x1F; u16 tmp; - bcma_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */, - bcma_pcicore_polarity_workaround(pc)); - tmp = bcma_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */); - if (tmp & 0x4000) - bcma_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000); + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX, + BCMA_CORE_PCI_SERDES_RX_CTRL, + bcma_pcicore_polarity_workaround(pc)); + tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL); + if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN) + bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL, + BCMA_CORE_PCI_SERDES_PLL_CTRL, + tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN); } -/************************************************** - * Init. - **************************************************/ - -static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) +static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc) { - bcma_pcicore_serdes_workaround(pc); + struct bcma_device *core = pc->core; + u16 val16, core_index; + uint regoff; + + regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET); + core_index = (u16)core->core_index; + + val16 = pcicore_read16(pc, regoff); + if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT) + != core_index) { + val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) | + (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK); + pcicore_write16(pc, regoff, val16); + } } -static bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) +/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */ +/* Needs to happen when coming out of 'standby'/'hibernate' */ +static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) { - struct bcma_bus *bus = pc->core->bus; - u16 chipid_top; + u16 val16; + uint regoff; - chipid_top = (bus->chipinfo.id & 0xFF00); - if (chipid_top != 0x4700 && - chipid_top != 0x5300) - return false; + regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG); -#ifdef CONFIG_SSB_DRIVER_PCICORE - if (bus->sprom.boardflags_lo & SSB_BFL_NOPCI) - return false; -#endif /* CONFIG_SSB_DRIVER_PCICORE */ + val16 = pcicore_read16(pc, regoff); -#if 0 - /* TODO: on BCMA we use address from EROM instead of magic formula */ - u32 tmp; - return !mips_busprobe32(tmp, (bus->mmio + - (pc->core->core_index * BCMA_CORE_SIZE))); -#endif + if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) { + val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST; + pcicore_write16(pc, regoff, val16); + } +} + +/************************************************** + * Init. + **************************************************/ - return true; +static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) +{ + bcma_core_pci_fixcfg(pc); + bcma_pcicore_serdes_workaround(pc); + bcma_core_pci_config_fixup(pc); } void bcma_core_pci_init(struct bcma_drv_pci *pc) @@ -193,33 +226,58 @@ void bcma_core_pci_init(struct bcma_drv_pci *pc) if (pc->setup_done) return; - if (bcma_core_pci_is_in_hostmode(pc)) { #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE + pc->hostmode = bcma_core_pci_is_in_hostmode(pc); + if (pc->hostmode) bcma_core_pci_hostmode_init(pc); -#else - pr_err("Driver compiled without support for hostmode PCI\n"); #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ - } else { + + if (!pc->hostmode) bcma_core_pci_clientmode_init(pc); - } +} + +void bcma_core_pci_power_save(struct bcma_bus *bus, bool up) +{ + struct bcma_drv_pci *pc; + u16 data; - pc->setup_done = true; + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) { + data = up ? 0x74 : 0x7C; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) { + data = up ? 0x75 : 0x7D; + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65); + bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1, + BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data); + } } +EXPORT_SYMBOL_GPL(bcma_core_pci_power_save); int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core, bool enable) { - struct pci_dev *pdev = pc->core->bus->host_pci; + struct pci_dev *pdev; u32 coremask, tmp; int err = 0; - if (core->bus->hosttype != BCMA_HOSTTYPE_PCI) { + if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) { /* This bcma device is not on a PCI host-bus. So the IRQs are * not routed through the PCI core. * So we must not enable routing through the PCI core. */ goto out; } + pdev = pc->core->bus->host_pci; + err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp); if (err) goto out; @@ -236,3 +294,42 @@ out: return err; } EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl); + +static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend) +{ + u32 w; + + w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); + if (extend) + w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND; + else + w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND; + bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w); + bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG); +} + +void bcma_core_pci_up(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, true); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_up); + +void bcma_core_pci_down(struct bcma_bus *bus) +{ + struct bcma_drv_pci *pc; + + if (bus->hosttype != BCMA_HOSTTYPE_PCI) + return; + + pc = &bus->drv_pci[0]; + + bcma_core_pci_extend_L1timer(pc, false); +} +EXPORT_SYMBOL_GPL(bcma_core_pci_down); |
