//go:build k210

package machine

import (
	"device/kendryte"
	"device/riscv"
	"errors"
	"runtime/interrupt"
	"unsafe"
)

const deviceName = kendryte.Device

func CPUFrequency() uint32 {
	return 390000000
}

type fpioaPullMode uint8
type PinChange uint8

// Pin modes.
const (
	PinInput PinMode = iota
	PinInputPullup
	PinInputPulldown
	PinOutput
)

// Deprecated: use PinInputPullup and PinInputPulldown instead.
const (
	PinInputPullUp   = PinInputPullup
	PinInputPullDown = PinInputPulldown
)

// FPIOA internal pull resistors.
const (
	fpioaPullNone fpioaPullMode = iota
	fpioaPullDown
	fpioaPullUp
)

// GPIOHS pin interrupt events.
const (
	PinRising PinChange = 1 << iota
	PinFalling
	PinToggle = PinRising | PinFalling
)

var (
	errUnsupportedSPIController = errors.New("SPI controller not supported. Use SPI0 or SPI1.")
	errI2CTxAbort               = errors.New("I2C transmission has been aborted.")
)

func (p Pin) setFPIOAIOPull(pull fpioaPullMode) {
	switch pull {
	case fpioaPullNone:
		kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PU & kendryte.FPIOA_IO_PD)
	case fpioaPullUp:
		kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_PU)
		kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PD)
	case fpioaPullDown:
		kendryte.FPIOA.IO[uint8(p)].ClearBits(kendryte.FPIOA_IO_PU)
		kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_PD)
	}
}

// SetFPIOAFunction is used to configure the pin for one of the FPIOA functions.
// Each pin on the Kendryte K210 can be configured with any of the available FPIOA functions.
func (p Pin) SetFPIOAFunction(f FPIOAFunction) {
	kendryte.FPIOA.IO[uint8(p)].Set(fpioaFuncDefaults[uint8(f)])
}

// FPIOAFunction returns the current FPIOA function of the pin.
func (p Pin) FPIOAFunction() FPIOAFunction {
	return FPIOAFunction((kendryte.FPIOA.IO[uint8(p)].Get() & kendryte.FPIOA_IO_CH_SEL_Msk))
}

// Configure this pin with the given configuration.
// The pin must already be set as GPIO or GPIOHS pin.
func (p Pin) Configure(config PinConfig) {
	var input bool

	// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
	f := p.FPIOAFunction()
	if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
		return // The pin is not configured as GPIO or GPIOHS.
	}

	// Configure pin.
	kendryte.FPIOA.IO[uint8(p)].SetBits(kendryte.FPIOA_IO_OE_EN | kendryte.FPIOA_IO_IE_EN | kendryte.FPIOA_IO_ST | kendryte.FPIOA_IO_DS_Msk)

	switch config.Mode {
	case PinInput:
		p.setFPIOAIOPull(fpioaPullNone)
		input = true
	case PinInputPullup:
		p.setFPIOAIOPull(fpioaPullUp)
		input = true
	case PinInputPulldown:
		p.setFPIOAIOPull(fpioaPullDown)
		input = true
	case PinOutput:
		p.setFPIOAIOPull(fpioaPullNone)
		input = false
	}

	if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
		// Converts the IO pin number in the effective GPIO number (based on the FPIOA function).
		gpioPin := uint8(f - FUNC_GPIO0)

		if input {
			kendryte.GPIO.DIRECTION.ClearBits(1 << gpioPin)
		} else {
			kendryte.GPIO.DIRECTION.SetBits(1 << gpioPin)
		}
	} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
		// Converts the IO pin number in the effective GPIOHS number (based on the FPIOA function).
		gpioPin := uint8(f - FUNC_GPIOHS0)

		if input {
			kendryte.GPIOHS.INPUT_EN.SetBits(1 << gpioPin)
			kendryte.GPIOHS.OUTPUT_EN.ClearBits(1 << gpioPin)
		} else {
			kendryte.GPIOHS.OUTPUT_EN.SetBits(1 << gpioPin)
			kendryte.GPIOHS.INPUT_EN.ClearBits(1 << gpioPin)
		}
	}
}

// Set the pin to high or low.
func (p Pin) Set(high bool) {

	// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
	f := p.FPIOAFunction()
	if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
		return // The pin is not configured as GPIO or GPIOHS.
	}

	if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
		gpioPin := uint8(f - FUNC_GPIO0)

		if high {
			kendryte.GPIO.DATA_OUTPUT.SetBits(1 << gpioPin)
		} else {
			kendryte.GPIO.DATA_OUTPUT.ClearBits(1 << gpioPin)
		}
	} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
		gpioPin := uint8(f - FUNC_GPIOHS0)

		if high {
			kendryte.GPIOHS.OUTPUT_VAL.SetBits(1 << gpioPin)
		} else {
			kendryte.GPIOHS.OUTPUT_VAL.ClearBits(1 << gpioPin)
		}
	}
}

// Get returns the current value of a GPIO pin.
func (p Pin) Get() bool {

	// Check if the current pin's FPIOA function is either GPIO or GPIOHS.
	f := p.FPIOAFunction()
	if f < FUNC_GPIOHS0 || f > FUNC_GPIO7 {
		return false // The pin is not configured as GPIO or GPIOHS.
	}

	var val uint32
	if f >= FUNC_GPIO0 && f <= FUNC_GPIO7 {
		gpioPin := uint8(f - FUNC_GPIO0)
		val = kendryte.GPIO.DATA_INPUT.Get() & (1 << gpioPin)
	} else if f >= FUNC_GPIOHS0 && f <= FUNC_GPIOHS31 {
		gpioPin := uint8(f - FUNC_GPIOHS0)
		val = kendryte.GPIOHS.INPUT_VAL.Get() & (1 << gpioPin)
	}
	return (val > 0)
}

// Callbacks to be called for GPIOHS pins configured with SetInterrupt.
var pinCallbacks [32]func(Pin)

// SetInterrupt sets an interrupt to be executed when a particular pin changes
// state. The pin should already be configured as an input, including a pull up
// or down if no external pull is provided.
//
// You can pass a nil func to unset the pin change interrupt. If you do so,
// the change parameter is ignored and can be set to any value (such as 0).
// If the pin is already configured with a callback, you must first unset
// this pins interrupt before you can set a new callback.
func (p Pin) SetInterrupt(change PinChange, callback func(Pin)) error {

	// Check if the pin is a GPIOHS pin.
	f := p.FPIOAFunction()
	if f < FUNC_GPIOHS0 || f > FUNC_GPIOHS31 {
		return ErrInvalidDataPin
	}

	gpioPin := uint8(f - FUNC_GPIOHS0)

	// Clear all interrupts.
	kendryte.GPIOHS.RISE_IE.ClearBits(1 << gpioPin)
	kendryte.GPIOHS.FALL_IE.ClearBits(1 << gpioPin)
	kendryte.GPIOHS.HIGH_IE.ClearBits(1 << gpioPin)
	kendryte.GPIOHS.LOW_IE.ClearBits(1 << gpioPin)

	// Clear all the pending bits for this pin.
	kendryte.GPIOHS.RISE_IP.SetBits(1 << gpioPin)
	kendryte.GPIOHS.FALL_IP.SetBits(1 << gpioPin)
	kendryte.GPIOHS.HIGH_IP.SetBits(1 << gpioPin)
	kendryte.GPIOHS.LOW_IP.SetBits(1 << gpioPin)

	if callback == nil {
		if pinCallbacks[gpioPin] != nil {
			pinCallbacks[gpioPin] = nil
		}
		return nil
	}

	if pinCallbacks[gpioPin] != nil {
		// The pin was already configured.
		// To properly re-configure a pin, unset it first and set a new
		// configuration.
		return ErrNoPinChangeChannel
	}

	pinCallbacks[gpioPin] = callback

	// Enable interrupts.
	if change&PinRising != 0 {
		kendryte.GPIOHS.RISE_IE.SetBits(1 << gpioPin)
	}
	if change&PinFalling != 0 {
		kendryte.GPIOHS.FALL_IE.SetBits(1 << gpioPin)
	}

	handleInterrupt := func(inter interrupt.Interrupt) {

		pin := uint8(inter.GetNumber() - kendryte.IRQ_GPIOHS0)

		if kendryte.GPIOHS.RISE_IE.HasBits(1 << pin) {
			kendryte.GPIOHS.RISE_IE.ClearBits(1 << pin)
			// Acknowledge interrupt atomically.
			riscv.AsmFull(
				"amoor.w {}, {mask}, ({reg})",
				map[string]interface{}{
					"mask": uint32(1 << pin),
					"reg":  uintptr(unsafe.Pointer(&kendryte.GPIOHS.RISE_IP.Reg)),
				})
			kendryte.GPIOHS.RISE_IE.SetBits(1 << pin)
		}

		if kendryte.GPIOHS.FALL_IE.HasBits(1 << pin) {
			kendryte.GPIOHS.FALL_IE.ClearBits(1 << pin)
			// Acknowledge interrupt atomically.
			riscv.AsmFull(
				"amoor.w {}, {mask}, ({reg})",
				map[string]interface{}{
					"mask": uint32(1 << pin),
					"reg":  uintptr(unsafe.Pointer(&kendryte.GPIOHS.FALL_IP.Reg)),
				})
			kendryte.GPIOHS.FALL_IE.SetBits(1 << pin)
		}

		pinCallbacks[pin](Pin(pin))
	}

	var ir interrupt.Interrupt

	switch f {
	case FUNC_GPIOHS0:
		ir = interrupt.New(kendryte.IRQ_GPIOHS0, handleInterrupt)
	case FUNC_GPIOHS1:
		ir = interrupt.New(kendryte.IRQ_GPIOHS1, handleInterrupt)
	case FUNC_GPIOHS2:
		ir = interrupt.New(kendryte.IRQ_GPIOHS2, handleInterrupt)
	case FUNC_GPIOHS3:
		ir = interrupt.New(kendryte.IRQ_GPIOHS3, handleInterrupt)
	case FUNC_GPIOHS4:
		ir = interrupt.New(kendryte.IRQ_GPIOHS4, handleInterrupt)
	case FUNC_GPIOHS5:
		ir = interrupt.New(kendryte.IRQ_GPIOHS5, handleInterrupt)
	case FUNC_GPIOHS6:
		ir = interrupt.New(kendryte.IRQ_GPIOHS6, handleInterrupt)
	case FUNC_GPIOHS7:
		ir = interrupt.New(kendryte.IRQ_GPIOHS7, handleInterrupt)
	case FUNC_GPIOHS8:
		ir = interrupt.New(kendryte.IRQ_GPIOHS8, handleInterrupt)
	case FUNC_GPIOHS9:
		ir = interrupt.New(kendryte.IRQ_GPIOHS9, handleInterrupt)
	case FUNC_GPIOHS10:
		ir = interrupt.New(kendryte.IRQ_GPIOHS10, handleInterrupt)
	case FUNC_GPIOHS11:
		ir = interrupt.New(kendryte.IRQ_GPIOHS11, handleInterrupt)
	case FUNC_GPIOHS12:
		ir = interrupt.New(kendryte.IRQ_GPIOHS12, handleInterrupt)
	case FUNC_GPIOHS13:
		ir = interrupt.New(kendryte.IRQ_GPIOHS13, handleInterrupt)
	case FUNC_GPIOHS14:
		ir = interrupt.New(kendryte.IRQ_GPIOHS14, handleInterrupt)
	case FUNC_GPIOHS15:
		ir = interrupt.New(kendryte.IRQ_GPIOHS15, handleInterrupt)
	case FUNC_GPIOHS16:
		ir = interrupt.New(kendryte.IRQ_GPIOHS16, handleInterrupt)
	case FUNC_GPIOHS17:
		ir = interrupt.New(kendryte.IRQ_GPIOHS17, handleInterrupt)
	case FUNC_GPIOHS18:
		ir = interrupt.New(kendryte.IRQ_GPIOHS18, handleInterrupt)
	case FUNC_GPIOHS19:
		ir = interrupt.New(kendryte.IRQ_GPIOHS19, handleInterrupt)
	case FUNC_GPIOHS20:
		ir = interrupt.New(kendryte.IRQ_GPIOHS20, handleInterrupt)
	case FUNC_GPIOHS21:
		ir = interrupt.New(kendryte.IRQ_GPIOHS21, handleInterrupt)
	case FUNC_GPIOHS22:
		ir = interrupt.New(kendryte.IRQ_GPIOHS22, handleInterrupt)
	case FUNC_GPIOHS23:
		ir = interrupt.New(kendryte.IRQ_GPIOHS23, handleInterrupt)
	case FUNC_GPIOHS24:
		ir = interrupt.New(kendryte.IRQ_GPIOHS24, handleInterrupt)
	case FUNC_GPIOHS25:
		ir = interrupt.New(kendryte.IRQ_GPIOHS25, handleInterrupt)
	case FUNC_GPIOHS26:
		ir = interrupt.New(kendryte.IRQ_GPIOHS26, handleInterrupt)
	case FUNC_GPIOHS27:
		ir = interrupt.New(kendryte.IRQ_GPIOHS27, handleInterrupt)
	case FUNC_GPIOHS28:
		ir = interrupt.New(kendryte.IRQ_GPIOHS28, handleInterrupt)
	case FUNC_GPIOHS29:
		ir = interrupt.New(kendryte.IRQ_GPIOHS29, handleInterrupt)
	case FUNC_GPIOHS30:
		ir = interrupt.New(kendryte.IRQ_GPIOHS30, handleInterrupt)
	case FUNC_GPIOHS31:
		ir = interrupt.New(kendryte.IRQ_GPIOHS31, handleInterrupt)
	}

	ir.SetPriority(5)
	ir.Enable()

	return nil

}

type UART struct {
	Bus    *kendryte.UARTHS_Type
	Buffer *RingBuffer
}

var (
	UART0  = &_UART0
	_UART0 = UART{Bus: kendryte.UARTHS, Buffer: NewRingBuffer()}
)

func (uart *UART) Configure(config UARTConfig) {

	// Use default baudrate  if not set.
	if config.BaudRate == 0 {
		config.BaudRate = 115200
	}

	// Use default pins if not set.
	if config.TX == 0 && config.RX == 0 {
		config.TX = UART_TX_PIN
		config.RX = UART_RX_PIN
	}

	config.TX.SetFPIOAFunction(FUNC_UARTHS_TX)
	config.RX.SetFPIOAFunction(FUNC_UARTHS_RX)

	div := CPUFrequency()/config.BaudRate - 1

	uart.Bus.DIV.Set(div)
	uart.Bus.TXCTRL.Set(kendryte.UARTHS_TXCTRL_TXEN)
	uart.Bus.RXCTRL.Set(kendryte.UARTHS_RXCTRL_RXEN)

	// Enable interrupts on receive.
	uart.Bus.IE.Set(kendryte.UARTHS_IE_RXWM)

	intr := interrupt.New(kendryte.IRQ_UARTHS, _UART0.handleInterrupt)
	intr.SetPriority(5)
	intr.Enable()
}

func (uart *UART) handleInterrupt(interrupt.Interrupt) {
	rxdata := uart.Bus.RXDATA.Get()
	c := byte(rxdata)
	if uint32(c) != rxdata {
		// The rxdata has other bits set than just the low 8 bits. This probably
		// means that the 'empty' flag is set, which indicates there is no data
		// to be read and the byte is garbage. Ignore this byte.
		return
	}
	uart.Receive(c)
}

func (uart *UART) writeByte(c byte) error {
	for uart.Bus.TXDATA.Get()&kendryte.UARTHS_TXDATA_FULL != 0 {
	}

	uart.Bus.TXDATA.Set(uint32(c))
	return nil
}

func (uart *UART) flush() {}

type SPI struct {
	Bus *kendryte.SPI_Type
}

// SPIConfig is used to store config info for SPI.
type SPIConfig struct {
	Frequency uint32
	SCK       Pin
	SDO       Pin
	SDI       Pin
	LSBFirst  bool
	Mode      uint8
}

// Configure is intended to setup the SPI interface.
// Only SPI controller 0 and 1 can be used because SPI2 is a special
// peripheral-mode controller and SPI3 is used for flashing.
func (spi SPI) Configure(config SPIConfig) error {
	// Use default pins if not set.
	if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 {
		config.SCK = SPI0_SCK_PIN
		config.SDO = SPI0_SDO_PIN
		config.SDI = SPI0_SDI_PIN
	}

	// Enable APB2 clock.
	kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB2_CLK_EN)

	switch spi.Bus {
	case kendryte.SPI0:
		// Initialize SPI clock.
		kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_SPI0_CLK_EN)
		kendryte.SYSCTL.CLK_TH1.ClearBits(kendryte.SYSCTL_CLK_TH1_SPI0_CLK_Msk)

		// Initialize pins.
		config.SCK.SetFPIOAFunction(FUNC_SPI0_SCLK)
		config.SDO.SetFPIOAFunction(FUNC_SPI0_D0)
		config.SDI.SetFPIOAFunction(FUNC_SPI0_D1)
	case kendryte.SPI1:
		// Initialize SPI clock.
		kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_SPI1_CLK_EN)
		kendryte.SYSCTL.CLK_TH1.ClearBits(kendryte.SYSCTL_CLK_TH1_SPI1_CLK_Msk)

		// Initialize pins.
		config.SCK.SetFPIOAFunction(FUNC_SPI1_SCLK)
		config.SDO.SetFPIOAFunction(FUNC_SPI1_D0)
		config.SDI.SetFPIOAFunction(FUNC_SPI1_D1)
	default:
		return errUnsupportedSPIController
	}

	// Set default frequency.
	if config.Frequency == 0 {
		config.Frequency = 4000000 // 4MHz
	}

	baudr := CPUFrequency() / config.Frequency
	spi.Bus.BAUDR.Set(baudr)

	// Configure SPI mode 0, standard frame format, 8-bit data, little-endian.
	spi.Bus.IMR.Set(0)
	spi.Bus.DMACR.Set(0)
	spi.Bus.DMATDLR.Set(0x10)
	spi.Bus.DMARDLR.Set(0)
	spi.Bus.SER.Set(0)
	spi.Bus.SSIENR.Set(0)
	spi.Bus.CTRLR0.Set((7 << 16))
	spi.Bus.SPI_CTRLR0.Set(0)
	spi.Bus.ENDIAN.Set(0)

	return nil
}

// Transfer writes/reads a single byte using the SPI interface.
func (spi SPI) Transfer(w byte) (byte, error) {
	spi.Bus.SSIENR.Set(0)

	// Set transfer-receive mode.
	spi.Bus.CTRLR0.ClearBits(0x3 << 8)

	// Enable/disable SPI.
	spi.Bus.SSIENR.Set(1)
	defer spi.Bus.SSIENR.Set(0)

	// Enable/disable device.
	spi.Bus.SER.Set(0x1)
	defer spi.Bus.SER.Set(0)

	spi.Bus.DR0.Set(uint32(w))

	// Wait for transfer.
	for spi.Bus.SR.Get()&0x05 != 0x04 {
	}

	// Wait for data.
	for spi.Bus.RXFLR.Get() == 0 {
	}

	return byte(spi.Bus.DR0.Get()), nil
}

// I2C on the K210.
type I2C struct {
	Bus kendryte.I2C_Type
}

var (
	I2C0 = (*I2C)(unsafe.Pointer(kendryte.I2C0))
	I2C1 = (*I2C)(unsafe.Pointer(kendryte.I2C1))
	I2C2 = (*I2C)(unsafe.Pointer(kendryte.I2C2))
)

// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
	Frequency uint32
	SCL       Pin
	SDA       Pin
}

// Configure is intended to setup the I2C interface.
func (i2c *I2C) Configure(config I2CConfig) error {

	if config.Frequency == 0 {
		config.Frequency = 100 * KHz
	}

	if config.SDA == 0 && config.SCL == 0 {
		config.SDA = I2C0_SDA_PIN
		config.SCL = I2C0_SCL_PIN
	}

	// Enable APB0 clock.
	kendryte.SYSCTL.CLK_EN_CENT.SetBits(kendryte.SYSCTL_CLK_EN_CENT_APB0_CLK_EN)

	switch &i2c.Bus {
	case kendryte.I2C0:
		// Initialize I2C0 clock.
		kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C0_CLK_EN)
		kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C0_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C0_CLK_Pos)

		// Initialize pins.
		config.SDA.SetFPIOAFunction(FUNC_I2C0_SDA)
		config.SCL.SetFPIOAFunction(FUNC_I2C0_SCLK)
	case kendryte.I2C1:
		// Initialize I2C1 clock.
		kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C1_CLK_EN)
		kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C1_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C1_CLK_Pos)

		// Initialize pins.
		config.SDA.SetFPIOAFunction(FUNC_I2C1_SDA)
		config.SCL.SetFPIOAFunction(FUNC_I2C1_SCLK)
	case kendryte.I2C2:
		// Initialize I2C2 clock.
		kendryte.SYSCTL.CLK_EN_PERI.SetBits(kendryte.SYSCTL_CLK_EN_PERI_I2C2_CLK_EN)
		kendryte.SYSCTL.CLK_TH5.ReplaceBits(0x03, kendryte.SYSCTL_CLK_TH5_I2C2_CLK_Msk, kendryte.SYSCTL_CLK_TH5_I2C2_CLK_Pos)

		// Initialize pins.
		config.SDA.SetFPIOAFunction(FUNC_I2C2_SDA)
		config.SCL.SetFPIOAFunction(FUNC_I2C2_SCLK)
	}

	i2c.SetBaudRate(config.Frequency)

	i2c.Bus.INTR_MASK.Set(0)
	i2c.Bus.DMA_CR.Set(0x03)
	i2c.Bus.DMA_RDLR.Set(0)
	i2c.Bus.DMA_TDLR.Set(0x4)

	return nil
}

// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
	div := CPUFrequency() / br / 16

	// Disable controller before setting the prescale register.
	i2c.Bus.ENABLE.Set(0)

	i2c.Bus.CON.Set(0x63)

	// Set prescaler registers.
	i2c.Bus.SS_SCL_HCNT.Set(uint32(div))
	i2c.Bus.SS_SCL_LCNT.Set(uint32(div))

	return nil
}

// Tx does a single I2C transaction at the specified address.
// It clocks out the given address, writes the bytes in w, reads back len(r)
// bytes and stores them in r, and generates a stop condition on the bus.
func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
	// Set peripheral address.
	i2c.Bus.TAR.Set(uint32(addr))
	// Enable controller.
	i2c.Bus.ENABLE.Set(1)

	if len(w) != 0 {
		i2c.Bus.CLR_TX_ABRT.Set(i2c.Bus.CLR_TX_ABRT.Get())
		dataLen := uint32(len(w))
		di := 0

		for dataLen != 0 {
			fifoLen := 8 - i2c.Bus.TXFLR.Get()
			if dataLen < fifoLen {
				fifoLen = dataLen
			}

			for i := uint32(0); i < fifoLen; i++ {
				i2c.Bus.DATA_CMD.Set(uint32(w[di]))
				di += 1
			}
			if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
				return errI2CTxAbort
			}
			dataLen -= fifoLen
		}

		// Wait for transmission to complete.
		for i2c.Bus.STATUS.HasBits(kendryte.I2C_STATUS_ACTIVITY) || !i2c.Bus.STATUS.HasBits(kendryte.I2C_STATUS_TFE) {
		}

		if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
			return errI2CTxAbort
		}
	}
	if len(r) != 0 {
		dataLen := uint32(len(r))
		cmdLen := uint32(len(r))
		di := 0

		for dataLen != 0 || cmdLen != 0 {
			fifoLen := i2c.Bus.RXFLR.Get()
			if dataLen < fifoLen {
				fifoLen = dataLen
			}
			for i := uint32(0); i < fifoLen; i++ {
				r[di] = byte(i2c.Bus.DATA_CMD.Get())
				di += 1
			}
			dataLen -= fifoLen

			fifoLen = 8 - i2c.Bus.TXFLR.Get()
			if cmdLen < fifoLen {
				fifoLen = cmdLen
			}
			for i := uint32(0); i < fifoLen; i++ {
				i2c.Bus.DATA_CMD.Set(0x100)
			}
			if i2c.Bus.TX_ABRT_SOURCE.Get() != 0 {
				return errI2CTxAbort
			}
			cmdLen -= fifoLen
		}
	}

	return nil
}
