reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

commit e1874b4661044040cfac1e5b90102bbb003eb46f
parent 503f1359db71b4eb632fbbb77282257cc8fc68c0
Author: nanocodebug <nanocodebug@gmail.com>
Date:   Tue, 21 Jun 2022 16:47:29 -0700

fix driver names, add support for power supply class

Diffstat:
Mreform2-lpc-driver/Makefile | 22++++++++++++++--------
Dreform2-lpc-driver/src/lpc-reform2.c | 137-------------------------------------------------------------------------------
Areform2-lpc-driver/src/reform2-lpc.c | 427+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 441 insertions(+), 145 deletions(-)

diff --git a/reform2-lpc-driver/Makefile b/reform2-lpc-driver/Makefile @@ -1,5 +1,5 @@ KPATH := /usr/src/linux-headers-`uname -r` -obj-m := ./src/lpc-reform2.o +obj-m := ./src/reform2-lpc.o .PHONY: all clean doc @@ -11,16 +11,22 @@ clean: install: mkdir -p /lib/modules/`uname -r`/misc - cp ./src/lpc-reform2.ko /lib/modules/`uname -r`/misc + cp ./src/reform2-lpc.ko /lib/modules/`uname -r`/misc depmod -a - modprobe -r lpc-reform2 - modprobe lpc-reform2 + modprobe -r reform2-lpc + modprobe reform2-lpc remove: - modprobe -r lpc-reform2 - $(RM) /lib/modules/`uname -r`/misc/lpc-reform2.ko + modprobe -r reform2-lpc + $(RM) /lib/modules/`uname -r`/misc/reform2-lpc.ko depmod -a read: - cat /sys/bus/spi/drivers/lpc-reform2/spi1.0/status - + cat /sys/bus/spi/drivers/reform2-lpc/spi1.0/status + @echo + cat /sys/bus/spi/drivers/reform2-lpc/spi1.0/cells + @echo + cat /sys/bus/spi/drivers/reform2-lpc/spi1.0/firmware + @echo + cat /sys/bus/spi/drivers/reform2-lpc/spi1.0/capacity + @echo diff --git a/reform2-lpc-driver/src/lpc-reform2.c b/reform2-lpc-driver/src/lpc-reform2.c @@ -1,137 +0,0 @@ -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/mutex.h> -#include <linux/spi/spi.h> -#include <linux/delay.h> - -static int spiProbe(struct spi_device *spi); -static void spiRemove(struct spi_device *spi); -static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf); - -typedef struct t_private_data -{ - struct spi_device *spi; - struct mutex lock; -} T_PRIVATE_DATA; - -static DEVICE_ATTR(status, 0444, showStatus, NULL); - -static struct spi_board_info g_spi_board_info = { - .modalias = "lpc-reform2", - .max_speed_hz = 400000, - .bus_num = 0, - .chip_select = 0, - .mode = SPI_MODE_1, -}; - -static int spiProbe(struct spi_device *spi) -{ - T_PRIVATE_DATA *data; - int ret; - - printk(KERN_INFO "%s: probing ...\n", "lpc-reform2"); - - spi->max_speed_hz = g_spi_board_info.max_speed_hz; - spi->mode = g_spi_board_info.mode; - spi->bits_per_word = 8; - - ret = spi_setup(spi); - if (ret) - { - printk(KERN_ERR "%s: spi_setup failed\n", __func__); - return -ENODEV; - } - - data = kzalloc(sizeof(T_PRIVATE_DATA), GFP_KERNEL); - if (data == NULL) - { - printk(KERN_ERR "%s: kzalloc failed\n", __func__); - return -ENOMEM; - } - - data->spi = spi; - mutex_init(&data->lock); - spi_set_drvdata(spi, data); - - ret = device_create_file(&spi->dev, &dev_attr_status); - if (ret) - { - printk(KERN_ERR "%s: device_create_file failed\n", __func__); - } - - return 0; -} - -static void spiRemove(struct spi_device *spi) -{ - T_PRIVATE_DATA *data = (T_PRIVATE_DATA *)spi_get_drvdata(spi); - - printk(KERN_INFO "%s: removing ... \n", "lpc-reform2"); - - device_remove_file(&spi->dev, &dev_attr_status); - - kfree(data); -} - -static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf) -{ - T_PRIVATE_DATA *data = (T_PRIVATE_DATA *)dev_get_drvdata(dev); - uint8_t statusCommand[3] = {0xB5, 'q', 0x00}; - uint8_t statusResult[8]; - int ret; - int16_t voltage; - int16_t amps; - int8_t percentage; - uint8_t status; - - mutex_lock(&data->lock); - - ret = spi_write(data->spi, statusCommand, 3); - if (ret) - { - printk(KERN_INFO "%s: spi_write failed\n", __func__); - } - mdelay(300); - - ret = spi_read(data->spi, statusResult, 8); - if (ret) - { - printk(KERN_INFO "%s: spi_read failed\n", __func__); - } - - mutex_unlock(&data->lock); - - voltage = (int16_t)statusResult[0] | ((int16_t)statusResult[1] << 8); - amps = (int16_t)statusResult[2] | ((int16_t)statusResult[3] << 8); - percentage = statusResult[4]; - status = statusResult[5]; - - return snprintf(buf, PAGE_SIZE, "%d.%d, %d.%d, %2d%%, %d", - voltage / 1000, voltage % 1000, amps / 1000, abs(amps % 1000), percentage, status); -} - -static const struct of_device_id of_tis_spi_match[] = { - {.compatible = "mntre,lpc-reform2", .data = 0}, - {}}; -MODULE_DEVICE_TABLE(of, of_tis_spi_match); - -static struct spi_device_id g_spi_dev_id_list[] = { - {"lpc-reform2", 0}, - {}, -}; -MODULE_DEVICE_TABLE(spi, g_spi_dev_id_list); - -static struct spi_driver g_spi_driver = { - .probe = spiProbe, - .remove = spiRemove, - .driver = { - .of_match_table = of_match_ptr(of_tis_spi_match), - .owner = THIS_MODULE, - .name = "lpc-reform2", - }, - .id_table = g_spi_dev_id_list, -}; -module_spi_driver(g_spi_driver); - -MODULE_DESCRIPTION("Reform 2 LPC Driver"); -MODULE_LICENSE("GPL"); diff --git a/reform2-lpc-driver/src/reform2-lpc.c b/reform2-lpc-driver/src/reform2-lpc.c @@ -0,0 +1,427 @@ +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> +#include <linux/power_supply.h> + +static int spiProbe(struct spi_device *spi); +static void spiRemove(struct spi_device *spi); +static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t showCells(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t showFirmware(struct device *dev, struct device_attribute *attr, char *buf); +static ssize_t showCapacity(struct device *dev, struct device_attribute *attr, char *buf); + +static ssize_t lpcCommand(struct device *dev, char command, uint8_t arg1, uint8_t *response); +static int bat_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); + +typedef struct t_private_data +{ + struct spi_device *spi; + struct power_supply *bat; + struct mutex lock; +} T_PRIVATE_DATA; + +static DEVICE_ATTR(status, 0444, showStatus, NULL); +static DEVICE_ATTR(cells, 0444, showCells, NULL); +static DEVICE_ATTR(firmware, 0444, showFirmware, NULL); +static DEVICE_ATTR(capacity, 0444, showCapacity, NULL); + +static struct spi_board_info g_spi_board_info = { + .modalias = "reform2-lpc", + .max_speed_hz = 400000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_1, +}; + +static enum power_supply_property bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, // lifepo4 + POWER_SUPPLY_PROP_VOLTAGE_NOW, // uV + POWER_SUPPLY_PROP_CURRENT_NOW, // uA + POWER_SUPPLY_PROP_CAPACITY, // percent + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, //uAh hardcoded to 1800 atm, param 2 + POWER_SUPPLY_PROP_CHARGE_NOW, //uAh capacity_accu_ampsecs param 0 + POWER_SUPPLY_PROP_CHARGE_EMPTY, // uAh param 1 +}; + +static struct power_supply_desc bat_desc = { + .name = "reform2_battery", + // .technology = POWER_SUPPLY_TECHNOLOGY_LiFe, + .properties = bat_props, + .num_properties = ARRAY_SIZE(bat_props), + .get_property = bat_get_property, +}; + +static int spiProbe(struct spi_device *spi) +{ + T_PRIVATE_DATA *data; + int ret; + struct power_supply_config psy_cfg = {}; + + printk(KERN_INFO "%s: probing ...\n", "reform2-lpc"); + + spi->max_speed_hz = g_spi_board_info.max_speed_hz; + spi->mode = g_spi_board_info.mode; + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret) + { + printk(KERN_ERR "%s: spi_setup failed\n", __func__); + return -ENODEV; + } + + data = kzalloc(sizeof(T_PRIVATE_DATA), GFP_KERNEL); + if (data == NULL) + { + printk(KERN_ERR "%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + + data->spi = spi; + mutex_init(&data->lock); + spi_set_drvdata(spi, data); + + ret = device_create_file(&spi->dev, &dev_attr_status); + if (ret) + { + printk(KERN_ERR "%s: device_create_file failed\n", __func__); + } + + ret = device_create_file(&spi->dev, &dev_attr_cells); + if (ret) + { + printk(KERN_ERR "%s: device_create_file failed\n", __func__); + } + + ret = device_create_file(&spi->dev, &dev_attr_firmware); + if (ret) + { + printk(KERN_ERR "%s: device_create_file failed\n", __func__); + } + + ret = device_create_file(&spi->dev, &dev_attr_capacity); + if (ret) + { + printk(KERN_ERR "%s: device_create_file failed\n", __func__); + } + + psy_cfg.of_node = spi->dev.of_node, + psy_cfg.drv_data = data, + + data->bat = power_supply_register_no_ws(&spi->dev, &bat_desc, &psy_cfg); + if (IS_ERR(data->bat)) + { + printk(KERN_ERR "%s: power_supply_register_no_ws failed\n", __func__); + return PTR_ERR(data->bat); + } + + return 0; +} + +static void spiRemove(struct spi_device *spi) +{ + T_PRIVATE_DATA *data = (T_PRIVATE_DATA *)spi_get_drvdata(spi); + + printk(KERN_INFO "%s: removing ... \n", "reform2-lpc"); + + device_remove_file(&spi->dev, &dev_attr_status); + device_remove_file(&spi->dev, &dev_attr_firmware); + device_remove_file(&spi->dev, &dev_attr_cells); + device_remove_file(&spi->dev, &dev_attr_capacity); + + power_supply_unregister(data->bat); + + kfree(data); +} + +static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t statusResult[8]; + int ret = 0; + int16_t voltage; + int16_t amps; + uint8_t percentage; + uint8_t status; + + ret = lpcCommand(dev, 'q', 0, statusResult); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + voltage = (int16_t)statusResult[0] | ((int16_t)statusResult[1] << 8); + amps = (int16_t)statusResult[2] | ((int16_t)statusResult[3] << 8); + percentage = statusResult[4]; + status = statusResult[5]; + + return snprintf(buf, PAGE_SIZE, "%d.%d %d.%d %2d%% %d", + voltage / 1000, voltage % 1000, amps / 1000, abs(amps % 1000), percentage, status); +} + +static ssize_t showCells(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t statusResult[8]; + uint16_t cells[8]; + int ret = 0; + ssize_t wroteChars = 0; + + ret = lpcCommand(dev, 'v', 0, statusResult); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + for(uint8_t s = 0; s < 4; s++) + { + cells[s] = statusResult[s*2] | statusResult[(s*2)+1] << 8; + } + + ret = lpcCommand(dev, 'v', 1, statusResult); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + for(uint8_t s = 0; s < 4; s++) + { + cells[s+4] = statusResult[s*2] | statusResult[(s*2)+1] << 8; + } + + for(uint8_t s = 0; s < 8; s++) + { + ret = snprintf(buf + wroteChars, PAGE_SIZE - wroteChars, "%d.%d ", cells[s]/1000, cells[s]%1000); + if(ret != -1) + { + wroteChars += ret; + } + } + + // drop the trailing whitespace + if(wroteChars > 0) + { + wroteChars--; + } + + return wroteChars; +} + +static ssize_t showFirmware(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t str1[9]; + uint8_t str2[9]; + uint8_t str3[9]; + int ret = 0; + + + ret = lpcCommand(dev, 'f', 0, str1); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + ret = lpcCommand(dev, 'f', 1, str2); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + ret = lpcCommand(dev, 'f', 2, str3); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + str1[8] = '\0'; + str2[8] = '\0'; + str3[8] = '\0'; + + return snprintf(buf, PAGE_SIZE, "%s %s %s", str1, str2, str3); +} + +static ssize_t showCapacity(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t buffer[8]; + int ret = 0; + uint16_t cap_accu, cap_min, cap_max; + ret = lpcCommand(dev, 'c', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + + // units in mAh + cap_accu = buffer[0] | (buffer[1] << 8); + cap_min = buffer[2] | (buffer[3] << 8); + cap_max = buffer[4] | (buffer[5] << 8); + + return snprintf(buf, PAGE_SIZE, "%d %d %d", cap_accu, cap_min, cap_max); +} + +static ssize_t lpcCommand(struct device *dev, char command, uint8_t arg1, uint8_t *responseBuffer) +{ + T_PRIVATE_DATA *data = (T_PRIVATE_DATA *)dev_get_drvdata(dev); + uint8_t commandBuffer[4] = {0xB5, command, arg1, 0x0}; + int ret = 0; + + mutex_lock(&data->lock); + + ret = spi_write(data->spi, commandBuffer, 4); + if (ret) + { + printk(KERN_INFO "%s: spi_write failed\n", __func__); + } + mdelay(70); + + ret = spi_read(data->spi, responseBuffer, 8); + if (ret) + { + printk(KERN_INFO "%s: spi_read failed\n", __func__); + } + mdelay(70); + mutex_unlock(&data->lock); + + return ret; +} + +static int bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + uint8_t buffer[8]; + T_PRIVATE_DATA *data; + struct device *dev; + int16_t temp = 0; + + data = (T_PRIVATE_DATA *)power_supply_get_drvdata(psy); + dev = &(data->spi->dev); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + ret = lpcCommand(dev, 'q', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + temp = (int16_t)buffer[2] | ((int16_t)buffer[3] << 8); + if(temp < 0) + { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } + else if(temp == 0) + { + if(buffer[4] == 100) + { + val->intval = POWER_SUPPLY_STATUS_FULL; + } + else + { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + } + else + { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = lpcCommand(dev, 'q', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = (buffer[0] | buffer[1] << 8) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = lpcCommand(dev, 'q', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = (buffer[2] | buffer[3] << 8) * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = lpcCommand(dev, 'q', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = buffer[4]; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = lpcCommand(dev, 'c', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = (buffer[4] | buffer[5] << 8) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = lpcCommand(dev, 'c', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = (buffer[0] | buffer[1] << 8) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + ret = lpcCommand(dev, 'c', 0, buffer); + if (ret) + { + printk(KERN_INFO "%s: lpcCommand failed\n", __func__); + } + val->intval = (buffer[2] | buffer[3] << 8) * 1000; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + ret = -EINVAL; + break; + } + return ret; +} + +// enum state_t { +// ST_CHARGE, -- charging +// ST_OVERVOLTED, -- charging? full? balancing +// ST_COOLDOWN, -- charging? full? balancing +// ST_UNDERVOLTED, -- cells under voltage, discharging? +// ST_MISSING, -- cells missing +// ST_FULLY_CHARGED, -- full charged +// ST_POWERSAVE -- deep sleep of lpc +// }; + +static const struct of_device_id of_tis_spi_match[] = { + {.compatible = "mntre,lpc11u24", .data = 0}, + {.compatible = "mntre,lpc-reform2", .data = 0}, + {}}; +MODULE_DEVICE_TABLE(of, of_tis_spi_match); + +static struct spi_device_id g_spi_dev_id_list[] = { + /* device name, device id */ + { "lpc11u24", 0 }, + { "lpc-reform2", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(spi, g_spi_dev_id_list); + +static struct spi_driver g_spi_driver = { + .probe = spiProbe, + .remove = spiRemove, + .driver = { + .of_match_table = of_match_ptr(of_tis_spi_match), + .owner = THIS_MODULE, + .name = "reform2-lpc", + }, + .id_table = g_spi_dev_id_list, +}; +module_spi_driver(g_spi_driver); + +MODULE_DESCRIPTION("Reform 2 LPC Driver"); +MODULE_LICENSE("GPL");