/* SPDX-License-Identifier: GPL-2.0-only */ #include <stdio.h> #include <stdlib.h> #include <getopt.h> #include <sys/io.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdint.h> #include "pmh7tool.h" uint8_t pmh7_register_read(uint16_t reg) { outb(reg & 0xff, EC_LENOVO_PMH7_ADDR_L); outb((reg & 0xff00) >> 8, EC_LENOVO_PMH7_ADDR_H); return inb(EC_LENOVO_PMH7_DATA); } void pmh7_register_write(uint16_t reg, uint8_t val) { outb(reg & 0xff, EC_LENOVO_PMH7_ADDR_L); outb((reg & 0xff00) >> 8, EC_LENOVO_PMH7_ADDR_H); outb(val, EC_LENOVO_PMH7_DATA); } void pmh7_register_set_bit(uint16_t reg, uint8_t bit) { uint8_t val; val = pmh7_register_read(reg); pmh7_register_write(reg, val | (1 << bit)); } void pmh7_register_clear_bit(uint16_t reg, uint8_t bit) { uint8_t val; val = pmh7_register_read(reg); pmh7_register_write(reg, val & ~(1 << bit)); } uint8_t pmh7_register_read_bit(int16_t reg, uint8_t bit) { uint8_t val; val = pmh7_register_read(reg); return (val >> bit) & 1; } void print_usage(const char *name) { printf("usage: %s\n", name); printf("\n" " -h, --help: print this help\n" " -d, --dump: print registers\n" " -w, --write <addr> <data>: write to register\n" " -r, --read <addr>: read from register\n" " -b, --read-bit <addr> <bit> read bit\n" " -c, --clear-bit <addr> <bit> clear bit\n" " -s, --set-bit <addr> <bit> set bit\n" "\n" "Attention! Writing to PMH7 registers is very dangerous, as you\n" " directly manipulate the power rails, enable lines,\n" " interrupt lines or something else of the device.\n" " Proceed with caution." "\n"); } enum action {HELP, DUMP, WRITE, READ, READBIT, CLEAR, SET}; int main(int argc, char *argv[]) { enum action act = HELP; int opt, option_index = 0; long input_addr = 0, input_data = 0; static struct option long_options[] = { {"help", 0, 0, 'h'}, {"dump", 0, 0, 'd'}, {"write", 1, 0, 'w'}, {"read", 1, 0, 'r'}, {"read-bit", 1, 0, 'b'}, {"clear-bit", 1, 0, 'c'}, {"set-bit", 1, 0, 's'}, {0, 0, 0, 0} }; if (argv[1] == NULL) { print_usage(argv[0]); exit(0); } while ((opt = getopt_long(argc, argv, "hdw:r:c:s:b:", long_options, &option_index)) != EOF) { switch (opt) { case 'd': act = DUMP; break; case 'r': input_addr = strtoul(optarg, NULL, 16); act = READ; break; case 'w': case 'b': case 'c': case 's': input_addr = strtoul(optarg, NULL, 16); if (optind < argc && *argv[optind] != '-') { input_data = strtoul(argv[optind], NULL, 16); optind++; } else { fprintf(stderr, "Error: -%c option requires two arguments\n", opt); exit(1); } switch (opt) { case 'w': act = WRITE; break; case 'b': act = READBIT; break; case 'c': act = CLEAR; break; case 's': act = SET; break; } break; } } if (optind < argc) { fprintf(stderr, "Error: Extra parameter found.\n"); print_usage(argv[0]); exit(1); } if (act == HELP) { print_usage(argv[0]); exit(0); } if (input_addr > 0x1ff) { fprintf(stderr, "Error: <addr> cannot be greater than 9 bits long.\n"); exit(1); } if (act == SET || act == CLEAR || act == READBIT) { if (input_data > 7) { fprintf(stderr, "Error: <bit> cannot be greater than 7.\n"); exit(1); } } else { if (input_data > 0xff) { fprintf(stderr, "Error: <data> cannot be greater than 8 bits long.\n"); exit(1); } } if (geteuid() != 0) { fprintf(stderr, "You must be root.\n"); exit(1); } if (ioperm(EC_LENOVO_PMH7_BASE, 0x10, 1)) { fprintf(stderr, "ioperm: %s\n", strerror(errno)); exit(1); } switch (act) { case DUMP: for (int i = 0; i < 0x200; i++) { if ((i % 0x10) == 0) { if (i != 0) printf("\n"); printf("%04x: ", i); } printf("%02x ", pmh7_register_read(i)); } printf("\n"); break; case READ: printf("%02x\n", pmh7_register_read(input_addr)); break; case WRITE: pmh7_register_write(input_addr, input_data); break; case READBIT: printf("%d\n", pmh7_register_read_bit(input_addr, input_data)); break; case CLEAR: pmh7_register_clear_bit(input_addr, input_data); break; case SET: pmh7_register_set_bit(input_addr, input_data); break; default: break; } return 0; }