/**************************************************************************** * drivers/power/battery/goldfish_battery.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define GOLDFISH_BATTERY_READ(data, addr) \ (*(FAR volatile uint32_t *)(data->reg_base + addr)) #define GOLDFISH_BATTERY_WRITE(data, addr, x) \ (*(FAR volatile uint32_t *)(data->reg_base + addr) = (x)) #define GOLDFISH_GAUGE "/dev/charge/goldfish_battery" /**************************************************************************** * Private type ****************************************************************************/ enum { /* Status register */ BATTERY_INT_STATUS = 0x00, /* Set this to enable IRQ */ BATTERY_INT_ENABLE = 0x04, BATTERY_AC_ONLINE = 0x08, BATTERY_STATUS = 0x0c, BATTERY_HEALTH = 0x10, BATTERY_PRESENT = 0x14, BATTERY_CAPACITY = 0x18, BATTERY_VOLTAGE = 0x1c, BATTERY_TEMP = 0x20, BATTERY_CHARGE_COUNTER = 0x24, BATTERY_VOLTAGE_MAX = 0x28, BATTERY_CURRENT_MAX = 0x2c, BATTERY_CURRENT_NOW = 0x30, BATTERY_CURRENT_AVG = 0x34, BATTERY_CHARGE_FULL_UAH = 0x38, BATTERY_CYCLE_COUNT = 0x40, BATTERY_STATUS_CHANGED = 1u << 0, AC_STATUS_CHANGED = 1u << 1, BATTERY_INT_MASK = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED, }; enum { POWER_SUPPLY_STATUS_UNKNOWN, POWER_SUPPLY_STATUS_CHARGING, POWER_SUPPLY_STATUS_DISCHARGING, POWER_SUPPLY_STATUS_NOT_CHARGING, POWER_SUPPLY_STATUS_FULL, }; struct goldfish_battery_data_s { FAR void *reg_base; int irq; struct battery_gauge_dev_s battery; struct work_s work; }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ static int goldfish_charger_online(FAR struct battery_gauge_dev_s *dev, FAR bool *status); static int goldfish_battery_state(FAR struct battery_gauge_dev_s *dev, FAR int *status); static int goldfish_battery_voltage(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value); static int goldfish_battery_capacity(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value); static int goldfish_battery_current(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value); static int goldfish_battery_temp(FAR struct battery_gauge_dev_s *dev, FAR b8_t *value); /**************************************************************************** * Private Data ****************************************************************************/ static const struct battery_gauge_operations_s g_goldfish_gauge_ops = { goldfish_battery_state, goldfish_charger_online, goldfish_battery_voltage, goldfish_battery_capacity, goldfish_battery_current, goldfish_battery_temp, NULL, }; /**************************************************************************** * Private Functions ****************************************************************************/ static int goldfish_charger_online(FAR struct battery_gauge_dev_s *dev, FAR bool *status) { FAR struct goldfish_battery_data_s *priv = container_of(dev, struct goldfish_battery_data_s, battery); *status = GOLDFISH_BATTERY_READ(priv, BATTERY_AC_ONLINE) & 0x1; return OK; } static int goldfish_battery_state(FAR struct battery_gauge_dev_s *dev, FAR int *status) { FAR struct goldfish_battery_data_s *priv = container_of(dev, struct goldfish_battery_data_s, battery); uint32_t regval; regval = GOLDFISH_BATTERY_READ(priv, BATTERY_STATUS); switch (regval) { case POWER_SUPPLY_STATUS_UNKNOWN: *status = BATTERY_UNKNOWN; break; case POWER_SUPPLY_STATUS_CHARGING: *status = BATTERY_CHARGING; break; case POWER_SUPPLY_STATUS_DISCHARGING: *status = BATTERY_DISCHARGING; break; case POWER_SUPPLY_STATUS_NOT_CHARGING: *status = BATTERY_IDLE; break; case POWER_SUPPLY_STATUS_FULL: *status = BATTERY_FULL; break; default: *status = BATTERY_UNKNOWN; break; } return OK; } static int goldfish_battery_voltage(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value) { FAR struct goldfish_battery_data_s *data = container_of(dev, struct goldfish_battery_data_s, battery); uint32_t regval; float vol; /* BATTERY_VOLTAGE units is µV */ regval = GOLDFISH_BATTERY_READ(data, BATTERY_VOLTAGE); /* convert to unit V and fill b16_t */ vol = regval / 1000000.0f; *value = ftob16(vol); return OK; } static int goldfish_battery_capacity(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value) { FAR struct goldfish_battery_data_s *data = container_of(dev, struct goldfish_battery_data_s, battery); uint32_t regval; /* BATTERY_CAPACITY units is percentage */ regval = GOLDFISH_BATTERY_READ(data, BATTERY_CAPACITY); *value = uitoub16(regval); return OK; } static int goldfish_battery_current(FAR struct battery_gauge_dev_s *dev, FAR b16_t *value) { FAR struct goldfish_battery_data_s *data = container_of(dev, struct goldfish_battery_data_s, battery); uint32_t regval; float current; /* BATTERY_CURRENT_NOW units is µA */ regval = GOLDFISH_BATTERY_READ(data, BATTERY_CURRENT_NOW); /* convert to unit mA and fill b16_t */ current = regval / 1000.0f; *value = ftob16(current); return OK; } static int goldfish_battery_temp(FAR struct battery_gauge_dev_s *dev, FAR b8_t *value) { FAR struct goldfish_battery_data_s *data = container_of(dev, struct goldfish_battery_data_s, battery); int32_t regval; float temp; /* BATTERY_TEMP units is 0.1 celsuis */ regval = GOLDFISH_BATTERY_READ(data, BATTERY_TEMP); /* convert to unit celsuis and fill b16_t */ temp = regval / 10.0f; *value = ftob8(temp); return OK; } static void goldfish_battery_work(FAR void *arg) { FAR struct goldfish_battery_data_s *data = arg; uint32_t mask = BATTERY_STATE_CHANGED | BATTERY_VOLTAGE_CHANGED | BATTERY_CURRENT_CHANGED | BATTERY_CAPACITY_CHANGED | BATTERY_TEMPERATURE_CHANGED | BATTERY_ONLINE_CHANGED; int ret; ret = battery_gauge_changed(&data->battery, mask); if (ret < 0) { baterr("goldfish battery changed failed %d\n", ret); } return; } static int goldfish_battery_interrupt(int irq, FAR void *context, void *arg) { FAR struct goldfish_battery_data_s *data = arg; uint32_t status; int ret; /* read status flags, which will clear the interrupt */ status = GOLDFISH_BATTERY_READ(data, BATTERY_INT_STATUS); status &= BATTERY_INT_MASK; if (status) { ret = work_queue(HPWORK, &data->work, goldfish_battery_work, data, 0); if (ret < 0) { baterr("battery interrupt %"PRIu32 "work_battery ret:%d\n", status, ret); } } return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ int goldfish_battery_register(FAR void *regs, int irq) { int ret; FAR struct goldfish_battery_data_s *data; if (NULL == regs || irq < 0) { baterr(" regs:%p irq:%d\n", regs, irq); return -EINVAL; } data = kmm_zalloc(sizeof(struct goldfish_battery_data_s)); if (NULL == data) { baterr(" no enough memory\n"); return -ENOMEM; } data->irq = irq; data->reg_base = regs; ret = irq_attach(data->irq, goldfish_battery_interrupt, data); if (ret < 0) { baterr(" attach irq %d failed\n", irq); goto fail; } data->battery.ops = &g_goldfish_gauge_ops; ret = battery_gauge_register(GOLDFISH_GAUGE, &data->battery); if (ret < 0) { baterr("battery_gauge_register %s failed", GOLDFISH_GAUGE); irq_detach(data->irq); goto fail; } GOLDFISH_BATTERY_WRITE(data, BATTERY_INT_ENABLE, BATTERY_INT_MASK); up_enable_irq(data->irq); batinfo("goldfish_battery_register over"); return 0; fail: kmm_free(data); return ret; }