reform

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

reform2-lpc.c (12264B)


      1 #include <linux/module.h>
      2 #include <linux/slab.h>
      3 #include <linux/mutex.h>
      4 #include <linux/spi/spi.h>
      5 #include <linux/delay.h>
      6 #include <linux/power_supply.h>
      7 
      8 static int lpcProbe(struct spi_device *spi);
      9 static void lpcRemove(struct spi_device *spi);
     10 static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf);
     11 static ssize_t showCells(struct device *dev, struct device_attribute *attr, char *buf);
     12 static ssize_t showFirmware(struct device *dev, struct device_attribute *attr, char *buf);
     13 static ssize_t showCapacity(struct device *dev, struct device_attribute *attr, char *buf);
     14 
     15 static ssize_t lpcCommand(struct device *dev, char command, uint8_t arg1, uint8_t *response);
     16 static int getBatProperty(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val);
     17 
     18 typedef struct lpc_driver_data
     19 {
     20     struct spi_device *spi;
     21     struct power_supply *bat;
     22     struct mutex lock;
     23 } lpc_driver_data;
     24 
     25 static DEVICE_ATTR(status, 0444, showStatus, NULL);
     26 static DEVICE_ATTR(cells, 0444, showCells, NULL);
     27 static DEVICE_ATTR(firmware, 0444, showFirmware, NULL);
     28 static DEVICE_ATTR(capacity, 0444, showCapacity, NULL);
     29 
     30 static struct spi_board_info g_spi_board_info = {
     31     .modalias = "reform2-lpc",
     32     .max_speed_hz = 400000,
     33     .bus_num = 0,
     34     .chip_select = 0,
     35     .mode = SPI_MODE_1,
     36 };
     37 
     38 static enum power_supply_property bat_props[] = {
     39     POWER_SUPPLY_PROP_STATUS,
     40     POWER_SUPPLY_PROP_TECHNOLOGY,
     41     POWER_SUPPLY_PROP_VOLTAGE_NOW,
     42     POWER_SUPPLY_PROP_CURRENT_NOW,
     43     POWER_SUPPLY_PROP_CAPACITY,
     44     POWER_SUPPLY_PROP_CHARGE_FULL,
     45     POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
     46     POWER_SUPPLY_PROP_CHARGE_NOW,
     47     POWER_SUPPLY_PROP_CHARGE_EMPTY,
     48 };
     49 
     50 static struct power_supply_desc bat_desc = {
     51     .name = "8xlifepo4",
     52     .properties = bat_props,
     53     .num_properties = ARRAY_SIZE(bat_props),
     54     .get_property = getBatProperty,
     55     .type = POWER_SUPPLY_TYPE_BATTERY,
     56 };
     57 
     58 static struct power_supply_config psy_cfg = {};
     59 
     60 static int lpcProbe(struct spi_device *spi)
     61 {
     62     struct lpc_driver_data *data;
     63     int ret;
     64 
     65     printk(KERN_INFO "%s: probing ...\n", "reform2-lpc");
     66 
     67     spi->max_speed_hz = g_spi_board_info.max_speed_hz;
     68     spi->mode = g_spi_board_info.mode;
     69     spi->bits_per_word = 8;
     70 
     71     ret = spi_setup(spi);
     72     if (ret)
     73     {
     74         printk(KERN_ERR "%s: spi_setup failed\n", __func__);
     75         return -ENODEV;
     76     }
     77 
     78     data = kzalloc(sizeof(struct lpc_driver_data), GFP_KERNEL);
     79     if (data == NULL)
     80     {
     81         printk(KERN_ERR "%s: kzalloc failed\n", __func__);
     82         return -ENOMEM;
     83     }
     84 
     85     data->spi = spi;
     86     mutex_init(&data->lock);
     87     spi_set_drvdata(spi, data);
     88 
     89     ret = device_create_file(&spi->dev, &dev_attr_status);
     90     if (ret)
     91     {
     92         printk(KERN_ERR "%s: device_create_file failed\n", __func__);
     93     }
     94 
     95     ret = device_create_file(&spi->dev, &dev_attr_cells);
     96     if (ret)
     97     {
     98         printk(KERN_ERR "%s: device_create_file failed\n", __func__);
     99     }
    100 
    101     ret = device_create_file(&spi->dev, &dev_attr_firmware);
    102     if (ret)
    103     {
    104         printk(KERN_ERR "%s: device_create_file failed\n", __func__);
    105     }
    106 
    107     ret = device_create_file(&spi->dev, &dev_attr_capacity);
    108     if (ret)
    109     {
    110         printk(KERN_ERR "%s: device_create_file failed\n", __func__);
    111     }
    112 
    113     psy_cfg.of_node = spi->dev.of_node,
    114     psy_cfg.drv_data = data,
    115 
    116     data->bat = power_supply_register_no_ws(&spi->dev, &bat_desc, &psy_cfg);
    117     if (IS_ERR(data->bat))
    118     {
    119         printk(KERN_ERR "%s: power_supply_register_no_ws failed\n", __func__);
    120         return PTR_ERR(data->bat);
    121     }
    122 
    123     return ret;
    124 }
    125 
    126 static void lpcRemove(struct spi_device *spi)
    127 {
    128     struct lpc_driver_data *data = (struct lpc_driver_data *)spi_get_drvdata(spi);
    129 
    130     printk(KERN_INFO "%s: removing ... \n", "reform2-lpc");
    131 
    132     device_remove_file(&spi->dev, &dev_attr_status);
    133     device_remove_file(&spi->dev, &dev_attr_firmware);
    134     device_remove_file(&spi->dev, &dev_attr_cells);
    135     device_remove_file(&spi->dev, &dev_attr_capacity);
    136 
    137     power_supply_unregister(data->bat);
    138 
    139     kfree(data);
    140 }
    141 
    142 static ssize_t showStatus(struct device *dev, struct device_attribute *attr, char *buf)
    143 {
    144     uint8_t buffer[8];
    145     int16_t voltage;
    146     int16_t amps;
    147     uint8_t percentage;
    148     uint8_t status;
    149     int ret = 0;
    150 
    151     ret = lpcCommand(dev, 'q', 0, buffer);
    152     if (ret)
    153     {
    154         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    155     }
    156 
    157     voltage = (int16_t)buffer[0] | ((int16_t)buffer[1] << 8);
    158     amps = (int16_t)buffer[2] | ((int16_t)buffer[3] << 8);
    159     percentage = buffer[4];
    160     status = buffer[5];
    161 
    162     return snprintf(buf, PAGE_SIZE, "%d.%d %d.%d %2d%% %d",
    163                     voltage / 1000, voltage % 1000, 
    164                     amps / 1000, abs(amps % 1000), 
    165                     percentage, status);
    166 }
    167 
    168 static ssize_t showCells(struct device *dev, struct device_attribute *attr, char *buf)
    169 {
    170     uint8_t buffer[8];
    171     uint16_t cells[8];
    172     ssize_t wroteChars = 0;
    173     int ret = 0;
    174 
    175     ret = lpcCommand(dev, 'v', 0, buffer);
    176     if (ret)
    177     {
    178         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    179     }
    180 
    181     for(uint8_t s = 0; s < 4; s++)
    182     {
    183         cells[s] = buffer[s*2] | buffer[(s*2)+1] << 8;
    184     }
    185     
    186     ret = lpcCommand(dev, 'v', 1, buffer);
    187     if (ret)
    188     {
    189         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    190     }
    191 
    192     for(uint8_t s = 0; s < 4; s++)
    193     {
    194         cells[s+4] = buffer[s*2] | buffer[(s*2)+1] << 8;
    195     }
    196 
    197     for(uint8_t s = 0; s < 8; s++)
    198     {
    199         ret = snprintf(buf + wroteChars, PAGE_SIZE - wroteChars, "%d.%d ", cells[s]/1000, cells[s]%1000);
    200         if(ret != -1)
    201         {
    202             wroteChars += ret;
    203         }
    204     }
    205 
    206     // drop the trailing whitespace
    207     if(wroteChars > 0)
    208     {
    209         wroteChars--;
    210     }
    211     
    212     return wroteChars;
    213 }
    214 
    215 static ssize_t showFirmware(struct device *dev, struct device_attribute *attr, char *buf)
    216 {
    217     uint8_t str1[9];
    218     uint8_t str2[9];
    219     uint8_t str3[9];
    220     int ret = 0;
    221     
    222     ret = lpcCommand(dev, 'f', 0, str1);
    223     if (ret)
    224     {
    225         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    226     }
    227 
    228     ret = lpcCommand(dev, 'f', 1, str2);
    229     if (ret)
    230     {
    231         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    232     }
    233 
    234     ret = lpcCommand(dev, 'f', 2, str3);
    235     if (ret)
    236     {
    237         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    238     }
    239 
    240     str1[8] = '\0';
    241     str2[8] = '\0';
    242     str3[8] = '\0';
    243 
    244     return snprintf(buf, PAGE_SIZE, "%s %s %s", str1, str2, str3);
    245 }
    246 
    247 static ssize_t showCapacity(struct device *dev, struct device_attribute *attr, char *buf)
    248 {
    249     uint8_t buffer[8];
    250     int ret = 0;
    251     uint16_t cap_accu_mah, cap_min_mah, cap_max_mah;
    252     ret = lpcCommand(dev, 'c', 0, buffer);
    253     if (ret)
    254     {
    255         printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    256     }
    257 
    258     cap_accu_mah = buffer[0] | (buffer[1] << 8);
    259     cap_min_mah =  buffer[2] | (buffer[3] << 8);
    260     cap_max_mah = buffer[4] | (buffer[5] << 8);
    261 
    262     return snprintf(buf, PAGE_SIZE, "%d %d %d", cap_accu_mah, cap_min_mah, cap_max_mah);
    263 }
    264 
    265 static ssize_t lpcCommand(struct device *dev, char command, uint8_t arg1, uint8_t *responseBuffer)
    266 {
    267     struct lpc_driver_data *data = (struct lpc_driver_data *)dev_get_drvdata(dev);
    268     uint8_t commandBuffer[4] = {0xB5, command, arg1, 0x0};
    269     int ret = 0;
    270 
    271     mutex_lock(&data->lock);
    272     
    273     ret = spi_write(data->spi, commandBuffer, 4);
    274     if (ret)
    275     {
    276         printk(KERN_INFO "%s: spi_write failed\n", __func__);
    277     }
    278     msleep(50);
    279 
    280     ret = spi_read(data->spi, responseBuffer, 8);
    281     if (ret)
    282     {
    283         printk(KERN_INFO "%s: spi_read failed\n", __func__);
    284     }
    285     msleep(50);
    286     mutex_unlock(&data->lock);
    287 
    288     return ret;
    289 }
    290 
    291 static int getBatProperty(struct power_supply *psy,
    292                     enum power_supply_property psp,
    293                     union power_supply_propval *val)
    294 {
    295     int ret = 0;
    296     uint8_t buffer[8];
    297     struct lpc_driver_data *data;
    298     struct device *dev;
    299     int16_t amp;
    300 
    301     data = (struct lpc_driver_data *)power_supply_get_drvdata(psy);
    302     dev = &(data->spi->dev);
    303 
    304     switch (psp) 
    305     {
    306         case POWER_SUPPLY_PROP_STATUS:
    307             val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
    308             ret = lpcCommand(dev, 'q', 0, buffer);
    309             if (ret)
    310             {
    311                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    312             }
    313             amp = (int16_t)buffer[2] | ((int16_t)buffer[3] << 8);
    314             if (amp < 0)
    315             {
    316                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
    317             }
    318             else if (amp == 0)
    319             {
    320                 if (buffer[4] == 100)
    321                 {
    322                     val->intval = POWER_SUPPLY_STATUS_FULL;
    323                 }
    324                 else
    325                 {
    326                     val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    327                 }
    328             }
    329             else
    330             {
    331                 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    332             }
    333             break;
    334             
    335         case POWER_SUPPLY_PROP_TECHNOLOGY:
    336             val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
    337             break;
    338             
    339         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    340             ret = lpcCommand(dev, 'q', 0, buffer);
    341             if (ret)
    342             {
    343                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    344                 ret = -EINVAL;
    345             }
    346             val->intval = (buffer[0] | buffer[1] << 8) * 1000;
    347             break;
    348             
    349         case POWER_SUPPLY_PROP_CURRENT_NOW:
    350             ret = lpcCommand(dev, 'q', 0, buffer);
    351             if (ret)
    352             {
    353                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    354                 ret = -EINVAL;
    355             }
    356             amp = (int16_t)buffer[2] | ((int16_t)buffer[3] << 8);
    357             // negative current, battery is charging
    358             // reporting a negative value is out of spec
    359             if(amp < 0)
    360             {
    361                 amp = 0;
    362             }
    363             val->intval = amp * 1000;
    364             break;
    365             
    366         case POWER_SUPPLY_PROP_CAPACITY:
    367             ret = lpcCommand(dev, 'q', 0, buffer);
    368             if (ret)
    369             {
    370                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    371                 ret = -EINVAL;
    372             }
    373             val->intval = buffer[4];
    374             break;
    375             
    376         case POWER_SUPPLY_PROP_CHARGE_FULL:
    377         case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
    378             ret = lpcCommand(dev, 'c', 0, buffer);
    379             if (ret)
    380             {
    381                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    382                 ret = -EINVAL;
    383             }
    384             val->intval = (buffer[4] | buffer[5] << 8) * 1000;
    385             break;
    386             
    387         case POWER_SUPPLY_PROP_CHARGE_NOW:
    388             ret = lpcCommand(dev, 'c', 0, buffer);
    389             if (ret)
    390             {
    391                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    392                 ret = -EINVAL;
    393             }
    394             val->intval = (buffer[0] | buffer[1] << 8) * 1000;
    395             break;
    396             
    397         case POWER_SUPPLY_PROP_CHARGE_EMPTY:
    398             ret = lpcCommand(dev, 'c', 0, buffer);
    399             if (ret)
    400             {
    401                 printk(KERN_INFO "%s: lpcCommand failed\n", __func__);
    402                 ret = -EINVAL;
    403             }
    404             val->intval = (buffer[2] | buffer[3] << 8) * 1000;
    405             break;
    406             
    407         default:
    408             val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
    409             ret = -EINVAL;
    410             break;
    411     }
    412     return ret;
    413 }
    414 
    415 static const struct of_device_id of_tis_spi_match[] = {
    416     {.compatible = "mntre,lpc11u24", .data = 0},
    417     {}};
    418 MODULE_DEVICE_TABLE(of, of_tis_spi_match);
    419 
    420 static struct spi_device_id g_spi_dev_id_list[] = {
    421         { "lpc11u24", 0 },
    422         { },
    423 };
    424 MODULE_DEVICE_TABLE(spi, g_spi_dev_id_list);
    425 
    426 static struct spi_driver g_spi_driver = {
    427     .probe = lpcProbe,
    428     .remove = lpcRemove,
    429     .driver = {
    430         .of_match_table = of_match_ptr(of_tis_spi_match),
    431         .owner = THIS_MODULE,
    432         .name = "reform2-lpc",
    433     },
    434     .id_table = g_spi_dev_id_list,
    435 };
    436 module_spi_driver(g_spi_driver);
    437 
    438 MODULE_DESCRIPTION("Reform 2 LPC Driver");
    439 MODULE_LICENSE("GPL");