2 * This file is part of the coreboot project.
4 * Copyright (C) 2005 Digital Design Corporation
5 * Copyright (C) 2008-2009 coresystems GmbH
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.
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.
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
21 /* RAM-based driver for SMSC LPC47N227 Super I/O chip. */
24 #include <device/device.h>
25 #include <device/pnp.h>
26 #include <console/console.h>
27 #include <device/smbus.h>
34 #include "lpc47n227.h"
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);
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);
49 static void pnp_enter_conf_state(device_t dev);
50 static void pnp_exit_conf_state(device_t dev);
52 struct chip_operations superio_smsc_lpc47n227_ops = {
53 CHIP_NAME("SMSC LPC47N227 Super I/O")
54 .enable_dev = enable_dev,
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,
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},
74 * Create device structures and allocate resources to devices specified in the
75 * pnp_dev_info array (above).
77 * @param dev Pointer to structure describing a Super I/O device.
79 static void enable_dev(device_t dev)
81 pnp_enable_devices(dev, &pnp_ops,
82 ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
86 * Configure the specified Super I/O device with the resources (I/O space,
87 * etc.) that have been allocate for it.
89 * @param dev Pointer to structure describing a Super I/O device.
91 void lpc47n227_pnp_set_resources(device_t dev)
95 pnp_enter_conf_state(dev);
98 * NOTE: Cannot use pnp_set_resources() here because it assumes chip
99 * support for logical devices, which the LPC47N227 doesn't have.
101 for (res = dev->resource_list; res; res = res->next)
102 lpc47n227_pnp_set_resource(dev, res);
104 pnp_exit_conf_state(dev);
107 void lpc47n227_pnp_enable_resources(device_t dev)
109 pnp_enter_conf_state(dev);
112 * NOTE: Cannot use pnp_enable_resources() here because it assumes chip
113 * support for logical devices, which the LPC47N227 doesn't have.
115 lpc47n227_pnp_set_enable(dev, 1);
117 pnp_exit_conf_state(dev);
120 void lpc47n227_pnp_enable(device_t dev)
122 pnp_enter_conf_state(dev);
125 * NOTE: Cannot use pnp_set_enable() here because it assumes chip
126 * support for logical devices, which the LPC47N227 doesn't have.
129 lpc47n227_pnp_set_enable(dev, 1);
131 lpc47n227_pnp_set_enable(dev, 0);
134 pnp_exit_conf_state(dev);
138 * Initialize the specified Super I/O device.
140 * Devices other than COM ports and keyboard controller are ignored.
141 * For COM ports, we configure the baud rate.
143 * @param dev Pointer to structure describing a Super I/O device.
145 static void lpc47n227_init(device_t dev)
147 struct superio_smsc_lpc47n227_config *conf = dev->chip_info;
148 struct resource *res0;
153 switch (dev->path.pnp.device) {
155 res0 = find_resource(dev, PNP_IDX_IO0);
156 init_uart8250(res0->base, &conf->com1);
160 res0 = find_resource(dev, PNP_IDX_IO0);
161 init_uart8250(res0->base, &conf->com2);
165 printk(BIOS_DEBUG, "LPC47N227: Initializing keyboard.\n");
166 pc_keyboard_init(&conf->keyboard);
171 static void lpc47n227_pnp_set_resource(device_t dev, struct resource *resource)
173 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
174 printk(BIOS_ERR, "ERROR: %s %02lx not allocated\n",
175 dev_path(dev), resource->index);
179 /* Now store the resource */
181 * NOTE: Cannot use pnp_set_XXX() here because they assume chip
182 * support for logical devices, which the LPC47N227 doesn't have.
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);
191 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
192 dev_path(dev), resource->index);
195 resource->flags |= IORESOURCE_STORED;
197 report_resource_stored(dev, resource, "");
200 void lpc47n227_pnp_set_iobase(device_t dev, unsigned iobase)
202 ASSERT(!(iobase & 0x3));
204 switch (dev->path.pnp.device) {
206 pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);
210 pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);
214 pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);
226 void lpc47n227_pnp_set_drq(device_t dev, unsigned drq)
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);
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);
243 void lpc47n227_pnp_set_irq(device_t dev, unsigned irq)
245 uint8_t irq_config_register = 0;
246 uint8_t irq_config_mask = 0;
247 uint8_t current_config;
250 switch (dev->path.pnp.device) {
252 irq_config_register = 0x27;
253 irq_config_mask = 0x0F;
257 irq_config_register = 0x28;
258 irq_config_mask = 0xF0;
263 irq_config_register = 0x28;
264 irq_config_mask = 0x0F;
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);
280 void lpc47n227_pnp_set_enable(device_t dev, int enable)
282 uint8_t power_register = 0;
283 uint8_t power_mask = 0;
284 uint8_t current_power;
287 switch (dev->path.pnp.device) {
289 power_register = 0x01;
294 power_register = 0x02;
299 power_register = 0x02;
311 current_power = pnp_read_config(dev, power_register);
312 new_power = current_power & ~power_mask; // disable by default
315 struct resource *ioport_resource =
316 find_resource(dev, PNP_IDX_IO0);
317 lpc47n227_pnp_set_iobase(dev, ioport_resource->base);
319 new_power |= power_mask; // Enable
322 lpc47n227_pnp_set_iobase(dev, 0);
324 pnp_write_config(dev, power_register, new_power);
327 /** Enable access to the LPC47N227's configuration registers. */
328 static void pnp_enter_conf_state(device_t dev)
330 outb(0x55, dev->path.pnp.port);
333 /** Disable access to the LPC47N227's configuration registers. */
334 static void pnp_exit_conf_state(device_t dev)
336 outb(0xaa, dev->path.pnp.port);