linux/drivers/bcma/driver_pci.c
<<
>>
Prefs
   1/*
   2 * Broadcom specific AMBA
   3 * PCI Core
   4 *
   5 * Copyright 2005, 2011, Broadcom Corporation
   6 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
   7 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
   8 *
   9 * Licensed under the GNU/GPL. See COPYING for details.
  10 */
  11
  12#include "bcma_private.h"
  13#include <linux/export.h>
  14#include <linux/bcma/bcma.h>
  15
  16/**************************************************
  17 * R/W ops.
  18 **************************************************/
  19
  20u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
  21{
  22        pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
  23        pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
  24        return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
  25}
  26
  27static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
  28{
  29        pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
  30        pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
  31        pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
  32}
  33
  34static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy)
  35{
  36        u32 v;
  37        int i;
  38
  39        v = BCMA_CORE_PCI_MDIODATA_START;
  40        v |= BCMA_CORE_PCI_MDIODATA_WRITE;
  41        v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
  42              BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
  43        v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
  44              BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
  45        v |= BCMA_CORE_PCI_MDIODATA_TA;
  46        v |= (phy << 4);
  47        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
  48
  49        udelay(10);
  50        for (i = 0; i < 200; i++) {
  51                v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
  52                if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
  53                        break;
  54                usleep_range(1000, 2000);
  55        }
  56}
  57
  58static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address)
  59{
  60        int max_retries = 10;
  61        u16 ret = 0;
  62        u32 v;
  63        int i;
  64
  65        /* enable mdio access to SERDES */
  66        v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
  67        v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
  68        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
  69
  70        if (pc->core->id.rev >= 10) {
  71                max_retries = 200;
  72                bcma_pcie_mdio_set_phy(pc, device);
  73                v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
  74                     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
  75                v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
  76        } else {
  77                v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
  78                v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
  79        }
  80
  81        v = BCMA_CORE_PCI_MDIODATA_START;
  82        v |= BCMA_CORE_PCI_MDIODATA_READ;
  83        v |= BCMA_CORE_PCI_MDIODATA_TA;
  84
  85        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
  86        /* Wait for the device to complete the transaction */
  87        udelay(10);
  88        for (i = 0; i < max_retries; i++) {
  89                v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
  90                if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
  91                        udelay(10);
  92                        ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
  93                        break;
  94                }
  95                usleep_range(1000, 2000);
  96        }
  97        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
  98        return ret;
  99}
 100
 101static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device,
 102                                u8 address, u16 data)
 103{
 104        int max_retries = 10;
 105        u32 v;
 106        int i;
 107
 108        /* enable mdio access to SERDES */
 109        v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
 110        v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
 111        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
 112
 113        if (pc->core->id.rev >= 10) {
 114                max_retries = 200;
 115                bcma_pcie_mdio_set_phy(pc, device);
 116                v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
 117                     BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
 118                v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
 119        } else {
 120                v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
 121                v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
 122        }
 123
 124        v = BCMA_CORE_PCI_MDIODATA_START;
 125        v |= BCMA_CORE_PCI_MDIODATA_WRITE;
 126        v |= BCMA_CORE_PCI_MDIODATA_TA;
 127        v |= data;
 128        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
 129        /* Wait for the device to complete the transaction */
 130        udelay(10);
 131        for (i = 0; i < max_retries; i++) {
 132                v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
 133                if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
 134                        break;
 135                usleep_range(1000, 2000);
 136        }
 137        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
 138}
 139
 140static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device,
 141                                    u8 address, u16 data)
 142{
 143        bcma_pcie_mdio_write(pc, device, address, data);
 144        return bcma_pcie_mdio_read(pc, device, address);
 145}
 146
 147/**************************************************
 148 * Workarounds.
 149 **************************************************/
 150
 151static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
 152{
 153        u32 tmp;
 154
 155        tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
 156        if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
 157                return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
 158                       BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
 159        else
 160                return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
 161}
 162
 163static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
 164{
 165        u16 tmp;
 166
 167        bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
 168                             BCMA_CORE_PCI_SERDES_RX_CTRL,
 169                             bcma_pcicore_polarity_workaround(pc));
 170        tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
 171                                  BCMA_CORE_PCI_SERDES_PLL_CTRL);
 172        if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
 173                bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
 174                                     BCMA_CORE_PCI_SERDES_PLL_CTRL,
 175                                     tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
 176}
 177
 178static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
 179{
 180        struct bcma_device *core = pc->core;
 181        u16 val16, core_index;
 182        uint regoff;
 183
 184        regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
 185        core_index = (u16)core->core_index;
 186
 187        val16 = pcicore_read16(pc, regoff);
 188        if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
 189             != core_index) {
 190                val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
 191                        (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
 192                pcicore_write16(pc, regoff, val16);
 193        }
 194}
 195
 196/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
 197/* Needs to happen when coming out of 'standby'/'hibernate' */
 198static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
 199{
 200        u16 val16;
 201        uint regoff;
 202
 203        regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);
 204
 205        val16 = pcicore_read16(pc, regoff);
 206
 207        if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
 208                val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
 209                pcicore_write16(pc, regoff, val16);
 210        }
 211}
 212
 213/**************************************************
 214 * Init.
 215 **************************************************/
 216
 217static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 218{
 219        bcma_core_pci_fixcfg(pc);
 220        bcma_pcicore_serdes_workaround(pc);
 221        bcma_core_pci_config_fixup(pc);
 222}
 223
 224void bcma_core_pci_init(struct bcma_drv_pci *pc)
 225{
 226        if (pc->setup_done)
 227                return;
 228
 229#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 230        pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
 231        if (pc->hostmode)
 232                bcma_core_pci_hostmode_init(pc);
 233#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
 234
 235        if (!pc->hostmode)
 236                bcma_core_pci_clientmode_init(pc);
 237}
 238
 239void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
 240{
 241        struct bcma_drv_pci *pc;
 242        u16 data;
 243
 244        if (bus->hosttype != BCMA_HOSTTYPE_PCI)
 245                return;
 246
 247        pc = &bus->drv_pci[0];
 248
 249        if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
 250                data = up ? 0x74 : 0x7C;
 251                bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
 252                                         BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
 253                bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
 254                                         BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
 255        } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
 256                data = up ? 0x75 : 0x7D;
 257                bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
 258                                         BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
 259                bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
 260                                         BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
 261        }
 262}
 263EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
 264
 265int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
 266                          bool enable)
 267{
 268        struct pci_dev *pdev;
 269        u32 coremask, tmp;
 270        int err = 0;
 271
 272        if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
 273                /* This bcma device is not on a PCI host-bus. So the IRQs are
 274                 * not routed through the PCI core.
 275                 * So we must not enable routing through the PCI core. */
 276                goto out;
 277        }
 278
 279        pdev = pc->core->bus->host_pci;
 280
 281        err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
 282        if (err)
 283                goto out;
 284
 285        coremask = BIT(core->core_index) << 8;
 286        if (enable)
 287                tmp |= coremask;
 288        else
 289                tmp &= ~coremask;
 290
 291        err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
 292
 293out:
 294        return err;
 295}
 296EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
 297
 298static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
 299{
 300        u32 w;
 301
 302        w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 303        if (extend)
 304                w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
 305        else
 306                w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
 307        bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
 308        bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 309}
 310
 311void bcma_core_pci_up(struct bcma_bus *bus)
 312{
 313        struct bcma_drv_pci *pc;
 314
 315        if (bus->hosttype != BCMA_HOSTTYPE_PCI)
 316                return;
 317
 318        pc = &bus->drv_pci[0];
 319
 320        bcma_core_pci_extend_L1timer(pc, true);
 321}
 322EXPORT_SYMBOL_GPL(bcma_core_pci_up);
 323
 324void bcma_core_pci_down(struct bcma_bus *bus)
 325{
 326        struct bcma_drv_pci *pc;
 327
 328        if (bus->hosttype != BCMA_HOSTTYPE_PCI)
 329                return;
 330
 331        pc = &bus->drv_pci[0];
 332
 333        bcma_core_pci_extend_L1timer(pc, false);
 334}
 335EXPORT_SYMBOL_GPL(bcma_core_pci_down);
 336
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.