Alexander PolakovConvert to new API5b1246e13 years agoHistory
407 lines · 8.4 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <stdio.h>#include <aml.h>#include "acpi.h" #define ACPI_PDC_REVID		0x1#define ACPI_PDC_SMP		0xa#define ACPI_PDC_MSR		0x1 /* _PDC Intel capabilities flags from linux */#define ACPI_PDC_P_FFH		0x0001#define ACPI_PDC_C_C1_HALT	0x0002#define ACPI_PDC_T_FFH		0x0004#define ACPI_PDC_SMP_C1PT	0x0008#define ACPI_PDC_SMP_C2C3	0x0010#define ACPI_PDC_SMP_P_SWCOORD	0x0020#define ACPI_PDC_SMP_C_SWCOORD	0x0040#define ACPI_PDC_SMP_T_SWCOORD	0x0080#define ACPI_PDC_C_C1_FFH	0x0100#define ACPI_PDC_C_C2C3_FFH	0x0200 struct acpicpu_tss {	uint	tss_core_perc;	uint	tss_power;	uint	tss_trans_latency;	uint	tss_ctrl;	uint	tss_status;}; struct acpicpu_pss {	uint	pss_core_freq;	uint	pss_power;	uint	pss_trans_latency;	uint 	pss_bus_latency;	uint	pss_ctrl;	uint	pss_status;};  struct acpi_gas {	uchar space;	uchar bitwidth;	uchar bitoff;	uchar size;	uchar addr[8];}; struct acpi_grd {	uchar	grd_descriptor;	uchar	grd_length[2];}; struct acpicpu_pct {	struct acpi_grd ctrl;	struct acpi_grd status;}; struct acpicpu {	struct acpicpu_tss **tss;	struct acpicpu_pss **pss;	int ntss;	int npss;	void (*set_tss)(uchar);	void (*get_tss)(uchar*);	File *throttling;	File *powerstates;}; enum { 	PerfStatus = 0x198, 	PerfCtl = 0x199}; #define MSR_LEN 8#define TSS_LEN 200#define STATUS_LEN 50 intacpicpu_match(char *){	/* never attach to _HID */	return 0;} /* These two are for Intel, don't try it on AMD */static voidreadmsr(uchar *msr, uint reg){	int fd; 	if((fd = open("/dev/msr", OREAD)) == -1)		sysfatal("readmsr: %r");	if(pread(fd, msr, MSR_LEN, reg) != MSR_LEN)		sysfatal("readmsr: %r");	close(fd);} static voidreadpss(uchar *msr){	readmsr(msr, PerfStatus);} static voidwritemsr(uchar val){	int fd;	uchar msr[MSR_LEN]; 	if((fd = open("/dev/msr", OWRITE)) == -1)		sysfatal("writemsr: %r");	readmsr(msr, PerfCtl);		memset(msr, 0, 2);	msr[0] |= val;	if(pwrite(fd, msr, MSR_LEN, PerfCtl) != MSR_LEN)		sysfatal("writemsr2: %r");	readpss(msr);	if(msr[0] & val)		print("0x%x written successfully\n", val);	close(fd);} /* we don't use _PTC, but keep it here  * if sometime we want to. * Why not? It tells us to use i/o port * which doesn't actually work.  *//*voidgetptc(void *dot){	void *m;	struct acpicpu_pct pct;	struct acpi_gas *gas;	uchar *buf; 	if((m = amlwalk(dot, "^_PTC")) == 0) {		print("cpu: no _PTC\n");		return 1;	}	if(amleval(m, "", &p) < 0) {		print("tss: _PTC not working\n");		return 1;	}	if (amllen(p) != 2) {		print("invalid _PTC len\n");		return 1;	}	a = amlval(p);	memcpy(&pct.ctrl, a[0], sizeof(pct.ctrl));	memcpy(&pct.status, a[1], sizeof(pct.status)); 	buf = a[0];	gas = (struct acpi_gas *)(buf + 3);	print("_PTC(ctrl)  : %02x %04x %02x %02x %02x %02x %016ullx\n",	    pct.ctrl.grd_descriptor,	    get16(pct.ctrl.grd_length),	    gas->space,	    gas->bitwidth,	    gas->bitoff,	    gas->size,	    get64(buf + 7));	print("space = %d\n", gas->space);	print("0x%x\n", 0x410); 	buf = a[1];	for(i = 0; i < 17; i++) {		print("%x, ", buf[i]);	}	gas = (struct acpi_gas *)(buf + 3);	print("_PTC(status)  : %02x %04x %02x %02x %02x %02x %016ullx\n",	    pct.ctrl.grd_descriptor,	    get16(pct.ctrl.grd_length),	    gas->space,	    gas->bitwidth,	    gas->bitoff,	    gas->size,	    get64(buf + 7));	print("space = %d\n", gas->space);}*/ static char *status(struct acpidev *dev, File *file){	struct acpicpu *cpu;	uchar msr[8];	char *buf;	int i, n, pss = 0; 	cpu = (struct acpicpu*)dev->data;	if(file == cpu->throttling) {		if((buf = malloc(TSS_LEN)) == 0)			sysfatal("%r");		n = 0;		n += snprint(buf, TSS_LEN, "%%\t\t power\tctl\n");		for(i = 0; i < cpu->ntss; i++) {			n += snprint(buf+n, TSS_LEN - n, "%3d%%\t%5d\t0x%x\n",					cpu->tss[i]->tss_core_perc, 					cpu->tss[i]->tss_power,					cpu->tss[i]->tss_ctrl);		}				return buf;	}	if(file == cpu->powerstates) {		if((buf = malloc(TSS_LEN)) == 0)			sysfatal("%r");		n = 0;		n += snprint(buf, TSS_LEN, "Mhz\t\tpower\tctl\n");		for(i = 0; i < cpu->npss; i++) {			n += snprint(buf+n, TSS_LEN - n, "%4d\t%5d\t0x%x\n",					cpu->pss[i]->pss_core_freq, 					cpu->pss[i]->pss_power,					cpu->pss[i]->pss_ctrl);		}				return buf;	}	cpu->get_tss(msr);	for(i = 0; i < cpu->ntss; i++) {		if(cpu->tss[i]->tss_ctrl == msr[0])			break;	}	if(i == cpu->ntss) {		for(i = 0; i < cpu->npss; i++) {			if(cpu->pss[i]->pss_ctrl == msr[0])				break;		}		if(i < cpu->npss)			pss = 1;	}	if((buf = malloc(STATUS_LEN)) == 0)		sysfatal("%r");	if(pss == 0) {		snprint(buf, STATUS_LEN, "unknown state 0x%x\n", msr[0]);	} else {		if(pss)			snprint(buf, STATUS_LEN, "%dMhz, power: %d\n",				cpu->pss[i]->pss_core_freq, cpu->pss[i]->pss_power);		else			snprint(buf, STATUS_LEN, "throttling: %d%%, power: %d\n",				cpu->tss[i]->tss_core_perc, cpu->tss[i]->tss_power);	}	return buf;} static void control(struct acpidev *dev, char *data, u32int len, char *err){	int i;	struct acpicpu *cpu;	uint val; 	cpu = (struct acpicpu*)dev->data;	if (len < 3)		goto notfound;	if(sscanf(data, "0x%x", &val) != 1)		goto notfound;	for(i = 0; i < cpu->ntss; i++) {		if (val == cpu->tss[i]->tss_ctrl)			break;	}	if(i < cpu->ntss)		goto found; 	for(i = 0; i < cpu->npss; i++) {		if (val == cpu->pss[i]->pss_ctrl)			break;	}	if(i == cpu->npss)		goto notfound;found:	cpu->set_tss(val);	err[0] = '\0';	return;notfound:	snprint(err, ERRMAX, "bad value");} static intevalpss(struct acpidev *dev){	void *p, *m;	int i;	void **a;	void **pkg;	struct acpicpu *cpu = dev->data; 	if((m = amlwalk(dev->node, "^_PSS")) == 0) {		fprint(2, "no _PSS\n");		return 0;	}	if(amleval(m, "", &p) < 0) {		fprint(2, "cpu: _PSS not working\n");		return 0;	}	pkg = amlval(p);	cpu->npss = amllen(p);	if ((cpu->pss = malloc(cpu->npss * sizeof(struct acpicpu_pss*))) == 0)		sysfatal("%r");	for(i = 0; i < cpu->npss; i++) {		a = amlval(pkg[i]);		if ((cpu->pss[i] = malloc(sizeof(struct acpicpu_pss))) == 0)			sysfatal("%r");		cpu->pss[i]->pss_core_freq = amlint(a[0]);		cpu->pss[i]->pss_power = amlint(a[1]);		cpu->pss[i]->pss_trans_latency = amlint(a[2]);		cpu->pss[i]->pss_bus_latency = amlint(a[3]);		cpu->pss[i]->pss_ctrl = amlint(a[4]);		cpu->pss[i]->pss_status = amlint(a[5]);	}	return 1;} static intevaltss(struct acpidev *dev){	struct acpicpu *cpu = dev->data;	void *p, **a, **pkg, *m;	int i; 	if((m = amlwalk(dev->node, "^_TSS")) == 0) {		fprint(2, "cpu: no _TSS");		return 0;	}	if(amleval(m, "", &p) < 0) {		fprint(2, "cpu: _TSS not working\n");		return 0;	}	pkg = amlval(p);	cpu->ntss = amllen(p);	if ((cpu->tss = malloc(cpu->ntss * sizeof(struct acpicpu_tss*))) == 0)		sysfatal("%r");	for(i = 0; i < amllen(p); i++) {		a = amlval(pkg[i]);		if ((cpu->tss[i] = malloc(sizeof(struct acpicpu_tss))) == 0)			sysfatal("%r");		cpu->tss[i]->tss_core_perc = amlint(a[0]);		cpu->tss[i]->tss_power = amlint(a[1]);		cpu->tss[i]->tss_trans_latency = amlint(a[2]);		cpu->tss[i]->tss_ctrl = amlint(a[3]);		cpu->tss[i]->tss_status = amlint(a[4]);	}	return 1;} static intevalpdc(struct acpidev *dev){	void *m;	uint buf[3];	void *b, *oscb;	char *p;	static uchar cpu_oscuuid[] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29,					   0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70,					   0x58, 0x71, 0x39, 0x53 }; 	m = dev->node;	buf[0] = ACPI_PDC_REVID;	buf[1] = 1;	buf[2] = ACPI_PDC_C_C1_HALT | ACPI_PDC_P_FFH | ACPI_PDC_C_C1_FFH	    | ACPI_PDC_C_C2C3_FFH | ACPI_PDC_SMP_P_SWCOORD | ACPI_PDC_SMP_C2C3	    | ACPI_PDC_SMP_C1PT; 	b = amlnew('b', sizeof(buf));	memcpy(b, buf, sizeof(buf));	if(amleval(m, "b", b, &p) < 0) {		print("_PDC not working");		return 0;	}	if((m = amlwalk(dev->node, "^_OSC")) == 0) {		print("no _OSC");		return 0;	}	oscb = amlnew('b', sizeof(cpu_oscuuid));	memcpy(oscb, cpu_oscuuid, sizeof(cpu_oscuuid));	if(amleval(m, "biib", oscb, 1, 1, b, &p) < 0) {		print("_OSC not working");		return 0;	}	return 1;} intacpicpu_attach(struct acpidev *dev){	struct acpicpu *cpu; 	if(evalpdc(dev) == 0)		return 0;	if((cpu = calloc(1, sizeof *cpu)) == 0)		sysfatal("%r");	dev->data = cpu;	if(!evalpss(dev) && !evaltss(dev)) {		free(cpu);		return 0;	}	cpu->set_tss = writemsr;	cpu->get_tss = readpss;	dev->status = status;	dev->control = control;		if(cpu->ntss != 0)		cpu->throttling = createfile(dev->dir, "throttling", "sys", 0444, dev);	if(cpu->pss != 0)		cpu->powerstates = createfile(dev->dir, "powerstates", "sys", 0444, dev);	return 1;}