 Hello everybody. Thank you for being here. This is a basic introduction to using I2C on Linux. I am Luca Ceresoli, embedded Linux engineer at Bootlin, and we provide consultancy on embedded Linux systems on the Linux kernel, especially device drivers, bootloaders, build systems, and so on. And we also provide trainings on all of those topics. I live in Bergamo, Italy, and I'm speaking a bit fast because I have a lot to say. I hope everything can be understood, but stop me if anything is not clear. So let me introduce briefly what is I2C. I2C is a communication protocol meant for communication between interpreter circuits on a board, on a PCB typically, over a short distance. It is designed to be very simple to implement in hardware. It has only two wires, and on two wires many chips can communicate, and it is not discoverable, no plug-in. It doesn't have many advanced features, but it's very popular because it is very effective. It is a pretty low speed, usually 100 or 400 kHz, and you can also find it named with different acronyms, depending on the vendor. So I2C, IIC, TWI, and so on, but it's basically always the same thing. You can find documentation on Wikipedia and also in the kernel documentation. There are mainly two roles in I2C, so a chip on the line can be either an adapter or a client. The adapter in right in picture is also called a master in the I2C specification, or the adapter is the name used in the Linux kernel. It's also called the controller, and sometimes a bit confusingly a bust. It is the device that starts all the transactions, all the communication, and there is usually one per bus, but also multi-master is supported. The adapter has no address. The other role is called the client within the Linux kernel. The specification calls it slave. It's also called device sometimes, and it's the green boxes in the picture. It's absolutely normal to have multiple per bus. It is the kind of device that responds to transaction made by the adapter, and it has an address to be made by seven bits, even though there is a 10-bit extension, and the address is set in hardware. It's not automatically assigned by the adapter like in USB. It's really depending on the chip, and it's in its own hardware, in its own silicon. The two lines are called officially SDA and SCL. SDA is the data line. It is bidirectional, so depending on the protocol, in some moments the adapter is driving it. In some moments it's the device, the client, depending on the protocol. The SCL line is usually called the clock line. If you expect that a clock is a line that moves at a constant period, it's not exactly the case in I2C, but it is still the line that tells when things should be done, and so it is called usually the clock. All the lines are open collector, which is useful to have all of the chips in the same line. This is how a communication works in I2C. There is always initially a start condition, which is the one here, and then it's always the adapter which sends the first byte. All communication are 8 bits in I2C. It sends the first 8 bytes, which contain the address of the client to contact, which is 7-bit plus one direction bit, read or write. Then the client sends in a cloning bit. Then there is another byte, and since this is a read transaction, then this byte will be sent by the client, and then the adapter will send the acknowledge. Finally, there is a stop condition at the end. This is an entire transaction that cannot be interrupted on the line. I2C defines this relatively simple structure. There are bytes going in one or the other direction, and that's pretty much it. It doesn't define what you should put in those bytes. But there is another protocol, which is called SMBus, which is actually designed for chip communication on a motherboard. It is basically a subset of I2C, but in addition it defines a lot of common transaction types, like to read or write a register, for example, or to read a write and buffer. This is very useful, and many I2C devices actually use the same command structure as SMBus. Basically, the two protocols are largely overlapping. Typically, I2C and SMBus devices can coexist on the same bus. In the Linux kernel, for I2C devices, it is recommended to use SMBus commands whenever the I2C chip uses the same command structure. The two protocols are really very close to each other, very much overlapping. Let's see how I2C hooks into the Linux driver model, which models whatever is a device connected to the system. The device model is made by layers, which pretty much map the structure of the devices in the hardware from the CPU to the peripherals. A driver for an I2C connected chip is represented in green. In this example, we assume we have an external GPIO controller connected over I2C, and so it will have a driver. There will be a GPIO subsystem that will send us commands via this set or get callbacks implemented by the GPIO chip driver. We are receiving requests from the upper layer to do something that is specific to the feature provided by the chip. In response to that, our I2C chip driver, GPIO in this example, will prepare a transaction that it wants to do to the chip and ask the I2C subsystem to do this transaction, which will actually be fed to the adapter driver. The adapter will then do this transaction doing whatever action like writing on the platform bus, for example, to actually let the transaction start and after the end it will return back the result to the original color. So when the system starts, the various drivers are populated from bottom to the top, so usually the platform will create the adapter driver, which in turn will inform the I2C subsystem that there is a new adapter, so a new bus, and then the I2C subsystem will instantiate the driver for all of the peripherals on the physical bus and which in turn will create GPIO chips or RTC or audio codecs or whatever is connected there. Once this is in place, you can see in CSFS the devices that are present in the system, well the devices that are known by the kernel at least, so in CSFS I2C devices you can see two families of objects, in the bottom part you can see the adapter of the buses actually, bus and adapter are really one to each other, there is one bus per adapter. So we have four buses in this case which are numbered from zero to three and in the higher part of this list you can see the actual clients whose name is number of the adapter dash address of the I2C client. And so inside those directories there are more files where you can see some information about all of the devices so it can be useful to at least to see if the kernel knows your devices there which is already the first thing to check and also some additional information that is specific to that device. How things are populated into the kernel in embedded systems it's almost always done by device tree so I will focus on that. If you already know something about device tree you are probably familiar with this kind of syntax and basically you almost always have already the I2C adapter described inside your system on chip device tree file and so you don't need to declare the adapter usually. You need to reference it so 100% I2C4 here and to set some additional parameters some additional properties specific to your own board. So in this case for example we set the clock frequency which is very common to a maximum rate of 400 kilohertz and say it is okay to enable the device and then for each client that is connected on the on the bus of this adapter we have a sub node. The sub node needs to have a compatible string as usual in device tree to be able to probe the appropriate driver and then the reg property which is the client address the slave address as it's mentioned in usually in the sheets and so it's the only parameter that is always required by I2C to well this is always needed. All of the other properties are specific to this device so this specific chip needs this interrupt ping control and so on it's not really I2C specific. So the structure is very simple in device tree to describe your adapter and the clients connected to it and there are many more properties optional properties I'm not covering all of them but there is a link here to the documentation where you can see all the common properties for especially for I2C adapters. Okay so in your life you will possibly never have to write an adapter driver because it's usually done by whoever implements the dbsp for the specific system on chip but it's absolutely common that you will need to implement an I2C device drive client device driver and because there are so many models around and so that's what I'm going to cover. Let's see an example I found in the kernel one that is the simplest I was able to find and it's a GPIO controller actually and I'm starting from the end of the file because it's where actually is the entry point of a driver so you have to populate a struct I2C driver which whose name is quite self explaining you give it a name you have to give it two tables do you want to I will cover those two tables later and then a probe function which I also will cover later and finally you call the module I2C driver macro to inform the kernel that there is a driver so it will be known to the kernel and used whenever needed so these are the two tables to populate the second one is particularly important because it's the one used for device three and the one that matches your compatible string with this specific driver so if you have this compatible string in your device three then this driver will be loaded and used and there can be additional parameters for a specific version of the chip like here we support a four GPIO and an eight GPIO variant so we have some additional parameters for that the probe function is the function that gets called by the kernel to ask the driver to initialize and prepare everything about the device and make it ready to work so this is the typical structure it first allocates some memory for driver specific information so information that is specific of that chip and then it initializes internal structure in this case these are specific for the GPIO chip kind of device it would be different if it were an RTC and IO in audio codec or whatever but it's important that here in this example here the GPIO subsystem will need to call this to function get and set to modify or read the status of GPIOs then drivers usually also write to the device in this function to initialize it it's not the case in this driver because it's a simple chip and then you call i2c client data which will take the device specific pointer and copy that put that into the i2c client structure so that you can have it back later if needed and finally there is pretty much always a call to the upper layer to the upper subsystem to inform it there is a new device in this case there is a new GPIO GPIO chip device and so you are calling the GPIO chip add to add a new GPIO chip into the kernel it could be different if you are creating an ADC or an RTC or whatever so but basically you are populating in the upper layer rock so this is really the minimal structure of the driver and this is a recap of all the steps that I went through for reference then you have to implement those callbacks for in this case the GPIO subsystem at some point the GPIO subsystem will call you tell him please set this GPIO output or please read those inputs and this is a simplified version of the of the actual function in the kernel so basically what it does it it obtains the pointer to the i2c client for from its own internal structures then it prepares the buffer to send to the device which in this case is just a byte because it's a simple device and then it calls in this case it's i2c smbus write byte which sends a byte to the device as you can see we are using as smbus call because it's this device needs a sort of transaction that is identical to the one defined in the smbus and so we are using the smbus calls so this is a very very simple example of sending a transaction of a function in the driver to send the transaction to the device there are other functions to do transaction to the device there is the this family where the write byte function that we have just seen is there is a corresponding read function and there is there are also functions to send a buffer not only a byte to send or receive a buffer but i think the most most widely used function is probably those two have register like access because many devices really present a set of registers that you can read and write via i2c and so the calls are i2c smbus write byte data or read byte data which take as a parameter the register number and then of course the the value there are also variants of these to transfer more than one byte but you can see all of them in the documentation especially in the smbus protocol there are many variants so probably you will find one that will suit the needs of your specific chip you need to implement but if you don't find any no problem there is i2c transfer which i call the Swiss armin knife or linux i2c because it really allows you to implement any sort of transaction or even better any sort of sequence of transactions so it can do many transactions basically as many as you want and by default it does repeated start between them to keep them connected together and it's very customizable it has many flags applicable to each transaction so an example of i2c transfer is from another driver so basically what you need to do is to create some i2c message structure in an array actually in this case we need two transactions for this for this device and then populate those with the with the data to send or receive the first transaction has no flags which means by default it's a write transaction so it will send data and so you have to pre-populate a buffer and put that buffer and its length into the structure and you have to set the client address in the second transaction again you send the client address which could be different between transactions it's not common but it's possible and then you have a receive buffer and a flag indicating it is a read transaction so in the end all you have to do after populating the array is call i2c transfer passing the array and it will just do all the transaction and get back to you with the return value indicating the overall result and so this is the most powerful call to do the transaction you might need so that's the overall picture of how to write a driver for an i2c connected device and now let's move on the top floor on the higher floor so in user space so drivers work in the kernel but let's see what you can do in user space and the first rule about using i2c from user space is do not use i2c from user space you know i2c is a communication bus which is meant to connect the CPU to some external chip which in turns provides some functionality so that functionality will be an RTC it will be an audio codec it will be whatever and so you should use that functionality the higher level functionality and the kernel in turn will go to your i2c adapter and whatever to bring you that functionality which has a standardized interface and so on but still there are valid use cases for using tools in user space and so let's have a look at them i2c tools is the name of a package containing multiple tools it's available in every distribution every build system and so on and they are useful mostly for debugging testing prototyping and such like activities which are probably useful for you when writing a driver actually this is not supposed to be used as the tool for your real real usage many of these tools assume that a device has registers SMBus like so they are not always this is not always a correct assumption when you run these tools they will produce this warning this problem can confuse your i2c bus goes data loss and worse so you are warned because i2c does not standardize the type of transaction so if you do the wrong command you might screw up something or produce explosions or whatever so be careful learn your hardware before just running random commands so the first tool that i want to show you is i2c detect which can well we can list the buses but most important it can go on on a bus and try to contact each each client address and see if there is a response and so it will show you this nice table showing that at most addresses there is dash dash which means there was no response at this address there is a number here 28 which means i got a response i got an acknowledge basically from from when i try to contact this address so there is probably a chip there and then there is a uu which means this address is already used by the kernel so there is already a driver for it so you should not use the user based tools on this chip or the driver would be probably get confused other tools are i2c get and i2c set which allow to set and get the value of a register they can do multiple types of sm bus and i2c transaction so it's pretty flexible and the only big limitation of these tools is they assume the register address is 8 bits for trust they are they are they are very flexible so it's pretty simple you just i2c get the bus number and then the register address and then you you will get well no sorry the bus number the client address and the register address and you get the value and that's it so it's very simple to to to use and can be very useful i2c down is a sort of powerful version of i2c get which iterates over the entire 8 bit address in space so it will basically print the all of the value of all of the registers in your device which is extremely useful in some cases to debug to understand if your settings have been taken or not and so on and then if none of those tools is enough for you there is again something called i2c transfer which is the swiss army knife of linus i2c in user space so it can also do an arbitrary number of transactions it takes multiple parameters so for example here we are saying write one byte at this client address and then this is the byte to send and then read one byte from the same address so basically with this line i re-implemented the i2c get command that we saw in a previous slide with i2c transfer so you can do more powerful things with this it could be just to test if you correctly understood the protocol of the device before implementing that in the driver you want to make sure you understood that correctly okay so this is a good set a good toolkit of software tools but it might not be enough you might need also hardware tools if something goes wrong especially you will need them one of them is possibly an oscilloscope which is able to show you whatever happens on each of the lines simultaneously it's very it's very useful if you need to check the voltage levels if there is any noise any spikes and so on that could produce unwanted effects and many many oscilloscope nowadays can also decode i2c very nicely so this is an example screenshot i don't know if it's very visible but well this is the data line and the clock line and in the bottom area we have the decoding so it says you are accessing address client address 51 and there was no acknowledgement and this was a right transaction but you can also see for example there are small spikes here and it's useful to do the mostly hardware debugging that could cause strange problems in the software this is another example with an entire registered read actually so the decoding also says you are access doing a right transaction to other to client address 52 and the data is 22 which is the registered address and then this is the read transaction which gets you back the the data another tool that you can use is a logic analyzer actually there are software implementation of this i use pulse view which is part of the sigrock project it's an open source software it's in your distribution and it it can work with very cheap data acquisition devices i have this one that i paid like 15 euro and it's perfect for i2c so it can sample the lines and a logic analyzer does not show you the levels it has only high and low that's it but for the rest it's very powerful because you can see very nicely the transactions here and puts view the codes them in a very readable way there is the start condition then the address is 42 and then it's a right transaction and there is no acknowledgement and there is a stop condition so it's very it's very easy to to read and to see if anything is wrong so here we have the same transaction as before reading a register this is also pretty easy to read and there is the repeated start here which is a start condition that happens after another start condition without the stop condition in the middle and it's used to ensure that there is nothing for example another master that starts another transaction in the middle so it's another thing you can see from this from this kind of tool okay and so probably the main goal of all of those tools is to do troubleshooting of course you will need troubleshooting at some point and especially when with the vice drivers it could be in the great area between software and hardware so here is a brief overview of some troubles and how to investigate them and what can be the reason so one but first the first rule you have to keep in mind about troubleshooting is never ignore the return values from the functions so you don't have to assume transaction have succeeded because anything would happen if you have a wrong assumption so always check the return value so after you done your homework look at the kernel logs they might contain some useful error message and so a good point to to investigate if it doesn't work you can use i-square three tools to check whether for example your register has really the correct value and so on and if those are not enough you go onto the oscilloscope or logic analyzer so one typical problem with i-square c is you don't get an act an acknowledge from your your device so this is the error message from i-square three tools and this is the error code returned by the internal calls so the first tool tool to use to the back i would recommend to use is i-square detect you could just find a it could just find some response from a different address than the one you expect like the the next address or similar and so maybe you didn't configure correctly the address pins of the device so it is responding to another address so you check the data sheet and or check the schematics and maybe that device can respond to different addresses and based on the pins it's configured for another address than you expect again with i-square c detect you might just see that there is no response from any address or i mean from any unexpected address and so as if the device were not there so maybe the device is just not power or it is held in reset your reset polarity is wrong or it is broken it is and so it has an unsoldered pin so it can be all sort of things it's mostly a hardware debugging thing now then if those fail you should take the oscilloscope or the logic analyzer and for example you see no activity on the lines even though you are running i-square c detect or you are running your driver and so the colonel says i've been doing transactions but you don't see anything on the lines so the first thing to check is the pin mooks maybe you have mooks your adapter onto the wrong pins and so it is not driving those lines it's driving somewhere else so check those and then you should also check your device tree you might have put your your client under the wrong bus so you are contacted in the wrong bus and so on again with an oscilloscope you could also see that the lines are always low so if they are always high it's the previous case so the pull-ups keep the line high and nobody drives them low on the opposite if they are always low that is possibly due to missing pull-up resistors so somebody is driving them low and nobody will ever pull them high so again something to check on your hardware as they work around you might use internal pull-ups in your system on chip if you cannot fix it quickly in hardware the same problem of a missing acknowledge instead of being systematic happening always could be happening only once in a while sporadically and so in this case it's a bit more tricky but one possibility is that your clock or data lines are returning to the high polarity too slowly it's a pull-up it's an open collector line so you might have resistors that are just too weak you need stronger resistors and so one work around to move on before you can fix them is to reduce the clock frequency so you have more time for the transaction again if you see with an oscilloscope you see a noise on the lines that's extremely bad because it could be interpreted as a couple or the data could be read incorrectly and so this needs absolutely to be fixed in the hardware no bus can work if there is too much noise another possibility is that you have a mismatch between the timings wrong timings it's not very common in sqrc because it says low bus but if it happens you could well you probably should improve your pcb design but otherwise some adapters support the device three properties to modify the delays of one line with respect to the other so it might be helpful in some cases or as a work around you could again reduce the clock frequency until you have your hardware fixed a different set of problems is that well it is something that actually caused me some headache when you have an unclean reboot like watchdog reset for example after that you are hung during the booting or some device just does not respond during the boot and as if it were not there and this is typically due to a driver that is that was in the middle of a transaction and then the cpu rebooted so if it did the clean reboot hopefully the driver would put back the device in a clean situation able to be reinitialized but since the cpu rebooted suddenly in the middle of a transaction the device could be there stuck somewhere and not responding correctly or even keeping your your bus hung which is extremely bad so one one thing you should do is to reset your chips during boot possibly in hardware but if it's not possible in the bootloader so even but bootloader you should just set all of the reset pins of all of the devices on the bus so that they are reset before if the first i2c access and you are safe then in in in your reboots okay another situation is the bus busy situation it's actually quite easy to spot if you look at the kernel log because there is really a bus busy message so it's very clear bus busy means that the clock line is kept low by somebody so the but not the adapter so the adapter has released it waiting for the resistor to the pull up resistor to pull it up again but this didn't happen and so it might be for example a multi-master well no well first thing is it could be a short circuit if it's happening systematically so you should check the hardware or maybe you have a mounting problem or maybe just some solder incorrectly done and so on so if that is the systematic but if it's happening only sporadically it could be just a chip that in some cases goes crazy and or gets him badly configured and so he keeps the line low for some reason so you well you should fix that if possible if it's a driver bug or maybe find a better chip but the kernel has some self-defense mechanism there is a recovery procedure which is actually also visible here so the kernel will try to recover the bus by moving the lines in such a way that a client should interpret as well it should move the device from its status and it can also be the the recovery mechanism can also be provided to GPIO pins that it will use to take over the bus instead of using the adapter it will use the GPIOs to be able to drive the bus in every possible waveform so to get your device unlocked so it's quite powerful but still you should not rely on this but rather fix your your investigate the problem and fix your hardware or or drivers or whatever is the cause another cause for this kind of problem could be multi master multi master in my experience is not very common but still if you have multi master then you first have to make sure that the other master is not keeping the the the bus down because the other master is typically not under the control of your system it's maybe a microcontroller whatever you have to check that okay that is all I hope that was interesting useful we have a few minutes for questions if you have any yep sorry I don't know that so there is a an error correction mechanism in s4c okay no I don't know that I probably it needs a client the cheap implementation I guess yeah okay is it I've done okay is it popular among chips I don't think so okay I see yeah yeah sorry I don't know that you mean two ice proceed devices that has the same the same address yeah if you really cannot change their address and so on there is here there are chips called i square c switch or i square c muxes which well I think the picture represents pretty well what they do so basically they they connect your adapter to one or another line or they can have four or eight lines so they and you can change the switches based on i square c itself to the to the mux chip or to using GPIO it depends really on the on the on the model and this is very useful to access to use this this situation you have like eight or 10 or 20 chips of the same model you probably cannot solve that with using only the address pins and so this is quite useful it's well supported in the link channel so I'm sorry I don't hear yeah in in device tree you will have under the adapter node you will have the mux node and under the mux node one sub node for each of the uh downstream buses and the chip devices in there yeah yeah you're welcome yep I'm sorry I don't hear the okay so considering the yeah sure I didn't mention that because there was no time available but there is a system in a linux called reg map which allows to abstract register access and it can work with i square c spi and many others and uh you should use reg map when I would say when it provides some useful features to you like it can do caching if you frequently modify the same register it it's definitely to be used pretty much mandatory if you have the same chip that can have an i square c connection or an spi connection which is quite common in audio cortex for example or well other drivers still use reg map even though they don't have a strong requirement for it but it it's because it also provides some extra features like they can expose in the bag of s I think all of the registers and so on so it's actually it's up to you it's whatever you feel it's better if you don't have a strong need it's really up to I personally I think it could be okay to use just the the i square c calls at least for simple drivers it's also easier if as an entry point if you are not super experienced but reg map can be quite useful especially for larger situations yep uh stop one more okay okay yep uh you get the wrong address with i square c detect I don't know any other tools similar to i square c detect uh if so the question is I have some unexpected results uh from what I expect from the board I don't think there is any other tool at least not that I know there probably are but uh if they don't work maybe it should just be debug then understood why if i square c detect is wrong or if your assumption on the board are wrong which is I would suggest using a logic analyzer would be better here because it can sample a lot of data with respect to no oscilloscope so it could sample easily the entire run over all the addresses that i square c detect does and so you can inspect why do you you expect a an act here but you don't get it in i square c detect so you can see if on the line really the act is not there or if it is there and sees the problem is in your hardware or in the tool and if you find a bug in the tool it would be very good to send the patch to fix it but honestly I it's widely used so I would be surprised it's also quite a simple tool it would be it could be anyway a bug in the adapter driver maybe uh because it's also in the line between you and and the device yeah this is actually one quite common problem when working right with i square c on linux this shifting yeah good good point uh the addresses that I that you saw in all of my examples is here for example 28 is the seven bits using from the lowest bit so it basically the highest address is seven fx but in some data sheets in many data sheets actually they are represented in the other way so using the seven highest bits so the the it will use only odd numbers or even numbers uh so you have to take care of that and you might need to shift to take the number in the data sheet shifted down one bit and that use that in linux so linux always uses the lower seven bits which is different from what you see on the bus actually so very good point yeah yes exactly so yeah the remark is i square c detect uses different commands uh it can use one or a read or write command and some devices respond only on one or only on the other so you should check this heavily look at the help from i square c detect and uh and keep in mind the general rule that those tools are the abusing a protocol so they are assuming uh some kind of responses which is what most chips do but not all so really uh understand what your device needs and make sure you do that transaction if none of the tools does them out of the box you can use i square c transfer and prepare your own transaction uh or just write that into the driver if you already have a driver skeleton okay i think we have to stop yeah uh one question from the virtual conference yes i'm sorry i didn't yeah oh yeah oh that's just device three syntax uh yeah so here uh yeah you will find this everywhere not only in i square c the same address is here and there so basically the reg one is the one that is used really by by the kernel code and the at 28 here is i think it's only a convention it is nice when you read it in in this is a fast for example or in the bug uh the bug logging uh it's informative but i don't think it really has any active role or but i'm not a really a device three expert it's mandatory by the specification okay okay so all right yeah so by the specification of well device three it has to to be like that so it doesn't provide any feature but then you have to do that there yeah yeah what does that make the name unique oh yeah sure of course yeah it also makes the name unique you there are other techniques to do that but this is one and it's it works yeah sure all right yeah thank you very much have a good lunch