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