рд▓рд┐рдирдХреНрд╕ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рд▓рд┐рдЦрдирд╛: I2C

рд╣реЗрдмреНрд░, рдирдорд╕реНрдХрд╛рд░!

рдпрд╣ рд▓реЗрдЦ I2C (рдЗрдВрдЯрд░-рдЗрдВрдЯреАрдЧреНрд░реЗрдЯреЗрдб рд╕рд░реНрдХрд┐рдЯ) рд▓рд┐рдирдХреНрд╕ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд╡рд┐рдХрд╛рд╕ рдкрд░ рдХреЗрдВрджреНрд░рд┐рдд рд╣реИред рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд I2C рдбреНрд░рд╛рдЗрд╡рд░ рдХреА рдореВрд▓ рд╕рдВрд░рдЪрдирд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдЖрдк рдЖрд╡рд╢реНрдпрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдЖрд╕рд╛рдиреА рд╕реЗ рдЬреЛрдбрд╝ рд╕рдХрддреЗ рд╣реИрдВред

рд╣рдо рдЗрдирдкреБрдЯ рдбреЗрдЯрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ: FPGA рдХреЗ рд▓рд┐рдП рдирдП рдкреНрд░реЛрд╕реЗрд╕рд░ "рд╡рд╛рдпрд░реНрдб" рдХреЗ рд▓рд┐рдП I2C рдмреНрд▓реЙрдХ, рд▓рд┐рдирдХреНрд╕ рд╕рдВрд╕реНрдХрд░рдг 3.18.19 рдФрд░ рдмрд╛рд╣реНрдп рдЙрдкрдХрд░рдгреЛрдВ рдХреЛ рдЪрд▓рд╛рдиреЗ рд╡рд╛рд▓рд╛ (EEPROM AT24C64 рдФрд░ BME280)ред

I2C рдХреЗ рд╕рдВрдЪрд╛рд▓рди рдХрд╛ рд╕рд┐рджреНрдзрд╛рдВрдд рдХрд╛рдлреА рд╕рд░рд▓ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЖрдкрдХреЛ рдЬреНрдЮрд╛рди рдкрд░ рдмреНрд░рд╢ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рддреЛ рдЖрдк рдпрд╣рд╛рдВ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред


рдЪрд┐рддреНрд░рд╛ 1. I2C рдмрд╕ рд╕рдВрдХреЗрддреЛрдВ рдХрд╛ рд╕рдордп рдЖрд░реЗрдЦ

рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╢реБрд░реВ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реНрдкреЗрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЗрд╕рдХреЗ рд▓рд┐рдП рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд╕рд╛рде рдХреИрд╕реЗ рдЗрдВрдЯрд░реИрдХреНрдЯ рдХрд░рддреЗ рд╣реИрдВ:

  1. рд╣рдо рдПрдХ рдЫреЛрдЯреЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕реНрдкреЗрд╕ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХрд╛ рдЙрджреНрджреЗрд╢реНрдп рдбрд┐рд╡рд╛рдЗрд╕ рдХреА рдЕрджреНрд╡рд┐рддреАрдп I2C рд░рдЬрд┐рд╕реНрдЯрд░ рдЖрдИрдбреА рдХреЛ рдкрдврд╝рдирд╛ рд╣реИред рдпрд╣ рдЪрд░рдг рдЖрдкрдХреЛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рд╕рдордЭрдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдЧрд╛ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдФрд░ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рдмреАрдЪ рд╡рд┐рдирд┐рдордп рд╣реЛрддрд╛ рд╣реИ;
  2. рдЖрдЗрдП рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рджреНрд╡рд╛рд░рд╛ I2C рд╕рдВрджреЗрд╢реЛрдВ рдХреЛ рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд╡рд┐рдХрд▓реНрдк рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛрдВ;
  3. рдЕрд╕реЗрдВрдмрд▓реА рдореЗрдВ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдбрд┐рд╡рд╛рдЗрд╕ рдЯреНрд░реА рдореЗрдВ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рд╣рд╛рд░реНрдбрд╡реЗрдпрд░ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ;
  4. рд╣рдо рдХреБрдЫ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рдХреЗ рд╕рд╛рде I2C рдЪрд╛рд▓рдХ рдХреА рд╕рд╛рдорд╛рдиреНрдп рд╕рдВрд░рдЪрдирд╛ (рдХрдВрдХрд╛рд▓) рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВред

рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рд╡рд┐рдХрд╕рд┐рдд рдбреНрд░рд╛рдЗрд╡рд░ рдХреЗ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕реНрд░реЛрддреЛрдВ рдХреЛ рд╕рдВрд▓рдЧреНрди рдХрд░рдирд╛ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореИрдВ рдпрд╣ рдиреЛрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдХрд┐ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд╕рднреА рдирд╛рдо, рдирд╛рдо рдФрд░ рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд╛рд░реНрдб рдмрджрд▓ рджрд┐рдП рдЧрдП рд╣реИрдВред рд╡рд┐рдХрд╕рд┐рдд рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рдЖрдзрд╛ рднреА рдбреНрд░рд╛рдЗрд╡рд░ рдХреЗ рдХрдВрдХрд╛рд▓ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рдерд╛, рд╣рд╛рд▓рд╛рдВрдХрд┐, рдбреНрд░рд╛рдЗрд╡рд░ рд╕рдВрд░рдЪрдирд╛ рд╡рд┐рдХрд╛рд╕ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдЪреНрдЫрд╛ рдкреНрд░рд╛рд░рдВрднрд┐рдХ рдмрд┐рдВрджреБ рд╣реИред I2C рдбреНрд░рд╛рдЗрд╡рд░реЛрдВ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдпрд╣рд╛рдВ рджреЗрдЦреЗ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ ред

рдкрд╣рд▓рд╛ рдХрджрдо


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП i2cdetect рдЙрдкрдпреЛрдЧрд┐рддрд╛ рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛрдВред I2cdetect рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╣реИ:
./i2cdetect -y 0 0 1 2 3 4 5 6 7 8 9 abcdef 00: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 10: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 20: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 30: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 40: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 50: 50 тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 60: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 70: тАФ тАФ тАФ тАФ тАФ тАФ тАФ тАФ 

рдЙрдкрдпреЛрдЧрд┐рддрд╛ рдХреНрд░рдорд┐рдХ рд░реВрдк рд╕реЗ I2C рдкрд░ рдбрд┐рд╡рд╛рдЗрд╕ рдкрддрд╛ рдмрд╕ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рддреА рд╣реИ рдФрд░, рд╕рдХрд╛рд░рд╛рддреНрдордХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдиреЗ рдкрд░ (рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕рдХрд╛рд░рд╛рддреНрдордХ рдЬрд╡рд╛рдм рдПрд╕реАрдХреЗ рд╣реИ), рдХрдВрд╕реЛрд▓ рдореЗрдВ рдмрд╕ рдкрд░ рдбрд┐рд╡рд╛рдЗрд╕ рдкрддрд╛ рдирдВрдмрд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИред

рд╣рдо рдПрдХ рдЫреЛрдЯрд╛ рдкреНрд░реЛрдЧреНрд░рд╛рдо рд▓рд┐рдЦреЗрдВрдЧреЗ рдЬреЛ рддрд╛рдкрдорд╛рди рд╕реЗрдВрд╕рд░ рдХреА рдЕрдиреВрдареА рдЖрдИрдбреА рдХреЛ рдкрдврд╝рддрд╛ рд╣реИ рдФрд░ рдХрдВрд╕реЛрд▓ рдореЗрдВ рдЗрд╕рдХреЗ рдХрд╛рдо рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдмрд╣реБрдд рд╕рд░рд▓ рд▓рдЧрддрд╛ рд╣реИ:

 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #define I2C_ADAPTER "/dev/i2c-0" int read_buffer(int fd) { struct i2c_rdwr_ioctl_data data; struct i2c_msg messages[2]; unsigned char write_buf[1] = {0xD0}, read_buf[1] = {0x00}; unsigned char write[200]; /* * .addr -   () * .flags -     (0 - w, 1 - r) * .len - - /  * .buf -      */ messages[0].addr = 0x50; messages[0].flags = 0; messages[0].len = 1; messages[0].buf = write_buf; messages[1].addr = 0x50; messages[1].flags = 1; messages[1].len = 1; messages[1].buf = read_buf; data.msgs = messages; data.nmsgs = 2; if (ioctl(fd, I2C_RDWR, &data) < 0) printf("Cant send data!\n"); else printf("ID = 0x%x\n", read_buf[0]); } int main(int argc, char **argv) { int fd; /* * Open I2C file descriptor. */ fd = open(I2C_ADAPTER, O_RDWR); if (fd < 0) { printf("Unable to open i2c file\n"); return 0; } read_buffer(fd); return 0; } 

рдпрд╣ рд╕реНрдкрд╖реНрдЯ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ i2c_rdwr_ioctl_data рд╕рдВрджреЗрд╢ рдлрд╝реАрд▓реНрдб рдХреЗ рд░реВрдк рдореЗрдВ рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред рд╕рдВрд░рдЪрдирд╛ рдореЗрдВ i2c_msg рдФрд░ nmsgs рдЬреИрд╕реЗ рдлрд╝реАрд▓реНрдб рд╢рд╛рдорд┐рд▓ рд╣реИрдВ, рдЬрд┐рдирдХрд╛ рдЙрдкрдпреЛрдЧ рдЯреНрд░рд╛рдВрд╕рдорд┐рд╢рди рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ:

  • .addr - рдЙрдкрдХрд░рдг рдкрддреЗ;
  • .flags - рдСрдкрд░реЗрд╢рди рдХрд╛ рдкреНрд░рдХрд╛рд░ (рдкрдврд╝реЗрдВ рдпрд╛ рд▓рд┐рдЦреЗрдВ);
  • .len - рд╡рд░реНрддрдорд╛рди рд╕рдВрджреЗрд╢ рдХреА рд▓рдВрдмрд╛рдИ;
  • .buf- рдХреНрд▓рд┐рдкрдмреЛрд░реНрдбред

рджреВрд╕рд░рд╛ рдХрджрдо


рдЕрдм, рдореИрдВ рдЗрдирд╕рд╛рдЗрдЯреНрд╕ рдореЗрдВ рддрд▓реНрд▓реАрди рдирд╣реАрдВ рдХрд░рддрд╛, I2C рдбреНрд░рд╛рдЗрд╡рд░ рдХреЗ рдПрдХ рд╕рдВрд╕реНрдХрд░рдг рд╕реЗ рдкрд░рд┐рдЪрд┐рдд рд╣реЛ рдЧрдпрд╛ред
рдЬреИрд╕рд╛ рдХрд┐ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд╕реНрдерд╛рдкрд┐рдд рд╣реИ, рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдПрдХ рд╕рдВрд░рдЪрдирд╛ рдХреЗ рд░реВрдк рдореЗрдВ рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд▓реЗрдЦрди рдСрдкрд░реЗрд╢рди (рд╣рд╛рд░реНрдбрд╡реЗрдпрд░-рдирд┐рд░реНрднрд░ рднрд╛рдЧ) рдХрд░рддреЗ рд╕рдордп рдбреНрд░рд╛рдЗрд╡рд░ рдХреЗ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ:

  • рдкрд╣рд▓реЗ TX FIFO рднрд░рд╛ рдЬрд╛рддрд╛ рд╣реИ: рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рдкрддрд╛ рдкрд╣рд▓реЗ рдЖрддрд╛ рд╣реИ, рдФрд░ рдлрд┐рд░ рд╢реЗрд╖ рдбреЗрдЯрд╛ рдкреНрд░рд╕рд╛рд░рд┐рдд рд╣реЛрддрд╛ рд╣реИ;
  • ISR рд╡реНрдпрд╡рдзрд╛рди рд╕реНрдерд┐рддрд┐ рд░рдЬрд┐рд╕реНрдЯрд░ рд╕рд╛рдлрд╝ рд╣реЛ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ IER рд░рдЬрд┐рд╕реНрдЯрд░ рдореЗрдВ рд╡реНрдпрд╡рдзрд╛рди рд╕рдХреНрд╖рдо рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВ (рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЬрдм TX FIFO рдореЗрдВ рдХреЛрдИ рдбреЗрдЯрд╛ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ рддреЛ рдПрдХ рд╡реНрдпрд╡рдзрд╛рди рдЙрддреНрдкрдиреНрди рд╣реЛрддрд╛ рд╣реИ);
  • рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдорд┐рд╢рди рдХреА рдЕрдиреБрдорддрд┐ рд╣реИ рдФрд░ рд╕реНрдЯрд╛рд░реНрдЯ рдмрд┐рдЯ рдмрд╕ рдкрд░ рд╕реЗрдЯ рд╣реИред

рдмрд╛рдж рдХреЗ рд╕рднреА рдбреЗрдЯрд╛ рд╡рд┐рдирд┐рдордп рдмрд╛рдзрд╛ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рд╣реЛрдВрдЧреЗред
рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рджрдо рдкрд░ рдХрд╛рдо рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдбреНрд░рд╛рдЗрд╡рд░ рдпрд╣рд╛рдВ рджреЗрдЦреЗ рдЬрд╛ рд╕рдХрддреЗ рд╣реИрдВ ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдирд┐рдпрдВрддреНрд░рдХ рдореЗрдВ FIFO рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдХреЗрд╡рд▓ рдПрдХ рд╣реА рд╕реНрдерд╛рдирд╛рдВрддрд░рдг рд░рдЬрд┐рд╕реНрдЯрд░ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдПрдХ FIFO рдЖрдХрд╛рд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд┐рд╢реЗрд╖ рдорд╛рдорд▓рд╛ рд╣реИред

рдЪрд░рдг рддреАрди


рдЕрд╕реЗрдВрдмрд▓реА рдореЗрдВ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдбрд┐рд╡рд╛рдЗрд╕ рдЯреНрд░реА рдореЗрдВ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рд╣рд╛рд░реНрдбрд╡реЗрдпрд░ рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ:

1. рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдореЗрдВ рдПрдХ рд╕реНрд░реЛрдд рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдПрдБ:

 cd drivers/i2c/busses/ vim i2c-skel.c :wq 

рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдк, рдлрд╝рд╛рдЗрд▓ рджрд┐рдЦрд╛рдИ рджреЗрддреА рд╣реИ:

 drivers/i2c/busses/i2c-skel.c 


2. рдбреНрд░рд╛рдЗрд╡рд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЛ рдбреНрд░рд╛рдЗрд╡рд░реЛрдВ / i2c / busses / Kconfig рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ :

 config I2C_SKEL tristate "I2C adapter" help If you say yes to this option, support will be included for the I2C interface. 

3. рдЕрд╕реЗрдВрдмрд▓реА рдореЗрдВ рдбреНрд░рд╛рдЗрд╡рд░ / i2c / busses / Makefile рдбреНрд░рд╛рдЗрд╡рд░ рдЬреЛрдбрд╝реЗрдВ:

 obj-$(CONFIG_I2C_SKEL) += i2c-skel.o 

4. I2C рдмреНрд▓реЙрдХ рдХрд╛ рд╡рд┐рд╡рд░рдг рдбрд┐рд╡рд┐рд╕реЗрдЯреНрд░реА (* .dts) рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ, рдФрд░ рддреБрд░рдВрдд eeprom рдбрд┐рд╡рд╛рдЗрд╕ рдХрд╛ рднреА рд╕рдорд░реНрдерди рдХрд░реЗрдВ:

  i2c: i2c@f8f01d00 { compatible = "skel,skel-i2c"; #address-cells = <1>; #size-cells = <0>; reg = <0x43c00000 0x100>; interrupt-parent = <&ps7_scugic_0>; interrupts = <0 29 4>; clock-names = "skel-i2c"; clocks = <&clkc 38>; clock-frequency = <100000>; 24c64@50 { compatible = "at,24c64"; pagesize = <32>; reg = <0x50>; }; } ; 

рдЙрдкрд░реЛрдХреНрдд рдЪрд░рдгреЛрдВ рдХреЛ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рдирд╣реАрдВ рдорд╛рдирд╛ рдЬрд╛рдПрдЧрд╛, рд▓реЗрдХрд┐рди рдЬрд┐рдЬреНрдЮрд╛рд╕реБ рдкрд╛рдардХ рдпрд╣рд╛рдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред

рдЪреМрдерд╛ рдЪрд░рдг


рдЪрд╛рд▓рдХ рдХреЗ рд╕рд┐рджреНрдзрд╛рдВрдд рдХреЗ рд╕рд╛рде рдЦреБрдж рдХреЛ рдкрд░рд┐рдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдЪрд▓реЛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдЖрдЧреЗ рдмрдврд╝реЗрдВред
рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╣реЗрдбрд░ рдлрд╝рд╛рдЗрд▓реЛрдВ рдХреЛ рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВ, "рд╡рд░реНрдЪреБрдЕрд▓" рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд╛рд░реНрдб рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВ, рд╕рд╛рде рд╣реА I2C рдбреНрд░рд╛рдЗрд╡рд░ рдХреА рдкреНрд░рд╕реНрддреБрддрд┐ рднреАред

 /* i2c-skel.c: I2C bus driver. * * Name Surname <name@surname.ru> * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/interrupt.h> #include <linux/time.h> #include <linux/delay.h> #include <linux/device.h> /* * Registers description. */ #define SKEL_I2C_ID 0x00 /* Core Identifier register */ #define SKEL_I2C_ISR 0x14 /* Interrupt Status Register */ #define SKEL_I2C_ISR_DNE BIT(0) /* One byte transaction done */ #define SKEL_I2C_ISR_ARB BIT(1) /* Arbitration lost */ #define SKEL_I2C_ISR_TXE BIT(2) /* RX FIFO nearly full */ #define SKEL_I2C_ISR_NACK BIT(3) /* No ACK */ #define SKEL_I2C_IER 0x18 /* Interrupt Enable Register */ #define SKEL_I2C_IER_DNE BIT(0) /* Enable DNE IRQ */ #define SKEL_I2C_IER_ARB BIT(1) /* Enable ARB LOSR IRQ */ #define SKEL_I2C_IER_TXE BIT(2) /* Enable TX FIFO EPMTY IRQ */ #define SKEL_I2C_IER_NACK BIT(3) /* Enable NACK IRQ */ #define SKEL_I2C_CTRL 0x1C /* Control Register */ #define SKEL_I2C_CTRL_EN BIT(0) /* Enable I2C controller */ #define SKEL_I2C_CTRL_START BIT(1) /* Send START condition */ #define SKEL_I2C_CTRL_R BIT(2) /* Read command */ #define SKEL_I2C_CTRL_W BIT(3) /* Write command */ #define SKEL_I2C_CTRL_STOP BIT(4) /* Send STOP cindition */ #define SKEL_I2C_TX 0x20 /* TX FIFO */ #define SKEL_I2C_RX 0x24 /* RX FIFO */ #define SKEL_I2C_CLK 0x28 /* Clock Prescale Register*/ #define SKEL_I2C_TIMEOUT 100000 #define SKEL_I2C_XFER_TIMEOUT (msecs_to_jiffies(500)) #define FIFO_SIZE_TX 1024 #define FIFO_SIZE_RX 1024 int presc = -1; module_param(presc, int, S_IRUGO | S_IWUSR); /* * skel_i2c - I2C device context * @base: pointer to register struct * @msg: pointer to current message * @mlen: number of bytes transferred in msg * @dev: device reference * @adap: i2c core abstraction * @msg_complete: xfer completion object * @clk: reference for i2c input clock * @err: error occured * @buf: ptr to msg buffer * @bus_clock: current i2c bus clock rate * @lock: spinlock for IRQ synchronization */ struct skel_i2c { void __iomem *base; struct i2c_msg *msg; size_t mlen; struct device *dev; struct i2c_adapter adap; struct completion msg_complete; struct clk *clk; u32 bus_clock; int err; u32 addr; u8 *buf; spinlock_t lock; }; 

рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рдореБрдЦреНрдп рдирд┐рдпрдВрддреНрд░рдг рд░рдЬрд┐рд╕реНрдЯрд░ рд╣реИрдВ:

  • рдирд┐рдпрдВрддреНрд░рдг рд░рдЬрд┐рд╕реНрдЯрд░ (CTRL) - рдирд┐рдпрдВрддреНрд░рдг рд░рдЬрд┐рд╕реНрдЯрд░;
  • рдЗрдВрдЯрд░рдкреНрдЯ рд╕реНрдЯреЗрдЯрд╕ рд░рдЬрд┐рд╕реНрдЯрд░ (ISR) - рд╡реНрдпрд╡рдзрд╛рди рд╕реНрдЯреЗрдЯрд╕ рд░рдЬрд┐рд╕реНрдЯрд░;
  • рдЗрдВрдЯрд░рдкреНрдЯ рдЗрдиреЗрдмрд▓ рд░рдЬрд┐рд╕реНрдЯрд░ (IER) - рдЗрдВрдЯрд░рдкреНрдЯ рдорд╛рд╕реНрдХ рд░рдЬрд┐рд╕реНрдЯрд░ред

рдбреНрд░рд╛рдЗрд╡рд░ рдХрд╛ рджрд┐рд▓ skel_i2c рд╕рдВрд░рдЪрдирд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдлрд╝реАрд▓реНрдб рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:

  • .base - рд░рдЬрд┐рд╕реНрдЯрд░ рдХрд╛рд░реНрдб рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рд╕реВрдЪрдХ;
  • .msg - рд╡рд░реНрддрдорд╛рди рд╕рдВрджреЗрд╢ рдХреЗ рд▓рд┐рдП рд╕реВрдЪрдХ;
  • .adap - I2C рдЕрдореВрд░реНрдд (рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ) ред

рдЖрдЗрдП рдЕрдзрд┐рдХ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рднрд╛рдЧ рдкрд░ рдЪрд▓рддреЗ рд╣реИрдВ, рдЪрд╛рд▓рдХ рджреНрд╡рд╛рд░рд╛ рд╕рдорд░реНрдерд┐рдд рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ,
I2C рдПрдбрд╛рдкреНрдЯрд░ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдФрд░ I2C рд╕рдВрджреЗрд╢ рдЗрдВрдЯрд░рдлрд╝реЗрд╕:

 static const struct of_device_id skel_i2c_match[] = { { .compatible = "skel,skel-i2c", }, { .compatible = "at,24c64", }, {}, }; static u32 skel_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm skel_i2c_algo = { .master_xfer = skel_i2c_xfer, .functionality = skel_i2c_func, }; static struct platform_driver skel_i2c_driver = { .probe = skel_i2c_probe, .remove = skel_i2c_remove, .driver = { .name = "skel-i2c", .of_match_table = skel_i2c_match, }, }; module_platform_driver(skel_i2c_driver); MODULE_AUTHOR("Name Surname"); MODULE_DESCRIPTION("I2C bus driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:skel-i2c"); 

рд╕рдВрд░рдЪрдирд╛рдУрдВ рдФрд░ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рдирд╛рдо рд╕реЗ, рдЙрдирдХрд╛ рдЙрджреНрджреЗрд╢реНрдп рд╕реНрдкрд╖реНрдЯ рд╣реИ, рд╣рдо рдКрдкрд░ рд╕реЗ рдХреЗрд╡рд▓ рдореБрдЦреНрдп рд╕рдВрд░рдЪрдирд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ:

  • skel_i2c_driver - рдбреНрд░рд╛рдЗрд╡рд░ рдХрд╛ рдирд╛рдо, рд╕рдорд░реНрдерд┐рдд рдЙрдкрдХрд░рдгреЛрдВ рдФрд░ рдлрд╝рдВрдХреНрд╢рдВрд╕ рдХреА рдПрдХ рддрд╛рд▓рд┐рдХрд╛ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдХреЛ рд╕рд┐рд╕реНрдЯрдо рд╕реЗ рд▓реЛрдб рдпрд╛ рд╣рдЯрд╛рдП рдЬрд╛рдиреЗ рдкрд░ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИред

рдпрд╣ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рдирд┐рдпрдВрддреНрд░рдХ рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдлрд╝рдВрдХреНрд╢рди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛, рдФрд░ skel_i2c_probe (рдЬрдм рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ) рдФрд░ skel_i2c_remove (рдЬрдм рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рд╕рд┐рд╕реНрдЯрдо рд╕реЗ рдирд┐рдХрд╛рд▓рд╛ рдЬрд╛рддрд╛ рд╣реИ) рдХрд╛ рд╡рд░реНрдгрди рдХрд░реЗрдВред

 static int skel_i2c_init(struct skel_i2c *rdev) { u32 bus_clk_khz = rdev->bus_clock / 1000; u32 clk_khz = clk_get_rate(rdev->clk) / 1000; int prescale; int diff; prescale = clk_khz / (5 * bus_clk_khz) - 1; prescale = clamp(prescale, 0, 0xFFFF); diff = clk_khz / (5 * (prescale 1)) - bus_clk_khz; if (abs(diff) > bus_clk_khz / 10) { dev_err(rdev->dev, "Unsupported clock settings: clk: %d KHz, bus: %d KHz\n", clk_khz, bus_clk_khz); return -EINVAL; } if (presc != -1) i2c_write(presc, rdev->base, SKEL_I2C_CLK); else i2c_write(prescale, rdev->base, SKEL_I2C_CLK); return 0; } static int skel_i2c_probe(struct platform_device *pdev) { struct skel_i2c *rdev = NULL; struct resource *res; int irq, ret; u32 val; rdev = devm_kzalloc(&pdev->dev, sizeof(*rdev), GFP_KERNEL); if (!rdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rdev->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(rdev->base)) return PTR_ERR(rdev->base); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "Missing interrupt resource\n"); return irq; } rdev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rdev->clk)) { dev_err(&pdev->dev, "Missing clock\n"); return PTR_ERR(rdev->clk); } rdev->dev = &pdev->dev; init_completion(&rdev->msg_complete); spin_lock_init(&rdev->lock); val = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &rdev->bus_clock); if (val) { dev_err(&pdev->dev, "Default to 100kHz\n"); rdev->bus_clock = 100000; /* default clock rate */ } if (rdev->bus_clock > 400000) { dev_err(&pdev->dev, "Invalid clock-frequency %d\n", rdev->bus_clock); return -EINVAL; } ret = devm_request_irq(&pdev->dev, irq, skel_i2c_isr, 0, pdev->name, rdev); if (ret) { dev_err(&pdev->dev, "Failed to claim IRQ %d\n", irq); return ret; } ret = clk_prepare_enable(rdev->clk); if (ret) { dev_err(&pdev->dev, "Failed to enable clock\n"); return ret; } skel_i2c_init(rdev); i2c_set_adapdata(&rdev->adap, rdev); strlcpy(rdev->adap.name, pdev->name, sizeof(rdev->adap.name)); rdev->adap.owner = THIS_MODULE; rdev->adap.algo = &skel_i2c_algo; rdev->adap.dev.parent = &pdev->dev; rdev->adap.dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, rdev); ret = i2c_add_adapter(&rdev->adap); if (ret) { clk_disable_unprepare(rdev->clk); return ret; } dev_info(&pdev->dev, "I2C probe complete\n"); return 0; } static int skel_i2c_remove(struct platform_device *pdev) { struct skel_i2c *rdev = platform_get_drvdata(pdev); clk_disable_unprepare(rdev->clk); i2c_del_adapter(&rdev->adap); return 0; } 

рд╕рдмрд╕реЗ рд╕рд░рд▓ рдХрд╛рд░реНрдп skel_i2c_remove рд╣реИ, рдЬреЛ рдШрдбрд╝реА рдХреЗ рд╕реНрд░реЛрдд рдХреЛ рдмрдВрдж рдХрд░ рджреЗрддрд╛ рд╣реИ рдФрд░ рдЙрдкрдпреЛрдЧ рдХреА рдЧрдИ рдореЗрдореЛрд░реА рдХреЛ рдореБрдХреНрдд рдХрд░рддрд╛ рд╣реИред Skel_i2c_init рдлрд╝рдВрдХреНрд╢рди I2C рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рдкреНрд░рд╛рд░рдВрдн рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддрд╛ рд╣реИред

рдЬреИрд╕рд╛ рдХрд┐ рдкрд╣рд▓реЗ рдЙрд▓реНрд▓реЗрдЦ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, skel_i2c_probe рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдбреНрд░рд╛рдЗрд╡рд░ рдХреЛ рдкрдВрдЬреАрдХреГрдд рдХрд░рддрд╛ рд╣реИред рд╕рд╢рд░реНрдд рд░реВрдк рд╕реЗ, рдХреНрд░рд┐рдпрд╛рдУрдВ рдХреЗ рдЕрдиреБрдХреНрд░рдо рдХреЛ рджреЛ рдЪрд░рдгреЛрдВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

  • рд╕рд┐рд╕реНрдЯрдо рд╕рдВрд╕рд╛рдзрди рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдФрд░ рдПрдХ рдмрд╛рдзрд╛ рд╣реИрдВрдбрд▓рд░ skel_i2c_isr рдкрдВрдЬреАрдХреГрдд рдХрд░рдирд╛;
  • рд╕рдВрд░рдЪрдирд╛ рдХреНрд╖реЗрддреНрд░реЛрдВ рдореЗрдВ рднрд░рдирд╛ рдФрд░ рдПрдХ рдирдпрд╛ I2C рдПрдбрд╛рдкреНрдЯрд░ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдХреЙрд▓ рдХрд░рдирд╛ред

рдбреНрд░рд╛рдЗрд╡рд░ рд╕рд┐рд╕реНрдЯрдо рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдЖрдк рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдкрд░ рд╕рдВрджреЗрд╢ рд╕реНрдерд╛рдирд╛рдВрддрд░рдг рддрд░реНрдХ рдХреЛ рд▓рд╛рдЧреВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:

 static inline void i2c_write(uint32_t value, void *base, uint32_t addr) { writel(value, base addr); #if defined DEBUG dev_dbg(rdev->dev, "iowrite32(0x%x, base 0x%x);\n", value, addr); #endif } static inline uint32_t i2c_read(void *base, uint32_t addr) { uint32_t reg = readl(base addr); #if defined DEBUG dev_dbg(rdev->dev, "/* ioread32(base 0x%x) == 0x%x */\n", addr, reg); #endif return reg; } static irqreturn_t skel_i2c_isr(int irq, void *dev) { if (unlikely(int_stat & skel_I2C_ISR_ARB)) { } else if (unlikely(int_stat & skel_I2C_ISR_NACK)) { } if (read) fill_rx_fifo(rdev); else fill_tx_fifo(rdev); complete(&rdev->msg_complete); return IRQ_HANDLED; } static int skel_i2c_xfer_msg(struct skel_i2c *rdev, struct i2c_msg *msg) { unsigned long time; rdev->msg = msg; rdev->mlen = msg->len; rdev->addr = msg->addr; rdev->buf = msg->buf; rdev->err = 0; reinit_completion(&rdev->msg_complete); skel_i2c_start_trans(rdev, msg); time = wait_for_completion_timeout(&rdev->msg_complete, skel_I2C_XFER_TIMEOUT); if (time == 0) rdev->err = -ETIMEDOUT; rdev->curr; return rdev->err; } static int skel_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct skel_i2c *rdev = i2c_get_adapdata(adap); int i, ret = 0; for (i = 0; (ret == 0) && (i < num); i) ret = skel_i2c_xfer_msg(rdev, msgs); skel_i2c_snd_stop(rdev); return ret ? : num; } 

рдкрд╣рд▓рд╛ рдЪрд░рдг рд╕рд┐рд╕реНрдЯрдо рдХрд░реНрдиреЗрд▓ рдореЙрдбреНрдпреВрд▓ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЕрдВрддрд░рд┐рдХреНрд╖ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХреА рдмрд╛рддрдЪреАрдд рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИред рд╣рдордиреЗ рдбреНрд░рд╛рдЗрд╡рд░ рдЗрдВрдЯрд░реНрдирд▓реНрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рджреЗрдЦрдирд╛ рдЖрд╕рд╛рди рд╣реИ рдЬрд┐рд╕рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рд╡рд┐рдирд┐рдордп рд╣реЛрддрд╛ рд╣реИред рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рд╕рдВрджреЗрд╢ рдкрд╛рд╕ рдХрд░рдирд╛ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╣реИ:

  • skel_i2c_xfer - рдлрд╝рдВрдХреНрд╢рди рд╕реАрдзреЗ рдкреНрд░рд╕рд╛рд░рдг рдХреЗ рд▓рд┐рдП рд╕рдВрджреЗрд╢ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдХреНрд░рдорд┐рдХ рд░реВрдк рд╕реЗ рдкреНрд░рддреНрдпреЗрдХ рд╕рдВрджреЗрд╢ рдХреЛ skel_i2c_xfer_msg рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрджрд┐ рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рдХреЗ рджреМрд░рд╛рди рдХреЛрдИ рддреНрд░реБрдЯрд┐ рд╣реБрдИ, рддреЛ рдбреЗрдЯрд╛ рдЯреНрд░рд╛рдВрд╕рдлрд░ рд░реБрдХ рдЬрд╛рддрд╛ рд╣реИ;
  • skel_i2c_xfer_msg - рдлрд╝рдВрдХреНрд╢рди рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдбреНрд░рд╛рдЗрд╡рд░ рдлрд╝реАрд▓реНрдб рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╕рдВрджреЗрд╢ рдкреНрд░рд╕рд╛рд░рдг рдХреА рд╢реБрд░реБрдЖрдд рдХрд░рддрд╛ рд╣реИ;
  • skel_i2c_isr - рдкреНрд░рд╕рдВрд╕реНрдХрд░рдг рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдирд┐рдпрдорд┐рдд рдХрд░рдирд╛ред рдпрд╣реАрдВ рдкрд░ рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдФрд░ рдмрд╕ рд╕рдВрдЪрд╛рд░ рд╣реЛрддрд╛ рд╣реИред рдпрджрд┐ рд╕рднреА рдбреЗрдЯрд╛ рднреЗрдЬреЗ / рдкреНрд░рд╛рдкреНрдд рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рдХрд┐рдП рдЧрдП рдзреНрд╡рдЬ рдХреЛ рдкреВрд░реНрдг рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдХреЙрд▓ рдХрд░рдХреЗ рд╕реЗрдЯ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рд╕рдВрджреЗрд╢ рд╕рдВрдЪрд░рдг рдХреЗ рдкреВрд░рд╛ рд╣реЛрдиреЗ рдХрд╛ рд╕рдВрдХреЗрдд рджреЗрддрд╛ рд╣реИред

рд▓реЗрдЦ рдореЗрдВ рдХрд╛рдо рдХреА рдХреБрдЫ рд╕реВрдХреНрд╖реНрдорддрд╛рдУрдВ рдХрд╛ рд╡рд░реНрдгрди рдирд╣реАрдВ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╕рдВрджреЗрд╢ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдХреНрд░рд┐рдпрд╛рдУрдВ рдХрд╛ рдХреНрд░рдо, рдЪреВрдВрдХрд┐ рдЗрд╕ рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣рд╛рд░реНрдбрд╡реЗрдпрд░-рдирд┐рд░реНрднрд░ рд╣реИред рд╣рдордиреЗ рдбреНрд░рд╛рдЗрд╡рд░ рдХреЗ рд╕рд╛рдорд╛рдиреНрдп рднрд╛рдЧ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд┐рдпрд╛, рдирд┐рдпрдВрддреНрд░рдХ рдХреА рд╣рд╛рд░реНрдбрд╡реЗрдпрд░ рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреА рдкрд░рд╡рд╛рд╣ рдХрд┐рдП рдмрд┐рдирд╛ред

рдкреВрд░реНрдг рдЪрд╛рд▓рдХ рдХрдВрдХрд╛рд▓ рдиреАрдЪреЗ рд╕рдВрд▓рдЧреНрди рд╣реИред рдХреГрдкрдпрд╛, рдпрджрд┐ рдЖрдкрдХреЛ рддреНрд░реБрдЯрд┐рдпрд╛рдВ / рдЧрд▓рддрд┐рдпрд╛рдБ рдорд┐рд▓рддреА рд╣реИрдВ, рдпрд╛ рдЖрдкрдХреЗ рдкрд╛рд╕ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рд╣реИ, рддреЛ рдкреАрдПрдо рдпрд╛ рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдореЗрдВ рд▓рд┐рдЦреЗрдВред

рдЪрд╛рд▓рдХ рдХрдВрдХрд╛рд▓
 /* i2c-skel.c: I2C bus driver. * * Name Surname <name@surname.ru> * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/interrupt.h> #include <linux/time.h> #include <linux/delay.h> #include <linux/device.h> /* * Registers description. */ #define SKEL_I2C_ID 0x00 /* Core Identifier register */ #define SKEL_I2C_ISR 0x14 /* Interrupt Status Register */ #define SKEL_I2C_ISR_DNE BIT(0) /* One byte transaction done */ #define SKEL_I2C_ISR_ARB BIT(1) /* Arbitration lost */ #define SKEL_I2C_ISR_TXE BIT(2) /* RX FIFO nearly full */ #define SKEL_I2C_ISR_NACK BIT(3) /* No ACK */ #define SKEL_I2C_IER 0x18 /* Interrupt Enable Register */ #define SKEL_I2C_IER_DNE BIT(0) /* Enable DNE IRQ */ #define SKEL_I2C_IER_ARB BIT(1) /* Enable ARB LOSR IRQ */ #define SKEL_I2C_IER_TXE BIT(2) /* Enable TX FIFO EPMTY IRQ */ #define SKEL_I2C_IER_NACK BIT(3) /* Enable NACK IRQ */ #define SKEL_I2C_CTRL 0x1C /* Control Register */ #define SKEL_I2C_CTRL_EN BIT(0) /* Enable I2C controller */ #define SKEL_I2C_CTRL_START BIT(1) /* Send START condition */ #define SKEL_I2C_CTRL_R BIT(2) /* Read command */ #define SKEL_I2C_CTRL_W BIT(3) /* Write command */ #define SKEL_I2C_CTRL_STOP BIT(4) /* Send STOP cindition */ #define SKEL_I2C_TX 0x20 /* TX FIFO */ #define SKEL_I2C_RX 0x24 /* RX FIFO */ #define SKEL_I2C_CLK 0x28 /* Clock Prescale Register*/ #define SKEL_I2C_TIMEOUT 100000 #define SKEL_I2C_XFER_TIMEOUT (msecs_to_jiffies(500)) #define FIFO_SIZE_TX 1024 #define FIFO_SIZE_RX 1024 int presc = -1; module_param(presc, int, S_IRUGO | S_IWUSR); /* * skel_i2c - I2C device context * @base: pointer to register struct * @msg: pointer to current message * @mlen: number of bytes transferred in msg * @dev: device reference * @adap: i2c core abstraction * @msg_complete: xfer completion object * @clk: reference for i2c input clock * @err: error occured * @buf: ptr to msg buffer * @bus_clock: current i2c bus clock rate * @lock: spinlock for IRQ synchronization */ struct skel_i2c { void __iomem *base; struct i2c_msg *msg; size_t mlen; struct device *dev; struct i2c_adapter adap; struct completion msg_complete; struct clk *clk; u32 bus_clock; int err;; u32 addr; u8 *buf; spinlock_t lock; }; static const struct of_device_id skel_i2c_match[] = { { .compatible = "skel,skel-i2c", }, { .compatible = "at,24c64", }, {}, }; static inline void i2c_write(uint32_t value, void *base, uint32_t addr) { writel(value, base + addr); #if defined DEBUG dev_dbg(rdev->dev, "iowrite32(0x%x, base 0x%x);\n", value, addr); #endif } static inline uint32_t i2c_read(void *base, uint32_t addr) { uint32_t reg = readl(base + addr); #if defined DEBUG dev_dbg(rdev->dev, "/* ioread32(base 0x%x) == 0x%x */\n", addr, reg); #endif return reg; } static void skel_i2c_transfer(struct skel_i2c *rdev, u32 data) { i2c_write(data, rdev->base, SKEL_I2C_TX); } static void fill_tx_fifo(struct skel_i2c *rdev) { size_t tx_fifo_avail = FIFO_SIZE_TX; int bytes_to_transfer = min(tx_fifo_avail, rdev->mlen); while (bytes_to_transfer-- > 0) { skel_i2c_transfer(rdev, *rdev->buf); rdev->mlen--; } } static void fill_rx_fifo(struct skel_i2c *rdev) { size_t rx_fifo_avail = FIFO_SIZE_RX; int receive = min(rx_fifo_avail, rdev->mlen); while (receive-- > 0) { *rdev->buf = i2c_read(rdev->base, SKEL_I2C_RX); rdev->mlen--; } } void skel_i2c_snd_stop(struct skel_i2c *rdev) { u32 control = i2c_read(rdev->base, SKEL_I2C_CTRL); i2c_write(control | SKEL_I2C_CTRL_STOP, rdev->base, SKEL_I2C_CTRL); } static irqreturn_t skel_i2c_isr(int irq, void *dev) { struct skel_i2c *rdev = dev; u32 int_stat, read; int_stat = i2c_read(rdev->base, SKEL_I2C_ISR); read = rdev->msg->flags & I2C_M_RD; if (unlikely(int_stat & SKEL_I2C_ISR_ARB)) { } else if (unlikely(int_stat & SKEL_I2C_ISR_NACK)) { } if (read) fill_rx_fifo(rdev); else fill_tx_fifo(rdev); complete(&rdev->msg_complete); return IRQ_HANDLED; } static void skel_i2c_start_trans(struct skel_i2c *rdev, struct i2c_msg *msg) { } static int skel_i2c_xfer_msg(struct skel_i2c *rdev, struct i2c_msg *msg) { unsigned long time; rdev->msg = msg; rdev->mlen = msg->len; rdev->addr = msg->addr; rdev->buf = msg->buf; rdev->err = 0; reinit_completion(&rdev->msg_complete); skel_i2c_start_trans(rdev, msg); time = wait_for_completion_timeout(&rdev->msg_complete, SKEL_I2C_XFER_TIMEOUT); if (time == 0) rdev->err = -ETIMEDOUT; return rdev->err; } static int skel_i2c_init(struct skel_i2c *rdev) { u32 bus_clk_khz = rdev->bus_clock / 1000; u32 clk_khz = clk_get_rate(rdev->clk) / 1000; int prescale; int diff; prescale = clk_khz / (5 * bus_clk_khz) - 1; prescale = clamp(prescale, 0, 0xFFFF); diff = clk_khz / (5 * (prescale - 1)) - bus_clk_khz; if (abs(diff) > bus_clk_khz / 10) { dev_err(rdev->dev, "Unsupported clock settings: clk: %d KHz, bus: %d KHz\n", clk_khz, bus_clk_khz); return -EINVAL; } if (presc != -1) i2c_write(presc, rdev->base, SKEL_I2C_CLK); else i2c_write(prescale, rdev->base, SKEL_I2C_CLK); return 0; } static int skel_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct skel_i2c *rdev = i2c_get_adapdata(adap); int i, ret = 0; for (i = 0; (ret == 0) && (i < num); i++) ret = skel_i2c_xfer_msg(rdev, msgs); skel_i2c_snd_stop(rdev); return ret ? : num; } static u32 skel_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm skel_i2c_algo = { .master_xfer = skel_i2c_xfer, .functionality = skel_i2c_func, }; static int skel_i2c_probe(struct platform_device *pdev) { struct skel_i2c *rdev = NULL; struct resource *res; int irq, ret; u32 val; rdev = devm_kzalloc(&pdev->dev, sizeof(*rdev), GFP_KERNEL); if (!rdev) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rdev->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(rdev->base)) return PTR_ERR(rdev->base); irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "Missing interrupt resource\n"); return irq; } rdev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rdev->clk)) { dev_err(&pdev->dev, "Missing clock\n"); return PTR_ERR(rdev->clk); } rdev->dev = &pdev->dev; init_completion(&rdev->msg_complete); spin_lock_init(&rdev->lock); val = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &rdev->bus_clock); if (val) { dev_err(&pdev->dev, "Default to 100kHz\n"); rdev->bus_clock = 100000; /* default clock rate */ } if (rdev->bus_clock > 400000) { dev_err(&pdev->dev, "Invalid clock-frequency %d\n", rdev->bus_clock); return -EINVAL; } ret = devm_request_irq(&pdev->dev, irq, skel_i2c_isr, 0, pdev->name, rdev); if (ret) { dev_err(&pdev->dev, "Failed to claim IRQ %d\n", irq); return ret; } ret = clk_prepare_enable(rdev->clk); if (ret) { dev_err(&pdev->dev, "Failed to enable clock\n"); return ret; } skel_i2c_init(rdev); i2c_set_adapdata(&rdev->adap, rdev); strlcpy(rdev->adap.name, pdev->name, sizeof(rdev->adap.name)); rdev->adap.owner = THIS_MODULE; rdev->adap.algo = &skel_i2c_algo; rdev->adap.dev.parent = &pdev->dev; rdev->adap.dev.of_node = pdev->dev.of_node; platform_set_drvdata(pdev, rdev); ret = i2c_add_adapter(&rdev->adap); if (ret) { clk_disable_unprepare(rdev->clk); return ret; } dev_info(&pdev->dev, "I2C probe complete\n"); return 0; } static int skel_i2c_remove(struct platform_device *pdev) { struct skel_i2c *rdev = platform_get_drvdata(pdev); clk_disable_unprepare(rdev->clk); i2c_del_adapter(&rdev->adap); return 0; } static struct platform_driver skel_i2c_driver = { .probe = skel_i2c_probe, .remove = skel_i2c_remove, .driver = { .name = "skel-i2c", .of_match_table = skel_i2c_match, }, }; module_platform_driver(skel_i2c_driver); MODULE_AUTHOR("Name Surname"); MODULE_DESCRIPTION("I2C bus driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:skel-i2c"); 


рдЖрдкрдХрд╛ рдзреНрдпрд╛рди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!

Source: https://habr.com/ru/post/hi413249/


All Articles