1use core::cell::Cell;
54
55use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
56use kernel::hil::gpio;
57use kernel::hil::i2c;
58use kernel::syscall::{CommandReturn, SyscallDriver};
59use kernel::utilities::cells::{OptionalCell, TakeCell};
60use kernel::{ErrorCode, ProcessId};
61
62use capsules_core::driver;
64pub const DRIVER_NUM: usize = driver::NUM::Ltc294x as usize;
65
66pub const BUF_LEN: usize = 20;
67
68#[allow(dead_code)]
69enum Registers {
70 Status = 0x00,
71 Control = 0x01,
72 AccumulatedChargeMSB = 0x02,
73 AccumulatedChargeLSB = 0x03,
74 ChargeThresholdHighMSB = 0x04,
75 ChargeThresholdHighLSB = 0x05,
76 ChargeThresholdLowMSB = 0x06,
77 ChargeThresholdLowLSB = 0x07,
78 VoltageMSB = 0x08,
79 VoltageLSB = 0x09,
80 CurrentMSB = 0x0E,
81 CurrentLSB = 0x0F,
82}
83
84#[derive(Clone, Copy, PartialEq)]
85enum State {
86 Idle,
87
88 ReadStatus,
90 ReadCharge,
91 ReadVoltage,
92 ReadCurrent,
93 ReadShutdown,
94
95 Done,
96}
97
98#[derive(Clone, Copy)]
100pub enum ChipModel {
101 LTC2941 = 1,
102 LTC2942 = 2,
103 LTC2943 = 3,
104}
105
106pub enum InterruptPinConf {
108 Disabled = 0x00,
109 ChargeCompleteMode = 0x01,
110 AlertMode = 0x02,
111}
112
113pub enum VBatAlert {
115 Off = 0x00,
116 Threshold2V8 = 0x01,
117 Threshold2V9 = 0x02,
118 Threshold3V0 = 0x03,
119}
120
121#[derive(Default)]
122pub struct App {}
123
124pub trait LTC294XClient {
126 fn interrupt(&self);
127 fn status(
128 &self,
129 undervolt_lockout: bool,
130 vbat_alert: bool,
131 charge_alert_low: bool,
132 charge_alert_high: bool,
133 accumulated_charge_overflow: bool,
134 );
135 fn charge(&self, charge: u16);
136 fn voltage(&self, voltage: u16);
137 fn current(&self, current: u16);
138 fn done(&self);
139}
140
141pub struct LTC294X<'a, I: i2c::I2CDevice> {
143 i2c: &'a I,
144 interrupt_pin: Option<&'a dyn gpio::InterruptPin<'a>>,
145 model: Cell<ChipModel>,
146 state: Cell<State>,
147 buffer: TakeCell<'static, [u8]>,
148 client: OptionalCell<&'static dyn LTC294XClient>,
149}
150
151impl<'a, I: i2c::I2CDevice> LTC294X<'a, I> {
152 pub fn new(
153 i2c: &'a I,
154 interrupt_pin: Option<&'a dyn gpio::InterruptPin<'a>>,
155 buffer: &'static mut [u8],
156 ) -> LTC294X<'a, I> {
157 LTC294X {
158 i2c,
159 interrupt_pin,
160 model: Cell::new(ChipModel::LTC2941),
161 state: Cell::new(State::Idle),
162 buffer: TakeCell::new(buffer),
163 client: OptionalCell::empty(),
164 }
165 }
166
167 pub fn set_client<C: LTC294XClient>(&self, client: &'static C) {
168 self.client.set(client);
169
170 self.interrupt_pin.map(|interrupt_pin| {
171 interrupt_pin.make_input();
172 interrupt_pin.enable_interrupts(gpio::InterruptEdge::FallingEdge);
173 });
174 }
175
176 pub fn read_status(&self) -> Result<(), ErrorCode> {
177 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
178 self.i2c.enable();
179
180 let _ = self.i2c.read(buffer, 1);
183 self.state.set(State::ReadStatus);
184
185 Ok(())
186 })
187 }
188
189 fn configure(
190 &self,
191 int_pin_conf: InterruptPinConf,
192 prescaler: u8,
193 vbat_alert: VBatAlert,
194 ) -> Result<(), ErrorCode> {
195 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
196 self.i2c.enable();
197
198 buffer[0] = Registers::Control as u8;
199 buffer[1] = ((int_pin_conf as u8) << 1) | (prescaler << 3) | ((vbat_alert as u8) << 6);
200
201 let _ = self.i2c.write(buffer, 2);
203 self.state.set(State::Done);
204
205 Ok(())
206 })
207 }
208
209 fn reset_charge(&self) -> Result<(), ErrorCode> {
211 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
212 self.i2c.enable();
213
214 buffer[0] = Registers::AccumulatedChargeMSB as u8;
215 buffer[1] = 0;
216 buffer[2] = 0;
217
218 let _ = self.i2c.write(buffer, 3);
220 self.state.set(State::Done);
221
222 Ok(())
223 })
224 }
225
226 fn set_high_threshold(&self, threshold: u16) -> Result<(), ErrorCode> {
227 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
228 self.i2c.enable();
229
230 buffer[0] = Registers::ChargeThresholdHighMSB as u8;
231 buffer[1] = ((threshold & 0xFF00) >> 8) as u8;
232 buffer[2] = (threshold & 0xFF) as u8;
233
234 let _ = self.i2c.write(buffer, 3);
236 self.state.set(State::Done);
237
238 Ok(())
239 })
240 }
241
242 fn set_low_threshold(&self, threshold: u16) -> Result<(), ErrorCode> {
243 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
244 self.i2c.enable();
245
246 buffer[0] = Registers::ChargeThresholdLowMSB as u8;
247 buffer[1] = ((threshold & 0xFF00) >> 8) as u8;
248 buffer[2] = (threshold & 0xFF) as u8;
249
250 let _ = self.i2c.write(buffer, 3);
252 self.state.set(State::Done);
253
254 Ok(())
255 })
256 }
257
258 fn get_charge(&self) -> Result<(), ErrorCode> {
260 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
261 self.i2c.enable();
262
263 let _ = self.i2c.read(buffer, 4);
267 self.state.set(State::ReadCharge);
268
269 Ok(())
270 })
271 }
272
273 fn get_voltage(&self) -> Result<(), ErrorCode> {
275 match self.model.get() {
277 ChipModel::LTC2942 | ChipModel::LTC2943 => {
278 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
279 self.i2c.enable();
280
281 let _ = self.i2c.read(buffer, 10);
283 self.state.set(State::ReadVoltage);
284
285 Ok(())
286 })
287 }
288 _ => Err(ErrorCode::NOSUPPORT),
289 }
290 }
291
292 fn get_current(&self) -> Result<(), ErrorCode> {
294 match self.model.get() {
296 ChipModel::LTC2943 => self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
297 self.i2c.enable();
298
299 let _ = self.i2c.read(buffer, 16);
301 self.state.set(State::ReadCurrent);
302
303 Ok(())
304 }),
305 _ => Err(ErrorCode::NOSUPPORT),
306 }
307 }
308
309 fn shutdown(&self) -> Result<(), ErrorCode> {
311 self.buffer.take().map_or(Err(ErrorCode::NOMEM), |buffer| {
312 self.i2c.enable();
313
314 let _ = self.i2c.read(buffer, 2);
318 self.state.set(State::ReadShutdown);
319
320 Ok(())
321 })
322 }
323
324 fn set_model(&self, model_num: usize) -> Result<(), ErrorCode> {
326 match model_num {
327 1 => {
328 self.model.set(ChipModel::LTC2941);
329 Ok(())
330 }
331 2 => {
332 self.model.set(ChipModel::LTC2942);
333 Ok(())
334 }
335 3 => {
336 self.model.set(ChipModel::LTC2943);
337 Ok(())
338 }
339 _ => Err(ErrorCode::NODEVICE),
340 }
341 }
342}
343
344impl<I: i2c::I2CDevice> i2c::I2CClient for LTC294X<'_, I> {
345 fn command_complete(&self, buffer: &'static mut [u8], _status: Result<(), i2c::Error>) {
346 match self.state.get() {
347 State::ReadStatus => {
348 let status = buffer[0];
349 let uvlock = (status & 0x01) > 0;
350 let vbata = (status & 0x02) > 0;
351 let ca_low = (status & 0x04) > 0;
352 let ca_high = (status & 0x08) > 0;
353 let accover = (status & 0x20) > 0;
354 self.client.map(|client| {
355 client.status(uvlock, vbata, ca_low, ca_high, accover);
356 });
357
358 self.buffer.replace(buffer);
359 self.i2c.disable();
360 self.state.set(State::Idle);
361 }
362 State::ReadCharge => {
363 let charge = ((buffer[2] as u16) << 8) | (buffer[3] as u16);
365 self.client.map(|client| {
366 client.charge(charge);
367 });
368
369 self.buffer.replace(buffer);
370 self.i2c.disable();
371 self.state.set(State::Idle);
372 }
373 State::ReadVoltage => {
374 let voltage = ((buffer[8] as u16) << 8) | (buffer[9] as u16);
375 self.client.map(|client| {
376 client.voltage(voltage);
377 });
378
379 self.buffer.replace(buffer);
380 self.i2c.disable();
381 self.state.set(State::Idle);
382 }
383 State::ReadCurrent => {
384 let current = ((buffer[14] as u16) << 8) | (buffer[15] as u16);
385 self.client.map(|client| {
386 client.current(current);
387 });
388
389 self.buffer.replace(buffer);
390 self.i2c.disable();
391 self.state.set(State::Idle);
392 }
393 State::ReadShutdown => {
394 buffer[1] |= 0x01;
396
397 buffer[0] = Registers::Control as u8;
400 let _ = self.i2c.write(buffer, 2);
402 self.state.set(State::Done);
403 }
404 State::Done => {
405 self.client.map(|client| {
406 client.done();
407 });
408
409 self.buffer.replace(buffer);
410 self.i2c.disable();
411 self.state.set(State::Idle);
412 }
413 _ => {}
414 }
415 }
416}
417
418impl<I: i2c::I2CDevice> gpio::Client for LTC294X<'_, I> {
419 fn fired(&self) {
420 self.client.map(|client| {
421 client.interrupt();
422 });
423 }
424}
425
426mod upcall {
428 pub const EVENT_FINISHED: usize = 0;
438 pub const COUNT: u8 = 1;
440}
441
442pub struct LTC294XDriver<'a, I: i2c::I2CDevice> {
445 ltc294x: &'a LTC294X<'a, I>,
446 grants: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
447 owning_process: OptionalCell<ProcessId>,
448}
449
450impl<'a, I: i2c::I2CDevice> LTC294XDriver<'a, I> {
451 pub fn new(
452 ltc: &'a LTC294X<'a, I>,
453 grants: Grant<App, UpcallCount<{ upcall::COUNT }>, AllowRoCount<0>, AllowRwCount<0>>,
454 ) -> LTC294XDriver<'a, I> {
455 LTC294XDriver {
456 ltc294x: ltc,
457 grants,
458 owning_process: OptionalCell::empty(),
459 }
460 }
461}
462
463impl<I: i2c::I2CDevice> LTC294XClient for LTC294XDriver<'_, I> {
464 fn interrupt(&self) {
465 self.owning_process.map(|pid| {
466 let _res = self.grants.enter(pid, |_app, upcalls| {
467 upcalls
468 .schedule_upcall(upcall::EVENT_FINISHED, (0, 0, 0))
469 .ok();
470 });
471 });
472 }
473
474 fn status(
475 &self,
476 undervolt_lockout: bool,
477 vbat_alert: bool,
478 charge_alert_low: bool,
479 charge_alert_high: bool,
480 accumulated_charge_overflow: bool,
481 ) {
482 let ret = (undervolt_lockout as usize)
483 | ((vbat_alert as usize) << 1)
484 | ((charge_alert_low as usize) << 2)
485 | ((charge_alert_high as usize) << 3)
486 | ((accumulated_charge_overflow as usize) << 4);
487 self.owning_process.map(|pid| {
488 let _res = self.grants.enter(pid, |_app, upcalls| {
489 upcalls
490 .schedule_upcall(
491 upcall::EVENT_FINISHED,
492 (1, ret, self.ltc294x.model.get() as usize),
493 )
494 .ok();
495 });
496 });
497 }
498
499 fn charge(&self, charge: u16) {
500 self.owning_process.map(|pid| {
501 let _res = self.grants.enter(pid, |_app, upcalls| {
502 upcalls
503 .schedule_upcall(upcall::EVENT_FINISHED, (2, charge as usize, 0))
504 .ok();
505 });
506 });
507 }
508
509 fn done(&self) {
510 self.owning_process.map(|pid| {
511 let _res = self.grants.enter(pid, |_app, upcalls| {
512 upcalls
513 .schedule_upcall(upcall::EVENT_FINISHED, (3, 0, 0))
514 .ok();
515 });
516 });
517 }
518
519 fn voltage(&self, voltage: u16) {
520 self.owning_process.map(|pid| {
521 let _res = self.grants.enter(pid, |_app, upcalls| {
522 upcalls
523 .schedule_upcall(upcall::EVENT_FINISHED, (4, voltage as usize, 0))
524 .ok();
525 });
526 });
527 }
528
529 fn current(&self, current: u16) {
530 self.owning_process.map(|pid| {
531 let _res = self.grants.enter(pid, |_app, upcalls| {
532 upcalls
533 .schedule_upcall(upcall::EVENT_FINISHED, (5, current as usize, 0))
534 .ok();
535 });
536 });
537 }
538}
539
540impl<I: i2c::I2CDevice> SyscallDriver for LTC294XDriver<'_, I> {
541 fn command(
559 &self,
560 command_num: usize,
561 data: usize,
562 _: usize,
563 process_id: ProcessId,
564 ) -> CommandReturn {
565 if command_num == 0 {
566 return CommandReturn::success();
569 }
570
571 let match_or_empty_or_nonexistant = self.owning_process.map_or(true, |current_process| {
572 self.grants
573 .enter(current_process, |_, _| current_process == process_id)
574 .unwrap_or(true)
575 });
576 if match_or_empty_or_nonexistant {
577 self.owning_process.set(process_id);
578 } else {
579 return CommandReturn::failure(ErrorCode::NOMEM);
580 }
581
582 match command_num {
583 1 => self.ltc294x.read_status().into(),
585
586 2 => {
588 let int_pin_raw = data & 0x03;
589 let prescaler = (data >> 2) & 0x07;
590 let vbat_raw = (data >> 5) & 0x03;
591 let int_pin_conf = match int_pin_raw {
592 0 => InterruptPinConf::Disabled,
593 1 => InterruptPinConf::ChargeCompleteMode,
594 2 => InterruptPinConf::AlertMode,
595 _ => InterruptPinConf::Disabled,
596 };
597 let vbat_alert = match vbat_raw {
598 0 => VBatAlert::Off,
599 1 => VBatAlert::Threshold2V8,
600 2 => VBatAlert::Threshold2V9,
601 3 => VBatAlert::Threshold3V0,
602 _ => VBatAlert::Off,
603 };
604
605 self.ltc294x
606 .configure(int_pin_conf, prescaler as u8, vbat_alert)
607 .into()
608 }
609
610 3 => self.ltc294x.reset_charge().into(),
612
613 4 => self.ltc294x.set_high_threshold(data as u16).into(),
615
616 5 => self.ltc294x.set_low_threshold(data as u16).into(),
618
619 6 => self.ltc294x.get_charge().into(),
621
622 7 => self.ltc294x.shutdown().into(),
624
625 8 => self.ltc294x.get_voltage().into(),
627
628 9 => self.ltc294x.get_current().into(),
630
631 10 => self.ltc294x.set_model(data).into(),
633
634 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
636 }
637 }
638
639 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
640 self.grants.enter(processid, |_, _| {})
641 }
642}