capsules_extra/
max17205.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! SyscallDriver for the Maxim MAX17205 fuel gauge.
6//!
7//! <https://www.maximintegrated.com/en/products/power/battery-management/MAX17205.html>
8//!
9//! > The MAX1720x/MAX1721x are ultra-low power stand-alone fuel gauge ICs that
10//! > implement the Maxim ModelGauge™ m5 algorithm without requiring host
11//! > interaction for configuration. This feature makes the MAX1720x/MAX1721x
12//! > excellent pack-side fuel gauges. The MAX17201/MAX17211 monitor a single
13//! > cell pack. The MAX17205/MAX17215 monitor and balance a 2S or 3S pack or
14//! > monitor a multiple-series cell pack.
15//!
16//! Usage
17//! -----
18//!
19//! ```rust,ignore
20//! # use kernel::static_init;
21//!
22//! // Two i2c addresses are necessary.
23//! // Registers 0x000-0x0FF are accessed by address 0x36.
24//! // Registers 0x100-0x1FF are accessed by address 0x0B.
25//! let max17205_i2c_lower = static_init!(
26//!     capsules::virtual_i2c::I2CDevice,
27//!     capsules::virtual_i2c::I2CDevice::new(i2c_bus, 0x36));
28//! let max17205_i2c_upper = static_init!(
29//!     capsules::virtual_i2c::I2CDevice,
30//!     capsules::virtual_i2c::I2CDevice::new(i2c_bus, 0x0B));
31//! let max17205_buffer = static_init!([u8; capsules::max17205::BUFFER_LENGTH],
32//!                                    [0; capsules::max17205::BUFFER_LENGTH]);
33//! let max17205 = static_init!(
34//!     capsules::max17205::MAX17205<'static>,
35//!     capsules::max17205::MAX17205::new(max17205_i2c_lower, max17205_i2c_upper,
36//!                                       max17205_buffer));
37//! max17205_i2c.set_client(max17205);
38//!
39//! // For userspace.
40//! let max17205_driver = static_init!(
41//!     capsules::max17205::MAX17205Driver<'static>,
42//!     capsules::max17205::MAX17205Driver::new(max17205));
43//! max17205.set_client(max17205_driver);
44//! ```
45
46use core::cell::Cell;
47
48use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
49use kernel::hil::i2c;
50use kernel::syscall::{CommandReturn, SyscallDriver};
51use kernel::utilities::cells::{OptionalCell, TakeCell};
52use kernel::{ErrorCode, ProcessId};
53
54/// Syscall driver number.
55use capsules_core::driver;
56pub const DRIVER_NUM: usize = driver::NUM::Max17205 as usize;
57
58pub const BUFFER_LENGTH: usize = 8;
59
60// Addresses 0x000 - 0x0FF, 0x180 - 0x1FF can be written as blocks
61// Addresses 0x100 - 0x17F must be written by word
62
63// Addresses 0x000 - 0x0FF should use the i2c_lower device
64// Addresses 0x100 - 0x1FF should use the i2c_upper device
65enum Registers {
66    Status = 0x000,
67    RepCap = 0x005, // Reported capacity, LSB = 0.5 mAh
68    //RepSOC = 0x006, // Reported capacity, LSB = %/256
69    FullCapRep = 0x035, // Maximum capacity, LSB = 0.5 mAh
70    //NPackCfg = 0x1B5, // Pack configuration
71    NRomID = 0x1BC, //RomID - 64bit unique
72    //NRSense = 0x1CF, // Sense resistor
73    Batt = 0x0DA,    // Pack voltage, LSB = 1.25mV
74    Current = 0x00A, // Instantaneous current, LSB = 156.25 uA
75    Coulomb = 0x04D,
76}
77
78#[derive(Clone, Copy, PartialEq)]
79enum State {
80    Idle,
81
82    /// Simple read states
83    SetupReadCoulomb,
84    ReadCoulomb,
85    SetupReadStatus,
86    ReadStatus,
87    SetupReadSOC,
88    ReadSOC,
89    SetupReadCap,
90    ReadCap,
91    SetupReadVolt,
92    ReadVolt,
93    SetupReadCurrent,
94    ReadCurrent,
95    SetupReadRomID,
96    ReadRomID,
97}
98
99pub trait MAX17205Client {
100    fn status(&self, status: u16, error: Result<(), ErrorCode>);
101    fn state_of_charge(
102        &self,
103        percent: u16,
104        capacity: u16,
105        full_capacity: u16,
106        error: Result<(), ErrorCode>,
107    );
108    fn voltage_current(&self, voltage: u16, current: u16, error: Result<(), ErrorCode>);
109    fn coulomb(&self, coulomb: u16, error: Result<(), ErrorCode>);
110    fn romid(&self, rid: u64, error: Result<(), ErrorCode>);
111}
112
113pub struct MAX17205<'a, I: i2c::I2CDevice> {
114    i2c_lower: &'a I,
115    i2c_upper: &'a I,
116    state: Cell<State>,
117    soc: Cell<u16>,
118    soc_mah: Cell<u16>,
119    voltage: Cell<u16>,
120    buffer: TakeCell<'static, [u8]>,
121    client: OptionalCell<&'static dyn MAX17205Client>,
122}
123
124impl<'a, I: i2c::I2CDevice> MAX17205<'a, I> {
125    pub fn new(i2c_lower: &'a I, i2c_upper: &'a I, buffer: &'static mut [u8]) -> MAX17205<'a, I> {
126        MAX17205 {
127            i2c_lower,
128            i2c_upper,
129            state: Cell::new(State::Idle),
130            soc: Cell::new(0),
131            soc_mah: Cell::new(0),
132            voltage: Cell::new(0),
133            buffer: TakeCell::new(buffer),
134            client: OptionalCell::empty(),
135        }
136    }
137
138    pub fn set_client<C: MAX17205Client>(&self, client: &'static C) {
139        self.client.set(client);
140    }
141
142    fn setup_readstatus(&self) -> Result<(), ErrorCode> {
143        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
144            self.i2c_lower.enable();
145
146            buffer[0] = Registers::Status as u8;
147
148            // TODO verify errors
149            let _ = self.i2c_lower.write(buffer, 2);
150            self.state.set(State::SetupReadStatus);
151
152            Ok(())
153        })
154    }
155
156    fn setup_read_soc(&self) -> Result<(), ErrorCode> {
157        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
158            self.i2c_lower.enable();
159
160            // Get SOC mAh and percentage
161            // Write reqcap address
162            buffer[0] = Registers::RepCap as u8;
163            // TODO verify errors
164            let _ = self.i2c_lower.write(buffer, 1);
165            self.state.set(State::SetupReadSOC);
166
167            Ok(())
168        })
169    }
170
171    fn setup_read_curvolt(&self) -> Result<(), ErrorCode> {
172        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
173            self.i2c_lower.enable();
174
175            // Get current and voltage
176            // Write Batt address
177            buffer[0] = Registers::Batt as u8;
178            // TODO verify errors
179            let _ = self.i2c_lower.write(buffer, 1);
180            self.state.set(State::SetupReadVolt);
181
182            Ok(())
183        })
184    }
185
186    fn setup_read_coulomb(&self) -> Result<(), ErrorCode> {
187        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
188            self.i2c_lower.enable();
189
190            // Get raw coulomb count.
191            // Write Coulomb address
192            buffer[0] = Registers::Coulomb as u8;
193            // TODO verify errors
194            let _ = self.i2c_lower.write(buffer, 1);
195            self.state.set(State::SetupReadCoulomb);
196
197            Ok(())
198        })
199    }
200
201    fn setup_read_romid(&self) -> Result<(), ErrorCode> {
202        self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
203            self.i2c_upper.enable();
204            let nrom_id = Registers::NRomID as u16;
205
206            buffer[0] = (nrom_id & 0xFF) as u8;
207            buffer[1] = (nrom_id >> 8) as u8;
208            // TODO verify errors
209            let _ = self.i2c_upper.write(buffer, 1);
210            self.state.set(State::SetupReadRomID);
211
212            Ok(())
213        })
214    }
215}
216
217impl<I: i2c::I2CDevice> i2c::I2CClient for MAX17205<'_, I> {
218    fn command_complete(&self, buffer: &'static mut [u8], error: Result<(), i2c::Error>) {
219        match self.state.get() {
220            State::SetupReadStatus => {
221                // Read status
222                // TODO verify errors
223                let _ = self.i2c_lower.read(buffer, 2);
224                self.state.set(State::ReadStatus);
225            }
226            State::ReadStatus => {
227                let status = ((buffer[1] as u16) << 8) | (buffer[0] as u16);
228
229                self.client.map(|client| {
230                    client.status(
231                        status,
232                        match error {
233                            Ok(()) => Ok(()),
234                            Err(e) => Err(e.into()),
235                        },
236                    )
237                });
238
239                self.buffer.replace(buffer);
240                self.i2c_lower.disable();
241                self.state.set(State::Idle);
242            }
243            State::SetupReadSOC => {
244                // Write of SOC memory address complete, now issue read
245                // TODO verify errors
246                let _ = self.i2c_lower.read(buffer, 4);
247                self.state.set(State::ReadSOC);
248            }
249            State::ReadSOC => {
250                // Read of SOC memory address complete
251                self.soc_mah
252                    .set(((buffer[1] as u16) << 8) | (buffer[0] as u16));
253                self.soc.set(((buffer[3] as u16) << 8) | (buffer[2] as u16));
254
255                self.buffer.replace(buffer);
256
257                // Now issue write of memory address of full capacity
258                // Setup read capacity
259                self.buffer.take().map(|selfbuf| {
260                    // Get SOC mAh and percentage
261                    // Write reqcap address
262                    selfbuf[0] = (Registers::FullCapRep as u8) & 0xFF;
263                    // TODO verify errors
264                    let _ = self.i2c_lower.write(selfbuf, 1);
265
266                    self.state.set(State::SetupReadCap);
267                });
268            }
269            State::SetupReadCap => {
270                // Now issue read
271                // TODO verify errors
272                let _ = self.i2c_lower.read(buffer, 2);
273                self.state.set(State::ReadCap);
274            }
275            State::ReadCap => {
276                let full_mah = ((buffer[1] as u16) << 8) | (buffer[0] as u16);
277
278                self.client.map(|client| {
279                    client.state_of_charge(
280                        self.soc.get(),
281                        self.soc_mah.get(),
282                        full_mah,
283                        match error {
284                            Ok(()) => Ok(()),
285                            Err(e) => Err(e.into()),
286                        },
287                    );
288                });
289
290                self.buffer.replace(buffer);
291                self.i2c_lower.disable();
292                self.state.set(State::Idle);
293            }
294            State::SetupReadCoulomb => {
295                // Write of voltage memory address complete, now issue read
296                // TODO verify errors
297                let _ = self.i2c_lower.read(buffer, 2);
298                self.state.set(State::ReadCoulomb);
299            }
300            State::ReadCoulomb => {
301                // Read of voltage memory address complete
302                let coulomb = ((buffer[1] as u16) << 8) | (buffer[0] as u16);
303
304                self.client.map(|client| {
305                    client.coulomb(
306                        coulomb,
307                        match error {
308                            Ok(()) => Ok(()),
309                            Err(e) => Err(e.into()),
310                        },
311                    );
312                });
313
314                self.buffer.replace(buffer);
315                self.i2c_lower.disable();
316                self.state.set(State::Idle);
317            }
318            State::SetupReadVolt => {
319                // Write of voltage memory address complete, now issue read
320                // TODO verify errors
321                let _ = self.i2c_lower.read(buffer, 2);
322                self.state.set(State::ReadVolt);
323            }
324            State::ReadVolt => {
325                // Read of voltage memory address complete
326                self.voltage
327                    .set(((buffer[1] as u16) << 8) | (buffer[0] as u16));
328
329                self.buffer.replace(buffer);
330
331                // Now issue write of memory address of current
332                // Setup read capacity
333                self.buffer.take().map(|selfbuf| {
334                    selfbuf[0] = (Registers::Current as u8) & 0xFF;
335                    // TODO verify errors
336                    let _ = self.i2c_lower.write(selfbuf, 1);
337
338                    self.state.set(State::SetupReadCurrent);
339                });
340            }
341            State::SetupReadCurrent => {
342                // Now issue read
343                // TODO verify errors
344                let _ = self.i2c_lower.read(buffer, 2);
345                self.state.set(State::ReadCurrent);
346            }
347            State::ReadCurrent => {
348                let current = ((buffer[1] as u16) << 8) | (buffer[0] as u16);
349
350                self.client.map(|client| {
351                    client.voltage_current(
352                        self.voltage.get(),
353                        current,
354                        match error {
355                            Ok(()) => Ok(()),
356                            Err(e) => Err(e.into()),
357                        },
358                    )
359                });
360
361                self.buffer.replace(buffer);
362                self.i2c_lower.disable();
363                self.state.set(State::Idle);
364            }
365            State::SetupReadRomID => {
366                // TODO verify errors
367                let _ = self.i2c_upper.read(buffer, 8);
368                self.state.set(State::ReadRomID);
369            }
370            State::ReadRomID => {
371                // u64 from 8 bytes
372                let rid = buffer
373                    .iter()
374                    .take(8)
375                    .enumerate()
376                    .fold(0u64, |rid, (i, b)| rid | ((*b as u64) << (i * 8)));
377                self.buffer.replace(buffer);
378
379                self.client.map(|client| {
380                    client.romid(
381                        rid,
382                        match error {
383                            Ok(()) => Ok(()),
384                            Err(e) => Err(e.into()),
385                        },
386                    )
387                });
388
389                self.i2c_upper.disable();
390                self.state.set(State::Idle);
391            }
392            _ => {}
393        }
394    }
395}
396
397/// IDs for subscribed upcalls.
398mod upcall {
399    /// Callback for when all events complete or data is ready.
400    pub const EVENT_COMPLETE: usize = 0;
401    /// Number of upcalls.
402    pub const COUNT: u8 = 1;
403}
404
405#[derive(Default)]
406pub struct App {}
407
408pub struct MAX17205Driver<'a, I: i2c::I2CDevice> {
409    max17205: &'a MAX17205<'a, I>,
410    owning_process: OptionalCell<ProcessId>,
411    apps: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
412}
413
414impl<'a, I: i2c::I2CDevice> MAX17205Driver<'a, I> {
415    pub fn new(
416        max: &'a MAX17205<I>,
417        grant: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
418    ) -> Self {
419        Self {
420            max17205: max,
421            owning_process: OptionalCell::empty(),
422            apps: grant,
423        }
424    }
425}
426
427impl<I: i2c::I2CDevice> MAX17205Client for MAX17205Driver<'_, I> {
428    fn status(&self, status: u16, error: Result<(), ErrorCode>) {
429        self.owning_process.map(|pid| {
430            let _ = self.apps.enter(pid, |_app, upcalls| {
431                upcalls
432                    .schedule_upcall(
433                        upcall::EVENT_COMPLETE,
434                        (
435                            kernel::errorcode::into_statuscode(error),
436                            status as usize,
437                            0,
438                        ),
439                    )
440                    .ok();
441            });
442        });
443    }
444
445    fn state_of_charge(
446        &self,
447        percent: u16,
448        capacity: u16,
449        full_capacity: u16,
450        error: Result<(), ErrorCode>,
451    ) {
452        self.owning_process.map(|pid| {
453            let _ = self.apps.enter(pid, |_app, upcalls| {
454                upcalls
455                    .schedule_upcall(
456                        upcall::EVENT_COMPLETE,
457                        (
458                            kernel::errorcode::into_statuscode(error),
459                            percent as usize,
460                            (capacity as usize) << 16 | (full_capacity as usize),
461                        ),
462                    )
463                    .ok();
464            });
465        });
466    }
467
468    fn voltage_current(&self, voltage: u16, current: u16, error: Result<(), ErrorCode>) {
469        self.owning_process.map(|pid| {
470            let _ = self.apps.enter(pid, |_app, upcalls| {
471                upcalls
472                    .schedule_upcall(
473                        upcall::EVENT_COMPLETE,
474                        (
475                            kernel::errorcode::into_statuscode(error),
476                            voltage as usize,
477                            current as usize,
478                        ),
479                    )
480                    .ok();
481            });
482        });
483    }
484
485    fn coulomb(&self, coulomb: u16, error: Result<(), ErrorCode>) {
486        self.owning_process.map(|pid| {
487            let _ = self.apps.enter(pid, |_app, upcalls| {
488                upcalls
489                    .schedule_upcall(
490                        upcall::EVENT_COMPLETE,
491                        (
492                            kernel::errorcode::into_statuscode(error),
493                            coulomb as usize,
494                            0,
495                        ),
496                    )
497                    .ok();
498            });
499        });
500    }
501
502    fn romid(&self, rid: u64, error: Result<(), ErrorCode>) {
503        self.owning_process.map(|pid| {
504            let _ = self.apps.enter(pid, |_app, upcalls| {
505                upcalls
506                    .schedule_upcall(
507                        upcall::EVENT_COMPLETE,
508                        (
509                            kernel::errorcode::into_statuscode(error),
510                            (rid & 0xffffffff) as usize,
511                            (rid >> 32) as usize,
512                        ),
513                    )
514                    .ok();
515            });
516        });
517    }
518}
519
520impl<I: i2c::I2CDevice> SyscallDriver for MAX17205Driver<'_, I> {
521    /// Setup and read the MAX17205.
522    ///
523    /// ### `command_num`
524    ///
525    /// - `0`: Driver existence check.
526    /// - `1`: Read the current status of the MAX17205.
527    /// - `2`: Read the current state of charge percent.
528    /// - `3`: Read the current voltage and current draw.
529    /// - `4`: Read the raw coulomb count.
530    /// - `5`: Read the unique 64 bit RomID.
531    fn command(
532        &self,
533        command_num: usize,
534        _data: usize,
535        _: usize,
536        process_id: ProcessId,
537    ) -> CommandReturn {
538        if command_num == 0 {
539            // Handle this first as it should be returned
540            // unconditionally
541            return CommandReturn::success();
542        }
543        // Check if this non-virtualized driver is already in use by
544        // some (alive) process
545        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
546            self.apps
547                .enter(current_process, |_, _| current_process == process_id)
548                .unwrap_or(true)
549        });
550        if match_or_empty_or_nonexistant {
551            self.owning_process.set(process_id);
552        } else {
553            return CommandReturn::failure(ErrorCode::NOMEM);
554        }
555        match command_num {
556            0 => CommandReturn::success(),
557
558            // read status
559            1 => self.max17205.setup_readstatus().into(),
560
561            // get soc
562            2 => self.max17205.setup_read_soc().into(),
563
564            // get voltage & current
565            3 => self.max17205.setup_read_curvolt().into(),
566
567            // get raw coulombs
568            4 => self.max17205.setup_read_coulomb().into(),
569
570            //
571            5 => self.max17205.setup_read_romid().into(),
572
573            // default
574            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
575        }
576    }
577
578    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
579        self.apps.enter(processid, |_, _| {})
580    }
581}