Hi all running OpenBSD on Apple hardware:
I
was working on the asmc kernel driver for the Macbooks. I have a
MBPro8.2 (mid 2011), and came to get the driver installed, reading all
sensors and temperatures into the "hw.sensors" framework.
See
attached diffs. Beginning line 30 I describe the ideas for the driver.
Further information available, also a "todo list". Thx to Joshua@, who
helped me to create the diffs below, and provided input for the "todo
list". Looking forward to get some feedback, especially on other Apple
hardware.
rgds,
Volker
Index: dev/isa/asmc.c
===================================================================
RCS file: dev/isa/asmc.c
diff -N dev/isa/asmc.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ dev/isa/asmc.c 24 Jan 2014 20:24:38 -0000
@@ -0,0 +1,1317 @@
+/* $OpenBSD: asmc.c,v 0.1 2014/01/14 15:47:16 volker $ */
+/*
+ * Copyright (c) 2013, 2014 Volker Nowarra
+ * Complete rewrite of code in Nov/Dec 2013 from following sources:
+ *
+ * MACOSX:
+ * SmcFanControl.m by Hendrik Holtmann
+ * SmcCommand.c (no reference)
+ * FREEBSD driver by Rui Paulo <rpa...@freebsd.org>
+ * LINUX driver by Nicolas Boichat <nico...@boichat.ch> and
+ * Henrik Rydberg <rydb...@euromail.se>
+ * and the OpenBSD aps.c driver by Jonathan Gray <j...@openbsd.org> and
+ * Can Erkin Acar <cana...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for Apple's System Management Console (SMC). SMC can be found
+ * on the MacBook, MacBook Pro and Mac Mini, and possibly others ...
+ *
+ * Idea is to match / attach into the kernel, as defined in "man 8 config",
+ * section "EXAMPLES (kernel building)". The function asmc_match() would
+ * read the BIOS, compare it to a defined model in "*asmc_models[]", and
+ * if match, returns success. The asmc_attach() function would do the
+ * "man 9 bus_space" handling, and call on success asmc_init().
+ * asmc_init() tries to talk to the SMC chip, and if success prepares
+ * the sensors framework ("sysctl hw"), and sets up the the calls to
+ * the SMC chip to readsensor values. The Apple SMC has read and write
+ * areas, usually 10 read only values for FANs, 2 values for reading
+ * backlit, and 3 for motion sensors (SMS).
+ * Values can be written to change the FAN speeds. The different Apple
+ * Macs provide up to 36 temperatur sensors !
+ *
+ * Main driver for this development was reduction of code size into kernel,
+ * two binary integers are used for the sensors and temps. This way code
+ * size was reduced from 50KB down to 25KB, and to 15KB on i386.
+ * Details see "struct asmc_model".
+ */
+
+/*
+ * man style says, either param.h or types.h, but not both !
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/sensors.h>
+#include <sys/timeout.h>
+#include <sys/types.h>
+#include <sys/event.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+
+#include <machine/apmvar.h>
+#include <machine/biosvar.h>
+#include <machine/bus.h>
+#include <machine/smbiosvar.h>
+
+
+#define ASMCDEBUG
+#if defined(ASMCDEBUG)
+#define DPRINTF(x) do { printf x; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+/* ASMC DETAILED DEBUG :-) */
+#define XSMCDDEBUG
+#if defined(ASMCDDEBUG)
+#define DDPRINTF(x) do { printf x; } while (0)
+#else
+#define DDPRINTF(x)
+#endif
+
+
+/*
+ * Observed values, partially from Linux and FreeBSD driver
+ * data and command/status port used by Apple SMC
+ */
+
+#define ASMC_DATAPORT_OFFSET 0x00
+#define ASMC_CMDPORT_OFFSET 0x04
+#define ASMC_ADDR_SIZE 0x1f
+#define ASMC_MAX_DATA_LENGTH 32 /* 0x300-0x31f */
+
+/*
+ * Interrupt port
+ */
+#define ASMC_INTPORT_READ() bus_space_read_1(0x300, 0x00, ASMC_ADDR_SIZE)
+
+/*
+ * SMC command modes
+ */
+#define ASMC_READ_CMD 0x10
+#define ASMC_WRITE_CMD 0x11
+#define ASMC_GETKEYBYINDEX_CMD 0x12
+#define ASMC_GETKEYBYTYPE_CMD 0x13
+
+/*
+ * Timings
+ */
+#define ASMC_MIN_WAIT 0x0010
+#define ASMC_MAX_WAIT 0x8000
+#define ASMC_RETRY_WAIT 0x0400
+#define ASMC_REFRESH_RATE 7 /* in seconds */
+
+/*
+ * status from SMC chip
+ */
+#define ASMC_STATUS_02 0x02
+#define ASMC_STATUS_04 0x04
+#define ASMC_STATUS_05 0x05
+#define ASMC_STATUS_08 0x08
+#define ASMC_STATUS_0C 0x0c
+#define ASMC_STATUS_0e 0x0e
+#define ASMC_STATUS_MASK 0x0f
+
+/*
+ * Number of keys
+ */
+#define ASMC_NKEYS "#KEY" /* RO; 4 bytes */
+
+/*
+ * Clamshell
+ */
+#define ASMC_KEY_CLAMSHELL "MSLD" /* RO; 1 byte */
+
+/*
+ * Interrupt keys
+ */
+#define ASMC_KEY_INTOK "NTOK" /* WO; 1 byte */
+
+/*
+ * Fan control via SMC.
+ */
+#define ASMC_MAXFANS 2
+#define ASMC_FANCOUNT "FNum" /* RO; 1 byte */
+#define ASMC_FANMANUAL "FS! " /* RW; 2 bytes */
+
+/*
+ * Sudden Motion Sensor (SMS).
+ */
+#define ASMC_SMS_INIT1 0xe0
+#define ASMC_SMS_INIT2 0xf8
+#define ASMC_KEY_SMS "MOCN" /* RW; 2 bytes */
+#define ASMC_KEY_SMS_LOW "MOLT" /* RW; 2 bytes */
+#define ASMC_KEY_SMS_HIGH "MOHT" /* RW; 2 bytes */
+#define ASMC_KEY_SMS_LOW_INT "MOLD" /* RW; 1 byte */
+#define ASMC_KEY_SMS_HIGH_INT "MOHD" /* RW; 1 byte */
+#define ASMC_KEY_SMS_FLAG "MSDW" /* RW; 1 byte */
+#define ASMC_SMS_INTFF 0x60 /* Free fall Interrupt */
+#define ASMC_SMS_INTHA 0x6f /* High Acceleration Interrupt */
+#define ASMC_SMS_INTSH 0x80 /* Shock Interrupt */
+
+/*
+ * Keyboard backlight
+ */
+#define ASMC_KEY_LIGHTVALUE "LKSB" /* WO; 2 bytes */
+
+/*
+ * max no of read ony sensors into sensors framework (sysctl hw):
+ * 3 SMS, 10 Fans, 2 Lights, + max 36 Temps, to be verified ...
+ */
+#define ASMC_TOTAL_SENSORS 50
+
+
+/*
+ * OpenBSD Device driver interface.
+ */
+int asmc_match(struct device *, void *, void *);
+void asmc_attach(struct device *, struct device *, void *);
+
+struct asmc_softc {
+ struct device sc_dev; /* unclear ... */
+ bus_space_tag_t asmc_iot; /* bus io tag */
+ bus_space_handle_t asmc_ioh; /* io handler */
+ struct mutex sc_mutex; /* mutex */
+ int sc_nfan; /* number of fans */
+ struct ksensor sensors[ASMC_TOTAL_SENSORS];
+ struct ksensordev sensordev; /* kernel sensor device */
+};
+
+struct cfattach asmc_ca = {
+ sizeof(struct asmc_softc), asmc_match, asmc_attach
+};
+
+struct cfdriver asmc_cd = {
+ NULL, "asmc", DV_DULL
+};
+
+/*
+ * To integrate for automatic updates every x seconds,
+ * uncomment the next line ...
+ *
+ * struct timeout asmc_timeout;
+ */
+
+/*
+ * to convert numbers into binary values, only for debug purposes
+ */
+const char bit_rep[16][5] = {
+[ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
+[ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
+[ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
+[12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111"
+};
+
+/*
+ * descriptions of the Macs
+ */
+const char *asmc_descs[] = { "unknown",
+/* 1 */ "Apple SMC MacBook Core Duo",
+/* 2 */ "Apple SMC MacBook",
+/* 3 */ "Apple SMC MacBook Air",
+/* 4 */ "Apple SMC MacBook Pro Core Duo (15-inch)",
+/* 5 */ "Apple SMC MacBook Pro Core Duo (17-inch)",
+/* 6 */ "Apple SMC MacBook Pro Core 2 Duo (17-inch)",
+/* 7 */ "Apple SMC MacBook Pro Core 2 Duo (15-inch)",
+/* 8 */ "Apple SMC MacBook Pro Core 2 Duo (15-inch LED)",
+/* 9 */ "Apple SMC MacBook Pro Core 2 Duo (17-inch HD)",
+/* 10 */ "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
+/* 11 */ "Apple SMC MacBook Pro",
+/* 12 */ "Apple SMC Mac Mini",
+/* 13 */ "Apple SMC Mac Pro (8-core)"
+};
+
+/*
+ * descriptions of the SMC Versions
+ */
+const char *asmc_versions[] = { "unknown",
+"1.13f3", "1.15f3", "1.16f11", "1.18f5", "1.19f2",
+"1.1f5 ", "1.20f4", "1.23f20", "1.24f3", "1.25f4",
+"1.26f3", "1.27f2", "1.29f1", "1.2f10", "1.30f3",
+"1.31f1", "1.32f8", "1.33f8", "1.34f8", "1.35f1",
+"1.38f5", "1.39f5", "1.39f11", "1.3f4", "1.42f4",
+"1.47f2", "1.48f2", "1.49f2", "1.4f12", "1.51f53",
+"1.53f13", "1.54f36", "1.58f16", "1.5f10", "1.60f58",
+"1.62f6", "1.64f5", "1.66f54", "1.68f96", "1.69f1",
+"1.7f10", "1.8f2"
+};
+
+/*
+ * descriptions of the SMC types
+ */
+const char *asmc_types[] = { "unknown",
+"smc-napa", "smc-santarosa", "smc-mcp",
+"smc-piketon", "smc-huronriver", "smc-thurley"
+};
+
+/*
+ * all possible temp codes, names and descriptions
+ * temps are updated regularly, each ASMC_REFRESH_RATE
+ */
+const char *asmc_temp_field[] = {
+/* 1 */ "TA0P", "TA0P", "TA0P",
+/* 2 */ "TB0T", "enclosure", "Enclosure Bottom",
+/* 3 */ "TC0C", "TC0C", "TC0C",
+/* 4 */ "TC0D", "cpu", "CPU Temperature Diode",
+/* 5 */ "TC0P", "cpu2", "CPU Point 2",
+/* 6 */ "TC1C", "TC1C", "TC1C",
+/* 7 */ "TC1D", "TC1D", "TC1D",
+/* 8 */ "TC2C", "TC2C", "TC2C",
+/* 9 */ "TC2D", "TC2D", "TC2D",
+/* 10 */ "TC3C", "TC3C", "TC3C",
+/* 11 */ "TC3D", "TC3D", "TC3D",
+/* 12 */ "TCAG", "TCAG", "TCAG",
+/* 13 */ "TCAH", "TCAH", "TCAH",
+/* 14 */ "TCBG", "TCBG", "TCBG",
+/* 15 */ "TCBH", "TCBH", "TCBH",
+/* 16 */ "TG0D", "graphics", "Graphics Chip Diode",
+/* 17 */ "TG0H", "graphicssink", "Graphics Chip Heatsink",
+/* 18 */ "TG0P", "graphicssink", "Graphics Heatsink",
+/* 19 */ "TG0T", "unknown", "Unknown" ,
+/* 20 */ "Th0H", "heatsink1", "Main Heatsink 1",
+/* 21 */ "TH0P", "TH0P", "TH0P",
+/* 22 */ "Th1H", "heatsink2", "Main Heatsink 2",
+/* 23 */ "TH1P", "TH1P", "TH1P",
+/* 24 */ "Th2H", "heatsink3", "Main Heatsink 3",
+/* 25 */ "TH2P", "TH2P", "TH2P",
+/* 26 */ "TH3P", "TH3P", "TH3P",
+/* 27 */ "THTG", "THTG", "THTG",
+/* 28 */ "TM0P", "memory", "Memory Bank A",
+/* 29 */ "Tm0P", "memory", "Memory Controller",
+/* 30 */ "TM0P", "TM0P", "TM0P",
+/* 31 */ "TM0S", "TM0S", "TM0S",
+/* 32 */ "TM1P", "TM1P", "TM1P",
+/* 33 */ "TM1S", "TM1S", "TM1S",
+/* 34 */ "TM2P", "TM2P", "TM2P",
+/* 35 */ "TM2S", "TM2S", "TM2S",
+/* 36 */ "TM3S", "TM3S", "TM3S",
+/* 37 */ "TM8P", "TM8P", "TM8P",
+/* 38 */ "TM8S", "TM8S", "TM8S",
+/* 39 */ "TM9P", "TM9P", "TM9P",
+/* 40 */ "TM9S", "TM9S", "TM9S",
+/* 41 */ "TMAP", "TMAP", "TMAP",
+/* 42 */ "TMAS", "TMAS", "TMAS",
+/* 43 */ "TMBS", "TMBS", "TMBS",
+/* 44 */ "TN0H", "TN0H", "TN0H",
+/* 45 */ "TN0P", "northbridge1", "Northbridge Point 1",
+/* 46 */ "TN1P", "northbridge2", "Northbridge Point 2",
+/* 47 */ "TS0C", "TS0C", "TS0C",
+/* 48 */ "Ts0P", "unknown1", "Unknown",
+/* 49 */ "TTF0", "unknown2", "Unknown",
+/* 50 */ "TW0P", "wireless", "Wireless Module",
+
+/* 63 */ NULL, NULL, NULL
+};
+
+/*
+ * these sensors are updated regularly, each ASMC_REFRESH_RATE
+ */
+const char *asmc_sensors_reg[] = {
+/* 1 RO; 2 bytes */ "MO_X", "sms_x", "sms_x",
+/* 2 RO; 2 bytes */ "MO_Y", "sms_y", "sms_y",
+/* 3 RO; 2 bytes */ "MO_Z", "sms_z", "sms_z",
+/* 4 RO; 2 bytes */ "F0Ac", "fan0", "fan0_speed",
+/* 5 RO; 2 bytes */ "F1Ac", "fan1", "fan1_speed",
+/* 6 RW; 2 bytes */ "F0Tg", "fan0", "fan0_targetspeed",
+/* 7 RW; 2 bytes */ "F1Tg", "fan1", "fan1_targetspeed",
+/* 8 RO; 2 bytes */ "ALV0", "lightright", "lightright",
+/* 9 RO; 2 bytes */ "ALV1", "lightleft", "lightleft",
+/* 10 */
+/* 16 */ NULL, NULL, NULL
+};
+
+/*
+ * sensor codes, names and descriptions, used in reading
+ * the SMC and updating the sensors framework ("sysctl hw")
+ * init sensors are only update once on init procedure
+ */
+const char *asmc_sensors_irreg[] = {
+/* 1 RO; 2 bytes */ "F0Sf", "fan0", "fan0_safespeed",
+/* 2 RO; 2 bytes */ "F1Sf", "fan1", "fan1_safespeed",
+/* 3 RO; 2 bytes */ "F0Mn", "fan0", "fan0_minspeed",
+/* 4 RO; 2 bytes */ "F1Mn", "fan1", "fan1_minspeed",
+/* 5 RO; 2 bytes */ "F0Mx", "fan0", "fan0_maxspeed",
+/* 6 RO; 2 bytes */ "F1Mx", "fan1", "fan1_maxspeed",
+/* 7 */
+/* 8 */ NULL, NULL, NULL
+};
+
+
+/******************* DRIVER RELATED FUNCTIONS ******************************
+ *
+ * OpenBSD I/O functions.
+ */
+static int asmc_wait_status(struct asmc_softc *sc, uint8_t val);
+static int asmc_bus_write(struct asmc_softc *sc, int offset, int cmd);
+static int asmc_read_key(struct asmc_softc *sc, const char *key, int *buffer,
int len);
+/*
+ * AppleSMC related functions.
+ */
+static int asmc_update_sensors(struct asmc_softc *sc, bool status);
+static int asmc_get_sensors(struct asmc_softc *sc);
+void asmc_refresh(void *arg);
+static int asmc_init(struct asmc_softc *sc);
+
+/******************* SMC ATTRIBUTES PER COMPUTER ***************************
+ *
+ * This section prepares all options (SMS, fans, temperatures, ...)
+ */
+
+struct asmc_model {
+ const char *asmc_model; /* smbios unique name */
+ const uint8_t asmc_desc; /* system description */
+ const uint8_t asmc_version; /* SMC driver version */
+ const uint8_t asmc_type; /* SMC type */
+ const uint8_t asmc_sensors_irreg; /* sensor (init) */
+ const uint16_t asmc_sensors_reg; /* sensor (regularly) */
+ const uint64_t asmc_temps; /* temperatures */
+
+ /* Not every model has the same sensors (temps, fans, */
+ /* or SMS). Thus capabilities are reflected in */
+ /* binaries. The binary can be easily extended ... */
+ /* Each bit reflects the following capability: */
+ /* */
+ /* for temperatures: */
+ /* there are roughly 50 sensors defined ... */
+ /* asmc_temps[1] = "TA0P", "TA0P" */
+ /* asmc_temps[2] = "enclosure", "Enclosure Bottom" */
+ /* asmc_temps[3] = "TC0C", "TC0C" */
+ /* asmc_temps[4] = "cpu", "CPU Temperature Diode" */
+ /* asmc_temps[5] = "cpu2", "CPU Point 2" */
+ /* ... */
+ /* asmc_temps[45] = "northbridge1", "Northbridge 1" */
+ /* asmc_temps[46] = "northbridge2", "Northbridge 2" */
+ /* asmc_temps[47] = "TS0C", "TS0C" */
+ /* asmc_temps[48] = "unknown1", "Unknown" */
+ /* asmc_temps[49] = "unknown2", "Unknown" */
+ /* asmc_temps[50] = "wireless", "Wireless Module" */
+ /* asmc_temps[51] = "unknown", "Unknown" */
+ /* asmc_temps[52-63] = free for future usage */
+ /* */
+ /* the regularly updated sensors */
+ /* asmc_sensors_reg[0] = sms_x x shock motion sensor */
+ /* asmc_sensors_reg[1] = sms_y y shock motion sensor */
+ /* asmc_sensors_reg[2] = sms_z z shock motion sensor */
+ /* asmc_sensors_reg[3] = fan0_speed */
+ /* asmc_sensors_reg[4] = fan1_speed */
+ /* asmc_sensors_reg[5] = fan0_targetspeed */
+ /* asmc_sensors_reg[6] = fan1_targetspeed */
+ /* asmc_sensors_reg[7] = lightright */
+ /* asmc_sensors_reg[8] = lightleft */
+ /* asmc_sensors_reg[9-15] = free for future usage */
+ /* */
+ /* the "one time" updated sensors (at driver init) */
+ /* asmc_sensors_irreg[0] = fan0_safespeed */
+ /* asmc_sensors_irreg[1] = fan1_safespeed */
+ /* asmc_sensors_irreg[2] = fan0_minspeed */
+ /* asmc_sensors_irreg[3] = fan1_minspeed */
+ /* asmc_sensors_irreg[4] = fan0_maxspeed */
+ /* asmc_sensors_irreg[5] = fan1_maxspeed */
+ /* asmc_sensors_irreg[6-7] = free for future usage */
+ /* */
+};
+
+uint8_t asmc_model_index; /* index into asmc_model structure */
+struct asmc_model asmc_models[] = {
+ /********************************************************
+ * to convert binary and decimal values, use bc:
+ * $ echo "obase=2;63" | bc
+ * 111111
+ * $ echo "ibase=2;111111" | bc
+ * 63
+ ********************************************************/
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (127 = 00000000 01111111)
+ * asmc_temps[0-63] ( 10 = 000...00 00001010)
+ * MAC BOOK (no backlights!):
+ ********************************************************/
+ { "MacBook1,1", 1, 29, 1, 63, 127, 10 },
+ { "MacBook2,1", 1, 1, 1, 63, 127, 10 },
+ { "MacBook3,1", 1, 9, 1, 63, 127, 10 },
+ { "MacBook4,1", 2, 16, 2, 63, 127, 10 },
+ { "MacBook5,1", 2, 17, 3, 63, 127, 10 },
+ { "MacBook5,2", 2, 21, 3, 63, 127, 10 },
+ { "MacBook6,1", 2, 30, 3, 63, 127, 10 },
+ { "MacBook7,1", 2, 30, 3, 63, 127, 10 },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (511 = 00000001 11111111)
+ * asmc_temps[0-63], ( 10 = 000...00 00001010)
+ * MacBook Air (yes, they do have backlit !):
+ ********************************************************/
+ { "MacBookAir1,1", 3, 8, 2, 63, 511, 10 },
+ { "MacBookAir2,1", 3, 19, 3, 63, 511, 10 },
+ { "MacBookAir3,1", 3, 35, 3, 63, 511, 10 },
+ { "MacBookAir3,2", 3, 38, 3, 63, 511, 10 },
+ { "MacBookAir4,1", 3, 0, 0, 63, 511, 10 },
+ { "MacBookAir5,1", 3, 0, 0, 63, 511, 10 },
+ { "MacBookAir5,2", 3, 0, 0, 63, 511, 10 },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 60 = 00111100 )
+ * asmc_sensors_reg[0-15], (511 = 00000001 11111111)
+ * asmc_temps[0-63], 985162698031130 =
+ * 11 10000000 00000000 00010000 10101001 10000000 00011010
+ * MacBook Pro:
+ ********************************************************/
+ { "MacBookPro1,1", 4, 14, 1, 63, 511, 268402687 },
+ { "MacBookPro1,2", 5, 34, 1, 63, 511, 268402687 },
+ { "MacBookPro2,1", 6, 4, 1, 63, 511, 268402687 },
+ { "MacBookPro2,2", 7, 4, 2, 63, 511, 268402687 },
+ { "MacBookPro3,1", 8, 3, 0, 63, 511, 268402687 },
+ { "MacBookPro3,2", 9, 0, 0, 63, 511, 268402687 },
+ { "MacBookPro4,1", 10, 10, 2, 63, 511, 268402687 },
+ { "MacBookPro5,1", 11, 26, 3, 63, 511, 268402687 },
+ { "MacBookPro5,2", 11, 25, 3, 63, 511, 268402687 },
+ { "MacBookPro5,3", 11, 27, 3, 63, 511, 268402687 },
+ { "MacBookPro5,4", 11, 28, 3, 63, 511, 268402687 },
+ { "MacBookPro5,5", 11, 33, 3, 63, 511, 268402687 },
+ { "MacBookPro6,1", 11, 36, 4, 63, 511, 268402687 },
+ { "MacBookPro6,2", 11, 33, 4, 63, 511, 268402687 },
+ { "MacBookPro7,1", 11, 40, 3, 63, 511, 268402687 },
+ { "MacBookPro8,1", 10, 39, 5, 60, 511, 703687720730650LL },
+ { "MacBookPro8,2", 10, 39, 5, 60, 511, 703687720730650LL },
+ { "MacBookPro8,3", 11, 39, 5, 60, 511, 703687720730650LL },
+ { "MacBookPro9,1", 11, 0, 0, 60, 511, 703687720730650LL },
+ { "MacBookPro9,2", 11, 0, 0, 60, 511, 703687720730650LL },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (120 = 00000000 01111000)
+ * asmc_temps[0-63], ( 10 = 000...00 00001010)
+ * Mac Mini (has no SMS and no Backlight)
+ ********************************************************/
+ { "MacMini1,1", 13, 24, 1, 63, 120, 10 },
+ { "MacMini2,1", 13, 5, 1, 63, 120, 10 },
+ { "MacMini3,1", 13, 20, 3, 63, 120, 10 },
+ { "Macmini4,1", 13, 0, 0, 63, 120, 10 },
+ { "Macmini5,1", 13, 0, 0, 63, 120, 10 },
+ { "Macmini5,2", 13, 0, 0, 63, 120, 10 },
+ { "Macmini5,3", 13, 0, 0, 63, 120, 10 },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (120 = 00000000 01111000)
+ * asmc_temps[0-63], 10 = 000...00 00001010
+ * MacPro (has no SMS and no Backlight):
+ *******************************************************/
+ { "MacPro1,1", 14, 41, 1, 63, 120, 10 },
+ { "MacPro2,1", 14, 2, 1, 63, 120, 10 },
+ { "MacPro3,1", 14, 10, 1, 63, 120, 10 },
+ { "MacPro4,1", 14, 22, 6, 63, 120, 10 },
+ { "MacPro5,1", 14, 23, 6, 63, 120, 10 },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (120 = 00000000 01111000)
+ * asmc_temps[0-63]
+ * iMac 9.1: version "IM91.88Z.008D.B08.0904271717"
+ *******************************************************/
+ { "iMac4,1", 0, 6, 1, 63, 120, 10 },
+ { "iMac4,2", 0, 6, 1, 63, 120, 10 },
+ { "iMac5,1", 0, 42, 1, 63, 120, 10 },
+ { "iMac5,2", 0, 42, 1, 63, 120, 10 },
+ { "iMac6,1", 0, 42, 2, 63, 120, 10 },
+ { "iMac7,1", 0, 7, 2, 63, 120, 10 },
+ { "iMac8,1", 0, 13, 2, 63, 120, 10 },
+ { "iMac9,1", 0, 15, 1, 63, 120, 10 },
+ { "iMac10,1", 0, 31, 3, 63, 120, 10 },
+ { "iMac11,1", 0, 32, 4, 63, 120, 10 },
+ { "iMac11,2", 0, 37, 4, 63, 120, 10 },
+ { "iMac12,1", 0, 18, 5, 63, 120, 10 },
+ { "iMac12,2", 0, 18, 5, 63, 120, 10 },
+
+ /********************************************************
+ * MODEL, DESC, SMC_Version, SMC_Type,
+ * asmc_sensors_irreg[0-7], ( 63 = 00111111 )
+ * asmc_sensors_reg[0-15], (120 = 00000000 01111000)
+ * asmc_temps[0-63]
+ * XServers ...
+ *******************************************************/
+ { "Xserve1,1", 0, 11, 1, 63, 120, 10 },
+ { "Xserve2,1", 0, 11, 1, 63, 120, 10 },
+
+ { "UNKNOWN" },
+ { NULL }
+};
+
+/******************* DRIVER SUPPORT FUNCTIONS BEGIN HERE *********************
+ *
+ * asmc_update_sensors - update the sensors framework by (re-) reading
+ * current sensor values.
+ * status=0 if called from init function
+ * (also update "one time sensors", as defined in *asmc_sensors_irreg[])
+ * status=1 if called asmc_refresh function
+ * (update only regular sensors, as defined in *asmc_sensors_reg[])
+ */
+static int
+asmc_update_sensors(struct asmc_softc *sc, bool status)
+{
+ int buffer[6], val;
+ uint8_t i, k, sensor_number;
+ uint64_t j;
+
+ struct asmc_model *model;
+
+ DPRINTF(("ASMC_UPDATE_SENSORS\n"));
+
+ model = &asmc_models[asmc_model_index];
+
+ /*************************************************
+ * Read the possible TEMPERATURES
+ *************************************************/
+ DPRINTF((" ** TEMPERATURES \n"));
+ /*
+ * run through all temp types and update the values in sysctl
+ * framework "sysctl hw". The asmc_temps bitfield is sizeof(uint64_t)
+ * variable, so asmc_temps is 8 bytes, multiply it by 8 (bits)
+ * to loop through all the 64 bits. For each match call asmc_read_key.
+ */
+ sensor_number=1;
+ j=1;
+ for (i=0; i<8*sizeof(model->asmc_temps); i++) {
+ k=i*3;
+ if (model->asmc_temps & j) {
+ if (strlen(asmc_temp_field[k])) {
+ DPRINTF((" ** %s, %s, %s \n", asmc_temp_field[k],
+ asmc_temp_field[k+1],
+ asmc_temp_field[k+2]));
+ if(asmc_read_key(sc, asmc_temp_field[k], buffer, 2)) {
+ DPRINTF((" ** Error updating sensor %s. \n",
+ asmc_temp_field[k]));
+ sc->sensors[sensor_number].flags &= ~SENSOR_FUNKNOWN;
+ sc->sensors[sensor_number].status = SENSOR_S_UNKNOWN;
+ val = 0;
+ } else
+ val = buffer[0];
+ DPRINTF((" * %s = %d, sensor_number=%d \n",
+ asmc_temp_field[k], val, sensor_number));
+ sc->sensors[sensor_number++].value = val;
+ }
+ }
+ j = j << 1;
+ } /* end for loop */
+
+ /*
+ * run through all regularly updatable sensors and update the values
+ * in sysctl framework "sysctl hw". The sensors bitfield is
+ * sizeof(uint16_t) variable, so "asmc_sensors_reg" is two bytes,
+ * multiply it by 8 (bits) to loop through each of the 16 bits.
+ * Call for each match asmc_read_key.
+ */
+ DPRINTF((" ** REG-SENSORS \n"));
+ j=1;
+ for (i=0; i<8*sizeof(model->asmc_sensors_reg); i++) {
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ k=i*3;
+ if (model->asmc_sensors_reg & j) {
+ if (strlen(asmc_sensors_reg[k])) {
+ DPRINTF((" ** %s, %s, %s \n", asmc_sensors_reg[k],
+ asmc_sensors_reg[k+1],
+ asmc_sensors_reg[k+2]));
+ /********************************************
+ * Read X/Y/Z motion sensors. tbd!
+ * if (i<3) type=SENSOR_ACCEL;
+ *********************************************/
+ if (i<3) {
+ if(asmc_read_key(sc, asmc_sensors_reg[k], buffer, 2)) {
+ DPRINTF((" ** Error updating SMS sensor %s. \n",
+ asmc_sensors_reg[k]));
+ sc->sensors[sensor_number].flags &= ~SENSOR_FUNKNOWN;
+ sc->sensors[sensor_number].status = SENSOR_S_UNKNOWN;
+ val = 0;
+ } else {
+ val = ((int16_t)buffer[0] << 8);
+ }
+ }
+
+ /********************************************
+ * Read FANs
+ *********************************************/
+ if (i>=3 && i<=6) {
+ if(asmc_read_key(sc, asmc_sensors_reg[k], buffer, 2)) {
+ DPRINTF((" ** Error updating FAN sensor %s. \n",
+ asmc_sensors_reg[k]));
+ sc->sensors[sensor_number].flags &= ~SENSOR_FUNKNOWN;
+ sc->sensors[sensor_number].status = SENSOR_S_UNKNOWN;
+ val = 0;
+ } else {
+ val = (buffer[0] << 6) | (buffer[1] >> 2);
+ }
+ }
+
+ /********************************************
+ * Read lights
+ *********************************************/
+ if (i>=7 && i<=8) {
+ if(asmc_read_key(sc, asmc_sensors_reg[k], buffer, 6)) {
+ DPRINTF((" ** Error updating light sensor %s. \n",
+ asmc_sensors_reg[k]));
+ sc->sensors[sensor_number].flags &= ~SENSOR_FUNKNOWN;
+ sc->sensors[sensor_number].status = SENSOR_S_UNKNOWN;
+ val = 0;
+ } else {
+ val = buffer[2];
+ }
+ }
+ DPRINTF((" * %s = %d, sensor_number=%d \n",
+ asmc_sensors_reg[k], val, sensor_number));
+ sc->sensors[sensor_number++].value = val;
+ }
+ }
+ j = j << 1;
+ } /* end sensors for() loop */
+
+ /*
+ * run through all "only on init updated" sensors and put the values
+ * in the sysctl framework "sysctl hw". The sensors bitfield is
+ * sizeof(uint8_t) variable, so "asmc_sensors_irreg" is one byte,
+ * multiply it by 8 (bits) to loop through each of the 8 bits.
+ * Call for each match asmc_read_key.
+ * update only, if function parameter "status" = 0
+ */
+ DPRINTF((" ** IRREG-SENSORS, Status = %d\n", status));
+ if ( status ) {
+ j=1;
+ for (i=0; i<8*sizeof(model->asmc_sensors_irreg); i++) {
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+ buffer[3] = 0;
+ k=i*3;
+ if (model->asmc_sensors_irreg & j) {
+ if (strlen(asmc_sensors_irreg[k])) {
+ DPRINTF((" ** %s, %s, %s \n", asmc_sensors_irreg[k],
+ asmc_sensors_irreg[k+1],
+ asmc_sensors_irreg[k+2]));
+ if(asmc_read_key(sc, asmc_sensors_irreg[k], buffer, 2))
{
+ DPRINTF((" ** Error updating sensor %s. \n",
+ asmc_sensors_irreg[k]));
+ sc->sensors[sensor_number].flags &= ~SENSOR_FUNKNOWN;
+ sc->sensors[sensor_number].status = SENSOR_S_UNKNOWN;
+ val = 0;
+ } else
+ val = (buffer[0] << 6) | (buffer[1] >> 2);
+ DPRINTF((" * %s = %d, sensor_number=%d \n",
+ asmc_sensors_irreg[k], val, sensor_number));
+ sc->sensors[sensor_number++].value = val;
+ } /* end if strlen ... */
+ } /* end if model... & j */
+ j = j << 1;
+ } /* end "one time" sensors for() loop */
+ } else {
+ DPRINTF(("*** not in IRREG-SENSORS, Status = %d\n", status));
+ } /* end if status = ... */
+ return 0;
+}
+
+
+/*
+ * asmc_get_sensors - get the possible sensors from the asmc structure.
+ * The sensors are defined in several fields, every bit indicates an
+ * existing sensor. Not all models have the same amount or
+ * types of sensors. So need to get the binary
+ * (asmc_models[asmc_model_index].asmc_sensors) and run through it bit
+ * by bit. The variable "j" points for all temperatures and sensors into
+ * the corresponding arrays, to get the names or descriptions of the
+ * sensors and temps (from the asmcvar.h file).
+ */
+static int
+asmc_get_sensors(struct asmc_softc *sc)
+{
+ uint64_t j=1;
+ uint32_t i, k=0;
+ uint8_t bitval, sensor_number;
+
+ struct asmc_model *model;
+
+ DPRINTF(("ASMC_GET_SENSORS\n"));
+
+ bitval=0;
+ model = &asmc_models[asmc_model_index];
+
+ DDPRINTF((" asmc_model=%s\n", model->asmc_model));
+ DDPRINTF((" asmc_temp=%llu\n ", model->asmc_temps));
+ bitval = (model->asmc_temps >> 56) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 48) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 40) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 32) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 24) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 16) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps >> 8) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_temps) & 0xff;
+ DDPRINTF(("%s%s\n", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+
+ DDPRINTF((" asmc_sensors_reg=%d, ", model->asmc_sensors_reg));
+ bitval = (model->asmc_sensors_reg >> 8) & 0xff;
+ DDPRINTF(("%s%s ", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+ bitval = (model->asmc_sensors_reg) & 0xff;
+ DDPRINTF(("%s%s\n", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+
+ DDPRINTF((" asmc_sensors_irreg=%d, ", model->asmc_sensors_irreg));
+ bitval = (model->asmc_sensors_reg) & 0xff;
+ DDPRINTF(("%s%s\n", bit_rep[bitval >> 4], bit_rep[bitval & 0x0F]));
+
+ /*
+ * find a temperature by running through the asmc_temps bitfield.
+ * "model->asmc_temps" is a sizeof(uint64_t) variable, multiply by
+ * 8 (bits) to loop through each of the 64 bits. If bit is set,
+ * assign "aspplesmc" to the sensordev.xname. Add to the current
+ * sensor (sc->sensor) the type and attach it to sc->sensordev.
+ */
+ sensor_number=1;
+ i=0;
+ j=1;
+ k=0;
+ for (i=0; i<8*sizeof(model->asmc_temps); i++) {
+ k=i*3;
+ if (model->asmc_temps & j) {
+ DDPRINTF((" Temps: bit[%d], j=%llu, ", i, j));
+ DDPRINTF(("k=%d ", k));
+ strlcpy(sc->sensordev.xname, "applesmc",
sizeof(sc->sensordev.xname));
+ DDPRINTF(("--> "));
+ if (strlen(asmc_temp_field[k])) {
+ DDPRINTF(("%s, %s",
+ asmc_temp_field[k+1],
+ asmc_temp_field[k+2]));
+ /* sc->sensors[sensor_number].type=SENSOR_TEMP; */
+ sc->sensors[sensor_number].type=SENSOR_INTEGER;
+ strlcpy(sc->sensors[sensor_number].desc,
asmc_temp_field[k+2], sizeof(sc->sensors[sensor_number].desc));
+ }
+ DDPRINTF(("\n"));
+ /* All possible temps have been read, we attach to the
+ * sensors framework, and set all values to "invalid". */
+ sensor_attach(&sc->sensordev, &sc->sensors[sensor_number]);
+ sc->sensors[sensor_number].flags &= ~SENSOR_FINVALID;
+ sensor_number++;
+ }
+ j = j << 1;
+ }
+
+ /*
+ * find a "to be regularly updated" sensor by running through the
+ * "asmc_sensors_reg" bitfield. "model->asmc_temps" is a
+ * sizeof(uint16_t) variable, multiply by 8 (bits) to loop through
+ * each of the 16 bits. If bit is set, assign "aspplesmc" to the
+ * sensordev.xname. Add to the current sensor (sc->sensor) the type
+ * and attach it to sc->sensordev.
+ */
+ i=0;
+ j=1;
+ k=0;
+ for (i=0; i<8*sizeof(model->asmc_sensors_reg); i++) {
+ k=i*3;
+ if (model->asmc_sensors_reg & j) {
+ DDPRINTF((" Sensors: bit[%d], j=%llu, k=%d ", i, j, k));
+ strlcpy(sc->sensordev.xname, "applesmc",
+ sizeof(sc->sensordev.xname));
+ DDPRINTF(("--> "));
+ if (strlen(asmc_sensors_reg[k])) {
+ if (i<3)
+ sc->sensors[sensor_number].type=SENSOR_ACCEL;
+ if (i>=3 && i<=6)
+ sc->sensors[sensor_number].type=SENSOR_FANRPM;
+ if (i>=7 && i<=8)
+ sc->sensors[sensor_number].type=SENSOR_LUX;
+ DDPRINTF(("%s, %s\n",
+ asmc_sensors_reg[k+1],
+ asmc_sensors_reg[k+2]));
+ strlcpy(sc->sensors[sensor_number].desc,
+ asmc_sensors_reg[k+2],
+ sizeof(sc->sensors[sensor_number].desc));
+ }
+ /*
+ * All regularly updatable sensors have been read, we attach
+ * to the sensors framework, and set all values to "invalid".
+ */
+ sensor_attach(&sc->sensordev, &sc->sensors[sensor_number]);
+ sc->sensors[sensor_number].flags &= ~SENSOR_FINVALID;
+ sensor_number++;
+ }
+ j = j << 1;
+ } /* end for() loop */
+
+ /*
+ * find all irregularly updated sensors by running through the
+ * "asmc_sensors_irreg" bitfield. "model->asmc_sensors_irreg" is a
+ * sizeof(uint8_t) variable, multiply by 8 (bits) to loop through
+ * each of the 8 bits. If bit is set, assign "aspplesmc" to the
+ * sensordev.xname. Add to the current sensor (sc->sensor) the type
+ * and attach it to sc->sensordev.
+ */
+ i=0;
+ j=1;
+ k=0;
+ for (i=0; i<8*sizeof(model->asmc_sensors_irreg); i++) {
+ k=i*3;
+ if (model->asmc_sensors_irreg & j) {
+ DDPRINTF((" Sensors: bit[%d], j=%llu, k=%d ", i, j, k));
+ strlcpy(sc->sensordev.xname, "applesmc",
+ sizeof(sc->sensordev.xname));
+ DDPRINTF(("--> "));
+ if (strlen(asmc_sensors_irreg[k])) {
+ sc->sensors[sensor_number].type=SENSOR_FANRPM;
+ DDPRINTF(("%s, %s\n",
+ asmc_sensors_irreg[k+1],
+ asmc_sensors_irreg[k+2]));
+ strlcpy(sc->sensors[sensor_number].desc,
+ asmc_sensors_irreg[k+2],
+ sizeof(sc->sensors[sensor_number].desc));
+ }
+ /* All "one time" sensors have been read, we attach to the
+ * sensors framework, and set all values to "invalid". */
+ sensor_attach(&sc->sensordev, &sc->sensors[sensor_number]);
+ sc->sensors[sensor_number].flags &= ~SENSOR_FINVALID;
+ sensor_number++;
+ }
+ j = j << 1;
+ } /* end for() loop */
+
+ DDPRINTF(("\n"));
+ DDPRINTF(("sensordev_install(&sc->sensordev);\n"));
+ sensordev_install(&sc->sensordev);
+ return 0;
+}
+
+
+/*
+ * asmc_refresh - update the sensors regularly.
+ * The timeout_set() function reuires the refresh
+ * routine to be a void() function.
+ */
+void
+asmc_refresh(void *arg)
+{
+ struct asmc_softc *sc = (struct asmc_softc *)arg;
+
+ if (asmc_update_sensors(sc,NULL))
+ DPRINTF(("*** ERROR in update_sensors section ... \n"));
+
+ /* timeout_add_sec(&asmc_timeout, ASMC_REFRESH_RATE); */
+}
+
+/***************************************************************************
+ *
+ * asmc commands - to initialize, read and write values to the SMC
+ *
+ ***************************************************************************/
+
+
+/*
+ * asmc_wait_status - Wait for the status port to come back with a certain
+ * value (masked with 0x0f), returning zero if the value is obtained.
+ */
+static int
+asmc_wait_status(struct asmc_softc *sc, uint8_t val)
+{
+ int i, status;
+
+ DDPRINTF((" WAIT_STATUS, expected val=0x%02x,", val));
+ val = val & ASMC_STATUS_MASK;
+ DDPRINTF((" mask with 0x%02x: 0x%02x,", ASMC_STATUS_MASK, val));
+
+ for (i = ASMC_MIN_WAIT; i < ASMC_MAX_WAIT; i <<= 1) {
+ delay(ASMC_RETRY_WAIT); /* wait & resend */
+ mtx_enter(&sc->sc_mutex);
+ status=bus_space_read_1(sc->asmc_iot, sc->asmc_ioh,
ASMC_CMDPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ if (status & ASMC_STATUS_02) /* wait for smc to settle */
+ continue;
+ if (status & ASMC_STATUS_04) { /* cmd accepted, good ! */
+ DDPRINTF((" status=%s%s, *ok*\n", bit_rep[val >> 4],
bit_rep[val & 0x0F]));
+ return(0);
+ }
+ if (status & ASMC_STATUS_08) /* wait for smc to settle */
+ continue;
+ if (status & ASMC_STATUS_0e) /* wait for smc to settle */
+ continue;
+ if (i >= ASMC_MAX_WAIT) { /* timeout: give up */
+ DPRINTF(("\n ** failed, status=%s%s \n", bit_rep[val >> 4],
bit_rep[val & 0x0F]));
+ break;
+ }
+ DDPRINTF(("i=%d, status=0x%02x; ", i, status));
+ }
+ if ( i >= ASMC_MAX_WAIT ) DDPRINTF((" \n"));
+ DPRINTF(("*** wait status failed: status 0x%02x != expected value
0x%02x\n", status, val));
+ return -EIO;
+}
+
+
+/*
+ * asmc_bus_write - send command to write to a port
+ */
+static int
+asmc_bus_write(struct asmc_softc *sc, int offset, int val)
+{
+ DDPRINTF((" bus_space_write(iot0x%02x, ioh0x%02x, offset0x%02x,
val=0x%02x) \n", sc->asmc_iot, sc->asmc_ioh, offset, val));
+ mtx_enter(&sc->sc_mutex);
+ bus_space_write_1(sc->asmc_iot, sc->asmc_ioh, offset, val);
+ mtx_leave(&sc->sc_mutex);
+ if (asmc_wait_status(sc, ASMC_STATUS_0C)) {
+ DPRINTF(("*** Error getting result for 0x%02x\n", val));
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/*
+ * asmc_read_key - reads len bytes from ASMC_DATAPORT_OFFSET
+ * This is "the" major function of the APPLE SMC driver.
+ * To read sensors or temps, a four letter code must be sent to
+ * the SMC chip's I/O address. Procedure is like this:
+ * 1: write to the CMD port (0x304), that we want to read something
+ * 2: write to the data port (0x300) the four letter code
+ * 3: write the length ("int len") to the data port (0x300)
+ * 4: read the result from the data port (0x300)
+ * The SMC will reply with status codes for each activity, which are
+ * captured in the asmc_wait_status() routine.
+ * The result goes into our buffer.
+ */
+static int
+asmc_read_key(struct asmc_softc *sc, const char *key, int *buffer, int len)
+{
+ int i,y;
+ uint8_t status, data;
+
+ DDPRINTF((" ASMC_READ_KEY\n"));
+
+ status=data=0;
+
+ if (len > ASMC_ADDR_SIZE) {
+ DPRINTF(("*** Error: len is too big: %db \n", ASMC_ADDR_SIZE));
+ return -EINVAL;
+ }
+
+ DDPRINTF((" STEP1: asmc_bus_write, to send ASMC_READ_CMD\n"));
+ if(asmc_bus_write(sc, ASMC_CMDPORT_OFFSET, ASMC_READ_CMD))
+ return -EIO;
+
+ DDPRINTF((" STEP2: asmc_bus_write to send 4 letter code\n"));
+ for (i=0; i<4; i++) {
+ y=key[i];
+ DDPRINTF((" key[%d]=%s,",i,&y));
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, y))
+ return -EIO;
+ if (asmc_wait_status(sc, ASMC_STATUS_04))
+ return -EIO;
+ }
+ DDPRINTF((" STEP3: asmc_bus_write for length result, length=%d\n",
len));
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, len))
+ return -EIO;
+ DDPRINTF((" STEP4: wait with 0x05, and read result to buffer\n"));
+ for (i=0; i<len; i++) {
+ if (asmc_wait_status(sc, ASMC_STATUS_05))
+ return -EIO;
+ DDPRINTF((" bus_space_read:"));
+ mtx_enter(&sc->sc_mutex);
+ buffer[i] = bus_space_read_1(sc->asmc_iot,
+ sc->asmc_ioh, ASMC_DATAPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ DDPRINTF((" len=%d, buffer[%d]=%d\n", len, i, buffer[i]));
+ }
+ DDPRINTF((" buf[0]=%d, buf[1]=%d, buf[2]=%d, buf[3]=%d, buf[4]=%d,
buf[5]=%d\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],
buffer[5]));
+
+ /* read data port until bit 0 is cleared */
+ DDPRINTF((" read data port until bit 0 is cleared, "));
+ for (i=0; i<16; i++) {
+ delay(ASMC_RETRY_WAIT);
+ mtx_enter(&sc->sc_mutex);
+ status=bus_space_read_1(sc->asmc_iot, sc->asmc_ioh,
ASMC_CMDPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ if (!(status & 0x01))
+ break;
+ mtx_enter(&sc->sc_mutex);
+ data=bus_space_read_1(sc->asmc_iot, sc->asmc_ioh,
ASMC_DATAPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ }
+ if (i)
+ DDPRINTF((" flushed %d bytes, last value is: %d", i, data));
+ DDPRINTF(("\n"));
+ return 0;
+}
+
+
+static int
+asmc_init(struct asmc_softc *sc)
+{
+ uint8_t index[4];
+ uint8_t i;
+ int buffer[6], val;
+
+ char key[5] = { 0 };
+ char type[7] = { 0 };
+
+ struct asmc_model *model;
+
+ DPRINTF(("ASMC_INIT \n"));
+ model = &asmc_models[asmc_model_index];
+
+ /*************************************************
+ * Read the number of keys (32 ?)
+ *
+ * XXX: The number of keys is a 32 bit buffer, but
+ * right now Apple only uses the last 8 bit.
+ *************************************************/
+ DPRINTF((" ** ASMC_NKEYS\n"));
+ if (asmc_read_key(sc, ASMC_NKEYS, buffer, 4)) {
+ DPRINTF((" ** no value for ASMC_NKEYS, trying again ... \n"));
+ delay(ASMC_RETRY_WAIT);
+ if (asmc_read_key(sc, ASMC_NKEYS, buffer, 4))
+ DPRINTF(("*** Error in ASMC_NKEYS. \n"));
+ } else
+ DPRINTF((" * number of keys = %d\n", buffer[3]));
+
+ /*************************************************
+ * ok, generally we can speak to the SMC. Before
+ * preparing the sensors framework, need to be
+ * sure if we have fans in the system. All MACs
+ * have FANs, haven't they ?
+ * If so, we can continue reading all other sensors.
+ *
+ * Read the Number of FANs
+ *************************************************/
+ DPRINTF((" ** ASMC_FANCOUNT\n"));
+ if (asmc_read_key(sc, ASMC_FANCOUNT, buffer, 1)) {
+ DPRINTF((" ** no value for FANCOUNT, retrying ... \n"));
+ delay(ASMC_RETRY_WAIT);
+ if (asmc_read_key(sc, ASMC_FANCOUNT, buffer, 1)) {
+ DPRINTF(("*** Error in FANCOUNT. \n"));
+ return -EIO;
+ }
+ }
+ sc->sc_nfan = buffer[0];
+ DPRINTF((" * FANCOUNT=%d\n", sc->sc_nfan));
+
+ if ( sc->sc_nfan > ASMC_MAXFANS ) {
+ DPRINTF((" * FANCOUNT: %d exceeds MAX_FANCOUNT %d, setting FAN
count to 1\n", sc->sc_nfan, ASMC_MAXFANS));
+ sc->sc_nfan = 1;
+ }
+
+ /*************************************************
+ * ok, SMC replies to our requests in an expected
+ * manner, let's prepare the sensors framework,
+ * before reading all other sensors
+ *************************************************/
+ if(asmc_get_sensors(sc)) {
+ DPRINTF(("*** Error getting sensors ... \n"));
+ return -EIO;
+ }
+
+ /*************************************************
+ * and read the sensors values at the same time
+ *************************************************/
+ if (asmc_update_sensors(sc,1)) {
+ DPRINTF(("*** ERROR in update_sensors section ... \n"));
+ return 0;
+ }
+/*
+ timeout_set(&asmc_timeout, asmc_refresh, sc);
+ timeout_add_sec(&asmc_timeout, ASMC_REFRESH_RATE);
+*/
+ return 0;
+
+/*************************************************
+ * these functions exist in the FREEBSD driver,
+ * usage/return values currently unclear ...
+ *************************************************
+ * The function "GetKeybyIndex" is called in the
+ * FREEBSD driver with val=1;val<=100;val++ ...
+ * that should be 1 to keycount ?
+ *************************************************/
+ /*************************************************
+ * Get Key by Index
+ *************************************************/
+ val = 1;
+ index[0] = (val >> 24) & 0xff;
+ index[1] = (val >> 16) & 0xff;
+ index[2] = (val >> 8) & 0xff;
+ index[3] = (val) & 0xff;
+ DPRINTF(("*** GET KEY BY INDEX ***\n"));
+
+ if(asmc_bus_write(sc, ASMC_CMDPORT_OFFSET, ASMC_GETKEYBYINDEX_CMD))
+ DPRINTF((" ** Could not GET KEY BY INDEX ... \n"));
+ for (i = 0; i < 4; i++) {
+ val=index[i];
+ DDPRINTF(("FOR LOOP, index[%d]=%s%s \n", i, bit_rep[val >> 4],
bit_rep[val & 0x0F]));
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, index[i])) {
+ return -EIO;
+ }
+ if (asmc_wait_status(sc, ASMC_STATUS_04)) {
+ return -EIO;
+ }
+ }
+ DDPRINTF((" After for loop...\n"));
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, 4)) {
+ DPRINTF((" ** Could not GET KEY BY INDEX ... \n"));
+ }
+ for (i = 0; i < 4; i++) {
+ if (asmc_wait_status(sc, ASMC_STATUS_05)) {
+ return -EIO;
+ }
+ mtx_enter(&sc->sc_mutex);
+ key[i]=bus_space_read_1(sc->asmc_iot, sc->asmc_ioh,
ASMC_DATAPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ DPRINTF((" key[%d]=0x%02x\n", i, key[i]));
+ }
+ DPRINTF(("\n"));
+
+ /*************************************************
+ * Get Key by Type
+ *************************************************/
+ DPRINTF(("*** GET KEY BY TYPE ***\n"));
+ if(asmc_bus_write(sc, ASMC_CMDPORT_OFFSET, ASMC_GETKEYBYTYPE_CMD))
+ DPRINTF((" ** Could not GET KEY BY TYPE ... \n"));
+ for (i = 0; i < 4; i++) {
+ val=key[i];
+ DDPRINTF((" FOR LOOP, key[%d]=0x%02x, %s%s \n", i, key[i],
bit_rep[val >> 4], bit_rep[val & 0x0F]));
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, key[i]))
+ return -EIO;
+ if (asmc_wait_status(sc, ASMC_STATUS_04))
+ return -EIO;
+ }
+ if(asmc_bus_write(sc, ASMC_DATAPORT_OFFSET, 6))
+ DPRINTF((" ** Could not GET KEY BY TYPE ... \n"));
+ for (i = 0; i < 6; i++) {
+ if (asmc_wait_status(sc, ASMC_STATUS_05)) {
+ return -EIO;
+ }
+ mtx_enter(&sc->sc_mutex);
+ type[i]=bus_space_read_1(sc->asmc_iot, sc->asmc_ioh,
ASMC_DATAPORT_OFFSET);
+ mtx_leave(&sc->sc_mutex);
+ DPRINTF((" type[%d]=0x%02x\n", i, type[i]));
+ }
+ DPRINTF(("\n"));
+ return 0;
+}
+
+
+/******************* OpenBSD DRIVER HOOKS BEGIN HERE ************************
+ *
+ * This section implements asmc driver hooks into the kernel
+ ****************************************************************************/
+int
+asmc_match(struct device *parent, void *match, void *aux)
+{
+ /*
+ * We read the BIOS tables for our system, and get the
+ * vendor / product. Then we run through our asmc_models[]
+ * structure, and try to find a matching product.
+ */
+ unsigned i;
+ char bios_string[64]; /* the BIOS model */
+ struct smbios_sys *sbsys;
+ struct smbtable bios;
+ struct asmc_model *model;
+
+ DPRINTF(("ASMC_MATCH \n"));
+
+ if (smbios_find_table(SMBIOS_TYPE_SYSTEM, &bios)) {
+ sbsys = bios.tblhdr;
+ if ((smbios_get_string(&bios, sbsys->vendor, bios_string,
+ sizeof(bios_string))) != NULL)
+ DPRINTF((" BIOS: vendor %s,", bios_string));
+ if ((smbios_get_string(&bios, sbsys->product, bios_string,
+ sizeof(bios_string))) != NULL)
+ DPRINTF((" product %s\n", bios_string));
+ } else {
+ DPRINTF((" ASMC: could not find BIOS\n"));
+ return -EIO;
+ } /* end BIOS request */
+
+ /*
+ * now run through all models and see
+ * if there is something matching...
+ */
+ for (i=0; asmc_models[i].asmc_model; i++) {
+ asmc_model_index = i;
+ model = &asmc_models[i];
+ if (strncmp("UNKNOWN", model->asmc_model, strlen("UNKNOWN"))) {
+ if (!strncmp(bios_string, model->asmc_model,
strlen(bios_string))) {
+ DPRINTF((" asmc_models[%d]=%s;",
+ i, model->asmc_model));
+ DDPRINTF((" %d; %d; %d\n",
+ model->asmc_desc,
+ model->asmc_version,
+ model->asmc_type));
+ return (1);
+ }
+ } else {
+ DPRINTF((" ** ERROR: no model found for %s\n", bios_string));
+ return -EIO;
+ }
+ } /* end for loop */
+ DPRINTF((" ** ERROR: ASMC_MATCH: shouldn't reach here ..."));
+ return -EIO;
+}
+
+
+void
+asmc_attach(struct device *parent, struct device *self, void *aux)
+{
+ int iobase;
+
+ struct isa_attach_args *ia = aux;
+ struct asmc_softc *sc = (void *)self;
+
+ DPRINTF(("\nASMC_ATTACH\n"));
+
+ bus_space_handle_t ioh;
+ bus_space_tag_t iot = ia->ia_iot;
+ iobase = ia->ipa_io[0].base;
+ iot = sc->asmc_iot = ia->ia_iot;
+
+ DDPRINTF((" bus_space_map: iot:0x%02x, iobase:0x%02x, ASMC_ADDR_SIZE:
0x%02x \n", iot, iobase, ASMC_ADDR_SIZE));
+ if (bus_space_map(iot, iobase, ASMC_ADDR_SIZE, 0, &sc->asmc_ioh)) {
+ DPRINTF(("asmc attach: can't map i/o space\n"));
+ return;
+ }
+ ioh = sc->asmc_ioh;
+ mtx_init(&sc->sc_mutex, IPL_BIO);
+
+ /* see if we get an answer from the chip */
+ if (asmc_init(sc))
+ DPRINTF(("could not asmc_init correctly\n"));
+
+ bus_space_unmap(sc->asmc_iot, iobase, ASMC_ADDR_SIZE);
+ /* not sure, why this is required, but if not, the next line
+ * is collated to the asmc status line ... */
+ printf("\n");
+}
+
+
Index: dev/isa/files.isa
===================================================================
RCS file: /cvs/src/sys/dev/isa/files.isa,v
retrieving revision 1.112
diff -u -p -u -r1.112 files.isa
--- dev/isa/files.isa 5 Apr 2013 07:25:26 -0000 1.112
+++ dev/isa/files.isa 24 Jan 2014 20:24:38 -0000
@@ -350,6 +350,11 @@ device aps
attach aps at isa
file dev/isa/aps.c aps
+# Apple Macintosh SMC (System Management Controller)
+device asmc
+attach asmc at isa
+file dev/isa/asmc.c asmc
+
# ISA I/O mapped as GPIO
device isagpio: gpiobus
attach isagpio at isa
Index: arch/i386/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/i386/conf/GENERIC,v
retrieving revision 1.747
diff -u -p -u -r1.747 GENERIC
--- arch/i386/conf/GENERIC 1 Jun 2013 01:13:34 -0000 1.747
+++ arch/i386/conf/GENERIC 24 Jan 2014 20:24:38 -0000
@@ -152,6 +152,7 @@ uguru0 at isa? disable port 0xe0 # ABIT
fins0 at isa? port 0x4e # Fintek F71805 Super I/O
aps0 at isa? port 0x1600 # ThinkPad Active Protection System
+asmc0 at isa? port 0x300 irq 6 # Apple System Mgmt Controller
itherm* at pci? # Intel 3400 Thermal Sensor
adc* at iic? # Analog Devices AD7416/AD7417/7418
Index: arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.342
diff -u -p -u -r1.342 GENERIC
--- arch/amd64/conf/GENERIC 1 Jun 2013 01:13:33 -0000 1.342
+++ arch/amd64/conf/GENERIC 24 Jan 2014 20:24:38 -0000
@@ -103,6 +103,7 @@ lm* at wbsio?
uguru0 at isa? disable port 0xe0 # ABIT uGuru
aps0 at isa? port 0x1600 # ThinkPad Active Protection System
+asmc0 at isa? port 0x300 irq 6 # Apple System Mgmt Controller
piixpm* at pci? # Intel PIIX PM
iic* at piixpm?