Commit 465ea0ad authored by Alessandro Rubini's avatar Alessandro Rubini

i2c: implemented read/write functions, not used yet

parent 1c0185fb
......@@ -124,7 +124,7 @@ static struct fd_modlist mods[] = {
{"reset-again", fd_reset_again},
SUBSYS(acam),
SUBSYS(time),
//SUBSYS(i2c),
SUBSYS(i2c),
SUBSYS(zio),
};
......
......@@ -258,6 +258,15 @@ extern void fd_zio_exit(struct spec_fd *fd);
extern int fd_spec_init(void);
extern void fd_spec_exit(void);
/* Functions exported by i2c.c */
extern int fd_i2c_init(struct spec_fd *fd);
extern void fd_i2c_exit(struct spec_fd *fd);
extern int fd_eerom_read(struct spec_fd *fd, int i2c_addr, uint32_t offset,
uint8_t *buf, size_t size);
extern int fd_eeprom_write(struct spec_fd *fd, int i2c_addr, uint32_t offset,
uint8_t *buf, size_t size);
#endif /* __KERNEL__ */
#endif /* __FINE_DELAY_H__ */
/*
* I2C access (on-board EEPROM)
*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2 as published by the Free Software Foundation or, at your
* option, any later version.
*/
#include <linux/io.h>
#include <linux/time.h>
#include "fine-delay.h"
#include "hw/fd_main_regs.h"
static void set_sda(struct spec_fd *fd, int val)
{
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SDA_OUT;
if (val)
reg |= FD_I2CR_SDA_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
}
static void set_scl(struct spec_fd *fd, int val)
{
uint32_t reg;
reg = fd_readl(fd, FD_REG_I2CR) & ~FD_I2CR_SCL_OUT;
if (val)
reg |= FD_I2CR_SCL_OUT;
fd_writel(fd, reg, FD_REG_I2CR);
}
static int get_sda(struct spec_fd *fd)
{
return fd_readl(fd, FD_REG_I2CR) & FD_I2CR_SDA_IN ? 1 : 0;
};
static void mi2c_start(struct spec_fd *fd)
{
set_sda(fd, 0);
set_scl(fd, 0);
}
static void mi2c_stop(struct spec_fd *fd)
{
set_sda(fd, 0);
set_scl(fd, 1);
set_sda(fd, 1);
}
int mi2c_put_byte(struct spec_fd *fd, int data)
{
int i;
int ack;
for (i = 0; i < 8; i++, data<<=1) {
set_sda(fd, data & 0x80);
set_scl(fd, 1);
set_scl(fd, 0);
}
set_sda(fd, 1);
set_scl(fd, 1);
ack = get_sda(fd);
set_scl(fd, 0);
set_sda(fd, 0);
return ack ? -EIO : 0; /* ack low == success */
}
int mi2c_get_byte(struct spec_fd *fd, unsigned char *data, int sendack)
{
int i;
int indata = 0;
/* assert: scl is low */
set_scl(fd, 0);
set_sda(fd, 1);
for (i = 0; i < 8; i++) {
set_scl(fd, 1);
indata <<= 1;
if (get_sda(fd))
indata |= 0x01;
set_scl(fd, 0);
}
set_sda(fd, (sendack ? 0 : 1));
set_scl(fd, 1);
set_scl(fd, 0);
set_sda(fd, 0);
*data= indata;
return 0;
}
void mi2c_init(struct spec_fd *fd)
{
set_scl(fd, 1);
set_sda(fd, 1);
}
void mi2c_scan(struct spec_fd *fd)
{
int i;
for(i = 0; i < 256; i += 2) {
mi2c_start(fd);
if(!mi2c_put_byte(fd, i))
printk("%s: Found i2c device at 0x%x\n",
KBUILD_MODNAME, i >> 1);
mi2c_stop(fd);
}
}
/* FIXME: this is very inefficient: read several bytes in a row instead */
int fd_eeprom_read(struct spec_fd *fd, int i2c_addr, uint32_t offset,
uint8_t *buf, size_t size)
{
int i;
unsigned char c;
for(i = 0; i < size; i++) {
mi2c_start(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -EIO;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
offset++;
mi2c_stop(fd);
mi2c_start(fd);
mi2c_put_byte(fd, (i2c_addr << 1) | 1);
mi2c_get_byte(fd, &c, 0);
*buf++ = c;
mi2c_stop(fd);
}
return size;
}
int fd_eeprom_write(struct spec_fd *fd, int i2c_addr, uint32_t offset,
uint8_t *buf, size_t size)
{
int i, busy;
for(i = 0; i < size; i++) {
mi2c_start(fd);
if(mi2c_put_byte(fd, i2c_addr << 1) < 0) {
mi2c_stop(fd);
return -1;
}
mi2c_put_byte(fd, (offset >> 8) & 0xff);
mi2c_put_byte(fd, offset & 0xff);
mi2c_put_byte(fd, *buf++);
offset++;
mi2c_stop(fd);
do { /* wait until the chip becomes ready */
mi2c_start(fd);
busy = mi2c_put_byte(fd, i2c_addr << 1);
mi2c_stop(fd);
} while(busy);
}
return size;
}
int fd_i2c_init(struct spec_fd *fd)
{
mi2c_scan(fd);
/* FIXME: read calibration parameters from 0x50*/
return 0;
}
void fd_i2c_exit(struct spec_fd *fd)
{
/* nothing to do */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment