This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM437x: I2C handling in Linux

Other Parts Discussed in Thread: TPS65218

This is not a question, just a note about things I stumbled on while getting the I2C to work with the AM437X. This isn’t all inclusive and I hope others will add or correct it.

The two main problems were: accessing 16-bit I2C addresses with U-Boot and Linux tools, and I2C devices appearing always busy to Linux.

My I2C bus has a 32k eeprom (CAT24C256) and a power management integrated circuit (TPS65218). The interesting thing about these two devices is that the eeprom uses 16-bit addresses and the PMIC uses 8-bit addresses for the internal registers (memory).

To start, it’s best to confirm the hardware is working from U-Boot using the i2c commands. U-Boot works with one of the three I2C busses at a time. Select bus 1 (or substitute 0 or 2 to select those busses) with

i2c dev 1

then check that the devices can be seen with:

i2c probe

which should list the addresses of all devices on that bus.

You can test reading, for example, the 8-bit device register 0x12 with

i2c md 0x24 0x12 0x1

and writing, for example, an 0x6F at register 10 with

i2c mw 0x24 0x10 0x6F 0x1 (for the PMIC, don’t expect to read 0x10 and get 0x6F, though, see its TRM)

 

U-Boot doesn’t easily handle the 16-bit eeprom. It can read 8 bytes starting at address 0 with

i2c md 0x50 0 8

but I haven’t found an easy way to read from another address or write anywhere (suggestions?).

 

Moving on to Linux, the device tree configuration can be found in the Starter Kit file am437x-sk-evm.dts. You may need to change the “reg = <0x50> “ and  “reg = <0x24>” to the I2C addresses of the eeprom and PMIC, respectively, on your board.

If Linux and the device tree are configured correctly, you will see at boot up /dev/i2c-1 (or -0 or -2) and /sys/bus/i2c/devices, which will have a subdirectory for each device on the bus. You can test writing and reading the eeprom with

echo –n ‘12345’ > /sys/bus/i2c/devices/1-0050/eeprom.

and

cat /sys/bus/i2c/devices/1-0050/eeprom

Be sure to substitute your bus_number-device_address for 1-0050 in the above.

 

The Linux utilities i2cset and i2cget, like U-Boot, are designed for 8-bit address devices. Also, they must be forced to work (-f flag because they use an old device model, see below). To set the internal eeprom address counter to 0, issue a “dummy” write with

i2cset –y –f 1 0x50 0x00 0x00

then to read consecutive bytes using the eeprom’s immediate address read, repeatedly use

i2cget –y –f 1 0x50

This should return whatever you wrote using the “echo” previously.

A single byte can be written to the eeprom by concatenating the least significant byte of the address with the byte to be written and using a word write, like so:

I2cset –y –f 0x50 0x00 0x9901 w

where the address is 0x00 0x01 and the data written is 0x99.

The i2cset block write might also work

i2cset –y –f 1 0x50 0x00 0x00 0x77 i

but I haven’t used it successfully, and the above command sets the first 3 bytes of eeprom to 0 and the fourth to 0x77, instead of setting just the first byte to 0x77 as desired.

The technique used to access the eeprom via a C user applications has changed from older versions found on the web. You need to:

1)     open the /dev/i2c-1 file

2)     assign the device address to the file with ioctl(fd, I2C_SLAVE_FORCE,deviceAddress)

3)     read by first writing the 8-bit or 16-bit internal address, then doing a regular read.

4)     write by prepending the 8-bit or 16-bit internal address to the output and then doing a regular write.

5)     Check that the write has finished by doing a dummy read, or wait a while after a write before doing a read.

What’s changed is that I2C_SLAVE_FORCE must be used instead of I2C_SLAVE. This is why the Linux tools always show the device busy; the older tools don't use I2C_SLAVE_FORCE (there is a note in drivers/i2c/i2c-dev.c about this). Also, there is no longer any need to explicitly write the device address with the correct read-write bit; this is now handled by the driver when the usual write or read method is called.

 

I have a C program for accessing the I2C for either 8 or 16 bit address. I’ll post it if someone wants it or you can contact me and I’ll email you a copy.