/*
Copyright 2005, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

- Neither the name of Freie Universitaet Berlin (FUB) nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

This software is provided by FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

This implementation was developed by the CST group at the FUB.

For documentation and questions please use the web site
http://scatterweb.mi.fu-berlin.de and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2005
*/

/**
 * Part of the source code from ScatterWeb 2.2 (ScatterWeb.{Data,System}.c)
 * released by Freie Universitaet Berlin has been reworked and
 * reformatted to fit the Contiki ESB port.
 */

#include "contiki-conf.h"
#include "dev/ds1629.h"
#include <io.h>

#define SDA_HIGH  (P5OUT |= 0x01) /* RTC data line high */
#define SDA_LOW   (P5OUT &= 0xFE) /* RTC data line low */
#define SCL_HIGH  (P5OUT |= 0x02) /* RTC clock line high */
#define SCL_LOW   (P5OUT &= 0xFD) /* RTC clock line low */
#define BUS_READ         0x9F
#define BUS_WRITE        0x9E
#define ACC_CSR          0xAC     /* Access Configuration/Control Register */
#define ACC_CLOCK        0xC0     /* Access Clock Register */
#define ACC_CLOCK_ALARM  0xC7     /* Access Clock Alarm Register */
#define ACC_TH           0xA1     /* Access Thermostat Setpoint High */
#define ACC_TL           0xA2     /* Access Thermostat Setpoint Low */
#define ACC_CSRAM        0x17     /* Access Clock 32 byte SRAM */
#define ACC_RT           0xAA     /* Access Read Temperatur Register */
#define CSR_OS1         (0x80)
#define CSR_OS0         (0x40)
#define CSR_A1          (0x20)
#define CSR_A0          (0x10)
#define CSR_CNV         (0x04)
#define CSR_POL         (0x02)
#define CSR_1SH         (0x01)
#define CSR_DEFAULT     (CSR_OS1 + CSR_OS0 + CSR_A1 + CSR_CNV + CSR_1SH + CSR_POL)

/**
 *  Temperature type (built on a signed int). It's a signed (twos complement)
 *  fixed point value with 8 bits before comma and 7 bits after. So Bit 15 is
 *  sign, Bit14-7 is before comma and Bit 6-0 after comma.
 *
 * @since         2.0
 */
typedef union { unsigned int u; signed int s; } temp_t;

/*--------------------------------------------------------------------------*/
/* Puts the start condition on bus. */
static void
cl_start(void)
{
  P5DIR |= 0x03; /* ensure: P50(SDA), P51(SCL) output */
  SCL_LOW;  _NOP(); _NOP();
  SDA_HIGH; _NOP(); _NOP();
  SCL_HIGH; _NOP(); _NOP();
  SDA_LOW;  _NOP(); _NOP();
  SCL_LOW;  _NOP(); _NOP();
}
/*--------------------------------------------------------------------------*/
/* Puts the stop condition on bus. */
static void
cl_stop()
{
  SCL_LOW;  _NOP(); _NOP();
  SDA_LOW;  _NOP(); _NOP();
  SCL_HIGH; _NOP(); _NOP();
  SDA_HIGH; _NOP(); _NOP();
  SCL_LOW;  _NOP(); _NOP();
  P5DIR &= ~0x03;
}
/*--------------------------------------------------------------------------*/
/* Writes a byte on the bus, returns the acknowledge bit. */
static u16_t
cl_writeOnBus(u8_t byte)
{
  u16_t i, ack;
  for(i=0;i<8;i++) {
    if(byte & 0x80) SDA_HIGH; else SDA_LOW;
    SCL_HIGH;
    byte = byte << 1; _NOP();
    SCL_LOW; _NOP();
  }
  /* check ack */
  P5DIR &= 0xFE;                              /* P50(SDA) input */
  SCL_HIGH;
  if(P5IN & 0x01) ack = 0; else ack = 1;      /* test if ack=0, else error */
  _NOP();
  SCL_LOW;
  P5DIR |= 0x01;                              /* P50(SDA) output */
  return ack;
}
/*--------------------------------------------------------------------------*/
static u8_t
cl_readFromBus(u16_t ack)
{
  u16_t i;
  u8_t byte = 0;
  P5DIR &= 0xFE;                              /* P50(SDA) input */
  for(i=0;i<8;i++) {
    byte = byte << 1;
    SCL_HIGH;
    if(P5IN & 0x01) byte |= 0x01; else byte &= 0xFE;
    SCL_LOW;
  }
  P5DIR |= 0x01;                              /* P50(SDA) output */
  if(ack) SDA_LOW; else SDA_HIGH;
  SCL_HIGH;
  SCL_LOW;
  return byte;
}
/*--------------------------------------------------------------------------*/
static u16_t
getReg16bit(u8_t acc, u16_t bitmask)
{
  u16_t config = 0;
  do cl_start();
  while(!cl_writeOnBus(BUS_WRITE));
  cl_writeOnBus(acc);
  cl_start();
  cl_writeOnBus(BUS_READ);
  config = cl_readFromBus(1);
  config = config << 8;
  config += cl_readFromBus(0);
  cl_stop();
  config &= bitmask;
  _NOP();
  _NOP();
  return config;
}
/*--------------------------------------------------------------------------*/
/* Only first 8 bit of Configuration Status Register can be set */
static void
setCSReg(u8_t setting)
{
  do cl_start();
  while(!cl_writeOnBus(BUS_WRITE));
  cl_writeOnBus(ACC_CSR);
  cl_writeOnBus(setting);
  cl_stop();
  _NOP();
  _NOP();
  _NOP();
  _NOP();
}
/*--------------------------------------------------------------------------*/
static void
System_startConversion(void)
{
  do cl_start();                    /* do start until BUS_WRITE is acked */
  while(!cl_writeOnBus(BUS_WRITE)); /* control byte */
  cl_writeOnBus(0xEE);              /* start conversion */
  cl_stop();
}
/*--------------------------------------------------------------------------*/
/* RTC initialization. Initializes RTC with ::CSR_DEFAULT. */
static void
initClock(void)
{
  u8_t csr = getReg16bit(ACC_CSR,0xFF00) >> 8;
  if(csr!=CSR_DEFAULT) setCSReg(CSR_DEFAULT); /* if desired config isnt in clock => set it */
  /* IMPORTANT: Ensure quartz is generating 32768 Hz */
  /* (sometimes CH bit gets set when clock is read while reset) */
  do cl_start();                    /* Do start until BUS_WRITE is acked. */
  while(!cl_writeOnBus(BUS_WRITE)); /* Send control byte */
  cl_writeOnBus(ACC_CLOCK);         /* Send command byte ::ACC_CLOCK. */
  cl_writeOnBus(0x00);              /* Send starting address 0x00. */
  cl_writeOnBus(0x00);              /* Set CH to 0, tseconds and seconds will also be reset! */
  cl_stop();                        /* Stop condition. */
}
/*--------------------------------------------------------------------------*/
void
ds1629_init()
{
  initClock();
}
/*--------------------------------------------------------------------------*/
void
ds1629_start()
{
  System_startConversion();
}
/*--------------------------------------------------------------------------*/
signed int
ds1629_temperature()
{
  temp_t temperature;

  ds1629_start();

  temperature.u = getReg16bit(ACC_RT,0xFFFF);
  return temperature.s;
}
