f921ed85f3aeb6387f3b0f5a036a204d0427f915
[coreboot.git] / src / superio / smsc / lpc47n227 / superio.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * Copyright (C) 2005 Digital Design Corporation
5  * Copyright (C) 2008-2009 coresystems GmbH
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  */
20
21 /* RAM-based driver for SMSC LPC47N227 Super I/O chip. */
22
23 #include <arch/io.h>
24 #include <device/device.h>
25 #include <device/pnp.h>
26 #include <console/console.h>
27 #include <device/smbus.h>
28 #include <string.h>
29 #include <bitops.h>
30 #include <uart8250.h>
31 #include <assert.h>
32 #include <stdlib.h>
33 #include "chip.h"
34 #include "lpc47n227.h"
35
36 // Forward declarations
37 static void enable_dev(device_t dev);
38 void lpc47n227_pnp_set_resources(device_t dev);
39 void lpc47n227_pnp_enable_resources(device_t dev);
40 void lpc47n227_pnp_enable(device_t dev);
41 static void lpc47n227_init(device_t dev);
42
43 static void lpc47n227_pnp_set_resource(device_t dev, struct resource *resource);
44 void lpc47n227_pnp_set_iobase(device_t dev, unsigned iobase);
45 void lpc47n227_pnp_set_drq(device_t dev, unsigned drq);
46 void lpc47n227_pnp_set_irq(device_t dev, unsigned irq);
47 void lpc47n227_pnp_set_enable(device_t dev, int enable);
48
49 static void pnp_enter_conf_state(device_t dev);
50 static void pnp_exit_conf_state(device_t dev);
51
52 struct chip_operations superio_smsc_lpc47n227_ops = {
53         CHIP_NAME("SMSC LPC47N227 Super I/O")
54             .enable_dev = enable_dev,
55 };
56
57 static struct device_operations ops = {
58         .read_resources = pnp_read_resources,
59         .set_resources = lpc47n227_pnp_set_resources,
60         .enable_resources = lpc47n227_pnp_enable_resources,
61         .enable = lpc47n227_pnp_enable,
62         .init = lpc47n227_init,
63 };
64
65 static struct pnp_info pnp_dev_info[] = {
66         {&ops, LPC47N227_PP, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0, {0x07f8, 0},},
67         {&ops, LPC47N227_SP1, PNP_IO0 | PNP_IRQ0, {0x7f8, 0},},
68         {&ops, LPC47N227_SP2, PNP_IO0 | PNP_IRQ0, {0x7f8, 0},},
69         {&ops, LPC47N227_KBDC, PNP_IO0 | PNP_IO1 | PNP_IRQ0, {0x7f8, 0},
70          {0x7f8, 0x4},}
71 };
72
73 /**********************************************************************************/
74 /*                             PUBLIC INTERFACE                                   */
75 /**********************************************************************************/
76
77 //----------------------------------------------------------------------------------
78 // Function:            enable_dev
79 // Parameters:          dev - pointer to structure describing a Super I/O device
80 // Return Value:        None
81 // Description:         Create device structures and allocate resources to devices
82 //                      specified in the pnp_dev_info array (above).
83 //
84 static void enable_dev(device_t dev)
85 {
86         pnp_enable_devices(dev, &pnp_ops,
87                            ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
88 }
89
90 //----------------------------------------------------------------------------------
91 // Function:            lpc47n227_pnp_set_resources
92 // Parameters:          dev - pointer to structure describing a Super I/O device
93 // Return Value:        None
94 // Description:         Configure the specified Super I/O device with the resources
95 //                      (I/O space, etc.) that have been allocate for it.
96 //
97 void lpc47n227_pnp_set_resources(device_t dev)
98 {
99         struct resource *res;
100
101         pnp_enter_conf_state(dev);
102
103         // NOTE: Cannot use pnp_set_resources() here because it assumes chip
104         // support for logical devices, which the LPC47N227 doesn't have
105         for (res = dev->resource_list; res; res = res->next)
106                 lpc47n227_pnp_set_resource(dev, res);
107
108         pnp_exit_conf_state(dev);
109 }
110
111 void lpc47n227_pnp_enable_resources(device_t dev)
112 {
113         pnp_enter_conf_state(dev);
114
115         // NOTE: Cannot use pnp_enable_resources() here because it assumes chip
116         // support for logical devices, which the LPC47N227 doesn't have
117         lpc47n227_pnp_set_enable(dev, 1);
118
119         pnp_exit_conf_state(dev);
120 }
121
122 void lpc47n227_pnp_enable(device_t dev)
123 {
124         pnp_enter_conf_state(dev);
125
126         // NOTE: Cannot use pnp_set_enable() here because it assumes chip
127         // support for logical devices, which the LPC47N227 doesn't have
128
129         if (dev->enabled) {
130                 lpc47n227_pnp_set_enable(dev, 1);
131         } else {
132                 lpc47n227_pnp_set_enable(dev, 0);
133         }
134
135         pnp_exit_conf_state(dev);
136 }
137
138 //----------------------------------------------------------------------------------
139 // Function:            lpc47n227_init
140 // Parameters:          dev - pointer to structure describing a Super I/O device
141 // Return Value:        None
142 // Description:         Initialize the specified Super I/O device.
143 //                      Devices other than COM ports and keyboard controller are ignored.
144 //                      For COM ports, we configure the baud rate.
145 //
146 static void lpc47n227_init(device_t dev)
147 {
148         struct superio_smsc_lpc47n227_config *conf = dev->chip_info;
149         struct resource *res0;
150
151         if (!dev->enabled)
152                 return;
153
154         switch (dev->path.pnp.device) {
155         case LPC47N227_SP1:
156                 res0 = find_resource(dev, PNP_IDX_IO0);
157                 init_uart8250(res0->base, &conf->com1);
158                 break;
159
160         case LPC47N227_SP2:
161                 res0 = find_resource(dev, PNP_IDX_IO0);
162                 init_uart8250(res0->base, &conf->com2);
163                 break;
164
165         case LPC47N227_KBDC:
166                 printk(BIOS_DEBUG, "LPC47N227: Initializing keyboard.\n");
167                 pc_keyboard_init(&conf->keyboard);
168                 break;
169         }
170 }
171
172 /**********************************************************************************/
173 /*                              PRIVATE FUNCTIONS                                 */
174 /**********************************************************************************/
175
176 static void lpc47n227_pnp_set_resource(device_t dev, struct resource *resource)
177 {
178         if (!(resource->flags & IORESOURCE_ASSIGNED)) {
179                 printk(BIOS_ERR, "ERROR: %s %02lx not allocated\n",
180                            dev_path(dev), resource->index);
181                 return;
182         }
183
184         /* Now store the resource */
185         // NOTE: Cannot use pnp_set_XXX() here because they assume chip
186         // support for logical devices, which the LPC47N227 doesn't have
187
188         if (resource->flags & IORESOURCE_IO) {
189                 lpc47n227_pnp_set_iobase(dev, resource->base);
190         } else if (resource->flags & IORESOURCE_DRQ) {
191                 lpc47n227_pnp_set_drq(dev, resource->base);
192         } else if (resource->flags & IORESOURCE_IRQ) {
193                 lpc47n227_pnp_set_irq(dev, resource->base);
194         } else {
195                 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
196                            dev_path(dev), resource->index);
197                 return;
198         }
199         resource->flags |= IORESOURCE_STORED;
200
201         report_resource_stored(dev, resource, "");
202 }
203
204 void lpc47n227_pnp_set_iobase(device_t dev, unsigned iobase)
205 {
206         ASSERT(!(iobase & 0x3));
207
208         switch (dev->path.pnp.device) {
209         case LPC47N227_PP:
210                 pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);
211                 break;
212
213         case LPC47N227_SP1:
214                 pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);
215                 break;
216
217         case LPC47N227_SP2:
218                 pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);
219                 break;
220
221         case LPC47N227_KBDC:
222                 break;
223
224         default:
225                 BUG();
226                 break;
227         }
228 }
229
230 void lpc47n227_pnp_set_drq(device_t dev, unsigned drq)
231 {
232         if (dev->path.pnp.device == LPC47N227_PP) {
233                 const uint8_t PP_DMA_MASK = 0x0F;
234                 const uint8_t PP_DMA_SELECTION_REGISTER = 0x26;
235                 uint8_t current_config =
236                     pnp_read_config(dev, PP_DMA_SELECTION_REGISTER);
237                 uint8_t new_config;
238
239                 ASSERT(!(drq & ~PP_DMA_MASK));  // DRQ out of range??
240                 new_config = (current_config & ~PP_DMA_MASK) | drq;
241                 pnp_write_config(dev, PP_DMA_SELECTION_REGISTER, new_config);
242         } else {
243                 BUG();
244         }
245 }
246
247 void lpc47n227_pnp_set_irq(device_t dev, unsigned irq)
248 {
249         uint8_t irq_config_register = 0;
250         uint8_t irq_config_mask = 0;
251         uint8_t current_config;
252         uint8_t new_config;
253
254         switch (dev->path.pnp.device) {
255         case LPC47N227_PP:
256                 irq_config_register = 0x27;
257                 irq_config_mask = 0x0F;
258                 break;
259
260         case LPC47N227_SP1:
261                 irq_config_register = 0x28;
262                 irq_config_mask = 0xF0;
263                 irq <<= 4;
264                 break;
265
266         case LPC47N227_SP2:
267                 irq_config_register = 0x28;
268                 irq_config_mask = 0x0F;
269                 break;
270
271         case LPC47N227_KBDC:
272                 break;
273
274         default:
275                 BUG();
276                 return;
277         }
278
279         current_config = pnp_read_config(dev, irq_config_register);
280         new_config = (current_config & ~irq_config_mask) | irq;
281         pnp_write_config(dev, irq_config_register, new_config);
282 }
283
284 void lpc47n227_pnp_set_enable(device_t dev, int enable)
285 {
286         uint8_t power_register = 0;
287         uint8_t power_mask = 0;
288         uint8_t current_power;
289         uint8_t new_power;
290
291         switch (dev->path.pnp.device) {
292         case LPC47N227_PP:
293                 power_register = 0x01;
294                 power_mask = 0x04;
295                 break;
296
297         case LPC47N227_SP1:
298                 power_register = 0x02;
299                 power_mask = 0x08;
300                 break;
301
302         case LPC47N227_SP2:
303                 power_register = 0x02;
304                 power_mask = 0x80;
305                 break;
306
307         case LPC47N227_KBDC:
308                 break;
309
310         default:
311                 BUG();
312                 return;
313         }
314
315         current_power = pnp_read_config(dev, power_register);
316         new_power = current_power & ~power_mask;        // disable by default
317
318         if (enable) {
319                 struct resource *ioport_resource =
320                     find_resource(dev, PNP_IDX_IO0);
321                 lpc47n227_pnp_set_iobase(dev, ioport_resource->base);
322
323                 new_power |= power_mask;        // Enable
324
325         } else {
326                 lpc47n227_pnp_set_iobase(dev, 0);
327         }
328         pnp_write_config(dev, power_register, new_power);
329 }
330
331 //----------------------------------------------------------------------------------
332 // Function:            pnp_enter_conf_state
333 // Parameters:          dev - pointer to structure describing a Super I/O device
334 // Return Value:        None
335 // Description:         Enable access to the LPC47N227's configuration registers.
336 //
337 static void pnp_enter_conf_state(device_t dev)
338 {
339         outb(0x55, dev->path.pnp.port);
340 }
341
342 //----------------------------------------------------------------------------------
343 // Function:            pnp_exit_conf_state
344 // Parameters:          dev - pointer to structure describing a Super I/O device
345 // Return Value:        None
346 // Description:         Disable access to the LPC47N227's configuration registers.
347 //
348 static void pnp_exit_conf_state(device_t dev)
349 {
350         outb(0xaa, dev->path.pnp.port);
351 }
352