Updating FSF address in the code.
[coreboot.git] / src / superio / smsc / lpc47n217 / superio.c
1 /*\r
2  * $Header: /home/cvs/BIR/ca-cpu/freebios/src/superio/smsc/lpc47n217/superio.c,v 1.1.1.1 2005/07/11 15:28:51 smagnani Exp $\r
3  *\r
4  * superio.c: RAM-based driver for SMSC LPC47N217 Super I/O chip\r
5  *\r
6  * Based on LinuxBIOS code for SMSC 47B397:\r
7  * Copyright 2000  AG Electronics Ltd.\r
8  * Copyright 2003-2004 Linux Networx\r
9  * Copyright 2004 Tyan \r
10  *\r
11  * Copyright (C) 2005 Digital Design Corporation\r
12  *\r
13  * This program is free software; you can redistribute it and/or modify\r
14  * it under the terms of the GNU General Public License as published by\r
15  * the Free Software Foundation; either version 2 of the License, or\r
16  * (at your option) any later version.\r
17  *\r
18  * This program is distributed in the hope that it will be useful,\r
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
21  * GNU General Public License for more details.\r
22  *\r
23  * You should have received a copy of the GNU General Public License\r
24  * along with this program; if not, write to the Free Software\r
25  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
26  *\r
27  * $Log: superio.c,v $\r
28  * Revision 1.1.1.1  2005/07/11 15:28:51  smagnani\r
29  * Initial revision.\r
30  *\r
31  *\r
32  */\r
33 \r
34 #include <arch/io.h>\r
35 #include <device/device.h>\r
36 #include <device/pnp.h>\r
37 #include <console/console.h>\r
38 #include <device/smbus.h>\r
39 #include <string.h>\r
40 #include <bitops.h>\r
41 #include <uart8250.h>\r
42 #include <assert.h>\r
43 #include "chip.h"\r
44 #include "lpc47n217.h"\r
45 \r
46 // Forward declarations\r
47 static void enable_dev(device_t dev);\r
48 void lpc47n217_pnp_set_resources(device_t dev);\r
49 void lpc47n217_pnp_enable_resources(device_t dev);\r
50 void lpc47n217_pnp_enable(device_t dev);\r
51 static void lpc47n217_init(device_t dev);\r
52 \r
53 static void lpc47n217_pnp_set_resource(device_t dev, struct resource *resource);\r
54 void lpc47n217_pnp_set_iobase(device_t dev, unsigned iobase);\r
55 void lpc47n217_pnp_set_drq(device_t dev, unsigned drq);\r
56 void lpc47n217_pnp_set_irq(device_t dev, unsigned irq);\r
57 void lpc47n217_pnp_set_enable(device_t dev, int enable);\r
58 \r
59 static void pnp_enter_conf_state(device_t dev);\r
60 static void pnp_exit_conf_state(device_t dev);\r
61 \r
62 \r
63 struct chip_operations superio_smsc_lpc47n217_ops = {\r
64         CHIP_NAME("smsc lpc47n217")\r
65         .enable_dev = enable_dev,\r
66 };\r
67 \r
68 static struct device_operations ops = {\r
69         .read_resources   = pnp_read_resources,\r
70         .set_resources    = lpc47n217_pnp_set_resources,\r
71         .enable_resources = lpc47n217_pnp_enable_resources,\r
72         .enable           = lpc47n217_pnp_enable,\r
73         .init             = lpc47n217_init,\r
74 };\r
75 \r
76 static struct pnp_info pnp_dev_info[] = {\r
77         { &ops, LPC47N217_PP,   PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, { 0x07f8, 0}, },\r
78         { &ops, LPC47N217_SP1,  PNP_IO0 | PNP_IRQ0, { 0x7f8, 0 }, },\r
79         { &ops, LPC47N217_SP2,  PNP_IO0 | PNP_IRQ0, { 0x7f8, 0 }, }\r
80 };\r
81 \r
82 /**********************************************************************************/\r
83 /*                                                              PUBLIC INTERFACE                                                                  */\r
84 /**********************************************************************************/\r
85 \r
86 //----------------------------------------------------------------------------------\r
87 // Function:            enable_dev\r
88 // Parameters:          dev - pointer to structure describing a Super I/O device \r
89 // Return Value:        None\r
90 // Description:         Create device structures and allocate resources to devices \r
91 //                                      specified in the pnp_dev_info array (above).\r
92 //\r
93 static void enable_dev(device_t dev)\r
94 {\r
95         pnp_enable_devices(dev, &pnp_ops, \r
96                                            sizeof(pnp_dev_info)/sizeof(pnp_dev_info[0]), \r
97                                            pnp_dev_info);\r
98 }\r
99 \r
100 //----------------------------------------------------------------------------------\r
101 // Function:            lpc47n217_pnp_set_resources\r
102 // Parameters:          dev - pointer to structure describing a Super I/O device \r
103 // Return Value:        None\r
104 // Description:         Configure the specified Super I/O device with the resources\r
105 //                                      (I/O space, etc.) that have been allocate for it.\r
106 //\r
107 void lpc47n217_pnp_set_resources(device_t dev)\r
108 {\r
109         int i;\r
110         \r
111         pnp_enter_conf_state(dev);  \r
112 \r
113         // NOTE: Cannot use pnp_set_resources() here because it assumes chip\r
114         //               support for logical devices, which the LPC47N217 doesn't have\r
115         for(i = 0; i < dev->resources; i++)\r
116                 lpc47n217_pnp_set_resource(dev, &dev->resource[i]);\r
117 \r
118 //      dump_pnp_device(dev);\r
119                 \r
120         pnp_exit_conf_state(dev);  \r
121 }       \r
122 \r
123 void lpc47n217_pnp_enable_resources(device_t dev)\r
124 {       \r
125     pnp_enter_conf_state(dev);\r
126 \r
127         // NOTE: Cannot use pnp_enable_resources() here because it assumes chip\r
128         //               support for logical devices, which the LPC47N217 doesn't have\r
129     lpc47n217_pnp_set_enable(dev, 1);\r
130 \r
131     pnp_exit_conf_state(dev);\r
132 }\r
133 \r
134 void lpc47n217_pnp_enable(device_t dev)\r
135 {\r
136         pnp_enter_conf_state(dev);   \r
137 \r
138         // NOTE: Cannot use pnp_set_enable() here because it assumes chip\r
139         //               support for logical devices, which the LPC47N217 doesn't have\r
140 \r
141         if(dev->enabled) {\r
142                 lpc47n217_pnp_set_enable(dev, 1);\r
143         }\r
144         else {\r
145                 lpc47n217_pnp_set_enable(dev, 0);\r
146         }\r
147 \r
148         pnp_exit_conf_state(dev);  \r
149 }\r
150 \r
151 //----------------------------------------------------------------------------------\r
152 // Function:            lpc47n217_init\r
153 // Parameters:          dev - pointer to structure describing a Super I/O device \r
154 // Return Value:        None\r
155 // Description:         Initialize the specified Super I/O device.\r
156 //                                      Devices other than COM ports are ignored.\r
157 //                                      For COM ports, we configure the baud rate. \r
158 //\r
159 static void lpc47n217_init(device_t dev)\r
160 {\r
161         struct superio_smsc_lpc47n217_config* conf = dev->chip_info;\r
162         struct resource *res0;\r
163 \r
164         if (!dev->enabled)\r
165                 return;\r
166 \r
167         switch(dev->path.u.pnp.device) {\r
168         case LPC47N217_SP1: \r
169                 res0 = find_resource(dev, PNP_IDX_IO0);\r
170                 init_uart8250(res0->base, &conf->com1);\r
171                 break;\r
172 \r
173         case LPC47N217_SP2:\r
174                 res0 = find_resource(dev, PNP_IDX_IO0);\r
175                 init_uart8250(res0->base, &conf->com2);\r
176                 break;\r
177         }\r
178 }\r
179 \r
180 \r
181 /**********************************************************************************/\r
182 /*                                                              PRIVATE FUNCTIONS                                                             */\r
183 /**********************************************************************************/\r
184 \r
185 static void lpc47n217_pnp_set_resource(device_t dev, struct resource *resource)\r
186 {\r
187         if (!(resource->flags & IORESOURCE_ASSIGNED)) {\r
188                 printk_err("ERROR: %s %02x not allocated\n",\r
189                         dev_path(dev), resource->index);\r
190                 return;\r
191         }\r
192 \r
193         /* Now store the resource */\r
194         // NOTE: Cannot use pnp_set_XXX() here because they assume chip\r
195         //               support for logical devices, which the LPC47N217 doesn't have\r
196 \r
197         if (resource->flags & IORESOURCE_IO) {\r
198                 lpc47n217_pnp_set_iobase(dev, resource->base);\r
199         }\r
200         else if (resource->flags & IORESOURCE_DRQ) {\r
201                 lpc47n217_pnp_set_drq(dev, resource->base);\r
202         }\r
203         else if (resource->flags  & IORESOURCE_IRQ) {\r
204                 lpc47n217_pnp_set_irq(dev, resource->base);\r
205         }\r
206         else {\r
207                 printk_err("ERROR: %s %02x unknown resource type\n",\r
208                         dev_path(dev), resource->index);\r
209                 return;\r
210         }\r
211         resource->flags |= IORESOURCE_STORED;\r
212 \r
213         report_resource_stored(dev, resource, "");\r
214 }\r
215 \r
216 void lpc47n217_pnp_set_iobase(device_t dev, unsigned iobase)\r
217 {\r
218         ASSERT(!(iobase & 0x3));\r
219         \r
220         switch(dev->path.u.pnp.device) {\r
221         case LPC47N217_PP: \r
222                 pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);\r
223                 break;\r
224                 \r
225         case LPC47N217_SP1: \r
226                 pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);\r
227                 break;\r
228                 \r
229         case LPC47N217_SP2:\r
230                 pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);\r
231                 break;\r
232                 \r
233         default:\r
234                 BUG();\r
235                 break;\r
236         }\r
237 }\r
238 \r
239 void lpc47n217_pnp_set_drq(device_t dev, unsigned drq)\r
240 {\r
241         if (dev->path.u.pnp.device == LPC47N217_PP) {\r
242                 const uint8_t PP_DMA_MASK = 0x0F;\r
243                 const uint8_t PP_DMA_SELECTION_REGISTER = 0x26;\r
244                 uint8_t current_config = pnp_read_config(dev, PP_DMA_SELECTION_REGISTER);\r
245                 uint8_t new_config;\r
246 \r
247                 ASSERT(!(drq & ~PP_DMA_MASK));          // DRQ out of range??           \r
248                 new_config = (current_config & ~PP_DMA_MASK) | drq;\r
249                 pnp_write_config(dev, PP_DMA_SELECTION_REGISTER, new_config);\r
250         } else {\r
251                 BUG();\r
252         }\r
253 }\r
254 \r
255 void lpc47n217_pnp_set_irq(device_t dev, unsigned irq)\r
256 {\r
257         uint8_t irq_config_register = 0;\r
258         uint8_t irq_config_mask = 0;\r
259         uint8_t current_config;\r
260         uint8_t new_config;\r
261         \r
262         switch(dev->path.u.pnp.device) {\r
263         case LPC47N217_PP: \r
264                 irq_config_register = 0x27;\r
265                 irq_config_mask = 0x0F;\r
266                 break;\r
267                 \r
268         case LPC47N217_SP1: \r
269                 irq_config_register = 0x28;\r
270                 irq_config_mask = 0xF0;\r
271                 irq <<= 4;\r
272                 break;\r
273                 \r
274         case LPC47N217_SP2:\r
275                 irq_config_register = 0x28;\r
276                 irq_config_mask = 0x0F;\r
277                 break;\r
278                 \r
279         default:\r
280                 BUG();\r
281                 return;\r
282         }\r
283 \r
284         ASSERT(!(irq & ~irq_config_mask));              // IRQ out of range??\r
285         \r
286         current_config = pnp_read_config(dev, irq_config_register);\r
287         new_config = (current_config & ~irq_config_mask) | irq;\r
288         pnp_write_config(dev, irq_config_register, new_config);\r
289 }\r
290 \r
291 void lpc47n217_pnp_set_enable(device_t dev, int enable)\r
292 {\r
293         uint8_t power_register = 0;\r
294         uint8_t power_mask = 0;\r
295         uint8_t current_power;\r
296         uint8_t new_power;\r
297         \r
298         switch(dev->path.u.pnp.device) {\r
299         case LPC47N217_PP: \r
300                 power_register = 0x01;\r
301                 power_mask = 0x04;\r
302                 break;\r
303                 \r
304         case LPC47N217_SP1: \r
305                 power_register = 0x02;\r
306                 power_mask = 0x08;\r
307                 break;\r
308                 \r
309         case LPC47N217_SP2:\r
310                 power_register = 0x02;\r
311                 power_mask = 0x80;\r
312                 break;\r
313                 \r
314         default:\r
315                 BUG();\r
316                 return;\r
317         }\r
318 \r
319         current_power = pnp_read_config(dev, power_register);\r
320         new_power = current_power & ~power_mask;                // disable by default\r
321 \r
322         if (enable) {\r
323                 struct resource* ioport_resource = find_resource(dev, PNP_IDX_IO0);\r
324                 lpc47n217_pnp_set_iobase(dev, ioport_resource->base);\r
325                 \r
326                 new_power |= power_mask;                // Enable\r
327                 \r
328     } else {\r
329                 lpc47n217_pnp_set_iobase(dev, 0);\r
330         }\r
331         pnp_write_config(dev, power_register, new_power);\r
332 }\r
333 \r
334 \r
335 //----------------------------------------------------------------------------------\r
336 // Function:            pnp_enter_conf_state\r
337 // Parameters:          dev - pointer to structure describing a Super I/O device \r
338 // Return Value:        None\r
339 // Description:         Enable access to the LPC47N217's configuration registers.\r
340 //\r
341 static void pnp_enter_conf_state(device_t dev) \r
342 {\r
343         outb(0x55, dev->path.u.pnp.port);\r
344 }\r
345 \r
346 //----------------------------------------------------------------------------------\r
347 // Function:            pnp_exit_conf_state\r
348 // Parameters:          dev - pointer to structure describing a Super I/O device \r
349 // Return Value:        None\r
350 // Description:         Disable access to the LPC47N217's configuration registers.\r
351 //\r
352 static void pnp_exit_conf_state(device_t dev) \r
353 {\r
354     outb(0xaa, dev->path.u.pnp.port);\r
355 }\r
356 \r
357 #if 0\r
358 //----------------------------------------------------------------------------------\r
359 // Function:            dump_pnp_device\r
360 // Parameters:          dev - pointer to structure describing a Super I/O device \r
361 // Return Value:        None\r
362 // Description:         Print the values of all of the LPC47N217's configuration registers.\r
363 //                                      NOTE: The LPC47N217 must be in configuration mode when this\r
364 //                                                function is called.\r
365 //\r
366 static void dump_pnp_device(device_t dev)\r
367 {\r
368     int register_index;\r
369     print_debug("\r\n");\r
370 \r
371     for(register_index = 0; register_index <= LPC47N217_MAX_CONFIG_REGISTER; register_index++) {\r
372         uint8_t register_value;\r
373 \r
374         if ((register_index & 0x0f) == 0) {\r
375                 print_debug_hex8(register_index);\r
376                 print_debug_char(':');\r
377         }\r
378 \r
379                 // Skip over 'register' that would cause exit from configuration mode\r
380             if (register_index == 0xaa)\r
381                         register_value = 0xaa;\r
382                 else\r
383                 register_value = pnp_read_config(dev, register_index);\r
384 \r
385         print_debug_char(' ');\r
386         print_debug_hex8(register_value);\r
387         if ((register_index & 0x0f) == 0x0f) {\r
388                 print_debug("\r\n");\r
389         }\r
390     }\r
391 \r
392         print_debug("\r\n");\r
393 }\r
394 #endif\r