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, u8 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                msleep(1);
  55        }
  56}
  57
  58static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 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                msleep(1);
  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, u8 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                msleep(1);
 136        }
 137        pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
 138}
 139
 140/**************************************************
 141 * Workarounds.
 142 **************************************************/
 143
 144static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
 145{
 146        u32 tmp;
 147
 148        tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
 149        if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
 150                return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
 151                       BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
 152        else
 153                return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
 154}
 155
 156static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
 157{
 158        u16 tmp;
 159
 160        bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
 161                             BCMA_CORE_PCI_SERDES_RX_CTRL,
 162                             bcma_pcicore_polarity_workaround(pc));
 163        tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
 164                                  BCMA_CORE_PCI_SERDES_PLL_CTRL);
 165        if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
 166                bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
 167                                     BCMA_CORE_PCI_SERDES_PLL_CTRL,
 168                                     tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
 169}
 170
 171static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
 172{
 173        struct bcma_device *core = pc->core;
 174        u16 val16, core_index;
 175        uint regoff;
 176
 177        regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
 178        core_index = (u16)core->core_index;
 179
 180        val16 = pcicore_read16(pc, regoff);
 181        if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
 182             != core_index) {
 183                val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
 184                        (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
 185                pcicore_write16(pc, regoff, val16);
 186        }
 187}
 188
 189/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
 190/* Needs to happen when coming out of 'standby'/'hibernate' */
 191static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
 192{
 193        u16 val16;
 194        uint regoff;
 195
 196        regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);
 197
 198        val16 = pcicore_read16(pc, regoff);
 199
 200        if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
 201                val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
 202                pcicore_write16(pc, regoff, val16);
 203        }
 204}
 205
 206/**************************************************
 207 * Init.
 208 **************************************************/
 209
 210static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
 211{
 212        bcma_core_pci_fixcfg(pc);
 213        bcma_pcicore_serdes_workaround(pc);
 214        bcma_core_pci_config_fixup(pc);
 215}
 216
 217void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
 218{
 219        if (pc->setup_done)
 220                return;
 221
 222#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
 223        pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
 224        if (pc->hostmode)
 225                bcma_core_pci_hostmode_init(pc);
 226#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */
 227
 228        if (!pc->hostmode)
 229                bcma_core_pci_clientmode_init(pc);
 230}
 231
 232int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc, struct bcma_device *core,
 233                          bool enable)
 234{
 235        struct pci_dev *pdev;
 236        u32 coremask, tmp;
 237        int err = 0;
 238
 239        if (!pc || core->bus->hosttype != BCMA_HOSTTYPE_PCI) {
 240                /* This bcma device is not on a PCI host-bus. So the IRQs are
 241                 * not routed through the PCI core.
 242                 * So we must not enable routing through the PCI core. */
 243                goto out;
 244        }
 245
 246        pdev = pc->core->bus->host_pci;
 247
 248        err = pci_read_config_dword(pdev, BCMA_PCI_IRQMASK, &tmp);
 249        if (err)
 250                goto out;
 251
 252        coremask = BIT(core->core_index) << 8;
 253        if (enable)
 254                tmp |= coremask;
 255        else
 256                tmp &= ~coremask;
 257
 258        err = pci_write_config_dword(pdev, BCMA_PCI_IRQMASK, tmp);
 259
 260out:
 261        return err;
 262}
 263EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
 264
 265void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
 266{
 267        u32 w;
 268
 269        w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 270        if (extend)
 271                w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
 272        else
 273                w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
 274        bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
 275        bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
 276}
 277EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);
 278
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.