Merge pull request #14226 from benpicco/cpu/sam0_common/i2c_arbitrary_freqs

cpu/sam0_common: i2c: fix BAUD handling & cleanup
This commit is contained in:
Leandro Lanzieri 2020-08-04 14:49:10 +02:00 committed by GitHub
commit d7dbbb71ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 19 deletions

View File

@ -402,6 +402,15 @@ typedef enum {
/**
* @brief I2C device configuration
* The frequency f() of the clock `gclk_src` must fulfill the condition
*
* 4 * speed f(gclk_src) 512 * speed
*
* if speed 1 MHz and
*
* 12 * speed f(gclk_src) 520 * speed
*
* if speed > 1 MHz
*/
typedef struct {
SercomI2cm *dev; /**< pointer to the used I2C device */

View File

@ -80,6 +80,9 @@ void i2c_init(i2c_t dev)
assert(dev < I2C_NUMOF);
const uint32_t fSCL = i2c_config[dev].speed;
const uint32_t fGCLK = sam0_gclk_freq(i2c_config[dev].gclk_src);
/* Initialize mutex */
mutex_init(&locks[dev]);
/* DISABLE I2C MASTER */
@ -122,30 +125,31 @@ void i2c_init(i2c_t dev)
/* Enable Smart Mode (ACK is sent when DATA.DATA is read) */
bus(dev)->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
/* Find and set baudrate. Read speed configuration. Set transfer
* speed: SERCOM_I2CM_CTRLA_SPEED(0): Standard-mode (Sm) up to 100
* kHz and Fast-mode (Fm) up to 400 kHz */
switch (i2c_config[dev].speed) {
case I2C_SPEED_NORMAL:
case I2C_SPEED_FAST:
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0);
break;
case I2C_SPEED_HIGH:
/* Set SPEED */
if (fSCL > I2C_SPEED_FAST_PLUS) {
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2);
break;
default:
DEBUG("BAD BAUDRATE\n");
return;
} else if (fSCL > I2C_SPEED_FAST) {
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(1);
} else {
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0);
}
/* Get the baudrate */
tmp_baud = (int32_t)(((sam0_gclk_freq(i2c_config[dev].gclk_src) +
(2 * (i2c_config[dev].speed)) - 1) /
(2 * (i2c_config[dev].speed))) -
(i2c_config[dev].speed == I2C_SPEED_HIGH ? 1 : 5));
/* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */
/* fSCL = fGCLK / (2 + 2 * HSBAUD) -> HSBAUD = fGCLK / (2 * fSCL) - 1 */
tmp_baud = (fGCLK + (2 * fSCL) - 1) /* round up */
/ (2 * fSCL)
- (fSCL > I2C_SPEED_FAST_PLUS ? 1 : 5);
/* Ensure baudrate is within limits */
if (tmp_baud < 255 && tmp_baud > 0) {
assert(tmp_baud < 255 && tmp_baud > 0);
if (fSCL > I2C_SPEED_FAST_PLUS) {
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_HSBAUD(tmp_baud);
} else {
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud);
}
/* ENABLE I2C MASTER */
_i2c_poweron(dev);