/* * This file is part of the coreboot project. * * Copyright (C) 2004 Tyan Computer * Written by Yinghai Lu for Tyan Computer. * Copyright (C) 2006,2007 AMD * Written by Yinghai Lu for AMD. * Copyright (C) 2007 Silicon Integrated Systems Corp. (SiS) * Written by Morgan Tsai for SiS. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "sis966.h" uint8_t SiS_SiS191_init[6][3]={ {0x04, 0xFF, 0x07}, {0x2C, 0xFF, 0x39}, {0x2D, 0xFF, 0x10}, {0x2E, 0xFF, 0x91}, {0x2F, 0xFF, 0x01}, {0x00, 0x00, 0x00} //End of table }; #define StatusReg 0x1 #define SMI_READ 0x0 #define SMI_REQUEST 0x10 #define TRUE 1 #define FALSE 0 uint16_t MacAddr[3]; void writeApcByte(int addr, uint8_t value) { outb(addr,0x78); outb(value,0x79); } uint8_t readApcByte(int addr) { uint8_t value; outb(addr,0x78); value=inb(0x79); return(value); } static void readApcMacAddr(void) { uint8_t i; // enable APC in south bridge sis966 D2F0 outl(0x80001048,0xcf8); outl((inl(0xcfc) & 0xfffffffd),0xcfc ); // enable IO78/79h for APC Index/Data printk_debug("MAC addr in APC = "); for(i = 0x9 ; i <=0xe ; i++) { printk_debug("%2.2x",readApcByte(i)); } printk_debug("\n"); /* Set APC Reload */ writeApcByte(0x7,readApcByte(0x7)&0xf7); writeApcByte(0x7,readApcByte(0x7)|0x0a); /* disable APC in south bridge */ outl(0x80001048,0xcf8); outl(inl(0xcfc)&0xffffffbf,0xcfc); } static void set_apc(struct device *dev) { uint16_t addr; uint16_t i; uint8_t bTmp; /* enable APC in south bridge sis966 D2F0 */ outl(0x80001048,0xcf8); outl((inl(0xcfc) & 0xfffffffd),0xcfc ); // enable IO78/79h for APC Index/Data for(i = 0 ; i <3; i++) { addr=0x9+2*i; writeApcByte(addr,(uint8_t)(MacAddr[i]&0xFF)); writeApcByte(addr+1L,(uint8_t)((MacAddr[i]>>8)&0xFF)); // printf("%x - ",readMacAddrByte(0x59+i)); } /* Set APC Reload */ writeApcByte(0x7,readApcByte(0x7)&0xf7); writeApcByte(0x7,readApcByte(0x7)|0x0a); /* disable APC in south bridge */ outl(0x80001048,0xcf8); outl(inl(0xcfc)&0xffffffbf,0xcfc); // CFG reg0x73 bit=1, tell driver MAC Address load to APC bTmp = pci_read_config8(dev, 0x73); bTmp|=0x1; pci_write_config8(dev, 0x73, bTmp); } //----------------------------------------------------------------------------- // Procedure: ReadEEprom // // Description: This routine serially reads one word out of the EEPROM. // // Arguments: // Reg - EEPROM word to read. // // Returns: // Contents of EEPROM word (Reg). //----------------------------------------------------------------------------- #define LoopNum 200 static unsigned long ReadEEprom( struct device *dev, uint32_t base, uint32_t Reg) { uint32_t data; uint32_t i; uint32_t ulValue; ulValue = (0x80 | (0x2 << 8) | (Reg << 10)); //BIT_7 write32(base+0x3c, ulValue); mdelay(10); for(i=0 ; i <= LoopNum; i++) { ulValue=read32(base+0x3c); if(!(ulValue & 0x0080)) //BIT_7 break; mdelay(100); } mdelay(50); if(i==LoopNum) data=0x10000; else{ ulValue=read32(base+0x3c); data = ((ulValue & 0xffff0000) >> 16); } return data; } static int phy_read(uint32_t base, unsigned phy_addr, unsigned phy_reg) { uint32_t ulValue; uint32_t Read_Cmd; uint16_t usData; Read_Cmd = ((phy_reg << 11) | (phy_addr << 6) | SMI_READ | SMI_REQUEST); // SmiMgtInterface Reg is the SMI management interface register(offset 44h) of MAC write32(base+0x44, Read_Cmd); // Polling SMI_REQ bit to be deasserted indicated read command completed do { // Wait 20 usec before checking status mdelay(20); ulValue = read32(base+0x44); } while((ulValue & SMI_REQUEST) != 0); //printk_debug("base %x cmd %lx ret val %lx\n", tmp,Read_Cmd,ulValue); usData=(ulValue>>16); return usData; } // Detect a valid PHY // If there exist a valid PHY then return TRUE, else return FALSE static int phy_detect(uint32_t base,uint16_t *PhyAddr) //BOOL PHY_Detect() { int bFoundPhy = FALSE; uint16_t usData; int PhyAddress = 0; // Scan all PHY address(0 ~ 31) to find a valid PHY for(PhyAddress = 0; PhyAddress < 32; PhyAddress++) { usData=phy_read(base,PhyAddress,StatusReg); // Status register is a PHY's register(offset 01h) // Found a valid PHY if((usData != 0x0) && (usData != 0xffff)) { bFoundPhy = TRUE; break; } } if(!bFoundPhy) { printk_debug("PHY not found !!!! \n"); } *PhyAddr=PhyAddress; return bFoundPhy; } static void nic_init(struct device *dev) { int val; uint16_t PhyAddr; uint32_t base; struct resource *res; print_debug("NIC_INIT:---------->\n"); //-------------- enable NIC (SiS19x) ------------------------- { uint8_t temp8; int i=0; while(SiS_SiS191_init[i][0] != 0) { temp8 = pci_read_config8(dev, SiS_SiS191_init[i][0]); temp8 &= SiS_SiS191_init[i][1]; temp8 |= SiS_SiS191_init[i][2]; pci_write_config8(dev, SiS_SiS191_init[i][0], temp8); i++; }; } //----------------------------------------------------------- { unsigned long i; unsigned long ulValue; res = find_resource(dev, 0x10); if(!res) { printk_debug("NIC Cannot find resource..\r\n"); return; } base = res->base; printk_debug("NIC base address %lx\n",base); if(!(val=phy_detect(base,&PhyAddr))) { printk_debug("PHY detect fail !!!!\r\n"); return; } ulValue=read32(base + 0x38L); // check EEPROM existing if((ulValue & 0x0002)) { // read MAC address from EEPROM at first // if that is valid we will use that printk_debug("EEPROM contents %x \n",ReadEEprom( dev, base, 0LL)); for(i=0;i<3;i++) { //status = smbus_read_byte(dev_eeprom, i); ulValue=ReadEEprom( dev, base, i+3L); if (ulValue ==0x10000) break; // error MacAddr[i] =ulValue & 0xFFFF; } }else{ // read MAC address from firmware printk_debug("EEPROM invalid!!\nReg 0x38h=%.8lx \n",ulValue); MacAddr[0]=read16(0xffffffc0); // mac address store at here MacAddr[1]=read16(0xffffffc2); MacAddr[2]=read16(0xffffffc4); } set_apc(dev); readApcMacAddr(); #if DEBUG_NIC { int i; print_debug("****** NIC PCI config ******"); print_debug("\n 03020100 07060504 0B0A0908 0F0E0D0C"); for(i=0;i<0xff;i+=4){ if((i%16)==0){ print_debug("\r\n"); print_debug_hex8(i); print_debug(": "); } print_debug_hex32(pci_read_config32(dev,i)); print_debug(" "); } print_debug("\r\n"); } #endif } print_debug("NIC_INIT:<----------\n"); return; } static void lpci_set_subsystem(device_t dev, unsigned vendor, unsigned device) { pci_write_config32(dev, 0x40, ((device & 0xffff) << 16) | (vendor & 0xffff)); } static struct pci_operations lops_pci = { .set_subsystem = lpci_set_subsystem, }; static struct device_operations nic_ops = { .read_resources = pci_dev_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = pci_dev_enable_resources, .init = nic_init, .scan_bus = 0, // .enable = sis966_enable, .ops_pci = &lops_pci, }; static const struct pci_driver nic_driver __pci_driver = { .ops = &nic_ops, .vendor = PCI_VENDOR_ID_SIS, .device = PCI_DEVICE_ID_SIS_SIS966_NIC, };