Files
OpenCellular/chip/g/i2cs.h
Vadim Bendebury 430d55879d g: add 'recover hosed slave' i2cs capability
A common failure condition on the i2c bus is when the master
unexpectedly stops clocking the bus while the slave is driving the SDA
line low. In this case the master is not able to issue Stop or Start
sequences, which makes the bus unusable.

Good slave controllers are able to detect this condition and recover
from it by removing the pull down from the SDA line. This patch adds
this capability to the g chip i2c slave controller.

A new timer function is created which samples the SDA line twice a
second. If it detects that SDA is low in two consecutive invocations
and the number of i2cs read interrupts has not advanced, it decides
that the "hosed slave" condition is happening and reinitializes the
i2c driver, which removes the hold from the SDA line.

Even though the state of the SDA line is supposed to be accessible
through the I2CS_READVAL register, it in fact is not, reads always
return zero in the SDA bit. To work around this a GPIO (port 0, bit
14) is being allocated to allow to monitor the state of the line, it
is multiplexed to the same pin the SDA line uses.

When the AP is in low power modes the SDA line is held low, this state
should not trigger i2c reinitializations.

CQ-DEPEND=CL:616300
BRANCH=none
BUG=b:35648537
TEST=connected H1 on the test board to an I2c master capable of
     stopping clocking mid byte. Observed that the existing code would
     just sit in the "hosed" state indefinitely. The code with the fix
     recovers from the condition (drives the SDA line high) 500ms to
     1s after the failure condition is created.

Change-Id: Iafc7433bbae9e49975a72ef032a923274f8aab3b
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/614391
Reviewed-by: Aseda Aboagye <aaboagye@chromium.org>
2017-08-17 20:41:57 -07:00

65 lines
2.2 KiB
C

/*
* Copyright 2016 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef __CHIP_G_I2CS_H
#define __CHIP_G_I2CS_H
#include <stddef.h>
/*
* Write complete interrupt callback function prototype. This function expects
* two parameters: the address of the buffer containing received data and
* number of bytes in the buffer.
*/
typedef void (*wr_complete_handler_f)(void *i2cs_data, size_t i2cs_data_size);
/* Register the write complete interrupt handler. */
int i2cs_register_write_complete_handler(wr_complete_handler_f wc_handler);
/*
* Post a byte for the master to read. Blend the byte into the appropriate
* 4byte register of the master read register file.
*/
void i2cs_post_read_data(uint8_t byte_to_read);
/*
* Configure the pinmux registers required to connect the I2CS interface. This
* function is board specific and so it exists in the associated board.c file.
*/
void i2cs_set_pinmux(void);
/*
* Ensure no bytes are currently buffered in the I2CS READ fifo. This
* value is calculated by finding the difference between read pointer that's
* used by FW to add bytes to the HW fifo and the current value of the
* I2CS_READ_PTR register.
*
* @returns: the number of bytes buffered when the function is called
*/
size_t i2cs_zero_read_fifo_buffer_depth(void);
/*
* Write buffer of data into the I2CS HW read fifo. The function will operate a
* byte at a time until the fifo write pointer is word aligned. Then it will
* consume all remaining words of input data. There is another stage to handle
* any excess bytes. The efficiency benefits relative the byte at a time
* function diminish as the buffer size gets smaller and therefore not intended
* to be used for <= 4 byte buffers.
*/
void i2cs_post_read_fill_fifo(uint8_t *buffer, size_t len);
/*
* Provide upper layers with information with the I2CS interface
* status/statistics. The only piece of information currently provided is the
* counter of "hosed" i2c interface occurences, where i2c clocking stopped
* while slave was transmitting a zero.
*/
struct i2cs_status {
uint16_t read_recovery_count;
};
void i2cs_get_status(struct i2cs_status *status);
#endif /* ! __CHIP_G_I2CS_H */