18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * SNI specific PCI support for RM200/RM300. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 1997 - 2000, 2003 Ralf Baechle <ralf@linux-mips.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <asm/sni.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci * It seems that on the RM200 only lower 3 bits of the 5 bit PCI device 178c2ecf20Sopenharmony_ci * address are decoded. We therefore manually have to reject attempts at 188c2ecf20Sopenharmony_ci * reading outside this range. Being on the paranoid side we only do this 198c2ecf20Sopenharmony_ci * test for bus 0 and hope forwarding and decoding work properly for any 208c2ecf20Sopenharmony_ci * subordinated busses. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * ASIC PCI only supports type 1 config cycles. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic int set_config_address(unsigned int busno, unsigned int devfn, int reg) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci if ((devfn > 255) || (reg > 255)) 278c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci if (busno == 0 && devfn >= PCI_DEVFN(8, 0)) 308c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci *(volatile u32 *)PCIMT_CONFIG_ADDRESS = 338c2ecf20Sopenharmony_ci ((busno & 0xff) << 16) | 348c2ecf20Sopenharmony_ci ((devfn & 0xff) << 8) | 358c2ecf20Sopenharmony_ci (reg & 0xfc); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int pcimt_read(struct pci_bus *bus, unsigned int devfn, int reg, 418c2ecf20Sopenharmony_ci int size, u32 * val) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int res; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci if ((res = set_config_address(bus->number, devfn, reg))) 468c2ecf20Sopenharmony_ci return res; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci switch (size) { 498c2ecf20Sopenharmony_ci case 1: 508c2ecf20Sopenharmony_ci *val = inb(PCIMT_CONFIG_DATA + (reg & 3)); 518c2ecf20Sopenharmony_ci break; 528c2ecf20Sopenharmony_ci case 2: 538c2ecf20Sopenharmony_ci *val = inw(PCIMT_CONFIG_DATA + (reg & 2)); 548c2ecf20Sopenharmony_ci break; 558c2ecf20Sopenharmony_ci case 4: 568c2ecf20Sopenharmony_ci *val = inl(PCIMT_CONFIG_DATA); 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int pcimt_write(struct pci_bus *bus, unsigned int devfn, int reg, 648c2ecf20Sopenharmony_ci int size, u32 val) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci int res; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if ((res = set_config_address(bus->number, devfn, reg))) 698c2ecf20Sopenharmony_ci return res; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci switch (size) { 728c2ecf20Sopenharmony_ci case 1: 738c2ecf20Sopenharmony_ci outb(val, PCIMT_CONFIG_DATA + (reg & 3)); 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case 2: 768c2ecf20Sopenharmony_ci outw(val, PCIMT_CONFIG_DATA + (reg & 2)); 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 4: 798c2ecf20Sopenharmony_ci outl(val, PCIMT_CONFIG_DATA); 808c2ecf20Sopenharmony_ci break; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistruct pci_ops sni_pcimt_ops = { 878c2ecf20Sopenharmony_ci .read = pcimt_read, 888c2ecf20Sopenharmony_ci .write = pcimt_write, 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int pcit_set_config_address(unsigned int busno, unsigned int devfn, int reg) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci if ((devfn > 255) || (reg > 255) || (busno > 255)) 948c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci outl((1 << 31) | ((busno & 0xff) << 16) | ((devfn & 0xff) << 8) | (reg & 0xfc), 0xcf8); 978c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int pcit_read(struct pci_bus *bus, unsigned int devfn, int reg, 1018c2ecf20Sopenharmony_ci int size, u32 * val) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int res; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* 1068c2ecf20Sopenharmony_ci * on bus 0 we need to check, whether there is a device answering 1078c2ecf20Sopenharmony_ci * for the devfn by doing a config write and checking the result. If 1088c2ecf20Sopenharmony_ci * we don't do it, we will get a data bus error 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci if (bus->number == 0) { 1118c2ecf20Sopenharmony_ci pcit_set_config_address(0, 0, 0x68); 1128c2ecf20Sopenharmony_ci outl(inl(0xcfc) | 0xc0000000, 0xcfc); 1138c2ecf20Sopenharmony_ci if ((res = pcit_set_config_address(0, devfn, 0))) 1148c2ecf20Sopenharmony_ci return res; 1158c2ecf20Sopenharmony_ci outl(0xffffffff, 0xcfc); 1168c2ecf20Sopenharmony_ci pcit_set_config_address(0, 0, 0x68); 1178c2ecf20Sopenharmony_ci if (inl(0xcfc) & 0x100000) 1188c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci if ((res = pcit_set_config_address(bus->number, devfn, reg))) 1218c2ecf20Sopenharmony_ci return res; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci switch (size) { 1248c2ecf20Sopenharmony_ci case 1: 1258c2ecf20Sopenharmony_ci *val = inb(PCIMT_CONFIG_DATA + (reg & 3)); 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case 2: 1288c2ecf20Sopenharmony_ci *val = inw(PCIMT_CONFIG_DATA + (reg & 2)); 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 4: 1318c2ecf20Sopenharmony_ci *val = inl(PCIMT_CONFIG_DATA); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int pcit_write(struct pci_bus *bus, unsigned int devfn, int reg, 1388c2ecf20Sopenharmony_ci int size, u32 val) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci int res; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if ((res = pcit_set_config_address(bus->number, devfn, reg))) 1438c2ecf20Sopenharmony_ci return res; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci switch (size) { 1468c2ecf20Sopenharmony_ci case 1: 1478c2ecf20Sopenharmony_ci outb(val, PCIMT_CONFIG_DATA + (reg & 3)); 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 2: 1508c2ecf20Sopenharmony_ci outw(val, PCIMT_CONFIG_DATA + (reg & 2)); 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci case 4: 1538c2ecf20Sopenharmony_ci outl(val, PCIMT_CONFIG_DATA); 1548c2ecf20Sopenharmony_ci break; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistruct pci_ops sni_pcit_ops = { 1628c2ecf20Sopenharmony_ci .read = pcit_read, 1638c2ecf20Sopenharmony_ci .write = pcit_write, 1648c2ecf20Sopenharmony_ci}; 165