/ util / pmh7tool / pmh7tool.c
pmh7tool.c
  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  
 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  
 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  
 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  
 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  
 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  
 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  
 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  }