1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <commonlib/bsd/helpers.h>
5 #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>
14 int sch5545_get_gpio(uint8_t sio_port
, uint8_t gpio
)
17 uint16_t runtime_reg_base
;
18 uint8_t gpio_bank
, gpio_num
;
20 gpio_bank
= gpio
/ 10;
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).
30 else if (gpio_bank
== 7 && gpio_num
> 1)
32 else if (gpio_bank
> 7)
35 dev
= dev_find_slot_pnp(sio_port
, SCH5545_LDN_LPC
);
38 printk(BIOS_ERR
, "%s: ERROR: LPC interface LDN not present."
39 "Check the devicetree!\n", __func__
);
43 pnp_enter_conf_mode(dev
);
44 pnp_set_logical_device(dev
);
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;
49 pnp_exit_conf_mode(dev
);
51 if (runtime_reg_base
== 0)
54 outb(gpio_bank
* 8 + gpio_num
, runtime_reg_base
+ SCH5545_RR_GPIO_SEL
);
56 return inb(runtime_reg_base
+ SCH5545_RR_GPIO_READ
) & 1;
59 static void sch5545_init(struct device
*dev
)
64 switch (dev
->path
.pnp
.device
) {
66 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
);
72 pc_keyboard_init(NO_AUX_DEVICE
);
75 pnp_enter_conf_mode(dev
);
76 pnp_set_logical_device(dev
);
78 pnp_write_config(dev
, 0x24, pnp_read_config(dev
, 0x24) | 0x04);
79 pnp_exit_conf_mode(dev
);
84 static void sch5545_set_iobase(struct device
*dev
, u8 index
, u16 iobase
)
87 struct device
*lpc_if
;
90 lpc_if
= dev_find_slot_pnp(dev
->path
.pnp
.port
, SCH5545_LDN_LPC
);
93 printk(BIOS_ERR
, "%s LPC interface LDN not present."
94 "Check the devicetree!\n", dev_path(dev
));
98 switch (dev
->path
.pnp
.device
) {
100 iobase_reg
= SCH5545_BAR_EM_IF
;
102 case SCH5545_LDN_KBC
:
103 iobase_reg
= SCH5545_BAR_KBC
;
105 case SCH5545_LDN_UART1
:
106 iobase_reg
= SCH5545_BAR_UART1
;
108 case SCH5545_LDN_UART2
:
109 iobase_reg
= SCH5545_BAR_UART2
;
112 iobase_reg
= SCH5545_BAR_RUNTIME_REG
;
114 case SCH5545_LDN_FDC
:
115 iobase_reg
= SCH5545_BAR_FLOPPY
;
117 case SCH5545_LDN_LPC
:
118 iobase_reg
= SCH5545_BAR_LPC_IF
;
121 iobase_reg
= SCH5545_BAR_PARPORT
;
127 pnp_set_logical_device(lpc_if
);
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);
134 val
= pnp_read_config(lpc_if
, iobase_reg
+ 1);
136 pnp_write_config(lpc_if
, iobase_reg
+ 1, val
);
138 pnp_set_logical_device(dev
);
141 static void sch5545_set_irq(struct device
*dev
, u8 index
, u8 irq
)
144 struct device
*lpc_if
;
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
);
152 lpc_if
= dev_find_slot_pnp(dev
->path
.pnp
.port
, SCH5545_LDN_LPC
);
155 printk(BIOS_ERR
, "%s LPC interface LDN not present."
156 "Check the devicetree!\n", dev_path(dev
));
160 pnp_set_logical_device(lpc_if
);
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.
170 switch (dev
->path
.pnp
.device
) {
171 case SCH5545_LDN_EMI
:
172 case SCH5545_LDN_KBC
:
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.
187 pnp_write_config(lpc_if
, SCH5545_IRQ_BASE
+ irq
, dev
->path
.pnp
.device
| select_bit
);
188 pnp_set_logical_device(dev
);
191 static void sch5545_set_drq(struct device
*dev
, u8 index
, u8 drq
)
193 struct device
*lpc_if
;
196 printk(BIOS_ERR
, "%s %02x: Trying to set reserved DMA channel 4!\n",
197 dev_path(dev
), index
);
198 printk(BIOS_ERR
, "This configuration is untested. Trying to continue.\n");
201 /* DMA channel is programmed via LPC LDN */
202 lpc_if
= dev_find_slot_pnp(dev
->path
.pnp
.port
, SCH5545_LDN_LPC
);
205 printk(BIOS_ERR
, "%s LPC interface LDN not present."
206 "Check the devicetree!\n", dev_path(dev
));
210 pnp_set_logical_device(lpc_if
);
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.
219 pnp_write_config(dev
, SCH5545_DRQ_BASE
+ (drq
* 2) + 1, dev
->path
.pnp
.device
| 0x80);
221 pnp_set_logical_device(dev
);
224 static void sch5545_set_resource(struct device
*dev
, struct resource
*resource
)
226 if (!(resource
->flags
& IORESOURCE_ASSIGNED
)) {
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.
233 if (resource
->flags
& IORESOURCE_IRQ
&&
234 (resource
->index
!= PNP_IDX_IRQ0
) &&
235 (resource
->index
!= PNP_IDX_IRQ1
))
236 printk(BIOS_WARNING
, "%s %02lx %s size: "
237 "0x%010llx not assigned\n", dev_path(dev
),
238 resource
->index
, resource_type(resource
),
241 printk(BIOS_ERR
, "%s %02lx %s size: 0x%010llx "
242 "not assigned\n", dev_path(dev
), resource
->index
,
243 resource_type(resource
), resource
->size
);
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
);
255 printk(BIOS_ERR
, "%s %02lx unknown resource type\n",
256 dev_path(dev
), resource
->index
);
259 resource
->flags
|= IORESOURCE_STORED
;
261 report_resource_stored(dev
, resource
, "");
264 static void sch5545_set_resources(struct device
*dev
)
266 struct resource
*res
;
268 pnp_enter_conf_mode(dev
);
270 /* Select the logical device (LDN). */
271 pnp_set_logical_device(dev
);
273 for (res
= dev
->resource_list
; res
; res
= res
->next
)
274 sch5545_set_resource(dev
, res
);
276 pnp_exit_conf_mode(dev
);
279 static 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
,
288 static 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 },
300 static void enable_dev(struct device
*dev
)
302 pnp_enable_devices(dev
, &ops
, ARRAY_SIZE(pnp_dev_info
), pnp_dev_info
);
305 struct chip_operations superio_smsc_sch5545_ops
= {
306 CHIP_NAME("SMSC SCH5545 Super I/O")
307 .enable_dev
= enable_dev
,