xref: /aosp_15_r20/external/coreboot/src/ec/dell/mec5035/mec5035.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <arch/io.h>
4 #include <console/console.h>
5 #include <device/device.h>
6 #include <device/pnp.h>
7 #include <option.h>
8 #include <pc80/keyboard.h>
9 #include <stdint.h>
10 #include "mec5035.h"
11 
12 static const u16 MAILBOX_INDEX = 0x910;
13 static const u16 MAILBOX_DATA = MAILBOX_INDEX + 1;
14 
__get_mailbox_register(u8 index)15 static inline u8 __get_mailbox_register(u8 index)
16 {
17 	outb(index + 0x10, MAILBOX_INDEX);
18 	return inb(MAILBOX_DATA);
19 }
20 
__set_mailbox_register(u8 index,u8 data)21 static inline void __set_mailbox_register(u8 index, u8 data)
22 {
23 	outb(index + 0x10, MAILBOX_INDEX);
24 	outb(data, MAILBOX_DATA);
25 }
26 
wait_ec(void)27 static void wait_ec(void)
28 {
29 	u8 busy;
30 	do {
31 		outb(0, MAILBOX_INDEX);
32 		busy = inb(MAILBOX_DATA);
33 	} while (busy);
34 }
35 
36 
read_mailbox_regs(u8 * data,u8 start,u8 count)37 static enum cb_err read_mailbox_regs(u8 *data, u8 start, u8 count)
38 {
39 	if (start + count >= NUM_REGISTERS) {
40 		printk(BIOS_ERR, "%s: Invalid start or count argument.\n", __func__);
41 		return CB_ERR_ARG;
42 	}
43 
44 	while (count--) {
45 		*data = __get_mailbox_register(start);
46 		data++;
47 		start++;
48 	}
49 
50 	return CB_SUCCESS;
51 }
52 
write_mailbox_regs(const u8 * data,u8 start,u8 count)53 static enum cb_err write_mailbox_regs(const u8 *data, u8 start, u8 count)
54 {
55 	if (start + count >= NUM_REGISTERS) {
56 		printk(BIOS_ERR, "%s: Invalid start or count argument.\n", __func__);
57 		return CB_ERR_ARG;
58 	}
59 
60 	while (count--) {
61 		__set_mailbox_register(start, *data);
62 		data++;
63 		start++;
64 	}
65 
66 	return CB_SUCCESS;
67 }
68 
ec_command(u8 cmd)69 static void ec_command(u8 cmd)
70 {
71 	outb(0, MAILBOX_INDEX);
72 	outb(cmd, MAILBOX_DATA);
73 	wait_ec();
74 }
75 
mec5035_mouse_touchpad(u8 setting)76 u8 mec5035_mouse_touchpad(u8 setting)
77 {
78 	u8 buf[15] = {0};
79 	write_mailbox_regs(&setting, 2, 1);
80 	ec_command(CMD_MOUSE_TP);
81 	/* The vendor firmware reads 15 bytes starting at index 1, presumably
82 	   to get some sort of return code. Though I don't know for sure if
83 	   this is the case. Assume the first byte is the return code. */
84 	read_mailbox_regs(buf, 1, 15);
85 	return buf[0];
86 }
87 
mec5035_control_radio(enum ec_radio_dev dev,enum ec_radio_state state)88 void mec5035_control_radio(enum ec_radio_dev dev, enum ec_radio_state state)
89 {
90 	/* From LPC traces and userspace testing with other values,
91 	   the second byte has to be 2 for an unknown reason. */
92 	u8 buf[RADIO_CTRL_NUM_ARGS] = {(u8)dev, 2, (u8)state};
93 	write_mailbox_regs(buf, 2, RADIO_CTRL_NUM_ARGS);
94 	ec_command(CMD_RADIO_CTRL);
95 }
96 
mec5035_early_init(void)97 void mec5035_early_init(void)
98 {
99 	/* If this isn't sent the EC shuts down the system after about 15
100 	   seconds, flashing a pattern on the keyboard LEDs corresponding
101 	   to "processor failure" according to Dell service manuals. */
102 	ec_command(CMD_CPU_OK);
103 }
104 
mec5035_init(struct device * dev)105 static void mec5035_init(struct device *dev)
106 {
107 	/* Unconditionally use this argument for now as this setting
108 	   is probably the most sensible default out of the 3 choices. */
109 	mec5035_mouse_touchpad(TP_PS2_MOUSE);
110 
111 	pc_keyboard_init(NO_AUX_DEVICE);
112 
113 	mec5035_control_radio(RADIO_WLAN, get_uint_option("wlan", RADIO_ON));
114 	mec5035_control_radio(RADIO_WWAN, get_uint_option("wwan", RADIO_ON));
115 	mec5035_control_radio(RADIO_BT, get_uint_option("bluetooth", RADIO_ON));
116 }
117 
118 static struct device_operations ops = {
119 	.init = mec5035_init,
120 	.read_resources = noop_read_resources,
121 	.set_resources = noop_set_resources
122 };
123 
124 static struct pnp_info pnp_dev_info[] = {
125 	{ NULL, 0, 0, 0, }
126 };
127 
mec5035_enable(struct device * dev)128 static void mec5035_enable(struct device *dev)
129 {
130 	pnp_enable_devices(dev, &ops, ARRAY_SIZE(pnp_dev_info), pnp_dev_info);
131 }
132 
133 struct chip_operations ec_dell_mec5035_ops = {
134 	.name = "MEC5035 EC",
135 	.enable_dev = mec5035_enable,
136 };
137