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},
73 /**********************************************************************************/
74 /* PUBLIC INTERFACE */
75 /**********************************************************************************/
77 //----------------------------------------------------------------------------------
78 // Function: enable_dev
79 // Parameters: dev - pointer to structure describing a Super I/O device
81 // Description: Create device structures and allocate resources to devices
82 // specified in the pnp_dev_info array (above).
84 static void enable_dev(device_t dev)
86 pnp_enable_devices(dev, &pnp_ops,
87 ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
90 //----------------------------------------------------------------------------------
91 // Function: lpc47n227_pnp_set_resources
92 // Parameters: dev - pointer to structure describing a Super I/O device
94 // Description: Configure the specified Super I/O device with the resources
95 // (I/O space, etc.) that have been allocate for it.
97 void lpc47n227_pnp_set_resources(device_t dev)
101 pnp_enter_conf_state(dev);
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);
108 pnp_exit_conf_state(dev);
111 void lpc47n227_pnp_enable_resources(device_t dev)
113 pnp_enter_conf_state(dev);
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);
119 pnp_exit_conf_state(dev);
122 void lpc47n227_pnp_enable(device_t dev)
124 pnp_enter_conf_state(dev);
126 // NOTE: Cannot use pnp_set_enable() here because it assumes chip
127 // support for logical devices, which the LPC47N227 doesn't have
130 lpc47n227_pnp_set_enable(dev, 1);
132 lpc47n227_pnp_set_enable(dev, 0);
135 pnp_exit_conf_state(dev);
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.
146 static void lpc47n227_init(device_t dev)
148 struct superio_smsc_lpc47n227_config *conf = dev->chip_info;
149 struct resource *res0;
154 switch (dev->path.pnp.device) {
156 res0 = find_resource(dev, PNP_IDX_IO0);
157 init_uart8250(res0->base, &conf->com1);
161 res0 = find_resource(dev, PNP_IDX_IO0);
162 init_uart8250(res0->base, &conf->com2);
166 printk(BIOS_DEBUG, "LPC47N227: Initializing keyboard.\n");
167 pc_keyboard_init(&conf->keyboard);
172 /**********************************************************************************/
173 /* PRIVATE FUNCTIONS */
174 /**********************************************************************************/
176 static void lpc47n227_pnp_set_resource(device_t dev, struct resource *resource)
178 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
179 printk(BIOS_ERR, "ERROR: %s %02lx not allocated\n",
180 dev_path(dev), resource->index);
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
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);
195 printk(BIOS_ERR, "ERROR: %s %02lx unknown resource type\n",
196 dev_path(dev), resource->index);
199 resource->flags |= IORESOURCE_STORED;
201 report_resource_stored(dev, resource, "");
204 void lpc47n227_pnp_set_iobase(device_t dev, unsigned iobase)
206 ASSERT(!(iobase & 0x3));
208 switch (dev->path.pnp.device) {
210 pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);
214 pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);
218 pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);
230 void lpc47n227_pnp_set_drq(device_t dev, unsigned drq)
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);
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);
247 void lpc47n227_pnp_set_irq(device_t dev, unsigned irq)
249 uint8_t irq_config_register = 0;
250 uint8_t irq_config_mask = 0;
251 uint8_t current_config;
254 switch (dev->path.pnp.device) {
256 irq_config_register = 0x27;
257 irq_config_mask = 0x0F;
261 irq_config_register = 0x28;
262 irq_config_mask = 0xF0;
267 irq_config_register = 0x28;
268 irq_config_mask = 0x0F;
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);
284 void lpc47n227_pnp_set_enable(device_t dev, int enable)
286 uint8_t power_register = 0;
287 uint8_t power_mask = 0;
288 uint8_t current_power;
291 switch (dev->path.pnp.device) {
293 power_register = 0x01;
298 power_register = 0x02;
303 power_register = 0x02;
315 current_power = pnp_read_config(dev, power_register);
316 new_power = current_power & ~power_mask; // disable by default
319 struct resource *ioport_resource =
320 find_resource(dev, PNP_IDX_IO0);
321 lpc47n227_pnp_set_iobase(dev, ioport_resource->base);
323 new_power |= power_mask; // Enable
326 lpc47n227_pnp_set_iobase(dev, 0);
328 pnp_write_config(dev, power_register, new_power);
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.
337 static void pnp_enter_conf_state(device_t dev)
339 outb(0x55, dev->path.pnp.port);
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.
348 static void pnp_exit_conf_state(device_t dev)
350 outb(0xaa, dev->path.pnp.port);