Convert some comments to proper Doxygen syntax.
[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  * Create device structures and allocate resources to devices specified in the
75  * pnp_dev_info array (above).
76  *
77  * @param dev Pointer to structure describing a Super I/O device.
78  */
79 static void enable_dev(device_t dev)
80 {
81         pnp_enable_devices(dev, &pnp_ops,
82                            ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
83 }
84
85 /**
86  * Configure the specified Super I/O device with the resources (I/O space,
87  * etc.) that have been allocate for it.
88  *
89  * @param dev Pointer to structure describing a Super I/O device.
90  */
91 void lpc47n227_pnp_set_resources(device_t dev)
92 {
93         struct resource *res;
94
95         pnp_enter_conf_state(dev);
96
97         /*
98          * NOTE: Cannot use pnp_set_resources() here because it assumes chip
99          * support for logical devices, which the LPC47N227 doesn't have.
100          */
101         for (res = dev->resource_list; res; res = res->next)
102                 lpc47n227_pnp_set_resource(dev, res);
103
104         pnp_exit_conf_state(dev);
105 }
106
107 void lpc47n227_pnp_enable_resources(device_t dev)
108 {
109         pnp_enter_conf_state(dev);
110
111         /*
112          * NOTE: Cannot use pnp_enable_resources() here because it assumes chip
113          * support for logical devices, which the LPC47N227 doesn't have.
114          */
115         lpc47n227_pnp_set_enable(dev, 1);
116
117         pnp_exit_conf_state(dev);
118 }
119
120 void lpc47n227_pnp_enable(device_t dev)
121 {
122         pnp_enter_conf_state(dev);
123
124         /*
125          * NOTE: Cannot use pnp_set_enable() here because it assumes chip
126          * support for logical devices, which the LPC47N227 doesn't have.
127          */
128         if (dev->enabled) {
129                 lpc47n227_pnp_set_enable(dev, 1);
130         } else {
131                 lpc47n227_pnp_set_enable(dev, 0);
132         }
133
134         pnp_exit_conf_state(dev);
135 }
136
137 /**
138  * Initialize the specified Super I/O device.
139  *
140  * Devices other than COM ports and keyboard controller are ignored.
141  * For COM ports, we configure the baud rate.
142  *
143  * @param dev Pointer to structure describing a Super I/O device.
144  */
145 static void lpc47n227_init(device_t dev)
146 {
147         struct superio_smsc_lpc47n227_config *conf = dev->chip_info;
148         struct resource *res0;
149
150         if (!dev->enabled)
151                 return;
152
153         switch (dev->path.pnp.device) {
154         case LPC47N227_SP1:
155                 res0 = find_resource(dev, PNP_IDX_IO0);
156                 init_uart8250(res0->base, &conf->com1);
157                 break;
158
159         case LPC47N227_SP2:
160                 res0 = find_resource(dev, PNP_IDX_IO0);
161                 init_uart8250(res0->base, &conf->com2);
162                 break;
163
164         case LPC47N227_KBDC:
165                 printk(BIOS_DEBUG, "LPC47N227: Initializing keyboard.\n");
166                 pc_keyboard_init(&conf->keyboard);
167                 break;
168         }
169 }
170
171 static void lpc47n227_pnp_set_resource(device_t dev, struct resource *resource)
172 {
173         if (!(resource->flags & IORESOURCE_ASSIGNED)) {
174                 printk(BIOS_ERR, "ERROR: %s %02lx not allocated\n",
175                            dev_path(dev), resource->index);
176                 return;
177         }
178
179         /* Now store the resource */
180         /*
181          * NOTE: Cannot use pnp_set_XXX() here because they assume chip
182          * support for logical devices, which the LPC47N227 doesn't have.
183          */
184         if (resource->flags & IORESOURCE_IO) {
185                 lpc47n227_pnp_set_iobase(dev, resource->base);
186         } else if (resource->flags & IORESOURCE_DRQ) {
187                 lpc47n227_pnp_set_drq(dev, resource->base);
188         } else if (resource->flags & IORESOURCE_IRQ) {
189                 lpc47n227_pnp_set_irq(dev, resource->base);
190         } else {
191                 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
192                            dev_path(dev), resource->index);
193                 return;
194         }
195         resource->flags |= IORESOURCE_STORED;
196
197         report_resource_stored(dev, resource, "");
198 }
199
200 void lpc47n227_pnp_set_iobase(device_t dev, unsigned iobase)
201 {
202         ASSERT(!(iobase & 0x3));
203
204         switch (dev->path.pnp.device) {
205         case LPC47N227_PP:
206                 pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);
207                 break;
208
209         case LPC47N227_SP1:
210                 pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);
211                 break;
212
213         case LPC47N227_SP2:
214                 pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);
215                 break;
216
217         case LPC47N227_KBDC:
218                 break;
219
220         default:
221                 BUG();
222                 break;
223         }
224 }
225
226 void lpc47n227_pnp_set_drq(device_t dev, unsigned drq)
227 {
228         if (dev->path.pnp.device == LPC47N227_PP) {
229                 const uint8_t PP_DMA_MASK = 0x0F;
230                 const uint8_t PP_DMA_SELECTION_REGISTER = 0x26;
231                 uint8_t current_config =
232                     pnp_read_config(dev, PP_DMA_SELECTION_REGISTER);
233                 uint8_t new_config;
234
235                 ASSERT(!(drq & ~PP_DMA_MASK));  // DRQ out of range??
236                 new_config = (current_config & ~PP_DMA_MASK) | drq;
237                 pnp_write_config(dev, PP_DMA_SELECTION_REGISTER, new_config);
238         } else {
239                 BUG();
240         }
241 }
242
243 void lpc47n227_pnp_set_irq(device_t dev, unsigned irq)
244 {
245         uint8_t irq_config_register = 0;
246         uint8_t irq_config_mask = 0;
247         uint8_t current_config;
248         uint8_t new_config;
249
250         switch (dev->path.pnp.device) {
251         case LPC47N227_PP:
252                 irq_config_register = 0x27;
253                 irq_config_mask = 0x0F;
254                 break;
255
256         case LPC47N227_SP1:
257                 irq_config_register = 0x28;
258                 irq_config_mask = 0xF0;
259                 irq <<= 4;
260                 break;
261
262         case LPC47N227_SP2:
263                 irq_config_register = 0x28;
264                 irq_config_mask = 0x0F;
265                 break;
266
267         case LPC47N227_KBDC:
268                 break;
269
270         default:
271                 BUG();
272                 return;
273         }
274
275         current_config = pnp_read_config(dev, irq_config_register);
276         new_config = (current_config & ~irq_config_mask) | irq;
277         pnp_write_config(dev, irq_config_register, new_config);
278 }
279
280 void lpc47n227_pnp_set_enable(device_t dev, int enable)
281 {
282         uint8_t power_register = 0;
283         uint8_t power_mask = 0;
284         uint8_t current_power;
285         uint8_t new_power;
286
287         switch (dev->path.pnp.device) {
288         case LPC47N227_PP:
289                 power_register = 0x01;
290                 power_mask = 0x04;
291                 break;
292
293         case LPC47N227_SP1:
294                 power_register = 0x02;
295                 power_mask = 0x08;
296                 break;
297
298         case LPC47N227_SP2:
299                 power_register = 0x02;
300                 power_mask = 0x80;
301                 break;
302
303         case LPC47N227_KBDC:
304                 break;
305
306         default:
307                 BUG();
308                 return;
309         }
310
311         current_power = pnp_read_config(dev, power_register);
312         new_power = current_power & ~power_mask;        // disable by default
313
314         if (enable) {
315                 struct resource *ioport_resource =
316                     find_resource(dev, PNP_IDX_IO0);
317                 lpc47n227_pnp_set_iobase(dev, ioport_resource->base);
318
319                 new_power |= power_mask;        // Enable
320
321         } else {
322                 lpc47n227_pnp_set_iobase(dev, 0);
323         }
324         pnp_write_config(dev, power_register, new_power);
325 }
326
327 /** Enable access to the LPC47N227's configuration registers. */
328 static void pnp_enter_conf_state(device_t dev)
329 {
330         outb(0x55, dev->path.pnp.port);
331 }
332
333 /** Disable access to the LPC47N227's configuration registers. */
334 static void pnp_exit_conf_state(device_t dev)
335 {
336         outb(0xaa, dev->path.pnp.port);
337 }