From fb1d9c8aca0c7362b0f17518ee24e2ac8a2575ab Mon Sep 17 00:00:00 2001 From: ShaunakKDatar Date: Tue, 21 Jan 2025 23:52:53 +0530 Subject: [PATCH] bsp/aarch64/raspberrypi: Add I2C Support - Implements polling-based I2C communication. - Supports three independent I2C master controllers. - Adds support for 10-bit addressing mode. --- .../aarch64/raspberrypi/i2c/raspberrypi-i2c.c | 361 ++++++++++++++++++ .../raspberrypi/include/bsp/raspberrypi-i2c.h | 92 +++++ .../raspberrypi/include/bsp/raspberrypi.h | 27 +- .../aarch64/raspberrypi/bspraspberrypi4.yml | 2 + .../build/bsps/aarch64/raspberrypi/obji2c.yml | 17 + 5 files changed, 495 insertions(+), 4 deletions(-) create mode 100644 bsps/aarch64/raspberrypi/i2c/raspberrypi-i2c.c create mode 100644 bsps/aarch64/raspberrypi/include/bsp/raspberrypi-i2c.h create mode 100644 spec/build/bsps/aarch64/raspberrypi/obji2c.yml diff --git a/bsps/aarch64/raspberrypi/i2c/raspberrypi-i2c.c b/bsps/aarch64/raspberrypi/i2c/raspberrypi-i2c.c new file mode 100644 index 0000000000..191f437921 --- /dev/null +++ b/bsps/aarch64/raspberrypi/i2c/raspberrypi-i2c.c @@ -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 +#include +#include +#include +#include + +#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 ); +} \ No newline at end of file diff --git a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-i2c.h b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-i2c.h new file mode 100644 index 0000000000..a763c299ac --- /dev/null +++ b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-i2c.h @@ -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 +#include +#include +#include + +/** + * @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 */ \ No newline at end of file diff --git a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h index 63eaf5c6f1..fcab38df53 100644 --- a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h +++ b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h @@ -282,7 +282,6 @@ #define BCM2711_SPI4_BASE ( RPI_PERIPHERAL_BASE + 0x204800 ) #define BCM2711_SPI5_BASE ( RPI_PERIPHERAL_BASE + 0x204A00 ) #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_CTL 0xA0 #define BCM2711_CM_PWM_DIV 0xA4 @@ -299,7 +297,6 @@ #define CM_PWM_CTL_BUSY BSP_BIT32( 7 ) #define CM_PWM_CTL_ENAB BSP_BIT32( 4 ) #define CM_PWM_DIV_MASK 0xFFF - /** @} */ /** @@ -307,7 +304,6 @@ * * @{ */ - #define BCM2711_PWM0_BASE ( RPI_PERIPHERAL_BASE + 0x0020C000 ) #define BCM2711_PWM1_BASE ( RPI_PERIPHERAL_BASE + 0x0020C800 ) #define BCM2711_PWM_CONTROL 0x00 @@ -318,7 +314,30 @@ #define BCM2711_PWM_FIFO 0x18 #define BCM2711_PWM_RNG2 0x20 #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 /** @} */ /** diff --git a/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml b/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml index 011ee11ba0..6a6e47a17e 100644 --- a/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml +++ b/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml @@ -67,6 +67,8 @@ links: uid: objwatchdog - role: build-dependency uid: objpwm +- role: build-dependency + uid: obji2c source: - bsps/aarch64/raspberrypi/fdt/bsp_fdt.c - bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c diff --git a/spec/build/bsps/aarch64/raspberrypi/obji2c.yml b/spec/build/bsps/aarch64/raspberrypi/obji2c.yml new file mode 100644 index 0000000000..dea22373ee --- /dev/null +++ b/spec/build/bsps/aarch64/raspberrypi/obji2c.yml @@ -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