brintos

brintos / linux-shallow public Read only

0
0
Text · 18.8 KiB · d11b424 Raw
743 lines · c
1/*2 * Sonics Silicon Backplane3 * Broadcom PCI-core driver4 *5 * Copyright 2005, Broadcom Corporation6 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>7 *8 * Licensed under the GNU/GPL. See COPYING for details.9 */10 11#include "ssb_private.h"12 13#include <linux/ssb/ssb.h>14#include <linux/pci.h>15#include <linux/export.h>16#include <linux/delay.h>17#include <linux/ssb/ssb_embedded.h>18 19static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address);20static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data);21static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address);22static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,23				u8 address, u16 data);24 25static inline26u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset)27{28	return ssb_read32(pc->dev, offset);29}30 31static inline32void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value)33{34	ssb_write32(pc->dev, offset, value);35}36 37static inline38u16 pcicore_read16(struct ssb_pcicore *pc, u16 offset)39{40	return ssb_read16(pc->dev, offset);41}42 43static inline44void pcicore_write16(struct ssb_pcicore *pc, u16 offset, u16 value)45{46	ssb_write16(pc->dev, offset, value);47}48 49/**************************************************50 * Code for hostmode operation.51 **************************************************/52 53#ifdef CONFIG_SSB_PCICORE_HOSTMODE54 55#include <asm/paccess.h>56/* Probe a 32bit value on the bus and catch bus exceptions.57 * Returns nonzero on a bus exception.58 * This is MIPS specific59 */60#define mips_busprobe32(val, addr)	get_dbe((val), ((u32 *)(addr)))61 62/* Assume one-hot slot wiring */63#define SSB_PCI_SLOT_MAX	1664 65/* Global lock is OK, as we won't have more than one extpci anyway. */66static DEFINE_SPINLOCK(cfgspace_lock);67/* Core to access the external PCI config space. Can only have one. */68static struct ssb_pcicore *extpci_core;69 70 71static u32 get_cfgspace_addr(struct ssb_pcicore *pc,72			     unsigned int bus, unsigned int dev,73			     unsigned int func, unsigned int off)74{75	u32 addr = 0;76	u32 tmp;77 78	/* We do only have one cardbus device behind the bridge. */79	if (pc->cardbusmode && (dev > 1))80		goto out;81 82	if (bus == 0) {83		/* Type 0 transaction */84		if (unlikely(dev >= SSB_PCI_SLOT_MAX))85			goto out;86		/* Slide the window */87		tmp = SSB_PCICORE_SBTOPCI_CFG0;88		tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK);89		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp);90		/* Calculate the address */91		addr = SSB_PCI_CFG;92		addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK);93		addr |= (func << 8);94		addr |= (off & ~3);95	} else {96		/* Type 1 transaction */97		pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,98				SSB_PCICORE_SBTOPCI_CFG1);99		/* Calculate the address */100		addr = SSB_PCI_CFG;101		addr |= (bus << 16);102		addr |= (dev << 11);103		addr |= (func << 8);104		addr |= (off & ~3);105	}106out:107	return addr;108}109 110static int ssb_extpci_read_config(struct ssb_pcicore *pc,111				  unsigned int bus, unsigned int dev,112				  unsigned int func, unsigned int off,113				  void *buf, int len)114{115	int err = -EINVAL;116	u32 addr, val;117	void __iomem *mmio;118 119	WARN_ON(!pc->hostmode);120	if (unlikely(len != 1 && len != 2 && len != 4))121		goto out;122	addr = get_cfgspace_addr(pc, bus, dev, func, off);123	if (unlikely(!addr))124		goto out;125	err = -ENOMEM;126	mmio = ioremap(addr, len);127	if (!mmio)128		goto out;129 130	if (mips_busprobe32(val, mmio)) {131		val = 0xffffffff;132		goto unmap;133	}134 135	val = readl(mmio);136	val >>= (8 * (off & 3));137 138	switch (len) {139	case 1:140		*((u8 *)buf) = (u8)val;141		break;142	case 2:143		*((u16 *)buf) = (u16)val;144		break;145	case 4:146		*((u32 *)buf) = (u32)val;147		break;148	}149	err = 0;150unmap:151	iounmap(mmio);152out:153	return err;154}155 156static int ssb_extpci_write_config(struct ssb_pcicore *pc,157				   unsigned int bus, unsigned int dev,158				   unsigned int func, unsigned int off,159				   const void *buf, int len)160{161	int err = -EINVAL;162	u32 addr, val = 0;163	void __iomem *mmio;164 165	WARN_ON(!pc->hostmode);166	if (unlikely(len != 1 && len != 2 && len != 4))167		goto out;168	addr = get_cfgspace_addr(pc, bus, dev, func, off);169	if (unlikely(!addr))170		goto out;171	err = -ENOMEM;172	mmio = ioremap(addr, len);173	if (!mmio)174		goto out;175 176	if (mips_busprobe32(val, mmio)) {177		val = 0xffffffff;178		goto unmap;179	}180 181	switch (len) {182	case 1:183		val = readl(mmio);184		val &= ~(0xFF << (8 * (off & 3)));185		val |= *((const u8 *)buf) << (8 * (off & 3));186		break;187	case 2:188		val = readl(mmio);189		val &= ~(0xFFFF << (8 * (off & 3)));190		val |= *((const u16 *)buf) << (8 * (off & 3));191		break;192	case 4:193		val = *((const u32 *)buf);194		break;195	}196	writel(val, mmio);197 198	err = 0;199unmap:200	iounmap(mmio);201out:202	return err;203}204 205static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn,206				   int reg, int size, u32 *val)207{208	unsigned long flags;209	int err;210 211	spin_lock_irqsave(&cfgspace_lock, flags);212	err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn),213				     PCI_FUNC(devfn), reg, val, size);214	spin_unlock_irqrestore(&cfgspace_lock, flags);215 216	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;217}218 219static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn,220				    int reg, int size, u32 val)221{222	unsigned long flags;223	int err;224 225	spin_lock_irqsave(&cfgspace_lock, flags);226	err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn),227				      PCI_FUNC(devfn), reg, &val, size);228	spin_unlock_irqrestore(&cfgspace_lock, flags);229 230	return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;231}232 233static struct pci_ops ssb_pcicore_pciops = {234	.read	= ssb_pcicore_read_config,235	.write	= ssb_pcicore_write_config,236};237 238static struct resource ssb_pcicore_mem_resource = {239	.name	= "SSB PCIcore external memory",240	.start	= SSB_PCI_DMA,241	.end	= SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1,242	.flags	= IORESOURCE_MEM | IORESOURCE_PCI_FIXED,243};244 245static struct resource ssb_pcicore_io_resource = {246	.name	= "SSB PCIcore external I/O",247	.start	= 0x100,248	.end	= 0x7FF,249	.flags	= IORESOURCE_IO | IORESOURCE_PCI_FIXED,250};251 252static struct pci_controller ssb_pcicore_controller = {253	.pci_ops	= &ssb_pcicore_pciops,254	.io_resource	= &ssb_pcicore_io_resource,255	.mem_resource	= &ssb_pcicore_mem_resource,256};257 258/* This function is called when doing a pci_enable_device().259 * We must first check if the device is a device on the PCI-core bridge.260 */261int ssb_pcicore_plat_dev_init(struct pci_dev *d)262{263	if (d->bus->ops != &ssb_pcicore_pciops) {264		/* This is not a device on the PCI-core bridge. */265		return -ENODEV;266	}267 268	dev_info(&d->dev, "PCI: Fixing up device %s\n", pci_name(d));269 270	/* Fix up interrupt lines */271	d->irq = ssb_mips_irq(extpci_core->dev) + 2;272	pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);273 274	return 0;275}276 277/* Early PCI fixup for a device on the PCI-core bridge. */278static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)279{280	u8 lat;281 282	if (dev->bus->ops != &ssb_pcicore_pciops) {283		/* This is not a device on the PCI-core bridge. */284		return;285	}286	if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)287		return;288 289	dev_info(&dev->dev, "PCI: Fixing up bridge %s\n", pci_name(dev));290 291	/* Enable PCI bridge bus mastering and memory space */292	pci_set_master(dev);293	if (pcibios_enable_device(dev, ~0) < 0) {294		dev_err(&dev->dev, "PCI: SSB bridge enable failed\n");295		return;296	}297 298	/* Enable PCI bridge BAR1 prefetch and burst */299	pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);300 301	/* Make sure our latency is high enough to handle the devices behind us */302	lat = 168;303	dev_info(&dev->dev,304		 "PCI: Fixing latency timer of device %s to %u\n",305		 pci_name(dev), lat);306	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);307}308DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);309 310/* PCI device IRQ mapping. */311int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)312{313	if (dev->bus->ops != &ssb_pcicore_pciops) {314		/* This is not a device on the PCI-core bridge. */315		return -ENODEV;316	}317	return ssb_mips_irq(extpci_core->dev) + 2;318}319 320static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)321{322	u32 val;323 324	if (WARN_ON(extpci_core))325		return;326	extpci_core = pc;327 328	dev_dbg(pc->dev->dev, "PCIcore in host mode found\n");329	/* Reset devices on the external PCI bus */330	val = SSB_PCICORE_CTL_RST_OE;331	val |= SSB_PCICORE_CTL_CLK_OE;332	pcicore_write32(pc, SSB_PCICORE_CTL, val);333	val |= SSB_PCICORE_CTL_CLK; /* Clock on */334	pcicore_write32(pc, SSB_PCICORE_CTL, val);335	udelay(150); /* Assertion time demanded by the PCI standard */336	val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */337	pcicore_write32(pc, SSB_PCICORE_CTL, val);338	val = SSB_PCICORE_ARBCTL_INTERN;339	pcicore_write32(pc, SSB_PCICORE_ARBCTL, val);340	udelay(1); /* Assertion time demanded by the PCI standard */341 342	if (pc->dev->bus->has_cardbus_slot) {343		dev_dbg(pc->dev->dev, "CardBus slot detected\n");344		pc->cardbusmode = 1;345		/* GPIO 1 resets the bridge */346		ssb_gpio_out(pc->dev->bus, 1, 1);347		ssb_gpio_outen(pc->dev->bus, 1, 1);348		pcicore_write16(pc, SSB_PCICORE_SPROM(0),349				pcicore_read16(pc, SSB_PCICORE_SPROM(0))350				| 0x0400);351	}352 353	/* 64MB I/O window */354	pcicore_write32(pc, SSB_PCICORE_SBTOPCI0,355			SSB_PCICORE_SBTOPCI_IO);356	/* 64MB config space */357	pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,358			SSB_PCICORE_SBTOPCI_CFG0);359	/* 1GB memory window */360	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,361			SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);362 363	/*364	 * Accessing PCI config without a proper delay after devices reset (not365	 * GPIO reset) was causing reboots on WRT300N v1.0 (BCM4704).366	 * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it367	 * completely. Flushing all writes was also tested but with no luck.368	 * The same problem was reported for WRT350N v1 (BCM4705), so we just369	 * sleep here unconditionally.370	 */371	usleep_range(1000, 2000);372 373	/* Enable PCI bridge BAR0 prefetch and burst */374	val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;375	ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);376	/* Clear error conditions */377	val = 0;378	ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2);379 380	/* Enable PCI interrupts */381	pcicore_write32(pc, SSB_PCICORE_IMASK,382			SSB_PCICORE_IMASK_INTA);383 384	/* Ok, ready to run, register it to the system.385	 * The following needs change, if we want to port hostmode386	 * to non-MIPS platform.387	 */388	ssb_pcicore_controller.io_map_base = (unsigned long)ioremap(SSB_PCI_MEM, 0x04000000);389	set_io_port_base(ssb_pcicore_controller.io_map_base);390	/* Give some time to the PCI controller to configure itself with the new391	 * values. Not waiting at this point causes crashes of the machine.392	 */393	mdelay(10);394	register_pci_controller(&ssb_pcicore_controller);395}396 397static int pcicore_is_in_hostmode(struct ssb_pcicore *pc)398{399	struct ssb_bus *bus = pc->dev->bus;400	u16 chipid_top;401	u32 tmp;402 403	chipid_top = (bus->chip_id & 0xFF00);404	if (chipid_top != 0x4700 &&405	    chipid_top != 0x5300)406		return 0;407 408	if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)409		return 0;410 411	/* The 200-pin BCM4712 package does not bond out PCI. Even when412	 * PCI is bonded out, some boards may leave the pins floating.413	 */414	if (bus->chip_id == 0x4712) {415		if (bus->chip_package == SSB_CHIPPACK_BCM4712S)416			return 0;417		if (bus->chip_package == SSB_CHIPPACK_BCM4712M)418			return 0;419	}420	if (bus->chip_id == 0x5350)421		return 0;422 423	return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE)));424}425#endif /* CONFIG_SSB_PCICORE_HOSTMODE */426 427/**************************************************428 * Workarounds.429 **************************************************/430 431static void ssb_pcicore_fix_sprom_core_index(struct ssb_pcicore *pc)432{433	u16 tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(0));434	if (((tmp & 0xF000) >> 12) != pc->dev->core_index) {435		tmp &= ~0xF000;436		tmp |= (pc->dev->core_index << 12);437		pcicore_write16(pc, SSB_PCICORE_SPROM(0), tmp);438	}439}440 441static u8 ssb_pcicore_polarity_workaround(struct ssb_pcicore *pc)442{443	return (ssb_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;444}445 446static void ssb_pcicore_serdes_workaround(struct ssb_pcicore *pc)447{448	const u8 serdes_pll_device = 0x1D;449	const u8 serdes_rx_device = 0x1F;450	u16 tmp;451 452	ssb_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,453			    ssb_pcicore_polarity_workaround(pc));454	tmp = ssb_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);455	if (tmp & 0x4000)456		ssb_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);457}458 459static void ssb_pcicore_pci_setup_workarounds(struct ssb_pcicore *pc)460{461	struct ssb_device *pdev = pc->dev;462	struct ssb_bus *bus = pdev->bus;463	u32 tmp;464 465	tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);466	tmp |= SSB_PCICORE_SBTOPCI_PREF;467	tmp |= SSB_PCICORE_SBTOPCI_BURST;468	pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);469 470	if (pdev->id.revision < 5) {471		tmp = ssb_read32(pdev, SSB_IMCFGLO);472		tmp &= ~SSB_IMCFGLO_SERTO;473		tmp |= 2;474		tmp &= ~SSB_IMCFGLO_REQTO;475		tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT;476		ssb_write32(pdev, SSB_IMCFGLO, tmp);477		ssb_commit_settings(bus);478	} else if (pdev->id.revision >= 11) {479		tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);480		tmp |= SSB_PCICORE_SBTOPCI_MRM;481		pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);482	}483}484 485static void ssb_pcicore_pcie_setup_workarounds(struct ssb_pcicore *pc)486{487	u32 tmp;488	u8 rev = pc->dev->id.revision;489 490	if (rev == 0 || rev == 1) {491		/* TLP Workaround register. */492		tmp = ssb_pcie_read(pc, 0x4);493		tmp |= 0x8;494		ssb_pcie_write(pc, 0x4, tmp);495	}496	if (rev == 1) {497		/* DLLP Link Control register. */498		tmp = ssb_pcie_read(pc, 0x100);499		tmp |= 0x40;500		ssb_pcie_write(pc, 0x100, tmp);501	}502 503	if (rev == 0) {504		const u8 serdes_rx_device = 0x1F;505 506		ssb_pcie_mdio_write(pc, serdes_rx_device,507					2 /* Timer */, 0x8128);508		ssb_pcie_mdio_write(pc, serdes_rx_device,509					6 /* CDR */, 0x0100);510		ssb_pcie_mdio_write(pc, serdes_rx_device,511					7 /* CDR BW */, 0x1466);512	} else if (rev == 3 || rev == 4 || rev == 5) {513		/* TODO: DLLP Power Management Threshold */514		ssb_pcicore_serdes_workaround(pc);515		/* TODO: ASPM */516	} else if (rev == 7) {517		/* TODO: No PLL down */518	}519 520	if (rev >= 6) {521		/* Miscellaneous Configuration Fixup */522		tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(5));523		if (!(tmp & 0x8000))524			pcicore_write16(pc, SSB_PCICORE_SPROM(5),525					tmp | 0x8000);526	}527}528 529/**************************************************530 * Generic and Clientmode operation code.531 **************************************************/532 533static void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc)534{535	struct ssb_device *pdev = pc->dev;536	struct ssb_bus *bus = pdev->bus;537 538	if (bus->bustype == SSB_BUSTYPE_PCI)539		ssb_pcicore_fix_sprom_core_index(pc);540 541	/* Disable PCI interrupts. */542	ssb_write32(pdev, SSB_INTVEC, 0);543 544	/* Additional PCIe always once-executed workarounds */545	if (pc->dev->id.coreid == SSB_DEV_PCIE) {546		ssb_pcicore_serdes_workaround(pc);547		/* TODO: ASPM */548		/* TODO: Clock Request Update */549	}550}551 552void ssb_pcicore_init(struct ssb_pcicore *pc)553{554	struct ssb_device *dev = pc->dev;555 556	if (!dev)557		return;558	if (!ssb_device_is_enabled(dev))559		ssb_device_enable(dev, 0);560 561#ifdef CONFIG_SSB_PCICORE_HOSTMODE562	pc->hostmode = pcicore_is_in_hostmode(pc);563	if (pc->hostmode)564		ssb_pcicore_init_hostmode(pc);565#endif /* CONFIG_SSB_PCICORE_HOSTMODE */566	if (!pc->hostmode)567		ssb_pcicore_init_clientmode(pc);568}569 570static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address)571{572	pcicore_write32(pc, 0x130, address);573	return pcicore_read32(pc, 0x134);574}575 576static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data)577{578	pcicore_write32(pc, 0x130, address);579	pcicore_write32(pc, 0x134, data);580}581 582static void ssb_pcie_mdio_set_phy(struct ssb_pcicore *pc, u8 phy)583{584	const u16 mdio_control = 0x128;585	const u16 mdio_data = 0x12C;586	u32 v;587	int i;588 589	v = (1 << 30); /* Start of Transaction */590	v |= (1 << 28); /* Write Transaction */591	v |= (1 << 17); /* Turnaround */592	v |= (0x1F << 18);593	v |= (phy << 4);594	pcicore_write32(pc, mdio_data, v);595 596	udelay(10);597	for (i = 0; i < 200; i++) {598		v = pcicore_read32(pc, mdio_control);599		if (v & 0x100 /* Trans complete */)600			break;601		msleep(1);602	}603}604 605static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address)606{607	const u16 mdio_control = 0x128;608	const u16 mdio_data = 0x12C;609	int max_retries = 10;610	u16 ret = 0;611	u32 v;612	int i;613 614	v = 0x80; /* Enable Preamble Sequence */615	v |= 0x2; /* MDIO Clock Divisor */616	pcicore_write32(pc, mdio_control, v);617 618	if (pc->dev->id.revision >= 10) {619		max_retries = 200;620		ssb_pcie_mdio_set_phy(pc, device);621	}622 623	v = (1 << 30); /* Start of Transaction */624	v |= (1 << 29); /* Read Transaction */625	v |= (1 << 17); /* Turnaround */626	if (pc->dev->id.revision < 10)627		v |= (u32)device << 22;628	v |= (u32)address << 18;629	pcicore_write32(pc, mdio_data, v);630	/* Wait for the device to complete the transaction */631	udelay(10);632	for (i = 0; i < max_retries; i++) {633		v = pcicore_read32(pc, mdio_control);634		if (v & 0x100 /* Trans complete */) {635			udelay(10);636			ret = pcicore_read32(pc, mdio_data);637			break;638		}639		msleep(1);640	}641	pcicore_write32(pc, mdio_control, 0);642	return ret;643}644 645static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,646				u8 address, u16 data)647{648	const u16 mdio_control = 0x128;649	const u16 mdio_data = 0x12C;650	int max_retries = 10;651	u32 v;652	int i;653 654	v = 0x80; /* Enable Preamble Sequence */655	v |= 0x2; /* MDIO Clock Divisor */656	pcicore_write32(pc, mdio_control, v);657 658	if (pc->dev->id.revision >= 10) {659		max_retries = 200;660		ssb_pcie_mdio_set_phy(pc, device);661	}662 663	v = (1 << 30); /* Start of Transaction */664	v |= (1 << 28); /* Write Transaction */665	v |= (1 << 17); /* Turnaround */666	if (pc->dev->id.revision < 10)667		v |= (u32)device << 22;668	v |= (u32)address << 18;669	v |= data;670	pcicore_write32(pc, mdio_data, v);671	/* Wait for the device to complete the transaction */672	udelay(10);673	for (i = 0; i < max_retries; i++) {674		v = pcicore_read32(pc, mdio_control);675		if (v & 0x100 /* Trans complete */)676			break;677		msleep(1);678	}679	pcicore_write32(pc, mdio_control, 0);680}681 682int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,683				   struct ssb_device *dev)684{685	struct ssb_device *pdev = pc->dev;686	struct ssb_bus *bus;687	int err = 0;688	u32 tmp;689 690	if (dev->bus->bustype != SSB_BUSTYPE_PCI) {691		/* This SSB device is not on a PCI host-bus. So the IRQs are692		 * not routed through the PCI core.693		 * So we must not enable routing through the PCI core.694		 */695		goto out;696	}697 698	if (!pdev)699		goto out;700	bus = pdev->bus;701 702	might_sleep_if(pdev->id.coreid != SSB_DEV_PCI);703 704	/* Enable interrupts for this device. */705	if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) {706		u32 coremask;707 708		/* Calculate the "coremask" for the device. */709		coremask = (1 << dev->core_index);710 711		WARN_ON(bus->bustype != SSB_BUSTYPE_PCI);712		err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);713		if (err)714			goto out;715		tmp |= coremask << 8;716		err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp);717		if (err)718			goto out;719	} else {720		u32 intvec;721 722		intvec = ssb_read32(pdev, SSB_INTVEC);723		tmp = ssb_read32(dev, SSB_TPSFLAG);724		tmp &= SSB_TPSFLAG_BPFLAG;725		intvec |= (1 << tmp);726		ssb_write32(pdev, SSB_INTVEC, intvec);727	}728 729	/* Setup PCIcore operation. */730	if (pc->setup_done)731		goto out;732	if (pdev->id.coreid == SSB_DEV_PCI) {733		ssb_pcicore_pci_setup_workarounds(pc);734	} else {735		WARN_ON(pdev->id.coreid != SSB_DEV_PCIE);736		ssb_pcicore_pcie_setup_workarounds(pc);737	}738	pc->setup_done = 1;739out:740	return err;741}742EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable);743