drivers: can: Wait for MCP2515 to reset

The device may be prematurely configured before it is ready during
reset without first checking. After the driver writes to the registers
and normal mode gets selected, CANSTAT is read back to ensure the
device is now configured in normal mode (0x00). However, an
unresponsive device will be read as 0x00, the driver then assumes it is
configured correctly.

The MCP2515 will reset into configuration mode (0x04) to allow bit
timings to be configured. Adding a check for this mode prevents the
device from being misconfigured.

Signed-off-by: Roman Vaughan <nzsmartie@gmail.com>
This commit is contained in:
Roman Vaughan 2020-08-16 17:38:16 +12:00 committed by Maureen Helm
commit 127e82e3b8

View file

@ -271,6 +271,24 @@ const int mcp2515_set_mode(struct device *dev, uint8_t mcp2515_mode)
return 0;
}
static int mcp2515_get_mode(struct device *dev, uint8_t *mode)
{
uint8_t canstat;
if (mode == NULL) {
return -EINVAL;
}
if (mcp2515_cmd_read_reg(dev, MCP2515_ADDR_CANSTAT, &canstat, 1)) {
return -EIO;
}
*mode = (canstat & MCP2515_CANSTAT_MODE_MASK)
>> MCP2515_CANSTAT_MODE_POS;
return 0;
}
static int mcp2515_configure(struct device *dev, enum can_mode mode,
uint32_t bitrate)
{
@ -280,6 +298,7 @@ static int mcp2515_configure(struct device *dev, enum can_mode mode,
/* CNF3, CNF2, CNF1, CANINTE */
uint8_t config_buf[4];
uint8_t reset_mode;
if (bitrate == 0) {
bitrate = dev_cfg->bus_speed;
@ -345,6 +364,12 @@ static int mcp2515_configure(struct device *dev, enum can_mode mode,
config_buf[3] = caninte;
k_mutex_lock(&dev_data->mutex, K_FOREVER);
/* Wait 128 OSC1 clock cycles at 1MHz (minimum clock in frequency)
* see MCP2515 datasheet section 8.1 Oscillator Start-up Timer
*/
k_usleep(128);
/* will enter configuration mode automatically */
ret = mcp2515_cmd_soft_reset(dev);
if (ret < 0) {
@ -352,6 +377,22 @@ static int mcp2515_configure(struct device *dev, enum can_mode mode,
goto done;
}
k_usleep(128);
ret = mcp2515_get_mode(dev, &reset_mode);
if (ret < 0) {
LOG_ERR("Failed to read device mode [%d]",
ret);
goto done;
}
if (reset_mode != MCP2515_MODE_CONFIGURATION) {
LOG_ERR("Device did not reset into configuration mode [%d]",
reset_mode);
ret = -EIO;
goto done;
}
ret = mcp2515_cmd_write_reg(dev, MCP2515_ADDR_CNF3, config_buf,
sizeof(config_buf));
if (ret < 0) {