Skip to content

Commit

Permalink
Support power configuration.
Browse files Browse the repository at this point in the history
Add configuration option to prohibit power off and power on when charger
is attached.
  • Loading branch information
surban committed Aug 31, 2023
1 parent 4874a92 commit ecdb7ca
Show file tree
Hide file tree
Showing 16 changed files with 500 additions and 12 deletions.
2 changes: 2 additions & 0 deletions openemc-driver/openemc.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
/* Power register definitions */
#define OPENEMC_POWER_OFF 0x40
#define OPENEMC_POWER_RESTART 0x41
#define OPENEMC_POWER_OFF_PROHIBITED 0x42
#define OPENEMC_POWER_ON_BY_CHARGING 0x43

/* GPIO register definitions */
#define OPENEMC_GPIO_COUNT 0x50
Expand Down
6 changes: 5 additions & 1 deletion openemc-driver/openemc_gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ static int openemc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
return irq_create_fwspec_mapping(&fwspec);
}

#if defined(CONFIG_OF_GPIO)
static int openemc_gpio_of_xlate(struct gpio_chip *chip,
const struct of_phandle_args *gpio_desc,
u32 *flags)
Expand All @@ -226,6 +227,7 @@ static int openemc_gpio_of_xlate(struct gpio_chip *chip,

return gpio_desc->args[0];
}
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
static int openemc_gpio_of_gpio_ranges_fallback(struct gpio_chip *gc,
Expand Down Expand Up @@ -261,11 +263,13 @@ static const struct gpio_chip openemc_gpio_template = {
.set_config = gpiochip_generic_config,
.init_valid_mask = openemc_gpio_init_valid_mask,
.to_irq = openemc_gpio_to_irq,
.of_xlate = openemc_gpio_of_xlate,
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)
.of_gpio_ranges_fallback = openemc_gpio_of_gpio_ranges_fallback,
#endif
#if defined(CONFIG_OF_GPIO)
.of_gpio_n_cells = 2,
.of_xlate = openemc_gpio_of_xlate,
#endif
.base = -1,
.can_sleep = true,
};
Expand Down
81 changes: 81 additions & 0 deletions openemc-driver/openemc_power.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,83 @@ static int openemc_power_restart(struct notifier_block *nb,
return NOTIFY_DONE;
}

static ssize_t openemc_power_power_off_prohibited_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct openemc_power *power = dev_get_drvdata(dev);
u8 value;
int ret;

ret = openemc_read_u8(power->emc, OPENEMC_POWER_OFF_PROHIBITED, &value);
if (ret != 0)
return ret;

return sprintf(buf, "%hhu", value);
}
static ssize_t
openemc_power_power_off_prohibited_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct openemc_power *power = dev_get_drvdata(dev);
u8 value;
int ret;

ret = sscanf(buf, "%hhu", &value);
if (ret != 1)
return -EINVAL;

ret = openemc_read_u8(power->emc, OPENEMC_POWER_OFF_PROHIBITED, &value);
if (ret != 0)
return ret;

return 0;
}
static DEVICE_ATTR_RW(openemc_power_power_off_prohibited);

static ssize_t openemc_power_power_on_by_charging_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct openemc_power *power = dev_get_drvdata(dev);
u8 value;
int ret;

ret = openemc_read_u8(power->emc, OPENEMC_POWER_ON_BY_CHARGING, &value);
if (ret != 0)
return ret;

return sprintf(buf, "%hhu", value);
}
static ssize_t
openemc_power_power_on_by_charging_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct openemc_power *power = dev_get_drvdata(dev);
u8 value;
int ret;

ret = sscanf(buf, "%hhu", &value);
if (ret != 1)
return -EINVAL;

ret = openemc_read_u8(power->emc, OPENEMC_POWER_ON_BY_CHARGING, &value);
if (ret != 0)
return ret;

return 0;
}
static DEVICE_ATTR_RW(openemc_power_power_on_by_charging);

static struct attribute *openemc_power_attrs[] = {
&dev_attr_openemc_power_power_off_prohibited.attr,
&dev_attr_openemc_power_power_on_by_charging.attr, NULL
};

static const struct attribute_group openemc_power_group = {
.attrs = openemc_power_attrs,
};

static int openemc_power_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
Expand Down Expand Up @@ -213,6 +290,10 @@ static int openemc_power_probe(struct platform_device *pdev)
"OpenEMC handles system power off and restart");
}

ret = devm_device_add_group(&pdev->dev, &openemc_power_group);
if (ret < 0)
return ret;

dev_info(power->dev, "OpenEMC power control registered");

return 0;
Expand Down
7 changes: 7 additions & 0 deletions openemc-firmware/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions openemc-firmware/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ defmt = { version = "0.3", features = ["encoding-rzcobs"] }
defmt-rtt = { version = "0.4", optional = true }
nb = "1.0"
heapless = "0.7"
embedded-crc32c = "0.1"

[dev-dependencies]
defmt-test = "0.3"
Expand Down
2 changes: 2 additions & 0 deletions openemc-firmware/memory-big.x
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ SECTIONS
} > RAM
}
INSERT BEFORE .data;

__flash_end = ORIGIN(FLASH) + LENGTH(FLASH);
2 changes: 2 additions & 0 deletions openemc-firmware/memory-normal.x
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ SECTIONS
} > RAM
}
INSERT BEFORE .data;

__flash_end = ORIGIN(FLASH) + LENGTH(FLASH);
2 changes: 2 additions & 0 deletions openemc-firmware/memory-standalone.x
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ SECTIONS
} > RAM
}
INSERT BEFORE .data;

__flash_end = ORIGIN(FLASH) + LENGTH(FLASH);
3 changes: 2 additions & 1 deletion openemc-firmware/src/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use stm32f1xx_hal::{afio, i2c};
use crate::{
boot,
bq25713::Bq25713Cfg,
cfg::Cfg,
i2c_reg_slave,
supply::{max14636::Max14636, FixedSinkPdo},
Delay, Duration, PowerMode, I2C_BUFFER_SIZE,
Expand Down Expand Up @@ -71,7 +72,7 @@ pub trait Board {
const CHARGING_LED_MIN_CURRENT: i32 = 0;

/// Create a new instance.
fn new(boot_info: &'static BootInfo, afio: &mut afio::Parts, delay: &mut Delay) -> Self;
fn new(boot_info: &'static BootInfo, afio: &mut afio::Parts, delay: &mut Delay, cfg: &Cfg) -> Self;

/// Returns whether the board id is supported.
fn is_supported(&self, model: &[u8]) -> bool {
Expand Down
3 changes: 2 additions & 1 deletion openemc-firmware/src/boards/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
use openemc_shared::BootInfo;
use stm32f1xx_hal::afio;

use crate::cfg::Cfg;
use crate::{board::Board, Delay};

/// Generic board.
pub struct BoardImpl;

impl Board for BoardImpl {
fn new(_boot_info: &'static BootInfo, _afio: &mut afio::Parts, _delay: &mut Delay) -> BoardImpl {
fn new(_boot_info: &'static BootInfo, _afio: &mut afio::Parts, _delay: &mut Delay, _cfg: &Cfg) -> BoardImpl {
Self
}

Expand Down
3 changes: 2 additions & 1 deletion openemc-firmware/src/boards/stm_nucleo_f103rb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use stm32f1xx_hal::{
};
use systick_monotonic::fugit::Rate;

use crate::cfg::Cfg;
use crate::{
board::{Board, UnknownI2cRegister, PORTS},
i2c_reg_slave::Response,
Expand All @@ -37,7 +38,7 @@ impl Board for BoardImpl {
const STUSB4500_I2C_ADDR: Option<u8> = Some(0x28);
const USB_MAXIMUM_VOLTAGE: u32 = 10_000;

fn new(boot_info: &'static BootInfo, afio: &mut afio::Parts, delay: &mut Delay) -> BoardImpl {
fn new(boot_info: &'static BootInfo, afio: &mut afio::Parts, delay: &mut Delay, cfg: &Cfg) -> BoardImpl {
let mut dp = unsafe { Peripherals::steal() };
let mut gpioa = unsafe { dp.GPIOA.split_without_reset() };
let mut gpiob = unsafe { dp.GPIOB.split_without_reset() };
Expand Down
68 changes: 68 additions & 0 deletions openemc-firmware/src/cfg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//! Configuration.

use core::{mem::MaybeUninit, ptr::addr_of_mut};
use defmt::Format;

use crate::flash_data::Validate;

/// Behavior when charger is attached while device is powered off.
#[derive(Default, Format, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ChargerAttached {
/// Go into charge mode.
#[default]
ChargeMode = 0,
/// Power on device.
PowerOn = 1,
}

impl TryFrom<u8> for ChargerAttached {
type Error = ();

fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::ChargeMode),
1 => Ok(Self::PowerOn),
_ => Err(()),
}
}
}

/// Configuration stored in flash.
#[derive(Format, Clone, Copy)]
#[repr(C)]
pub struct Cfg {
/// Behavior when charger is attached while device is powered off.
pub charger_attached: ChargerAttached,
/// Whether power off is prohibited.
///
/// If true, power off requests are converted into resets.
pub prohibit_power_off: bool,
}

impl Cfg {
/// Log configuration change.
pub fn log(&self) {
defmt::info!("Configuration changed: {:?}", self);
}
}

impl Validate for Cfg {
fn validate(mut this: MaybeUninit<Self>) -> Self {
unsafe {
let ptr = this.as_mut_ptr();

let charger_attached_ptr = addr_of_mut!((*ptr).charger_attached) as *mut u8;
if charger_attached_ptr.read() > 1 {
charger_attached_ptr.write(0);
}

let prohibit_power_off_ptr = addr_of_mut!((*ptr).prohibit_power_off) as *mut u8;
if prohibit_power_off_ptr.read() > 1 {
prohibit_power_off_ptr.write(0);
}

this.assume_init()
}
}
}
Loading

0 comments on commit ecdb7ca

Please sign in to comment.