262 lines
7.1 KiB
C
262 lines
7.1 KiB
C
/***************************************************************************
|
|
* Copyright (C) 02/2008 by Olaf Rempel *
|
|
* razzor@kopf-tisch.de *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; version 2 of the License *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
|
|
#include "AT91SAM7S256.h"
|
|
#include "board.h"
|
|
#include "at91_twi.h"
|
|
#include "at91_pio.h"
|
|
|
|
/*
|
|
* undocumented TWI_SR flags, at least OVRE seems to be present on a sam7s256
|
|
* taken from linux-2.6.24/include/asm-arm/arch-at91/at91_twi.h
|
|
*/
|
|
#define AT91_TWI_OVRE (1<<6) /* Overrun */
|
|
#define AT91_TWI_UNRE (1<<7) /* Underrun */
|
|
|
|
/*
|
|
* while ((cldiv = ((MCK / (2 * TWI)) -3) / (1 << ckdiv)) > 255)
|
|
* ckdiv++ ;
|
|
*
|
|
* works for TWI >= 100kHz
|
|
*/
|
|
#define TWI_CLK(x) (((MCK / (2 * (x))) -3)<<8 | ((MCK / (2 * (x))) -3))
|
|
|
|
enum twi_states {
|
|
TWI_IDLE = 0x00,
|
|
TWI_ERROR,
|
|
TWI_GENERIC_CMD = 0x10,
|
|
TWI_BLMC_UPDATE = 0x20,
|
|
};
|
|
|
|
static volatile uint32_t twi_state = TWI_IDLE;
|
|
static uint8_t *twi_data;
|
|
static uint32_t twi_size;
|
|
static uint32_t twi_count;
|
|
|
|
static void twi_isr(void)
|
|
{
|
|
/* get status */
|
|
uint32_t status = *AT91C_TWI_SR;
|
|
status &= *AT91C_TWI_IMR;
|
|
|
|
/* NACK - disable all interrupts and go to state TWI_ERROR */
|
|
if (status & AT91C_TWI_NACK) {
|
|
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
|
twi_state = TWI_ERROR;
|
|
|
|
// TODO: TWI_BLMC_UPDATE?
|
|
return;
|
|
}
|
|
|
|
/* tx register ready for new data */
|
|
if (status & AT91C_TWI_TXRDY) {
|
|
if (twi_count != twi_size) {
|
|
/* feed next byte */
|
|
*AT91C_TWI_THR = twi_data[twi_count++];
|
|
|
|
} else {
|
|
/* wait for TXCOMP */
|
|
*AT91C_TWI_IDR = AT91C_TWI_RXRDY | AT91C_TWI_TXRDY;
|
|
*AT91C_TWI_IER = AT91C_TWI_TXCOMP | AT91C_TWI_NACK;
|
|
}
|
|
}
|
|
|
|
/* rx register has data */
|
|
if (status & AT91C_TWI_RXRDY) {
|
|
/* get data */
|
|
twi_data[twi_count++] = *AT91C_TWI_RHR;
|
|
|
|
/* transfer complete? */
|
|
if (twi_count == twi_size) {
|
|
/* send STOP and wait for TXCOMP */
|
|
*AT91C_TWI_CR = AT91C_TWI_STOP;
|
|
*AT91C_TWI_IDR = AT91C_TWI_TXRDY;
|
|
*AT91C_TWI_IER = AT91C_TWI_TXCOMP | AT91C_TWI_NACK;
|
|
}
|
|
}
|
|
|
|
/* transfer really complete? */
|
|
if (status & AT91C_TWI_TXCOMP) {
|
|
|
|
/* are we doing a blmc update? */
|
|
if (twi_state == TWI_BLMC_UPDATE) {
|
|
uint32_t addr = (*AT91C_TWI_MMR >> 16) & 0x7F;
|
|
if (addr != TWI_ADDR_BL4) {
|
|
/* increase address */
|
|
*AT91C_TWI_MMR += (1<<16);
|
|
|
|
/* send next value to next blmc */
|
|
*AT91C_TWI_THR = *twi_data++;
|
|
} else {
|
|
// TODO:
|
|
}
|
|
|
|
} else {
|
|
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
|
twi_state = TWI_IDLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t twi_setpwm(uint8_t *values)
|
|
{
|
|
if (twi_state != TWI_IDLE)
|
|
return 1;
|
|
|
|
twi_state = TWI_BLMC_UPDATE;
|
|
twi_data = values; /* data is not copied! */
|
|
twi_size = 0;
|
|
twi_count = 0;
|
|
|
|
*AT91C_TWI_MMR = (TWI_ADDR_BL1 << 16) | AT91C_TWI_IADRSZ_1_BYTE;
|
|
*AT91C_TWI_IADR = CMD_SET_PWM;
|
|
|
|
*AT91C_TWI_THR = *twi_data++;
|
|
*AT91C_TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t twi_cmd(uint8_t addr, struct blmc_cmd *cmd)
|
|
{
|
|
if (twi_state != TWI_IDLE)
|
|
return 1;
|
|
|
|
/* TODO: locking needed? */
|
|
twi_state = TWI_GENERIC_CMD;
|
|
|
|
/* read transfer, or write transfer with payload */
|
|
if (cmd->mode & BLMC_CMD_READ || cmd->size != 0) {
|
|
/* set address, direction, argument count and command bytes */
|
|
*AT91C_TWI_MMR = (addr << 16) | (cmd->mode & 0xFF) << 8;
|
|
*AT91C_TWI_IADR = cmd->cmd;
|
|
|
|
/* write transfer without payload */
|
|
} else {
|
|
/* use one cmd byte as payload (needed to start transfer) */
|
|
cmd->mode--;
|
|
*AT91C_TWI_MMR = (addr << 16) | (cmd->mode & 0xFF) << 8;
|
|
*AT91C_TWI_IADR = (cmd->cmd) >> 8;
|
|
}
|
|
|
|
/* isr needs data & size parameters */
|
|
twi_data = cmd->data;
|
|
twi_size = cmd->size;
|
|
twi_count = 0;
|
|
|
|
if (cmd->mode & BLMC_CMD_READ) {
|
|
*AT91C_TWI_CR = AT91C_TWI_START;
|
|
*AT91C_TWI_IER = AT91C_TWI_RXRDY | AT91C_TWI_NACK;
|
|
|
|
} else {
|
|
*AT91C_TWI_THR = (twi_size != 0) ? cmd->data[twi_count++] : (cmd->cmd & 0xFF);
|
|
*AT91C_TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
|
}
|
|
|
|
/*
|
|
* wait for end
|
|
* TODO: locking needed?
|
|
* TODO: timeout?
|
|
*/
|
|
while (twi_state != TWI_IDLE && twi_state != TWI_ERROR);
|
|
if (twi_state != TWI_IDLE) {
|
|
twi_state = TWI_IDLE;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void at91_twi_test(void)
|
|
{
|
|
uint32_t i;
|
|
for (i = TWI_ADDR_BL1; i <= TWI_ADDR_BL4; i++) {
|
|
struct blmc_cmd cmd = {
|
|
.cmd = CMD_BOOT_LOADER,
|
|
.mode = BLMC_CMD_WRITE | BLMC_CMD_0_ARG,
|
|
};
|
|
uint32_t ret = twi_cmd(i, &cmd);
|
|
printf("twi[0x%02lx](%ld) ", i, ret);
|
|
|
|
volatile uint32_t x;
|
|
for (x = 0; x < 200000; x++);
|
|
|
|
uint8_t buf[16];
|
|
cmd.cmd = CMD_GET_INFO,
|
|
cmd.mode = BLMC_CMD_READ | BLMC_CMD_0_ARG,
|
|
cmd.size = sizeof(buf),
|
|
cmd.data = buf,
|
|
ret = twi_cmd(i, &cmd);
|
|
printf("boot(%ld):'%s' ", ret, buf);
|
|
|
|
cmd.cmd = CMD_GET_SIGNATURE;
|
|
cmd.size = 4;
|
|
ret = twi_cmd(i, &cmd);
|
|
printf("sig(%ld):0x%02x%02x%02x ", ret, buf[0], buf[1], buf[2]);
|
|
|
|
cmd.cmd = CMD_BOOT_APPLICATION;
|
|
cmd.mode = BLMC_CMD_WRITE | BLMC_CMD_0_ARG;
|
|
cmd.size = 0;
|
|
ret = twi_cmd(i, &cmd);
|
|
|
|
for (x = 0; x < 200000; x++);
|
|
|
|
cmd.cmd = CMD_GET_INFO,
|
|
cmd.mode = BLMC_CMD_READ | BLMC_CMD_0_ARG,
|
|
cmd.size = sizeof(buf),
|
|
cmd.data = buf,
|
|
ret = twi_cmd(i, &cmd);
|
|
printf("app(%ld):'%s'\n\r", ret, buf);
|
|
}
|
|
}
|
|
|
|
void at91_twi_init(void)
|
|
{
|
|
/* enable Clock */
|
|
*AT91C_PMC_PCER = (1 << AT91C_ID_TWI);
|
|
|
|
/* SDA & SCL from Peripheral A, Open Drain, no Pullup */
|
|
AT91S_PIO *pio = AT91C_BASE_PIOA;
|
|
|
|
/* do a software reset (bus not connected) */
|
|
*AT91C_TWI_CR = AT91C_TWI_SWRST;
|
|
|
|
pio->PIO_MDER = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
|
pio->PIO_PPUDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
|
pio->PIO_ASR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
|
pio->PIO_PDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
|
|
|
|
/* set TWI Clock */
|
|
*AT91C_TWI_CWGR = TWI_CLK(400000); //| (5<<16);
|
|
|
|
/* disable all (known) interrupts */
|
|
*AT91C_TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY | AT91C_TWI_TXRDY | AT91C_TWI_NACK;
|
|
|
|
/* level triggered, own vector */
|
|
AT91S_AIC *aic = AT91C_BASE_AIC;
|
|
aic->AIC_SMR[AT91C_ID_TWI] = IRQPRIO_TWI | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
|
|
aic->AIC_SVR[AT91C_ID_TWI] = (uint32_t)twi_isr;
|
|
aic->AIC_IECR = (1 << AT91C_ID_TWI);
|
|
|
|
/* enable teh monster */
|
|
*AT91C_TWI_CR = AT91C_TWI_MSEN;
|
|
}
|