cancel
Showing results for 
Search instead for 
Did you mean: 

Is it possible to use i2c1 (0x40012000) in tf-a?

GLaure
Senior

Hi!

As far as I looked into the source code, only is i2c4 is used for communication to pmic.

Our custom board has an external PLL connected to i2c1. The PLL needs an initial configuraion to provide 48MHz.

It would be ideal if I could do this during tf-a bl2 stage.

What I have tried:

Added i2c1 to the device tree:

&i2c1 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c1_pins_c>;
	i2c-scl-rising-time-ns = <100>;
	i2c-scl-falling-time-ns = <7>;
	clock-frequency = <400000>;
	status = "okay";
};

This is my i2c1 test code:

		int ret, val;
		struct dt_node_info i2c_info;
		struct i2c_handle_s *i2c = &i2c_handle;
		struct stm32_i2c_init_s i2c_init;
		uint32_t pll_i2c_addr = 0x6D;
		uint8_t data[18] = {0};
 
		i2c_info.base = 0x40012000;
		i2c_info.status = 2;
		i2c_info.clock = I2C1_K;
 
		i2c->i2c_base_addr		= i2c_info.base;
		i2c->dt_status			= i2c_info.status;
		i2c->clock				= i2c_info.clock;
 
		i2c->i2c_state				= I2C_STATE_RESET;
		i2c_init.own_address1		= pll_i2c_addr;
		i2c_init.addressing_mode	= I2C_ADDRESSINGMODE_7BIT;
		i2c_init.dual_address_mode	= I2C_DUALADDRESS_DISABLE;
		i2c_init.own_address2		= 0;
		i2c_init.own_address2_masks	= I2C_OAR2_OA2NOMASK;
		i2c_init.general_call_mode	= I2C_GENERALCALL_DISABLE;
		i2c_init.no_stretch_mode	= I2C_NOSTRETCH_DISABLE;
		i2c_init.rise_time = 185;
		i2c_init.fall_time = 20;
		i2c_init.bus_rate = 400000;
		i2c_init.analog_filter		= 1;
		i2c_init.digital_filter_coef	= 0;
 
		ERROR("setup_nexdaq_pll\n");
		ERROR("I2C base addr:	0x%x\n", i2c_info.base);
		ERROR("I2C dt_status:	0x%x\n", i2c_info.status);
		ERROR("I2C clock:		0x%x\n", i2c_info.clock);
		ERROR("pll_i2c_addr:	0x%x\n", pll_i2c_addr);
 
		ret = stm32_i2c_init(i2c, &i2c_init);
		if (ret != 0) {
			ERROR("Cannot initialize I2C %x (%d)\n",
		      i2c->i2c_base_addr, ret);
			//panic();
		}
 
		{
			val = stm32_i2c_mem_read(i2c, pll_i2c_addr, 0x94, 
				I2C_MEMADD_SIZE_8BIT, data, 1, 50);
			if (val < 0) {
				ERROR("stm32_i2c_mem_read failed = %d\n", val);
			}
 
			ERROR("data: %x\n", data[0]);
		}
 
		if (!stm32_i2c_is_device_ready(i2c, pll_i2c_addr, 1,
				       I2C_TIMEOUT_BUSY_MS)) {
			ERROR("I2C device not ready\n");
			//panic();
		}

This is the output with additional logging:

NOTICE:  CPU: STM32MP157FAC Rev.Z
NOTICE:  Model: DEWETRON NEXDAQ tf-a
ERROR:   setup_nexdaq_pll
ERROR:   I2C base addr: 0x40012000
ERROR:   I2C dt_status: 0x2
ERROR:   I2C clock:             0x89
ERROR:   pmic_i2c_addr: 0x6d
ERROR:   I2C timeout_init_us
ERROR:   I2C i2c_request_memory_read
ERROR:   i2c_request_memory_read
ERROR:   stm32_i2c_mem_read failed = -5
ERROR:   data: 0
ERROR:   I2C device not ready

In Linux access to the device is possible:

root@stm32mp1-nexio:~# i2cdetect 0
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-0.
I will probe address range 0x08-0x77.
Continue? [Y/n] y
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- 6d -- -- 
70: -- -- -- -- -- -- -- --                         
root@stm32mp1-nexio:~# i2cde

1 ACCEPTED SOLUTION

Accepted Solutions
GLaure
Senior

It is possible and I got it running:

Add the clock gate to stm32mp1_clk.c

	_CLK_SC_SELEC(SEC, RCC_MP_APB1ENSETR, 21, I2C1_K, _I2C12_SEL),

Add gpio pins

&pinctrl {
	i2c1_pins_c: i2c1-2 {
		pins {
			pinmux = <STM32_PINMUX('H', 12, AF5)>, /* I2C1_SCL */
			         <STM32_PINMUX('H', 11, AF5)>; /* I2C1_SDA */
			bias-disable;
			drive-open-drain;
			slew-rate = <0>;
		};
	};
};

Add i2c1

		i2c1: i2c@40012000 {
			compatible = "st,nxpll";
			reg = <0x40012000 0x400>;
			interrupt-names = "event", "error";
			interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
					      <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&rcc I2C1_K>;
			resets = <&rcc I2C1_R>;
			#address-cells = <1>;
			#size-cells = <0>;
			st,syscfg-fmp = <&syscfg 0x4 0x1>;
			wakeup-source;
			status = "disabled";
		};

Add missing definitions to stm32mp1_def.h:

 #if STM32MP15
#define I2C1_BASE			U(0x40012000)
 #define I2C4_BASE			U(0x5C002000)
 #define I2C6_BASE			U(0x5c009000)
 #endif

stm32mp1_shared_resources.c:

 	case UART7_BASE:
 	case UART8_BASE:
 	case IWDG2_BASE:
	case I2C1_BASE:
 		/* Allow drivers to register some non-secure resources */
 		VERBOSE("IO for non-secure resource 0x%x\n",
 			(unsigned int)base);

Now i2c1 can be used in similar way the pmic uses i2c4.

Bye Gunther

View solution in original post

2 REPLIES 2
GLaure
Senior

Update:

Added i2c1 pincontrol settings (same as kernel):

&pinctrl {
	i2c1_pins_c: i2c1-2 {
		pins {
			pinmux = <STM32_PINMUX('H', 12, AF5)>, /* I2C1_SCL */
			         <STM32_PINMUX('H', 11, AF5)>; /* I2C1_SDA */
			bias-disable;
			drive-open-drain;
			slew-rate = <0>;
		};
	};
};

and used

stm32_i2c_get_setup_from_fdt to apply settings before calling stm32_i2c_init.

Now I get this verbose output:

ERROR:   stm32_i2c_get_setup_from_fdt
ERROR:   rise_time = 100
ERROR:   fall_time = 7
ERROR:   dt_set_gpio_config
ERROR:   mode = 2, bank = 7, pin = 12
ERROR:   GPIO 7 mode set to 0xfeffffff
ERROR:   GPIO 7 type set to 0x1000
ERROR:   GPIO 7 speed set to 0x0
ERROR:   GPIO 7 mode pull to 0x0
ERROR:   GPIO 7 mode alternate low to 0x0
ERROR:   GPIO 7 mode alternate high to 0x50000
ERROR:   GPIO 7 output data set to 0x0
ERROR:   mode = 2, bank = 7, pin = 11
ERROR:   GPIO 7 mode set to 0xfebfffff
ERROR:   GPIO 7 type set to 0x1800
ERROR:   GPIO 7 speed set to 0x0
ERROR:   GPIO 7 mode pull to 0x0
ERROR:   GPIO 7 mode alternate low to 0x0
ERROR:   GPIO 7 mode alternate high to 0x55000
ERROR:   GPIO 7 output data set to 0x0
ERROR:   I2C timeout_init_us
ERROR:   I2C i2c_request_memory_read
ERROR:   i2c_wait_txis
ERROR:   i2c_wait_txis ERR
ERROR:   i2c_request_memory_read
ERROR:   stm32_i2c_mem_read failed = -5
ERROR:   data: 0
ERROR:   I2C device not ready

Going to get an osci to check if anything happens on i2c1 bus.

GLaure
Senior

It is possible and I got it running:

Add the clock gate to stm32mp1_clk.c

	_CLK_SC_SELEC(SEC, RCC_MP_APB1ENSETR, 21, I2C1_K, _I2C12_SEL),

Add gpio pins

&pinctrl {
	i2c1_pins_c: i2c1-2 {
		pins {
			pinmux = <STM32_PINMUX('H', 12, AF5)>, /* I2C1_SCL */
			         <STM32_PINMUX('H', 11, AF5)>; /* I2C1_SDA */
			bias-disable;
			drive-open-drain;
			slew-rate = <0>;
		};
	};
};

Add i2c1

		i2c1: i2c@40012000 {
			compatible = "st,nxpll";
			reg = <0x40012000 0x400>;
			interrupt-names = "event", "error";
			interrupts-extended = <&exti 21 IRQ_TYPE_LEVEL_HIGH>,
					      <&intc GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&rcc I2C1_K>;
			resets = <&rcc I2C1_R>;
			#address-cells = <1>;
			#size-cells = <0>;
			st,syscfg-fmp = <&syscfg 0x4 0x1>;
			wakeup-source;
			status = "disabled";
		};

Add missing definitions to stm32mp1_def.h:

 #if STM32MP15
#define I2C1_BASE			U(0x40012000)
 #define I2C4_BASE			U(0x5C002000)
 #define I2C6_BASE			U(0x5c009000)
 #endif

stm32mp1_shared_resources.c:

 	case UART7_BASE:
 	case UART8_BASE:
 	case IWDG2_BASE:
	case I2C1_BASE:
 		/* Allow drivers to register some non-secure resources */
 		VERBOSE("IO for non-secure resource 0x%x\n",
 			(unsigned int)base);

Now i2c1 can be used in similar way the pmic uses i2c4.

Bye Gunther