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                let _ = upcalls.schedule_upcall(
432                    upcall::EVENT_COMPLETE,
433                    (
434                        kernel::errorcode::into_statuscode(error),
435                        status as usize,
436                        0,
437                    ),
438                );
439            });
440        });
441    }
442
443    fn state_of_charge(
444        &self,
445        percent: u16,
446        capacity: u16,
447        full_capacity: u16,
448        error: Result<(), ErrorCode>,
449    ) {
450        self.owning_process.map(|pid| {
451            let _ = self.apps.enter(pid, |_app, upcalls| {
452                let _ = upcalls.schedule_upcall(
453                    upcall::EVENT_COMPLETE,
454                    (
455                        kernel::errorcode::into_statuscode(error),
456                        percent as usize,
457                        (capacity as usize) << 16 | (full_capacity as usize),
458                    ),
459                );
460            });
461        });
462    }
463
464    fn voltage_current(&self, voltage: u16, current: u16, error: Result<(), ErrorCode>) {
465        self.owning_process.map(|pid| {
466            let _ = self.apps.enter(pid, |_app, upcalls| {
467                let _ = upcalls.schedule_upcall(
468                    upcall::EVENT_COMPLETE,
469                    (
470                        kernel::errorcode::into_statuscode(error),
471                        voltage as usize,
472                        current as usize,
473                    ),
474                );
475            });
476        });
477    }
478
479    fn coulomb(&self, coulomb: u16, error: Result<(), ErrorCode>) {
480        self.owning_process.map(|pid| {
481            let _ = self.apps.enter(pid, |_app, upcalls| {
482                let _ = upcalls.schedule_upcall(
483                    upcall::EVENT_COMPLETE,
484                    (
485                        kernel::errorcode::into_statuscode(error),
486                        coulomb as usize,
487                        0,
488                    ),
489                );
490            });
491        });
492    }
493
494    fn romid(&self, rid: u64, error: Result<(), ErrorCode>) {
495        self.owning_process.map(|pid| {
496            let _ = self.apps.enter(pid, |_app, upcalls| {
497                let _ = upcalls.schedule_upcall(
498                    upcall::EVENT_COMPLETE,
499                    (
500                        kernel::errorcode::into_statuscode(error),
501                        (rid & 0xffffffff) as usize,
502                        (rid >> 32) as usize,
503                    ),
504                );
505            });
506        });
507    }
508}
509
510impl<I: i2c::I2CDevice> SyscallDriver for MAX17205Driver<'_, I> {
511    /// Setup and read the MAX17205.
512    ///
513    /// ### `command_num`
514    ///
515    /// - `0`: Driver existence check.
516    /// - `1`: Read the current status of the MAX17205.
517    /// - `2`: Read the current state of charge percent.
518    /// - `3`: Read the current voltage and current draw.
519    /// - `4`: Read the raw coulomb count.
520    /// - `5`: Read the unique 64 bit RomID.
521    fn command(
522        &self,
523        command_num: usize,
524        _data: usize,
525        _: usize,
526        process_id: ProcessId,
527    ) -> CommandReturn {
528        if command_num == 0 {
529            // Handle this first as it should be returned
530            // unconditionally
531            return CommandReturn::success();
532        }
533        // Check if this non-virtualized driver is already in use by
534        // some (alive) process
535        let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
536            self.apps
537                .enter(current_process, |_, _| current_process == process_id)
538                .unwrap_or(true)
539        });
540        if match_or_empty_or_nonexistant {
541            self.owning_process.set(process_id);
542        } else {
543            return CommandReturn::failure(ErrorCode::NOMEM);
544        }
545        match command_num {
546            0 => CommandReturn::success(),
547
548            // read status
549            1 => self.max17205.setup_readstatus().into(),
550
551            // get soc
552            2 => self.max17205.setup_read_soc().into(),
553
554            // get voltage & current
555            3 => self.max17205.setup_read_curvolt().into(),
556
557            // get raw coulombs
558            4 => self.max17205.setup_read_coulomb().into(),
559
560            //
561            5 => self.max17205.setup_read_romid().into(),
562
563            // default
564            _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
565        }
566    }
567
568    fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
569        self.apps.enter(processid, |_, _| {})
570    }
571}