Add support for MacBook4,1.

Submitted by:	Christoph Langguth <christoph at rosenkeller.org>
MFC after:	2 weeks
Approved by:	re (kib)
This commit is contained in:
Rui Paulo 2009-06-26 10:23:17 +00:00
parent 1e5fd3f467
commit be80e49a01
2 changed files with 143 additions and 46 deletions

View file

@ -68,7 +68,9 @@ static int asmc_detach(device_t dev);
* SMC functions.
*/
static int asmc_init(device_t dev);
static int asmc_command(device_t dev, uint8_t command);
static int asmc_wait(device_t dev, uint8_t val);
static int asmc_wait_ack(device_t dev, uint8_t val, int amount);
static int asmc_key_write(device_t dev, const char *key, uint8_t *buf,
uint8_t len);
static int asmc_key_read(device_t dev, const char *key, uint8_t *buf,
@ -99,6 +101,7 @@ static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_sms_z(SYSCTL_HANDLER_ARGS);
static int asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS);
static int asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS);
static int asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS);
struct asmc_model {
const char *smc_model; /* smbios.system.product env var. */
@ -115,6 +118,7 @@ struct asmc_model {
int (*smc_fan_targetspeed)(SYSCTL_HANDLER_ARGS);
int (*smc_light_left)(SYSCTL_HANDLER_ARGS);
int (*smc_light_right)(SYSCTL_HANDLER_ARGS);
int (*smc_light_control)(SYSCTL_HANDLER_ARGS);
const char *smc_temps[ASMC_TEMP_MAX];
const char *smc_tempnames[ASMC_TEMP_MAX];
@ -131,18 +135,19 @@ static struct asmc_model *asmc_match(device_t dev);
asmc_mb_sysctl_fanmaxspeed, \
asmc_mb_sysctl_fantargetspeed
#define ASMC_LIGHT_FUNCS asmc_mbp_sysctl_light_left, \
asmc_mbp_sysctl_light_right
asmc_mbp_sysctl_light_right, \
asmc_mbp_sysctl_light_control
struct asmc_model asmc_models[] = {
{
"MacBook1,1", "Apple SMC MacBook Core Duo",
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
},
{
"MacBook2,1", "Apple SMC MacBook Core 2 Duo",
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
ASMC_MB_TEMPS, ASMC_MB_TEMPNAMES, ASMC_MB_TEMPDESCS
},
@ -182,12 +187,18 @@ struct asmc_model asmc_models[] = {
ASMC_MBP_TEMPS, ASMC_MBP_TEMPNAMES, ASMC_MBP_TEMPDESCS
},
{
"MacBookPro4,1", "Apple SMC MacBook Pro Core 2 Duo (Penryn)",
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, ASMC_LIGHT_FUNCS,
ASMC_MBP4_TEMPS, ASMC_MBP4_TEMPNAMES, ASMC_MBP4_TEMPDESCS
},
/* The Mac Mini has no SMS */
{
"Macmini1,1", "Apple SMC Mac Mini",
NULL, NULL, NULL,
ASMC_FAN_FUNCS,
NULL, NULL,
NULL, NULL, NULL,
ASMC_MM_TEMPS, ASMC_MM_TEMPNAMES, ASMC_MM_TEMPDESCS
},
@ -196,13 +207,13 @@ struct asmc_model asmc_models[] = {
"MacPro2", "Apple SMC Mac Pro (8-core)",
NULL, NULL, NULL,
ASMC_FAN_FUNCS,
NULL, NULL,
NULL, NULL, NULL,
ASMC_MP_TEMPS, ASMC_MP_TEMPNAMES, ASMC_MP_TEMPDESCS
},
{
"MacBookAir1,1", "Apple SMC MacBook Air",
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL,
ASMC_SMS_FUNCS, ASMC_FAN_FUNCS, NULL, NULL, NULL,
ASMC_MBA_TEMPS, ASMC_MBA_TEMPNAMES, ASMC_MBA_TEMPDESCS
},
@ -242,6 +253,7 @@ ACPI_MODULE_NAME("ASMC")
#define ASMC_DPRINTF(str)
#endif
/* NB: can't be const */
static char *asmc_ids[] = { "APP0001", NULL };
static devclass_t asmc_devclass;
@ -385,6 +397,33 @@ asmc_attach(device_t dev)
model->smc_tempdescs[i]);
}
/*
* dev.asmc.n.light
*/
if (model->smc_light_left) {
sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
CTLFLAG_RD, 0, "Keyboard backlight sensors");
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sc->sc_light_tree),
OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RD,
dev, 0, model->smc_light_left, "I",
"Keyboard backlight left sensor");
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sc->sc_light_tree),
OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RD,
dev, 0, model->smc_light_right, "I",
"Keyboard backlight right sensor");
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sc->sc_light_tree),
OID_AUTO, "control", CTLTYPE_INT | CTLFLAG_RW,
dev, 0, model->smc_light_control, "I",
"Keyboard backlight brightness control");
}
if (model->smc_sms_x == NULL)
goto nosms;
@ -413,27 +452,6 @@ asmc_attach(device_t dev)
dev, 0, model->smc_sms_z, "I",
"Sudden Motion Sensor Z value");
/*
* dev.asmc.n.light
*/
if (model->smc_light_left) {
sc->sc_light_tree = SYSCTL_ADD_NODE(sysctlctx,
SYSCTL_CHILDREN(sysctlnode), OID_AUTO, "light",
CTLFLAG_RD, 0, "Keyboard backlight sensors");
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sc->sc_light_tree),
OID_AUTO, "left", CTLTYPE_INT | CTLFLAG_RW,
dev, 0, model->smc_light_left, "I",
"Keyboard backlight left sensor");
SYSCTL_ADD_PROC(sysctlctx,
SYSCTL_CHILDREN(sc->sc_light_tree),
OID_AUTO, "right", CTLTYPE_INT | CTLFLAG_RW,
dev, 0, model->smc_light_right, "I",
"Keyboard backlight right sensor");
}
/*
* Need a taskqueue to send devctl_notify() events
* when the SMS interrupt us.
@ -606,38 +624,81 @@ nosms:
/*
* We need to make sure that the SMC acks the byte sent.
* Just wait up to 100 ms.
* Just wait up to (amount * 10) ms.
*/
static int
asmc_wait(device_t dev, uint8_t val)
asmc_wait_ack(device_t dev, uint8_t val, int amount)
{
struct asmc_softc *sc = device_get_softc(dev);
u_int i;
val = val & ASMC_STATUS_MASK;
for (i = 0; i < 1000; i++) {
for (i = 0; i < amount; i++) {
if ((ASMC_CMDPORT_READ(sc) & ASMC_STATUS_MASK) == val)
return (0);
DELAY(10);
}
return (1);
}
/*
* We need to make sure that the SMC acks the byte sent.
* Just wait up to 100 ms.
*/
static int
asmc_wait(device_t dev, uint8_t val)
{
struct asmc_softc *sc;
if (asmc_wait_ack(dev, val, 1000) == 0)
return (0);
sc = device_get_softc(dev);
val = val & ASMC_STATUS_MASK;
#ifdef DEBUG
device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, val,
ASMC_CMDPORT_READ(sc));
#endif
return (1);
}
/*
* Send the given command, retrying up to 10 times if
* the acknowledgement fails.
*/
static int
asmc_command(device_t dev, uint8_t command) {
int i;
struct asmc_softc *sc = device_get_softc(dev);
for (i=0; i < 10; i++) {
ASMC_CMDPORT_WRITE(sc, command);
if (asmc_wait_ack(dev, 0x0c, 100) == 0) {
return (0);
}
}
#ifdef DEBUG
device_printf(dev, "%s failed: 0x%x, 0x%x\n", __func__, command,
ASMC_CMDPORT_READ(sc));
#endif
return (1);
}
static int
asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
{
int i, error = 1;
int i, error = 1, try = 0;
struct asmc_softc *sc = device_get_softc(dev);
mtx_lock_spin(&sc->sc_mtx);
ASMC_CMDPORT_WRITE(sc, ASMC_CMDREAD);
if (asmc_wait(dev, 0x0c))
begin:
if (asmc_command(dev, ASMC_CMDREAD))
goto out;
for (i = 0; i < 4; i++) {
@ -656,6 +717,12 @@ asmc_key_read(device_t dev, const char *key, uint8_t *buf, uint8_t len)
error = 0;
out:
if (error) {
if (++try < 10) goto begin;
device_printf(dev,"%s for key %s failed %d times, giving up\n",
__func__, key, try);
}
mtx_unlock_spin(&sc->sc_mtx);
return (error);
@ -664,14 +731,14 @@ out:
static int
asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
{
int i, error = -1;
int i, error = -1, try = 0;
struct asmc_softc *sc = device_get_softc(dev);
mtx_lock_spin(&sc->sc_mtx);
begin:
ASMC_DPRINTF(("cmd port: cmd write\n"));
ASMC_CMDPORT_WRITE(sc, ASMC_CMDWRITE);
if (asmc_wait(dev, 0x0c))
if (asmc_command(dev, ASMC_CMDWRITE))
goto out;
ASMC_DPRINTF(("data port: key\n"));
@ -692,6 +759,12 @@ asmc_key_write(device_t dev, const char *key, uint8_t *buf, uint8_t len)
error = 0;
out:
if (error) {
if (++try < 10) goto begin;
device_printf(dev,"%s for key %s failed %d times, giving up\n",
__func__, key, try);
}
mtx_unlock_spin(&sc->sc_mtx);
return (error);
@ -993,20 +1066,11 @@ asmc_mbp_sysctl_light_left(SYSCTL_HANDLER_ARGS)
device_t dev = (device_t) arg1;
uint8_t buf[6];
int error;
unsigned int level;
int32_t v;
asmc_key_read(dev, ASMC_KEY_LIGHTLEFT, buf, 6);
v = buf[2];
error = sysctl_handle_int(oidp, &v, sizeof(v), req);
if (error == 0 && req->newptr != NULL) {
level = *(unsigned int *)req->newptr;
if (level > 255)
return (EINVAL);
buf[0] = level;
buf[1] = 0x00;
asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);
}
return (error);
}
@ -1017,16 +1081,30 @@ asmc_mbp_sysctl_light_right(SYSCTL_HANDLER_ARGS)
device_t dev = (device_t) arg1;
uint8_t buf[6];
int error;
unsigned int level;
int32_t v;
asmc_key_read(dev, ASMC_KEY_LIGHTRIGHT, buf, 6);
v = buf[2];
error = sysctl_handle_int(oidp, &v, sizeof(v), req);
return (error);
}
static int
asmc_mbp_sysctl_light_control(SYSCTL_HANDLER_ARGS)
{
device_t dev = (device_t) arg1;
uint8_t buf[2];
int error;
unsigned int level;
static int32_t v;
error = sysctl_handle_int(oidp, &v, sizeof(v), req);
if (error == 0 && req->newptr != NULL) {
level = *(unsigned int *)req->newptr;
if (level > 255)
return (EINVAL);
v = level;
buf[0] = level;
buf[1] = 0x00;
asmc_key_write(dev, ASMC_KEY_LIGHTVALUE, buf, 2);

View file

@ -155,6 +155,25 @@ struct asmc_softc {
"Graphics Chip", "Graphics Heatsink", \
"Unknown", }
#define ASMC_MBP4_TEMPS { "TB0T", "Th0H", "Th1H", "Th2H", "Tm0P", \
"TG0H", "TG0D", "TC0D", "TC0P", "Ts0P", \
"TTF0", "TW0P", NULL }
#define ASMC_MBP4_TEMPNAMES { "enclosure", "heatsink1", "heatsink2", \
"heatsink3", "memory", "graphicssink", \
"graphics", "cpu", "cpu2", "unknown1", \
"unknown2", "wireless", }
#define ASMC_MBP4_TEMPDESCS { "Enclosure Bottomside", \
"Main Heatsink 1", "Main Heatsink 2", \
"Main Heatsink 3", \
"Memory Controller", \
"Graphics Chip Heatsink", \
"Graphics Chip Diode", \
"CPU Temperature Diode", "CPU Point 2", \
"Unknown", "Unknown", \
"Wireless Module", }
#define ASMC_MM_TEMPS { "TN0P", "TN1P", NULL }
#define ASMC_MM_TEMPNAMES { "northbridge1", "northbridge2" }
#define ASMC_MM_TEMPDESCS { "Northbridge Point 1", \