samd21: Add support for basic i2c support (write only)

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2018-11-28 19:56:41 -05:00
parent 6d90ebe6f8
commit 58d61c7887
4 changed files with 125 additions and 0 deletions

View File

@ -6,6 +6,7 @@ config SAMD_SELECT
bool bool
default y default y
select HAVE_GPIO select HAVE_GPIO
select HAVE_GPIO_I2C
select HAVE_GPIO_BITBANGING select HAVE_GPIO_BITBANGING
config BOARD_DIRECTORY config BOARD_DIRECTORY

View File

@ -20,6 +20,7 @@ src-y += generic/armcm_irq.c generic/timer_irq.c
src-y += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c src-y += ../lib/samd21/samd21a/gcc/gcc/startup_samd21.c
src-$(CONFIG_USBSERIAL) += samd21/usbserial.c generic/usb_cdc.c src-$(CONFIG_USBSERIAL) += samd21/usbserial.c generic/usb_cdc.c
src-$(CONFIG_SERIAL) += samd21/serial.c generic/serial_irq.c src-$(CONFIG_SERIAL) += samd21/serial.c generic/serial_irq.c
src-$(CONFIG_HAVE_GPIO_I2C) += samd21/i2c.c
# Support bootloader offset address # Support bootloader offset address
target-y := $(OUT)samd21.ld $(target-y) target-y := $(OUT)samd21.ld $(target-y)

View File

@ -21,4 +21,13 @@ struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
void gpio_in_reset(struct gpio_in g, int8_t pull_up); void gpio_in_reset(struct gpio_in g, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in g); uint8_t gpio_in_read(struct gpio_in g);
struct i2c_config {
uint8_t addr;
};
struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr);
void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write);
void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read);
#endif // gpio.h #endif // gpio.h

114
src/samd21/i2c.c Normal file
View File

@ -0,0 +1,114 @@
// i2c support on samd21
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_CLOCK_FREQ
#include "internal.h" // enable_pclock
#include "command.h" // shutdown
#include "gpio.h" // i2c_setup
#include "samd21.h" // SERCOM3
#include "sched.h" // sched_shutdown
#define TIME_RISE 125ULL // 125 nanoseconds
#define I2C_FREQ 100000
static void
i2c_init(void)
{
static int have_run_init;
if (have_run_init)
return;
have_run_init = 1;
// Setup clock
enable_pclock(SERCOM3_GCLK_ID_CORE, PM_APBCMASK_SERCOM3);
// Configure SDA, SCL pins
gpio_peripheral(GPIO('A', 22), 'C', 0);
gpio_peripheral(GPIO('A', 23), 'C', 0);
// Configure i2c
SercomI2cm *si = &SERCOM3->I2CM;
si->CTRLA.reg = 0;
uint32_t areg = (SERCOM_I2CM_CTRLA_LOWTOUTEN
| SERCOM_I2CM_CTRLA_INACTOUT(3)
| SERCOM_I2CM_STATUS_SEXTTOUT
| SERCOM_I2CM_STATUS_MEXTTOUT
| SERCOM_I2CM_CTRLA_MODE_I2C_MASTER);
si->CTRLA.reg = areg;
uint32_t baud = (CONFIG_CLOCK_FREQ / I2C_FREQ
- 10 - CONFIG_CLOCK_FREQ*TIME_RISE/1000000000) / 2;
si->BAUD.reg = baud;
si->CTRLA.reg = areg | SERCOM_I2CM_CTRLA_ENABLE;
while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_ENABLE)
;
// Go into idle mode
si->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1);
while (si->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_SYSOP)
;
}
struct i2c_config
i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr)
{
if (bus)
shutdown("Unsupported i2c bus");
i2c_init();
return (struct i2c_config){ .addr=addr };
}
static void
i2c_wait(SercomI2cm *si)
{
for (;;) {
uint32_t intflag = si->INTFLAG.reg;
if (!(intflag & SERCOM_I2CM_INTFLAG_MB)) {
if (si->STATUS.reg & SERCOM_I2CM_STATUS_BUSERR)
shutdown("i2c buserror");
continue;
}
if (intflag & SERCOM_I2CM_INTFLAG_ERROR)
shutdown("i2c error");
break;
}
}
static void
i2c_start(SercomI2cm *si, uint8_t addr)
{
si->ADDR.reg = addr;
i2c_wait(si);
}
static void
i2c_send_byte(SercomI2cm *si, uint8_t b)
{
si->DATA.reg = b;
i2c_wait(si);
}
static void
i2c_stop(SercomI2cm *si)
{
si->CTRLB.reg = SERCOM_I2CM_CTRLB_CMD(3);
}
void
i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write)
{
SercomI2cm *si = &SERCOM3->I2CM;
i2c_start(si, config.addr);
while (write_len--)
i2c_send_byte(si, *write++);
i2c_stop(si);
}
void
i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read)
{
shutdown("i2c_read not supported on samd21");
}