blob: 7b8e5a063e42d84a15239957a67ffe63d3624853 [file] [log] [blame]
Michał Żygowskifba08302020-04-13 20:51:32 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <arch/io.h>
Elyes HAOUASef6139f2022-01-04 19:11:59 +01004#include <commonlib/bsd/helpers.h>
Michał Żygowskifba08302020-04-13 20:51:32 +02005#include <device/device.h>
6#include <device/pnp.h>
7#include <superio/conf_mode.h>
8#include <console/console.h>
9#include <pc80/keyboard.h>
Elyes HAOUASef6139f2022-01-04 19:11:59 +010010#include <stdint.h>
Michał Żygowskifba08302020-04-13 20:51:32 +020011
12#include "sch5545.h"
13
14int sch5545_get_gpio(uint8_t sio_port, uint8_t gpio)
15{
16 struct device *dev;
17 uint16_t runtime_reg_base;
18 uint8_t gpio_bank, gpio_num;
19
20 gpio_bank = gpio / 10;
21 gpio_num = gpio % 10;
22 /*
23 * GPIOs are divided into banks of 8 GPIOs (kind of). Each group starts
24 * at decimal base, i.e. 8 GPIOs from GPIO000, 8 GPIOs from GPIO010,
25 * etc., up to GPIO071 and GPIO072 which are an exception (only two
26 * gpios in the bank 7).
27 */
28 if (gpio_num > 7)
29 return -1;
30 else if (gpio_bank == 7 && gpio_num > 1)
31 return -1;
32 else if (gpio_bank > 7)
33 return -1;
34
35 dev = dev_find_slot_pnp(sio_port, SCH5545_LDN_LPC);
36
37 if (!dev) {
38 printk(BIOS_ERR, "%s: ERROR: LPC interface LDN not present."
39 "Check the devicetree!\n", __func__);
40 return -1;
41 }
42
43 pnp_enter_conf_mode(dev);
44 pnp_set_logical_device(dev);
45
46 runtime_reg_base = pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 2);
47 runtime_reg_base |= pnp_read_config(dev, SCH5545_BAR_RUNTIME_REG + 3) << 8;
48
49 pnp_exit_conf_mode(dev);
50
51 if (runtime_reg_base == 0)
52 return -1;
53
54 outb(gpio_bank * 8 + gpio_num, runtime_reg_base + SCH5545_RR_GPIO_SEL);
55
56 return inb(runtime_reg_base + SCH5545_RR_GPIO_READ) & 1;
57}
58
59static void sch5545_init(struct device *dev)
60{
61 if (!dev->enabled)
62 return;
63
64 switch (dev->path.pnp.device) {
65 case SCH5545_LDN_KBC:
Michał Żygowski5aba2ae2021-11-25 21:49:28 +010066 pnp_enter_conf_mode(dev);
67 pnp_set_logical_device(dev);
68 /* Disable PS/2 clock and data isolation */
69 pnp_unset_and_set_config(dev, 0xf0,
70 SCH5545_KBD_ISOLATION | SCH5545_MOUSE_ISOLATION, 0);
71 pnp_exit_conf_mode(dev);
Michał Żygowskifba08302020-04-13 20:51:32 +020072 pc_keyboard_init(NO_AUX_DEVICE);
73 break;
74 case SCH5545_LDN_LPC:
75 pnp_enter_conf_mode(dev);
76 pnp_set_logical_device(dev);
77 /* Enable SERIRQ */
78 pnp_write_config(dev, 0x24, pnp_read_config(dev, 0x24) | 0x04);
79 pnp_exit_conf_mode(dev);
80 break;
81 }
82}
83
84static void sch5545_set_iobase(struct device *dev, u8 index, u16 iobase)
85{
86 u8 val;
87 struct device *lpc_if;
88 u8 iobase_reg = 0;
89
90 lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC);
91
92 if (!lpc_if) {
Julius Wernere9665952022-01-21 17:06:20 -080093 printk(BIOS_ERR, "%s LPC interface LDN not present."
Michał Żygowskifba08302020-04-13 20:51:32 +020094 "Check the devicetree!\n", dev_path(dev));
95 return;
96 }
97
98 switch (dev->path.pnp.device) {
99 case SCH5545_LDN_EMI:
100 iobase_reg = SCH5545_BAR_EM_IF;
101 break;
102 case SCH5545_LDN_KBC:
103 iobase_reg = SCH5545_BAR_KBC;
104 break;
105 case SCH5545_LDN_UART1:
106 iobase_reg = SCH5545_BAR_UART1;
107 break;
108 case SCH5545_LDN_UART2:
109 iobase_reg = SCH5545_BAR_UART2;
110 break;
111 case SCH5545_LDN_RR:
112 iobase_reg = SCH5545_BAR_RUNTIME_REG;
113 break;
114 case SCH5545_LDN_FDC:
115 iobase_reg = SCH5545_BAR_FLOPPY;
116 break;
117 case SCH5545_LDN_LPC:
118 iobase_reg = SCH5545_BAR_LPC_IF;
119 break;
120 case SCH5545_LDN_PP:
121 iobase_reg = SCH5545_BAR_PARPORT;
122 break;
123 default:
124 return;
125 }
126
127 pnp_set_logical_device(lpc_if);
128
129 /* Flip the bytes in IO base, LSB goes first */
130 pnp_write_config(lpc_if, iobase_reg + 2, iobase & 0xff);
131 pnp_write_config(lpc_if, iobase_reg + 3, (iobase >> 8) & 0xff);
132
133 /* Set valid bit */
134 val = pnp_read_config(lpc_if, iobase_reg + 1);
135 val |= 0x80;
136 pnp_write_config(lpc_if, iobase_reg + 1, val);
137
138 pnp_set_logical_device(dev);
139}
140
141static void sch5545_set_irq(struct device *dev, u8 index, u8 irq)
142{
143 u8 select_bit = 0;
144 struct device *lpc_if;
145
146 /* In case it is not the IRQ, write misc register directly */
147 if (index >= PNP_IDX_MSC0) {
148 pnp_write_config(dev, index, irq);
149 return;
150 }
151
152 lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC);
153
154 if (!lpc_if) {
Julius Wernere9665952022-01-21 17:06:20 -0800155 printk(BIOS_ERR, "%s LPC interface LDN not present."
Michał Żygowskifba08302020-04-13 20:51:32 +0200156 "Check the devicetree!\n", dev_path(dev));
157 return;
158 }
159
160 pnp_set_logical_device(lpc_if);
161
162 /*
163 * Some LDNs can generate IRQs from two sources, i.e.
164 * - EMI may generate interrupts for Mailbox and INT source register
165 * - KBC may generate separate IRQ for mouse and keyboard,
166 * - RR LDN may generate IRQ for PME and SMI etc.
167 * SELECT bit allows to distinguish IRQ source for single LDN.
168 * Use the standard IRQs for devices.
169 */
170 switch (dev->path.pnp.device) {
171 case SCH5545_LDN_EMI:
172 case SCH5545_LDN_KBC:
173 case SCH5545_LDN_RR:
174 if (index == 0x72)
175 select_bit = 0x80;
176 break;
177 default:
178 break;
179 }
180
181 /*
182 * IRQs are set in a little different manner. Each IRQ number has its
183 * own register which is programmed with LDN number which should use
184 * the IRQ. Ignore the index offset and choose register based on IRQ
185 * number counting from IRQ base.
186 */
187 pnp_write_config(lpc_if, SCH5545_IRQ_BASE + irq, dev->path.pnp.device | select_bit);
188 pnp_set_logical_device(dev);
189}
190
191static void sch5545_set_drq(struct device *dev, u8 index, u8 drq)
192{
193 struct device *lpc_if;
194
195 if (drq == 4) {
Julius Wernere9665952022-01-21 17:06:20 -0800196 printk(BIOS_ERR, "%s %02x: Trying to set reserved DMA channel 4!\n",
Michał Żygowskifba08302020-04-13 20:51:32 +0200197 dev_path(dev), index);
198 printk(BIOS_ERR, "This configuration is untested. Trying to continue.\n");
199 }
200
201 /* DMA channel is programmed via LPC LDN */
202 lpc_if = dev_find_slot_pnp(dev->path.pnp.port, SCH5545_LDN_LPC);
203
204 if (!lpc_if) {
Julius Wernere9665952022-01-21 17:06:20 -0800205 printk(BIOS_ERR, "%s LPC interface LDN not present."
Michał Żygowskifba08302020-04-13 20:51:32 +0200206 "Check the devicetree!\n", dev_path(dev));
207 return;
208 }
209
210 pnp_set_logical_device(lpc_if);
211
212 /*
213 * There are 8 configurable DMA channels. DRQs are set in a little
214 * different manner. Each DMA channel has its own 16-bit register which
215 * is programmed with LDN number (in higher byte) which should use the
216 * IRQ. Ignore the index offset and choose register based on IRQ number
217 * counting from IRQ base. Set valid bit (bit 7) additionally.
218 */
219 pnp_write_config(dev, SCH5545_DRQ_BASE + (drq * 2) + 1, dev->path.pnp.device | 0x80);
220
221 pnp_set_logical_device(dev);
222}
223
224static void sch5545_set_resource(struct device *dev, struct resource *resource)
225{
226 if (!(resource->flags & IORESOURCE_ASSIGNED)) {
227 /*
228 * The PNP_MSC super IO registers have the IRQ flag set. If no
229 * value is assigned in the devicetree, the corresponding
230 * PNP_MSC register doesn't get written, which should be printed
231 * as warning and not as error.
232 */
233 if (resource->flags & IORESOURCE_IRQ &&
234 (resource->index != PNP_IDX_IRQ0) &&
235 (resource->index != PNP_IDX_IRQ1))
Julius Wernere9665952022-01-21 17:06:20 -0800236 printk(BIOS_WARNING, "%s %02lx %s size: "
Michał Żygowskifba08302020-04-13 20:51:32 +0200237 "0x%010llx not assigned\n", dev_path(dev),
238 resource->index, resource_type(resource),
239 resource->size);
240 else
Julius Wernere9665952022-01-21 17:06:20 -0800241 printk(BIOS_ERR, "%s %02lx %s size: 0x%010llx "
Michał Żygowskifba08302020-04-13 20:51:32 +0200242 "not assigned\n", dev_path(dev), resource->index,
243 resource_type(resource), resource->size);
244 return;
245 }
246
247 /* Now store the resource. */
248 if (resource->flags & IORESOURCE_IO) {
249 sch5545_set_iobase(dev, resource->index, resource->base);
250 } else if (resource->flags & IORESOURCE_DRQ) {
251 sch5545_set_drq(dev, resource->index, resource->base);
252 } else if (resource->flags & IORESOURCE_IRQ) {
253 sch5545_set_irq(dev, resource->index, resource->base);
254 } else {
Julius Wernere9665952022-01-21 17:06:20 -0800255 printk(BIOS_ERR, "%s %02lx unknown resource type\n",
Michał Żygowskifba08302020-04-13 20:51:32 +0200256 dev_path(dev), resource->index);
257 return;
258 }
259 resource->flags |= IORESOURCE_STORED;
260
261 report_resource_stored(dev, resource, "");
262}
263
264static void sch5545_set_resources(struct device *dev)
265{
266 struct resource *res;
267
268 pnp_enter_conf_mode(dev);
269
270 /* Select the logical device (LDN). */
271 pnp_set_logical_device(dev);
272
273 for (res = dev->resource_list; res; res = res->next)
274 sch5545_set_resource(dev, res);
275
276 pnp_exit_conf_mode(dev);
277}
278
279static struct device_operations ops = {
280 .read_resources = pnp_read_resources,
281 .set_resources = sch5545_set_resources,
282 .enable_resources = pnp_enable_resources,
283 .enable = pnp_alt_enable,
284 .init = sch5545_init,
285 .ops_pnp_mode = &pnp_conf_mode_55_aa,
286};
287
288static struct pnp_info pnp_dev_info[] = {
289 { NULL, SCH5545_LDN_EMI, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1, 0x0ff0 },
290 { NULL, SCH5545_LDN_KBC, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1 | PNP_MSC0 | PNP_MSC1, 0x0fff },
291 { NULL, SCH5545_LDN_UART1, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 },
292 { NULL, SCH5545_LDN_UART2, PNP_IO0 | PNP_IRQ0 | PNP_MSC0, 0x0ff8 },
293 { NULL, SCH5545_LDN_RR, PNP_IO0 | PNP_IRQ0 | PNP_IRQ1 | PNP_MSC0, 0x0fc0 },
294 { NULL, SCH5545_LDN_FDC, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0 | PNP_MSC0 | PNP_MSC1 |
295 PNP_MSC2 | PNP_MSC3 | PNP_MSC4 | PNP_MSC5, 0x0ff8, },
296 { NULL, SCH5545_LDN_LPC, PNP_IO0, 0x0ffe },
297 { NULL, SCH5545_LDN_PP, PNP_IO0 | PNP_IRQ0 | PNP_DRQ0 | PNP_MSC0 | PNP_MSC1, 0x0ff8 },
298};
299
300static void enable_dev(struct device *dev)
301{
302 pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
303}
304
305struct chip_operations superio_smsc_sch5545_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900306 .name = "SMSC SCH5545 Super I/O",
Michał Żygowskifba08302020-04-13 20:51:32 +0200307 .enable_dev = enable_dev,
308};