/ src / superio / common / ssdt.c
ssdt.c
  1  /* SPDX-License-Identifier: GPL-2.0-or-later */
  2  
  3  #include <superio/common/ssdt.h>
  4  
  5  #include <device/device.h>
  6  #include <device/pnp.h>
  7  #include <acpi/acpigen.h>
  8  #include <acpi/acpi.h>
  9  #include <console/console.h>
 10  #include <types.h>
 11  #include <stdio.h>
 12  #include <string.h>
 13  
 14  struct superio_dev {
 15  	const char *acpi_hid;
 16  	u16 io_base[4];
 17  	u8 irq[2];
 18  };
 19  
 20  static const struct superio_dev superio_devs[] = {
 21  	{ACPI_HID_FDC, {0x3f0, 0x3f2, 0x3f7}, {6, } },
 22  	{ACPI_HID_KEYBOARD, {60, 64, }, {1, } },
 23  	{ACPI_HID_MOUSE, {60, 64, }, {12, } },
 24  	{ACPI_HID_COM, {0x3f8, 0x2f8, 0x3e8, 0x2e8}, {4, 3} },
 25  	{ACPI_HID_LPT, {0x378, }, {7, } },
 26  };
 27  
 28  static const u8 io_idx[] = {PNP_IDX_IO0, PNP_IDX_IO1, PNP_IDX_IO2, PNP_IDX_IO3};
 29  static const u8 irq_idx[] = {PNP_IDX_IRQ0, PNP_IDX_IRQ1};
 30  
 31  static const struct superio_dev *superio_guess_function(const struct device *dev)
 32  {
 33  	for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
 34  		struct resource *res = probe_resource(dev, io_idx[i]);
 35  		if (!res || !res->base)
 36  			continue;
 37  
 38  		for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
 39  			for (size_t k = 0; k < 4; k++) {
 40  				if (!superio_devs[j].io_base[k])
 41  					continue;
 42  				if (superio_devs[j].io_base[k] == res->base)
 43  					return &superio_devs[j];
 44  			}
 45  		}
 46  	}
 47  	for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
 48  		struct resource *res = probe_resource(dev, irq_idx[i]);
 49  		if (!res || !res->size)
 50  			continue;
 51  		for (size_t j = 0; j < ARRAY_SIZE(superio_devs); j++) {
 52  			for (size_t k = 0; k < 2; k++) {
 53  				if (!superio_devs[j].irq[k])
 54  					continue;
 55  				if (superio_devs[j].irq[k] == res->base)
 56  					return &superio_devs[j];
 57  			}
 58  		}
 59  	}
 60  	return NULL;
 61  }
 62  
 63  /* Return true if there are resources to report */
 64  static bool has_resources(const struct device *dev)
 65  {
 66  	for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
 67  		struct resource *res = probe_resource(dev, io_idx[i]);
 68  		if (!res || !res->base || !res->size)
 69  			continue;
 70  		return 1;
 71  	}
 72  	for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
 73  		struct resource *res = probe_resource(dev, irq_idx[i]);
 74  		if (!res || !res->size || res->base > 16)
 75  			continue;
 76  		return 1;
 77  	}
 78  	return 0;
 79  }
 80  
 81  /* Add IO and IRQ resources for _CRS or _PRS */
 82  static void ldn_gen_resources(const struct device *dev)
 83  {
 84  	uint16_t irq = 0;
 85  	for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
 86  		struct resource *res = probe_resource(dev, io_idx[i]);
 87  		if (!res || !res->base)
 88  			continue;
 89  		resource_t base = res->base;
 90  		resource_t size = res->size;
 91  		while (size > 0) {
 92  			resource_t sz = size > 255 ? 255 : size;
 93  			/* TODO: Needs test with regions >= 256 bytes */
 94  			acpigen_write_io16(base, base, 1, sz, 1);
 95  			size -= sz;
 96  			base += sz;
 97  		}
 98  	}
 99  	for (size_t i = 0; i < ARRAY_SIZE(irq_idx); i++) {
100  		struct resource *res = probe_resource(dev, irq_idx[i]);
101  		if (!res || !res->size || res->base >= 16)
102  			continue;
103  		irq |= 1 << res->base;
104  	}
105  	if (irq)
106  		acpigen_write_irq(irq);
107  }
108  
109  /* Add resource base and size for additional SuperIO code */
110  static void ldn_gen_resources_use(const struct device *dev)
111  {
112  	char name[5];
113  	for (size_t i = 0; i < ARRAY_SIZE(io_idx); i++) {
114  		struct resource *res = probe_resource(dev, io_idx[i]);
115  		if (!res || !res->base || !res->size)
116  			continue;
117  
118  		snprintf(name, sizeof(name), "IO%zXB", i);
119  		name[4] = '\0';
120  		acpigen_write_name_integer(name, res->base);
121  
122  		snprintf(name, sizeof(name), "IO%zXS", i);
123  		name[4] = '\0';
124  		acpigen_write_name_integer(name, res->size);
125  	}
126  }
127  
128  const char *superio_common_ldn_acpi_name(const struct device *dev)
129  {
130  	u8 ldn = dev->path.pnp.device & 0xff;
131  	u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
132  	static char name[5];
133  
134  	snprintf(name, sizeof(name), "L%02X%01X", ldn, vldn);
135  
136  	name[4] = '\0';
137  
138  	return name;
139  }
140  
141  static const char *name_from_hid(const char *hid)
142  {
143  	static const struct {
144  		const char *hid;
145  		const char *name;
146  	} lookup[] = {
147  		{ACPI_HID_FDC, "FDC" },
148  		{ACPI_HID_KEYBOARD, "PS2 Keyboard" },
149  		{ACPI_HID_MOUSE, "PS2 Mouse"},
150  		{ACPI_HID_COM, "COM port" },
151  		{ACPI_HID_LPT, "LPT" },
152  		{ACPI_HID_PNP, "Generic PNP device" },
153  	};
154  
155  	for (size_t i = 0; hid && i < ARRAY_SIZE(lookup); i++) {
156  		if (strcmp(hid, lookup[i].hid) == 0)
157  			return lookup[i].name;
158  	}
159  	return "Generic device";
160  }
161  
162  void superio_common_fill_ssdt_generator(const struct device *dev)
163  {
164  	if (!dev || !dev->upstream || !dev->upstream->dev) {
165  		printk(BIOS_CRIT, "BUG: Invalid argument in %s!\n", __func__);
166  		return;
167  	}
168  
169  	const char *scope = acpi_device_scope(dev);
170  	const char *name = acpi_device_name(dev);
171  	const u8 ldn = dev->path.pnp.device & 0xff;
172  	const u8 vldn = (dev->path.pnp.device >> 8) & 0x7;
173  	const char *hid;
174  
175  	/* Validate devicetree settings */
176  	bool bug = false;
177  	if (dev->upstream->dev->path.type != DEVICE_PATH_PNP) {
178  		bug = true;
179  		printk(BIOS_CRIT, "BUG: Parent of device %s is not a PNP device\n",
180  			dev_path(dev));
181  	} else if (dev->upstream->dev->path.pnp.port != dev->path.pnp.port) {
182  		bug = true;
183  		printk(BIOS_CRIT, "BUG: Parent of device %s has wrong I/O port\n",
184  			dev_path(dev));
185  	}
186  	if (bug) {
187  		printk(BIOS_CRIT, "BUG: Check your devicetree!\n");
188  		return;
189  	}
190  
191  	if (!scope || !name) {
192  		printk(BIOS_ERR, "%s: Missing ACPI path/scope\n", dev_path(dev));
193  		return;
194  	}
195  	if (vldn) {
196  		printk(BIOS_DEBUG, "%s: Ignoring virtual LDN\n", dev_path(dev));
197  		return;
198  	}
199  
200  	printk(BIOS_DEBUG, "%s.%s: %s\n", scope, name, dev_path(dev));
201  
202  	/* Scope */
203  	acpigen_write_scope(scope);
204  
205  	/* Device */
206  	acpigen_write_device(name);
207  
208  	acpi_device_write_uid(dev);
209  
210  	acpigen_write_name_byte("LDN", ldn);
211  	acpigen_write_name_byte("VLDN", vldn);
212  
213  	acpigen_write_method("_STA", 0);
214  	{
215  		acpigen_write_store();
216  		acpigen_emit_namestring("^^QLDN");
217  		acpigen_write_integer(ldn);
218  		acpigen_emit_byte(LOCAL0_OP);
219  
220  		/* Multiply (Local0, 0xf, Local0) */
221  		acpigen_emit_byte(MULTIPLY_OP);
222  		acpigen_emit_byte(LOCAL0_OP);
223  		acpigen_write_integer(0xf);
224  		acpigen_emit_byte(LOCAL0_OP);
225  
226  		acpigen_emit_byte(RETURN_OP);
227  		acpigen_emit_byte(LOCAL0_OP);
228  	}
229  	acpigen_pop_len(); /* Method */
230  
231  	/*
232  	 * The ACPI6.2 spec Chapter 6.1.5 requires to set a  _HID if no _ADR
233  	 * is present. Tests on Windows 10 showed that this is also true for
234  	 * disabled (_STA = 0) devices, otherwise it BSODs.
235  	 */
236  
237  	hid = acpi_device_hid(dev);
238  	if (!hid) {
239  		printk(BIOS_ERR, "%s: SuperIO driver doesn't provide a _HID\n", dev_path(dev));
240  		/* Try to guess it... */
241  		const struct superio_dev *sdev = superio_guess_function(dev);
242  		if (sdev && sdev->acpi_hid) {
243  			hid = sdev->acpi_hid;
244  			printk(BIOS_WARNING, "%s: Guessed _HID is '%s'\n", dev_path(dev), hid);
245  		} else {
246  			hid = ACPI_HID_PNP;
247  			printk(BIOS_ERR, "%s: Failed to guessed _HID\n", dev_path(dev));
248  		}
249  	}
250  
251  	acpigen_write_name_string("_HID", hid);
252  	acpigen_write_name_string("_DDN", name_from_hid(hid));
253  
254  	acpigen_write_method("_DIS", 0);
255  	{
256  		acpigen_emit_namestring("^^DLDN");
257  		acpigen_write_integer(ldn);
258  	}
259  	acpigen_pop_len(); /* Method */
260  
261  	if (dev->enabled && has_resources(dev)) {
262  		/* Resources - _CRS */
263  		acpigen_write_name("_CRS");
264  		acpigen_write_resourcetemplate_header();
265  		ldn_gen_resources(dev);
266  		acpigen_write_resourcetemplate_footer();
267  
268  		/* Resources - _PRS */
269  		acpigen_write_name("_PRS");
270  		acpigen_write_resourcetemplate_header();
271  		ldn_gen_resources(dev);
272  		acpigen_write_resourcetemplate_footer();
273  
274  		/* Resources base and size for 3rd party ACPI code */
275  		ldn_gen_resources_use(dev);
276  	}
277  
278  	acpigen_pop_len(); /* Device */
279  	acpigen_pop_len(); /* Scope */
280  }