2007-10-11 Daniel Hellstrom <daniel@gaisler.com>

* Makefile.am, shared/can/occan.c, shared/include/ambapp.h: Add initial
	i2c and update OC-CAN support.
	* shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files.
This commit is contained in:
Joel Sherrill
2007-10-11 12:56:09 +00:00
parent 8a781ad7ee
commit 0743eaed1c
6 changed files with 456 additions and 21 deletions

View File

@@ -1,3 +1,9 @@
2007-10-11 Daniel Hellstrom <daniel@gaisler.com>
* Makefile.am, shared/can/occan.c, shared/include/ambapp.h: Add initial
i2c and update OC-CAN support.
* shared/i2c/i2cmst.c, shared/include/i2cmst.h: New files.
2007-09-21 Daniel Hellstrom <daniel@gaisler.com>
* shared/can/occan.c: Fix warning on Diab compiler.

View File

@@ -59,5 +59,9 @@ EXTRA_DIST += shared/include/b1553brm.h
EXTRA_DIST += shared/include/b1553brm_pci.h
EXTRA_DIST += shared/include/b1553brm_rasta.h
# I2C-master (I2CMST)
EXTRA_DIST += shared/i2c/i2cmst.c
EXTRA_DIST += shared/include/i2cmst.h
include $(top_srcdir)/../../../automake/subdirs.am
include $(top_srcdir)/../../../automake/local.am

View File

@@ -198,8 +198,8 @@ typedef struct {
} pelican_regs;
#endif
#define MAX_TSEG1 7
#define MAX_TSEG2 15
#define MAX_TSEG2 7
#define MAX_TSEG1 15
#if 0
typedef struct {
@@ -728,16 +728,13 @@ static void occan_stat_print(occan_stats *stats){
}
#endif
/* This function calculates BTR0 BTR1 values for a given bitrate.
* Heavily based on mgt_mscan_bitrate() from peak driver, which
* in turn is based on work by Arnaud Westenberg.
/* This function calculates BTR0 and BTR1 values for a given bitrate.
*
* Set communication parameters.
* baud rate in Hz
* input clock frequency of can core in Hz (system frequency)
* sjw synchronization jump width (0-3) prescaled clock cycles
* sampl_pt sample point in % (0-100) sets (TSEG1+2)/(TSEG1+TSEG2+3)
* ratio
* \param clock_hz OC_CAN Core frequency in Hz.
* \param rate Requested baud rate in bits/second.
* \param result Pointer to where resulting BTRs will be stored.
* \return zero if successful to calculate a baud rate.
*/
static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_speed_regs *result){
int best_error = 1000000000;
@@ -748,7 +745,7 @@ static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_
int clock = clock_hz / 2;
int sampl_pt = 90;
if ( (rate<10000) || (rate>1000000) ){
if ( (rate<5000) || (rate>1000000) ){
/* invalid speed mode */
return -1;
}
@@ -816,12 +813,7 @@ static int occan_calc_speedregs(unsigned int clock_hz, unsigned int rate, occan_
tseg1 = MAX_TSEG1;
tseg2 = best_tseg - tseg1 - 2;
}
/*
result->sjw = sjw;
result->brp = best_brp;
result->tseg1 = tseg1;
result->tseg2 = tseg2;
*/
result->btr0 = (sjw<<OCCAN_BUSTIM_SJW_BIT) | (best_brp&OCCAN_BUSTIM_BRP);
result->btr1 = (0<<7) | (tseg2<<OCCAN_BUSTIM_TSEG2_BIT) | tseg1;
@@ -834,10 +826,6 @@ static int occan_set_speedregs(occan_priv *priv, occan_speed_regs *timing){
priv->regs->bustim0 = timing->btr0;
priv->regs->bustim1 = timing->btr1;
/*
priv->regs->bustim0 = (timing->sjw<<OCCAN_BUSTIM_SJW_BIT) | (timing->brp&OCCAN_BUSTIM_BRP);
priv->regs->bustim1 = (timing->sam<<7) | (timing->tseg2<<OCCAN_BUSTIM_TSEG2_BIT) | timing->tseg1;
*/
return 0;
}

View File

@@ -0,0 +1,358 @@
/*
* Driver for GRLIB port of OpenCores I2C-master
*
* COPYRIGHT (c) 2007 Gaisler Research
* based on the RTEMS MPC83xx I2C driver (c) 2007 Embedded Brains GmbH.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* This file contains the driver and initialization code
*
* 2007-09-27: First version of driver (jan@gaisler.com)
*/
#include <bsp.h>
#include <i2cmst.h>
#include <ambapp.h>
#include <rtems/libi2c.h>
/* Enable debug printks? */
/* #define DEBUG */
/* Default to 40 MHz system clock? */
/*
#ifndef SYS_FREQ_kHZ
#define SYS_FREQ_kHZ 40000
#endif
*/
/* Calculates the scaler value for 100 kHz operation */
static int gr_i2cmst_calc_scaler(int sysfreq)
{
return sysfreq/500 - 1;
}
/* Wait for the current transfer to end */
static int gr_i2cmst_wait(gr_i2cmst_prv_t *prv_ptr, uint8_t expected_sts)
{
uint32_t tout = 0;
int current_sts;
#if defined(DEBUG)
printk("(gr_i2cmst_wait called...");
#endif
do {
if (tout++ > 1000000) {
return RTEMS_TIMEOUT;
}
} while (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP);
current_sts = prv_ptr->reg_ptr->cmdsts & ~GRI2C_STS_IF & ~GRI2C_STS_BUSY;
if (current_sts != expected_sts) {
#if defined(DEBUG)
if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_RXACK) {
printk("Transfer NAKed..");
}
if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_AL) {
printk("arbitration lost..");
}
if (prv_ptr->reg_ptr->cmdsts & GRI2C_STS_TIP) {
printk("transfer still in progress, huh?..");
}
printk("exited with IO error..)");
#endif
return RTEMS_IO_ERROR;
}
#if defined(DEBUG)
printk("exited...)");
#endif
return RTEMS_SUCCESSFUL;
}
/* Initialize hardware core */
static rtems_status_code gr_i2cmst_init(rtems_libi2c_bus_t *bushdl)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
#if defined(DEBUG)
printk("gr_i2cmst_init called...");
#endif
/* Disable core before changing prescale register */
prv_ptr->reg_ptr->ctrl = 0;
/* Calculate and set prescale value */
prv_ptr->reg_ptr->prescl = gr_i2cmst_calc_scaler(prv_ptr->sysfreq);
/* Enable core, interrupts are not enabled */
prv_ptr->reg_ptr->ctrl = GRI2C_CTRL_EN;
/* Clear possible START condition */
prv_ptr->sendstart = 0;
#if defined(DEBUG)
printk("exited\n");
#endif
return RTEMS_SUCCESSFUL;
}
static rtems_status_code gr_i2cmst_send_start(rtems_libi2c_bus_t *bushdl)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
#if defined(DEBUG)
printk("gr_i2cmst_send_start called...");
#endif
/* The OC I2C core does not work with stand alone START events,
instead the event is buffered */
prv_ptr->sendstart = GRI2C_CMD_STA;
#if defined(DEBUG)
printk("exited\n");
#endif
return RTEMS_SUCCESSFUL;
}
static rtems_status_code gr_i2cmst_send_stop(rtems_libi2c_bus_t *bushdl)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
#if defined(DEBUG)
printk("gr_i2cmst_send_stop called...");
#endif
prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_STO;
#if defined(DEBUG)
printk("exited\n");
#endif
return RTEMS_SUCCESSFUL;
}
static rtems_status_code gr_i2cmst_send_addr(rtems_libi2c_bus_t *bushdl,
uint32_t addr, int rw)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
uint8_t addr_byte;
rtems_status_code rc;
#if defined(DEBUG)
printk("gr_i2cmst_send_addr called, addr = 0x%x, rw = %d...",
addr, rw);
#endif
/* Check if long address is needed */
if (addr > 0x7f) {
addr_byte = ((addr >> 7) & 0x06) | (rw ? 1 : 0);
prv_ptr->reg_ptr->tdrd = addr_byte;
prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
prv_ptr->sendstart = 0;
/* Wait for transfer to complete */
rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
if (rc != RTEMS_SUCCESSFUL) {
#if defined(DEBUG)
printk("exited with error\n");
#endif
return -rc;
}
}
/* For 10-bit adresses the last byte should only be written for a
write operation */
rc = RTEMS_SUCCESSFUL;
if (addr <= 0x7f || rw == 0) {
addr_byte = (addr << 1) | (rw ? 1 : 0);
prv_ptr->reg_ptr->tdrd = addr_byte;
prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
prv_ptr->sendstart = 0;
/* Wait for transfer to complete */
rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
if (rc != RTEMS_SUCCESSFUL) {
#if defined(DEBUG)
printk("exited with error\n");
#endif
return -rc;
}
}
#if defined(DEBUG)
printk("exited\n");
#endif
return rc;
}
static int gr_i2cmst_read_bytes(rtems_libi2c_bus_t *bushdl,
unsigned char *bytes, int nbytes)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
unsigned char *buf = bytes;
rtems_status_code rc;
unsigned char expected_sts = GRI2C_STATUS_IDLE;
#if defined(DEBUG)
printk("gr_i2cmst_read_bytes called, nbytes = %d...", nbytes);
#endif
while (nbytes-- > 0) {
if (nbytes == 0) {
/* Respond with NAK to end sequential read */
prv_ptr->reg_ptr->cmdsts = (GRI2C_CMD_RD | GRI2C_CMD_ACK |
prv_ptr->sendstart);
expected_sts = GRI2C_STS_RXACK;
} else {
prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_RD | prv_ptr->sendstart;
}
prv_ptr->sendstart = 0;
/* Wait until end of transfer */
rc = gr_i2cmst_wait(prv_ptr, expected_sts);
if (rc != RTEMS_SUCCESSFUL) {
return -rc;
#if defined(DEBUG)
printk("exited with error\n");
#endif
}
*buf++ = prv_ptr->reg_ptr->tdrd;
}
#if defined(DEBUG)
printk("exited\n");
#endif
return buf - bytes;
}
static int gr_i2cmst_write_bytes(rtems_libi2c_bus_t *bushdl,
unsigned char *bytes, int nbytes)
{
gr_i2cmst_prv_t *prv_ptr = &(((gr_i2cmst_desc_t *)(bushdl))->prv);
unsigned char *buf = bytes;
rtems_status_code rc;
#if defined(DEBUG)
printk("gr_i2cmst_write_bytes called, nbytes = %d...", nbytes);
#endif
while (nbytes-- > 0) {
#if defined(DEBUG)
printk("writing byte 0x%02X...", *buf);
#endif
prv_ptr->reg_ptr->tdrd = *buf++;
prv_ptr->reg_ptr->cmdsts = GRI2C_CMD_WR | prv_ptr->sendstart;
prv_ptr->sendstart = 0;
/* Wait for transfer to complete */
rc = gr_i2cmst_wait(prv_ptr, GRI2C_STATUS_IDLE);
if (rc != RTEMS_SUCCESSFUL) {
#if defined(DEBUG)
printk("exited with error\n");
#endif
return -rc;
}
}
#if defined(DEBUG)
printk("exited\n");
#endif
return buf - bytes;
}
static rtems_libi2c_bus_ops_t gr_i2cmst_ops = {
init: gr_i2cmst_init,
send_start: gr_i2cmst_send_start,
send_stop: gr_i2cmst_send_stop,
send_addr: gr_i2cmst_send_addr,
read_bytes: gr_i2cmst_read_bytes,
write_bytes: gr_i2cmst_write_bytes,
};
static gr_i2cmst_desc_t gr_i2cmst_desc = {
{ /* rtems_libi2c_bus_t */
ops : &gr_i2cmst_ops,
size : sizeof(gr_i2cmst_ops),
},
{ /* gr_i2cmst_prv_t, private data */
reg_ptr : NULL,
sysfreq : 40000,
}
};
/* Scans for I2CMST core and initalizes i2c library */
rtems_status_code leon_register_i2c(amba_confarea_type *abus)
{
#if defined(DEBUG)
printk("leon_register_i2c called...");
#endif
int rc;
int device_found = 0;
amba_apb_device apbi2cmst;
/* Scan AMBA bus for I2CMST core */
device_found = amba_find_apbslv(abus, VENDOR_GAISLER, GAISLER_I2CMST,
&apbi2cmst);
if (device_found == 1) {
/* Initialize i2c library */
rc = rtems_libi2c_initialize();
if (rc < 0) {
#if defined(DEBUG)
printk("rtems_libi2x_initialize failed, exiting...\n");
#endif
return rc;
}
gr_i2cmst_desc.prv.reg_ptr = (gr_i2cmst_regs_t *)apbi2cmst.start;
/* Detect system frequency, same as in apbuart_initialize */
#ifndef SYS_FREQ_kHZ
#if defined(LEON3)
/* LEON3: find timer address via AMBA Plug&Play info */
{
amba_apb_device gptimer;
LEON3_Timer_Regs_Map *tregs;
if (amba_find_apbslv(abus,VENDOR_GAISLER,
GAISLER_GPTIMER,&gptimer) == 1 ) {
tregs = (LEON3_Timer_Regs_Map *)gptimer.start;
gr_i2cmst_desc.prv.sysfreq = (tregs->scaler_reload+1)*1000;
} else {
gr_i2cmst_desc.prv.sysfreq = 40000; /* Default to 40MHz */
}
}
#elif defined(LEON2)
/* LEON2: use hardcoded address to get to timer */
{
LEON_Register_Map *regs = (LEON_Register_Map *)0x80000000;
gr_i2cmst_desc.prv.sysfreq = (regs->Scaler_Reload+1)*1000;
}
#else
#error CPU not supported for I2CMST driver */
#endif
#else
/* Use hardcoded frequency */
gr_i2cmst_desc.prv.sysfreq = SYS_FREQ_kHZ;
#endif
rc = rtems_libi2c_register_bus("/dev/i2c1", &gr_i2cmst_desc.bus_desc);
if (rc < 0) {
#if defined(DEBUG)
printk("rtems_libi2c_register_bus failed, exiting..\n");
#endif
return -rc;
}
}
#if defined(DEBUG)
printk("exited\n");
#endif
return 0;
}

View File

@@ -58,6 +58,8 @@ extern "C" {
#define GAISLER_ETHMAC 0x1D
#define GAISLER_SPACEWIRE 0x1f
#define GAISLER_AHB2AHB 0x20
#define GAISLER_I2CMST 0x28
#define GAISLER_GRSPW2 0x29
#define GAISLER_GRHCAN 0x34
#define GAISLER_GRFIFO 0x35
#define GAISLER_GRPULSE 0x37

View File

@@ -0,0 +1,77 @@
/*
* Driver for GRLIB port of OpenCores I2C-master
*
* COPYRIGHT (c) 2007 Gaisler Research
* with parts from the RTEMS MPC83xx I2C driver (c) 2007 Embedded Brains GmbH.
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* This file contains the driver declarations
*/
#ifndef _I2CMST_H
#define _I2CMST_H
#include <rtems/libi2c.h>
#include <ambapp.h>
#ifdef __cplusplus
extern "C" {
#endif
/* I2C-master operational registers */
typedef struct gr_i2cmst_regs {
volatile unsigned int prescl; /* Prescale register */
volatile unsigned int ctrl; /* Control register */
volatile unsigned int tdrd; /* Transmit and Receive registers */
volatile unsigned int cmdsts; /* Command and Status registers */
} gr_i2cmst_regs_t;
/* Control (CTRL) register */
#define GRI2C_CTRL_EN 0x00000080 /* Enable core */
#define GRI2C_CTRL_IEN 0x00000040 /* Interrupt enable */
/* Command (CMD) register */
#define GRI2C_CMD_STA 0x00000080 /* Generate START condition */
#define GRI2C_CMD_STO 0x00000040 /* Generate STOP condition */
#define GRI2C_CMD_RD 0x00000020 /* Read from slave */
#define GRI2C_CMD_WR 0x00000010 /* Write to slave */
#define GRI2C_CMD_ACK 0x00000008 /* Acknowledge */
#define GRI2C_CMD_IACK 0x00000001 /* Interrupt acknowledge */
/* Status (STS) register */
#define GRI2C_STS_RXACK 0x00000080 /* Receive acknowledge */
#define GRI2C_STS_BUSY 0x00000040 /* I2C-bus busy */
#define GRI2C_STS_AL 0x00000020 /* Arbitration lost */
#define GRI2C_STS_TIP 0x00000002 /* Transfer in progress */
#define GRI2C_STS_IF 0x00000001 /* Interrupt flag */
#define GRI2C_STATUS_IDLE 0x00000000
/* The OC I2C core will perform a write after a start unless the RD bit
in the command register has been set. Since the rtems framework has
a send_start function we buffer that command and use it when the first
data is written. The START is buffered in the sendstart member below */
typedef struct gr_i2cmst_prv {
gr_i2cmst_regs_t *reg_ptr;
unsigned int sysfreq; /* System clock frequency in kHz */
unsigned char sendstart; /* START events are buffered here */
/* rtems_irq_number irq_number; */
/* rtems_id irq_sema_id; */
} gr_i2cmst_prv_t;
typedef struct gr_i2cmst_desc {
rtems_libi2c_bus_t bus_desc;
gr_i2cmst_prv_t prv;
} gr_i2cmst_desc_t;
/* Scans for I2CMST core and initalizes i2c library */
rtems_status_code leon_register_i2c(amba_confarea_type *abus);
#ifdef __cplusplus
}
#endif
#endif /* _I2CMST_H */