xref: /aosp_15_r20/external/coreboot/util/pmh7tool/pmh7tool.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <getopt.h>
6 #include <sys/io.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <unistd.h>
10 #include <stdint.h>
11 #include "pmh7tool.h"
12 
pmh7_register_read(uint16_t reg)13 uint8_t pmh7_register_read(uint16_t reg)
14 {
15 	outb(reg & 0xff, EC_LENOVO_PMH7_ADDR_L);
16 	outb((reg & 0xff00) >> 8, EC_LENOVO_PMH7_ADDR_H);
17 	return inb(EC_LENOVO_PMH7_DATA);
18 }
19 
pmh7_register_write(uint16_t reg,uint8_t val)20 void pmh7_register_write(uint16_t reg, uint8_t val)
21 {
22 	outb(reg & 0xff, EC_LENOVO_PMH7_ADDR_L);
23 	outb((reg & 0xff00) >> 8, EC_LENOVO_PMH7_ADDR_H);
24 	outb(val, EC_LENOVO_PMH7_DATA);
25 }
26 
pmh7_register_set_bit(uint16_t reg,uint8_t bit)27 void pmh7_register_set_bit(uint16_t reg, uint8_t bit)
28 {
29 	uint8_t val;
30 
31 	val = pmh7_register_read(reg);
32 	pmh7_register_write(reg, val | (1 << bit));
33 }
34 
pmh7_register_clear_bit(uint16_t reg,uint8_t bit)35 void pmh7_register_clear_bit(uint16_t reg, uint8_t bit)
36 {
37 	uint8_t val;
38 
39 	val = pmh7_register_read(reg);
40 	pmh7_register_write(reg, val & ~(1 << bit));
41 }
42 
pmh7_register_read_bit(int16_t reg,uint8_t bit)43 uint8_t pmh7_register_read_bit(int16_t reg, uint8_t bit)
44 {
45 	uint8_t val;
46 
47 	val = pmh7_register_read(reg);
48 	return (val >> bit) & 1;
49 }
50 
print_usage(const char * name)51 void print_usage(const char *name)
52 {
53 	printf("usage: %s\n", name);
54 	printf("\n"
55 		   "	-h, --help:                   print this help\n"
56 		   "	-d, --dump:                   print registers\n"
57 		   "	-w, --write <addr> <data>:    write to register\n"
58 		   "	-r, --read <addr>:            read from register\n"
59 		   "	-b, --read-bit <addr> <bit>   read bit\n"
60 		   "	-c, --clear-bit <addr> <bit>  clear bit\n"
61 		   "	-s, --set-bit <addr> <bit>    set bit\n"
62 		   "\n"
63 		   "Attention! Writing to PMH7 registers is very dangerous, as you\n"
64 		   "           directly manipulate the power rails, enable lines,\n"
65 		   "           interrupt lines or something else of the device.\n"
66 		   "           Proceed with caution."
67 		   "\n");
68 }
69 
70 enum action {HELP, DUMP, WRITE, READ, READBIT, CLEAR, SET};
71 
main(int argc,char * argv[])72 int main(int argc, char *argv[])
73 {
74 	enum action act = HELP;
75 	int opt, option_index = 0;
76 	long input_addr = 0, input_data = 0;
77 
78 	static struct option long_options[] = {
79 		{"help",        0, 0, 'h'},
80 		{"dump",        0, 0, 'd'},
81 		{"write",       1, 0, 'w'},
82 		{"read",        1, 0, 'r'},
83 		{"read-bit",    1, 0, 'b'},
84 		{"clear-bit",   1, 0, 'c'},
85 		{"set-bit",     1, 0, 's'},
86 		{0, 0, 0, 0}
87 	};
88 
89 	if (argv[1] == NULL) {
90 		print_usage(argv[0]);
91 		exit(0);
92 	}
93 
94 	while ((opt = getopt_long(argc, argv, "hdw:r:c:s:b:",
95 				  long_options, &option_index)) != EOF) {
96 		switch (opt) {
97 		case 'd':
98 			act = DUMP;
99 			break;
100 
101 		case 'r':
102 			input_addr = strtoul(optarg, NULL, 16);
103 			act = READ;
104 			break;
105 
106 		case 'w':
107 		case 'b':
108 		case 'c':
109 		case 's':
110 			input_addr = strtoul(optarg, NULL, 16);
111 
112 			if (optind < argc && *argv[optind] != '-') {
113 				input_data = strtoul(argv[optind], NULL, 16);
114 				optind++;
115 			} else {
116 				fprintf(stderr,
117 					"Error: -%c option requires two arguments\n",
118 					opt);
119 				exit(1);
120 			}
121 
122 			switch (opt) {
123 			case 'w':
124 				act = WRITE;
125 				break;
126 			case 'b':
127 				act = READBIT;
128 				break;
129 			case 'c':
130 				act = CLEAR;
131 				break;
132 			case 's':
133 				act = SET;
134 				break;
135 			}
136 			break;
137 		}
138 	}
139 
140 	if (optind < argc) {
141 		fprintf(stderr, "Error: Extra parameter found.\n");
142 		print_usage(argv[0]);
143 		exit(1);
144 	}
145 
146 	if (act == HELP) {
147 		print_usage(argv[0]);
148 		exit(0);
149 	}
150 
151 	if (input_addr > 0x1ff) {
152 		fprintf(stderr,
153 			"Error: <addr> cannot be greater than 9 bits long.\n");
154 		exit(1);
155 	}
156 
157 	if (act == SET || act == CLEAR || act == READBIT) {
158 		if (input_data > 7) {
159 			fprintf(stderr,
160 				"Error: <bit> cannot be greater than 7.\n");
161 			exit(1);
162 		}
163 	} else {
164 		if (input_data > 0xff) {
165 			fprintf(stderr,
166 				"Error: <data> cannot be greater than 8 bits long.\n");
167 			exit(1);
168 		}
169 	}
170 
171 	if (geteuid() != 0) {
172 		fprintf(stderr, "You must be root.\n");
173 		exit(1);
174 	}
175 
176 	if (ioperm(EC_LENOVO_PMH7_BASE, 0x10, 1)) {
177 		fprintf(stderr, "ioperm: %s\n", strerror(errno));
178 		exit(1);
179 	}
180 
181 	switch (act) {
182 	case DUMP:
183 		for (int i = 0; i < 0x200; i++) {
184 			if ((i % 0x10) == 0) {
185 				if (i != 0)
186 					printf("\n");
187 				printf("%04x: ", i);
188 			}
189 			printf("%02x ", pmh7_register_read(i));
190 		}
191 		printf("\n");
192 		break;
193 
194 	case READ:
195 		printf("%02x\n", pmh7_register_read(input_addr));
196 		break;
197 
198 	case WRITE:
199 		pmh7_register_write(input_addr, input_data);
200 		break;
201 
202 	case READBIT:
203 		printf("%d\n", pmh7_register_read_bit(input_addr, input_data));
204 		break;
205 
206 	case CLEAR:
207 		pmh7_register_clear_bit(input_addr, input_data);
208 		break;
209 
210 	case SET:
211 		pmh7_register_set_bit(input_addr, input_data);
212 		break;
213 
214 	default:
215 		break;
216 	}
217 
218 	return 0;
219 }
220