Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Broadcom specific AMBA
0003  * PCI Core
0004  *
0005  * Copyright 2005, 2011, Broadcom Corporation
0006  * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
0007  * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
0008  *
0009  * Licensed under the GNU/GPL. See COPYING for details.
0010  */
0011 
0012 #include "bcma_private.h"
0013 #include <linux/export.h>
0014 #include <linux/bcma/bcma.h>
0015 
0016 /**************************************************
0017  * R/W ops.
0018  **************************************************/
0019 
0020 u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
0021 {
0022     pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
0023     pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
0024     return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
0025 }
0026 
0027 static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
0028 {
0029     pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
0030     pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
0031     pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
0032 }
0033 
0034 static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy)
0035 {
0036     u32 v;
0037     int i;
0038 
0039     v = BCMA_CORE_PCI_MDIODATA_START;
0040     v |= BCMA_CORE_PCI_MDIODATA_WRITE;
0041     v |= (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
0042           BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
0043     v |= (BCMA_CORE_PCI_MDIODATA_BLK_ADDR <<
0044           BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
0045     v |= BCMA_CORE_PCI_MDIODATA_TA;
0046     v |= (phy << 4);
0047     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
0048 
0049     udelay(10);
0050     for (i = 0; i < 200; i++) {
0051         v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
0052         if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
0053             break;
0054         usleep_range(1000, 2000);
0055     }
0056 }
0057 
0058 static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address)
0059 {
0060     int max_retries = 10;
0061     u16 ret = 0;
0062     u32 v;
0063     int i;
0064 
0065     /* enable mdio access to SERDES */
0066     v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
0067     v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
0068     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
0069 
0070     if (pc->core->id.rev >= 10) {
0071         max_retries = 200;
0072         bcma_pcie_mdio_set_phy(pc, device);
0073         v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
0074              BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
0075         v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
0076     } else {
0077         v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
0078         v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
0079     }
0080 
0081     v |= BCMA_CORE_PCI_MDIODATA_START;
0082     v |= BCMA_CORE_PCI_MDIODATA_READ;
0083     v |= BCMA_CORE_PCI_MDIODATA_TA;
0084 
0085     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
0086     /* Wait for the device to complete the transaction */
0087     udelay(10);
0088     for (i = 0; i < max_retries; i++) {
0089         v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
0090         if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE) {
0091             udelay(10);
0092             ret = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_DATA);
0093             break;
0094         }
0095         usleep_range(1000, 2000);
0096     }
0097     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
0098     return ret;
0099 }
0100 
0101 static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device,
0102                 u8 address, u16 data)
0103 {
0104     int max_retries = 10;
0105     u32 v;
0106     int i;
0107 
0108     /* enable mdio access to SERDES */
0109     v = BCMA_CORE_PCI_MDIOCTL_PREAM_EN;
0110     v |= BCMA_CORE_PCI_MDIOCTL_DIVISOR_VAL;
0111     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, v);
0112 
0113     if (pc->core->id.rev >= 10) {
0114         max_retries = 200;
0115         bcma_pcie_mdio_set_phy(pc, device);
0116         v = (BCMA_CORE_PCI_MDIODATA_DEV_ADDR <<
0117              BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF);
0118         v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF);
0119     } else {
0120         v = (device << BCMA_CORE_PCI_MDIODATA_DEVADDR_SHF_OLD);
0121         v |= (address << BCMA_CORE_PCI_MDIODATA_REGADDR_SHF_OLD);
0122     }
0123 
0124     v |= BCMA_CORE_PCI_MDIODATA_START;
0125     v |= BCMA_CORE_PCI_MDIODATA_WRITE;
0126     v |= BCMA_CORE_PCI_MDIODATA_TA;
0127     v |= data;
0128     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_DATA, v);
0129     /* Wait for the device to complete the transaction */
0130     udelay(10);
0131     for (i = 0; i < max_retries; i++) {
0132         v = pcicore_read32(pc, BCMA_CORE_PCI_MDIO_CONTROL);
0133         if (v & BCMA_CORE_PCI_MDIOCTL_ACCESS_DONE)
0134             break;
0135         usleep_range(1000, 2000);
0136     }
0137     pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
0138 }
0139 
0140 static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device,
0141                     u8 address, u16 data)
0142 {
0143     bcma_pcie_mdio_write(pc, device, address, data);
0144     return bcma_pcie_mdio_read(pc, device, address);
0145 }
0146 
0147 /**************************************************
0148  * Early init.
0149  **************************************************/
0150 
0151 static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
0152 {
0153     struct bcma_device *core = pc->core;
0154     u16 val16, core_index;
0155     uint regoff;
0156 
0157     regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
0158     core_index = (u16)core->core_index;
0159 
0160     val16 = pcicore_read16(pc, regoff);
0161     if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
0162          != core_index) {
0163         val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
0164             (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
0165         pcicore_write16(pc, regoff, val16);
0166     }
0167 }
0168 
0169 /*
0170  * Apply some early fixes required before accessing SPROM.
0171  * See also si_pci_fixcfg.
0172  */
0173 void bcma_core_pci_early_init(struct bcma_drv_pci *pc)
0174 {
0175     if (pc->early_setup_done)
0176         return;
0177 
0178     pc->hostmode = bcma_core_pci_is_in_hostmode(pc);
0179     if (pc->hostmode)
0180         goto out;
0181 
0182     bcma_core_pci_fixcfg(pc);
0183 
0184 out:
0185     pc->early_setup_done = true;
0186 }
0187 
0188 /**************************************************
0189  * Workarounds.
0190  **************************************************/
0191 
0192 static u8 bcma_pcicore_polarity_workaround(struct bcma_drv_pci *pc)
0193 {
0194     u32 tmp;
0195 
0196     tmp = bcma_pcie_read(pc, BCMA_CORE_PCI_PLP_STATUSREG);
0197     if (tmp & BCMA_CORE_PCI_PLP_POLARITYINV_STAT)
0198         return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE |
0199                BCMA_CORE_PCI_SERDES_RX_CTRL_POLARITY;
0200     else
0201         return BCMA_CORE_PCI_SERDES_RX_CTRL_FORCE;
0202 }
0203 
0204 static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
0205 {
0206     u16 tmp;
0207 
0208     bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_RX,
0209                          BCMA_CORE_PCI_SERDES_RX_CTRL,
0210                  bcma_pcicore_polarity_workaround(pc));
0211     tmp = bcma_pcie_mdio_read(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
0212                               BCMA_CORE_PCI_SERDES_PLL_CTRL);
0213     if (tmp & BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN)
0214         bcma_pcie_mdio_write(pc, BCMA_CORE_PCI_MDIODATA_DEV_PLL,
0215                              BCMA_CORE_PCI_SERDES_PLL_CTRL,
0216                              tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
0217 }
0218 
0219 /* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
0220 /* Needs to happen when coming out of 'standby'/'hibernate' */
0221 static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
0222 {
0223     u16 val16;
0224     uint regoff;
0225 
0226     regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);
0227 
0228     val16 = pcicore_read16(pc, regoff);
0229 
0230     if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
0231         val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
0232         pcicore_write16(pc, regoff, val16);
0233     }
0234 }
0235 
0236 /**************************************************
0237  * Init.
0238  **************************************************/
0239 
0240 static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
0241 {
0242     bcma_pcicore_serdes_workaround(pc);
0243     bcma_core_pci_config_fixup(pc);
0244 }
0245 
0246 void bcma_core_pci_init(struct bcma_drv_pci *pc)
0247 {
0248     if (pc->setup_done)
0249         return;
0250 
0251     bcma_core_pci_early_init(pc);
0252 
0253     if (pc->hostmode)
0254         bcma_core_pci_hostmode_init(pc);
0255     else
0256         bcma_core_pci_clientmode_init(pc);
0257 }
0258 
0259 void bcma_core_pci_power_save(struct bcma_bus *bus, bool up)
0260 {
0261     struct bcma_drv_pci *pc;
0262     u16 data;
0263 
0264     if (bus->hosttype != BCMA_HOSTTYPE_PCI)
0265         return;
0266 
0267     pc = &bus->drv_pci[0];
0268 
0269     if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
0270         data = up ? 0x74 : 0x7C;
0271         bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
0272                      BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
0273         bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
0274                      BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
0275     } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
0276         data = up ? 0x75 : 0x7D;
0277         bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
0278                      BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
0279         bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
0280                      BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
0281     }
0282 }
0283 EXPORT_SYMBOL_GPL(bcma_core_pci_power_save);
0284 
0285 static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
0286 {
0287     u32 w;
0288 
0289     w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
0290     if (extend)
0291         w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
0292     else
0293         w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
0294     bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
0295     bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
0296 }
0297 
0298 void bcma_core_pci_up(struct bcma_drv_pci *pc)
0299 {
0300     bcma_core_pci_extend_L1timer(pc, true);
0301 }
0302 
0303 void bcma_core_pci_down(struct bcma_drv_pci *pc)
0304 {
0305     bcma_core_pci_extend_L1timer(pc, false);
0306 }