bsp/aarch64/raspberrypi: Add I2C Support

- Implements polling-based I2C communication.
- Supports three independent I2C master controllers.
- Adds support for 10-bit addressing mode.
This commit is contained in:
ShaunakKDatar
2025-01-21 23:52:53 +05:30
committed by Shaunak Datar
parent b00a9d9993
commit fb1d9c8aca
5 changed files with 495 additions and 4 deletions

View File

@@ -0,0 +1,361 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup raspberrypi_4_i2c
*
* @brief I2C Driver
*/
/*
* Copyright (C) 2025 Shaunak Datar
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <bsp/irq.h>
#include <bsp/raspberrypi-i2c.h>
#include <bsp/raspberrypi.h>
#include <bsp/rpi-gpio.h>
#include <dev/i2c/i2c.h>
#define C_REG( bus ) BCM2835_REG( ( bus )->base_address + BCM2711_I2C_CONTROL )
#define C_I2CEN ( 1 << 15 )
#define C_INTR ( 1 << 10 )
#define C_INTT ( 1 << 9 )
#define C_INTD ( 1 << 8 )
#define C_ST ( 1 << 7 )
#define C_CLEAR ( 1 << 5 )
#define C_READ ( 1 << 0 )
#define BSC_CORE_CLK_HZ 150000000
#define S_REG( bus ) BCM2835_REG( ( bus )->base_address + BCM2711_I2C_STATUS )
#define S_CLKT ( 1 << 9 )
#define S_ERR ( 1 << 8 )
#define S_RXF ( 1 << 7 )
#define S_TXE ( 1 << 6 )
#define S_RXD ( 1 << 5 )
#define S_TXD ( 1 << 4 )
#define S_RXR ( 1 << 3 )
#define S_TXW ( 1 << 2 )
#define S_DONE ( 1 << 1 )
#define S_TA ( 1 << 0 )
#define S_ERROR ( S_CLKT | S_ERR | S_DONE )
typedef struct {
i2c_bus base;
uint32_t input_clock;
uintptr_t base_address;
raspberrypi_bsc_masters device;
uint32_t remaining_bytes;
uint32_t remaining_transfers;
uint8_t *current_buffer;
uint32_t current_buffer_size;
bool read_transfer;
} raspberrypi_i2c_bus;
static int i2c_polling_read( raspberrypi_i2c_bus *bus )
{
while ( !( S_REG( bus ) & S_DONE ) && ( bus->remaining_bytes > 0 ) ) {
while ( ( S_REG( bus ) & S_RXD ) && ( bus->remaining_bytes > 0 ) ) {
*bus->current_buffer = BCM2835_REG(
bus->base_address + BCM2711_I2C_FIFO
) &
BCM2711_I2C_FIFO_MASK;
bus->current_buffer++;
bus->remaining_bytes--;
/* Check for errors */
if ( S_REG( bus ) & ( S_CLKT | S_ERR ) ) {
return -EIO;
}
}
}
return 0;
}
static int i2c_polling_write( raspberrypi_i2c_bus *bus )
{
while ( !( S_REG( bus ) & S_DONE ) && ( bus->remaining_bytes > 0 ) ) {
while ( bus->remaining_bytes > 0 && ( S_REG( bus ) & S_TXD ) ) {
BCM2835_REG( bus->base_address + BCM2711_I2C_FIFO ) = *(
bus->current_buffer
);
bus->current_buffer++;
bus->remaining_bytes--;
/* Check for errors */
if ( S_REG( bus ) & ( S_CLKT | S_ERR ) ) {
return -EIO;
}
}
}
return 0;
}
static int rpi_i2c_bus_transfer( raspberrypi_i2c_bus *bus )
{
int rv;
if ( bus->read_transfer ) {
rv = i2c_polling_read( bus );
if ( rv < 0 ) {
return rv;
}
} else {
rv = i2c_polling_write( bus );
if ( rv < 0 ) {
return rv;
}
}
if ( ( S_REG( bus ) & S_ERR ) || ( S_REG( bus ) & S_CLKT ) ||
( bus->remaining_bytes != 0 ) ) {
return -EIO;
}
S_REG( bus ) = S_DONE;
return 0;
}
static void rpi_i2c_destroy( i2c_bus *base )
{
raspberrypi_i2c_bus *bus = (raspberrypi_i2c_bus *) base;
i2c_bus_destroy_and_free( &bus->base );
}
static int rpi_i2c_set_clock( i2c_bus *base, unsigned long clock )
{
raspberrypi_i2c_bus *bus = (raspberrypi_i2c_bus *) base;
uint32_t clock_rate;
uint16_t divider;
divider = BSC_CORE_CLK_HZ / clock;
clock_rate = BSC_CORE_CLK_HZ / divider;
while ( clock_rate > clock ) {
++divider;
clock_rate = BSC_CORE_CLK_HZ / divider;
}
BCM2835_REG( bus->base_address + BCM2711_I2C_DIV ) = divider;
return 0;
}
static int rpi_i2c_setup_and_transfer( raspberrypi_i2c_bus *bus )
{
int rv;
while ( bus->remaining_transfers > 0 ) {
bus->remaining_bytes = bus->remaining_transfers > 1 ?
BCM2711_I2C_DLEN_MASK :
( bus->current_buffer_size & BCM2711_I2C_DLEN_MASK
);
BCM2835_REG( bus->base_address + BCM2711_I2C_DLEN ) = bus->remaining_bytes;
/* Clear the error bits before starting new transfer */
S_REG( bus ) = S_ERROR;
rv = rpi_i2c_bus_transfer( bus );
if ( rv < 0 ) {
return rv;
}
--bus->remaining_transfers;
}
return 0;
}
static int rpi_i2c_transfer( i2c_bus *base, i2c_msg *msgs, uint32_t msg_count )
{
raspberrypi_i2c_bus *bus = (raspberrypi_i2c_bus *) base;
int rv = 0;
uint32_t i;
uint8_t msbs;
int supported_flags = I2C_M_TEN | I2C_M_RD;
for ( i = 0; i < msg_count; i++ ) {
if ( msgs[ i ].len == 0 || msgs[ i ].buf == NULL ) {
return -EINVAL;
}
if ( ( msgs[ i ].flags & ~supported_flags ) != 0 ) {
return -EINVAL;
}
}
for ( i = 0; i < msg_count; i++ ) {
bus->current_buffer = msgs[ i ].buf;
bus->current_buffer_size = msgs[ i ].len;
bus->remaining_transfers = ( bus->current_buffer_size +
( BCM2711_I2C_DLEN_MASK - 1 ) ) /
BCM2711_I2C_DLEN_MASK;
/* 10-bit slave address */
if ( msgs[ i ].flags & I2C_M_TEN ) {
/* Add the 8 lsbs of the 10-bit slave address to the fifo register */
BCM2835_REG(
bus->base_address + BCM2711_I2C_FIFO
) = msgs[ i ].addr & BCM2711_I2C_FIFO_MASK;
msbs = msgs[ i ].addr >> 8;
BCM2835_REG(
bus->base_address + BCM2711_I2C_SLAVE_ADDRESS
) = BCM2711_10_BIT_ADDR_MASK | msbs;
} else {
BCM2835_REG(
bus->base_address + BCM2711_I2C_SLAVE_ADDRESS
) = msgs[ i ].addr;
}
if ( msgs[ i ].flags & I2C_M_RD ) {
C_REG( bus ) |= C_CLEAR | C_READ | C_ST; // Read packet transfer
bus->read_transfer = true;
} else {
C_REG( bus ) |= C_CLEAR | C_ST; // Write packet transfer
bus->read_transfer = false;
}
/* Disable clock stretch timeout */
BCM2835_REG( bus->base_address + BCM2711_I2C_CLKT ) = 0;
rv = rpi_i2c_setup_and_transfer( bus );
if ( rv < 0 ) {
return rv;
}
}
return rv;
}
static rtems_status_code rpi_i2c_gpio_init(
raspberrypi_bsc_masters device,
raspberrypi_i2c_bus *bus
)
{
switch ( device ) {
case raspberrypi_bscm0:
raspberrypi_gpio_set_function( 0, GPIO_AF0 );
raspberrypi_gpio_set_function( 1, GPIO_AF0 );
bus->base_address = BCM2711_I2C0_BASE;
break;
case raspberrypi_bscm1:
raspberrypi_gpio_set_function( 2, GPIO_AF0 );
raspberrypi_gpio_set_function( 3, GPIO_AF0 );
bus->base_address = BCM2711_I2C1_BASE;
break;
case raspberrypi_bscm3:
raspberrypi_gpio_set_function( 4, GPIO_AF5 );
raspberrypi_gpio_set_function( 5, GPIO_AF5 );
bus->base_address = BCM2711_I2C3_BASE;
break;
case raspberrypi_bscm4:
raspberrypi_gpio_set_function( 6, GPIO_AF5 );
raspberrypi_gpio_set_function( 7, GPIO_AF5 );
bus->base_address = BCM2711_I2C4_BASE;
break;
case raspberrypi_bscm5:
raspberrypi_gpio_set_function( 10, GPIO_AF5 );
raspberrypi_gpio_set_function( 11, GPIO_AF5 );
bus->base_address = BCM2711_I2C5_BASE;
break;
case raspberrypi_bscm6:
raspberrypi_gpio_set_function( 22, GPIO_AF5 );
raspberrypi_gpio_set_function( 23, GPIO_AF5 );
bus->base_address = BCM2711_I2C6_BASE;
break;
default:
return RTEMS_INVALID_ADDRESS;
}
return RTEMS_SUCCESSFUL;
}
static char *rpi_select_bus( raspberrypi_bsc_masters device )
{
switch ( device ) {
case raspberrypi_bscm0:
return "/dev/i2c-0";
case raspberrypi_bscm1:
return "/dev/i2c-1";
case raspberrypi_bscm3:
return "/dev/i2c-3";
case raspberrypi_bscm4:
return "/dev/i2c-4";
case raspberrypi_bscm5:
return "/dev/i2c-5";
case raspberrypi_bscm6:
return "/dev/i2c-6";
default:
return NULL;
}
}
rtems_status_code rpi_i2c_init(
raspberrypi_bsc_masters device,
uint32_t bus_clock
)
{
raspberrypi_i2c_bus *bus;
rtems_status_code sc;
const char *bus_path;
if ( device != raspberrypi_bscm0 && device != raspberrypi_bscm1 &&
device != raspberrypi_bscm3 && device != raspberrypi_bscm4 &&
device != raspberrypi_bscm5 && device != raspberrypi_bscm6 ) {
return RTEMS_INVALID_NUMBER;
}
bus_path = rpi_select_bus( device );
if ( bus_path == NULL ) {
return RTEMS_INVALID_NUMBER;
}
bus = (raspberrypi_i2c_bus *) i2c_bus_alloc_and_init( sizeof( *bus ) );
if ( bus == NULL ) {
return RTEMS_NO_MEMORY;
}
sc = rpi_i2c_gpio_init( device, bus );
if ( sc != RTEMS_SUCCESSFUL ) {
i2c_bus_destroy_and_free( &bus->base );
return sc;
}
/* Enable I2C */
C_REG( bus ) = C_CLEAR;
C_REG( bus ) = C_I2CEN;
sc = rpi_i2c_set_clock( &bus->base, bus_clock );
if ( sc != RTEMS_SUCCESSFUL ) {
i2c_bus_destroy_and_free( &bus->base );
return sc;
}
bus->base.transfer = rpi_i2c_transfer;
bus->base.set_clock = rpi_i2c_set_clock;
bus->base.destroy = rpi_i2c_destroy;
bus->base.functionality = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
return i2c_bus_register( &bus->base, bus_path );
}

View File

@@ -0,0 +1,92 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup raspberrypi_4_i2c
*
* @brief Raspberry Pi specific I2C definitions.
*/
/*
* Copyright (C) 2025 Shaunak Datar
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LIBBSP_AARCH64_RASPBERRYPI_I2C_H
#define LIBBSP_AARCH64_RASPBERRYPI_I2C_H
#include <bsp/raspberrypi.h>
#include <bsp/rpi-gpio.h>
#include <bsp/utility.h>
#include <dev/i2c/i2c.h>
/**
* @brief I2C controller instances on Raspberry Pi 4.
*/
typedef enum {
/**
* @brief BSC Master 0 (GPIO 0/1)
*/
raspberrypi_bscm0,
/**
* @brief BSC Master 1 (GPIO 2/3)
*/
raspberrypi_bscm1,
/**
* @brief BSC Master 3 (GPIO 4/5)
*/
raspberrypi_bscm3,
/**
* @brief BSC Master 4 (GPIO 6/7)
*/
raspberrypi_bscm4,
/**
* @brief BSC Master 5 (GPIO 10/11)
*/
raspberrypi_bscm5,
/**
* @brief BSC Master 6 (GPIO 22/23)
*/
raspberrypi_bscm6
} raspberrypi_bsc_masters;
/**
* @brief Initialize the I2C bus for a specified master.
*
* @param device The BSC master to initialize.
* @param bus_clock The desired bus clock frequency in Hz.
*
* @return RTEMS status code indicating success or failure.
*/
rtems_status_code rpi_i2c_init(
raspberrypi_bsc_masters device,
uint32_t bus_clock
);
#endif /* LIBBSP_AARCH64_RASPBERRYPI_I2C_H */

View File

@@ -282,7 +282,6 @@
#define BCM2711_SPI4_BASE ( RPI_PERIPHERAL_BASE + 0x204800 ) #define BCM2711_SPI4_BASE ( RPI_PERIPHERAL_BASE + 0x204800 )
#define BCM2711_SPI5_BASE ( RPI_PERIPHERAL_BASE + 0x204A00 ) #define BCM2711_SPI5_BASE ( RPI_PERIPHERAL_BASE + 0x204A00 )
#define BCM2711_SPI6_BASE ( RPI_PERIPHERAL_BASE + 0x204C00 ) #define BCM2711_SPI6_BASE ( RPI_PERIPHERAL_BASE + 0x204C00 )
/** @} */ /** @} */
/** /**
@@ -290,7 +289,6 @@
* *
* @{ * @{
*/ */
#define BCM2711_CM_PWM_BASE ( RPI_PERIPHERAL_BASE + 0x00101000 ) #define BCM2711_CM_PWM_BASE ( RPI_PERIPHERAL_BASE + 0x00101000 )
#define BCM2711_CM_PWM_CTL 0xA0 #define BCM2711_CM_PWM_CTL 0xA0
#define BCM2711_CM_PWM_DIV 0xA4 #define BCM2711_CM_PWM_DIV 0xA4
@@ -299,7 +297,6 @@
#define CM_PWM_CTL_BUSY BSP_BIT32( 7 ) #define CM_PWM_CTL_BUSY BSP_BIT32( 7 )
#define CM_PWM_CTL_ENAB BSP_BIT32( 4 ) #define CM_PWM_CTL_ENAB BSP_BIT32( 4 )
#define CM_PWM_DIV_MASK 0xFFF #define CM_PWM_DIV_MASK 0xFFF
/** @} */ /** @} */
/** /**
@@ -307,7 +304,6 @@
* *
* @{ * @{
*/ */
#define BCM2711_PWM0_BASE ( RPI_PERIPHERAL_BASE + 0x0020C000 ) #define BCM2711_PWM0_BASE ( RPI_PERIPHERAL_BASE + 0x0020C000 )
#define BCM2711_PWM1_BASE ( RPI_PERIPHERAL_BASE + 0x0020C800 ) #define BCM2711_PWM1_BASE ( RPI_PERIPHERAL_BASE + 0x0020C800 )
#define BCM2711_PWM_CONTROL 0x00 #define BCM2711_PWM_CONTROL 0x00
@@ -318,7 +314,30 @@
#define BCM2711_PWM_FIFO 0x18 #define BCM2711_PWM_FIFO 0x18
#define BCM2711_PWM_RNG2 0x20 #define BCM2711_PWM_RNG2 0x20
#define BCM2711_PWM_DAT2 0x24 #define BCM2711_PWM_DAT2 0x24
/** @} */
/**
* @name I2C Registers
*
* @{
*/
#define BCM2711_I2C0_BASE ( RPI_PERIPHERAL_BASE + 0x00205000 )
#define BCM2711_I2C1_BASE ( RPI_PERIPHERAL_BASE + 0x00804000 )
#define BCM2711_I2C3_BASE ( RPI_PERIPHERAL_BASE + 0x00205600 )
#define BCM2711_I2C4_BASE ( RPI_PERIPHERAL_BASE + 0x00205800 )
#define BCM2711_I2C5_BASE ( RPI_PERIPHERAL_BASE + 0x00205a80 )
#define BCM2711_I2C6_BASE ( RPI_PERIPHERAL_BASE + 0x00205c00 )
#define BCM2711_I2C_CONTROL 0x0
#define BCM2711_I2C_STATUS 0x4
#define BCM2711_I2C_DLEN 0x8
#define BCM2711_I2C_SLAVE_ADDRESS 0xc
#define BCM2711_I2C_FIFO 0x10
#define BCM2711_I2C_DIV 0x14
#define BCM2711_DELAY 0x18
#define BCM2711_I2C_CLKT 0x1c
#define BCM2711_I2C_FIFO_MASK 0xFF
#define BCM2711_I2C_DLEN_MASK 0xFFFF
#define BCM2711_10_BIT_ADDR_MASK 0x78
/** @} */ /** @} */
/** /**

View File

@@ -67,6 +67,8 @@ links:
uid: objwatchdog uid: objwatchdog
- role: build-dependency - role: build-dependency
uid: objpwm uid: objpwm
- role: build-dependency
uid: obji2c
source: source:
- bsps/aarch64/raspberrypi/fdt/bsp_fdt.c - bsps/aarch64/raspberrypi/fdt/bsp_fdt.c
- bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c - bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c

View File

@@ -0,0 +1,17 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: objects
cflags: []
copyrights:
- Copyright (C) 2025 Shaunak Datar
cppflags: []
cxxflags: []
enabled-by: true
includes: []
install:
- destination: ${BSP_INCLUDEDIR}/bsp
source:
- bsps/aarch64/raspberrypi/include/bsp/raspberrypi-i2c.h
links: []
source:
- bsps/aarch64/raspberrypi/i2c/raspberrypi-i2c.c
type: build