fcfa963e424c9d86e0afa4052d5b209b6b3b04f9
[coreboot.git] / src / southbridge / sis / sis966 / sis966.c
1 /*
2  * This file is part of the LinuxBIOS project.
3  *
4  * Copyright (C) 2004 Tyan Computer
5  * Written by Yinghai Lu <yhlu@tyan.com> for Tyan Computer.
6  * Copyright (C) 2006,2007 AMD
7  * Written by Yinghai Lu <yinghai.lu@amd.com> for AMD.
8  * Copyright (C) 2007 Silicon Integrated Systems Corp. (SiS)
9  * Written by Morgan Tsai <my_tsai@sis.com> for SiS.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
24  */
25
26 #include <console/console.h>
27
28 #include <arch/io.h>
29
30 #include <device/device.h>
31 #include <device/pci.h>
32 #include <device/pci_ids.h>
33 #include <device/pci_ops.h>
34 #include "sis966.h"
35
36 static uint32_t final_reg;
37
38 static device_t find_lpc_dev( device_t dev,  unsigned devfn)
39 {
40
41         device_t lpc_dev;
42
43         lpc_dev = dev_find_slot(dev->bus->secondary, devfn);
44
45         if ( !lpc_dev ) return lpc_dev;
46
47 if ((lpc_dev->vendor != PCI_VENDOR_ID_SIS) || (
48                 (lpc_dev->device != PCI_DEVICE_ID_SIS_SIS966_LPC)
49                 ) ) {
50                         uint32_t id;
51                         id = pci_read_config32(lpc_dev, PCI_VENDOR_ID);
52                         if ( (id < (PCI_VENDOR_ID_SIS | (PCI_DEVICE_ID_SIS_SIS966_LPC << 16)))
53                                 ) {
54                                 lpc_dev = 0;
55                         }
56         }
57
58         return lpc_dev;
59 }
60
61 void sis966_enable(device_t dev)
62 {
63         device_t lpc_dev = 0;
64         device_t sm_dev = 0;
65         uint16_t index = 0;
66         uint16_t index2 = 0;
67         uint32_t reg_old, reg;
68         uint8_t byte;
69         uint16_t deviceid;
70         uint16_t vendorid;
71         uint16_t devfn;
72
73         struct southbridge_sis_sis966_config *conf;
74         conf = dev->chip_info;
75         int i;
76
77         if(dev->device==0x0000) {
78                 vendorid = pci_read_config32(dev, PCI_VENDOR_ID);
79                 deviceid = (vendorid>>16) & 0xffff;
80 //              vendorid &= 0xffff;
81         } else {
82 //              vendorid = dev->vendor;
83                 deviceid = dev->device;
84         }
85
86         devfn = (dev->path.u.pci.devfn) & ~7;
87         switch(deviceid) {
88                 case PCI_DEVICE_ID_SIS_SIS966_USB:
89                         devfn -= (1<<3);
90                         index = 8;
91                         break;
92                 case PCI_DEVICE_ID_SIS_SIS966_USB2:
93                         devfn -= (1<<3);
94                         index = 20;
95                         break;
96                 case PCI_DEVICE_ID_SIS_SIS966_NIC:
97                         devfn -= (7<<3);
98                         index = 10;
99                         for(i=0;i<2;i++) {
100                                 lpc_dev = find_lpc_dev(dev, devfn - (i<<3));
101                                 if(!lpc_dev) continue;
102                                 index -= i;
103                                 devfn -= (i<<3);
104                                 break;
105                         }
106                         break;
107                 case PCI_DEVICE_ID_SIS_SIS966_HD_AUDIO:
108                         devfn -= (5<<3);
109                         index = 11;
110                         break;
111                 case PCI_DEVICE_ID_SIS_SIS966_IDE:
112                         devfn -= (3<<3);
113                         index = 14;
114                         break;
115                 case PCI_DEVICE_ID_SIS_SIS966_SATA:
116                         devfn -= (4<<3);
117                         index = 22;
118                         i = (dev->path.u.pci.devfn) & 7;
119                         if(i>0) {
120                                 index -= (i+3);
121                         }
122                         break;
123                 case PCI_DEVICE_ID_SIS_SIS966_PCIE:
124                         devfn -= (0x9<<3);  // to LPC
125                         index2 = 9;
126                         break;
127                 default:
128                         index = 0;
129         }
130
131         if(!lpc_dev)
132                 lpc_dev = find_lpc_dev(dev, devfn);
133
134         if ( !lpc_dev ) return;
135
136         if(index2!=0) {
137                 sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
138                 if(!sm_dev) return;
139
140                 if ( sm_dev ) {
141                         reg_old = reg =  pci_read_config32(sm_dev, 0xe4);
142
143                         if (!dev->enabled) { //disable it
144                                 reg |= (1<<index2);
145                         }
146
147                         if (reg != reg_old) {
148                                 pci_write_config32(sm_dev, 0xe4, reg);
149                         }
150                 }
151
152                 index2 = 0;
153                 return;
154         }
155
156
157         if ( index == 0) {  // for LPC
158
159                 // expose ioapic base
160                 byte = pci_read_config8(lpc_dev, 0x74);
161                 byte |= ((1<<1)); // expose the BAR
162                 pci_write_config8(dev, 0x74, byte);
163
164                 // expose trap base
165                 byte = pci_read_config8(lpc_dev, 0xdd);
166                 byte |= ((1<<0)|(1<<3)); // expose the BAR and enable write
167                 pci_write_config8(dev, 0xdd, byte);
168                 return;
169
170         }
171
172         if( index == 16) {
173                 sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
174                 if(!sm_dev) return;
175
176                 final_reg = pci_read_config32(sm_dev, 0xe8);
177                 final_reg &= ~0x0057cf00;
178                 pci_write_config32(sm_dev, 0xe8, final_reg); //enable all at first
179         }
180
181         if (!dev->enabled) {
182                 final_reg |= (1 << index);// disable it
183                 //The reason for using final_reg, if diable func 1, the func 2 will be func 1 so We need disable them one time.
184         }
185
186         if(index == 9 ) { //NIC1 is the final, We need update final reg to 0xe8
187                 sm_dev = dev_find_slot(dev->bus->secondary, devfn + 1);
188                 if(!sm_dev) return;
189                 reg_old = pci_read_config32(sm_dev, 0xe8);
190                 if (final_reg != reg_old) {
191                         pci_write_config32(sm_dev, 0xe8, final_reg);
192                 }
193
194         }
195 }
196
197 struct chip_operations southbridge_sis_sis966_ops = {
198         CHIP_NAME("SiS SiS966 Southbridge")
199         .enable_dev     = sis966_enable,
200 };