139990074SConstantine A. Murenin /* $OpenBSD: kate.c,v 1.2 2008/03/27 04:52:03 cnst Exp $ */
239990074SConstantine A. Murenin
339990074SConstantine A. Murenin /*
439990074SConstantine A. Murenin * Copyright (c) 2008/2010 Constantine A. Murenin <cnst+dfly@bugmail.mojo.ru>
539990074SConstantine A. Murenin *
639990074SConstantine A. Murenin * Permission to use, copy, modify, and distribute this software for any
739990074SConstantine A. Murenin * purpose with or without fee is hereby granted, provided that the above
839990074SConstantine A. Murenin * copyright notice and this permission notice appear in all copies.
939990074SConstantine A. Murenin *
1039990074SConstantine A. Murenin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1139990074SConstantine A. Murenin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1239990074SConstantine A. Murenin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1339990074SConstantine A. Murenin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1439990074SConstantine A. Murenin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1539990074SConstantine A. Murenin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1639990074SConstantine A. Murenin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1739990074SConstantine A. Murenin */
1839990074SConstantine A. Murenin
1939990074SConstantine A. Murenin #include <sys/param.h>
2039990074SConstantine A. Murenin #include <sys/systm.h>
2139990074SConstantine A. Murenin #include <sys/bus.h>
2239990074SConstantine A. Murenin #include <sys/sensors.h>
2339990074SConstantine A. Murenin
241d855c79SSascha Wildner #include <machine/specialreg.h>
251d855c79SSascha Wildner
2639990074SConstantine A. Murenin #include <bus/pci/pcivar.h>
27dcb4b80dSSascha Wildner #include "pcidevs.h"
2839990074SConstantine A. Murenin
2939990074SConstantine A. Murenin
3039990074SConstantine A. Murenin /*
3139990074SConstantine A. Murenin * AMD NPT Family 0Fh Processors, Function 3 -- Miscellaneous Control
3239990074SConstantine A. Murenin */
3339990074SConstantine A. Murenin
3439990074SConstantine A. Murenin /* Function 3 Registers */
3539990074SConstantine A. Murenin #define K_THERMTRIP_STAT_R 0xe4
3639990074SConstantine A. Murenin #define K_NORTHBRIDGE_CAP_R 0xe8
3739990074SConstantine A. Murenin #define K_CPUID_FAMILY_MODEL_R 0xfc
3839990074SConstantine A. Murenin
3939990074SConstantine A. Murenin /* Bits within Thermtrip Status Register */
4039990074SConstantine A. Murenin #define K_THERM_SENSE_SEL (1 << 6)
4139990074SConstantine A. Murenin #define K_THERM_SENSE_CORE_SEL (1 << 2)
4239990074SConstantine A. Murenin
4339990074SConstantine A. Murenin /* Flip core and sensor selection bits */
4439990074SConstantine A. Murenin #define K_T_SEL_C0(v) (v |= K_THERM_SENSE_CORE_SEL)
4539990074SConstantine A. Murenin #define K_T_SEL_C1(v) (v &= ~(K_THERM_SENSE_CORE_SEL))
4639990074SConstantine A. Murenin #define K_T_SEL_S0(v) (v &= ~(K_THERM_SENSE_SEL))
4739990074SConstantine A. Murenin #define K_T_SEL_S1(v) (v |= K_THERM_SENSE_SEL)
4839990074SConstantine A. Murenin
4939990074SConstantine A. Murenin
5039990074SConstantine A. Murenin /*
5139990074SConstantine A. Murenin * Revision Guide for AMD NPT Family 0Fh Processors,
5239990074SConstantine A. Murenin * Publication # 33610, Revision 3.30, February 2008
5339990074SConstantine A. Murenin */
5439990074SConstantine A. Murenin static const struct {
5539990074SConstantine A. Murenin const char rev[5];
5639990074SConstantine A. Murenin const uint32_t cpuid[5];
5739990074SConstantine A. Murenin } kate_proc[] = {
5839990074SConstantine A. Murenin { "BH-F", { 0x00040FB0, 0x00040F80, 0, 0, 0 } }, /* F2 */
5939990074SConstantine A. Murenin { "DH-F", { 0x00040FF0, 0x00050FF0, 0x00040FC0, 0, 0 } }, /* F2, F3 */
6039990074SConstantine A. Murenin { "JH-F", { 0x00040F10, 0x00040F30, 0x000C0F10, 0, 0 } }, /* F2, F3 */
6139990074SConstantine A. Murenin { "BH-G", { 0x00060FB0, 0x00060F80, 0, 0, 0 } }, /* G1, G2 */
6239990074SConstantine A. Murenin { "DH-G", { 0x00070FF0, 0x00060FF0,
6339990074SConstantine A. Murenin 0x00060FC0, 0x00070FC0, 0 } } /* G1, G2 */
6439990074SConstantine A. Murenin };
6539990074SConstantine A. Murenin
6639990074SConstantine A. Murenin
6739990074SConstantine A. Murenin struct kate_softc {
68*5d302545SFrançois Tigeot device_t sc_dev;
6939990074SConstantine A. Murenin
7039990074SConstantine A. Murenin struct ksensor sc_sensors[4];
7139990074SConstantine A. Murenin struct ksensordev sc_sensordev;
7239990074SConstantine A. Murenin
7339990074SConstantine A. Murenin char sc_rev;
7439990074SConstantine A. Murenin int8_t sc_ii;
7539990074SConstantine A. Murenin int8_t sc_in;
761d855c79SSascha Wildner int32_t sc_flags;
771d855c79SSascha Wildner #define KATE_FLAG_ALT_OFFSET 0x04 /* CurTmp starts at -28C. */
7839990074SConstantine A. Murenin };
7939990074SConstantine A. Murenin
80*5d302545SFrançois Tigeot static void kate_identify(driver_t *, device_t);
81*5d302545SFrançois Tigeot static int kate_probe(device_t);
82*5d302545SFrançois Tigeot static int kate_attach(device_t);
83*5d302545SFrançois Tigeot static int kate_detach(device_t);
8439990074SConstantine A. Murenin static void kate_refresh(void *);
8539990074SConstantine A. Murenin
8639990074SConstantine A. Murenin static device_method_t kate_methods[] = {
8739990074SConstantine A. Murenin DEVMETHOD(device_identify, kate_identify),
8839990074SConstantine A. Murenin DEVMETHOD(device_probe, kate_probe),
8939990074SConstantine A. Murenin DEVMETHOD(device_attach, kate_attach),
9039990074SConstantine A. Murenin DEVMETHOD(device_detach, kate_detach),
9139990074SConstantine A. Murenin { NULL, NULL }
9239990074SConstantine A. Murenin };
9339990074SConstantine A. Murenin
9439990074SConstantine A. Murenin static driver_t kate_driver = {
9539990074SConstantine A. Murenin "kate",
9639990074SConstantine A. Murenin kate_methods,
9739990074SConstantine A. Murenin sizeof(struct kate_softc)
9839990074SConstantine A. Murenin };
9939990074SConstantine A. Murenin
10039990074SConstantine A. Murenin static devclass_t kate_devclass;
10139990074SConstantine A. Murenin
10239990074SConstantine A. Murenin DRIVER_MODULE(kate, hostb, kate_driver, kate_devclass, NULL, NULL);
10339990074SConstantine A. Murenin
10439990074SConstantine A. Murenin
10539990074SConstantine A. Murenin static void
kate_identify(driver_t * driver,device_t parent)106*5d302545SFrançois Tigeot kate_identify(driver_t *driver, device_t parent)
10739990074SConstantine A. Murenin {
10839990074SConstantine A. Murenin if (kate_probe(parent) == ENXIO)
10939990074SConstantine A. Murenin return;
11039990074SConstantine A. Murenin if (device_find_child(parent, driver->name, -1) != NULL)
11139990074SConstantine A. Murenin return;
11239990074SConstantine A. Murenin device_add_child(parent, driver->name, -1);
11339990074SConstantine A. Murenin }
11439990074SConstantine A. Murenin
11539990074SConstantine A. Murenin static int
kate_probe(device_t dev)116*5d302545SFrançois Tigeot kate_probe(device_t dev)
11739990074SConstantine A. Murenin {
11839990074SConstantine A. Murenin #ifndef KATE_STRICT
11939990074SConstantine A. Murenin struct kate_softc ks;
12039990074SConstantine A. Murenin struct kate_softc *sc = &ks;
12139990074SConstantine A. Murenin #endif
12239990074SConstantine A. Murenin uint32_t c;
12339990074SConstantine A. Murenin int i, j;
12439990074SConstantine A. Murenin
12539990074SConstantine A. Murenin if (pci_get_vendor(dev) != PCI_VENDOR_AMD ||
12639990074SConstantine A. Murenin pci_get_device(dev) != PCI_PRODUCT_AMD_AMD64_MISC)
12739990074SConstantine A. Murenin return ENXIO;
12839990074SConstantine A. Murenin
12939990074SConstantine A. Murenin /* just in case we probe successfully, set the description */
13039990074SConstantine A. Murenin if (device_get_desc(dev) == NULL)
13139990074SConstantine A. Murenin device_set_desc(dev,
13239990074SConstantine A. Murenin "AMD Family 0Fh temperature sensors");
13339990074SConstantine A. Murenin
13439990074SConstantine A. Murenin /*
13539990074SConstantine A. Murenin * First, let's probe for chips at or after Revision F, which is
13639990074SConstantine A. Murenin * when the temperature readings were officially introduced.
13739990074SConstantine A. Murenin */
13839990074SConstantine A. Murenin c = pci_read_config(dev, K_CPUID_FAMILY_MODEL_R, 4);
139c157ff7aSSascha Wildner for (i = 0; i < NELEM(kate_proc); i++)
14039990074SConstantine A. Murenin for (j = 0; kate_proc[i].cpuid[j] != 0; j++)
14139990074SConstantine A. Murenin if ((c & ~0xf) == kate_proc[i].cpuid[j])
14239990074SConstantine A. Murenin return 0;
14339990074SConstantine A. Murenin
14439990074SConstantine A. Murenin #ifndef KATE_STRICT
14539990074SConstantine A. Murenin /*
14639990074SConstantine A. Murenin * If the probe above was not successful, let's try to actually
14739990074SConstantine A. Murenin * read the sensors from the chip, and see if they make any sense.
14839990074SConstantine A. Murenin */
14939990074SConstantine A. Murenin sc->sc_ii = 0;
15039990074SConstantine A. Murenin sc->sc_in = 4;
15139990074SConstantine A. Murenin sc->sc_dev = dev;
15239990074SConstantine A. Murenin kate_refresh(sc);
15339990074SConstantine A. Murenin for (i = 0; i < 4; i++)
15439990074SConstantine A. Murenin if (!(sc->sc_sensors[i].flags & SENSOR_FINVALID))
15539990074SConstantine A. Murenin return 0;
15639990074SConstantine A. Murenin #endif /* !KATE_STRICT */
15739990074SConstantine A. Murenin
15839990074SConstantine A. Murenin return ENXIO;
15939990074SConstantine A. Murenin }
16039990074SConstantine A. Murenin
16139990074SConstantine A. Murenin static int
kate_attach(device_t dev)162*5d302545SFrançois Tigeot kate_attach(device_t dev)
16339990074SConstantine A. Murenin {
16439990074SConstantine A. Murenin struct kate_softc *sc;
16539990074SConstantine A. Murenin uint32_t c, d;
1661d855c79SSascha Wildner int i, j, cmpcap, model;
1671d855c79SSascha Wildner u_int regs[4], brand_id;
16839990074SConstantine A. Murenin
16939990074SConstantine A. Murenin sc = device_get_softc(dev);
17039990074SConstantine A. Murenin sc->sc_dev = dev;
17139990074SConstantine A. Murenin
17239990074SConstantine A. Murenin c = pci_read_config(dev, K_CPUID_FAMILY_MODEL_R, 4);
173c157ff7aSSascha Wildner for (i = 0; i < NELEM(kate_proc) && sc->sc_rev == '\0'; i++)
17439990074SConstantine A. Murenin for (j = 0; kate_proc[i].cpuid[j] != 0; j++)
17539990074SConstantine A. Murenin if ((c & ~0xf) == kate_proc[i].cpuid[j]) {
17639990074SConstantine A. Murenin sc->sc_rev = kate_proc[i].rev[3];
17781a9d7c8SConstantine A. Murenin device_printf(dev, "core rev %.4s%.1x\n",
17839990074SConstantine A. Murenin kate_proc[i].rev, c & 0xf);
17939990074SConstantine A. Murenin break;
18039990074SConstantine A. Murenin }
18139990074SConstantine A. Murenin
18239990074SConstantine A. Murenin if (c != 0x0 && sc->sc_rev == '\0') {
18339990074SConstantine A. Murenin /* CPUID Family Model Register was introduced in Revision F */
18439990074SConstantine A. Murenin sc->sc_rev = 'G'; /* newer than E, assume G */
18581a9d7c8SConstantine A. Murenin device_printf(dev, "cpuid 0x%x\n", c);
18639990074SConstantine A. Murenin }
18739990074SConstantine A. Murenin
1881d855c79SSascha Wildner model = CPUID_TO_MODEL(c);
1891d855c79SSascha Wildner if (model >= 0x60 && model != 0xc1) {
1901d855c79SSascha Wildner do_cpuid(0x80000001, regs);
1911d855c79SSascha Wildner brand_id = (regs[1] >> 9) & 0x1f;
1921d855c79SSascha Wildner
1931d855c79SSascha Wildner switch (model) {
1941d855c79SSascha Wildner case 0x68: /* Socket S1g1 */
1951d855c79SSascha Wildner case 0x6c:
1961d855c79SSascha Wildner case 0x7c:
1971d855c79SSascha Wildner break;
1981d855c79SSascha Wildner case 0x6b: /* Socket AM2 and ASB1 (2 cores) */
1991d855c79SSascha Wildner if (brand_id != 0x0b && brand_id != 0x0c)
2001d855c79SSascha Wildner sc->sc_flags |= KATE_FLAG_ALT_OFFSET;
2011d855c79SSascha Wildner break;
2021d855c79SSascha Wildner case 0x6f: /* Socket AM2 and ASB1 (1 core) */
2031d855c79SSascha Wildner case 0x7f:
2041d855c79SSascha Wildner if (brand_id != 0x07 && brand_id != 0x09 &&
2051d855c79SSascha Wildner brand_id != 0x0c)
2061d855c79SSascha Wildner sc->sc_flags |= KATE_FLAG_ALT_OFFSET;
2071d855c79SSascha Wildner break;
2081d855c79SSascha Wildner default:
2091d855c79SSascha Wildner sc->sc_flags |= KATE_FLAG_ALT_OFFSET;
2101d855c79SSascha Wildner }
2111d855c79SSascha Wildner }
2121d855c79SSascha Wildner
21339990074SConstantine A. Murenin d = pci_read_config(dev, K_NORTHBRIDGE_CAP_R, 4);
21439990074SConstantine A. Murenin cmpcap = (d >> 12) & 0x3;
21539990074SConstantine A. Murenin
21639990074SConstantine A. Murenin #ifndef KATE_STRICT
21739990074SConstantine A. Murenin sc->sc_ii = 0;
21839990074SConstantine A. Murenin sc->sc_in = 4;
21939990074SConstantine A. Murenin kate_refresh(sc);
22039990074SConstantine A. Murenin if (cmpcap == 0) {
22139990074SConstantine A. Murenin if ((sc->sc_sensors[0].flags & SENSOR_FINVALID) &&
22239990074SConstantine A. Murenin (sc->sc_sensors[1].flags & SENSOR_FINVALID))
22339990074SConstantine A. Murenin sc->sc_ii = 2;
224082d190bSMatthew Dillon if ((sc->sc_sensors[3].flags & SENSOR_FINVALID))
22539990074SConstantine A. Murenin sc->sc_in = 3;
22639990074SConstantine A. Murenin }
22739990074SConstantine A. Murenin #else
22839990074SConstantine A. Murenin sc->sc_ii = cmpcap ? 0 : 2;
22939990074SConstantine A. Murenin sc->sc_in = 4;
23039990074SConstantine A. Murenin #endif /* !KATE_STRICT */
23139990074SConstantine A. Murenin
23239990074SConstantine A. Murenin strlcpy(sc->sc_sensordev.xname, device_get_nameunit(dev),
23339990074SConstantine A. Murenin sizeof(sc->sc_sensordev.xname));
23439990074SConstantine A. Murenin
23539990074SConstantine A. Murenin for (i = sc->sc_ii; i < sc->sc_in; i++) {
23639990074SConstantine A. Murenin sc->sc_sensors[i].type = SENSOR_TEMP;
23739990074SConstantine A. Murenin sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
23839990074SConstantine A. Murenin }
23939990074SConstantine A. Murenin
2401bedd63aSSepherosa Ziehau sensor_task_register(sc, kate_refresh, 5);
24139990074SConstantine A. Murenin
24239990074SConstantine A. Murenin sensordev_install(&sc->sc_sensordev);
24339990074SConstantine A. Murenin return 0;
24439990074SConstantine A. Murenin }
24539990074SConstantine A. Murenin
24639990074SConstantine A. Murenin static int
kate_detach(device_t dev)247*5d302545SFrançois Tigeot kate_detach(device_t dev)
24839990074SConstantine A. Murenin {
24939990074SConstantine A. Murenin struct kate_softc *sc = device_get_softc(dev);
25039990074SConstantine A. Murenin
25139990074SConstantine A. Murenin sensordev_deinstall(&sc->sc_sensordev);
25239990074SConstantine A. Murenin sensor_task_unregister(sc);
25339990074SConstantine A. Murenin return 0;
25439990074SConstantine A. Murenin }
25539990074SConstantine A. Murenin
2560d4e06bcSSascha Wildner static void
kate_refresh(void * arg)25739990074SConstantine A. Murenin kate_refresh(void *arg)
25839990074SConstantine A. Murenin {
25939990074SConstantine A. Murenin struct kate_softc *sc = arg;
26039990074SConstantine A. Murenin struct ksensor *s = sc->sc_sensors;
26139990074SConstantine A. Murenin uint32_t t, m;
2628674b960SSascha Wildner int64_t temp;
26339990074SConstantine A. Murenin int i, v;
26439990074SConstantine A. Murenin
26539990074SConstantine A. Murenin t = pci_read_config(sc->sc_dev, K_THERMTRIP_STAT_R, 4);
26639990074SConstantine A. Murenin
26739990074SConstantine A. Murenin for (i = sc->sc_ii; i < sc->sc_in; i++) {
26839990074SConstantine A. Murenin switch(i) {
26939990074SConstantine A. Murenin case 0:
27039990074SConstantine A. Murenin K_T_SEL_C0(t);
27139990074SConstantine A. Murenin K_T_SEL_S0(t);
27239990074SConstantine A. Murenin break;
27339990074SConstantine A. Murenin case 1:
27439990074SConstantine A. Murenin K_T_SEL_C0(t);
27539990074SConstantine A. Murenin K_T_SEL_S1(t);
27639990074SConstantine A. Murenin break;
27739990074SConstantine A. Murenin case 2:
27839990074SConstantine A. Murenin K_T_SEL_C1(t);
27939990074SConstantine A. Murenin K_T_SEL_S0(t);
28039990074SConstantine A. Murenin break;
28139990074SConstantine A. Murenin case 3:
28239990074SConstantine A. Murenin K_T_SEL_C1(t);
28339990074SConstantine A. Murenin K_T_SEL_S1(t);
28439990074SConstantine A. Murenin break;
28539990074SConstantine A. Murenin }
28639990074SConstantine A. Murenin m = t & (K_THERM_SENSE_CORE_SEL | K_THERM_SENSE_SEL);
28739990074SConstantine A. Murenin pci_write_config(sc->sc_dev, K_THERMTRIP_STAT_R, t, 4);
28839990074SConstantine A. Murenin t = pci_read_config(sc->sc_dev, K_THERMTRIP_STAT_R, 4);
28939990074SConstantine A. Murenin v = 0x3ff & (t >> 14);
29039990074SConstantine A. Murenin #ifdef KATE_STRICT
29139990074SConstantine A. Murenin if (sc->sc_rev != 'G')
29239990074SConstantine A. Murenin v &= ~0x3;
29339990074SConstantine A. Murenin #endif /* KATE_STRICT */
29439990074SConstantine A. Murenin if ((t & (K_THERM_SENSE_CORE_SEL | K_THERM_SENSE_SEL)) == m &&
29539990074SConstantine A. Murenin (v & ~0x3) != 0)
29639990074SConstantine A. Murenin s[i].flags &= ~SENSOR_FINVALID;
29739990074SConstantine A. Murenin else
29839990074SConstantine A. Murenin s[i].flags |= SENSOR_FINVALID;
2998674b960SSascha Wildner temp = v * 250000;
3008674b960SSascha Wildner temp -= (sc->sc_flags & KATE_FLAG_ALT_OFFSET) != 0 ?
3011d855c79SSascha Wildner 28000000 : 49000000;
3028674b960SSascha Wildner temp += 273150000;
3038674b960SSascha Wildner s[i].value = temp;
30439990074SConstantine A. Murenin }
30539990074SConstantine A. Murenin }
306