summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/i2c/i2cmst.c
blob: 02710e1b84cc1964481ec305138ff68f51e05194 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                                       
                                        









                                                        
                  
























































































                                                                           

      














                                                                        
      















                                                                        
                                                               





                                                    
 








































                                                                   
                                                           
















                                                                    
                                                                   


















                                               
                                                            






                                                                    
      



                                         
      





                                                                 
 


























                                               
                                    




                                       
 


                                                      
                                                            






                                        
                                   
 
                                     

                                                                         

                          
 









                                                                     
 


                                                                
                                                               
         
                                         
                                     
 

                                                                    
                                                         

                                                                       
                                                                      























                                                                          
 




                     
/*
 * 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.org/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 <grlib.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(struct ambapp_bus *abus)
{
#if defined(DEBUG)
  printk("leon_register_i2c called...");
#endif

  int rc;
  int device_found = 0;
  struct ambapp_apb_info apbi2cmst;

  /* Scan AMBA bus for I2CMST core */
  device_found = ambapp_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 */
	{
	  struct ambapp_apb_info gptimer;
	  struct gptimer_regs *tregs;

	  if (ambapp_find_apbslv(abus, VENDOR_GAISLER,
				 GAISLER_GPTIMER, &gptimer) == 1 ) {
	    tregs = (struct gptimer_regs *)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;
}