From 32334cc03a12c51f64613608a8b261738406e1e2 Mon Sep 17 00:00:00 2001 From: Martin Peres Date: Sat, 18 Aug 2012 16:33:53 +0200 Subject: [PATCH] drm/nouveau/therm: use the EXTDEV table to detect i2c monitoring devices This commit also adds a static list of all known devices and their possible i2c addresses. v2: use the common table parsing technique as suggested by darktama Signed-off-by: Martin Peres Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/Makefile | 1 + .../drm/nouveau/core/include/subdev/bios/extdev.h | 30 +++++++ drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c | 100 +++++++++++++++++++++ drivers/gpu/drm/nouveau/nouveau_temp.c | 43 +++++++++ 4 files changed, 174 insertions(+) create mode 100644 drivers/gpu/drm/nouveau/core/include/subdev/bios/extdev.h create mode 100644 drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index b6a6fa7..6adf145 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -30,6 +30,7 @@ nouveau-y += core/subdev/bios/bit.o nouveau-y += core/subdev/bios/conn.o nouveau-y += core/subdev/bios/dcb.o nouveau-y += core/subdev/bios/dp.o +nouveau-y += core/subdev/bios/extdev.o nouveau-y += core/subdev/bios/gpio.o nouveau-y += core/subdev/bios/i2c.o nouveau-y += core/subdev/bios/init.o diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/extdev.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/extdev.h new file mode 100644 index 0000000..949fee3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/extdev.h @@ -0,0 +1,30 @@ +#ifndef __NVBIOS_EXTDEV_H__ +#define __NVBIOS_EXTDEV_H__ + +struct nouveau_bios; + +enum nvbios_extdev_type { + NVBIOS_EXTDEV_LM89 = 0x02, + NVBIOS_EXTDEV_VT1103M = 0x40, + NVBIOS_EXTDEV_PX3540 = 0x41, + NVBIOS_EXTDEV_VT1105M = 0x42, /* or close enough... */ + NVBIOS_EXTDEV_ADT7473 = 0x70, /* can also be a LM64 */ + NVBIOS_EXTDEV_HDCP_EEPROM = 0x90, + NVBIOS_EXTDEV_NONE = 0xff, +}; + +struct nvbios_extdev_func { + u8 type; + u8 addr; + u8 bus; +}; + +int +nvbios_extdev_parse(struct nouveau_bios *, int, struct nvbios_extdev_func *); + +int +nvbios_extdev_find(struct nouveau_bios *, enum nvbios_extdev_type, + struct nvbios_extdev_func *); + + +#endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c b/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c new file mode 100644 index 0000000..5afb568 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/extdev.c @@ -0,0 +1,100 @@ +/* + * Copyright 2012 Nouveau Community + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres + */ + +#include +#include +#include + +static u16 +extdev_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *len, u8 *cnt) +{ + u8 dcb_ver, dcb_hdr, dcb_cnt, dcb_len; + u16 dcb, extdev = 0; + + dcb = dcb_table(bios, &dcb_ver, &dcb_hdr, &dcb_cnt, &dcb_len); + if (!dcb || (dcb_ver != 0x30 && dcb_ver != 0x40)) + return 0x0000; + + extdev = nv_ro16(bios, dcb + 18); + if (!extdev) + return 0x0000; + + *ver = nv_ro08(bios, extdev + 0); + *hdr = nv_ro08(bios, extdev + 1); + *cnt = nv_ro08(bios, extdev + 2); + *len = nv_ro08(bios, extdev + 3); + + return extdev + *hdr; +} + +u16 +nvbios_extdev_entry(struct nouveau_bios *bios, int idx, u8 *ver, u8 *len) +{ + u8 hdr, cnt; + u16 extdev = extdev_table(bios, ver, &hdr, len, &cnt); + if (extdev && idx < cnt) + return extdev + idx * *len; + return 0x0000; +} + +static void +extdev_parse_entry(struct nouveau_bios *bios, u16 offset, + struct nvbios_extdev_func *entry) +{ + entry->type = nv_ro08(bios, offset + 0); + entry->addr = nv_ro08(bios, offset + 1); + entry->bus = (nv_ro08(bios, offset + 2) >> 4) & 1; +} + +int +nvbios_extdev_parse(struct nouveau_bios *bios, int idx, + struct nvbios_extdev_func *func) +{ + u8 ver, len; + u16 entry; + + if (!(entry = nvbios_extdev_entry(bios, idx, &ver, &len))) + return -EINVAL; + + extdev_parse_entry(bios, entry, func); + + return 0; +} + +int +nvbios_extdev_find(struct nouveau_bios *bios, enum nvbios_extdev_type type, + struct nvbios_extdev_func *func) +{ + u8 ver, len, i; + u16 entry; + + i = 0; + while (!(entry = nvbios_extdev_entry(bios, i++, &ver, &len))) { + extdev_parse_entry(bios, entry, func); + if (func->type == type) + return 0; + } + + return -EINVAL; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_temp.c b/drivers/gpu/drm/nouveau/nouveau_temp.c index 5465abf..6d2b957 100644 --- a/drivers/gpu/drm/nouveau/nouveau_temp.c +++ b/drivers/gpu/drm/nouveau/nouveau_temp.c @@ -31,6 +31,7 @@ #include #include +#include static int nv40_sensor_setup(struct drm_device *dev) @@ -132,16 +133,58 @@ static void nouveau_temp_probe_i2c(struct drm_device *dev) { struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_bios *bios = nouveau_bios(device); struct nouveau_i2c *i2c = nouveau_i2c(device); + struct nvbios_extdev_func extdev_entry; struct i2c_board_info info[] = { { I2C_BOARD_INFO("w83l785ts", 0x2d) }, { I2C_BOARD_INFO("w83781d", 0x2d) }, { I2C_BOARD_INFO("adt7473", 0x2e) }, + { I2C_BOARD_INFO("adt7473", 0x2d) }, + { I2C_BOARD_INFO("adt7473", 0x2c) }, { I2C_BOARD_INFO("f75375", 0x2e) }, { I2C_BOARD_INFO("lm99", 0x4c) }, + { I2C_BOARD_INFO("lm90", 0x4c) }, + { I2C_BOARD_INFO("lm90", 0x4d) }, + { I2C_BOARD_INFO("adm1021", 0x18) }, + { I2C_BOARD_INFO("adm1021", 0x19) }, + { I2C_BOARD_INFO("adm1021", 0x1a) }, + { I2C_BOARD_INFO("adm1021", 0x29) }, + { I2C_BOARD_INFO("adm1021", 0x2a) }, + { I2C_BOARD_INFO("adm1021", 0x2b) }, + { I2C_BOARD_INFO("adm1021", 0x4c) }, + { I2C_BOARD_INFO("adm1021", 0x4d) }, + { I2C_BOARD_INFO("adm1021", 0x4e) }, + { I2C_BOARD_INFO("lm63", 0x18) }, + { I2C_BOARD_INFO("lm63", 0x4e) }, { } }; + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_LM89, &extdev_entry)) { + struct i2c_board_info board[] = { + { I2C_BOARD_INFO("lm90", extdev_entry.addr >> 1) }, + { } + }; + + if (i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + board, probe_monitoring_device)) + return; + } + + if (!nvbios_extdev_find(bios, NVBIOS_EXTDEV_ADT7473, &extdev_entry)) { + struct i2c_board_info board[] = { + { I2C_BOARD_INFO("adt7473", extdev_entry.addr >> 1) }, + { } + }; + + if (i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", + board, probe_monitoring_device)) + return; + } + + /* The vbios doesn't provide the address of an exisiting monitoring + device. Let's try our static list. + */ i2c->identify(i2c, NV_I2C_DEFAULT(0), "monitoring device", info, probe_monitoring_device); } -- 2.7.4