bios/sdram: expose I2C functions
This commit is contained in:
parent
bdc7eb5c48
commit
472bf9ac71
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <generated/csr.h>
|
#include <generated/csr.h>
|
||||||
|
|
||||||
|
@ -222,24 +223,136 @@ define_command(sdrlevel, sdrlevel, "Perform read/write leveling", LITEDRAM_CMDS)
|
||||||
define_command(memtest, memtest, "Run a memory test", LITEDRAM_CMDS);
|
define_command(memtest, memtest, "Run a memory test", LITEDRAM_CMDS);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command "i2creset"
|
||||||
|
*
|
||||||
|
* Reset I2C line state in case a slave locks the line.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifdef CSR_I2C_BASE
|
||||||
|
define_command(i2creset, i2c_reset, "Reset I2C line state", LITEDRAM_CMDS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command "i2cwr"
|
||||||
|
*
|
||||||
|
* Write I2C slave memory using 7-bit slave address and 8-bit memory address.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifdef CSR_I2C_BASE
|
||||||
|
static void i2cwr_handler(int nb_params, char **params)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *c;
|
||||||
|
unsigned char write_params[32]; // also indirectly limited by CMD_LINE_BUFFER_SIZE
|
||||||
|
|
||||||
|
if (nb_params < 2) {
|
||||||
|
printf("i2cwr <slaveaddr7bit> <addr> [<data>, ...]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb_params - 1 > sizeof(write_params)) {
|
||||||
|
printf("Max data length is %d", sizeof(write_params));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nb_params; ++i) {
|
||||||
|
write_params[i] = strtoul(params[i], &c, 0);
|
||||||
|
if (*c != 0) {
|
||||||
|
printf("Incorrect value of parameter %d", i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i2c_write(write_params[0], write_params[1], &write_params[2], nb_params - 2)) {
|
||||||
|
printf("Error during I2C write");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
define_command(i2cwr, i2cwr_handler, "Write over I2C", LITEDRAM_CMDS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command "i2crd"
|
||||||
|
*
|
||||||
|
* Read I2C slave memory using 7-bit slave address and 8-bit memory address.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifdef CSR_I2C_BASE
|
||||||
|
static void i2crd_handler(int nb_params, char **params)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
int len;
|
||||||
|
unsigned char slave_addr, addr;
|
||||||
|
unsigned char buf[256];
|
||||||
|
bool send_stop = true;
|
||||||
|
|
||||||
|
if (nb_params < 3) {
|
||||||
|
printf("i2crd <slaveaddr7bit> <addr> <len> [<send_stop>]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
slave_addr = strtoul(params[0], &c, 0);
|
||||||
|
if (*c != 0) {
|
||||||
|
printf("Incorrect slave address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = strtoul(params[1], &c, 0);
|
||||||
|
if (*c != 0) {
|
||||||
|
printf("Incorrect memory address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strtoul(params[2], &c, 0);
|
||||||
|
if (*c != 0) {
|
||||||
|
printf("Incorrect data length");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (len > sizeof(buf)) {
|
||||||
|
printf("Max data count is %d", sizeof(buf));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nb_params > 3) {
|
||||||
|
send_stop = strtoul(params[3], &c, 0) != 0;
|
||||||
|
if (*c != 0) {
|
||||||
|
printf("Incorrect send_stop value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i2c_read(slave_addr, addr, buf, len, send_stop)) {
|
||||||
|
printf("Error during I2C read");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_bytes((unsigned int *) buf, len, addr);
|
||||||
|
}
|
||||||
|
define_command(i2crd, i2crd_handler, "Read over I2C", LITEDRAM_CMDS);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command "spdread"
|
* Command "spdread"
|
||||||
*
|
*
|
||||||
* Read contents of SPD EEPROM memory.
|
* Read contents of SPD EEPROM memory.
|
||||||
* SPD address is defined by the pins A0, A1, A2.
|
* SPD address is a 3-bit address defined by the pins A0, A1, A2.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#ifdef CSR_I2C_BASE
|
#ifdef CSR_I2C_BASE
|
||||||
|
#define SPD_RW_PREAMBLE 0b1010
|
||||||
|
#define SPD_RW_ADDR(a210) ((SPD_RW_PREAMBLE << 3) | ((a210) & 0b111))
|
||||||
|
|
||||||
static void spdread_handler(int nb_params, char **params)
|
static void spdread_handler(int nb_params, char **params)
|
||||||
{
|
{
|
||||||
unsigned char buf[256];
|
|
||||||
unsigned int spdaddr;
|
|
||||||
int length = sizeof(buf);
|
|
||||||
char *c;
|
char *c;
|
||||||
|
unsigned char spdaddr;
|
||||||
|
unsigned char buf[256];
|
||||||
|
int len = sizeof(buf);
|
||||||
|
bool send_stop = true;
|
||||||
|
|
||||||
if (nb_params < 1) {
|
if (nb_params < 1) {
|
||||||
printf("spdread <spdaddr> [<length>]");
|
printf("spdread <spdaddr> [<send_stop>]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,23 +367,19 @@ static void spdread_handler(int nb_params, char **params)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nb_params > 1) {
|
if (nb_params > 1) {
|
||||||
length = strtoul(params[1], &c, 0);
|
send_stop = strtoul(params[1], &c, 0) != 0;
|
||||||
if (*c != 0) {
|
if (*c != 0) {
|
||||||
printf("Incorrect address");
|
printf("Incorrect send_stop value");
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (length > sizeof(buf)) {
|
|
||||||
printf("Max length is %d", sizeof(buf));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spdread(spdaddr, 0, buf, length)) {
|
if (!i2c_read(SPD_RW_ADDR(spdaddr), 0, buf, len, send_stop)) {
|
||||||
printf("Error when reading SPD EEPROM");
|
printf("Error when reading SPD EEPROM");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_bytes((unsigned int *) buf, length, 0);
|
dump_bytes((unsigned int *) buf, len, 0);
|
||||||
}
|
}
|
||||||
define_command(spdread, spdread_handler, "Read SPD EEPROM", LITEDRAM_CMDS);
|
define_command(spdread, spdread_handler, "Read SPD EEPROM", LITEDRAM_CMDS);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
include ../include/generated/variables.mak
|
include ../include/generated/variables.mak
|
||||||
include $(SOC_DIRECTORY)/software/common.mak
|
include $(SOC_DIRECTORY)/software/common.mak
|
||||||
|
|
||||||
OBJECTS = sdram.o spd.o
|
OBJECTS = sdram.o i2c.o
|
||||||
|
|
||||||
all: liblitedram.a
|
all: liblitedram.a
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
// This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
|
// This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "spd.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
#ifdef CSR_I2C_BASE
|
#ifdef CSR_I2C_BASE
|
||||||
|
|
||||||
// SMBus uses frequency 10-100 kHz
|
|
||||||
#define I2C_FREQ_HZ 50000
|
|
||||||
#define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ)
|
#define I2C_PERIOD_CYCLES (CONFIG_CLOCK_FREQUENCY / I2C_FREQ_HZ)
|
||||||
#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4)
|
#define I2C_DELAY(n) cdelay((n)*I2C_PERIOD_CYCLES/4)
|
||||||
|
|
||||||
static void cdelay(int i)
|
static inline void cdelay(int i)
|
||||||
{
|
{
|
||||||
while(i > 0) {
|
while(i > 0) {
|
||||||
__asm__ volatile(CONFIG_CPU_NOP);
|
__asm__ volatile(CONFIG_CPU_NOP);
|
||||||
|
@ -18,16 +16,15 @@ static void cdelay(int i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_oe_scl_sda(int oe, int scl, int sda)
|
static inline void i2c_oe_scl_sda(bool oe, bool scl, bool sda)
|
||||||
{
|
{
|
||||||
i2c_w_write(
|
i2c_w_write(
|
||||||
((oe & 1) << CSR_I2C_W_OE_OFFSET) |
|
((oe & 1) << CSR_I2C_W_OE_OFFSET) |
|
||||||
((scl & 1) << CSR_I2C_W_SCL_OFFSET) |
|
((scl & 1) << CSR_I2C_W_SCL_OFFSET) |
|
||||||
((sda & 1) << CSR_I2C_W_SDA_OFFSET)
|
((sda & 1) << CSR_I2C_W_SDA_OFFSET)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// START condition: 1-to-0 transition of SDA when SCL is 1
|
// START condition: 1-to-0 transition of SDA when SCL is 1
|
||||||
static void i2c_start(void)
|
static void i2c_start(void)
|
||||||
{
|
{
|
||||||
|
@ -51,25 +48,6 @@ static void i2c_stop(void)
|
||||||
i2c_oe_scl_sda(0, 1, 1);
|
i2c_oe_scl_sda(0, 1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset line state
|
|
||||||
static void i2c_reset(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
i2c_oe_scl_sda(1, 1, 1);
|
|
||||||
I2C_DELAY(8);
|
|
||||||
for (i = 0; i < 9; ++i) {
|
|
||||||
i2c_oe_scl_sda(1, 0, 1);
|
|
||||||
I2C_DELAY(2);
|
|
||||||
i2c_oe_scl_sda(1, 1, 1);
|
|
||||||
I2C_DELAY(2);
|
|
||||||
}
|
|
||||||
i2c_oe_scl_sda(0, 0, 1);
|
|
||||||
I2C_DELAY(1);
|
|
||||||
i2c_stop();
|
|
||||||
i2c_oe_scl_sda(0, 1, 1);
|
|
||||||
I2C_DELAY(8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call when in the middle of SCL low, advances one clk period
|
// Call when in the middle of SCL low, advances one clk period
|
||||||
static void i2c_transmit_bit(int value)
|
static void i2c_transmit_bit(int value)
|
||||||
{
|
{
|
||||||
|
@ -99,10 +77,10 @@ static int i2c_receive_bit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send data byte and return 1 if slave sends ACK
|
// Send data byte and return 1 if slave sends ACK
|
||||||
static int i2c_transmit(unsigned char data)
|
static bool i2c_transmit_byte(unsigned char data)
|
||||||
{
|
{
|
||||||
int ack;
|
|
||||||
int i;
|
int i;
|
||||||
|
int ack;
|
||||||
|
|
||||||
// SCL should have already been low for 1/4 cycle
|
// SCL should have already been low for 1/4 cycle
|
||||||
i2c_oe_scl_sda(0, 0, 0);
|
i2c_oe_scl_sda(0, 0, 0);
|
||||||
|
@ -118,13 +96,11 @@ static int i2c_transmit(unsigned char data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read data byte and send ACK if ack=1
|
// Read data byte and send ACK if ack=1
|
||||||
static unsigned char i2c_receive(int ack)
|
static unsigned char i2c_receive_byte(bool ack)
|
||||||
{
|
{
|
||||||
unsigned char data = 0;
|
|
||||||
int i;
|
int i;
|
||||||
|
unsigned char data = 0;
|
||||||
|
|
||||||
i2c_oe_scl_sda(0, 0, 0);
|
|
||||||
I2C_DELAY(1);
|
|
||||||
for (i = 0; i < 8; ++i) {
|
for (i = 0; i < 8; ++i) {
|
||||||
data <<= 1;
|
data <<= 1;
|
||||||
data |= i2c_receive_bit();
|
data |= i2c_receive_bit();
|
||||||
|
@ -134,47 +110,96 @@ static unsigned char i2c_receive(int ack)
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset line state
|
||||||
#define ADDR_PREAMBLE_RW 0b1010
|
void i2c_reset(void)
|
||||||
#define ADDR_7BIT(addr) ((ADDR_PREAMBLE_RW << 3) | ((addr) & 0b111))
|
{
|
||||||
#define ADDR_WRITE(addr) ((ADDR_7BIT(addr) << 1) & (~1u))
|
int i;
|
||||||
#define ADDR_READ(addr) ((ADDR_7BIT(addr) << 1) | 1u)
|
i2c_oe_scl_sda(1, 1, 1);
|
||||||
|
I2C_DELAY(8);
|
||||||
|
for (i = 0; i < 9; ++i) {
|
||||||
|
i2c_oe_scl_sda(1, 0, 1);
|
||||||
|
I2C_DELAY(2);
|
||||||
|
i2c_oe_scl_sda(1, 1, 1);
|
||||||
|
I2C_DELAY(2);
|
||||||
|
}
|
||||||
|
i2c_oe_scl_sda(0, 0, 1);
|
||||||
|
I2C_DELAY(1);
|
||||||
|
i2c_stop();
|
||||||
|
i2c_oe_scl_sda(0, 1, 1);
|
||||||
|
I2C_DELAY(8);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read SPD memory content
|
* Read slave memory over I2C starting at given address
|
||||||
*
|
*
|
||||||
* spdaddr: address of SPD EEPROM defined by pins A0, A1, A2
|
* First writes the memory starting address, then reads the data:
|
||||||
* addr: memory starting address
|
* START WR(slaveaddr) WR(addr) STOP START WR(slaveaddr) RD(data) RD(data) ... STOP
|
||||||
|
* Some chips require that after transmiting the address, there will be no STOP in between:
|
||||||
|
* START WR(slaveaddr) WR(addr) START WR(slaveaddr) RD(data) RD(data) ... STOP
|
||||||
*/
|
*/
|
||||||
int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len) {
|
bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
i2c_reset();
|
|
||||||
|
|
||||||
// To read from random address, we have to first send a "data-less" WRITE,
|
|
||||||
// followed by START condition with a READ (no STOP condition)
|
|
||||||
i2c_start();
|
i2c_start();
|
||||||
|
|
||||||
if(!i2c_transmit(ADDR_WRITE(spdaddr))) {
|
if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) {
|
||||||
i2c_reset();
|
i2c_stop();
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if(!i2c_transmit(addr)) {
|
if(!i2c_transmit_byte(addr)) {
|
||||||
i2c_reset();
|
i2c_stop();
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
I2C_DELAY(1);
|
if (send_stop) {
|
||||||
|
i2c_stop();
|
||||||
|
}
|
||||||
i2c_start();
|
i2c_start();
|
||||||
if(!i2c_transmit(ADDR_READ(spdaddr))) {
|
|
||||||
i2c_reset();
|
if(!i2c_transmit_byte(I2C_ADDR_RD(slave_addr))) {
|
||||||
return 0;
|
i2c_stop();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
buf[i] = i2c_receive(i != len - 1);
|
data[i] = i2c_receive_byte(i != len - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_stop();
|
i2c_stop();
|
||||||
|
|
||||||
return 1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write slave memory over I2C starting at given address
|
||||||
|
*
|
||||||
|
* First writes the memory starting address, then writes the data:
|
||||||
|
* START WR(slaveaddr) WR(addr) WR(data) WR(data) ... STOP
|
||||||
|
*/
|
||||||
|
bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i2c_start();
|
||||||
|
|
||||||
|
if(!i2c_transmit_byte(I2C_ADDR_WR(slave_addr))) {
|
||||||
|
i2c_stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!i2c_transmit_byte(addr)) {
|
||||||
|
i2c_stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
if(!i2c_transmit_byte(data[i])) {
|
||||||
|
i2c_stop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i2c_stop();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CSR_I2C_BASE */
|
#endif /* CSR_I2C_BASE */
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef __I2C_H
|
||||||
|
#define __I2C_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <generated/csr.h>
|
||||||
|
|
||||||
|
/* I2C frequency defaults to a safe value in range 10-100 kHz to be compatible with SMBus */
|
||||||
|
#ifndef I2C_FREQ_HZ
|
||||||
|
#define I2C_FREQ_HZ 50000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define I2C_ADDR_WR(addr) ((addr) << 1)
|
||||||
|
#define I2C_ADDR_RD(addr) (((addr) << 1) | 1u)
|
||||||
|
|
||||||
|
void i2c_reset(void);
|
||||||
|
bool i2c_write(unsigned char slave_addr, unsigned char addr, const unsigned char *data, unsigned int len);
|
||||||
|
bool i2c_read(unsigned char slave_addr, unsigned char addr, unsigned char *data, unsigned int len, bool send_stop);
|
||||||
|
|
||||||
|
#endif /* __I2C_H */
|
|
@ -2,7 +2,7 @@
|
||||||
#define __SDRAM_H
|
#define __SDRAM_H
|
||||||
|
|
||||||
#include <generated/csr.h>
|
#include <generated/csr.h>
|
||||||
#include "spd.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
void sdrsw(void);
|
void sdrsw(void);
|
||||||
void sdrhw(void);
|
void sdrhw(void);
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#ifndef __SPD_H
|
|
||||||
#define __SPD_H
|
|
||||||
|
|
||||||
#include <generated/csr.h>
|
|
||||||
|
|
||||||
int spdread(unsigned int spdaddr, unsigned int addr, unsigned char *buf, unsigned int len);
|
|
||||||
|
|
||||||
#endif /* __SPD_H */
|
|
Loading…
Reference in New Issue