samd21: Add support for basic i2c support (write only)
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
6d90ebe6f8
commit
58d61c7887
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
Loading…
Reference in New Issue