I2C linux driver can't find device - linux-kernel

i have written a little i2c driver just for testing purposes.
I am using a Rasperry Pi 3 and I have connected two ssd1306 OLED displays to my I2C pins on the GPIO pin header. I am able to connect to it, using the i2c-tools using the adresses 0x3c and 0x3d.
I am able to send data to the displays using i2c-set:
i2cset -y 1 0x3c [data]
i2cset -y 1 0x3d [data]
The command
i2cdetect -y 1
gives me the following output:
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c 3d -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
I am using the following driver to see what it does and how I2C in Linux may work:
#include "i2c_test.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#define print_client(c) printk("%s: Client (Name: %s), (Flags: %x), (Addr: %x), (Adapter: ), (Device: ), (IRQ: %d)\n",\
__FUNCTION__, c->name, c->flags, c->addr, c->irq)
#define print_board(b) printk("%s: Board (Type: %s), (Flags: %x), (Addr: %x), (IRQ: %d)\n",\
__FUNCTION__, b->type, (unsigned int)b->flags, (unsigned int)b->addr, b->irq)
static struct i2c_device_id ssd1306_idtable[] = {
{ "ssd1306", 0 },
{}
};
const unsigned short ssd1306_address_list[] = {
0x3c,
0x3d,
0x7a,
0x78,
};
MODULE_DEVICE_TABLE(i2c, ssd1306_idtable);
struct dev_pm_ops ssd1306_pm_ops = {
// Don't know how to use
};
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
int ssd1306_probe(struct i2c_client* client, const struct i2c_device_id * dev_id)
{
print_client(client);
return 0;
}
#else //LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0)
int ssd1306_probe_new(struct i2c_client* client)
{
print_client(client);
return 0;
}
#endif // Kernel version
int ssd1306_remove(struct i2c_client* client)
{
print_client(client);
return 0;
}
void ssd1306_shutdown(struct i2c_client* client)
{
print_client(client);
}
int ssd1306_detect(struct i2c_client* client, struct i2c_board_info* board_info)
{
print_client(client);
print_board(board_info);
return 0;
}
static struct i2c_driver ssd1306_driver = {
.driver = {
.name = "i2c_test",
.pm = &ssd1306_pm_ops
},
.id_table = ssd1306_idtable,
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
.probe = ssd1306_probe,
#else //LINUX_VERSION_CODE > KERNEL_VERSION(4,10,0)
.probe_new = ssd1306_probe_new, //needs Kernel 4.10 or later
#endif //Kernel Version
.remove = ssd1306_remove,
.class = I2C_CLASS_HWMON, // correct??
.detect = ssd1306_detect,
.address_list = ssd1306_address_list,
//.command = ssd1306_command,
.shutdown = ssd1306_shutdown
};
static int __init mod_init(void)
{
printk("init " __FILE__ "\n");
i2c_add_driver(&ssd1306_driver);
printk("driver added!\n");
return 0;
}
static void __exit mod_exit(void)
{
printk("remove driver\n");
i2c_del_driver(&ssd1306_driver);
printk("driver removed\n");
printk("exit " __FILE__ "\n");
}
module_init( mod_init );
module_exit( mod_exit );
MODULE_LICENSE("GPL");
I get the following output by dmesg:
[ 1676.649683] init /home/pi/projects/playground/i2c_test/i2c_test.c
[ 1676.649790] driver added!
[ 1812.043182] remove driver
[ 1812.043301] driver removed
[ 1812.043306] exit /home/pi/projects/playground/i2c_test/i2c_test.c
It would be nice if someone know what to do or what I am doing wrong and could help me.
Thanks
p0kR

I2C devices are not probed automatically when module is loaded (I2C does not offer any standard method for this). So, to have your driver's probe function called, you need to tell the kernel which I2C addr should be handled by your driver. Simple method using sysfs is:
# echo [your_device_name] [your_device_i2c_addr] > /sys/bus/i2c/devices/i2c-[i2c_bus_number]/new_device
(replace parts in square brackets by appropriate numbers and names)
In your particular case it would be
# echo ssd1306 0x3c > /sys/bus/i2c/devices/i2c-1/new_device
Note that kernel won't do any checking for you, so your probe function will be called regardless the I2C device is connected or not.
Other methods include, for example, specifying your device and driver in device tree data. For more details refer to linux kernel documentation.
Edit: Actually in some situation autodetection exists (and then detect function is called), but I2C bus has to agree on it (and device class have to match, among others). But this method is not used for general devices as far as I know, rather for internal monitoring sensors in PC etc. Specifying your device explicitly is preferred method in the most situations.

Firstly I2C devices are not dynamically enumerated as like USB devices.
If your driver is built-in obviously your driver will be called. No need to access it via sysfs interface.
If you are using device tree add your i2c device details in .dts and let your driver invoked during bootup.

Related

HLS: How to separate AXI4 signals

I am trying to write a module that uses the AXI4 streaming protocol to communicate with the previous and next modules. The modules use the following communication signals:
TDATA, which is 16 bits,
TKEEP, which is 2 bits,
TUSER, which is 1 bit,
TVALID, which is 1 bit,
TREADY, which is 1 bit and goes towards the previous module, and
TLAST, which is 1 bit.
These all need to be separate signals. I tried to implement it using the following code:
#include "core.h"
void core_module(hls::stream<ap_axis_str> &input_stream, hls::stream<ap_axis_str> &output_stream){
#pragma HLS INTERFACE axis port=input_stream
#pragma HLS INTERFACE axis port=output_stream
#pragma HLS INTERFACE s_axilite port=return bundle=CTRL
ap_axis_str strm_val_in;
ap_axis_str strm_val_out;
for (int i = 0; i<NDATA; i++){
strm_val_in = input_stream.read();
strm_val_out.data = strm_val_in.data * 2;
strm_val_out.keep = 3;
strm_val_out.valid = 1;
strm_val_in.ready = 1;
strm_val_out.user = ((i%2)==0);
strm_val_out.last = (i == NDATA-1) ? 1:0;
output_stream.write(strm_val_out);
}
}
where the header file is
#ifndef core_h
#define core_h
#include <ap_int.h>
#include <ap_axi_sdata.h>
#include <hls_stream.h>
typedef ap_uint<16> word;
#define NDATA 10
struct ap_axis_str {
word data;
ap_uint<2> keep;
bool user;
bool last;
bool ready;
bool valid;
};
void core_module(hls::stream<ap_axis_str> &input_stream, hls::stream<ap_axis_str> &output_stream);
#endif
The problem is that this doesn't separate the signals. When I synthesise it and run it in the co-simulation (giving it values from 0 to 9), even if the result is what I expect it to be, the waveform produced looks like this:
We can see that TREADY, TVALID, and TDATA are there, but not the other 3. Furthermore, looking at the contents of TDATA (which for some reason are 64 bits) we notice that they contain all the signals. They are the following:
0001000001030000,
0001000000030002,
0001000001030004,
0001000000030006,
...
000100000003000c, (they are in base 16)
0001000001030010,
0001000100030012.
From which we can see that the 3 in position 12 is probably what was intended to be TKEEP, the 1 in position 8 which only appears in the last case is probably what was intended to be TUSER, the last 4 digits are what was supposed to be TDATA, etc. Additionally, the program drops TREADY when it isn't ready to receive data, which is what is intended of TREADY, but I didn't program it to work this way, which means that it's automatically generated and probably has nothing to do with the TREADY I told it to have.
So my question is: How do I make a module that gives out the correct 6 separate signals for the version of the AXI4 protocol that we are using?
Well, according to the Xilinx Documentation,
If you specify an hls::stream object with a data type other than ap_axis or ap_axiu, the tool will infer an AXI4-Stream interface without the TLAST signal, or any of the side-channel signals. This implementation of the AXI4-Stream interface consumes fewer device resources, but offers no visibility into when the stream is ending.
Now I had already imported the needed module with#include <ap_axi_sdata.h>, all I needed to do was actually use it by removing
struct ap_axis_str {
word data;
ap_uint<2> keep;
bool user;
bool last;
bool ready;
bool valid;
};
and replacing it with
typedef ap_axiu<16, 1, 0, 0> ap_axis_str;
Additionally, I needed to remove my manual attempt to control TREADY and TVALID, as those are done automatically.

Yocto Patch Linux Kernel In-Tree-Module with extern symbol exported from Out-Of-Tree Module

I am using Yocto to build an SD Card image for my Embedded Linux Project. The Yocto branch is Warrior and the Linux kernel version is 4.19.78-linux4sam-6.2.
I am currently working on a way to read memory from an external QSPI device in the initramfs and stick the contents into a file in procfs. That part works and I echo data into the proc file and read it out successfully later in user space Linux after the board has booted.
Now I need to use the Linux Kernel module EXPORT_SYMBOL() functionality to allow an in-tree kernel module to know about my out-of-tree custom kernel module exported symbol.
In my custom module, I do this:
static unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
And I patched the official kernel build in a bitbake bbappend file with this:
diff -Naur kernel-source/drivers/net/usb/smsc95xx.c kernel-source.new/drivers/net/usb/smsc95xx.c
--- kernel-source/drivers/net/usb/smsc95xx.c 2020-08-04 22:34:02.767157368 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.c 2020-08-04 23:34:27.528435689 +0000
## -917,6 +917,27 ##
{
const u8 *mac_addr;
+ printk("=== smsc95xx_init_mac_address ===\n");
+ printk("%x:%x:%x:%x:%x:%x\n",
+ lan9730_mac_address_buffer[0],
+ lan9730_mac_address_buffer[1],
+ lan9730_mac_address_buffer[2],
+ lan9730_mac_address_buffer[3],
+ lan9730_mac_address_buffer[4],
+ lan9730_mac_address_buffer[5]);
+ printk("=== mac_addr is set ===\n");
+ if (lan9730_mac_address_buffer[0] != 0xff &&
+ lan9730_mac_address_buffer[1] != 0xff &&
+ lan9730_mac_address_buffer[2] != 0xff &&
+ lan9730_mac_address_buffer[3] != 0xff &&
+ lan9730_mac_address_buffer[4] != 0xff &&
+ lan9730_mac_address_buffer[5] != 0xff) {
+ printk("=== SUCCESS ===\n");
+ memcpy(dev->net->dev_addr, lan9730_mac_address_buffer, ETH_ALEN);
+ return;
+ }
+ printk("=== FAILURE ===\n");
+
/* maybe the boot loader passed the MAC address in devicetree */
mac_addr = of_get_mac_address(dev->udev->dev.of_node);
if (!IS_ERR(mac_addr)) {
diff -Naur kernel-source/drivers/net/usb/smsc95xx.h kernel-source.new/drivers/net/usb/smsc95xx.h
--- kernel-source/drivers/net/usb/smsc95xx.h 2020-08-04 22:32:30.824951447 +0000
+++ kernel-source.new/drivers/net/usb/smsc95xx.h 2020-08-04 23:33:50.486778978 +0000
## -361,4 +361,6 ##
#define INT_ENP_TDFO_ ((u32)BIT(12)) /* TX FIFO Overrun */
#define INT_ENP_RXDF_ ((u32)BIT(11)) /* RX Dropped Frame */
+extern unsigned char lan9730_mac_address_buffer[6];
+
#endif /* _SMSC95XX_H */
However, the Problem is that the Kernel fails to build with this error:
| GEN ./Makefile
| Using /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source as source for kernel
| CALL /home/me/Desktop/poky/build-microchip/tmp/work-shared/sama5d27-som1-ek-sd/kernel-source/scripts/checksyscalls.sh
| Building modules, stage 2.
| MODPOST 279 modules
| ERROR: "lan9730_mac_address_buffer" [drivers/net/usb/smsc95xx.ko] undefined!
How can I refer to the Out-Of-Tree kernel module exported symbol in a patched In-Tree kernel module?
initramfs relevant code:
msg "Inserting lan9730-mac-address.ko..."
insmod /mnt/lib/modules/4.19.78-linux4sam-6.2/extra/lan9730-mac-address.ko
ls -rlt /proc/lan9730-mac-address
head -c 6 /dev/mtdblock0 > /proc/lan9730-mac-address
Out-Of-Tree module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
const int BUFFER_SIZE = 6;
int write_length, read_length;
unsigned char lan9730_mac_address_buffer[6];
EXPORT_SYMBOL(lan9730_mac_address_buffer);
int read_proc(struct file *filp, char *buf, size_t count, loff_t *offp)
{
// Read bytes (returning the byte count) until all bytes are read.
// Then return count=0 to signal the end of the operation.
if (count > read_length)
count = read_length;
read_length = read_length - count;
copy_to_user(buf, lan9730_mac_address_buffer, count);
if (count == 0)
read_length = write_length;
return count;
}
int write_proc(struct file *filp, const char *buf, size_t count, loff_t *offp)
{
if (count > BUFFER_SIZE)
count = BUFFER_SIZE;
copy_from_user(lan9730_mac_address_buffer, buf, count);
write_length = count;
read_length = count;
return count;
}
struct file_operations proc_fops = {
read: read_proc,
write: write_proc
};
void create_new_proc_entry(void) //use of void for no arguments is compulsory now
{
proc_create("lan9730-mac-address", 0, NULL, &proc_fops);
}
int proc_init (void) {
create_new_proc_entry();
memset(lan9730_mac_address_buffer, 0x00, sizeof(lan9730_mac_address_buffer));
return 0;
}
void proc_cleanup(void) {
remove_proc_entry("lan9730-mac-address", NULL);
}
MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);
There are several ways to achieve what you want (taking into account different aspects, like module can be compiled in or be a module).
Convert Out-Of-Tree module to be In-Tree one (in your custom kernel build). This will require simple export and import as you basically done and nothing special is required, just maybe providing a header with the symbol and depmod -a run after module installation. Note, you have to use modprobe in-tree which reads and satisfies dependencies.
Turn other way around, i.e. export symbol from in-tree module and file it in the out-of-tree. In this case you simply have to check if it has been filed or not (since it's a MAC address the check against all 0's will work, no additional flags needed)
BUT, these ways are simply wrong. The driver and even your patch clearly show that it supports OF (Device Tree) and your board has support of it. So, this is a first part of the solution, you may provide correct MAC to the network card using Device Tree.
In the case you want to change it runtime the procfs approach is very strange to begin with. Network device interface in Linux has all means to update MAC from user space at any time user wants to do it. Just use ip command, like /sbin/ip link set <$ETH> addr <$MACADDR>, where <$ETH> is a network interface, for example, eth0 and <$MACADDR> is a desired address to set.
So, if this question rather about module symbols, you need to find better example for it because it's really depends to use case. You may consider to read How to export symbol from Linux kernel module in this case? as an alternative way to exporting. Another possibility how to do it right is to use software nodes (it's a new concept in recent Linux kernel).

setting an i2c register to high

I have this project that my boss asked me to do and the first step is to figure out how to set a given I2C register to high or low using the silicon lab library, if anyone knows any good sources for this type of problem please provide them thank you. The pic that I am using is the pic16f1823, I've already looked at the documentation of the pic but into only states how to read and write to an I2c.
I use this as a header file and seems to work well for PIC16F1827 which is basically the same as the 1823. It used the peripheral of the PIC. Just include in in any c file you want to use i2c in. Make sure you #define FOSC in order to calculate the correct baud rate. Also double check the port and tris assignments are correct for your device and make adjustments.
It uses polling instead of an interrupt. Uncomment the interrupt setup code and write an interrupt service routine to catch the interrupts.
#ifndef I2C_H
#define I2C_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* Hi-Tech C I2C library for 12F1822
* Master mode routines for I2C MSSP port to read and write to slave device
* Copyright (C)2011 HobbyTronics.co.uk 2011
* Freely distributable.
*/
#define I2C_WRITE 0
#define I2C_READ 1
// Initialise MSSP port. (12F1822 - other devices may differ)
void i2c_Init(void){
// Initialise I2C MSSP
// Master 100KHz
TRISB2 = 1;
TRISB5 = 1;
SSP1CON1 = 0b00101000; // I2C Master mode
SSP1CON2 = 0b00000000;
SSP1CON3 = 0b00000000;
//SSP1MSK = 0b00000000;
SSP1ADD = I2C_BRG; // clock = FOSC/(4 * (SSPxADD+1))
//SSP1IE = 1; // enable interrupt
SSP1STAT = 0b10000000;
}
// i2c_Wait - wait for I2C transfer to finish
void i2c_Wait(void){
while ( ( SSP1CON2 & 0x1F ) || ( SSPSTAT & 0x04 ) );
}
// i2c_Start - Start I2C communication
void i2c_Start(void)
{
i2c_Wait();
SSP1CON2bits.SEN=1;
}
// i2c_Restart - Re-Start I2C communication
void i2c_Restart(void){
i2c_Wait();
SSP1CON2bits.RSEN=1;
}
// i2c_Stop - Stop I2C communication
void i2c_Stop(void)
{
i2c_Wait();
SSP1CON2bits.PEN=1;
}
// i2c_Write - Sends one byte of data
void i2c_Write(unsigned char data)
{
i2c_Wait();
SSPBUF = data;
}
// i2c_Address - Sends Slave Address and Read/Write mode
// mode is either I2C_WRITE or I2C_READ
void i2c_Address(unsigned char address, unsigned char mode)
{
unsigned char l_address;
l_address=address<<1;
l_address+=mode;
i2c_Wait();
SSPBUF = l_address;
}
// i2c_Read - Reads a byte from Slave device
unsigned char i2c_Read(unsigned char ack)
{
// Read data from slave
// ack should be 1 if there is going to be more data read
// ack should be 0 if this is the last byte of data read
unsigned char i2cReadData;
i2c_Wait();
SSP1CON2bits.RCEN=1;
i2c_Wait();
i2cReadData = SSPBUF;
i2c_Wait();
if ( ack ) SSP1CON2bits.ACKDT=0; // Ack
else SSP1CON2bits.ACKDT=1; // NAck
SSP1CON2bits.ACKEN=1; // send acknowledge sequence
return( i2cReadData );
}
#ifdef __cplusplus
}
#endif
#endif /* I2C_H */
Then you can use the higher level functions defined above to control a device, which is described in the datasheet of the slave device.
For example, to read from an eeprom:
#include <xc.h>
#define FOSC 16000000
#include "i2c.h"
unsigned char i2c_read_eeprom( unsigned char slaveaddress, unsigned char memaddress )
{
unsigned char data;
data = 123;
i2c_Start();
i2c_Address( slaveaddress, I2C_WRITE);
i2c_Write(memaddress);
if( SSP1CON2bits.ACKSTAT )
txstring("ACK!\r\n");
else txstring("nACK!\r\n");
i2c_Start();
i2c_Address( slaveaddress, I2C_READ);
data = i2c_Read(0);
i2c_Stop();
return data;
}

Two 16F873 I2C Communication Addressing

I'm trying to communicate between 2 16F873 by using I2C. I wired ;
SDA > SDA ( Pull-up with 3.3k resistors )
SCL > SCL ( Pull-up with 3.3k resistors )
GND > GND ( Common)
Used 4Mhz crystal on both with x2 22pF cap to OSC1 and OSC1 pins.
5V with 10k resistor to MCLR* pin.
Here are the Master and Slave codes which sends "0xFF" data to set all B Pins High. I connected a led with 220ohm resistor to any of these b pins but cant see that the led lights.
So assume i have a problem with addressing of the slave. As i read from the datasheet, address of slave is 7-bit and the last bit (bit 0) indicates data goes from master to slave ( for lsb 0 ). That's why master say 0x20 and slave address is 0x10. (0 is added as LSB).
Do i have to add additional register bits to set anything else? What is the problem here.
I've compiled all codes with PIC C Compiler (5.007)
Master Code
#include <main.h>
void main()
{
i2c_start();
i2c_write(0x20);//slave address
i2c_write(0xFF);//data
i2c_stop();
while(TRUE)
{
//TODO: User Code
}
}
Main.h (for master)
#include <16F873.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(crystal=4MHz)
#use i2c(Master,Slow,sda=PIN_C4,scl=PIN_C3)
Slave Code
#include <main.h>
#use i2c(Slave,Slow,sda=PIN_C4,scl=PIN_C3,address=0x1d)
#INT_SSP //Interrupt for I2C activity
int8 data;
void sspinterupt()
{
int8 state;
state = i2c_isr_state();
if(state < 0x80) //Master is sending data
{
data = i2c_read(); //An array will be needed to store data if more than one byte is transferred
}
if(state == 0x80) //Master is requesting data
{
i2c_write(data);
}
}
void main()
{
set_tris_b(0x00);
enable_interrupts(INT_SSP);
enable_interrupts(GLOBAL);
while(TRUE)
{
output_b(data); //Output data to port B to visualize
}
}
Main.h (Slave)
#include <16F873.h>
#device ADC=16
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(crystal=4MHz)
Proteus Design
Proteus Design

How to access GPIO from kernel space? (zynq-microzed board)

I am using zynq-microzed board and I want to access GPIO with kernel space.
Can anyone please tell me how can i attempt doing this?
*NOTE: This is from the Zynq-7000. I believe it's largely the same.
Assuming you're using a devicetree, this is an example entry (in the devicetree):
gpio-device {
compatible = "gpio-control";
gpios = <&gpio0 54 0>; //(Add 32 to get the actual pin number. This is GPIO 86)
};
And you need to state in the driver that you're compatible with the devicetree entry (look at other drivers to see where to put this line):
.compatible = "gpio-control"
In your driver, include #include <linux/gpio.h> and read the pin from the devicetree:
struct device_node *np = pdev->dev.of_node;
int pin;
pin = of_get_gpio(np, 0);
if (pin < 0) {
pr_err("failed to get GPIO from device tree\n");
return -1;
}
Request the use of the GPIO:
int ret = gpio_request(pin, "Some name"); //Name it whatever you want
And set it's direction:
int ret = gpio_direction_output(pin, 0); //The second parameter is the initial value. 0 is low, 1 is high.
Afterwards, set the value like so:
gpio_set_value(pin, 1);
For input:
ret = gpio_direction_input(pin);
value = gpio_get_value(pin);
Free the GPIO when you're finished with it (including on error!):
gpio_free(pin);
At the end of the day, a good method is to grep around the kernel to find drivers that do what you want. In fact grep -r gpio <<kernel_source>> will tell you everything in this answer and more.
Check the following link: enter link description here
Summarizing:
There is an include file for working with GPIOs:
#include <linux/gpio.h>
GPIOs must be allocated before use:
int gpio_request(unsigned int gpio, const char *label);
And GPIO can be returned to the system with:
void gpio_free(unsigned int gpio);
Configure GPIO as Input/Output:
int gpio_direction_input(unsigned int gpio);
int gpio_direction_output(unsigned int gpio, int value);
Operations:
int gpio_get_value(unsigned int gpio);
void gpio_set_value(unsigned int gpio, int value);
Regards.

Resources