2009-10-29 18:30:44     sc16is7xx i2c to uart bridge - driver?

Document created by Aaronwu Employee on Aug 27, 2013
Version 1Show Document
  • View in full screen mode

2009-10-29 18:30:44     sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 81880   




On our custom BF537 board (quite similar to the STAMP) we have a sc16is750 i2c to uart bridge. This chip is a combined IO-expander and UART, with an i2c/spi host interface. On our board, it is connected to the i2c-bus alongside a couple of pca9539 IO-expanders.


We are using the 2009R1 kernel, and I am very satisfied with the new GPIO/sysfs-based interface to GPIO.


The pca9539s has working drivers, so no problems there. However, I couldn't find any driver for the sc16is750, so I tried creating one based on the pca9539 driver. I was successful in controlling the GPIOs, but sadly I also need to provide a driver/interface to the UART within this chip.


I've tried searching for drivers for similar chips, and I found that there is a max3100 driver in the kernel trunk (but not in the 2009R1 release), but this chip is SPI based, and it has no GPIO. My problem is that I am inexperienced with driver development, and I am really unsure how I would create a driver that could handle both the GPIO and the UART within the sc16is750.


As I mentioned, I have had some success with the GPIO part of the sc16is750, but I don't know how to add support for the UART. Ideally, I would want the UART to be exposed as a regular serial port device (dev/ttySC16x or something like that), but since I also need to access the GPIOs, I don't know how to proceed.


I would be grateful for any pointers and help that I could get.  :-)










2009-10-30 11:49:14     Re: sc16is7xx i2c to uart bridge - driver?


Message: 81914   




There are multi-function devices ( drivers/mfd) in Linux -- it should be easy to use that framework. Michael did one for the adp5520 - it wasn't too bad. It is just a lot of files, in different places.















2009-10-30 15:42:37     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 81921   


Thanks. I have looked at that driver, and it may prove useful as an example.


Anyway, I gather a multi-function driver is the way to go, then. The alternative would be to have separate drivers, one for the GPIO and one for the uart, but doing it all in one driver is probably the best way to proceed.


Very often I am puzzled by the file hierarchy in the distro, and it is difficult finding out which files belong together. It is just quite complex, and only experience can remedy that.  :-)


-Øyv ind




2009-11-05 16:38:44     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82101   


I've now started implementing this driver, and I am using the adp5520 driver as a base.


First goal was to implement the GPIO-part, and in the testing phase I am doing the development out of tree.


The driver (for now) consists of two modules, one for the GPIO part, and one for the parent I2C device. I copy these two modules to the board, and insert them with insmod (parent device first). This works as expected, and I get a new virtual gpiochip in /sys/class/gpio. I can control the pins, and everything is nice.


However, I am running into a problem I don't know how to debug. I can use rmmod to remove and insert the gpio-driver (first unexporting the pins, of course) without any problems, but when I try to remove the parent driver, I get this on the console:


BUG: failure at m/slab.c:591page_get_cache()!                                                                                                  

Kernel panic - not syncing: BUG!


This also happens if I just insert and then remove the parent driver without inserting the GPIO-driver, so the problem obviously lies within the parent driver. Howver, this code is pretty much identical to the one in the adp5520 driver, and I am too inexperienced to know how to attack this.


I've traced the problem down to this code:


static int __remove_subdev(struct device *dev, void *unused)




   return 0;



static int sc16is7xx_remove_subdevs(struct sc16is7xx_chip *chip)


   return device_for_each_child(chip->dev, NULL, __remove_subdev);



The function sc16is7xx_remove_subdevs is called from the main driver remove, and this is what causes the panic. I am using the 2009R1 kernel and corresponding toolchain.


Some general advice on how to debug drivers will also be welcome.. :-)








2009-11-06 05:53:45     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82122   


I'm still stumped on this. I've turned on some debugging in the kernel, and the trace looks like this:


Hardware Trace:                                                                                       

   0 Target : <0x00004458> { _dump_bfin_trace_buffer + 0x0 }                                                                                             

     Source : <0x0003688c> { _kfree + 0x3c } CALL pcrel                                                                                                  

   1 Target : <0x00036884> { _kfree + 0x34 }                                                                                                             

     Source : <0x00036878> { _kfree + 0x28 } IF CC JUMP                                                                                                  

   2 Target : <0x00036850> { _kfree + 0x0 }                                                                                                              

     Source : <0x0009b782> { _platform_device_release + 0xe } CALL pcrel                                                                                 

   3 Target : <0x0009b774> { _platform_device_release + 0x0 }                                                                                            

     Source : <0x00098a6c> { _device_release + 0x28 } JUMP (P2)                                                                                          

   4 Target : <0x00098a68> { _device_release + 0x24 }                                                                                                    

     Source : <0x00098a56> { _device_release + 0x12 } IF CC JUMP                                                                                         

   5 Target : <0x00098a44> { _device_release + 0x0 }                                                                                                     

     Source : <0x0008121c> { _kobject_release + 0x44 } CALL (P2)                                                                                         

   6 Target : <0x00081210> { _kobject_release + 0x38 }                                                                                                   

     Source : <0x00081208> { _kobject_release + 0x30 } IF CC JUMP                                                                                        

   7 Target : <0x00081200> { _kobject_release + 0x28 }                                                                                                   

     Source : <0x000811f6> { _kobject_release + 0x1e } IF CC JUMP                                                                                        

   8 Target : <0x000811d8> { _kobject_release + 0x0 }                                                                                                    

     Source : <0x00081ba6> { _kref_put + 0x62 } CALL (P4)                                                                                                

   9 Target : <0x00081ba4> { _kref_put + 0x60 }                                                                                                          

     Source : <0x00081b9e> { _kref_put + 0x5a } IF !CC JUMP                                                                                              

  10 Target : <0x00081b7e> { _kref_put + 0x3a }                                                                                                          

     Source : <0x00081b6c> { _kref_put + 0x28 } IF CC JUMP                                                                                               

  11 Target : <0x00081b62> { _kref_put + 0x1e }                                                                                                          

     Source : <0x00081b50> { _kref_put + 0xc } IF CC JUMP                                                                                                

  12 Target : <0x00081b44> { _kref_put + 0x0 }                                                                                                           

     Source : <0x00081164> { _kobject_put + 0x44 } JUMP.L                                                                                                

  13 Target : <0x00081152> { _kobject_put + 0x32 }                                                                                                       

     Source : <0x00081132> { _kobject_put + 0x12 } IF CC JUMP                                                                                            

  14 Target : <0x00081120> { _kobject_put + 0x0 }                                                                                                        

     Source : <0x00098650> { _put_device + 0xc } JUMP.L                                                                                                  

  15 Target : <0x00098644> { _put_device + 0x0 }                                                                                                         

     Source : <0x0009865a> { _klist_children_put + 0x2 } JUMP.L                                                                                          

BUG: failure at mm/slab.c:591/page_get_cache()!                                                                                                          

Kernel panic - not syncing: BUG!


There is no doubt that this happens after this call:




However, I can't see why this fails. I've traced the involved kernel functions, and I can't see why it panics.


The code involved in adding/removing platform devices are identical to what's in the adp5520.c, but removing the module fails. If no subdevices are added (by deliberately commenting it out), it does not fail.








2009-11-06 07:07:53     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82125   


Talking to myself here, but I found a (kind of) solution. I looked at the trunk version of the adp5520 driver, and noticed that it had been simplified somewhat. It also uses the platform_device_register_data function to add the subdevices (instead of the homegrown function from the previous version), and I tried duplicating that. However, that symbol was not exported in the kernel version I am using, so I added the export myself (in drivers/base/platform.c).


For some reason this does the trick, and no more panics when removing the driver.








2009-11-07 06:40:35     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82178   


The GPIO part of my driver now seems to work, but I am struggling a bit with the UART driver.


The problem is that I don't have any hardware interrupt from the chip (the chip has one, but it is not connected on my hardware). I will therefore have to use polling for the UART. However, I am unsure how to do that in a device driver. I guess it must be possible, but which mechanism would I use? Can a driver/module have a separate thread for such a purpose?


Any pointers will be appreciated.








2009-11-07 08:21:18     Re: sc16is7xx i2c to uart bridge - driver?

Mike Frysinger (UNITED STATES)

Message: 82179   


if you look at drivers/char/bfin_jtag_comm.c, that is a device driver that has no interrupt, so it creates a dedicated thread to poll the hardware periodically.


keep in mind it's a TTY device only, not a UART, so the interactions with the other parts of the kernel layers wont be the same.  but i dont think it matters for the example you need.




2009-11-07 12:27:37     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82183   


Thanks. That was probably just what I needed.


I had looked at the (old) tiny_serial example driver which used the kernel timer api, but I guess a kernel thread is probably better.








2009-11-12 18:10:33     Re: sc16is7xx i2c to uart bridge - driver?

Øyvind Kaurstad (NORWAY)

Message: 82330   


Well, now I have a working (well, at least useable) driver. Up until now I've kept all my files out of tree, and just tested by inserting my modules in the running target for testing.


The process is to make/compile the module(s), then copy them to the target, and inserting/removing as needed. This works beautifully, and I am only compiling my own modules after changing my code. However, many drivers/modules are inside the kernel tree. What is the normal procedure when maintaining such code? Is it better to just have a copy of the relevant sources outside the tree, and when finished fixing the bugs (or adding features) to just copy the files back into the tree again?








2009-11-12 21:04:24     Re: sc16is7xx i2c to uart bridge - driver?

Mike Frysinger (UNITED STATES)

Message: 82334   


either you post things to lkml (and thus get your driver merged), or you keep it out of tree




2010-05-29 03:06:22     Re: sc16is7xx i2c to uart bridge - driver?

Stéphane Cachat (FRANCE)

Message: 89979   




Finally, did you merged your driver in the tree. If not, where can I find it?








S. Cachat




2010-07-01 17:47:54     Re: sc16is7xx i2c to uart bridge - driver?

Mark Klunder (UNITED STATES)

Message: 90812   




Is the work you did available for others?  I would really like to reuse your code if possible.








2011-08-25 14:42:37     Re: sc16is7xx i2c to uart bridge - driver?

James Armstrong (UNITED STATES)

Message: 103122   


I was also wondering if your code / patch for the sc16is7xx is available? It sure would save a lot of time and headache duplicating it again.