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.

AM335x custom I2C EEPROM driver

Hi

Team I am new to driver development!

I am currently working on writing a eeprom based client driver for i2c

I need help on the following

I have taken the reference code of eeprom which is already there and I have attached this for the reference

As a new bie I am familiar with the framework of writing the client driver for api such as i2c_add_driver and how probe works! 

I have taken the code from driver folder and trying to alter this to make it work for my board

I have just written the skeleton I read the data sheet but not able to find the eeprom address and the settings that hastto be done and what address the data has to be written and read from

#include <linux/kernel.h>
 20 #include <linux/module.h>
 21 #include <linux/device.h>
 22 #include <linux/jiffies.h>
 23 #include <linux/i2c.h>
 24 #include <linux/mutex.h>
 25 
 26 /* Addresses to scan */
 27 static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
 28                                         0x55, 0x56, 0x57, I2C_CLIENT_END };
 29 
 30 
 31 /* Size of EEPROM in bytes */
 32 #define EEPROM_SIZE             256
 33 
 34 /* possible types of eeprom devices */
 35 enum eeprom_nature {
 36         UNKNOWN,
 37         VAIO,
 38 };
 39 
 40 /* Each client has this additional data */
 41 struct eeprom_data {
 42         struct mutex update_lock;
 43         u8 valid;                       /* bitfield, bit!=0 if slice is valid */
 44         unsigned long last_updated[8];  /* In jiffies, 8 slices */
 45         u8 data[EEPROM_SIZE];           /* Register values */
 46         enum eeprom_nature nature;
 47 };
 48 
 49 
 50 static void eeprom_update_client(struct i2c_client *client, u8 slice)
 51 {
 52         struct eeprom_data *data = i2c_get_clientdata(client);
 53         int i;
 54 
 55         mutex_lock(&data->update_lock);
 56 
 57         if (!(data->valid & (1 << slice)) ||
 58             time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
 59                 dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
 60 
 61                 if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
 62                         for (i = slice << 5; i < (slice + 1) << 5; i += 32)
 63                                 if (i2c_smbus_read_i2c_block_data(client, i,
 64                                                         32, data->data + i)
 65                                                         != 32)
 66                                         goto exit;
 67                 } else {
 68                         for (i = slice << 5; i < (slice + 1) << 5; i += 2) {
 69                                 int word = i2c_smbus_read_word_data(client, i);
 70                                 if (word < 0)
 71                                         goto exit;
 72                                 data->data[i] = word & 0xff;
 73                                 data->data[i + 1] = word >> 8;
 74                         }
 75                 }
 76                 data->last_updated[slice] = jiffies;
 77                 data->valid |= (1 << slice);
 78         }
 79 exit:
 80         mutex_unlock(&data->update_lock);
 81 }
 82 
 83 static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
 84                            struct bin_attribute *bin_attr,
 85                            char *buf, loff_t off, size_t count)
 86 {
 87         struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
 88         struct eeprom_data *data = i2c_get_clientdata(client);
 89         u8 slice;
 90 
 91         /* Only refresh slices which contain requested bytes */
 92         for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
 93                 eeprom_update_client(client, slice);
 94 
 95         /* Hide Vaio private settings to regular users:
 96            - BIOS passwords: bytes 0x00 to 0x0f
 97            - UUID: bytes 0x10 to 0x1f
 98            - Serial number: 0xc0 to 0xdf */
 99         if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) {
100                 int i;
101 
102                 for (i = 0; i < count; i++) {
103                         if ((off + i <= 0x1f) ||
104                             (off + i >= 0xc0 && off + i <= 0xdf))
105                                 buf[i] = 0;
106                         else
107                                 buf[i] = data->data[off + i];
108                 }
109         } else {
110                 memcpy(buf, &data->data[off], count);
111         }
112 
113         return count;
114 }
115 
116 static struct bin_attribute eeprom_attr = {
117         .attr = {
118                 .name = "eeprom",
119                 .mode = S_IRUGO,
120         },
121         .size = EEPROM_SIZE,
122         .read = eeprom_read,
123 };
124 
125 /* Return 0 if detection is successful, -ENODEV otherwise */
126 static int eeprom_detect(struct i2c_client *client, struct i2c_board_info *info)
127 {
128         struct i2c_adapter *adapter = client->adapter;
129 
130         /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all
131            addresses 0x50-0x57, but we only care about 0x50. So decline
132            attaching to addresses >= 0x51 on DDC buses */
133         if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51)
134                 return -ENODEV;
135 
136         /* There are four ways we can read the EEPROM data:
137            (1) I2C block reads (faster, but unsupported by most adapters)
138            (2) Word reads (128% overhead)
139            (3) Consecutive byte reads (88% overhead, unsafe)
140            (4) Regular byte data reads (265% overhead)
141            The third and fourth methods are not implemented by this driver
142            because all known adapters support one of the first two. */
143         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
144          && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
145                 return -ENODEV;
146 
147         strlcpy(info->type, "eeprom", I2C_NAME_SIZE);
148 
149         return 0;
150 }
151 
152 static int eeprom_probe(struct i2c_client *client,
153                         const struct i2c_device_id *id)
154 {
155         struct i2c_adapter *adapter = client->adapter;
156         struct eeprom_data *data;
157 
158         data = devm_kzalloc(&client->dev, sizeof(struct eeprom_data),
159                             GFP_KERNEL);
160         if (!data)
161                 return -ENOMEM;
162 
163         memset(data->data, 0xff, EEPROM_SIZE);
164         i2c_set_clientdata(client, data);
165         mutex_init(&data->update_lock);
166         data->nature = UNKNOWN;
167 
168         /* Detect the Vaio nature of EEPROMs.
169            We use the "PCG-" or "VGN-" prefix as the signature. */
170         if (client->addr == 0x57
171          && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
172                 char name[4];
173 
174                 name[0] = i2c_smbus_read_byte_data(client, 0x80);
175                 name[1] = i2c_smbus_read_byte_data(client, 0x81);
176                 name[2] = i2c_smbus_read_byte_data(client, 0x82);
177                 name[3] = i2c_smbus_read_byte_data(client, 0x83);
178 
179                 if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) {
180                         dev_info(&client->dev, "Vaio EEPROM detected, "
181                                  "enabling privacy protection\n");
182                         data->nature = VAIO;
183                 }
184         }
185 
186         /* create the sysfs eeprom file */
187         return sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr);
188 }
189 
190 static int eeprom_remove(struct i2c_client *client)
191 {
192         sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
193 
194         return 0;
195 }
196 
197 static const struct i2c_device_id eeprom_id[] = {
198         { "eeprom", 0 },
199         { }
200 };
201 
202 static struct i2c_driver eeprom_driver = {
203         .driver = {
204                 .name   = "eeprom",
205         },
206         .probe          = eeprom_probe,
207         .remove         = eeprom_remove,
208         .id_table       = eeprom_id,
209 
210         .class          = I2C_CLASS_DDC | I2C_CLASS_SPD,
211         .detect         = eeprom_detect,
212         .address_list   = normal_i2c,
213 };
214 
215 module_i2c_driver(eeprom_driver);
216 
217 MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
218                 "Philip Edelbrock <phil@netroedge.com> and "
219                 "Greg Kroah-Hartman <greg@kroah.com>");
220 MODULE_DESCRIPTION("I2C EEPROM driver");
221 MODULE_LICENSE("GPL");

As I have attached this iam trying to edit this for my beagle bone not sure where to refer for eeprom details weather is it present on board! and what is the address

And I have gone through the code where they have given   .attr = {
118                 .name = "eeprom",
119                 .mode = S_IRUGO,
120         },
121         .size = EEPROM_SIZE,
122         .read = eeprom_read,


we have only read attribute that is set for reading and who to implement write to eeprom ! any reference would help??????

How to check the address of eeprom for setting??

if I want this eeprom to work from uaser space with a standard applications there is no internal IOCtl handled here and read write interfaces for application is not present! how to implement this ???

what should I refer for eeprom details like base address?

got stuck I only know frame work and protocol that I have written without Linux for other boards but where to chek these for beagle bone

kindly help out!

Thank you

Deepak R

 

  • The software team have been notified. They will respond here.
  • Hi,

    Which kernel version is this?
    For latest TISDK 03.01 (kernel 4.4.19) you need to add I2C child node, which corresponds to the EEPROM. You can take a reference from what is done for the BeagleBone board -> arch/arm/boot/dts/am335x-bone-common.dtsi:
    &i2c0 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c0_pins>;

    status = "okay";
    clock-frequency = <400000>;

    tps: tps@24 {
    reg = <0x24>;
    };

    baseboard_eeprom: baseboard_eeprom@50 {
    compatible = "at,24c256";
    reg = <0x50>;

    #address-cells = <1>;
    #size-cells = <1>;
    baseboard_data: baseboard_data@0 {
    reg = <0 0x100>;
    };
    };
    };

    You can see that the eeprom is located on i2c0 address 0x50. The driver related to the eeprom chip is: at,24c256 (or drivers/misc/eeprom/at24.c).

    Best Regards,
    Yordan
  • Thanks yordan for the quick solution I would experiment this!
    I am analysing the code ! let me know any reference application to test this how to enable this module in kernel as we are using yacto project ./build_script is building this!
    Help needed for knowing how to test this test driver on board! from application and command line

    Thank you
    Deepak R
  • Hi,

    Help needed for knowing how to test this test driver on board! from application and command line


    From the command line you can use the i2c-tools:
    www.lm-sensors.org/.../I2CTools
    elinux.org/Interfacing_with_I2C_Devices

    For using the i2c interface from applications, you can use the /dev/i2cx.

    Best Regards,
    Yordan
  • Hi
    Yordan
    Thanks for your reference i have few doubts in the code
    I have gone through the DOCUMENTATION OF LINUX FOR I2C but even that i am not confident about the explanation he gives i am trying to add the following api that i am not clear about
    Kind;ly let me know what links to be used to understand this.
    I have now compiled the code using make file anf generated the ko i am not sure what is the configuration that i have to do in menuconfig for building or adding support for the eeprom drivers to work for

    1)
    .acpi_match_table = ACPI_PTR(at24_acpi_ids), ??NOT SURE I CHECKED IT SAYS FOR ADVANCE CONFIGURATIONAND POWER MANAGEMENT

    2)
    acpi_match_device(at24_acpi_ids, &client->dev);

    3)
    static const struct acpi_device_id at24_acpi_ids[] = {
    { "INT3499", AT24_DEVICE_MAGIC(8192 / 8, 0) },
    { }
    };
    MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
    should i add an entry in this table as my eeprom is
    { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, ??????/

    KIndly let me know what does this apci is used i have checked the documentation folder but its not so clear in understanding
    Other help needed!

    Shoul i eanble something in my menuconfig wehn i do ./build_kernel.sh for i2c

    Thank you
    Deepak R
  • HI
    Team
    I could find some link that explains the acpi module ! hope this will provide clear understanding oid acpi use!
    lwn.net/.../

    Thank you
    Deepak R