brintos

brintos / linux-shallow public Read only

0
0
Text · 7.4 KiB · c1acfe6 Raw
280 lines · c
1/*2 * Copyright 2011 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24#include "mxms.h"25 26#include <core/option.h>27#include <subdev/bios.h>28#include <subdev/bios/mxm.h>29#include <subdev/i2c.h>30 31static bool32mxm_shadow_rom_fetch(struct nvkm_i2c_bus *bus, u8 addr,33		     u8 offset, u8 size, u8 *data)34{35	struct i2c_msg msgs[] = {36		{ .addr = addr, .flags = 0, .len = 1, .buf = &offset },37		{ .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, },38	};39 40	return i2c_transfer(&bus->i2c, msgs, 2) == 2;41}42 43static bool44mxm_shadow_rom(struct nvkm_mxm *mxm, u8 version)45{46	struct nvkm_device *device = mxm->subdev.device;47	struct nvkm_bios *bios = device->bios;48	struct nvkm_i2c *i2c = device->i2c;49	struct nvkm_i2c_bus *bus = NULL;50	u8 i2cidx, mxms[6], addr, size;51 52	i2cidx = mxm_ddc_map(bios, 1 /* LVDS_DDC */) & 0x0f;53	if (i2cidx < 0x0f)54		bus = nvkm_i2c_bus_find(i2c, i2cidx);55	if (!bus)56		return false;57 58	addr = 0x54;59	if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms)) {60		addr = 0x56;61		if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms))62			return false;63	}64 65	mxm->mxms = mxms;66	size = mxms_headerlen(mxm) + mxms_structlen(mxm);67	mxm->mxms = kmalloc(size, GFP_KERNEL);68 69	if (mxm->mxms &&70	    mxm_shadow_rom_fetch(bus, addr, 0, size, mxm->mxms))71		return true;72 73	kfree(mxm->mxms);74	mxm->mxms = NULL;75	return false;76}77 78#if defined(CONFIG_ACPI)79static bool80mxm_shadow_dsm(struct nvkm_mxm *mxm, u8 version)81{82	struct nvkm_subdev *subdev = &mxm->subdev;83	struct nvkm_device *device = subdev->device;84	static guid_t muid =85		GUID_INIT(0x4004A400, 0x917D, 0x4CF2,86			  0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65);87	u32 mxms_args[] = { 0x00000000 };88	union acpi_object argv4 = {89		.buffer.type = ACPI_TYPE_BUFFER,90		.buffer.length = sizeof(mxms_args),91		.buffer.pointer = (char *)mxms_args,92	};93	union acpi_object *obj;94	acpi_handle handle;95	int rev;96 97	handle = ACPI_HANDLE(device->dev);98	if (!handle)99		return false;100 101	/*102	 * spec says this can be zero to mean "highest revision", but103	 * of course there's at least one bios out there which fails104	 * unless you pass in exactly the version it supports..105	 */106	rev = (version & 0xf0) << 4 | (version & 0x0f);107	obj = acpi_evaluate_dsm(handle, &muid, rev, 0x00000010, &argv4);108	if (!obj) {109		nvkm_debug(subdev, "DSM MXMS failed\n");110		return false;111	}112 113	if (obj->type == ACPI_TYPE_BUFFER) {114		mxm->mxms = kmemdup(obj->buffer.pointer,115					 obj->buffer.length, GFP_KERNEL);116	} else if (obj->type == ACPI_TYPE_INTEGER) {117		nvkm_debug(subdev, "DSM MXMS returned 0x%llx\n",118			   obj->integer.value);119	}120 121	ACPI_FREE(obj);122	return mxm->mxms != NULL;123}124#endif125 126#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)127 128#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"129 130static u8131wmi_wmmx_mxmi(struct nvkm_mxm *mxm, u8 version)132{133	struct nvkm_subdev *subdev = &mxm->subdev;134	u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 };135	struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args };136	struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };137	union acpi_object *obj;138	acpi_status status;139 140	status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);141	if (ACPI_FAILURE(status)) {142		nvkm_debug(subdev, "WMMX MXMI returned %d\n", status);143		return 0x00;144	}145 146	obj = retn.pointer;147	if (obj->type == ACPI_TYPE_INTEGER) {148		version = obj->integer.value;149		nvkm_debug(subdev, "WMMX MXMI version %d.%d\n",150			   (version >> 4), version & 0x0f);151	} else {152		version = 0;153		nvkm_debug(subdev, "WMMX MXMI returned non-integer\n");154	}155 156	kfree(obj);157	return version;158}159 160static bool161mxm_shadow_wmi(struct nvkm_mxm *mxm, u8 version)162{163	struct nvkm_subdev *subdev = &mxm->subdev;164	u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 };165	struct acpi_buffer args = { sizeof(mxms_args), mxms_args };166	struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };167	union acpi_object *obj;168	acpi_status status;169 170	if (!wmi_has_guid(WMI_WMMX_GUID)) {171		nvkm_debug(subdev, "WMMX GUID not found\n");172		return false;173	}174 175	mxms_args[1] = wmi_wmmx_mxmi(mxm, 0x00);176	if (!mxms_args[1])177		mxms_args[1] = wmi_wmmx_mxmi(mxm, version);178	if (!mxms_args[1])179		return false;180 181	status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);182	if (ACPI_FAILURE(status)) {183		nvkm_debug(subdev, "WMMX MXMS returned %d\n", status);184		return false;185	}186 187	obj = retn.pointer;188	if (obj->type == ACPI_TYPE_BUFFER) {189		mxm->mxms = kmemdup(obj->buffer.pointer,190				    obj->buffer.length, GFP_KERNEL);191	}192 193	kfree(obj);194	return mxm->mxms != NULL;195}196#endif197 198static struct mxm_shadow_h {199	const char *name;200	bool (*exec)(struct nvkm_mxm *, u8 version);201} _mxm_shadow[] = {202	{ "ROM", mxm_shadow_rom },203#if defined(CONFIG_ACPI)204	{ "DSM", mxm_shadow_dsm },205#endif206#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)207	{ "WMI", mxm_shadow_wmi },208#endif209	{}210};211 212static int213mxm_shadow(struct nvkm_mxm *mxm, u8 version)214{215	struct mxm_shadow_h *shadow = _mxm_shadow;216	do {217		nvkm_debug(&mxm->subdev, "checking %s\n", shadow->name);218		if (shadow->exec(mxm, version)) {219			if (mxms_valid(mxm))220				return 0;221			kfree(mxm->mxms);222			mxm->mxms = NULL;223		}224	} while ((++shadow)->name);225	return -ENOENT;226}227 228static const struct nvkm_subdev_func229nvkm_mxm = {230};231 232int233nvkm_mxm_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,234	      struct nvkm_mxm **pmxm)235{236	struct nvkm_bios *bios = device->bios;237	struct nvkm_mxm *mxm;238	u8  ver, len;239	u16 data;240 241	if (!(mxm = *pmxm = kzalloc(sizeof(*mxm), GFP_KERNEL)))242		return -ENOMEM;243 244	nvkm_subdev_ctor(&nvkm_mxm, device, type, inst, &mxm->subdev);245 246	data = mxm_table(bios, &ver, &len);247	if (!data || !(ver = nvbios_rd08(bios, data))) {248		nvkm_debug(&mxm->subdev, "no VBIOS data, nothing to do\n");249		return 0;250	}251 252	nvkm_info(&mxm->subdev, "BIOS version %d.%d\n", ver >> 4, ver & 0x0f);253	nvkm_debug(&mxm->subdev, "module flags: %02x\n",254		   nvbios_rd08(bios, data + 0x01));255	nvkm_debug(&mxm->subdev, "config flags: %02x\n",256		   nvbios_rd08(bios, data + 0x02));257 258	if (mxm_shadow(mxm, ver)) {259		nvkm_warn(&mxm->subdev, "failed to locate valid SIS\n");260#if 0261		/* we should, perhaps, fall back to some kind of limited262		 * mode here if the x86 vbios hasn't already done the263		 * work for us (so we prevent loading with completely264		 * whacked vbios tables).265		 */266		return -EINVAL;267#else268		return 0;269#endif270	}271 272	nvkm_debug(&mxm->subdev, "MXMS Version %d.%d\n",273		   mxms_version(mxm) >> 8, mxms_version(mxm) & 0xff);274	mxms_foreach(mxm, 0, NULL, NULL);275 276	if (nvkm_boolopt(device->cfgopt, "NvMXMDCB", true))277		mxm->action |= MXM_SANITISE_DCB;278	return 0;279}280