rt-thread/bsp/raspberry-pi/raspi3-64/driver/bcm283x.c

576 lines
16 KiB
C

/*
* Copyright (c) 2006-2019, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-07-29 zdzn first version
*/
#include "bcm283x.h"
rt_uint32_t bcm283x_peri_read(volatile rt_ubase_t addr)
{
rt_uint32_t ret;
__sync_synchronize();
ret = HWREG32(addr);
__sync_synchronize();
return ret;
}
rt_uint32_t bcm283x_peri_read_nb(volatile rt_ubase_t addr)
{
return HWREG32(addr);
}
void bcm283x_peri_write(volatile rt_ubase_t addr, rt_uint32_t value)
{
__sync_synchronize();
HWREG32(addr) = value;
__sync_synchronize();
}
void bcm283x_peri_write_nb(volatile rt_ubase_t addr, rt_uint32_t value)
{
HWREG32(addr) = value;
}
void bcm283x_peri_set_bits(volatile rt_ubase_t addr, rt_uint32_t value, rt_uint32_t mask)
{
rt_uint32_t v = bcm283x_peri_read(addr);
v = (v & ~mask) | (value & mask);
bcm283x_peri_write(addr, v);
}
void bcm283x_gpio_fsel(rt_uint8_t pin, rt_uint8_t mode)
{
volatile rt_ubase_t addr = (BCM283X_GPIO_BASE + BCM283X_GPIO_GPFSEL0 + (pin / 10) * 4);
rt_uint8_t shift = (pin % 10) * 3;
rt_uint32_t mask = BCM283X_GPIO_FSEL_MASK << shift;
rt_uint32_t value = mode << shift;
bcm283x_peri_set_bits(addr, value, mask);
}
void bcm283x_gpio_set(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPSET0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
bcm283x_peri_write(addr, 1 << shift);
}
void bcm283x_gpio_clr(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPCLR0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
bcm283x_peri_write(addr, 1 << shift);
}
rt_uint8_t bcm283x_gpio_lev(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM2835_GPIO_GPLEV0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = bcm283x_peri_read(addr);
return (value & (1 << shift)) ? HIGH : LOW;
}
rt_uint8_t bcm283x_gpio_eds(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPEDS0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = bcm283x_peri_read(addr);
return (value & (1 << shift)) ? HIGH : LOW;
}
/* Write a 1 to clear the bit in EDS */
void bcm283x_gpio_set_eds(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPEDS0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_write(addr, value);
}
/* Rising edge detect enable */
void bcm283x_gpio_ren(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPREN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_ren(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPREN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* Falling edge detect enable */
void bcm283x_gpio_fen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPFEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_fen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPFEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* High detect enable */
void bcm283x_gpio_hen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPHEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_hen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPHEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* Low detect enable */
void bcm283x_gpio_len(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPLEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_len(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPLEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* Async rising edge detect enable */
void bcm283x_gpio_aren(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPAREN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_aren(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPAREN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* Async falling edge detect enable */
void bcm283x_gpio_afen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPAFEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, value, value);
}
void bcm283x_gpio_clr_afen(rt_uint8_t pin)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPAFEN0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
rt_uint32_t value = 1 << shift;
bcm283x_peri_set_bits(addr, 0, value);
}
/* Set pullup/down */
void bcm283x_gpio_pud(rt_uint8_t pud)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPPUD;
bcm283x_peri_write(addr, pud);
}
/* Pullup/down clock
// Clocks the value of pud into the GPIO pin
*/
void bcm283x_gpio_pudclk(rt_uint8_t pin, rt_uint8_t on)
{
volatile rt_ubase_t addr = BCM283X_GPIO_BASE + BCM283X_GPIO_GPPUDCLK0 + (pin / 32) * 4;
rt_uint8_t shift = pin % 32;
bcm283x_peri_write(addr, (on? 1 : 0) << shift);
}
void bcm283x_gpio_set_pud(rt_uint8_t pin, rt_uint8_t pud)
{
bcm283x_gpio_pud(pud);
bcm283x_clo_delayMicros(10);
bcm283x_gpio_pudclk(pin, 1);
bcm283x_clo_delayMicros(10);
bcm283x_gpio_pud(BCM283X_GPIO_PUD_OFF);
bcm283x_gpio_pudclk(pin, 0);
}
void bcm283x_gpio_write(rt_uint8_t pin, rt_uint8_t val)
{
if (val)
bcm283x_gpio_set(pin);
else
bcm283x_gpio_clr(pin);
}
rt_uint64_t bcm283x_st_read(void)
{
volatile rt_ubase_t addr;
rt_uint32_t hi, lo;
rt_uint64_t st;
addr = BCM283X_ST_BASE + BCM283X_ST_CHI;
hi = bcm283x_peri_read(addr);
addr = BCM283X_ST_BASE + BCM283X_ST_CLO;
lo = bcm283x_peri_read(addr);
addr = BCM283X_ST_BASE + BCM283X_ST_CHI;
st = bcm283x_peri_read(addr);
/* Test for overflow */
if (st == hi)
{
rt_kprintf(">> 1crash???\n");
st <<= 32;
st += lo;
rt_kprintf(">> 2crash!!!\n");
}
else
{
st <<= 32;
addr = BCM283X_ST_BASE + BCM283X_ST_CLO;
st += bcm283x_peri_read(addr);
}
return st;
}
/* microseconds */
void bcm283x_delayMicroseconds(rt_uint64_t micros)
{
rt_uint64_t start;
start = bcm283x_st_read();
rt_kprintf("bcm283x_st_read result: %d\n", start);
/* Not allowed to access timer registers (result is not as precise)*/
if (start==0)
return;
bcm283x_st_delay(start, micros);
}
void bcm283x_clo_delayMicros(rt_uint32_t micros)
{
volatile rt_uint32_t addr;
rt_uint32_t compare;
addr = BCM283X_ST_BASE + BCM283X_ST_CLO;
compare = bcm283x_peri_read(addr) + micros;
while(bcm283x_peri_read(addr) < compare);
}
void bcm283x_st_delay(rt_uint64_t offset_micros, rt_uint64_t micros)
{
rt_uint64_t compare = offset_micros + micros;
while(bcm283x_st_read() < compare);
}
/* Read an number of bytes from I2C */
rt_uint8_t bcm283x_i2c_read(rt_uint32_t base, rt_uint8_t* buf, rt_uint32_t len)
{
volatile rt_uint32_t dlen = base + BCM283X_BSC_DLEN;
volatile rt_uint32_t fifo = base + BCM283X_BSC_FIFO;
volatile rt_uint32_t status = base + BCM283X_BSC_S;
volatile rt_uint32_t control = base + BCM283X_BSC_C;
rt_uint32_t remaining = len;
rt_uint32_t i = 0;
rt_uint8_t reason = BCM283X_I2C_REASON_OK;
/* Clear FIFO */
bcm283x_peri_set_bits(control, BCM283X_BSC_C_CLEAR_1, BCM283X_BSC_C_CLEAR_1);
/* Clear Status */
bcm283x_peri_write_nb(status, BCM283X_BSC_S_CLKT | BCM283X_BSC_S_ERR | BCM283X_BSC_S_DONE);
/* Set Data Length */
bcm283x_peri_write_nb(dlen, len);
/* Start read */
bcm283x_peri_write_nb(control, BCM283X_BSC_C_I2CEN | BCM283X_BSC_C_ST | BCM283X_BSC_C_READ);
/* wait for transfer to complete */
while (!(bcm283x_peri_read_nb(status) & BCM283X_BSC_S_DONE))
{
/* we must empty the FIFO as it is populated and not use any delay */
while (remaining && bcm283x_peri_read_nb(status) & BCM283X_BSC_S_RXD)
{
/* Read from FIFO, no barrier */
buf[i] = bcm283x_peri_read_nb(fifo);
i++;
remaining--;
}
}
/* transfer has finished - grab any remaining stuff in FIFO */
while (remaining && (bcm283x_peri_read_nb(status) & BCM283X_BSC_S_RXD))
{
/* Read from FIFO, no barrier */
buf[i] = bcm283x_peri_read_nb(fifo);
i++;
remaining--;
}
/* Received a NACK */
if (bcm283x_peri_read(status) & BCM283X_BSC_S_ERR)
{
reason = BCM283X_I2C_REASON_ERROR_NACK;
}
/* Received Clock Stretch Timeout */
else if (bcm283x_peri_read(status) & BCM283X_BSC_S_CLKT)
{
reason = BCM283X_I2C_REASON_ERROR_CLKT;
}
/* Not all data is received */
else if (remaining)
{
reason = BCM283X_I2C_REASON_ERROR_DATA;
}
bcm283x_peri_set_bits(control, BCM283X_BSC_S_DONE, BCM283X_BSC_S_DONE);
return reason;
}
int bcm283x_i2c_begin(int no)
{
if (0 == no)
{
bcm283x_gpio_fsel(BCM_GPIO_PIN_0, BCM283X_GPIO_FSEL_ALT0); /* SDA */
bcm283x_gpio_fsel(BCM_GPIO_PIN_1, BCM283X_GPIO_FSEL_ALT0); /* SCL */
}
else
{
bcm283x_gpio_fsel(BCM_GPIO_PIN_2, BCM283X_GPIO_FSEL_ALT0); /* SDA */
bcm283x_gpio_fsel(BCM_GPIO_PIN_3, BCM283X_GPIO_FSEL_ALT0); /* SCL */
}
return 0;
}
void bcm283x_i2c_end(int no)
{
if (0 == no)
{
bcm283x_gpio_fsel(BCM_GPIO_PIN_0, BCM283X_GPIO_FSEL_INPT); /* SDA */
bcm283x_gpio_fsel(BCM_GPIO_PIN_1, BCM283X_GPIO_FSEL_INPT); /* SCL */
}
else
{
bcm283x_gpio_fsel(BCM_GPIO_PIN_2, BCM283X_GPIO_FSEL_INPT); /* SDA */
bcm283x_gpio_fsel(BCM_GPIO_PIN_3, BCM283X_GPIO_FSEL_INPT); /* SCL */
}
}
void bcm283x_i2c_setSlaveAddress(int no, rt_uint8_t saddr)
{
volatile rt_uint32_t addr;
if (0 == no)
addr = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_A;
else
addr = PER_BASE + BCM283X_BSC1_BASE + BCM283X_BSC_A;
bcm283x_peri_write(addr, saddr);
}
void bcm283x_i2c_setClockDivider(int no, rt_uint16_t divider)
{
volatile rt_uint32_t addr;
if (0 == no)
addr = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_DIV;
else
addr = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_DIV;
bcm283x_peri_write(addr, divider);
}
void bcm283x_i2c_set_baudrate(int no, rt_uint32_t baudrate)
{
rt_uint32_t divider;
divider = (BCM283X_CORE_CLK_HZ / baudrate) & 0xFFFE;
bcm283x_i2c_setClockDivider(no, (rt_uint16_t)divider);
}
/* Writes an number of bytes to I2C */
rt_uint8_t bcm283x_i2c_write(rt_uint32_t base, const rt_uint8_t * buf, rt_uint32_t len)
{
volatile rt_uint32_t dlen = base + BCM283X_BSC_DLEN;
volatile rt_uint32_t fifo = base + BCM283X_BSC_FIFO;
volatile rt_uint32_t status = base + BCM283X_BSC_S;
volatile rt_uint32_t control = base + BCM283X_BSC_C;
rt_uint32_t remaining = len;
rt_uint32_t i = 0;
rt_uint8_t reason = BCM283X_I2C_REASON_OK;
/* Clear FIFO */
bcm283x_peri_set_bits(control, BCM283X_BSC_C_CLEAR_1, BCM283X_BSC_C_CLEAR_1);
/* Clear Status */
bcm283x_peri_write(status, BCM283X_BSC_S_CLKT | BCM283X_BSC_S_ERR | BCM283X_BSC_S_DONE);
/* Set Data Length */
bcm283x_peri_write(dlen, len);
/* pre populate FIFO with max buffer */
while(remaining && (i < BCM283X_BSC_FIFO_SIZE))
{
bcm283x_peri_write_nb(fifo, buf[i]);
i++;
remaining--;
}
/* Enable device and start transfer */
bcm283x_peri_write(control, BCM283X_BSC_C_I2CEN | BCM283X_BSC_C_ST);
/* Transfer is over when BCM2835_BSC_S_DONE */
while(!(bcm283x_peri_read(status) & BCM283X_BSC_S_DONE))
{
while (remaining && (bcm283x_peri_read(status) & BCM283X_BSC_S_TXD))
{
/* Write to FIFO */
bcm283x_peri_write(fifo, buf[i]);
i++;
remaining--;
}
}
/* Received a NACK */
if (bcm283x_peri_read(status) & BCM283X_BSC_S_ERR)
{
reason = BCM283X_I2C_REASON_ERROR_NACK;
}
/* Received Clock Stretch Timeout */
else if (bcm283x_peri_read(status) & BCM283X_BSC_S_CLKT)
{
reason = BCM283X_I2C_REASON_ERROR_CLKT;
}
/* Not all data is sent */
else if (remaining)
{
reason = BCM283X_I2C_REASON_ERROR_DATA;
}
bcm283x_peri_set_bits(control, BCM283X_BSC_S_DONE, BCM283X_BSC_S_DONE);
return reason;
}
rt_uint8_t bcm283x_i2c_write_read_rs(char* cmds, rt_uint32_t cmds_len, char* buf, rt_uint32_t buf_len)
{
volatile rt_uint32_t dlen = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_DLEN;
volatile rt_uint32_t fifo = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_FIFO;
volatile rt_uint32_t status = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_S;
volatile rt_uint32_t control = PER_BASE + BCM283X_BSC0_BASE + BCM283X_BSC_C;
rt_uint32_t remaining = cmds_len;
rt_uint32_t i = 0;
rt_uint8_t reason = BCM283X_I2C_REASON_OK;
/* Clear FIFO */
bcm283x_peri_set_bits(control, BCM283X_BSC_C_CLEAR_1, BCM283X_BSC_C_CLEAR_1);
/* Clear Status */
bcm283x_peri_write(status, BCM283X_BSC_S_CLKT | BCM283X_BSC_S_ERR | BCM283X_BSC_S_DONE);
/* Set Data Length */
bcm283x_peri_write(dlen, cmds_len);
/* pre populate FIFO with max buffer */
while(remaining && (i < BCM283X_BSC_FIFO_SIZE))
{
bcm283x_peri_write_nb(fifo, cmds[i]);
i++;
remaining--;
}
/* Enable device and start transfer */
bcm283x_peri_write(control, BCM283X_BSC_C_I2CEN | BCM283X_BSC_C_ST);
/* poll for transfer has started (way to do repeated start, from BCM2835 datasheet) */
while (!(bcm283x_peri_read(status) & BCM283X_BSC_S_TA))
{
/* Linux may cause us to miss entire transfer stage */
if (bcm283x_peri_read_nb(status) & BCM283X_BSC_S_DONE)
break;
}
remaining = buf_len;
i = 0;
/* Send a repeated start with read bit set in address */
bcm283x_peri_write(dlen, buf_len);
bcm283x_peri_write(control, BCM283X_BSC_C_I2CEN | BCM283X_BSC_C_ST | BCM283X_BSC_C_READ);
/* Wait for write to complete and first byte back. */
bcm283x_clo_delayMicros(100);
/* wait for transfer to complete */
while (!(bcm283x_peri_read_nb(status) & BCM283X_BSC_S_DONE))
{
/* we must empty the FIFO as it is populated and not use any delay */
while (remaining && bcm283x_peri_read(status) & BCM283X_BSC_S_RXD)
{
/* Read from FIFO, no barrier */
buf[i] = bcm283x_peri_read_nb(fifo);
i++;
remaining--;
}
}
/* transfer has finished - grab any remaining stuff in FIFO */
while (remaining && (bcm283x_peri_read(status) & BCM283X_BSC_S_RXD))
{
/* Read from FIFO */
buf[i] = bcm283x_peri_read(fifo);
i++;
remaining--;
}
/* Received a NACK */
if (bcm283x_peri_read(status) & BCM283X_BSC_S_ERR)
{
reason = BCM283X_I2C_REASON_ERROR_NACK;
}
/* Received Clock Stretch Timeout */
else if (bcm283x_peri_read(status) & BCM283X_BSC_S_CLKT)
{
reason = BCM283X_I2C_REASON_ERROR_CLKT;
}
/* Not all data is sent */
else if (remaining)
{
reason = BCM283X_I2C_REASON_ERROR_DATA;
}
bcm283x_peri_set_bits(control, BCM283X_BSC_S_DONE, BCM283X_BSC_S_DONE);
return reason;
}