0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024 #include "mxms.h"
0025
0026 #include <core/option.h>
0027 #include <subdev/bios.h>
0028 #include <subdev/bios/mxm.h>
0029 #include <subdev/i2c.h>
0030
0031 static bool
0032 mxm_shadow_rom_fetch(struct nvkm_i2c_bus *bus, u8 addr,
0033 u8 offset, u8 size, u8 *data)
0034 {
0035 struct i2c_msg msgs[] = {
0036 { .addr = addr, .flags = 0, .len = 1, .buf = &offset },
0037 { .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, },
0038 };
0039
0040 return i2c_transfer(&bus->i2c, msgs, 2) == 2;
0041 }
0042
0043 static bool
0044 mxm_shadow_rom(struct nvkm_mxm *mxm, u8 version)
0045 {
0046 struct nvkm_device *device = mxm->subdev.device;
0047 struct nvkm_bios *bios = device->bios;
0048 struct nvkm_i2c *i2c = device->i2c;
0049 struct nvkm_i2c_bus *bus = NULL;
0050 u8 i2cidx, mxms[6], addr, size;
0051
0052 i2cidx = mxm_ddc_map(bios, 1 ) & 0x0f;
0053 if (i2cidx < 0x0f)
0054 bus = nvkm_i2c_bus_find(i2c, i2cidx);
0055 if (!bus)
0056 return false;
0057
0058 addr = 0x54;
0059 if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms)) {
0060 addr = 0x56;
0061 if (!mxm_shadow_rom_fetch(bus, addr, 0, 6, mxms))
0062 return false;
0063 }
0064
0065 mxm->mxms = mxms;
0066 size = mxms_headerlen(mxm) + mxms_structlen(mxm);
0067 mxm->mxms = kmalloc(size, GFP_KERNEL);
0068
0069 if (mxm->mxms &&
0070 mxm_shadow_rom_fetch(bus, addr, 0, size, mxm->mxms))
0071 return true;
0072
0073 kfree(mxm->mxms);
0074 mxm->mxms = NULL;
0075 return false;
0076 }
0077
0078 #if defined(CONFIG_ACPI)
0079 static bool
0080 mxm_shadow_dsm(struct nvkm_mxm *mxm, u8 version)
0081 {
0082 struct nvkm_subdev *subdev = &mxm->subdev;
0083 struct nvkm_device *device = subdev->device;
0084 static guid_t muid =
0085 GUID_INIT(0x4004A400, 0x917D, 0x4CF2,
0086 0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65);
0087 u32 mxms_args[] = { 0x00000000 };
0088 union acpi_object argv4 = {
0089 .buffer.type = ACPI_TYPE_BUFFER,
0090 .buffer.length = sizeof(mxms_args),
0091 .buffer.pointer = (char *)mxms_args,
0092 };
0093 union acpi_object *obj;
0094 acpi_handle handle;
0095 int rev;
0096
0097 handle = ACPI_HANDLE(device->dev);
0098 if (!handle)
0099 return false;
0100
0101
0102
0103
0104
0105
0106 rev = (version & 0xf0) << 4 | (version & 0x0f);
0107 obj = acpi_evaluate_dsm(handle, &muid, rev, 0x00000010, &argv4);
0108 if (!obj) {
0109 nvkm_debug(subdev, "DSM MXMS failed\n");
0110 return false;
0111 }
0112
0113 if (obj->type == ACPI_TYPE_BUFFER) {
0114 mxm->mxms = kmemdup(obj->buffer.pointer,
0115 obj->buffer.length, GFP_KERNEL);
0116 } else if (obj->type == ACPI_TYPE_INTEGER) {
0117 nvkm_debug(subdev, "DSM MXMS returned 0x%llx\n",
0118 obj->integer.value);
0119 }
0120
0121 ACPI_FREE(obj);
0122 return mxm->mxms != NULL;
0123 }
0124 #endif
0125
0126 #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
0127
0128 #define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
0129
0130 static u8
0131 wmi_wmmx_mxmi(struct nvkm_mxm *mxm, u8 version)
0132 {
0133 struct nvkm_subdev *subdev = &mxm->subdev;
0134 u32 mxmi_args[] = { 0x494D584D , version, 0 };
0135 struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args };
0136 struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
0137 union acpi_object *obj;
0138 acpi_status status;
0139
0140 status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
0141 if (ACPI_FAILURE(status)) {
0142 nvkm_debug(subdev, "WMMX MXMI returned %d\n", status);
0143 return 0x00;
0144 }
0145
0146 obj = retn.pointer;
0147 if (obj->type == ACPI_TYPE_INTEGER) {
0148 version = obj->integer.value;
0149 nvkm_debug(subdev, "WMMX MXMI version %d.%d\n",
0150 (version >> 4), version & 0x0f);
0151 } else {
0152 version = 0;
0153 nvkm_debug(subdev, "WMMX MXMI returned non-integer\n");
0154 }
0155
0156 kfree(obj);
0157 return version;
0158 }
0159
0160 static bool
0161 mxm_shadow_wmi(struct nvkm_mxm *mxm, u8 version)
0162 {
0163 struct nvkm_subdev *subdev = &mxm->subdev;
0164 u32 mxms_args[] = { 0x534D584D , version, 0 };
0165 struct acpi_buffer args = { sizeof(mxms_args), mxms_args };
0166 struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
0167 union acpi_object *obj;
0168 acpi_status status;
0169
0170 if (!wmi_has_guid(WMI_WMMX_GUID)) {
0171 nvkm_debug(subdev, "WMMX GUID not found\n");
0172 return false;
0173 }
0174
0175 mxms_args[1] = wmi_wmmx_mxmi(mxm, 0x00);
0176 if (!mxms_args[1])
0177 mxms_args[1] = wmi_wmmx_mxmi(mxm, version);
0178 if (!mxms_args[1])
0179 return false;
0180
0181 status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
0182 if (ACPI_FAILURE(status)) {
0183 nvkm_debug(subdev, "WMMX MXMS returned %d\n", status);
0184 return false;
0185 }
0186
0187 obj = retn.pointer;
0188 if (obj->type == ACPI_TYPE_BUFFER) {
0189 mxm->mxms = kmemdup(obj->buffer.pointer,
0190 obj->buffer.length, GFP_KERNEL);
0191 }
0192
0193 kfree(obj);
0194 return mxm->mxms != NULL;
0195 }
0196 #endif
0197
0198 static struct mxm_shadow_h {
0199 const char *name;
0200 bool (*exec)(struct nvkm_mxm *, u8 version);
0201 } _mxm_shadow[] = {
0202 { "ROM", mxm_shadow_rom },
0203 #if defined(CONFIG_ACPI)
0204 { "DSM", mxm_shadow_dsm },
0205 #endif
0206 #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
0207 { "WMI", mxm_shadow_wmi },
0208 #endif
0209 {}
0210 };
0211
0212 static int
0213 mxm_shadow(struct nvkm_mxm *mxm, u8 version)
0214 {
0215 struct mxm_shadow_h *shadow = _mxm_shadow;
0216 do {
0217 nvkm_debug(&mxm->subdev, "checking %s\n", shadow->name);
0218 if (shadow->exec(mxm, version)) {
0219 if (mxms_valid(mxm))
0220 return 0;
0221 kfree(mxm->mxms);
0222 mxm->mxms = NULL;
0223 }
0224 } while ((++shadow)->name);
0225 return -ENOENT;
0226 }
0227
0228 static const struct nvkm_subdev_func
0229 nvkm_mxm = {
0230 };
0231
0232 int
0233 nvkm_mxm_new_(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
0234 struct nvkm_mxm **pmxm)
0235 {
0236 struct nvkm_bios *bios = device->bios;
0237 struct nvkm_mxm *mxm;
0238 u8 ver, len;
0239 u16 data;
0240
0241 if (!(mxm = *pmxm = kzalloc(sizeof(*mxm), GFP_KERNEL)))
0242 return -ENOMEM;
0243
0244 nvkm_subdev_ctor(&nvkm_mxm, device, type, inst, &mxm->subdev);
0245
0246 data = mxm_table(bios, &ver, &len);
0247 if (!data || !(ver = nvbios_rd08(bios, data))) {
0248 nvkm_debug(&mxm->subdev, "no VBIOS data, nothing to do\n");
0249 return 0;
0250 }
0251
0252 nvkm_info(&mxm->subdev, "BIOS version %d.%d\n", ver >> 4, ver & 0x0f);
0253 nvkm_debug(&mxm->subdev, "module flags: %02x\n",
0254 nvbios_rd08(bios, data + 0x01));
0255 nvkm_debug(&mxm->subdev, "config flags: %02x\n",
0256 nvbios_rd08(bios, data + 0x02));
0257
0258 if (mxm_shadow(mxm, ver)) {
0259 nvkm_warn(&mxm->subdev, "failed to locate valid SIS\n");
0260 #if 0
0261
0262
0263
0264
0265
0266 return -EINVAL;
0267 #else
0268 return 0;
0269 #endif
0270 }
0271
0272 nvkm_debug(&mxm->subdev, "MXMS Version %d.%d\n",
0273 mxms_version(mxm) >> 8, mxms_version(mxm) & 0xff);
0274 mxms_foreach(mxm, 0, NULL, NULL);
0275
0276 if (nvkm_boolopt(device->cfgopt, "NvMXMDCB", true))
0277 mxm->action |= MXM_SANITISE_DCB;
0278 return 0;
0279 }