18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000, 2004  MIPS Technologies, Inc.
48c2ecf20Sopenharmony_ci *	All rights reserved.
58c2ecf20Sopenharmony_ci *	Authors: Carsten Langgaard <carstenl@mips.com>
68c2ecf20Sopenharmony_ci *		 Maciej W. Rozycki <macro@mips.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * MIPS boards specific PCI support.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/pci.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <asm/mips-boards/bonito64.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define PCI_ACCESS_READ	 0
178c2ecf20Sopenharmony_ci#define PCI_ACCESS_WRITE 1
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define CFG_SPACE_REG(offset) (void *)CKSEG1ADDR(_pcictrl_bonito_pcicfg + (offset))
208c2ecf20Sopenharmony_ci#define ID_SEL_BEGIN 10
218c2ecf20Sopenharmony_ci#define MAX_DEV_NUM (31 - ID_SEL_BEGIN)
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int bonito64_pcibios_config_access(unsigned char access_type,
258c2ecf20Sopenharmony_ci				      struct pci_bus *bus,
268c2ecf20Sopenharmony_ci				      unsigned int devfn, int where,
278c2ecf20Sopenharmony_ci				      u32 * data)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	u32 busnum = bus->number;
308c2ecf20Sopenharmony_ci	u32 addr, type;
318c2ecf20Sopenharmony_ci	u32 dummy;
328c2ecf20Sopenharmony_ci	void *addrp;
338c2ecf20Sopenharmony_ci	int device = PCI_SLOT(devfn);
348c2ecf20Sopenharmony_ci	int function = PCI_FUNC(devfn);
358c2ecf20Sopenharmony_ci	int reg = where & ~3;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (busnum == 0) {
388c2ecf20Sopenharmony_ci		/* Type 0 configuration for onboard PCI bus */
398c2ecf20Sopenharmony_ci		if (device > MAX_DEV_NUM)
408c2ecf20Sopenharmony_ci			return -1;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		addr = (1 << (device + ID_SEL_BEGIN)) | (function << 8) | reg;
438c2ecf20Sopenharmony_ci		type = 0;
448c2ecf20Sopenharmony_ci	} else {
458c2ecf20Sopenharmony_ci		/* Type 1 configuration for offboard PCI bus */
468c2ecf20Sopenharmony_ci		addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
478c2ecf20Sopenharmony_ci		type = 0x10000;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	/* Clear aborts */
518c2ecf20Sopenharmony_ci	BONITO_PCICMD |= BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	BONITO_PCIMAP_CFG = (addr >> 16) | type;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* Flush Bonito register block */
568c2ecf20Sopenharmony_ci	dummy = BONITO_PCIMAP_CFG;
578c2ecf20Sopenharmony_ci	mmiowb();
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	addrp = CFG_SPACE_REG(addr & 0xffff);
608c2ecf20Sopenharmony_ci	if (access_type == PCI_ACCESS_WRITE) {
618c2ecf20Sopenharmony_ci		writel(cpu_to_le32(*data), addrp);
628c2ecf20Sopenharmony_ci		/* Wait till done */
638c2ecf20Sopenharmony_ci		while (BONITO_PCIMSTAT & 0xF);
648c2ecf20Sopenharmony_ci	} else {
658c2ecf20Sopenharmony_ci		*data = le32_to_cpu(readl(addrp));
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* Detect Master/Target abort */
698c2ecf20Sopenharmony_ci	if (BONITO_PCICMD & (BONITO_PCICMD_MABORT_CLR |
708c2ecf20Sopenharmony_ci			     BONITO_PCICMD_MTABORT_CLR)) {
718c2ecf20Sopenharmony_ci		/* Error occurred */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		/* Clear bits */
748c2ecf20Sopenharmony_ci		BONITO_PCICMD |= (BONITO_PCICMD_MABORT_CLR |
758c2ecf20Sopenharmony_ci				  BONITO_PCICMD_MTABORT_CLR);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci		return -1;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * We can't address 8 and 16 bit words directly.  Instead we have to
878c2ecf20Sopenharmony_ci * read/write a 32bit word and mask/modify the data we actually want.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic int bonito64_pcibios_read(struct pci_bus *bus, unsigned int devfn,
908c2ecf20Sopenharmony_ci			     int where, int size, u32 * val)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	u32 data = 0;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if ((size == 2) && (where & 1))
958c2ecf20Sopenharmony_ci		return PCIBIOS_BAD_REGISTER_NUMBER;
968c2ecf20Sopenharmony_ci	else if ((size == 4) && (where & 3))
978c2ecf20Sopenharmony_ci		return PCIBIOS_BAD_REGISTER_NUMBER;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (bonito64_pcibios_config_access(PCI_ACCESS_READ, bus, devfn, where,
1008c2ecf20Sopenharmony_ci				       &data))
1018c2ecf20Sopenharmony_ci		return -1;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (size == 1)
1048c2ecf20Sopenharmony_ci		*val = (data >> ((where & 3) << 3)) & 0xff;
1058c2ecf20Sopenharmony_ci	else if (size == 2)
1068c2ecf20Sopenharmony_ci		*val = (data >> ((where & 3) << 3)) & 0xffff;
1078c2ecf20Sopenharmony_ci	else
1088c2ecf20Sopenharmony_ci		*val = data;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int bonito64_pcibios_write(struct pci_bus *bus, unsigned int devfn,
1148c2ecf20Sopenharmony_ci			      int where, int size, u32 val)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u32 data = 0;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if ((size == 2) && (where & 1))
1198c2ecf20Sopenharmony_ci		return PCIBIOS_BAD_REGISTER_NUMBER;
1208c2ecf20Sopenharmony_ci	else if ((size == 4) && (where & 3))
1218c2ecf20Sopenharmony_ci		return PCIBIOS_BAD_REGISTER_NUMBER;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (size == 4)
1248c2ecf20Sopenharmony_ci		data = val;
1258c2ecf20Sopenharmony_ci	else {
1268c2ecf20Sopenharmony_ci		if (bonito64_pcibios_config_access(PCI_ACCESS_READ, bus, devfn,
1278c2ecf20Sopenharmony_ci					       where, &data))
1288c2ecf20Sopenharmony_ci			return -1;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		if (size == 1)
1318c2ecf20Sopenharmony_ci			data = (data & ~(0xff << ((where & 3) << 3))) |
1328c2ecf20Sopenharmony_ci				(val << ((where & 3) << 3));
1338c2ecf20Sopenharmony_ci		else if (size == 2)
1348c2ecf20Sopenharmony_ci			data = (data & ~(0xffff << ((where & 3) << 3))) |
1358c2ecf20Sopenharmony_ci				(val << ((where & 3) << 3));
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (bonito64_pcibios_config_access(PCI_ACCESS_WRITE, bus, devfn, where,
1398c2ecf20Sopenharmony_ci				       &data))
1408c2ecf20Sopenharmony_ci		return -1;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistruct pci_ops bonito64_pci_ops = {
1468c2ecf20Sopenharmony_ci	.read = bonito64_pcibios_read,
1478c2ecf20Sopenharmony_ci	.write = bonito64_pcibios_write
1488c2ecf20Sopenharmony_ci};
149