/* * battery.c * display pi-top battery status * * Copyright 2016, 2017 rricharz * MiSTer port. Copyright 2018 Sorgelig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * */ #include #include #include #include #include #include #include #include #include #include #include "battery.h" #define MAX_COUNT 20 // Maximum number of trials #define SLEEP_TIME 500 // time between two i2cget in microsec /////////////////////////////////////////////////////////////////////// // I2C definitions #define I2C_SLAVE 0x0703 #define I2C_SMBUS 0x0720 /* SMBus-level access */ #define I2C_SMBUS_READ 1 #define I2C_SMBUS_WRITE 0 // SMBus transaction types #define I2C_SMBUS_QUICK 0 #define I2C_SMBUS_BYTE 1 #define I2C_SMBUS_BYTE_DATA 2 #define I2C_SMBUS_WORD_DATA 3 #define I2C_SMBUS_PROC_CALL 4 #define I2C_SMBUS_BLOCK_DATA 5 #define I2C_SMBUS_I2C_BLOCK_BROKEN 6 #define I2C_SMBUS_BLOCK_PROC_CALL 7 /* SMBus 2.0 */ #define I2C_SMBUS_I2C_BLOCK_DATA 8 // SMBus messages #define I2C_SMBUS_BLOCK_MAX 32 /* As specified in SMBus standard */ #define I2C_SMBUS_I2C_BLOCK_MAX 32 /* Not specified but we use same structure */ // Structures used in the ioctl() calls union i2c_smbus_data { uint8_t byte; int8_t sbyte; uint16_t word; int16_t sword; uint8_t block [I2C_SMBUS_BLOCK_MAX + 2]; // block [0] is used for length + one more for PEC }; struct i2c_smbus_ioctl_data { char read_write; uint8_t command; int size; union i2c_smbus_data *data; }; static int i2c_smbus_access (int fd, char rw, uint8_t command, int size, union i2c_smbus_data *data) { struct i2c_smbus_ioctl_data args; args.read_write = rw; args.command = command; args.size = size; args.data = data; return ioctl (fd, I2C_SMBUS, &args); } static int i2c_smbus_write_quick(int fd, uint8_t value) { return i2c_smbus_access(fd, value, 0, I2C_SMBUS_QUICK, NULL); } /////////////////////////////////////////////////////////////////////// static int i2c_handle = -1; static int smbus_open(int dev_address) { // re-entry compatible if(i2c_handle < 0) { int fd; if ((fd = open ("/dev/i2c-1", O_RDWR)) < 0) { printf("Unable to open I2C device: %s\n", strerror(errno)); return 0; } if (ioctl (fd, I2C_SLAVE, dev_address) < 0) { printf("Unable to select I2C device: %s\n", strerror (errno)); close(fd); return 0; } if (i2c_smbus_write_quick(fd, I2C_SMBUS_WRITE) < 0) { printf("Unable to detect SMBUS device: %s\n", strerror(errno)); close(fd); return 0; } i2c_handle = fd; } return 1; } /* static void smbus_close() { if(i2c_handle > 0) close(i2c_handle); i2c_handle = -1; } */ static int smbus_get(int address, short *data) { union i2c_smbus_data smbus_data; if (i2c_smbus_access (i2c_handle, I2C_SMBUS_READ, address, I2C_SMBUS_WORD_DATA, &smbus_data)) return 0; *data = smbus_data.sword; return 1; } static int getReg(int reg, int min, int max) { int count = 0; short value = -1; while ((value == -1) && (count++ < MAX_COUNT)) { if (smbus_get(reg, &value)) { if ((value > max) || (value < min)) value = -1; // out of limits } usleep(SLEEP_TIME); } return value; } int getBattery(int quick, struct battery_data_t *data) { /* data->capacity = 80; data->load_current = -520; data->time = 312; data->current = 2510; data->voltage = 16200; data->cell[0] = 4101; data->cell[1] = 4102; data->cell[2] = 4103; data->cell[3] = 4104; return 1; */ // don't try to check if no battery device is present if (i2c_handle == -2) return 0; if (!smbus_open(0x0b)) { printf("No battery found.\n"); i2c_handle = -2; return 0; } data->capacity = getReg(0x0D, 0, 100); data->load_current = getReg(0x0A, -5000, 5000); if (quick) return 1; data->time = 0; if (data->load_current > 0) data->time = getReg(0x13, 1, 999); if (data->load_current < -1) data->time = getReg(0x12, 1, 960); data->current = getReg(0x0F, 0, 5000); data->voltage = getReg(0x09, 5000, 20000); /* data->cell[0] = getReg(0x3F, 1000, 5000); data->cell[1] = getReg(0x3E, 1000, 5000); data->cell[2] = getReg(0x3D, 1000, 5000); data->cell[3] = getReg(0x3C, 1000, 5000); */ return 1; }