i2c.c 3.56 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * 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>
16
#include <linux/slab.h>
17
#include <linux/delay.h>
18
#include <linux/random.h>
19 20 21
#include "fine-delay.h"
#include "hw/fd_main_regs.h"

22
static void set_sda(struct fd_dev *fd, int val)
23 24 25 26 27 28 29
{
	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);
30
	ndelay(2000);
31 32
}

33
static void set_scl(struct fd_dev *fd, int val)
34 35 36 37 38 39 40
{
	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);
41
	ndelay(2000);
42 43
}

44
static int get_sda(struct fd_dev *fd)
45 46 47 48
{
	return fd_readl(fd, FD_REG_I2CR) & FD_I2CR_SDA_IN ? 1 : 0;
};

49
static void mi2c_start(struct fd_dev *fd)
50 51 52 53 54
{
	set_sda(fd, 0);
	set_scl(fd, 0);
}

55
static void mi2c_stop(struct fd_dev *fd)
56 57 58 59 60 61
{
	set_sda(fd, 0);
	set_scl(fd, 1);
	set_sda(fd, 1);
}

62
int mi2c_put_byte(struct fd_dev *fd, int data)
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
{
	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 */
}

84
int mi2c_get_byte(struct fd_dev *fd, unsigned char *data, int sendack)
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
{
	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;
}

109
void mi2c_init(struct fd_dev *fd)
110 111 112 113 114
{
	set_scl(fd, 1);
	set_sda(fd, 1);
}

115
void mi2c_scan(struct fd_dev *fd)
116 117 118 119
{
	int i;
	for(i = 0; i < 256; i += 2) {
		mi2c_start(fd);
120
		if(!mi2c_put_byte(fd, i) && fd->verbose)
121 122 123
			dev_info(&fd->fmc->dev,
				 "%s: Found i2c device at 0x%x\n",
			         KBUILD_MODNAME, i >> 1);
124 125 126 127 128
		mi2c_stop(fd);
	}
}

/* FIXME: this is very inefficient: read several bytes in a row instead */
129
int fd_eeprom_read(struct fd_dev *fd, int i2c_addr, uint32_t offset,
130
		void *buf, size_t size)
131 132
{
	int i;
133
	uint8_t *buf8 = buf;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	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);
150
		*buf8++ = c;
151 152 153 154 155
		mi2c_stop(fd);
	}
	return size;
}

156
int fd_eeprom_write(struct fd_dev *fd, int i2c_addr, uint32_t offset,
157
		 void *buf, size_t size)
158 159
{
	int i, busy;
160
	uint8_t *buf8 = buf;
161 162 163 164 165 166 167 168 169 170

	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);
171
		mi2c_put_byte(fd, *buf8++);
172 173 174 175 176 177 178 179 180 181 182 183
		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;
}

184
int fd_i2c_init(struct fd_dev *fd)
185 186 187 188 189
{
	mi2c_scan(fd);
	return 0;
}

190
void fd_i2c_exit(struct fd_dev *fd)
191 192 193 194
{
	/* nothing to do */
}