• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /* Pre-RAM driver for SMSC LPC47N227 Super I/O chip. */
4 
5 #include <arch/io.h>
6 #include <assert.h>
7 #include <device/pnp_ops.h>
8 
9 #include "lpc47n227.h"
10 
pnp_enter_conf_state(pnp_devfn_t dev)11 void pnp_enter_conf_state(pnp_devfn_t dev)
12 {
13 	u16 port = dev >> 8;
14 	outb(0x55, port);
15 }
16 
pnp_exit_conf_state(pnp_devfn_t dev)17 void pnp_exit_conf_state(pnp_devfn_t dev)
18 {
19 	u16 port = dev >> 8;
20 	outb(0xaa, port);
21 }
22 
23 /**
24  * Program the base I/O port for the specified logical device.
25  *
26  * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number.
27  * @param iobase Base I/O port for the logical device.
28  */
lpc47n227_pnp_set_iobase(pnp_devfn_t dev,u16 iobase)29 static void lpc47n227_pnp_set_iobase(pnp_devfn_t dev, u16 iobase)
30 {
31 	/* LPC47N227 requires base ports to be a multiple of 4. */
32 	/* it's not very useful to do an ASSERT here: if it trips,
33 	 * there's no console to report it.
34 	ASSERT(!(iobase & 0x3));
35 	*/
36 
37 	switch (dev & 0xFF) {
38 	case LPC47N227_PP:
39 		pnp_write_config(dev, 0x23, (iobase >> 2) & 0xff);
40 		break;
41 	case LPC47N227_SP1:
42 		pnp_write_config(dev, 0x24, (iobase >> 2) & 0xff);
43 		break;
44 	case LPC47N227_SP2:
45 		pnp_write_config(dev, 0x25, (iobase >> 2) & 0xff);
46 		break;
47 	default:
48 		break;
49 	}
50 }
51 
52 /**
53  * Enable or disable the specified logical device.
54  *
55  * Technically, a full disable requires setting the device's base I/O port
56  * below 0x100. We don't do that here, because we don't have access to a data
57  * structure that specifies what the 'real' base port is (when asked to enable
58  * the device). Also the function is used only to disable the device while its
59  * true base port is programmed (see lpc47n227_enable_serial() below).
60  *
61  * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number.
62  * @param enable 0 to disable, anything else to enable.
63  */
lpc47n227_pnp_set_enable(pnp_devfn_t dev,int enable)64 static void lpc47n227_pnp_set_enable(pnp_devfn_t dev, int enable)
65 {
66 	u8 power_register = 0, power_mask = 0, current_power, new_power;
67 
68 	switch (dev & 0xFF) {
69 	case LPC47N227_PP:
70 		power_register = 0x01;
71 		power_mask = 0x04;
72 		break;
73 	case LPC47N227_SP1:
74 		power_register = 0x02;
75 		power_mask = 0x08;
76 		break;
77 	case LPC47N227_SP2:
78 		power_register = 0x02;
79 		power_mask = 0x80;
80 		break;
81 	default:
82 		return;
83 	}
84 
85 	current_power = pnp_read_config(dev, power_register);
86 	new_power = current_power & ~power_mask; /* Disable by default. */
87 	if (enable)
88 		new_power |= power_mask; /* Enable. */
89 	pnp_write_config(dev, power_register, new_power);
90 }
91 
92 /**
93  * Configure the base I/O port of the specified serial device and enable the
94  * serial device.
95  *
96  * @param dev High 8 bits = Super I/O port, low 8 bits = logical device number.
97  * @param iobase Processor I/O port address to assign to this serial device.
98  */
lpc47n227_enable_serial(pnp_devfn_t dev,u16 iobase)99 void lpc47n227_enable_serial(pnp_devfn_t dev, u16 iobase)
100 {
101 	/*
102 	 * NOTE: Cannot use pnp_set_XXX() here because they assume chip
103 	 * support for logical devices, which the LPC47N227 doesn't have.
104 	 */
105 	pnp_enter_conf_state(dev);
106 	lpc47n227_pnp_set_enable(dev, 0);
107 	lpc47n227_pnp_set_iobase(dev, iobase);
108 	lpc47n227_pnp_set_enable(dev, 1);
109 	pnp_exit_conf_state(dev);
110 }
111