161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/*
261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Sonics Silicon Backplane
361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Broadcom PCI-core driver
461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch *
561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Copyright 2005, Broadcom Corporation
6eb032b9837a958e21ca000358a5bde5e17192ddbMichael Buesch * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch *
861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Licensed under the GNU/GPL. See COPYING for details.
961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch */
1061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
1161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#include <linux/ssb/ssb.h>
1261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#include <linux/pci.h>
131014c22e42a6692660d1a77888d07f4811b2914dPaul Gortmaker#include <linux/export.h>
1461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#include <linux/delay.h>
157cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch#include <linux/ssb/ssb_embedded.h>
1661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
1761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#include "ssb_private.h"
1861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
19ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address);
20ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data);
21ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address);
22ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
23ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki				u8 address, u16 data);
2461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
2561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic inline
2661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschu32 pcicore_read32(struct ssb_pcicore *pc, u16 offset)
2761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
2861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return ssb_read32(pc->dev, offset);
2961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
3061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
3161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic inline
3261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschvoid pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value)
3361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
3461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	ssb_write32(pc->dev, offset, value);
3561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
3661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
377cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Bueschstatic inline
387cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Bueschu16 pcicore_read16(struct ssb_pcicore *pc, u16 offset)
397cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch{
407cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch	return ssb_read16(pc->dev, offset);
417cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch}
427cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch
437cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Bueschstatic inline
447cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Bueschvoid pcicore_write16(struct ssb_pcicore *pc, u16 offset, u16 value)
457cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch{
467cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch	ssb_write16(pc->dev, offset, value);
477cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch}
487cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch
4961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/**************************************************
5061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Code for hostmode operation.
5161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch **************************************************/
5261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
5361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#ifdef CONFIG_SSB_PCICORE_HOSTMODE
5461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
5561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#include <asm/paccess.h>
5661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/* Probe a 32bit value on the bus and catch bus exceptions.
5761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Returns nonzero on a bus exception.
5861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * This is MIPS specific */
5961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#define mips_busprobe32(val, addr)	get_dbe((val), ((u32 *)(addr)))
6061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/* Assume one-hot slot wiring */
6261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#define SSB_PCI_SLOT_MAX	16
6361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/* Global lock is OK, as we won't have more than one extpci anyway. */
6561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic DEFINE_SPINLOCK(cfgspace_lock);
6661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/* Core to access the external PCI config space. Can only have one. */
6761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic struct ssb_pcicore *extpci_core;
6861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
7061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic u32 get_cfgspace_addr(struct ssb_pcicore *pc,
7161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			     unsigned int bus, unsigned int dev,
7261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			     unsigned int func, unsigned int off)
7361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
7461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 addr = 0;
7561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 tmp;
7661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
777cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch	/* We do only have one cardbus device behind the bridge. */
78a6c84622b7fa3ea5417a9d1d0ce0bc3e7fbe3be1Hauke Mehrtens	if (pc->cardbusmode && (dev > 1))
7961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
807cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch
8161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (bus == 0) {
8261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Type 0 transaction */
8361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (unlikely(dev >= SSB_PCI_SLOT_MAX))
8461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			goto out;
8561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Slide the window */
8661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		tmp = SSB_PCICORE_SBTOPCI_CFG0;
8761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK);
8861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp);
8961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Calculate the address */
9061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr = SSB_PCI_CFG;
9161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK);
9261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (func << 8);
9361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (off & ~3);
9461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	} else {
9561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Type 1 transaction */
9661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
9761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				SSB_PCICORE_SBTOPCI_CFG1);
9861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Calculate the address */
9961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr = SSB_PCI_CFG;
10061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (bus << 16);
10161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (dev << 11);
10261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (func << 8);
10361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		addr |= (off & ~3);
10461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
10561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschout:
10661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return addr;
10761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
10861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
10961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic int ssb_extpci_read_config(struct ssb_pcicore *pc,
11061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				  unsigned int bus, unsigned int dev,
11161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				  unsigned int func, unsigned int off,
11261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				  void *buf, int len)
11361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
11461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int err = -EINVAL;
11561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 addr, val;
11661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	void __iomem *mmio;
11761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
11861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	SSB_WARN_ON(!pc->hostmode);
11961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (unlikely(len != 1 && len != 2 && len != 4))
12061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
12161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	addr = get_cfgspace_addr(pc, bus, dev, func, off);
12261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (unlikely(!addr))
12361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
12461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = -ENOMEM;
12561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	mmio = ioremap_nocache(addr, len);
12661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!mmio)
12761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
12861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
12961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (mips_busprobe32(val, mmio)) {
13061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val = 0xffffffff;
13161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto unmap;
13261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
13361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
13461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val = readl(mmio);
13561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val >>= (8 * (off & 3));
13661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
13761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	switch (len) {
13861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 1:
13961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		*((u8 *)buf) = (u8)val;
14061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
14161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 2:
14261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		*((u16 *)buf) = (u16)val;
14361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
14461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 4:
14561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		*((u32 *)buf) = (u32)val;
14661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
14761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
14861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = 0;
14961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschunmap:
15061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	iounmap(mmio);
15161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschout:
15261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return err;
15361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
15461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
15561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic int ssb_extpci_write_config(struct ssb_pcicore *pc,
15661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				   unsigned int bus, unsigned int dev,
15761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				   unsigned int func, unsigned int off,
15861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				   const void *buf, int len)
15961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
16061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int err = -EINVAL;
16161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 addr, val = 0;
16261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	void __iomem *mmio;
16361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
16461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	SSB_WARN_ON(!pc->hostmode);
16561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (unlikely(len != 1 && len != 2 && len != 4))
16661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
16761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	addr = get_cfgspace_addr(pc, bus, dev, func, off);
16861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (unlikely(!addr))
16961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
17061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = -ENOMEM;
17161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	mmio = ioremap_nocache(addr, len);
17261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!mmio)
17361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
17461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
17561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (mips_busprobe32(val, mmio)) {
17661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val = 0xffffffff;
17761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto unmap;
17861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
17961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
18061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	switch (len) {
18161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 1:
18261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val = readl(mmio);
18361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val &= ~(0xFF << (8 * (off & 3)));
18461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val |= *((const u8 *)buf) << (8 * (off & 3));
18561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
18661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 2:
18761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val = readl(mmio);
18861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val &= ~(0xFFFF << (8 * (off & 3)));
18961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val |= *((const u16 *)buf) << (8 * (off & 3));
19061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
19161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	case 4:
19261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		val = *((const u32 *)buf);
19361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		break;
19461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
19561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	writel(val, mmio);
19661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
19761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = 0;
19861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschunmap:
19961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	iounmap(mmio);
20061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschout:
20161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return err;
20261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
20361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
20461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn,
20561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				   int reg, int size, u32 *val)
20661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
20761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	unsigned long flags;
20861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int err;
20961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
21061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	spin_lock_irqsave(&cfgspace_lock, flags);
21161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn),
21261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				     PCI_FUNC(devfn), reg, val, size);
21361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	spin_unlock_irqrestore(&cfgspace_lock, flags);
21461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
21561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
21661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
21761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
21861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn,
21961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				    int reg, int size, u32 val)
22061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
22161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	unsigned long flags;
22261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int err;
22361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
22461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	spin_lock_irqsave(&cfgspace_lock, flags);
22561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn),
22661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				      PCI_FUNC(devfn), reg, &val, size);
22761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	spin_unlock_irqrestore(&cfgspace_lock, flags);
22861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
22961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
23061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
23161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
23261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic struct pci_ops ssb_pcicore_pciops = {
23361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.read	= ssb_pcicore_read_config,
23461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.write	= ssb_pcicore_write_config,
23561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch};
23661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
23761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic struct resource ssb_pcicore_mem_resource = {
23861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.name	= "SSB PCIcore external memory",
23961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.start	= SSB_PCI_DMA,
24061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.end	= SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1,
241fc71acc846c577473ada72a46c5ea9c935eca086Michael Buesch	.flags	= IORESOURCE_MEM | IORESOURCE_PCI_FIXED,
24261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch};
24361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
24461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic struct resource ssb_pcicore_io_resource = {
24561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.name	= "SSB PCIcore external I/O",
24661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.start	= 0x100,
24761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.end	= 0x7FF,
248fc71acc846c577473ada72a46c5ea9c935eca086Michael Buesch	.flags	= IORESOURCE_IO | IORESOURCE_PCI_FIXED,
24961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch};
25061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
25161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic struct pci_controller ssb_pcicore_controller = {
25261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.pci_ops	= &ssb_pcicore_pciops,
25361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.io_resource	= &ssb_pcicore_io_resource,
25461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	.mem_resource	= &ssb_pcicore_mem_resource,
25561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch};
25661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
257aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch/* This function is called when doing a pci_enable_device().
258aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch * We must first check if the device is a device on the PCI-core bridge. */
259aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Bueschint ssb_pcicore_plat_dev_init(struct pci_dev *d)
260aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch{
261aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	if (d->bus->ops != &ssb_pcicore_pciops) {
262aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		/* This is not a device on the PCI-core bridge. */
263aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		return -ENODEV;
264aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	}
265aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
266aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	ssb_printk(KERN_INFO "PCI: Fixing up device %s\n",
267aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		   pci_name(d));
268aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
269aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	/* Fix up interrupt lines */
270aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	d->irq = ssb_mips_irq(extpci_core->dev) + 2;
271aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
272aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
273aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	return 0;
274aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch}
275aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
276aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch/* Early PCI fixup for a device on the PCI-core bridge. */
277aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Bueschstatic void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
278aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch{
279aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	u8 lat;
280aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
281aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	if (dev->bus->ops != &ssb_pcicore_pciops) {
282aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		/* This is not a device on the PCI-core bridge. */
283aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		return;
284aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	}
285aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
286aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		return;
287aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
288aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	ssb_printk(KERN_INFO "PCI: Fixing up bridge %s\n", pci_name(dev));
289aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
290aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	/* Enable PCI bridge bus mastering and memory space */
291aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	pci_set_master(dev);
292aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	if (pcibios_enable_device(dev, ~0) < 0) {
293aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		ssb_printk(KERN_ERR "PCI: SSB bridge enable failed\n");
294aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		return;
295aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	}
296aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
297aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	/* Enable PCI bridge BAR1 prefetch and burst */
298aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
299aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
300aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	/* Make sure our latency is high enough to handle the devices behind us */
301aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	lat = 168;
302aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	ssb_printk(KERN_INFO "PCI: Fixing latency timer of device %s to %u\n",
303aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		   pci_name(dev), lat);
304aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
305aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch}
306aab547ce0d1493d400b6468c521a0137cd8c1edfMichael BueschDECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
307aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
308aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch/* PCI device IRQ mapping. */
309aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Bueschint ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
310aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch{
311aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	if (dev->bus->ops != &ssb_pcicore_pciops) {
312aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		/* This is not a device on the PCI-core bridge. */
313aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch		return -ENODEV;
314aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	}
315aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch	return ssb_mips_irq(extpci_core->dev) + 2;
316aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch}
317aab547ce0d1493d400b6468c521a0137cd8c1edfMichael Buesch
318cd15598707aff52da4302d9b6a3fc878bca27383Hauke Mehrtensstatic void __devinit ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
31961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
32061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 val;
32161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
32261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (WARN_ON(extpci_core))
32361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		return;
32461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	extpci_core = pc;
32561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
32661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	ssb_dprintk(KERN_INFO PFX "PCIcore in host mode found\n");
32761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Reset devices on the external PCI bus */
32861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val = SSB_PCICORE_CTL_RST_OE;
32961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val |= SSB_PCICORE_CTL_CLK_OE;
33061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_CTL, val);
33161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val |= SSB_PCICORE_CTL_CLK; /* Clock on */
33261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_CTL, val);
33361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	udelay(150); /* Assertion time demanded by the PCI standard */
33461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */
33561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_CTL, val);
33661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val = SSB_PCICORE_ARBCTL_INTERN;
33761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_ARBCTL, val);
33861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	udelay(1); /* Assertion time demanded by the PCI standard */
33961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
3407cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch	if (pc->dev->bus->has_cardbus_slot) {
3417cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		ssb_dprintk(KERN_INFO PFX "CardBus slot detected\n");
3427cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		pc->cardbusmode = 1;
3437cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		/* GPIO 1 resets the bridge */
3447cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		ssb_gpio_out(pc->dev->bus, 1, 1);
3457cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		ssb_gpio_outen(pc->dev->bus, 1, 1);
3467cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch		pcicore_write16(pc, SSB_PCICORE_SPROM(0),
3477cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch				pcicore_read16(pc, SSB_PCICORE_SPROM(0))
3487cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch				| 0x0400);
3497cb4461520f307a6e3fb2bb32cb8daee45aa1faeMichael Buesch	}
35061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
35161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* 64MB I/O window */
35261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_SBTOPCI0,
35361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			SSB_PCICORE_SBTOPCI_IO);
35461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* 64MB config space */
35561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
35661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			SSB_PCICORE_SBTOPCI_CFG0);
35761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* 1GB memory window */
35861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
35961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
36061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
36161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Enable PCI bridge BAR0 prefetch and burst */
36261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
36361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
36461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Clear error conditions */
36561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	val = 0;
36661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2);
36761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
36861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Enable PCI interrupts */
36961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, SSB_PCICORE_IMASK,
37061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			SSB_PCICORE_IMASK_INTA);
37161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
37261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Ok, ready to run, register it to the system.
37361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	 * The following needs change, if we want to port hostmode
37461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	 * to non-MIPS platform. */
375fc71acc846c577473ada72a46c5ea9c935eca086Michael Buesch	ssb_pcicore_controller.io_map_base = (unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000);
376fc71acc846c577473ada72a46c5ea9c935eca086Michael Buesch	set_io_port_base(ssb_pcicore_controller.io_map_base);
37761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Give some time to the PCI controller to configure itself with the new
37861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	 * values. Not waiting at this point causes crashes of the machine. */
37961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	mdelay(10);
38061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	register_pci_controller(&ssb_pcicore_controller);
38161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
38261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
383cd15598707aff52da4302d9b6a3fc878bca27383Hauke Mehrtensstatic int __devinit pcicore_is_in_hostmode(struct ssb_pcicore *pc)
38461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
38561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	struct ssb_bus *bus = pc->dev->bus;
38661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u16 chipid_top;
38761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 tmp;
38861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
38961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	chipid_top = (bus->chip_id & 0xFF00);
39061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (chipid_top != 0x4700 &&
39161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	    chipid_top != 0x5300)
39261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		return 0;
39361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
3942d8d4fdf78488f3362dccfd2a6bfdb4bc65c0858Aurelien Jarno	if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
39561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		return 0;
39661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
39761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* The 200-pin BCM4712 package does not bond out PCI. Even when
39861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	 * PCI is bonded out, some boards may leave the pins floating. */
39961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (bus->chip_id == 0x4712) {
40061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (bus->chip_package == SSB_CHIPPACK_BCM4712S)
40161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			return 0;
40261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (bus->chip_package == SSB_CHIPPACK_BCM4712M)
40361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			return 0;
40461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
40561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (bus->chip_id == 0x5350)
40661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		return 0;
40761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
40861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE)));
40961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
41061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
41161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
412ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki/**************************************************
413ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki * Workarounds.
414ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki **************************************************/
415ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki
416cd15598707aff52da4302d9b6a3fc878bca27383Hauke Mehrtensstatic void __devinit ssb_pcicore_fix_sprom_core_index(struct ssb_pcicore *pc)
417af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki{
418af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki	u16 tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(0));
419af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki	if (((tmp & 0xF000) >> 12) != pc->dev->core_index) {
420af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki		tmp &= ~0xF000;
421af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki		tmp |= (pc->dev->core_index << 12);
422af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki		pcicore_write16(pc, SSB_PCICORE_SPROM(0), tmp);
423af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki	}
424af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki}
425af335a6cbc3dfcba64ad31561c0da563d1c43a2dRafał Miłecki
426ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic u8 ssb_pcicore_polarity_workaround(struct ssb_pcicore *pc)
427ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki{
428ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	return (ssb_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;
429ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki}
430ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki
431ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłeckistatic void ssb_pcicore_serdes_workaround(struct ssb_pcicore *pc)
432ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki{
433ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	const u8 serdes_pll_device = 0x1D;
434ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	const u8 serdes_rx_device = 0x1F;
435ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	u16 tmp;
436ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki
437ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	ssb_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,
438ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki			    ssb_pcicore_polarity_workaround(pc));
439ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	tmp = ssb_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);
440ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki	if (tmp & 0x4000)
441ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki		ssb_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);
442ccc7c28af205888798b51b6cbc0b557ac1170a49Rafał Miłecki}
44361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
4446e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłeckistatic void ssb_pcicore_pci_setup_workarounds(struct ssb_pcicore *pc)
4456e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki{
4466e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	struct ssb_device *pdev = pc->dev;
4476e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	struct ssb_bus *bus = pdev->bus;
4486e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	u32 tmp;
4496e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
4506e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
4516e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	tmp |= SSB_PCICORE_SBTOPCI_PREF;
4526e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	tmp |= SSB_PCICORE_SBTOPCI_BURST;
4536e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
4546e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
4556e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	if (pdev->id.revision < 5) {
4566e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp = ssb_read32(pdev, SSB_IMCFGLO);
4576e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp &= ~SSB_IMCFGLO_SERTO;
4586e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp |= 2;
4596e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp &= ~SSB_IMCFGLO_REQTO;
4606e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT;
4616e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_write32(pdev, SSB_IMCFGLO, tmp);
4626e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_commit_settings(bus);
4636e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	} else if (pdev->id.revision >= 11) {
4646e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
4656e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp |= SSB_PCICORE_SBTOPCI_MRM;
4666e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
4676e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	}
4686e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki}
4696e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
4706e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłeckistatic void ssb_pcicore_pcie_setup_workarounds(struct ssb_pcicore *pc)
4716e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki{
4726e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	u32 tmp;
4735890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	u8 rev = pc->dev->id.revision;
4746e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
4755890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	if (rev == 0 || rev == 1) {
4766e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		/* TLP Workaround register. */
4776e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp = ssb_pcie_read(pc, 0x4);
4786e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		tmp |= 0x8;
4796e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcie_write(pc, 0x4, tmp);
4806e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	}
4815890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	if (rev == 1) {
4825890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		/* DLLP Link Control register. */
4835890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		tmp = ssb_pcie_read(pc, 0x100);
4845890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		tmp |= 0x40;
4855890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		ssb_pcie_write(pc, 0x100, tmp);
4865890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	}
4875890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki
4885890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	if (rev == 0) {
4896e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		const u8 serdes_rx_device = 0x1F;
4906e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
4916e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcie_mdio_write(pc, serdes_rx_device,
4926e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki					2 /* Timer */, 0x8128);
4936e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcie_mdio_write(pc, serdes_rx_device,
4946e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki					6 /* CDR */, 0x0100);
4956e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcie_mdio_write(pc, serdes_rx_device,
4966e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki					7 /* CDR BW */, 0x1466);
4975890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	} else if (rev == 3 || rev == 4 || rev == 5) {
4985890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		/* TODO: DLLP Power Management Threshold */
4995890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		ssb_pcicore_serdes_workaround(pc);
5005890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		/* TODO: ASPM */
5015890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	} else if (rev == 7) {
5025890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		/* TODO: No PLL down */
5035890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	}
5045890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki
5055890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki	if (rev >= 6) {
5065890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		/* Miscellaneous Configuration Fixup */
5075890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(5));
5085890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki		if (!(tmp & 0x8000))
5095890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki			pcicore_write16(pc, SSB_PCICORE_SPROM(5),
5105890a3ca34aae94dd736557ad8cb898ac2802aa0Rafał Miłecki					tmp | 0x8000);
5116e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki	}
5126e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki}
5136e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki
51461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch/**************************************************
51561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch * Generic and Clientmode operation code.
51661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch **************************************************/
51761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
518cd15598707aff52da4302d9b6a3fc878bca27383Hauke Mehrtensstatic void __devinit ssb_pcicore_init_clientmode(struct ssb_pcicore *pc)
51961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
520329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens	struct ssb_device *pdev = pc->dev;
521329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens	struct ssb_bus *bus = pdev->bus;
522329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens
523329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens	if (bus->bustype == SSB_BUSTYPE_PCI)
524329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens		ssb_pcicore_fix_sprom_core_index(pc);
5256ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki
52661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Disable PCI interrupts. */
527329456d1ffb416c220813725b7363cda9975c9aaHauke Mehrtens	ssb_write32(pdev, SSB_INTVEC, 0);
5286ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki
5296ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki	/* Additional PCIe always once-executed workarounds */
5306ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki	if (pc->dev->id.coreid == SSB_DEV_PCIE) {
5316ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki		ssb_pcicore_serdes_workaround(pc);
5326ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki		/* TODO: ASPM */
5336ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki		/* TODO: Clock Request Update */
5346ae8ec27868bfdbb815287bee8146acbefaee867Rafał Miłecki	}
53561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
53661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
537cd15598707aff52da4302d9b6a3fc878bca27383Hauke Mehrtensvoid __devinit ssb_pcicore_init(struct ssb_pcicore *pc)
53861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
53961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	struct ssb_device *dev = pc->dev;
54061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
54161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!dev)
54261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		return;
54361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!ssb_device_is_enabled(dev))
54461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		ssb_device_enable(dev, 0);
54561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
54661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#ifdef CONFIG_SSB_PCICORE_HOSTMODE
54761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pc->hostmode = pcicore_is_in_hostmode(pc);
54861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (pc->hostmode)
54961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		ssb_pcicore_init_hostmode(pc);
55061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
55161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!pc->hostmode)
55261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		ssb_pcicore_init_clientmode(pc);
55361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
55461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
55561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address)
55661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
55761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, 0x130, address);
55861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return pcicore_read32(pc, 0x134);
55961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
56061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
56161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data)
56261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
56361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, 0x130, address);
56461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, 0x134, data);
56561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
56661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
5671b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłeckistatic void ssb_pcie_mdio_set_phy(struct ssb_pcicore *pc, u8 phy)
5681b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki{
5691b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	const u16 mdio_control = 0x128;
5701b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	const u16 mdio_data = 0x12C;
5711b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	u32 v;
5721b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	int i;
5731b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki
5741b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	v = (1 << 30); /* Start of Transaction */
5751b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	v |= (1 << 28); /* Write Transaction */
5761b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	v |= (1 << 17); /* Turnaround */
5771b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	v |= (0x1F << 18);
5781b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	v |= (phy << 4);
5791b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	pcicore_write32(pc, mdio_data, v);
5801b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki
5811b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	udelay(10);
5821b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	for (i = 0; i < 200; i++) {
5831b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		v = pcicore_read32(pc, mdio_control);
5841b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		if (v & 0x100 /* Trans complete */)
5851b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki			break;
5861b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		msleep(1);
5871b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	}
5881b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki}
5891b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki
590ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłeckistatic u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address)
591ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki{
592ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	const u16 mdio_control = 0x128;
593ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	const u16 mdio_data = 0x12C;
594ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	int max_retries = 10;
595ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	u16 ret = 0;
596ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	u32 v;
597ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	int i;
598ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki
599ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v = 0x80; /* Enable Preamble Sequence */
600ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v |= 0x2; /* MDIO Clock Divisor */
601ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	pcicore_write32(pc, mdio_control, v);
602ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki
603ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	if (pc->dev->id.revision >= 10) {
604ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		max_retries = 200;
605ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		ssb_pcie_mdio_set_phy(pc, device);
606ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	}
607ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki
608ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v = (1 << 30); /* Start of Transaction */
609ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v |= (1 << 29); /* Read Transaction */
610ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v |= (1 << 17); /* Turnaround */
611ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	if (pc->dev->id.revision < 10)
612ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		v |= (u32)device << 22;
613ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	v |= (u32)address << 18;
614ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	pcicore_write32(pc, mdio_data, v);
615ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	/* Wait for the device to complete the transaction */
616ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	udelay(10);
6179be1cb39c6551231a4f210097685da11aa6a537bRafał Miłecki	for (i = 0; i < max_retries; i++) {
618ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		v = pcicore_read32(pc, mdio_control);
619ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		if (v & 0x100 /* Trans complete */) {
620ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki			udelay(10);
621ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki			ret = pcicore_read32(pc, mdio_data);
622ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki			break;
623ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		}
624ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki		msleep(1);
625ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	}
626ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	pcicore_write32(pc, mdio_control, 0);
627ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki	return ret;
628ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki}
629ba91d1a1bcccd90247b5b9703c1a236cc2e95698Rafał Miłecki
63061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschstatic void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
63161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				u8 address, u16 data)
63261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
63361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	const u16 mdio_control = 0x128;
63461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	const u16 mdio_data = 0x12C;
6351b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	int max_retries = 10;
63661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 v;
63761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int i;
63861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
63961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v = 0x80; /* Enable Preamble Sequence */
64061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v |= 0x2; /* MDIO Clock Divisor */
64161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, mdio_control, v);
64261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6431b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	if (pc->dev->id.revision >= 10) {
6441b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		max_retries = 200;
6451b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		ssb_pcie_mdio_set_phy(pc, device);
6461b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	}
6471b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki
64861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v = (1 << 30); /* Start of Transaction */
64961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v |= (1 << 28); /* Write Transaction */
65061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v |= (1 << 17); /* Turnaround */
6511b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	if (pc->dev->id.revision < 10)
6521b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki		v |= (u32)device << 22;
65361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v |= (u32)address << 18;
65461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	v |= data;
65561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, mdio_data, v);
65661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Wait for the device to complete the transaction */
65761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	udelay(10);
6581b1c7acd9709e545399d1b6b89888f025911c0a2Rafał Miłecki	for (i = 0; i < max_retries; i++) {
65961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		v = pcicore_read32(pc, mdio_control);
66061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (v & 0x100 /* Trans complete */)
66161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			break;
66261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		msleep(1);
66361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
66461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pcicore_write32(pc, mdio_control, 0);
66561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
66661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
66761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschint ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
66861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch				   struct ssb_device *dev)
66961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch{
67061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	struct ssb_device *pdev = pc->dev;
67161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	struct ssb_bus *bus;
67261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	int err = 0;
67361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	u32 tmp;
67461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6759e095a687b3561972272063260e14ab1bc21de08Michael Buesch	if (dev->bus->bustype != SSB_BUSTYPE_PCI) {
6769e095a687b3561972272063260e14ab1bc21de08Michael Buesch		/* This SSB device is not on a PCI host-bus. So the IRQs are
6779e095a687b3561972272063260e14ab1bc21de08Michael Buesch		 * not routed through the PCI core.
6789e095a687b3561972272063260e14ab1bc21de08Michael Buesch		 * So we must not enable routing through the PCI core. */
6799e095a687b3561972272063260e14ab1bc21de08Michael Buesch		goto out;
6809e095a687b3561972272063260e14ab1bc21de08Michael Buesch	}
6819e095a687b3561972272063260e14ab1bc21de08Michael Buesch
68261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (!pdev)
68361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
68461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	bus = pdev->bus;
68561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
686a3bafeedfff2ac5fa0a316bea4570e27900b6fccMichael Buesch	might_sleep_if(pdev->id.coreid != SSB_DEV_PCI);
687a3bafeedfff2ac5fa0a316bea4570e27900b6fccMichael Buesch
68861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Enable interrupts for this device. */
6898b45499ccb8a93cd68b1a8766786c2f8ea991ae2Michael Buesch	if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) {
69061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		u32 coremask;
69161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
69261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		/* Calculate the "coremask" for the device. */
69361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		coremask = (1 << dev->core_index);
69461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
6958b45499ccb8a93cd68b1a8766786c2f8ea991ae2Michael Buesch		SSB_WARN_ON(bus->bustype != SSB_BUSTYPE_PCI);
69661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);
69761e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (err)
69861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			goto out;
69961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		tmp |= coremask << 8;
70061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp);
70161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		if (err)
70261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch			goto out;
70361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	} else {
70461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		u32 intvec;
70561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
70661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		intvec = ssb_read32(pdev, SSB_INTVEC);
70751e8b885902fc8cc2ded48322ad9402bbcff23feMichael Buesch		tmp = ssb_read32(dev, SSB_TPSFLAG);
70851e8b885902fc8cc2ded48322ad9402bbcff23feMichael Buesch		tmp &= SSB_TPSFLAG_BPFLAG;
70951e8b885902fc8cc2ded48322ad9402bbcff23feMichael Buesch		intvec |= (1 << tmp);
71061e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		ssb_write32(pdev, SSB_INTVEC, intvec);
71161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
71261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch
71361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	/* Setup PCIcore operation. */
71461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (pc->setup_done)
71561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		goto out;
71661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	if (pdev->id.coreid == SSB_DEV_PCI) {
7176e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcicore_pci_setup_workarounds(pc);
71861e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	} else {
71961e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch		WARN_ON(pdev->id.coreid != SSB_DEV_PCIE);
7206e914101d47c76e09b0568d094ef44257dd3d6e9Rafał Miłecki		ssb_pcicore_pcie_setup_workarounds(pc);
72161e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	}
72261e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	pc->setup_done = 1;
72361e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Bueschout:
72461e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch	return err;
72561e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael Buesch}
72661e115a56d1aafd6e6a8a9fee8ac099a6128ac7bMichael BueschEXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable);
727