Replacement I2C kernel module for Raspbian

A while ago, I got I2C repeated starts and SMBus support to work from the Linux kernel on a Raspberry Pi. I recently built a new 3.10.25 kernel from the foundation’s kernel fork on Github and updated my own fork of their fork with just the I2C update in a new branch. In the process, I managed to misuse git so that commits now attempt to go to the Raspberry Pi Foundation’s kernel fork rather than my own. I tested the module on the copy of Raspbian that I have. It is running the 3.10.24 kernel, but is quite happy with the new kernel module (or for 3.10.37, or 3.12.24) and my MLX90614 test program. I also built the kernel module for the BMP085, but it looks like that requires more than just a kernel module file to get it working. No big deal for me; I’ll just replace the whole kernel.  I just wanted to see if I could make it really easy for someone else.

To use the updated I2C module, first download it. The file will need to replace the one in /lib/modules/<kernel version>/kernel/drivers/i2c/busses. You may want to keep a copy of what is already there. After copying it, check to see if the i2c-bcm2708 module is loaded. If not, load it up and have fun! If it is, you can either reboot or unload the i2c-bcm2708 kernel module. Before unloading will work, any dependent modules must first be unloaded. It wasn’t loaded for me right after boot, so it hopefully won’t be any trouble.

Update: I finally got the code up on Github. I hope the kernel module has been working out for anyone who has tried it. Please do leave a comment about any success or failures with it. I have yet to get any feedback, so I only have my own test case to claim that it works.

Advertisements

Tags: , , , , , , , ,

25 Responses to “Replacement I2C kernel module for Raspbian”

  1. I2C repeated starts implemented on the Raspberry Pi | Jovian Outpost Says:

    […] Update: If you just want a kernel module for something close to kernel version 3.10.25, look here. […]

  2. Dan Sandberg Says:

    Tried to get the MMA8452Q accelerometer working using your patched module but no luck so far. I’m using the python-smbus module and specifically the read_i2c_block_data command. Any ideas? Thanks!

    • jjackowski Says:

      I haven’t messed with a MMA8452Q before, so my best answer is based on some research I just did without the part. I also haven’t used python-smbus. Hopefully I won’t be too wrong.

      First of all, try to read out a single byte without using a block read. If this works, then reading a word (two bytes) should also work. Does that work with the I2C driver module I didn’t make?

      Second, check the output of dmesg for a string like:
      BSC%d Controller (mod) at 0x%08lx (irq %d) (baudrate %d)

      I added the “(mod)” part; if that isn’t there, you’re not running the modified module I made.

      After looking over kernel code and what seems to be the C code for python-smbus, it does seem like what you are doing will trigger the repeated start logic.

      I hope this helps!

  3. Ealanach Says:

    I tried the precompiled module and it didn’t work for me. However, I built a module from your source code and it worked perfectly with my application, also an MLX90614.

    Thanks for making it available. If you want the module I built to post on here let me know and I’ll send it on (I’m running Raspian Linux raspberrypi 3.10.37).

    • jjackowski Says:

      Good to hear the code worked, and thanks for the offer! I did recently get 3.10.37 running, too, but didn’t think to test with Raspbian again. Just edited the post with the link to the 3.10.37 module I built, but admittedly still haven’t tested it with Raspbian. I’ll probably need to build another for 3.12 soon.

  4. Dan Sandberg Says:

    Yikes, I forgot to update my original comment! I did get it working for the MMA8452Q. I had to limit the the “bus.read_i2c_block_data” command to only ask for at most 30 bytes. Any more than that and it didn’t work. But 30 bytes was sufficient for me.

    Thanks so much!

    • jjackowski Says:

      Thanks for the update; good to hear it works! The limit you found is imposed by the kernel for SMBus block transfers. I’m not sure about I2C.

  5. Dan Sandberg Says:

    Any idea why your patch was replaced by I different one that doesn’t seem to work? Could you say a few words here to try to help explain differences between your patch and the other one that doesn’t seem to work?

    https://github.com/raspberrypi/linux/pull/318

    • jjackowski Says:

      The patch from cbeytas looks simpler than mine, which might be why it was chosen. My solution is a little more complex because I avoided putting the repeated start logic in the interrupt handler. It didn’t seem to me to be a good idea to have a polling wait there. Instead, I put that logic where the driver starts the data transfer. That could explain the different results you are seeing.
      Don’t merge the two patches. The result may occasionally try a second repeated start for no good reason, or dereference a bad address.

  6. Dan Sandberg Says:

    My “merging” I meant taking your code and making it active when the “combined” flag is set. So you don’t see anything obviously wrong with the other code, eh? Too bad 🙂

    • jjackowski Says:

      I must have been a little tired. Making it work conditionally on a flag should be fine. I was thinking you’d include both attempted solutions.

      The only thing I see that looks problimatic with the other solution is the polling wait in the interrupt handler. I think that could prevent other interrupts from being handled in a timely manner, but I could be wrong (doubt it, though). The way I implemented it keeps it out of interrupt handling. The hardware requires a polling wait; I bet the engineer was on a tight deadline.

      • Dan Sandberg Says:

        Makes sense. BTW, with your code, I get an error rate of about 2% in the I2C transactions I am doing (reading from a centrifuge). Is this expected given the buggy hardware implementation or does it point to a bug somewhere?

      • jjackowski Says:

        I’m seeing some errors with the MLX90614, too. Happens a little less than once every two days with an otherwise idle system, and more often if not idle. This makes me think it is because it doesn’t respond to the polling wait fast enough to prevent the hardware from sending a stop condition. Since I put that code outside the interrupt, it should be running with the same priority as whatever invoked it, and a task switch to something else is possible. If this is the case, it should only affect cases that need a repeated start. With the MLX90614, I just enable PEC and handle error conditions; other devices might not fare as well. Still beats crashing the whole system, though. Using a high-priority thread, maybe a real-time priority, for the communication may mitigate the problem. I haven’t given it a try.

  7. Bart Van Thielen Says:

    It worked for me (with MLX IR sensor) until I upgraded to kernel 3.12.22.

    I downloaded the new kernel sourcecode and tried to recompile your i2c-bcm2708.c file into another .ko file but I am getting the errors on compilation (can’t seem to post them here)

    Do you know what might be the problem?

    If you have also upgraded to 3.12.xx, could you recompile the source and post it on this website?

    Thank you,

    Bart Van Thielen

    • jjackowski Says:

      I haven’t yet made the move to the 3.12 kernels, but since you asked, I got started on it. I built 3.12.24 from the source code I got not long ago and put in the changes I made earlier, I merged the i2c-bcm2708.c files rather than copy them, so I’m getting different results: no errors. I also took out the other fix since there isn’t a point in having both. I’ve updated my repository on Github with the change.

      I haven’t yet dealt with the update to the firmware, so I haven’t tried this kernel yet. I may get a chance tomorrow. I’ll update the original post with the link in case you, or anyone else, want to give it a try. Chances are that unloding the kernel module, copying in the new one, and reloading will be enough to try if you’re already running 3.12.x.

      • Bart Van Thielen Says:

        I copied the .ko file in the new link to “/lib/modules/3.12.22+/kernel/drivers/i2c/busses/” and rebooted the RPI, but I still have problems because the /dev files for i2c are not there:

        root@aardbei:/home/pi/cabineMonitor# i2cdetect -y 0
        Error: Could not open file `/dev/i2c-0′ or `/dev/i2c/0′: No such file or directory
        root@aardbei:/home/pi/cabineMonitor# i2cdetect -y 1
        Error: Could not open file `/dev/i2c-1′ or `/dev/i2c/1′: No such file or directory

        When I put back the original .ko file the /dev files appear and the above commands work.

      • jjackowski Says:

        I just got to testing it, but I had no problems. Then I realized I hadn’t built the module with the kernel configuration from the Foundation. Sorry about that. I rebuilt and uploaded it (same link), so maybe it’ll work now. There is still a possibility that since I used the 3.12.24 sources it won’t work with 3.12.22, but I’ve seen minor version mismatches not cause problems before.

      • Bart Van Thielen Says:

        It seems to work now: the /dev files have re-appeared and I can read data from the device
        Thanks!

  8. Bart Van Thielen Says:

    the first errors I am getting are:

    /home/pi/SMBUS_mod/i2c-bcm2708.c:314:22: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘bcm2708_i2c_probe
    /home/pi/SMBUS_mod/i2c-bcm2708.c:412:22: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘bcm2708_i2c_remove
    /home/pi/SMBUS_mod/i2c-bcm2708.c:433:12: error: ‘bcm2708_i2c_probe’ undeclared here (not in a function)

  9. Raspberry PI e i2c | Adelmo's World Says:

    […] Jackowski ha sviluppato un hack per questo problema. informazioni possono essere reperite qui e qui. Il codice per il dispositivo mpl3115 lo si può trovare qui e restituisce questo output: […]

  10. Wesley Witt Says:

    I would like to get a version of the driver for the 3.18 kernel in the current version of wheezy. What is the best way for me to do this? I don’t have a problem building the code, but I’m not sure what is required to do this.

    • jjackowski Says:

      You may not need to. The driver that is in the 3.18 kernel for the Raspberry Pi has a parameter, called combined, that when set to true will attempt to use repeated starts. By default it is false. The implementation is different from mine and puts the polling inside the interrupt handler. That has the potential to cause trouble, but it seems worth a try, especially since it is already there. You’ll need to load the kernel module with the parameter. You can unload it to avoid a reboot.

      If you really want to use my implementation, bringing it into the 3.18 kernel will require some code changes as well as building the kernel. The code changes will require familiarity with C and some understanding of the solution. I wrote about it in another post linked at the top of this one. There is some BCM2835 documentation available for download that is helpful, too.

      Unfortunately, I’m probably not going to be able to do this myself for a few weeks, but I can answer questions.

  11. Wesley Witt Says:

    Thanks so much for your reply. I actually merged your changes into the 3.18 kernel sources and have built the kernel & modules, but I have not been successful in loading the module I built. See this thread for context: http://www.raspberrypi.org/forums/viewtopic.php?f=66&t=105716&p=730035. In short it seems that the Linux kernel sources on GitHub are not the sources used to build the Wheezy image available for download and I cannot find the matching sources. This makes building a replacement kernel module impossible. While I’m a seasoned software engineer I’ve never built & replaced a kernel module on a PI before so I may misunderstand some of the process. Regardless it sounds like there may be a suitable solution built into 3.18 by default. What do I need to do to have the module load with the new parameter by default?

    • jjackowski Says:

      You can find a way to change the combined parameter for the module here:
      http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840&start=25
      It isn’t really setting it by default, but it could be put in a script run as the system boots, like one inside /etc/local.d.

      From the link you sent, it sounds like you have the right idea on how to build the kernel. I’m not sure why it isn’t working. I have used the Raspberry Pi sources on Github with success, but still haven’t tried 3.18. If you are trying not to replace the whole kernel, then using the same version of gcc and the same configuration is needed, but it seems you did that. I did the same thing with the module binaries I put on here after testing them with Raspbian. You could try using the whole kernel you built.

      I build the kernels using gcc built by crossdev on Gentoo Linux. I also use this script, which may be a little different from what you are using:

      #!/bin/sh
      # run this instead of make for configuring and building with a cross-compiler
      exec make ARCH=”arm” CROSS_COMPILE=”armv6j-hardfloat-linux-gnueabi-” INSTALL_MOD_PATH=”modules” “$@”

  12. Wesley Witt Says:

    Thanks. On the RPI2 enabling combined cannot be done as described in the linked thread. The kernel module is not loaded as it was on the 3.16 kernel. Now the kernel uses device tree and you need to add these lines to your /boot/config.txt:

    dtparam=i2c_arm=on
    dtparam=i2c_arm_combined=on

    As to the kernel compile issue the problem is that there is no matching source tree for the kernel that is distributed with the Raspian image. From what I can see if you want to rebuild and replace a kernel module, like the I2C driver, then you MUST replace the entire kernel so that the .ko has a matching OS.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: