CARPLAY版本整理

This commit is contained in:
2025-01-21 16:49:37 +08:00
commit f0fb64e4e6
26542 changed files with 13719676 additions and 0 deletions

View File

@ -0,0 +1,363 @@
#include "FreeRTOS.h"
#include "board.h"
#include "chip.h"
#ifdef ADC_TOUCH
#include "touch.h"
#endif
#ifdef ADC_KEY
#include "keypad.h"
#endif
#define ADC_CTR 0x00
#define ADC_CFG 0x04
#define ADC_IMR 0x08
#define ADC_STA 0x0C
#define ADC_BAT 0x10
#define ADC_AUX0 0x14
#define ADC_AUX1 0x18
#define ADC_AUX2 0x1C
#define ADC_AUX3 0x20
#define ADC_AUX4 0x38
#define ADC_AUX5 0x40
#define ADC_AUX6 0x44
#define ADC_AUX7 0x4C
#define ADC_PANXZ1 0x24
#define ADC_PANXZ2 0x28
#define ADC_DBNCNT 0x2C
#define ADC_DETINTER 0x30
#define ADC_SCTR 0x34
#define ADC_CLK_FREQ 1000000
#define ADC_DEBOUNCE_CNT 0x10000
static void adc_set_deinter(uint32_t count)
{
/* dbncnt * freq_adc / (2 * freq_apb) */
int mincnt = ADC_DEBOUNCE_CNT * ulClkGetRate(CLK_ADC) / 2 / ulClkGetRate(CLK_APB);
writel(configMAX(mincnt, count), REGS_ADC_BASE + ADC_DETINTER);
}
void adc_channel_enable(eAdcChannel ch)
{
uint32_t ctr, imr;
configASSERT(ch >= ADC_CH_BAT && ch <= ADC_CH_AUX7);
ctr = readl(REGS_ADC_BASE + ADC_CTR);
imr = readl(REGS_ADC_BASE + ADC_IMR);
switch(ch) {
case ADC_CH_BAT:
ctr |= 1 << ADC_CH_BAT;
imr &= ~BAT_INT;
break;
case ADC_CH_TP:
ctr |= 1 << ADC_CH_TP;
imr &= ~(TP_START_INT | TP_STOP_INT | TP_VALUE_INT);
break;
case ADC_CH_AUX0:
ctr |= (1 << 8) | (1 << ADC_CH_AUX0);
imr &= ~(AUX0_START_INT | AUX0_STOP_INT | AUX0_VALUE_INT);
break;
case ADC_CH_AUX1:
ctr |= (1 << 9) | (1 << ADC_CH_AUX1);
imr &= ~(AUX1_START_INT | AUX1_STOP_INT | AUX1_VALUE_INT);
break;
case ADC_CH_AUX2:
ctr |= (1 << 10) | (1 << ADC_CH_AUX2);
imr &= ~(AUX2_START_INT | AUX2_STOP_INT | AUX2_VALUE_INT);
break;
case ADC_CH_AUX3:
ctr |= (1 << 11) | (1 << ADC_CH_AUX3);
imr &= ~(AUX3_START_INT | AUX3_STOP_INT | AUX3_VALUE_INT);
break;
case ADC_CH_AUX4:
ctr |= (1 << 24) | (1 << 20);
imr &= ~(AUX4_START_INT | AUX4_STOP_INT | AUX4_VALUE_INT);
break;
case ADC_CH_AUX5:
ctr |= (1 << 25) | (1 << 21);
imr &= ~(AUX5_START_INT | AUX5_STOP_INT | AUX5_VALUE_INT);
break;
case ADC_CH_AUX6:
ctr |= (1 << 26) | (1 << 22);
imr &= ~(AUX6_START_INT | AUX6_STOP_INT | AUX6_VALUE_INT);
break;
case ADC_CH_AUX7:
ctr |= (1 << 27) | (1 << 23);
imr &= ~(AUX7_START_INT | AUX7_STOP_INT | AUX7_VALUE_INT);
break;
}
writel(ctr, REGS_ADC_BASE + ADC_CTR);
writel(imr, REGS_ADC_BASE + ADC_IMR);
}
void adc_channel_disable(eAdcChannel ch)
{
uint32_t ctr, imr;
configASSERT(ch >= ADC_CH_BAT && ch <= ADC_CH_AUX7);
ctr = readl(REGS_ADC_BASE + ADC_CTR);
imr = readl(REGS_ADC_BASE + ADC_IMR);
switch(ch) {
case ADC_CH_BAT:
ctr &= ~(1 << ADC_CH_BAT);
imr |= BAT_INT;
break;
case ADC_CH_TP:
ctr &= ~(1 << ADC_CH_TP);
imr |= (TP_START_INT | TP_STOP_INT | TP_VALUE_INT);
break;
case ADC_CH_AUX0:
ctr &= ~((1 << 8) | (1 << ADC_CH_AUX0));
imr |= (AUX0_START_INT | AUX0_STOP_INT | AUX0_VALUE_INT);
break;
case ADC_CH_AUX1:
ctr &= ~((1 << 9) | (1 << ADC_CH_AUX1));
imr |= (AUX1_START_INT | AUX1_STOP_INT | AUX1_VALUE_INT);
break;
case ADC_CH_AUX2:
ctr &= ~((1 << 10) | (1 << ADC_CH_AUX2));
imr |= (AUX2_START_INT | AUX2_STOP_INT | AUX2_VALUE_INT);
break;
case ADC_CH_AUX3:
ctr &= ~((1 << 11) | (1 << ADC_CH_AUX3));
imr |= (AUX3_START_INT | AUX3_STOP_INT | AUX3_VALUE_INT);
break;
case ADC_CH_AUX4:
ctr &= ~((1 << 24) | (1 << 20));
imr |= (AUX4_START_INT | AUX4_STOP_INT | AUX4_VALUE_INT);
break;
case ADC_CH_AUX5:
ctr &= ~((1 << 25) | (1 << 21));
imr |= (AUX5_START_INT | AUX5_STOP_INT | AUX5_VALUE_INT);
break;
case ADC_CH_AUX6:
ctr &= ~((1 << 26) | (1 << 22));
imr |= (AUX6_START_INT | AUX6_STOP_INT | AUX6_VALUE_INT);
break;
case ADC_CH_AUX7:
ctr &= ~((1 << 27) | (1 << 23));
imr |= (AUX7_START_INT | AUX7_STOP_INT | AUX7_VALUE_INT);
break;
}
writel(ctr, REGS_ADC_BASE + ADC_CTR);
writel(imr, REGS_ADC_BASE + ADC_IMR);
}
static void adc_int_handler(void *para)
{
uint32_t status;
#ifdef ADC_TOUCH
uint32_t xpos, ypos;
#endif
//uint32_t value;
status = readl(REGS_ADC_BASE + ADC_STA);
writel(0, REGS_ADC_BASE + ADC_STA);
//printf("adc_int_handler status=0x%x.\n", status);
if (status & TP_START_INT) {
#ifdef ADC_TOUCH
TouchEventHandler(TOUCH_START_EVENT, 0, 0);
#endif
}
if (status & TP_STOP_INT) {
#ifdef ADC_TOUCH
TouchEventHandler(TOUCH_STOP_EVENT, 0, 0);
#endif
}
if (status & TP_VALUE_INT) {
#ifdef ADC_TOUCH
xpos = readl(REGS_ADC_BASE + ADC_PANXZ1);
ypos = readl(REGS_ADC_BASE + ADC_PANXZ2);
//printf("tp press %d, %d.\n", xpos, ypos);
TouchEventHandler(TOUCH_SAMPLE_EVENT, xpos, ypos);
#endif
}
if (status & AUX0_START_INT) {
#ifdef ADC_KEY
KeyEventHandler(KEY_START_EVENT, 0, 0);
#endif
}
if (status & AUX0_STOP_INT) {
#ifdef ADC_KEY
KeyEventHandler(KEY_STOP_EVENT, 0, 0);
#endif
}
if (status & AUX0_VALUE_INT) {
#ifdef ADC_KEY
uint32_t value = readl(REGS_ADC_BASE + ADC_AUX0);
KeyEventHandler(KEY_SAMPLE_EVENT, value, 0);
#endif
}
#if 0
if (status & AUX1_START_INT) {
}
if (status & AUX1_STOP_INT) {
}
if (status & AUX1_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX1);
}
if (status & AUX2_START_INT) {
}
if (status & AUX2_STOP_INT) {
}
if (status & AUX2_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX2);
}
if (status & AUX3_START_INT) {
}
if (status & AUX3_STOP_INT) {
}
if (status & AUX3_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX3);
}
if (status & AUX4_START_INT) {
}
if (status & AUX4_STOP_INT) {
}
if (status & AUX4_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX4);
}
if (status & AUX5_START_INT) {
}
if (status & AUX5_STOP_INT) {
}
if (status & AUX5_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX5);
}
if (status & AUX6_START_INT) {
}
if (status & AUX6_STOP_INT) {
}
if (status & AUX6_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX6);
}
if (status & AUX7_START_INT) {
}
if (status & AUX7_STOP_INT) {
}
if (status & AUX7_VALUE_INT) {
value = readl(REGS_ADC_BASE + ADC_AUX7);
}
if (status & BAT_INT) {
}
#endif
}
void adc_init(void)
{
vSysctlConfigure(SYS_ANA1_CFG, 7, 1, 0); // ref : 3.3v
vClkSetRate(CLK_ADC, ADC_CLK_FREQ);
/* reset adc modulex */
writel(readl(REGS_ADC_BASE + ADC_CTR) | 1, REGS_ADC_BASE + ADC_CTR);
/* disable all adc channel */
writel(readl(REGS_ADC_BASE + ADC_CTR) & ~0x7e, REGS_ADC_BASE + ADC_CTR);
/* disable and clear irq */
writel(0xffffffff, REGS_ADC_BASE + ADC_IMR);
writel(0, REGS_ADC_BASE + ADC_STA);
/* set debounce count */
writel(ADC_DEBOUNCE_CNT, REGS_ADC_BASE + ADC_DBNCNT);
adc_set_deinter(50);
request_irq(ADC_IRQn, 0, adc_int_handler, NULL);
}
unsigned int adc_get_channel_value(int ch)
{
int i;
configASSERT(ch >= ADC_CH_AUX0 && ch <= ADC_CH_AUX7);
adc_channel_disable(ADC_CH_TP);
for (i = ADC_CH_AUX0; i <= ADC_CH_AUX7; i++) {
if (ch == i)
adc_channel_enable(i);
else
adc_channel_disable(i);
}
vTaskDelay(pdMS_TO_TICKS(10));
if(ch<=ADC_CH_AUX3)
{
return readl(REGS_ADC_BASE + ADC_AUX0 + 4 * (ch - ADC_CH_AUX0));
}
else
{
return readl(REGS_ADC_BASE + ADC_AUX4 + 4 * (ch - ADC_CH_AUX4));
}
}

View File

@ -0,0 +1,128 @@
/**
* \file
*
* Implementation of Ark Interrupt Controller (AIC) controller.
*
*/
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "chip.h"
#include <stdint.h>
#include <assert.h>
#include <string.h>
#define ICSET 0x00
#define ICPEND 0x04
#define ICMODE 0x08
#define ICMASK 0x0C
#define ICLEVEL 0x10
#define IRQPRIO 0x24
#define IRQISPR 0x3C
#define IRQISPC 0x40
#define IVEC_ADDR 0x78
typedef struct {
ISRFunction_t handler;
void *handler_param;
}IrqDesc_t;
static IrqDesc_t irq_descs[MAX_IRQ_NUM];
static volatile uint8_t interrupt_nest = 0;
/*----------------------------------------------------------------------------
* Exported functions
*----------------------------------------------------------------------------*/
void AIC_Initialize(void)
{
memset(irq_descs, 0, sizeof(irq_descs));
writel(0x8, REGS_AIC_BASE + ICSET);
vTimerUdelay(10);
writel(0x5, REGS_AIC_BASE + ICSET);
writel(0x0, REGS_AIC_BASE + ICMODE);
/* set irq 15-8 highest priority */
writel((1 << 12) | (0xf << 8) | (3 << 6) | (2 << 4) | (0 << 2) | 1, REGS_AIC_BASE + IRQPRIO);
/* set round-robin priority policy */
//writel(0, REGS_AIC_BASE + IRQPRIO);
writel(0xffffffff, REGS_AIC_BASE + ICMASK);
writel(0xffffffff, REGS_AIC_BASE + ICLEVEL);
writel(0xffffffff, REGS_AIC_BASE + IRQISPC);
}
/**
* \brief Enables interrupts coming from the given (unique) source (ID_xxx).
*
* \param source Interrupt source to enable.
*/
void AIC_EnableIT(uint32_t source)
{
writel(readl(REGS_AIC_BASE + ICMASK) & ~(1 << source), REGS_AIC_BASE + ICMASK);
}
/**
* \brief Disables interrupts coming from the given (unique) source (ID_xxx).
*
* \param source Interrupt source to disable.
*/
void AIC_DisableIT(uint32_t source)
{
writel(readl(REGS_AIC_BASE + ICMASK) | (1 << source), REGS_AIC_BASE + ICMASK);
}
int32_t request_irq(uint32_t irq_source, int32_t priority, ISRFunction_t func, void *param)
{
if (irq_source > MAX_IRQ_NUM - 1) {
return -1;
}
portENTER_CRITICAL();
irq_descs[irq_source].handler = func;
irq_descs[irq_source].handler_param = param;
AIC_EnableIT(irq_source);
portEXIT_CRITICAL();
return 0;
}
int32_t free_irq(uint32_t irq_source)
{
if (irq_source > MAX_IRQ_NUM - 1) {
return -1;
}
portENTER_CRITICAL();
irq_descs[irq_source].handler = NULL;
irq_descs[irq_source].handler_param = NULL;
AIC_DisableIT(irq_source);
portEXIT_CRITICAL();
return 0;
}
void AIC_IrqHandler(void)
{
int32_t source;
interrupt_nest++;
source = readl(REGS_AIC_BASE + IVEC_ADDR) >> 2;
if (irq_descs[source].handler)
irq_descs[source].handler(irq_descs[source].handler_param);
writel(1 << source, REGS_AIC_BASE + IRQISPC);
interrupt_nest--;
}
uint8_t interrupt_get_nest(void)
{
uint8_t ret;
UBaseType_t uxSavedInterruptStatus;
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
ret = interrupt_nest;
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus);
return ret;
}

View File

@ -0,0 +1,650 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#include "audio.h"
enum
{
REPLAY_EVT_NONE = 0x00,
REPLAY_EVT_START = 0x01,
REPLAY_EVT_STOP = 0x02,
};
struct audio_queue_data
{
uint8_t *data;
int size;
};
struct audio_device *audio_dev[I2S_NUMS] = {NULL, NULL};
static int audio_send_replay_frame(struct audio_device *audio)
{
int result = 0;
uint8_t *data;
size_t dst_size, src_size;
uint16_t position, remain_bytes = 0, index = 0;
struct audio_buf_info *buf_info;
struct audio_queue_data qdata;
configASSERT(audio != NULL);
buf_info = &audio->replay->buf_info;
/* save current pos */
position = audio->replay->pos;
dst_size = buf_info->block_size;
/* check replay queue is empty */
if (xQueueIsQueueEmptyFromISR(audio->replay->queue) == pdTRUE)
{
/* ack stop event */
if (audio->replay->event & REPLAY_EVT_STOP) {
xQueueSendFromISR(audio->replay->cmp, NULL, 0);
return 0;
}
/* send zero frames */
memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
}
else
{
memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);
/* copy data from memory pool to hardware device fifo */
while (index < dst_size)
{
result = xQueuePeekFromISR(audio->replay->queue, &qdata);
if (result != pdTRUE)
{
TRACE_DEBUG("under run %d, remain %d", audio->replay->pos, remain_bytes);
audio->replay->pos -= remain_bytes;
audio->replay->pos += dst_size;
audio->replay->pos %= buf_info->total_size;
audio->replay->read_index = 0;
result = -1;
break;
}
data = qdata.data;
src_size = qdata.size;
remain_bytes = configMIN((dst_size - index), (src_size - audio->replay->read_index));
memcpy(&buf_info->buffer[audio->replay->pos],
&data[audio->replay->read_index], remain_bytes);
index += remain_bytes;
audio->replay->read_index += remain_bytes;
audio->replay->pos += remain_bytes;
audio->replay->pos %= buf_info->total_size;
if (audio->replay->read_index == src_size)
{
audio->replay->read_index = 0;
xQueueReceiveFromISR(audio->replay->queue, &qdata, 0);
for (int i = 0; i < AUDIO_REPLAY_MP_BLOCK_COUNT; i++) {
if (qdata.data == audio->replay->mempool + AUDIO_REPLAY_MP_BLOCK_SIZE * i) {
audio->replay->mpstatus[i] = 0;
break;
}
}
}
}
}
if (audio->ops->transmit != NULL)
{
if (audio->ops->transmit(audio, &buf_info->buffer[position], NULL, dst_size) != dst_size)
result = -1;
}
return result;
}
static int audio_receive_record_frame(struct audio_device *audio)
{
struct audio_buf_info *buf_info;
size_t dst_size;
int read_pos;
int result = 0;
configASSERT(audio != NULL);
/* ack stop event */
if (audio->record->event & REPLAY_EVT_STOP) {
xQueueSendFromISR(audio->record->cmp, NULL, 0);
return 0;
}
buf_info = &audio->record->buf_info;
if(buf_info->buffer && buf_info->total_size)
{
if(audio->record->remain_size <= 0)
{
if(audio->record->read_index == buf_info->total_size)
{
if(audio->record->receive_cb)
audio->record->receive_cb(audio);
}
audio->record->remain_size = buf_info->total_size;
audio->record->read_index = 0;
}
read_pos = audio->record->read_index;
dst_size = buf_info->block_size;
if(audio->record->remain_size < buf_info->block_size)
dst_size = audio->record->remain_size;
if(audio->ops->transmit)
{
if(audio->ops->transmit(audio, NULL, &buf_info->buffer[read_pos], dst_size) != dst_size)
{
printf("%s() transmit failed.\n", __func__);
result = -1;
}
else
{
audio->record->read_index += dst_size;
audio->record->remain_size -= dst_size;
if(audio->record->remain_size < 0)
{
printf("%s() Invalid remain_size:%d.\n", __func__, audio->record->remain_size);
audio->record->remain_size = 0;
}
}
}
}
else
{
if(!buf_info->buffer)
printf("%s() record buffer is NULL.\n", __func__);
if(!buf_info->total_size)
printf("%s() record buffer size is 0.\n", __func__);
result = -1;
}
return result;
}
static int audio_flush_replay_frame(struct audio_device *audio)
{
int result = 0;
if (audio->replay->write_index)
{
struct audio_queue_data qdata = {audio->replay->write_data, audio->replay->write_index};
result = xQueueSend(audio->replay->queue, &qdata, portMAX_DELAY);
audio->replay->write_index = 0;
}
return result;
}
static int audio_replay_start(struct audio_device *audio)
{
int result = 0;
if (audio->replay->activated != true)
{
/* start playback hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = true;
TRACE_DEBUG("start audio replay device");
}
return result;
}
static int audio_replay_stop(struct audio_device *audio)
{
int result = 0;
if (audio->replay->activated == true)
{
/* flush replay remian frames */
audio_flush_replay_frame(audio);
/* notify irq(or thread) to stop the data transmission */
audio->replay->event |= REPLAY_EVT_STOP;
/* waiting for the remaining data transfer to complete */
xQueueReset(audio->replay->cmp);
xQueueReceive(audio->replay->cmp, NULL, pdMS_TO_TICKS(2000));
audio->replay->event &= ~REPLAY_EVT_STOP;
/* stop playback hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_REPLAY);
audio->replay->activated = false;
TRACE_DEBUG("stop audio replay device");
}
return result;
}
static int audio_record_start(struct audio_device *audio)
{
int result = 0;
if (audio->record->activated != true)
{
/* start record hardware device */
if (audio->ops->start)
result = audio->ops->start(audio, AUDIO_STREAM_RECORD);
audio->record->activated = true;
TRACE_DEBUG("start audio record device");
}
return result;
}
static int audio_record_stop(struct audio_device *audio)
{
int result = 0;
if (audio->record->activated == true)
{
/* notify irq(or thread) to stop the data transmission */
audio->record->event |= REPLAY_EVT_STOP;
/* waiting for the remaining data transfer to complete */
xQueueReset(audio->record->cmp);
xQueueReceive(audio->record->cmp, NULL, pdMS_TO_TICKS(1000));
/* stop record hardware device */
if (audio->ops->stop)
result = audio->ops->stop(audio, AUDIO_STREAM_RECORD);
audio->record->event &= ~REPLAY_EVT_STOP;
audio->record->activated = false;
TRACE_DEBUG("stop audio record device");
}
return result;
}
static int audio_dev_init(struct audio_device *audio)
{
int result = 0;
configASSERT(audio != NULL);
/* initialize replay & record */
audio->replay = NULL;
audio->record = NULL;
/* initialize replay */
if ((audio->flag & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
struct audio_replay *replay = (struct audio_replay *) pvPortMalloc(sizeof(struct audio_replay));
if (replay == NULL)
return -ENOMEM;
memset(replay, 0, sizeof(struct audio_replay));
/* alloc mempool */
replay->mempool = pvPortMalloc(AUDIO_REPLAY_MP_BLOCK_SIZE * AUDIO_REPLAY_MP_BLOCK_COUNT);
if (!replay->mempool)
return -ENOMEM;
/* init queue for audio replay */
replay->queue = xQueueCreate(CFG_AUDIO_REPLAY_QUEUE_COUNT, sizeof(struct audio_queue_data));
/* init mutex lock for audio replay */
replay->lock = xSemaphoreCreateMutex();
replay->cmp = xQueueCreate(1, 0);
replay->activated = false;
audio->replay = replay;
/* get replay buffer information */
if (audio->ops->buffer_info)
audio->ops->buffer_info(audio, &audio->replay->buf_info, AUDIO_FLAG_REPLAY);
}
/* initialize record */
if ((audio->flag & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
struct audio_record *record = (struct audio_record *) pvPortMalloc(sizeof(struct audio_record));
//uint8_t *buffer;
if (record == NULL)
return -ENOMEM;
memset(record, 0, sizeof(struct audio_record));
/* init pipe for record*/
/* buffer = pvPortMalloc(AUDIO_RECORD_PIPE_SIZE);
if (buffer == NULL)
{
vPortFree(record);
TRACE_ERROR("malloc memory for for record pipe failed");
return -ENOMEM;
}
audio_pipe_init(&record->pipe, "record",
(int32_t)(RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD),
buffer,
RT_AUDIO_RECORD_PIPE_SIZE); */
record->cmp = xQueueCreate(1, 0);
record->activated = false;
audio->record = record;
/* get record buffer information */
if (audio->ops->buffer_info)
audio->ops->buffer_info(audio, &audio->record->buf_info, AUDIO_FLAG_RECORD);
}
/* initialize hardware configuration */
if (audio->ops->init)
audio->ops->init(audio);
return result;
}
struct audio_device *audio_dev_open(uint32_t oflag)
{
struct audio_device *audio = NULL;
#ifdef AUDIO_REPLAY_I2S
/* initialize the Rx/Tx structure according to open flag */
if ((oflag & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
audio = audio_dev[AUDIO_REPLAY_I2S];
if (audio && audio->replay->activated != true)
{
TRACE_DEBUG("open audio replay device, oflag = %x\n", oflag);
audio->replay->write_index = 0;
audio->replay->read_index = 0;
audio->replay->pos = 0;
audio->replay->event = REPLAY_EVT_NONE;
for (int i = 0; i < AUDIO_REPLAY_MP_BLOCK_COUNT; i++)
audio->replay->mpstatus[i] = 0;
}
}
#endif
#ifdef AUDIO_RECORD_I2S
if ((oflag & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
audio = audio_dev[AUDIO_RECORD_I2S];
if (audio && audio->record->activated != true)
{
TRACE_DEBUG("open audio record device ,oflag = %x\n", oflag);
audio->record->event = REPLAY_EVT_NONE;
audio->record->read_index = 0;
audio->record->remain_size = 0;
}
}
#endif
return audio;
}
int audio_dev_close(struct audio_device *audio, uint32_t oflag)
{
configASSERT(audio != NULL);
if ((oflag & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
/* stop replay stream */
audio_replay_stop(audio);
}
if ((oflag & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
/* stop record stream */
audio_record_stop(audio);
}
return 0;
}
int audio_dev_register_record_callback(struct audio_device *audio, int (*callback)(struct audio_device *audio))
{
configASSERT(audio != NULL);
if (((audio->flag & AUDIO_FLAG_RECORD) != AUDIO_FLAG_RECORD) || (audio->record == NULL))
return -EINVAL;
audio->record->receive_cb = callback;
return 0;
}
int audio_dev_record_set_param(struct audio_device *audio, uint8_t *buf, int size)
{
if(buf && ((size > 0) && (size%32 == 0))) //size should align with 32 bytes.
{
audio->record->buf_info.total_size = size;
audio->record->remain_size = audio->record->buf_info.total_size;
audio->record->buf_info.buffer = buf;
audio->record->remain_size = 0;
audio->record->read_index = 0;
return 0;
}
return -1;
}
int audio_dev_record_start(struct audio_device *audio)
{
configASSERT(audio != NULL);
if (audio->record->activated != true)
{
if(!audio->record->buf_info.buffer || !audio->record->buf_info.total_size)
{
printf("%s() Invalid buffer:%p or size:%d\n", __func__, audio->record->buf_info.buffer, audio->record->buf_info.total_size);
return -1;
}
audio->record->remain_size = 0;
audio->record->read_index = 0;
audio_record_start(audio);
audio->record->activated = true;
}
return 0;
}
int audio_dev_record_stop(struct audio_device *audio)
{
if (audio->flag == AUDIO_FLAG_RECORD)
{
/* stop record stream */
audio_record_stop(audio);
}
return 0;
}
size_t audio_dev_read(struct audio_device *audio, void *buffer, size_t size)
{
configASSERT(audio != NULL);
if ((audio->flag != AUDIO_FLAG_RECORD) || (audio->record == NULL))
return 0;
printf("%s() Invalid interface.\n", __func__);
return 0;//device_read(RT_DEVICE(&audio->record->pipe), pos, buffer, size);
}
size_t audio_dev_write(struct audio_device *audio, const void *buffer, size_t size)
{
uint8_t *ptr;
uint16_t block_size, remain_bytes, index = 0;
configASSERT(audio != NULL);
if (!((audio->flag & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY) || (audio->replay == NULL))
return 0;
/* push a new frame to replay data queue */
ptr = (uint8_t *)buffer;
block_size = AUDIO_REPLAY_MP_BLOCK_SIZE;
xSemaphoreTake(audio->replay->lock, portMAX_DELAY);
while (index < size)
{
/* request buffer from replay memory pool */
if (audio->replay->write_index % block_size == 0)
{
uint8_t *mpbuf = NULL;
uint32_t st = xTaskGetTickCount();
while(1) {
int i;
portENTER_CRITICAL();
for (i = 0; i < AUDIO_REPLAY_MP_BLOCK_COUNT; i++) {
if (!audio->replay->mpstatus[i]) {
mpbuf = audio->replay->mempool + AUDIO_REPLAY_MP_BLOCK_SIZE * i;
audio->replay->mpstatus[i] = 1;
break;
}
}
portEXIT_CRITICAL();
if (mpbuf)
break;
if (xTaskGetTickCount() - st > pdMS_TO_TICKS(1000)) {
printf("wait mempool free timeout.\n");
mpbuf = audio->replay->mempool;
for (i = 1; i < AUDIO_REPLAY_MP_BLOCK_COUNT; i++)
audio->replay->mpstatus[i] = 0;
break;
}
vTaskDelay(1);
}
audio->replay->write_data = mpbuf;
memset(audio->replay->write_data, 0, block_size);
}
/* copy data to replay memory pool */
remain_bytes = configMIN((block_size - audio->replay->write_index), (size - index));
memcpy(&audio->replay->write_data[audio->replay->write_index], &ptr[index], remain_bytes);
index += remain_bytes;
audio->replay->write_index += remain_bytes;
audio->replay->write_index %= block_size;
if (audio->replay->write_index == 0)
{
struct audio_queue_data qdata = {audio->replay->write_data, block_size};
xQueueSend(audio->replay->queue, &qdata, portMAX_DELAY);
}
}
xSemaphoreGive(audio->replay->lock);
/* check replay state */
if (audio->replay->activated != true)
{
audio_replay_start(audio);
audio->replay->activated = true;
}
return index;
}
int audio_dev_configure(struct audio_device *audio, struct audio_caps *caps)
{
int result = 0;
if (audio->ops->configure != NULL)
{
result = audio->ops->configure(audio, caps);
}
return result;
}
int audio_register(struct audio_device *audio)
{
int result = 0;
configASSERT(audio != NULL);
audio->rx_indicate = NULL;
audio->tx_complete = NULL;
/* initialize audio device */
result = audio_dev_init(audio);
audio_dev[audio->id] = audio;
return result;
}
int audio_samplerate_to_speed(uint32_t bitValue)
{
int speed = 0;
switch (bitValue)
{
case AUDIO_SAMP_RATE_8K:
speed = 8000;
break;
case AUDIO_SAMP_RATE_11K:
speed = 11052;
break;
case AUDIO_SAMP_RATE_16K:
speed = 16000;
break;
case AUDIO_SAMP_RATE_22K:
speed = 22050;
break;
case AUDIO_SAMP_RATE_32K:
speed = 32000;
break;
case AUDIO_SAMP_RATE_44K:
speed = 44100;
break;
case AUDIO_SAMP_RATE_48K:
speed = 48000;
break;
case AUDIO_SAMP_RATE_96K:
speed = 96000;
break;
case AUDIO_SAMP_RATE_128K:
speed = 128000;
break;
case AUDIO_SAMP_RATE_160K:
speed = 160000;
break;
case AUDIO_SAMP_RATE_172K:
speed = 176400;
break;
case AUDIO_SAMP_RATE_192K:
speed = 192000;
break;
default:
break;
}
return speed;
}
void audio_tx_complete(struct audio_device *audio)
{
/* try to send next frame */
audio_send_replay_frame(audio);
}
int audio_rx_complete(struct audio_device *audio)
{
/* try to receive next frame */
return audio_receive_record_frame(audio);
}
void audio_rx_done(struct audio_device *audio, uint8_t *pbuf, size_t len)
{
/* save data to record pipe */
//device_write(RT_DEVICE(&audio->record->pipe), 0, pbuf, len);
/* invoke callback */
/* if (audio->parent.rx_indicate != NULL)
audio->parent.rx_indicate(&audio->parent, len); */
}

View File

@ -0,0 +1,195 @@
#ifndef __AUDIO_H__
#define __AUDIO_H__
/* AUDIO command */
#define _AUDIO_CTL(a) (0x10 + a)
#define AUDIO_CTL_GETCAPS _AUDIO_CTL(1)
#define AUDIO_CTL_CONFIGURE _AUDIO_CTL(2)
#define AUDIO_CTL_START _AUDIO_CTL(3)
#define AUDIO_CTL_STOP _AUDIO_CTL(4)
#define AUDIO_CTL_GETBUFFERINFO _AUDIO_CTL(5)
/* Audio Device Types */
#define AUDIO_TYPE_QUERY 0x00
#define AUDIO_TYPE_INPUT 0x01
#define AUDIO_TYPE_OUTPUT 0x02
#define AUDIO_TYPE_MIXER 0x04
/* Supported Sampling Rates */
#define AUDIO_SAMP_RATE_8K 0x0001
#define AUDIO_SAMP_RATE_11K 0x0002
#define AUDIO_SAMP_RATE_16K 0x0004
#define AUDIO_SAMP_RATE_22K 0x0008
#define AUDIO_SAMP_RATE_32K 0x0010
#define AUDIO_SAMP_RATE_44K 0x0020
#define AUDIO_SAMP_RATE_48K 0x0040
#define AUDIO_SAMP_RATE_96K 0x0080
#define AUDIO_SAMP_RATE_128K 0x0100
#define AUDIO_SAMP_RATE_160K 0x0200
#define AUDIO_SAMP_RATE_172K 0x0400
#define AUDIO_SAMP_RATE_192K 0x0800
/* Supported Bit Rates */
#define AUDIO_BIT_RATE_22K 0x01
#define AUDIO_BIT_RATE_44K 0x02
#define AUDIO_BIT_RATE_48K 0x04
#define AUDIO_BIT_RATE_96K 0x08
#define AUDIO_BIT_RATE_128K 0x10
#define AUDIO_BIT_RATE_160K 0x20
#define AUDIO_BIT_RATE_172K 0x40
#define AUDIO_BIT_RATE_192K 0x80
/* Support Dsp(input/output) Units controls */
#define AUDIO_DSP_PARAM 0 /* get/set all params */
#define AUDIO_DSP_SAMPLERATE 1 /* samplerate */
#define AUDIO_DSP_CHANNELS 2 /* channels */
#define AUDIO_DSP_SAMPLEBITS 3 /* sample bits width */
/* Supported Mixer Units controls */
#define AUDIO_MIXER_QUERY 0x0000
#define AUDIO_MIXER_MUTE 0x0001
#define AUDIO_MIXER_VOLUME 0x0002
#define AUDIO_MIXER_BASS 0x0004
#define AUDIO_MIXER_MID 0x0008
#define AUDIO_MIXER_TREBLE 0x0010
#define AUDIO_MIXER_EQUALIZER 0x0020
#define AUDIO_MIXER_LINE 0x0040
#define AUDIO_MIXER_DIGITAL 0x0080
#define AUDIO_MIXER_MIC 0x0100
#define AUDIO_MIXER_VITURAL 0x0200
#define AUDIO_MIXER_EXTEND 0x8000 /* extend mixer command */
#define AUDIO_VOLUME_MAX (100)
#define AUDIO_VOLUME_MIN (0)
#define CFG_AUDIO_REPLAY_QUEUE_COUNT 8
/**
* audio flags defitions
*/
//#define AUDIO_FLAG_REPLAY 0
//#define AUDIO_FLAG_RECORD 1
#define AUDIO_REPLAY_MP_BLOCK_SIZE 4096
#define AUDIO_REPLAY_MP_BLOCK_COUNT 4
#define AUDIO_RECORD_PIPE_SIZE 2048
//typedef int (*audio_record_callback)(struct audio_device *audio, void *buffer, int size);
enum
{
AUDIO_STREAM_REPLAY = 0,
AUDIO_STREAM_RECORD = 1,
};
/* the preferred number and size of audio pipeline buffer for the audio device */
struct audio_buf_info
{
uint8_t *buffer;
uint16_t block_size;
uint16_t block_count;
uint32_t total_size;
};
struct audio_device;
struct audio_caps;
struct audio_configure;
struct audio_ops
{
int (*getcaps)(struct audio_device *audio, struct audio_caps *caps);
int (*configure)(struct audio_device *audio, struct audio_caps *caps);
int (*init)(struct audio_device *audio);
int (*start)(struct audio_device *audio, int stream);
int (*stop)(struct audio_device *audio, int stream);
size_t (*transmit)(struct audio_device *audio, const void *writeBuf, void *readBuf, size_t size);
/* get page size of codec or private buffer's info */
void (*buffer_info)(struct audio_device *audio, struct audio_buf_info *info, int flags);
};
struct audio_configure
{
uint32_t samplerate;
uint16_t channels;
uint16_t samplebits;
};
struct audio_caps
{
int main_type;
int sub_type;
union
{
uint32_t mask;
int value;
struct audio_configure config;
} udata;
};
struct audio_replay
{
QueueHandle_t queue;
SemaphoreHandle_t lock;
QueueHandle_t cmp;
struct audio_buf_info buf_info;
uint8_t *mempool;
uint8_t mpstatus[AUDIO_REPLAY_MP_BLOCK_COUNT];
uint8_t *write_data;
uint16_t write_index;
uint16_t read_index;
uint32_t pos;
uint8_t event;
bool activated;
};
struct audio_record
{
QueueHandle_t cmp;
struct audio_buf_info buf_info;
int read_index;
int remain_size;
uint8_t event;
bool activated;
int (*receive_cb)(struct audio_device *audio);
};
struct audio_device
{
struct audio_ops *ops;
struct audio_replay *replay;
struct audio_record *record;
/* device call back */
int (*rx_indicate)(struct audio_device *audio, size_t size);
int (*tx_complete)(struct audio_device *audio, void *buffer);
uint32_t flag;
uint32_t id;
void *user_data; /**< device private data */
};
int audio_register(struct audio_device *audio);
void audio_tx_complete(struct audio_device *audio);
int audio_rx_complete(struct audio_device *audio);
void audio_rx_done(struct audio_device *audio, uint8_t *pbuf, size_t len);
struct audio_device *audio_dev_open(uint32_t oflag);
int audio_dev_close(struct audio_device *audio, uint32_t oflag);
size_t audio_dev_read(struct audio_device *audio, void *buffer, size_t size);
size_t audio_dev_write(struct audio_device *audio, const void *buffer, size_t size);
int audio_dev_configure(struct audio_device *audio, struct audio_caps *caps);
int audio_dev_register_record_callback(struct audio_device *audio, int (*callback)(struct audio_device *audio));
int audio_dev_record_set_param(struct audio_device *audio, uint8_t *buf, int size);
int audio_dev_record_start(struct audio_device *audio);
int audio_dev_record_stop(struct audio_device *audio);
/* Device Control Commands */
#define CODEC_CMD_RESET 0
#define CODEC_CMD_SET_VOLUME 1
#define CODEC_CMD_GET_VOLUME 2
#define CODEC_CMD_SAMPLERATE 3
#define CODEC_CMD_EQ 4
#define CODEC_CMD_3D 5
#define CODEC_VOLUME_MAX (63)
#endif /* __AUDIO_H__ */

View File

@ -0,0 +1,591 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "timers.h"
#include "board.h"
#include "i2s.h"
#include "audio.h"
#define TX_FIFO_SIZE (4096)
#define RX_FIFO_SIZE (4096)
#define I2S_DAC_NCO_REG 0x6000006C
#define I2S1_DAC_NCO_REG 0x60000148
struct ark_i2s_data amt630hv100_i2s_dac[I2S_NUMS] = {
{
.base = REGS_I2S_BASE,
.nco_reg = I2S_DAC_NCO_REG,
.id = I2S_ID0,
.clkid = CLK_I2S,
.extdata = NULL,
.dma_txch = NULL,
.dma_rxch = NULL,
},
{
.base = REGS_I2S1_BASE,
.nco_reg = I2S1_DAC_NCO_REG,
.id = I2S_ID1,
.clkid = CLK_I2S1,
.extdata = NULL,
.dma_txch = NULL,
.dma_rxch = NULL,
}
};
struct sound_device
{
struct ark_i2s_data *i2s;
struct audio_device audio;
struct audio_configure replay_config;
struct audio_configure record_config;
TimerHandle_t guard_tx_timer;
TimerHandle_t guard_rx_timer;
uint8_t volume;
uint8_t *tx_fifo;
uint8_t *rx_fifo;
};
static struct sound_device snd_dev[I2S_NUMS] = {0};
static int ark_audio_init(struct audio_device *audio)
{
struct sound_device *sdev;
configASSERT(audio != NULL);
sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
ark_i2s_init(sdev->i2s, audio->flag);
return 0;
}
static int ark_audio_start(struct audio_device *audio, int stream)
{
struct sound_device *sdev;
configASSERT(audio != NULL);
sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
if (stream == AUDIO_STREAM_REPLAY)
{
struct audio_caps caps;
caps.main_type = AUDIO_TYPE_OUTPUT;
caps.sub_type = AUDIO_DSP_SAMPLERATE;
audio->ops->getcaps(audio, &caps);
audio->ops->configure(audio, &caps);
audio_tx_complete(audio);
ark_i2s_startup(sdev->i2s, stream);
}
else if (stream == AUDIO_STREAM_RECORD)
{
struct audio_caps caps;
caps.main_type = AUDIO_TYPE_INPUT;
caps.sub_type = AUDIO_DSP_PARAM;
audio->ops->getcaps(audio, &caps);
audio->ops->configure(audio, &caps);
if(audio_rx_complete(audio) == 0)
{
if(sdev->guard_rx_timer)
xTimerStopFromISR(sdev->guard_rx_timer, 0);
ark_i2s_startup(sdev->i2s, stream);
if(sdev->guard_rx_timer)
{
xTimerResetFromISR(sdev->guard_rx_timer, 0);
xTimerStartFromISR(sdev->guard_rx_timer, 0);
}
}
else
printf("%s() audio_rx_complete failed.\n", __func__);
}
return 0;
}
static int ark_audio_stop(struct audio_device *audio, int stream)
{
struct sound_device *sdev;
configASSERT(audio != NULL);
sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
configASSERT(sdev->i2s != NULL);
ark_i2s_stop(sdev->i2s, stream);
if(stream == AUDIO_STREAM_REPLAY)
dma_stop_channel(sdev->i2s->dma_txch);
else if(stream == AUDIO_STREAM_RECORD)
dma_stop_channel(sdev->i2s->dma_rxch);
return 0;
}
static int ark_audio_getcaps(struct audio_device *audio, struct audio_caps *caps)
{
struct sound_device *sdev;
int result = 0;
configASSERT(audio != NULL);
sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
switch (caps->main_type)
{
case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
{
switch (caps->sub_type)
{
case AUDIO_TYPE_QUERY:
caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
break;
default:
result = -1;
break;
}
break;
}
case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = sdev->replay_config.samplerate;
caps->udata.config.channels = sdev->replay_config.channels;
caps->udata.config.samplebits = sdev->replay_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = sdev->replay_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = sdev->replay_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = sdev->replay_config.samplebits;
break;
default:
result = -1;
break;
}
break;
}
case AUDIO_TYPE_INPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
caps->udata.config.samplerate = sdev->record_config.samplerate;
caps->udata.config.channels = sdev->record_config.channels;
caps->udata.config.samplebits = sdev->record_config.samplebits;
break;
case AUDIO_DSP_SAMPLERATE:
caps->udata.config.samplerate = sdev->record_config.samplerate;
break;
case AUDIO_DSP_CHANNELS:
caps->udata.config.channels = sdev->record_config.channels;
break;
case AUDIO_DSP_SAMPLEBITS:
caps->udata.config.samplebits = sdev->record_config.samplebits;
break;
default:
result = -1;
break;
}
break;
}
case AUDIO_TYPE_MIXER: /* report the Mixer Units */
{
switch (caps->sub_type)
{
case AUDIO_MIXER_QUERY:
caps->udata.mask = AUDIO_MIXER_VOLUME;
break;
case AUDIO_MIXER_VOLUME:
caps->udata.value = sdev->volume;
break;
default:
result = -1;
break;
}
break;
}
default:
result = -1;
break;
}
return result;
}
static int ark_audio_configure(struct audio_device *audio, struct audio_caps *caps)
{
struct sound_device *sdev;
int result = 0;
configASSERT(audio != NULL);
sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
switch (caps->main_type)
{
case AUDIO_TYPE_MIXER:
{
switch (caps->sub_type)
{
case AUDIO_MIXER_MUTE:
{
ark_i2s_set_volume(sdev->i2s, 0, 0);
sdev->volume = 0;
break;
}
case AUDIO_MIXER_VOLUME:
{
int volume = caps->udata.value;
ark_i2s_set_volume(sdev->i2s, volume, volume);
sdev->volume = volume;
break;
}
}
break;
}
case AUDIO_TYPE_OUTPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
struct audio_configure config = caps->udata.config;
sdev->replay_config.channels = config.channels;
sdev->replay_config.samplebits = config.samplebits;
sdev->replay_config.samplerate = config.samplerate;
//ark_i2s_set_rate(sdev->i2s, config.samplerate);
ark_i2s_set_params(sdev->i2s, AUDIO_STREAM_REPLAY, config.samplerate, config.channels, config.samplebits);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
struct audio_configure config = caps->udata.config;
sdev->replay_config.samplerate = config.samplerate;
ark_i2s_set_rate(sdev->i2s, AUDIO_STREAM_REPLAY, config.samplerate);
break;
}
default:
result = -1;
break;
}
break;
}
case AUDIO_TYPE_INPUT:
{
switch (caps->sub_type)
{
case AUDIO_DSP_PARAM:
{
struct audio_configure config = caps->udata.config;
sdev->record_config.channels = config.channels;
sdev->record_config.samplebits = config.samplebits;
sdev->record_config.samplerate = config.samplerate;
ark_i2s_set_params(sdev->i2s, AUDIO_STREAM_RECORD, config.samplerate, config.channels, config.samplebits);
break;
}
case AUDIO_DSP_SAMPLERATE:
{
struct audio_configure config = caps->udata.config;
sdev->record_config.samplerate = config.samplerate;
ark_i2s_set_rate(sdev->i2s, AUDIO_STREAM_RECORD, config.samplerate);
break;
}
default:
result = -1;
break;
}
break;
}
}
return result;
}
static void ark_i2s_dma_tx_callback(void *param, unsigned int mask)
{
struct sound_device *sdev = (struct sound_device *)param;
configASSERT(sdev != NULL);
if(sdev->guard_tx_timer)
xTimerStopFromISR(sdev->guard_tx_timer, 0);
audio_tx_complete(&sdev->audio);
}
static void ark_i2s_dma_rx_callback(void *param, unsigned int mask)
{
struct sound_device *sdev = (struct sound_device *)param;
configASSERT(sdev != NULL);
if(sdev->guard_rx_timer)
xTimerStopFromISR(sdev->guard_rx_timer, 0);
audio_rx_complete(&sdev->audio);
}
#if 1
static void ark_i2s_dma_tx_timeout_callback(TimerHandle_t timer)
{
struct sound_device *sdev = (struct sound_device *)pvTimerGetTimerID(timer);
configASSERT(sdev != NULL);
configASSERT(sdev->i2s != NULL);
if(sdev->audio.flag & AUDIO_FLAG_REPLAY == AUDIO_FLAG_REPLAY) {
printf("i2s%d dma tx timeout.\n", sdev->i2s->id);
if(sdev->guard_tx_timer)
xTimerStopFromISR(sdev->guard_tx_timer, 0);
audio_tx_complete(&sdev->audio);
}
}
static void ark_i2s_dma_rx_timeout_callback(TimerHandle_t timer)
{
struct sound_device *sdev = (struct sound_device *)pvTimerGetTimerID(timer);
configASSERT(sdev != NULL);
configASSERT(sdev->i2s != NULL);
if(sdev->audio.flag & AUDIO_FLAG_RECORD == AUDIO_FLAG_RECORD) {
printf("i2s:%d dma rx timeout.\n", sdev->i2s->id);
if(sdev->guard_rx_timer)
xTimerStopFromISR(sdev->guard_rx_timer, 0);
audio_rx_complete(&sdev->audio);
}
}
#else
static void ark_i2s_dma_timeout_callback(TimerHandle_t timer)
{
struct sound_device *sdev = (struct sound_device *)pvTimerGetTimerID(timer);
configASSERT(sdev != NULL);
if((sdev->audio.flag & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY) {
printf("i2s dma tx timeout.\n");
if(sdev->guard_tx_timer)
xTimerStopFromISR(sdev->guard_tx_timer, 0);
audio_tx_complete(&sdev->audio);
} else if((sdev->audio.flag & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD) {
printf("i2s dma rx timeout.\n");
if(sdev->guard_rx_timer)
xTimerStopFromISR(sdev->guard_rx_timer, 0);
ark_i2s_restart_audio(sdev->i2s);
audio_rx_complete(&sdev->audio);
}
}
#endif
static size_t ark_audio_transmit(struct audio_device *audio, const void *writeBuf, void *readBuf, size_t size)
{
struct ark_i2s_data *i2s;
struct dma_config cfg = {0};
struct sound_device *sdev = (struct sound_device *)audio->user_data;
int ret;
configASSERT(sdev != NULL);
i2s = sdev->i2s;
cfg.dst_maxburst = 16;
cfg.src_maxburst = 16;
cfg.transfer_size = size;
if(writeBuf)
{
cfg.dst_addr_width = DMA_BUSWIDTH_4_BYTES;
cfg.src_addr_width = DMA_BUSWIDTH_4_BYTES;
cfg.src_addr = (dma_addr_t)writeBuf;
cfg.dst_addr = i2s->base + I2S_SADR;
cfg.direction = DMA_MEM_TO_DEV;
if(i2s->id == I2S_ID1)
cfg.dst_id = I2S1_TX;
else /*if(i2s->id == I2S_ID0)*/
cfg.dst_id = I2S_TX;
ret = dma_config_channel(i2s->dma_txch, &cfg);
if (ret) {
printf("%s, dma_config_channel tx failed.\n", __func__);
return -1;
}
dma_register_complete_callback(i2s->dma_txch, ark_i2s_dma_tx_callback, sdev);
/* clean cache before write */
CP15_clean_dcache_for_dma((u32)writeBuf, (u32)writeBuf + size);
dma_start_channel(i2s->dma_txch);
if(sdev->guard_tx_timer)
{
xTimerResetFromISR(sdev->guard_tx_timer, 0);
xTimerStartFromISR(sdev->guard_tx_timer, 0);
}
}
else if(readBuf)
{
if(i2s->cfg[AUDIO_STREAM_RECORD].channels == 2) {
cfg.dst_addr_width = DMA_BUSWIDTH_4_BYTES;
cfg.src_addr_width = DMA_BUSWIDTH_4_BYTES;
} else {
cfg.dst_addr_width = DMA_BUSWIDTH_2_BYTES;
cfg.src_addr_width = DMA_BUSWIDTH_2_BYTES;
}
cfg.src_addr = i2s->base + I2S_SADR;
cfg.dst_addr = (dma_addr_t)readBuf;
cfg.direction = DMA_DEV_TO_MEM;
if(i2s->id == I2S_ID1)
cfg.src_id = I2S1_RX;
else /*if(i2s->id == I2S_ID0)*/
cfg.src_id = I2S_RX;
ret = dma_config_channel(i2s->dma_rxch, &cfg);
if (ret) {
printf("%s, dma_config_channel rx failed.\n", __func__);
return -1;
}
dma_register_complete_callback(i2s->dma_rxch, ark_i2s_dma_rx_callback, sdev);
/* clean cache before read */
CP15_flush_dcache_for_dma((u32)readBuf, (u32)readBuf + size);
dma_start_channel(i2s->dma_rxch);
if(sdev->guard_rx_timer)
{
xTimerResetFromISR(sdev->guard_rx_timer, 0);
xTimerStartFromISR(sdev->guard_rx_timer, 0);
}
}
return size;
}
static void ark_audio_buffer_info(struct audio_device *audio, struct audio_buf_info *info, int flags)
{
configASSERT(audio != NULL);
struct sound_device *sdev = (struct sound_device *)audio->user_data;
configASSERT(sdev != NULL);
/**
* TX_FIFO
* +----------------+----------------+
* | block1 | block2 |
* +----------------+----------------+
* \ block_size /
*/
if(flags == AUDIO_FLAG_REPLAY)
{
info->buffer = sdev->tx_fifo;
info->total_size = TX_FIFO_SIZE;
info->block_size = TX_FIFO_SIZE / 2;
info->block_count = 2;
}
else if(flags == AUDIO_FLAG_RECORD)
{
info->buffer = sdev->rx_fifo;
info->total_size = RX_FIFO_SIZE;
info->block_size = RX_FIFO_SIZE/2;
info->block_count = 2;
}
}
static struct audio_ops audio_ops =
{
.getcaps = ark_audio_getcaps,
.configure = ark_audio_configure,
.init = ark_audio_init,
.start = ark_audio_start,
.stop = ark_audio_stop,
.transmit = ark_audio_transmit,
.buffer_info = ark_audio_buffer_info,
};
int sound_init(void)
{
uint8_t *tx_fifo = NULL;
struct sound_device *sdev;
int flags;
int i;
for(i=0; i<I2S_NUMS; i++)
{
flags = 0;
#ifdef AUDIO_REPLAY_I2S
if(AUDIO_REPLAY_I2S == i) {
flags |= AUDIO_FLAG_REPLAY;
}
#endif
#ifdef AUDIO_RECORD_I2S
if(AUDIO_RECORD_I2S == i) {
flags |= AUDIO_FLAG_RECORD;
}
#endif
if(!(flags & AUDIO_FLAG_REPLAY_RECORD))
continue;
sdev = &snd_dev[i];
sdev->i2s = &amt630hv100_i2s_dac[i];
/* init default configuration */
if((flags & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
tx_fifo = pvPortMalloc(TX_FIFO_SIZE);
if (!tx_fifo)
{
printf("%s, pvPortMalloc failed\n", __func__);
return -ENOMEM;
}
sdev->tx_fifo = tx_fifo;
sdev->replay_config.samplerate = 44100; //will be reset when open() audio.
sdev->replay_config.channels = 2;
sdev->replay_config.samplebits = 16;
sdev->volume = 100;
sdev->guard_tx_timer = xTimerCreate("Replay Timer", pdMS_TO_TICKS(200), pdFALSE,
//NULL, ark_i2s_dma_timeout_callback);
sdev, ark_i2s_dma_tx_timeout_callback);
}
if((flags & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
sdev->rx_fifo = NULL;
sdev->record_config.samplerate = 16000; //will be reset when open() audio.
sdev->record_config.channels = 2;
sdev->record_config.samplebits = 16;
sdev->volume = 100;
sdev->guard_rx_timer = xTimerCreate("Record Timer", pdMS_TO_TICKS(200), pdFALSE,
sdev, ark_i2s_dma_rx_timeout_callback);
}
sdev->audio.ops = &audio_ops;
sdev->audio.user_data = (void *)sdev;
sdev->audio.flag = flags;
sdev->audio.id = i;
audio_register(&sdev->audio);
}
return 0;
}

View File

@ -0,0 +1,465 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#include "lcd.h"
#include "blend2d.h"
#include "ff_stdio.h"
#define REGS_BLEND2D REGS_BLEND2D_BASE
/* BLEND2D MODULE */
#define BLEND2D_ENABLE 0x000
#define BLEND2D_START 0x004
#define BLEND2D_CTL_ENABLE 0x008
#define BLEND2D_MODE 0x00C
#define BLEND2D_WRITE_WAIT 0x010
#define BLEND2D_LAYER1_BURST_CTL 0x020
#define BLEND2D_LAYER1_CTL 0x024
#define BLEND2D_LAYER1_SIZE 0x028
#define BLEND2D_LAYER1_ADDR 0x02C
#define BLEND2D_LAYER2_BURST_CTL 0x030
#define BLEND2D_LAYER2_CTL 0x034
#define BLEND2D_LAYER2_SIZE 0x038
#define BLEND2D_LAYER2_ADDR 0x03C
#define BLEND2D_COLOR_KEY_MASK_THLD_LAYER1 0x040
#define BLEND2D_COLOR_KEY_MASK_THLD_LAYER2 0x044
#define BLEND2D_COLOR_KEY_MASK_VALUE_LAYER1 0x048
#define BLEND2D_COLOR_KEY_MASK_VALUE_LAYER2 0x04C
#define BLEND2D_LAYER1_WINDOW_POINT 0x050
#define BLEND2D_LAYER2_WINDOW_POINT 0x054
#define BLEND2D_LAYER1_SOURCE_SIZE 0x058
#define BLEND2D_LAYER2_SOURCE_SIZE 0x05C
#define BLEND2D_WRITE_BACK_SOURCE_SIZE 0x060
#define BLEND2D_BLD_SIZE 0x064
#define BLEND2D_BLD_WINDOW_POINT 0x068
#define BLEND2D_BLD_ADDR 0x06C
#define BLEND2D_BLD_CTL 0x070
#define BLEND2D_LAYER1_BACK_COLOR 0x074
#define BLEND2D_LAYER2_BACK_COLOR 0x078
#define BLEND2D_INT_CTL 0x080
#define BLEND2D_INT_CLR 0x084
#define BLEND2D_INT_STA 0x088
void blend2d_start(void)
{
writel(1, REGS_BLEND2D + BLEND2D_START);
udelay(10);
writel(0, REGS_BLEND2D + BLEND2D_START);
}
void blend2d_clear_int(void)
{
writel(0, REGS_BLEND2D + BLEND2D_INT_CLR);
}
void blend2d_read_wait_enable(int enable, unsigned int wait_time)
{
unsigned int reg;
reg = readl(REGS_BLEND2D + BLEND2D_MODE);
reg &= ~((0xffff << 16) | (1 <<5));
wait_time &= 0xffff;
if(enable)
reg |= (wait_time << 16) | (1 << 5);
writel(reg, REGS_BLEND2D + BLEND2D_MODE);
}
void blend2d_write_wait_enable(unsigned int enable, unsigned int wait_time)
{
unsigned int reg;
reg = readl(REGS_BLEND2D + BLEND2D_WRITE_WAIT);
reg &= ~((1 << 12) | 0xfff);
wait_time &= 0xfff;
if(enable)
reg |= (1 << 12) | wait_time;
writel(reg, REGS_BLEND2D + BLEND2D_WRITE_WAIT);
}
void blend2d_layer_enable(int layer, int enable)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_CTL_ENABLE);
if (layer == BLEND2D_LAYER1) {
if (enable)
reg |= 1;
else
reg &= ~1;
} else if (layer == BLEND2D_LAYER2) {
if (enable)
reg |= 1 << 1;
else
reg &= ~(1 << 1);
}
writel(reg, REGS_BLEND2D + BLEND2D_CTL_ENABLE);
}
void blend2d_set_layer_point(int layer, int x, int y)
{
x &= 0xfff;
y &= 0xfff;
if (layer == BLEND2D_LAYER1)
writel((y << 12) | x, REGS_BLEND2D + BLEND2D_LAYER1_WINDOW_POINT);
else if (layer == BLEND2D_LAYER2)
writel((y << 12) | x, REGS_BLEND2D + BLEND2D_LAYER2_WINDOW_POINT);
}
void blend2d_set_layer_size(int layer, int width, int height)
{
width &= 0xfff;
height &= 0xfff;
if (layer == BLEND2D_LAYER1)
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_LAYER1_SIZE);
else if (layer == BLEND2D_LAYER2)
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_LAYER2_SIZE);
}
void blend2d_set_layer_source_size(int layer, int width, int height)
{
width &= 0xfff;
height &= 0xfff;
if (layer == BLEND2D_LAYER1)
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_LAYER1_SOURCE_SIZE);
else if (layer == BLEND2D_LAYER2)
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_LAYER2_SOURCE_SIZE);
}
void blend2d_set_layer_addr(int layer, unsigned int addr)
{
if (layer == BLEND2D_LAYER1)
writel(addr, REGS_BLEND2D + BLEND2D_LAYER1_ADDR);
else if (layer == BLEND2D_LAYER2)
writel(addr, REGS_BLEND2D + BLEND2D_LAYER2_ADDR);
}
void blend2d_set_layer_format(int layer, int format, int rgb_order)
{
format &= 0xf;
rgb_order &= 0x7;
if (layer == BLEND2D_LAYER1)
writel(rgb_order << 4 | format, REGS_BLEND2D + BLEND2D_LAYER1_CTL);
else if (layer == BLEND2D_LAYER2)
writel(rgb_order << 4 | format, REGS_BLEND2D + BLEND2D_LAYER2_CTL);
}
void blend2d_set_layer_alpha_mode(int layer, int mode)
{
unsigned int reg;
if (layer == BLEND2D_LAYER1) {
reg = readl(REGS_BLEND2D + BLEND2D_LAYER1_BURST_CTL);
if (mode == BLEND2D_ALPHA_DATA)
reg &= ~(1 << 15);
else if (mode == BLEND2D_ALPHA_REG)
reg |= 1 << 15;
writel(reg, REGS_BLEND2D + BLEND2D_LAYER1_BURST_CTL);
} else if (layer == BLEND2D_LAYER2) {
reg = readl(REGS_BLEND2D + BLEND2D_LAYER2_BURST_CTL);
if (mode == BLEND2D_ALPHA_DATA)
reg &= ~(1 << 15);
else if (mode == BLEND2D_ALPHA_REG)
reg |= 1 << 15;
writel(reg, REGS_BLEND2D + BLEND2D_LAYER2_BURST_CTL);
}
}
void blend2d_set_layer_alpha(int layer, uint8_t alpha)
{
unsigned int reg;
if (layer == BLEND2D_LAYER1) {
reg = readl(REGS_BLEND2D + BLEND2D_LAYER1_BURST_CTL);
reg &= ~(0xf << 16);
reg |= alpha << 16;
writel(reg, REGS_BLEND2D + BLEND2D_LAYER1_BURST_CTL);
} else if (layer == BLEND2D_LAYER2) {
reg = readl(REGS_BLEND2D + BLEND2D_LAYER2_BURST_CTL);
reg &= ~(0xf << 16);
reg |= alpha << 16;
writel(reg, REGS_BLEND2D + BLEND2D_LAYER2_BURST_CTL);
}
}
void blend2d_set_layer_colorkey(int layer, int enable, uint8_t r, uint8_t g, uint8_t b)
{
unsigned int reg;
if (layer == BLEND2D_LAYER1) {
reg = readl(REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER1);
reg &= ~0x1ffffff;
reg |= (r << 16) | (g << 8) | b;
if (enable)
reg |= 1 << 24;
writel(reg, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER1);
} else if (layer == BLEND2D_LAYER2) {
reg = readl(REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER2);
reg &= ~0x1ffffff;
reg |= (r << 16) | (g << 8) | b;
if (enable)
reg |= 1 << 24;
writel(reg, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER2);
}
}
void blend2d_set_layer_colorkey_thld(int layer, uint8_t r, uint8_t g, uint8_t b)
{
if (layer == BLEND2D_LAYER1)
writel((r << 16) | (g << 8) | b, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_THLD_LAYER1);
else if (layer == BLEND2D_LAYER2)
writel((r << 16) | (g << 8) | b, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_THLD_LAYER2);
}
void blend2d_set_layer_colorkey_backcolor(int layer, int enable, uint8_t r, uint8_t g, uint8_t b)
{
unsigned int reg;
if (layer == BLEND2D_LAYER1) {
reg = readl(REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER1);
if (enable)
reg |= 1 << 25;
else
reg &= ~(1 << 25);
writel(reg, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER1);
writel((r << 16) | (g << 8) | b, REGS_BLEND2D + BLEND2D_LAYER1_BACK_COLOR);
} else if (layer == BLEND2D_LAYER2) {
reg = readl(REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER2);
if (enable)
reg |= 1 << 25;
else
reg &= ~(1 << 25);
writel(reg, REGS_BLEND2D + BLEND2D_COLOR_KEY_MASK_VALUE_LAYER2);
writel((r << 16) | (g << 8) | b, REGS_BLEND2D + BLEND2D_LAYER2_BACK_COLOR);
}
}
void blend2d_set_blend_point(int x, int y)
{
x &= 0xfff;
y &= 0xfff;
writel((y << 12) | x, REGS_BLEND2D + BLEND2D_BLD_WINDOW_POINT);
}
void blend2d_set_blend_source_size(int width, int height)
{
width &= 0xfff;
height &= 0xfff;
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_WRITE_BACK_SOURCE_SIZE);
}
void blend2d_set_blend_size(int width, int height)
{
width &= 0xfff;
height &= 0xfff;
writel((height << 12) | width, REGS_BLEND2D + BLEND2D_BLD_SIZE);
}
void blend2d_set_blend_mode(int mode)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_MODE);
reg &= ~((0xf << 8) | 0x1);
reg |= (mode << 8) | (0x1 << 4);
writel(reg, REGS_BLEND2D + BLEND2D_MODE);
}
void blend2d_set_blend_addr(uint32_t addr)
{
writel(addr, REGS_BLEND2D + BLEND2D_BLD_ADDR);
}
void blend2d_set_blend_format(int format)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_BLD_CTL);
format &= 0xf;
reg &= ~0xf;
reg |= format;
writel(reg, REGS_BLEND2D + BLEND2D_BLD_CTL);
}
void blend2d_set_blend_endian(int endian)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_BLD_CTL);
if (endian == BLEND2D_ARGB)
reg |= 1 << 4;
else if (endian == BLEND2D_RGBA)
reg &= ~(1 << 4);
writel(reg, REGS_BLEND2D + BLEND2D_BLD_CTL);
}
void blend2d_set_blend_alpha(uint8_t alpha)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_BLD_CTL);
reg &= ~(0xff << 24);
reg |= alpha << 24;
writel(reg, REGS_BLEND2D + BLEND2D_BLD_CTL);
}
void blend2d_set_blend_alpha_mode(int mode)
{
unsigned int reg = readl(REGS_BLEND2D + BLEND2D_BLD_CTL);
mode &= 3;
reg &= ~((0x1ff << 8) | (3 << 5));
reg |= (256 << 8) | (mode << 5);
writel(reg, REGS_BLEND2D + BLEND2D_BLD_CTL);
}
void blend2d_fill(uint32_t address, int xpos, int ypos, int width, int height, int source_width, int source_height,
uint8_t cr, uint8_t cg, uint8_t cb, int format, uint8_t opa, int alpha_byte)
{
/* printf("addr 0x%x, %d,%d,%d,%d-%d,%d r=%d, g=%d, b=%d, format=%d, opa=0x%x.\n",
address, xpos, ypos, width, height, source_width, source_height, cr, cg, cb, format, opa); */
blend2d_set_layer_format(BLEND2D_LAYER2, format & 0xf, (format >> 8) & 0xf);
blend2d_set_layer_addr(BLEND2D_LAYER2, address);
blend2d_set_layer_point(BLEND2D_LAYER2, 0, 0);
blend2d_set_layer_size(BLEND2D_LAYER2, width, height);
blend2d_set_layer_source_size(BLEND2D_LAYER2, width, height);
blend2d_set_layer_colorkey(BLEND2D_LAYER2, 1, 0, 0, 0);
blend2d_set_layer_colorkey_thld(BLEND2D_LAYER2, 0xff, 0xff, 0xff);
blend2d_set_layer_colorkey_backcolor(BLEND2D_LAYER2, 1, cr, cg, cb);
blend2d_set_layer_alpha(BLEND2D_LAYER2, opa);
blend2d_set_layer_alpha_mode(BLEND2D_LAYER2, BLEND2D_ALPHA_REG);
blend2d_layer_enable(BLEND2D_LAYER2, 1);
blend2d_set_layer_format(BLEND2D_LAYER1, format & 0xf, (format >> 8) & 0xf);
blend2d_set_layer_addr(BLEND2D_LAYER1, address);
blend2d_set_layer_point(BLEND2D_LAYER1, 0, 0);
blend2d_set_layer_size(BLEND2D_LAYER1, width, height);
blend2d_set_layer_source_size(BLEND2D_LAYER1, source_width, source_height);
blend2d_layer_enable(BLEND2D_LAYER1, 1);
blend2d_set_blend_format(format & 0xf);
blend2d_set_blend_addr(address);
blend2d_set_blend_point(xpos, ypos);
blend2d_set_blend_size(width, height);
blend2d_set_blend_source_size(source_width, source_height);
blend2d_set_blend_mode(BLEND2D_MIX_BLEND);
blend2d_set_blend_endian(BLEND2D_ARGB);
blend2d_set_blend_alpha(0xff);
if (alpha_byte)
blend2d_set_blend_alpha_mode(BLEND2D_ALPHA_LAYER2);
else
blend2d_set_blend_alpha_mode(BLEND2D_ALPHA_BLEND_REG);
blend2d_read_wait_enable(1, 0x3f);
}
void blend2d_blit(uint32_t dst_addr, int dst_w, int dst_h, int dst_x, int dst_y, int dst_format, int width, int height,
uint32_t src_addr, int src_w, int src_h, int src_x, int src_y, int src_format, uint8_t opa, int alpha_byte)
{
/* printf("dst 0x%x,%dx%d-%d,%d %dx%d src 0x%x,%dx%d-%d,%d, src_format=%d, opa=0x%x, alpha_byte=%d.\n",
dst_addr, dst_w, dst_h, dst_x, dst_y, width, height,
src_addr, src_w, src_h, src_x, src_y, src_format, opa, alpha_byte); */
blend2d_set_layer_format(BLEND2D_LAYER2, src_format & 0xf, (src_format >> 8) & 0xf);
blend2d_set_layer_addr(BLEND2D_LAYER2, src_addr);
blend2d_set_layer_point(BLEND2D_LAYER2, src_x, src_y);
blend2d_set_layer_size(BLEND2D_LAYER2, width, height);
blend2d_set_layer_source_size(BLEND2D_LAYER2, src_w, src_h);
blend2d_set_layer_colorkey(BLEND2D_LAYER2, 0, 0, 0, 0);
blend2d_set_layer_alpha(BLEND2D_LAYER2, opa);
if (alpha_byte)
blend2d_set_layer_alpha_mode(BLEND2D_LAYER2, BLEND2D_ALPHA_DATA);
else
blend2d_set_layer_alpha_mode(BLEND2D_LAYER2, BLEND2D_ALPHA_REG);
blend2d_layer_enable(BLEND2D_LAYER2, 1);
blend2d_set_layer_format(BLEND2D_LAYER1, dst_format & 0xf, (dst_format >> 8) & 0xf);
blend2d_set_layer_addr(BLEND2D_LAYER1, dst_addr);
blend2d_set_layer_point(BLEND2D_LAYER1, dst_x, dst_y);
blend2d_set_layer_size(BLEND2D_LAYER1, width, height);
blend2d_set_layer_source_size(BLEND2D_LAYER1, dst_w, dst_h);
blend2d_layer_enable(BLEND2D_LAYER1, 1);
blend2d_set_blend_format(dst_format & 0xf);
blend2d_set_blend_addr(dst_addr);
blend2d_set_blend_point(dst_x, dst_y);
blend2d_set_blend_size(width, height);
blend2d_set_blend_source_size(dst_w, dst_h);
blend2d_set_blend_mode(BLEND2D_MIX_BLEND);
blend2d_set_blend_endian(BLEND2D_ARGB);
//blend2d_set_blend_alpha(0xff);
if (alpha_byte)
blend2d_set_blend_alpha_mode(BLEND2D_ALPHA_LAYER1);
else
blend2d_set_blend_alpha_mode(BLEND2D_ALPHA_LAYER2);
blend2d_read_wait_enable(1, 0x3f);
}
static void blend2d_fill_test(void)
{
unsigned char *buf = pvPortMalloc(1024*600*4);
memset(buf, 0, 1024*600*4);
CP15_clean_dcache_for_dma((uint32_t)buf, (uint32_t)buf + 1024*600*4);
ark_lcd_set_osd_yaddr(LCD_OSD1, (uint32_t)buf);
ark_lcd_set_osd_sync(LCD_OSD1);
blend2d_fill((uint32_t)buf, 0, 0, 1024, 600, 1024, 600, 0xff, 0, 0, BLEND2D_FORAMT_ARGB888, 0x20, 0);
blend2d_run();
}
static void blend2d_demo_thread(void *param)
{
blend2d_fill_test();
vTaskDelay(portMAX_DELAY);
}
int blend2d_demo(void)
{
/* Create a task to play animation */
if (xTaskCreate(blend2d_demo_thread, "axiblendemo", configMINIMAL_STACK_SIZE, NULL,
configMAX_PRIORITIES / 3, NULL) != pdPASS) {
printf("create axiblend demo task fail.\n");
return -1;
}
vTaskDelay(portMAX_DELAY);
return 0;
}
static QueueHandle_t blend2d_done;
static void blend2d_int_handler(void *para)
{
unsigned int status;
status = readl(REGS_BLEND2D + BLEND2D_INT_STA);
blend2d_clear_int();
if(status & 0x2) {
printf("blend2d err int 0x%x.\n", status);
}
xQueueSendFromISR(blend2d_done, NULL, 0);
}
int blend2d_init(void)
{
blend2d_done = xQueueCreate(1, 0);
request_irq(BLEND2D_IRQn, 0, blend2d_int_handler, NULL);
writel(0x82, REGS_BLEND2D + BLEND2D_LAYER1_BURST_CTL);
writel(0x82, REGS_BLEND2D + BLEND2D_LAYER2_BURST_CTL);
writel(0x3, REGS_BLEND2D + BLEND2D_INT_CTL);
writel(1, REGS_BLEND2D + BLEND2D_ENABLE);
return 0;
}
int blend2d_run(void)
{
xQueueReset(blend2d_done);
blend2d_start();
if (xQueueReceive(blend2d_done, NULL, pdMS_TO_TICKS(100)) != pdTRUE) {
printf("blend2d_run timeout.\n");
return -1;
}
return 0;
}

View File

@ -0,0 +1,854 @@
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "gpio_protocol.h"
#if DEVICE_MXC_A58
// #define CAN_USE_TX_DEMO
#elif DEVICE_MXC_A59
// #define CAN_USE_TX_DEMO
#endif
#define CAN_RX_BUF_NUM 32
#define CAN_TX_BUF_NUM 32
CanMsg canRxMsg[CAN_NUM][CAN_RX_BUF_NUM] = {0};
ListItem_t canRxListItem[CAN_NUM][CAN_RX_BUF_NUM];
CanMsg canTxMsg[CAN_NUM][CAN_TX_BUF_NUM] = {0};
ListItem_t canTxListItem[CAN_NUM][CAN_TX_BUF_NUM];
static CanPort_t *pxCanPort[CAN_NUM] = {NULL};
static uint32_t ulCanBase[CAN_NUM] = {REGS_CAN0_BASE, REGS_CAN1_BASE};
static CAN_InitTypeDef CanInitValue[CAN_NUM];
static CAN_FilterInitTypeDef CanFilterInitValue[CAN_NUM];
static int nCanFilterEnable[CAN_NUM];
unsigned char can_set_reset_mode(CAN_TypeDef* CANx)
{
unsigned char status;
int i;
/*检查复位标志*/
status = CANx->MOD;
/* 关闭中断 */
CANx->IER = 0x00;
for (i = 0; i < 100; i++)
{
if((status & CAN_Mode_RM) == CAN_Mode_RM)
return CAN_InitStatus_Success;
/* 设置复位*/
CANx->MOD |= ((unsigned char)CAN_Mode_RM);
/*延时*/
udelay(10);
/*检查复位标志*/
status = CANx->MOD;
}
printf("Setting can into reset mode failed!\n");
return CAN_InitStatus_Failed;
}
static unsigned char set_normal_mode(CAN_TypeDef* CANx)
{
unsigned char status;
int i;
/*检查复位标志*/
status = CANx->MOD;
for (i = 0; i < 100; i++)
{
if((status & CAN_Mode_RM) != CAN_Mode_RM)
{
/*开所有中断 (总线错误中断不开)*/
CANx->IER |= (~(unsigned char)CAN_IR_BEI);
return CAN_InitStatus_Success;
}
/* 设置正常工作模式*/
CANx->MOD &= (~(unsigned char) CAN_Mode_RM);
/*延时*/
udelay(10);
status = CANx->MOD;
}
printf("Setting can into normal mode failed!\n");
return CAN_InitStatus_Failed;
}
unsigned char can_set_start(CAN_TypeDef* CANx)
{
/*复位TX错误计数器*/
CANx->TXERR = 0;
/*复位RX错误计数器*/
CANx->RXERR = 0;
/*时钟分频寄存器: PeliCAN模式; CBP=1,中止输入比较器, RX0激活*/
CANx->CDR = 0xC0;
return set_normal_mode(CANx);
}
unsigned char CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct)
{
unsigned char InitStatus = CAN_InitStatus_Failed;
unsigned char status;
status = CANx->MOD;
if( status == 0xFF)
{
printf("Probe can failed \n");
return CAN_InitStatus_Failed;
}
/* 进入复位模式 */
InitStatus = can_set_reset_mode(CANx);
/* set acceptance filter (accept all) */
CANx->IDE_RTR_DLC = 0x0;
CANx->ID[0] = 0x0;
CANx->ID[1] = 0x0;
CANx->ID[2] = 0x0;
CANx->ID[3] = 0xFF;
CANx->BUF[0] = 0xFF;
CANx->BUF[1] = 0xFF;
CANx->BUF[2] = 0xFF;
if((CAN_InitStruct->CAN_Mode & CAN_Mode_SM) == CAN_Mode_SM)
{
/* 睡眠模式 1: 睡眠 0: 唤醒*/
CANx->MOD|= (unsigned char)CAN_Mode_SM;
}
else
{
CANx->MOD&=~ (unsigned char)CAN_Mode_SM;
}
if((CAN_InitStruct->CAN_Mode & CAN_Mode_LOM) == CAN_Mode_LOM)
{
/*只听模式 1:只听 0:正常 */
CANx->MOD|= (unsigned char)CAN_Mode_LOM;
}
else
{
CANx->MOD&=~ (unsigned char)CAN_Mode_LOM;
}
if((CAN_InitStruct->CAN_Mode & CAN_Mode_AFM) == CAN_Mode_AFM)
{
/*单滤波模式 1:单 0: 双*/
CANx->MOD |= (unsigned char)CAN_Mode_AFM;
}
else
{
CANx->MOD&=~ (unsigned char)CAN_Mode_AFM;
}
if((CAN_InitStruct->CAN_Mode & CAN_Mode_STM) == CAN_Mode_STM)
{
/*自检测模式 1:自检测 0:正常 */
CANx->MOD |= (unsigned char)CAN_Mode_STM;
}
else
{
CANx->MOD&=~ (unsigned char)CAN_Mode_STM;
}
/* 配置时钟频率 */
CANx->BTR0 = (( unsigned char )( unsigned char )CAN_InitStruct->CAN_Prescaler -1) | \
(unsigned char)CAN_InitStruct->CAN_SJW << 6;
CANx->BTR1 = ((unsigned char)CAN_InitStruct->CAN_BS1) | \
((unsigned char)CAN_InitStruct->CAN_BS2 << 4);
/* 接收发送错误阈值,错误超过该值会产生错误中断
* 默认值是96有需要可以设置不同的值 */
//CANx->EWLR = 96;
/* 进入工作模式 */
can_set_start(CANx);
/* 返回初始化结果 */
return InitStatus;
}
void CAN_Transmit(CAN_TypeDef* CANx, CanMsg* TxMessage)
{
int i;
if (TxMessage->IDE == CAN_Id_Extended)
{
CANx->ID[0]= TxMessage ->ExtId>> 21;
CANx->ID[1]= TxMessage ->ExtId>> 13;
CANx->ID[2]= TxMessage ->ExtId>> 5;
CANx->ID[3]= TxMessage ->ExtId<<3;
CANx->IDE_RTR_DLC= (TxMessage ->IDE & 0x01) << 7 |\
(TxMessage ->RTR & 0x01) << 6 |\
(TxMessage ->DLC & 0x0F);
for( i=0;i<TxMessage ->DLC; i++)
{
CANx->BUF[i]= TxMessage->Data[i];
}
}
else if (TxMessage->IDE ==CAN_Id_Standard)
{
CANx->ID[0]= TxMessage ->StdId>> 3;
CANx->ID[1]= TxMessage ->StdId<< 5;
CANx->IDE_RTR_DLC= (TxMessage ->IDE & 0x01) << 7 |\
(TxMessage ->RTR & 0x01) << 6 |\
(TxMessage ->DLC & 0x0F);
CANx->ID[2]= TxMessage ->Data[0];
CANx->ID[3]= TxMessage ->Data[1];
for( i=0;i<TxMessage ->DLC-2; i++)
{
CANx->BUF[i]= TxMessage->Data[i+2];
}
}
CANx->CMR = CAN_CMR_TR ;
}
void CAN_Receive(CAN_TypeDef* CANx, CanMsg* RxMessage)
{
/* 获取 IDE */
RxMessage->IDE = (CANx->IDE_RTR_DLC & 0x80)>>7;
/* 获取 RTR */
RxMessage->RTR = (CANx->IDE_RTR_DLC & 0x40)>>4;
/* 获取 DLC */
RxMessage->DLC= (CANx->IDE_RTR_DLC & 0x0F);
if (RxMessage->IDE == CAN_Id_Standard)
{
RxMessage->StdId = CANx->ID[0]<<3 |CANx->ID[1]>>5 ;
/* 获取数据 */
RxMessage->Data[0] = (unsigned char)CANx->ID[2];
RxMessage->Data[1] = (unsigned char)CANx->ID[3];
RxMessage->Data[2] = (unsigned char)CANx->BUF[0];
RxMessage->Data[3] = (unsigned char)CANx->BUF[1];
RxMessage->Data[4] = (unsigned char)CANx->BUF[2];
RxMessage->Data[5] = (unsigned char)CANx->BUF[3];
RxMessage->Data[6] = (unsigned char)CANx->BUF[4];
RxMessage->Data[7] = (unsigned char)CANx->BUF[5];
}
else if (RxMessage->IDE == CAN_Id_Extended)
{
RxMessage->ExtId= CANx->ID[0]<<21 |CANx->ID[1]<<13|CANx->ID[2]<<5|CANx->ID[3]>>3 ;
/* 获取数据 */
RxMessage->Data[0] = (unsigned char)CANx->BUF[0];
RxMessage->Data[1] = (unsigned char)CANx->BUF[1];
RxMessage->Data[2] = (unsigned char)CANx->BUF[2];
RxMessage->Data[3] = (unsigned char)CANx->BUF[3];
RxMessage->Data[4] = (unsigned char)CANx->BUF[4];
RxMessage->Data[5] = (unsigned char)CANx->BUF[5];
RxMessage->Data[6] = (unsigned char)CANx->BUF[6];
RxMessage->Data[7] = (unsigned char)CANx->BUF[7];
}
}
static void can_reinit(CanPort_t *cap)
{
if (cap->id == CAN_ID0)
sys_soft_reset_from_isr(softreset_can0);
else if (cap->id == CAN_ID1)
sys_soft_reset_from_isr(softreset_can1);
CAN_Init(cap->pcan, &CanInitValue[cap->id]);
if (nCanFilterEnable[cap->id]) {
vCanSetFilter(cap, &CanFilterInitValue[cap->id]);
}
}
static void vCanIntHandler(void *param)
{
CanPort_t *cap = param;
CAN_TypeDef* CANx = cap->pcan;
CanMsg RxMessage;
unsigned char status;
/*读寄存器清除中断*/
status = CANx->IR;
/*接收中断*/
if (status & CAN_IR_RI)
{
/*清除RI 中断*/
CAN_Receive(CANx, &RxMessage);
CANx->CMR |= CAN_CMR_RRB;
CANx->CMR |= CAN_CMR_CDO;
//rt_hw_can_isr(&bxcan0, RT_CAN_EVENT_RX_IND);
//printf("Can0 int RX happened!\n");
if (!listLIST_IS_EMPTY(&cap->rxFreeList)) {
ListItem_t *item = listGET_HEAD_ENTRY(&cap->rxFreeList);
memcpy((void*)listGET_LIST_ITEM_VALUE(item), &RxMessage, sizeof(CanMsg));
uxListRemove(item);
vListInsertEnd(&cap->rxRevList, item);
xSemaphoreGiveFromISR(cap->xRev, 0);
} else {
/* can数据吞吐量大的时候中断处理函数添加打印会很容易造成数据接收溢出 */
;//printf("can rx buf is full, drop a message.\n");
}
}
/*发送中断*/
if (status & CAN_IR_TI)
{
//printf("Can0 int TX happened!\n");
xQueueSendFromISR(cap->tx_done, NULL, 0);
}
/*数据溢出中断*/
if (status & CAN_IR_DOI)
{
printf("Can%d int RX OF happened!\n", cap->id);
can_reinit(cap);
}
/*错误中断*/
if (status & CAN_IR_EI)
{
printf("Can%d int ERROR happened!\n", cap->id);
if (CANx->SR & CAN_SR_ES) {
/* 接收或者发送错误超过设置的阈值 */
can_reinit(cap);
} else if (CANx->SR & CAN_SR_BS) {
/* bus off错误退出reset模式控制器会在检测到128次11个连续的隐性位
* 后恢复到bus on状态 */
set_normal_mode(CANx);
}
}
}
static void can_transmit_thread(void *param)
{
CanPort_t *cap = param;
ListItem_t *item;
CanMsg *msgs;
for (;;) {
xSemaphoreTake(cap->xSend, portMAX_DELAY);
portENTER_CRITICAL();
if(!listLIST_IS_EMPTY(&cap->txSendList)) {
item = listGET_HEAD_ENTRY(&cap->txSendList);
msgs = (CanMsg *)listGET_LIST_ITEM_VALUE(item);
uxListRemove(item);
vListInsertEnd(&cap->txFreeList, item);
portEXIT_CRITICAL();
xQueueReset(cap->tx_done);
CAN_Transmit(cap->pcan, msgs);
if (xQueueReceive(cap->tx_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
/* transmit timeout, reset can */
can_reinit(cap);
}
} else {
portEXIT_CRITICAL();
}
}
}
CanPort_t *xCanOpen(uint32_t id)
{
int i;
if (id >= CAN_NUM) {
TRACE_INFO("Wrong input can id.\n");
return NULL;
}
if (pxCanPort[id] != NULL) {
TRACE_ERROR("Can %d is already in use.\n", id);
return NULL;
}
CanPort_t *cap = (CanPort_t *)pvPortMalloc(sizeof(CanPort_t));
if (cap == NULL) {
TRACE_ERROR("Out of memory for can%d.\n", id);
return NULL;
}
memset(cap, 0, sizeof(*cap));
cap->xRxMutex = xSemaphoreCreateMutex();
configASSERT(cap->xRxMutex);
cap->xRev = xSemaphoreCreateCounting(CAN_RX_BUF_NUM, 0);
configASSERT(cap->xRev);
cap->xSend = xSemaphoreCreateCounting(CAN_TX_BUF_NUM, 0);
configASSERT(cap->xSend);
cap->xTxMutex = xSemaphoreCreateMutex();
configASSERT(cap->xTxMutex);
cap->tx_done = xQueueCreate(1, 0);
configASSERT(cap->tx_done);
cap->id = id;
vListInitialise(&cap->rxRevList);
vListInitialise(&cap->rxFreeList);
for (i = 0; i < CAN_RX_BUF_NUM; i++) {
vListInitialiseItem(&canRxListItem[cap->id][i]);
listSET_LIST_ITEM_VALUE(&canRxListItem[cap->id][i], (uint32_t)&canRxMsg[cap->id][i]);
vListInsertEnd(&cap->rxFreeList, &canRxListItem[cap->id][i]);
}
vListInitialise(&cap->txSendList);
vListInitialise(&cap->txFreeList);
for (i = 0; i < CAN_TX_BUF_NUM; i++) {
vListInitialiseItem(&canTxListItem[cap->id][i]);
listSET_LIST_ITEM_VALUE(&canTxListItem[cap->id][i], (uint32_t)&canTxMsg[cap->id][i]);
vListInsertEnd(&cap->txFreeList, &canTxListItem[cap->id][i]);
}
cap->pcan = (CAN_TypeDef *)ulCanBase[id];
cap->irq = CAN0_IRQn + id;
request_irq(cap->irq, 0, vCanIntHandler, cap);
/* Create a task to transmit can msg */
if (xTaskCreate(can_transmit_thread, "cantrm", configMINIMAL_STACK_SIZE, cap,
configMAX_PRIORITIES / 3 + 2, &cap->xTxThread) != pdPASS) {
printf("create can transmit task fail.\n");
vCanClose(cap);
return NULL;
}
return pxCanPort[id] = cap;
};
void vCanInit(CanPort_t *cap, CanBPS_t baud, CanMode_t mode)
{
CAN_InitTypeDef CAN_InitStructure;
switch (mode)
{
case CAN_MODE_NORMAL:
CAN_InitStructure.CAN_Mode = 0x00;
break;
case CAN_MODE_LISEN:
CAN_InitStructure.CAN_Mode = CAN_Mode_LOM;
break;
case CAN_MODE_LOOPBACK:
CAN_InitStructure.CAN_Mode = CAN_Mode_STM;
break;
case CAN_MODE_LOOPBACKANLISEN:
CAN_InitStructure.CAN_Mode = CAN_Mode_STM|CAN_Mode_LOM;
break;
}
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
switch (baud)
{
case CAN1MBaud:
CAN_InitStructure.CAN_Prescaler = 11;
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
break;
case CAN800kBaud:
CAN_InitStructure.CAN_Prescaler = 11;
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
break;
case CAN500kBaud:
CAN_InitStructure.CAN_Prescaler = 22;
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
break;
case CAN250kBaud:
CAN_InitStructure.CAN_Prescaler = 44;
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
break;
case CAN125kBaud:
CAN_InitStructure.CAN_Prescaler = 44;
CAN_InitStructure.CAN_BS1 = CAN_BS1_14tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_3tq;
break;
default: //250K
CAN_InitStructure.CAN_Prescaler = 40;
CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_2tq;
break;
}
CanInitValue[cap->id] = CAN_InitStructure;
CAN_Init(cap->pcan, &CAN_InitStructure);
}
void vCanClose(CanPort_t *cap)
{
if (cap == NULL) return;
if (cap->xTxThread)
vTaskDelete(cap->xTxThread);
can_set_reset_mode(cap->pcan);
vQueueDelete(cap->tx_done);
vSemaphoreDelete(cap->xRev);
vSemaphoreDelete(cap->xSend);
vSemaphoreDelete(cap->xRxMutex);
vSemaphoreDelete(cap->xTxMutex);
free_irq(cap->irq);
vPortFree(cap);
pxCanPort[cap->id] = NULL;
}
void vCanSetFilter(CanPort_t *cap, CAN_FilterInitTypeDef * CAN_FilterInitStruct)
{
unsigned long rtr;
unsigned long fcase;
unsigned long ide;
unsigned long thisid, thisid1, thisid2;
unsigned long thismask, thismask1, thismask2;
unsigned long firstdata;
unsigned long datamask;
unsigned char CAN_FilterId0, CAN_FilterId1, CAN_FilterId2, CAN_FilterId3 ;
unsigned char CAN_FilterMaskId0, CAN_FilterMaskId1, CAN_FilterMaskId2, CAN_FilterMaskId3;
CAN_TypeDef* CANx = cap->pcan;
thisid = CAN_FilterInitStruct->ID;
thismask = CAN_FilterInitStruct->IDMASK;
thisid1 = (CAN_FilterInitStruct->ID & 0xFFFF0000 )>>16;
thismask1 = (CAN_FilterInitStruct->IDMASK & 0xFFFF0000 )>>16;
thisid2 = (CAN_FilterInitStruct->ID & 0x0000FFFF );
thismask2 = ( CAN_FilterInitStruct->IDMASK& 0x0000FFFF );
rtr = CAN_FilterInitStruct->RTR;
ide = CAN_FilterInitStruct->IDE;
firstdata = CAN_FilterInitStruct->First_Data;
datamask = CAN_FilterInitStruct->Data_Mask;
fcase = CAN_FilterInitStruct->MODE;
if(ide == 0)//标准帧
{
if(fcase == 0)// 0- 双滤波器模式
{
CAN_FilterId0 = thisid1>>3;
CAN_FilterMaskId0 = thismask1>>3;
CAN_FilterId1 = thisid1<<5 | firstdata>>4| rtr<<4;
CAN_FilterMaskId1 = thismask1<<4 | datamask>>4 ;
CAN_FilterId2 = thisid2 >> 3;
CAN_FilterMaskId2 = thismask2 >>3;
CAN_FilterId3 = firstdata & 0x0F | thisid2 <<5 | rtr<<4;
CAN_FilterMaskId3 = datamask <<4 ;
}
else if(fcase == 1)// 1-单滤波器模式
{
CAN_FilterId0 = thisid>>3;
CAN_FilterMaskId0 = thismask>>3;
CAN_FilterId1 = thisid<<5 | rtr<<4;
CAN_FilterMaskId1 = thismask<<5 ;
CAN_FilterMaskId1 |= 0x0F ;
CAN_FilterId2 = 0x00;
CAN_FilterMaskId2 = 0xFF;
CAN_FilterId3 = 0x00;
CAN_FilterMaskId3 = 0xFF ;
}
}
else if(ide == 1)//扩展帧
{
if(fcase == 0)// 0- 双滤波器模式
{
CAN_FilterId0 = thisid1>>8;
CAN_FilterMaskId0 = thismask1>>8;
CAN_FilterId1 = thisid1 ;
CAN_FilterMaskId1 = thismask1 ;
CAN_FilterId2 = thisid2>>8;
CAN_FilterMaskId2 = thismask2>>8;
CAN_FilterId3 = thisid2 ;
CAN_FilterMaskId3 = thismask2 ;
}
else if(fcase == 1)// 1-单滤波器模式
{
CAN_FilterId0 = thisid>>21;
CAN_FilterMaskId0 = thismask>>21;
CAN_FilterId1 = thisid>>13 ;
CAN_FilterMaskId1 = thismask>>13 ;
CAN_FilterId2 = thisid>>5;
CAN_FilterMaskId2 = thismask>>5;
CAN_FilterId3 = thisid<<3 | rtr<<2;
CAN_FilterMaskId3 = thismask<<3;
CAN_FilterMaskId3 |= 0x03;
}
}
/* 进入复位模式 */
can_set_reset_mode(CANx);
if(fcase == 1)// 1-单滤波器模式
{
/*单滤波模式 */
CANx->MOD |= (unsigned char)CAN_Mode_AFM;
}
else if(fcase == 0)// 0- 双滤波器模式
{
/*双滤波模式 */
CANx->MOD &=(~ (unsigned char) CAN_Mode_AFM);
}
CANx->IDE_RTR_DLC = CAN_FilterId0;
CANx->ID[0] = CAN_FilterId1;
CANx->ID[1] = CAN_FilterId2;
CANx->ID[2] = CAN_FilterId3;
CANx->ID[3] = CAN_FilterMaskId0;
CANx->BUF[0] = CAN_FilterMaskId1;
CANx->BUF[1] = CAN_FilterMaskId2;
CANx->BUF[2] = CAN_FilterMaskId3;
CanFilterInitValue[cap->id] = *CAN_FilterInitStruct;
nCanFilterEnable[cap->id] = 1;
/* 进入工作模式 */
can_set_start(CANx);
}
int iCanWrite(CanPort_t *cap, CanMsg* messages, int nmsgs, TickType_t xBlockTime)
{
TickType_t starttime = xTaskGetTickCount();
CanMsg *msgs = messages;
int count = 0;
xSemaphoreTake(cap->xTxMutex, portMAX_DELAY);
while (nmsgs) {
portENTER_CRITICAL();
if(!listLIST_IS_EMPTY(&cap->txFreeList)) {
ListItem_t *item = listGET_HEAD_ENTRY(&cap->txFreeList);
memcpy((void*)listGET_LIST_ITEM_VALUE(item), msgs, sizeof(CanMsg));
uxListRemove(item);
vListInsertEnd(&cap->txSendList, item);
portEXIT_CRITICAL();
msgs++;
count++;
nmsgs--;
xSemaphoreGive(cap->xSend);
} else {
portEXIT_CRITICAL();
if (xBlockTime && xTaskGetTickCount() - starttime > xBlockTime)
break;
vTaskDelay(1);
}
if (xBlockTime && xTaskGetTickCount() - starttime > xBlockTime)
break;
}
xSemaphoreGive(cap->xTxMutex);
return count;
}
int iCanRead(CanPort_t *cap, CanMsg* messages, int nmsgs, TickType_t xBlockTime)
{
TickType_t starttime = xTaskGetTickCount();
CanMsg *msgs = messages;
int count = 0;
xSemaphoreTake(cap->xRxMutex, portMAX_DELAY);
while (nmsgs) {
ListItem_t *item;
xSemaphoreTake(cap->xRev, pdMS_TO_TICKS(10));
portENTER_CRITICAL();
if(!listLIST_IS_EMPTY(&cap->rxRevList)) {
item = listGET_HEAD_ENTRY(&cap->rxRevList);
memcpy(msgs, (void*)listGET_LIST_ITEM_VALUE(item), sizeof(CanMsg));
uxListRemove(item);
vListInsertEnd(&cap->rxFreeList, item);
msgs++;
count++;
nmsgs--;
}
portEXIT_CRITICAL();
if (xBlockTime && xTaskGetTickCount() - starttime > xBlockTime)
break;
}
xSemaphoreGive(cap->xRxMutex);
return count;
}
int iCanGetReceiveErrorCount(CanPort_t *cap)
{
if (cap && cap->pcan)
return cap->pcan->RXERR;
return 0;
}
int iCanGetTransmitErrorCount(CanPort_t *cap)
{
if (cap && cap->pcan)
return cap->pcan->TXERR;
return 0;
}
#ifdef CAN_USE_TX_DEMO
static void can_txdemo_thread(void *param)
{
CanPort_t *cap = param;
// CanMsg txmsg = {0};
// txmsg.IDE = CAN_Id_Standard;
// txmsg.DLC = 4;
// txmsg.Data[0] = 0x11;
// txmsg.Data[1] = 0x22;
// txmsg.Data[2] = 0x33;
// txmsg.Data[3] = 0x44;
CanMsg txmsg = {0};
txmsg.StdId = 0x200;
txmsg.IDE = CAN_Id_Standard;
txmsg.DLC = 8;
txmsg.Data[0] = Get_sys_tcs_on_off()?0x8:0x00;
for (;;) {
txmsg.Data[0] = Get_sys_tcs_on_off()?0x8:0x00;
iCanWrite(cap, &txmsg, 1, 0);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
#endif
uint8_t can101_flag = 0;
uint8_t can_flag = 0;
static void can_rxdemo_thread(void *param)
{
CanPort_t *cap = param;
int tlv_data_value[8] = {0};
for (;;) {
CanMsg rxmsg[8] = {0};
int revlen;
int i, j;
if ((revlen = iCanRead(cap, rxmsg, 8, pdMS_TO_TICKS(10))) > 0 && (Get_sys_power_on_self_test() == 100)) {
for (i = 0; i < revlen; i++) {
for (j = 0; j < rxmsg[i].DLC; j++){
tlv_data_value[j] = rxmsg[i].Data[j];
}
// printf("can>> Id = %03x",rxmsg[i].StdId);
#if DEVICE_MXC_A58
switch(rxmsg[i].StdId){
case 0x37B:
if(can_flag != 10)
can_flag = 10;
tcsworking_event_handing(tlv_data_value);//TCS
break;
case 0x12B:
if(can_flag != 10)
can_flag = 10;
tcsswitch_event_handing(tlv_data_value);//tcs开关
break;
case 0x401:
break;
case 0x402://ECU故障码
break;
case 0xA5://ABS故障码
if(can_flag != 10)
can_flag = 10;
abs_dtc_event_handing(tlv_data_value);
break;
case 0xA3://ABS数据
break;
case 0x101://发送机数据
if(can101_flag != 10)
can101_flag = 10;
if(can_flag != 10)
can_flag = 10;
speed_event_handing(tlv_data_value);
break;
default:
break;
}
#elif DEVICE_MXC_A59
switch(rxmsg[i].StdId){
case 0x402://ECU故障码
if(can_flag != 10)
can_flag = 10;
A59_dtc_event_handing(tlv_data_value);
break;
// case 0x101://发送机数据
// if(can101_flag != 10)
// can101_flag = 10;
// if(can_flag != 10)
// can_flag = 10;
// speed_event_handing(tlv_data_value);
// break;
default:
break;
}
#else
switch(rxmsg[i].StdId){
#ifdef SPEED_DATA_CAN
case 0x101:
if(speed_flag != 5)
speed_flag = 5;
speed_event_handing(tlv_data_value);
break;
case 0x400:
tcs_control_event_handing(tlv_data_value);
break;
case 0x401:
mil_control_event_handing(tlv_data_value);//引擎开关
break;
#endif
case 0x402:
defect_code_event_handing(tlv_data_value);
break;
default:break;
}
#endif
}
}
}
}
int can_demo(void)
{
CanPort_t *cap = xCanOpen(CAN_ID0);
if (!cap) {
printf("open can %d fail.\n", CAN_ID0);
vTaskDelete(NULL);
return -1;
}
vCanInit(cap, CAN500kBaud, CAN_MODE_NORMAL);
#if 1
CAN_FilterInitTypeDef canfilter = {0};
/* 只接收ID的第0位为1的帧 */
canfilter.MODE = 1; /* 单滤波器模式 */
canfilter.ID = 0x37B;
canfilter.IDMASK = 0x7ff;
vCanSetFilter(cap, &canfilter);
#endif
/* Create a task to test read can msg */
if (xTaskCreate(can_rxdemo_thread, "canrx", configMINIMAL_STACK_SIZE, cap,
configMAX_PRIORITIES / 3, NULL) != pdPASS) {
printf("create can rxdemo task fail.\n");
return -1;
}
#ifdef CAN_USE_TX_DEMO
/* Create a task to test write can msg */
if (xTaskCreate(can_txdemo_thread, "cantx", configMINIMAL_STACK_SIZE, cap,
configMAX_PRIORITIES / 3 + 1, NULL) != pdPASS) {
printf("create can txdemo task fail.\n");
return -1;
}
#endif
return 0;
}

View File

@ -0,0 +1,431 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#define PLL_DIV_MASK 0xFF
#define PLL_DIV_OFFSET 0
#define PLL_NO_MASK 0x3
#define PLL_NO_OFFSET 12
#define PLL_ENA (1 << 14)
typedef struct {
uint32_t clkid;
uint32_t clktype;
uint32_t source;
int clksource[MAX_CLK_SOURCE_NUM];
int source_index;
int div;
uint32_t enable_reg[MAX_CLK_ENABLE_BITS];
int enable_offset[MAX_CLK_ENABLE_BITS];
int enable_bits;
union {
uint32_t fixed_freq;
struct {
int div;
int mult;
} fixed_fator_property;
struct {
uint32_t cfgreg;
uint32_t refclkreg;
uint32_t offset;
uint32_t mask;
} pll_property;
struct {
uint32_t cfgreg;
uint32_t analogreg;
} dds_property;
struct {
uint32_t cfgreg;
int index_offset;
int index_value;
uint32_t index_mask;
int div_offset;
int div_value;
uint32_t div_mask;
int div_mode;
uint32_t inv_reg;
int inv_offset;
uint32_t inv_mask;
int inv_value;
} sys_property;
} u;
} xClockProperty;
xClockProperty xClocks[] = {
{.clkid = CLK_XTAL32K, .clktype = FIXED_CLOCK, .u.fixed_freq = 32768},
{.clkid = CLK_XTAL24M, .clktype = FIXED_CLOCK, .u.fixed_freq = 24000000},
{.clkid = CLK_240MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 1, .u.fixed_fator_property.mult = 10},
{.clkid = CLK_12MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 2, .u.fixed_fator_property.mult = 1},
{.clkid = CLK_6MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 4, .u.fixed_fator_property.mult = 1},
{.clkid = CLK_CPUPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000088, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 0, .u.pll_property.mask = 1},
{.clkid = CLK_SYSPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x6000008c, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 1, .u.pll_property.mask = 1},
{.clkid = CLK_DDRPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000090, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 2, .u.pll_property.mask = 1},
{.clkid = CLK_VPUPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000094, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 3, .u.pll_property.mask = 1},
{.clkid = CLK_DDR, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_DDRPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 24,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 26, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_CPU, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 0,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 2, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_AHB, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_SYSPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 10, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_APB, .clktype = SYS_CLOCK, .clksource = {CLK_AHB},
.u.sys_property.cfgreg = 0x60000040,
.u.sys_property.div_offset = 16, .u.sys_property.div_mask = 0x3,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_DOUBLE,},
{.clkid = CLK_SPI0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.enable_reg = {0x60000064, 0x60000064}, .enable_offset = {30, 31}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000064, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 4, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_SPI1, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000064, .u.sys_property.index_offset = 20,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 16, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 10, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
#if DEVICE_TYPE_SELECT != EMMC_FLASH
{.clkid = CLK_SDMMC0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_SYSPLL},
.enable_reg = {0x60000048, 0x60000050, 0x60000058}, .enable_offset = {0, 12, 15}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000048, .u.sys_property.index_offset = 7,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,},
#else
{.clkid = CLK_SDMMC0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_SYSPLL},
.enable_reg = {0x60000048, 0x60000050, 0x60000058}, .enable_offset = {6, 12, 15}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000048, .u.sys_property.index_offset = 7,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = 10, .u.sys_property.div_mode = DIVMODE_PONEDOUBLE,},
#endif
{.clkid = CLK_LCD, .clktype = SYS_CLOCK, .clksource = {CLK_SYSPLL, CLK_VPUPLL, CLK_XTAL24M, CLK_XTAL24M},
.enable_reg = {0x60000050, 0x60000058, 0x60000058, 0x60000058}, .enable_offset = {3, 18, 17, 16}, .enable_bits = 4,
.u.sys_property.cfgreg = 0x6000004c, .u.sys_property.index_offset = 0,
.u.sys_property.index_mask = 0x3, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 3, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = 11, .u.sys_property.div_mode = DIVMODE_PLUSONE,
#ifdef LCD_CLK_INVERSE
.u.sys_property.inv_reg = 0x6000004c, .u.sys_property.inv_offset = 8,
.u.sys_property.inv_mask = 0x1, .u.sys_property.inv_value = 1,
#endif
},
{.clkid = CLK_TIMER, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_MFC, .clktype = SYS_CLOCK, .clksource = {CLK_SYSPLL, CLK_VPUPLL, CLK_XTAL24M},
.enable_reg = {0x60000050, 0x60000058}, .enable_offset = {6, 23}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x3, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 11, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_PWM, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_240MHZ},
.enable_reg = {0x60000054, 0x60000058}, .enable_offset = {10, 10}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000044, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 4, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_CAN0, .clktype = SYS_CLOCK, .clksource = {CLK_APB},
.enable_reg = {0x60000054}, .enable_offset = {19}, .enable_bits = 1,
.u.sys_property.div_value = 1,},
{.clkid = CLK_CAN1, .clktype = SYS_CLOCK, .clksource = {CLK_APB},
.enable_reg = {0x60000054}, .enable_offset = {20}, .enable_bits = 1,
.u.sys_property.div_value = 1,},
{.clkid = CLK_ADC, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M},
.enable_reg = {0x60000054, 0x60000058}, .enable_offset = {14, 13}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.div_offset = 16,
.u.sys_property.div_mask = 0x7fff, .u.sys_property.div_value = 642,
.u.sys_property.div_mode = DIVMODE_PONEDOUBLE,},
{.clkid = CLK_I2S, .clktype = SYS_CLOCK, .clksource = {CLK_240MHZ, CLK_SYSPLL},
.enable_reg = {0x60000054, 0x60000058, 0x60000058}, .enable_offset = {12, 12, 11}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000044, .u.sys_property.index_offset = 16,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_value = 1,},
{.clkid = CLK_I2S1, .clktype = SYS_CLOCK, .clksource = {CLK_240MHZ, CLK_SYSPLL},
.enable_reg = {0x60000054, 0x60000144, 0x60000144}, .enable_offset = {22, 1, 0}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000140, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_value = 1,},
};
#define CLOCK_NUM (sizeof(xClocks) / sizeof(xClocks[0]))
static xClockProperty *clk_get(uint32_t clkid)
{
int i;
for (i = 0; i < CLOCK_NUM; i++) {
if (xClocks[i].clkid == clkid) {
return &xClocks[i];
}
}
return NULL;
}
static uint32_t clk_fixed_get_rate(xClockProperty *clk)
{
return clk->u.fixed_freq;
}
static uint32_t clk_fixed_factor_get_rate(xClockProperty *clk)
{
xClockProperty *parentclk = clk_get(clk->clksource[0]);
configASSERT(parentclk);
return clk_fixed_get_rate(parentclk) * clk->u.fixed_fator_property.mult
/ clk->u.fixed_fator_property.div;
}
static uint32_t clk_pll_get_rate(xClockProperty *clk)
{
uint32_t parent_rate;
uint32_t div, no, reg;
configASSERT(clk);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
reg = readl(clk->u.pll_property.cfgreg);
no = (reg >> PLL_NO_OFFSET) & PLL_NO_MASK;
div = (reg >> PLL_DIV_OFFSET) & PLL_DIV_MASK;
return (parent_rate * div) / (1 << no);
}
static uint32_t clk_sys_get_rate(xClockProperty *clk)
{
uint32_t parent_rate;
configASSERT(clk);
configASSERT(clk->div);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
return parent_rate / clk->div;
}
static void clk_pll_init(xClockProperty *clk)
{
configASSERT(clk);
clk->source_index = (readl(clk->u.pll_property.refclkreg) >> clk->u.pll_property.offset)
& clk->u.pll_property.mask;
}
static int clk_get_div(int div, int divmode)
{
switch(divmode) {
case DIVMODE_NOZERO:
div = div ? div : 1;
break;
case DIVMODE_PLUSONE:
div = div + 1;
break;
case DIVMODE_DOUBLE:
div *= 2;
break;
case DIVMODE_EXPONENT:
div = 1 << div;
break;
case DIVMODE_PONEDOUBLE:
div = (div + 1) * 2;
break;
}
return div;
}
static __INLINE int clk_set_div(int div, int divmode)
{
switch(divmode) {
case DIVMODE_PLUSONE:
div = div - 1;
break;
case DIVMODE_DOUBLE:
div /= 2;
break;
case DIVMODE_EXPONENT:
div = fls(div) - 1;
break;
case DIVMODE_PONEDOUBLE:
div = div / 2 - 1;
break;
}
return div;
}
static void clk_sys_set_rate(xClockProperty *clk, uint32_t freq)
{
int div;
uint32_t reg;
uint32_t parent_rate;
configASSERT(clk);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
div = DIV_ROUND_UP(parent_rate, freq);
clk->div = div;
div = clk_set_div(div, clk->u.sys_property.div_mode);
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.div_mask << clk->u.sys_property.div_offset);
reg |= (div & clk->u.sys_property.div_mask) << clk->u.sys_property.div_offset;
writel(reg, clk->u.sys_property.cfgreg);
}
static void clk_sys_init(xClockProperty *clk)
{
uint32_t reg;
uint32_t val;
configASSERT(clk);
if (clk->u.sys_property.index_value >= 0 && clk->u.sys_property.index_mask) {
clk->source_index = clk->u.sys_property.index_value;
val = clk->u.sys_property.index_value == 3 ? 4 : clk->u.sys_property.index_value;
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.index_mask << clk->u.sys_property.index_offset);
reg |= (val & clk->u.sys_property.index_mask) << clk->u.sys_property.index_offset;
writel(reg, clk->u.sys_property.cfgreg);
} else if (clk->u.sys_property.index_mask) {
reg = readl(clk->u.sys_property.cfgreg);
val = (reg >> clk->u.sys_property.index_offset) & clk->u.sys_property.index_mask;
clk->source_index = val == 4 ? 3 : val;
}
if (clk->u.sys_property.div_value >= 0 && clk->u.sys_property.div_mask) {
val = clk_set_div(clk->u.sys_property.div_value, clk->u.sys_property.div_mode);
clk->div = clk_get_div(val, clk->u.sys_property.div_mode);
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.div_mask << clk->u.sys_property.div_offset);
reg |= (val & clk->u.sys_property.div_mask) << clk->u.sys_property.div_offset;
writel(reg, clk->u.sys_property.cfgreg);
} else if (clk->u.sys_property.div_mask) {
reg = readl(clk->u.sys_property.cfgreg);
val = (reg >> clk->u.sys_property.div_offset) & clk->u.sys_property.div_mask;
clk->div = clk_get_div(val, clk->u.sys_property.div_mode);
} else if (clk->u.sys_property.div_value > 0) {
clk->div = clk->u.sys_property.div_value;
} else {
clk->div = 1;
}
if (clk->u.sys_property.inv_reg) {
reg = readl(clk->u.sys_property.inv_reg);
reg &= ~(clk->u.sys_property.inv_mask << clk->u.sys_property.inv_offset);
reg |= (clk->u.sys_property.inv_value & clk->u.sys_property.inv_mask)
<< clk->u.sys_property.inv_offset;
writel(reg, clk->u.sys_property.inv_reg);
}
}
static void clk_sys_enable(xClockProperty *clk)
{
int i;
configASSERT(clk);
for (i = 0; i < clk->enable_bits; i++)
writel(readl(clk->enable_reg[i]) | (1 << clk->enable_offset[i]),
clk->enable_reg[i]);
}
static void clk_sys_disable(xClockProperty *clk)
{
int i;
configASSERT(clk);
for (i = 0; i < clk->enable_bits; i++)
writel(readl(clk->enable_reg[i]) & ~(1 << clk->enable_offset[i]),
clk->enable_reg[i]);
}
void vClkInit(void)
{
int i;
xClockProperty *clk;
for (i = 0; i < CLOCK_NUM; i++) {
clk = &xClocks[i];
if (clk->clktype == PLL_CLOCK) {
clk_pll_init(clk);
} else if (clk->clktype == SYS_CLOCK) {
clk_sys_init(clk);
}
}
}
uint32_t ulClkGetRate(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return 0;
switch (clk->clktype) {
case FIXED_CLOCK:
return clk_fixed_get_rate(clk);
case FIXED_FACTOR_CLOCK:
return clk_fixed_factor_get_rate(clk);
case PLL_CLOCK:
return clk_pll_get_rate(clk);
case SYS_CLOCK:
return clk_sys_get_rate(clk);
}
return 0;
}
void vClkSetRate(uint32_t clkid, uint32_t freq)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_set_rate(clk, freq);
}
void vClkEnable(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_enable(clk);
}
void vClkDisable(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_disable(clk);
}

View File

@ -0,0 +1,374 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#if LCD_INTERFACE_TYPE == LCD_INTERFACE_CPU
/*
Cpu 屏接口模式。
*/
#define LCD_BASE REGS_LCD_BASE
#define CPU_SCR_SOFT (56 * 4)
#define CPU_SCR_CTRL (57 * 4)
#define LCD_PARAM1 4
#define SYS_BASE REGS_SYSCTL_BASE
#define WR_Com(address) WriteCpuCmd(CPU_PANEL_DATA,address);
#define WR_D(data) WriteCpuData(CPU_PANEL_DATA,data);
/*
Cpu 控制信号数据位。
*/
#define RS_1 0X200000
#define CS_1 0X100000
#define RD_1 0X080000
#define WR_1 0X040000
#define RS_0 (~0X200000)
#define CS_0 (~0X100000)
#define RD_0 (~0X080000)
#define WR_0 (~0X040000)
void ConfigCpuInit(void)
{
u32 val = 0;
val=readl(LCD_BASE + LCD_PARAM1);
val&=~(BIT(19)|BIT(18)|BIT(17)|BIT(3)|BIT(2)|BIT(1));
val|=(0x06<<1);//CPU MODE RS RD CS NORMAL
writel(val, LCD_BASE + LCD_PARAM1);
}
void ConfigCpuPadmux(u8 Interface)
{
u32 val = 0;
//cpu_screen_rst cpu_scr_cs cpu_scr_wr cpu_scr_rs cpu_scr_rd
val=readl(SYS_BASE + SYS_PAD_CTRL05);
val &= ~0x00FFC000;
// rd rs wr cs reset
val |= 1<<22|1<<20|1<<18|1<<16|1<<14;
writel(val,SYS_BASE + SYS_PAD_CTRL05);
switch(Interface) {
case CPU_PANEL_18BIT_MODE:
val = readl(SYS_BASE + SYS_PAD_CTRL04);
val &= ~0xFFFFFFFF;
//d15 d14 d13 d12 d11 d10 d19 d8 d7 d6 d5 d4 d3 d2 d1 d0
val |= 1<<30|1<<28|1<<26|1<<24|1<<22|1<<20|1<<18|1<<16|1<<14|1<<12|1<<10|1<<8|1<<6|1<<4|1<<2|1<<0;//_018BIT_MODE 接D0-D17
writel(val,SYS_BASE + SYS_PAD_CTRL04);
val = readl(SYS_BASE + SYS_PAD_CTRL05);
val &= ~0x0000000f;
// D17 D16
val |= 1<<2|1<<0;
writel(val,SYS_BASE + SYS_PAD_CTRL05);
break;
case CPU_PANEL_16BIT_MODE:
val =readl(SYS_BASE + SYS_PAD_CTRL04);
val &= ~0xFFFFFFF0;
//d15 d14 d13 d12 d11 d10 d19 d8 d7 d6 d5 d4 d3 d2
val |= 1<<30|1<<28|1<<26|1<<24|1<<22|1<<20|1<<18|1<<16|1<<14|1<<12|1<<10|1<<8|1<<6|1<<4;//_016BIT_MODE 接D2-D17
writel(val,SYS_BASE + SYS_PAD_CTRL04);
val = readl(SYS_BASE + SYS_PAD_CTRL05);
val &= ~0x0000000f;
// D17 D16
val |= 1<<2|1<<0;
writel(val,SYS_BASE + SYS_PAD_CTRL05);
break;
case CPU_PANEL_9BIT_MODE:
val = readl(SYS_BASE + SYS_PAD_CTRL04);
val &= ~0xFFFC0000;
//d15 d14 d13 d12 d11 d10 d19
val |= 1<<30|1<<28|1<<26|1<<24|1<<22|1<<20|1<<18;//_09BIT_MODE 接D9-D17
writel(val,SYS_BASE + SYS_PAD_CTRL04);
val = readl(SYS_BASE + SYS_PAD_CTRL05);
val &= ~0x0000000f;
// D17 D16
val |= 1<<2|1<<0;
writel(val,SYS_BASE + SYS_PAD_CTRL05);
break;
case CPU_PANEL_8BIT_MODE:
val = readl(SYS_BASE + SYS_PAD_CTRL04);
val &= ~0xFFF00000;
//d15 d14 d13 d12 d11 d10
val |= 1<<30|1<<28|1<<26|1<<24|1<<22|1<<20;//_08BIT_MODE 接D10-D17
writel(val,SYS_BASE + SYS_PAD_CTRL04);
val=readl(SYS_BASE + SYS_PAD_CTRL05);
val &= ~0x0000000f;
// D17 D16
val |= 1<<2|1<<0;
writel(val,SYS_BASE + SYS_PAD_CTRL05);
break;
default:
break;
}
}
void CPU_Pannel_Reset(void)
{
u32 val=0;
val=readl(LCD_BASE + CPU_SCR_CTRL);
val|= BIT(9);
writel(val,LCD_BASE + CPU_SCR_CTRL);//cpu reset 1
mdelay(20);
val &= ~(BIT(9));
writel(val,LCD_BASE + CPU_SCR_CTRL);//cpu reset 0
mdelay(20);
val|= BIT(9);
writel(val,LCD_BASE + CPU_SCR_CTRL);//cpu reset 1
mdelay(20);
}
void SetCpuSoftwareMode()
{
u32 val=0;
val=readl(LCD_BASE + CPU_SCR_CTRL);
val |=BIT(0);
writel(val,LCD_BASE + CPU_SCR_CTRL);
}
void SetCpuHardwareMode()
{
u32 val=0;
val=readl(LCD_BASE + CPU_SCR_CTRL);
val &=~(BIT(0));
writel(val,LCD_BASE + CPU_SCR_CTRL);
}
u32 transfor18BitDate(u8 Interface,u16 dat)
{
u32 tempDat = 0x00;
switch(Interface)
{
case CPU_PANEL_18BIT_MODE:
tempDat = (dat>>8)&0xff;
tempDat |= dat & 0xff;
break;
case CPU_PANEL_16BIT_MODE:
tempDat = (dat>>8)&0xff;
tempDat <<= 9;
tempDat |= dat & 0xff;
tempDat <<= 1;
break;
case CPU_PANEL_9BIT_MODE:
tempDat = dat&0xff;
tempDat <<= 10;
break;
case CPU_PANEL_8BIT_MODE:
tempDat = dat&0xff;
tempDat <<= 10;
break;
default:
break;
}
return tempDat;
}
void WriteCpuCmd(u8 Interface,u16 Val)
{
u32 tmpDat;
tmpDat=transfor18BitDate(Interface,Val);
tmpDat |=( RD_1);
tmpDat &=( CS_0);
tmpDat &=( RS_0);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat&=( WR_0);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat |=( WR_1);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat |=( CS_1);
tmpDat &=( RS_0);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
}
void WriteCpuData(u8 Interface,u16 Val)
{
u32 tmpDat;
tmpDat=transfor18BitDate(Interface,Val);
tmpDat |=( RD_1);
tmpDat &=( CS_0);
tmpDat |=( RS_1);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat&=( WR_0);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat |=( WR_1);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
tmpDat |=( CS_1);
tmpDat |=( RS_1);
writel(tmpDat,LCD_BASE + CPU_SCR_SOFT);
}
void InitalCPU_TCXD035ABFON_8bit(void)
{
uint16_t i,j,k=0;
WR_Com(0x5E); // SET password
WR_D(0xA5);
mdelay(100);
WR_Com(0x49); // SET password
WR_D(0x0E);
WR_D(0x00);
WR_D(0x00);
WR_D(0xA5);
WR_Com(0x61); // Set Power Control
WR_D(0x8F); // n=2, all power on
WR_D(0x44);
WR_D(0x02);
WR_D(0xA5);
WR_Com(0x5A); // Set Power Control
WR_D(0x70); // n=2, all power on
WR_D(0x21);
WR_D(0xA5);
WR_D(0xA5);
WR_Com(0x71); //Set Electronic Volume1
WR_D(0x1E); //VCOM=-0.675V
WR_D( 0x0B); //VGH=15V
WR_D(0x0B); //VGL=-10V
WR_D(0xA5);
WR_Com( 0x72); //Set Electronic Volume2
WR_D(0x17); //GVDD=5.4V
WR_D(0x17); //GVCL=-5.4V
WR_D(0xA5);
WR_D(0xA5);
WR_Com( 0x81); //Set Gamma Positive1
WR_D(0x00);
WR_D(0x16);
WR_D(0x1B);
WR_D(0x1C);
WR_Com(0x82); //Set Gamma Positive2
WR_D(0x1E);
WR_D(0x1F);
WR_D(0x20);
WR_D(0x21);
WR_Com(0x83); //Set Gamma Positive3
WR_D(0x23);
WR_D(0x24);
WR_D(0x26);
WR_D(0x28);
WR_Com(0x84); //Set Gamma Positive4
WR_D(0x2B);
WR_D(0x2F);
WR_D(0x34);
WR_D(0x3F);
WR_Com(0x89); //Set Gamma Negative1
WR_D(0x00);
WR_D(0x16);
WR_D(0x1B);
WR_D(0x1C);
WR_Com(0x8A); //Set Gamma Negative2
WR_D(0x1E);
WR_D(0x1F);
WR_D(0x20);
WR_D(0x21);
WR_Com(0x8B); //Set Gamma Negative3
WR_D(0x23);
WR_D(0x24);
WR_D(0x26);
WR_D(0x28);
WR_Com(0x8C); //Set Gamma Negative4
WR_D(0x2B);
WR_D(0x2F);
WR_D(0x34);
WR_D(0x3F);
WR_Com(0x21); //Set Memory Address Control
WR_D(0x01);
WR_D(0xA5);
WR_D(0xA5);
WR_D(0xA5);
WR_Com(0x13); // SLEEP OUT
WR_D(0xA5); // VOFREG
mdelay(100);
WR_Com(0x25); //Set Page Address
WR_D(0x02); //from page0
WR_D(0xA5); //to page 159
WR_D(0xA5);
WR_D(0xA5);
WR_Com(0x22); //Set Page Address
WR_D(0x00); //from page0
WR_D(0x9F); //to page 159
WR_D(0x00);
WR_D(0xA5);
WR_Com(0x24);
WR_D(0x00);
WR_D(0xA5);
WR_D(0xA5);
WR_D(0xA5);
WR_Com(0x23); //Set Column Address
WR_D(0x00); //from col0
WR_D(0x00);
WR_D(0x00); //to col239
WR_D(0xEF);
WR_Com(0x12);
WR_D(0xA5);
WR_Com(0x3a);
WR_D(0xA5);
for(i=0;i<38400;i++)
WR_D(0xff);
// 16灰阶
for(k=0;k<16;k++) {
for(i=0;i<10;i++) {
for(j=0;j<240;j++)
WR_D(k<<4|k);
}
}
};
void Cpulcd_Init(void)
{
ConfigCpuInit();
ConfigCpuPadmux(CPU_PANEL_DATA);
CPU_Pannel_Reset();
SetCpuSoftwareMode();
InitalCPU_TCXD035ABFON_8bit();
SetCpuHardwareMode();
}
#endif

View File

@ -0,0 +1,349 @@
#include "FreeRTOS.h"
#include "chip.h"
#define DMA_CH_NUM 4
#define DMA_BLOCK_SIZE 0xfff
#define rDMACIntStatus *((volatile unsigned int *)(REGS_DMAC_BASE + 0x000))
#define rDMACIntTCStatus *((volatile unsigned int *)(REGS_DMAC_BASE + 0x004))
#define rDMACIntTCClear *((volatile unsigned int *)(REGS_DMAC_BASE + 0x008))
#define rDMACIntErrorStatus *((volatile unsigned int *)(REGS_DMAC_BASE + 0x00C))
#define rDMACIntErrClr *((volatile unsigned int *)(REGS_DMAC_BASE + 0x010))
#define rDMACRawIntTCStatus *((volatile unsigned int *)(REGS_DMAC_BASE + 0x014))
#define rDMACRawIntErrorStatus *((volatile unsigned int *)(REGS_DMAC_BASE + 0x018))
#define rDMACEnbldChns *((volatile unsigned int *)(REGS_DMAC_BASE + 0x01C))
#define rDMACSoftBReq *((volatile unsigned int *)(REGS_DMAC_BASE + 0x020))
#define rDMACSoftSReq *((volatile unsigned int *)(REGS_DMAC_BASE + 0x024))
#define rDMACSoftLBReq *((volatile unsigned int *)(REGS_DMAC_BASE + 0x028))
#define rDMACSoftLSReq *((volatile unsigned int *)(REGS_DMAC_BASE + 0x02C))
#define rDMACConfiguration *((volatile unsigned int *)(REGS_DMAC_BASE + 0x030))
#define rDMACSync *((volatile unsigned int *)(REGS_DMAC_BASE + 0x034))
#define rDMACCxSrcAddr(x) *((volatile unsigned int *)(REGS_DMAC_BASE + 0x100 + 0x00 + (x)*0x20))
#define rDMACCxDestAddr(x) *((volatile unsigned int *)(REGS_DMAC_BASE + 0x100 + 0x04 + (x)*0x20))
#define rDMACCxLLI(x) *((volatile unsigned int *)(REGS_DMAC_BASE + 0x100 + 0x08 + (x)*0x20))
#define rDMACCxControl(x) *((volatile unsigned int *)(REGS_DMAC_BASE + 0x100 + 0x0C + (x)*0x20))
#define rDMACCxConfiguration(x) *((volatile unsigned int *)(REGS_DMAC_BASE + 0x100 + 0x10 + (x)*0x20))
static struct dma_chan dma_ch[DMA_CH_NUM] = {0};
static SemaphoreHandle_t dma_mutex;
static QueueHandle_t dma_m2m_done = NULL;
struct dma_chan *dma_request_channel(int favorite_ch)
{
int i;
configASSERT (favorite_ch >= 0 && favorite_ch < DMA_CH_NUM)
xSemaphoreTake(dma_mutex, portMAX_DELAY);
if (!dma_ch[favorite_ch].in_use) {
dma_ch[favorite_ch].chan_id = favorite_ch;
dma_ch[favorite_ch].in_use = 1;
xSemaphoreGive(dma_mutex);
return &dma_ch[favorite_ch];
}
for (i = 0; i < DMA_CH_NUM; i++) {
if (!dma_ch[i].in_use) {
dma_ch[i].chan_id = i;
dma_ch[i].in_use = 1;
xSemaphoreGive(dma_mutex);
return &dma_ch[i];
}
}
xSemaphoreGive(dma_mutex);
return NULL;
}
void dma_release_channel(struct dma_chan *chan)
{
/* This channel is not in use, bail out */
if (!chan->in_use)
return;
dma_stop_channel(chan);
xSemaphoreTake(dma_mutex, portMAX_DELAY);
/* This channel is not in use anymore, free it */
chan->irq_callback = NULL;
chan->callback_param = NULL;
chan->in_use = 0;
xSemaphoreGive(dma_mutex);
}
/*
* Fix sconfig's burst size according to dw_dmac. We need to convert them as:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
*
* NOTE: burst size 2 is not supported by controller.
*
* This can be done by finding least significant bit set: n & (n - 1)
*/
static void convert_burst(u32 *maxburst)
{
if (*maxburst > 1)
*maxburst = fls(*maxburst) - 2;
else
*maxburst = 0;
}
int dma_config_channel(struct dma_chan *chan, struct dma_config *config)
{
unsigned int ctl;
unsigned int cfg;
unsigned int src_width, dst_width;
unsigned int src_id = 0, dst_id = 0;
unsigned int di = 0, si = 0;
unsigned int data_width = (1 << DMA_BUSWIDTH_4_BYTES);
convert_burst(&config->src_maxburst);
convert_burst(&config->dst_maxburst);
if (config->direction == DMA_MEM_TO_DEV) {
src_width = __ffs(data_width | config->src_addr | config->transfer_size);
dst_width = config->dst_addr_width;
dst_id = config->dst_id;
si = 1;
} else if (config->direction == DMA_DEV_TO_MEM) {
src_width = config->src_addr_width;
dst_width = __ffs(data_width | config->dst_addr | config->transfer_size);
src_id = config->src_id;
di = 1;
} else if (config->direction == DMA_MEM_TO_MEM) {
src_width = __ffs(data_width | config->src_addr | config->transfer_size);
dst_width = __ffs(data_width | config->dst_addr | config->transfer_size);
si = 1;
di = 1;
}
ctl = (1 << 31) | /* [31] I Read/write Terminal count interrupt enable bit */
(0 << 28) | /* [30:28] Prot Read/write Protection */
(di << 27) | /* [27] DI Read/write Destination increment */
(si << 26) | /* [26] SI Read/write Source increment */
(0 << 25) | /* [25] D Read/write Destination AHB master select */
(1 << 24) | /* [24] S Read/write Source AHB master select */
(dst_width << 21) | /* [23:21] DWidth Read/write Destination transfer width */
(src_width << 18) | /* [20:18] SWidth Read/write Source transfer width */
(config->dst_maxburst << 15) | /* [17:15] DBSize Read/write Destination burst size */
(config->src_maxburst << 12) | /* [14:12] SBSize Read/write Source burst size */
0; /* [11:0] TransferSize Read/write Transfer size */
cfg = (0 << 18) | /* [18] H Read/write Halt */
(0 << 16) | /* [16] L Read/write Lock */
(1 << 15) | /* [15] ITC Read/write Terminal count interrupt mask */
(1 << 14) | /* [14] IE Read/write Interrupt error mask */
(config->direction << 11) | /* [13:11] FlowCntrl Read/write Flow control and transfer type */
(dst_id << 6) | /* [9:6] DestPeripheral Read/write Destination peripheral */
(src_id << 1) | /* [4:1] SrcPeripheral Read/write Source peripheral */
0; /* [0] Channel enable */
if ((config->transfer_size >> src_width) > DMA_BLOCK_SIZE) {
unsigned int blk_size = config->transfer_size >> src_width;
int lli_num;
int i;
lli_num = (blk_size + DMA_BLOCK_SIZE - 1) / DMA_BLOCK_SIZE - 1;
if (chan->lli) {
vPortFree(chan->lli);
chan->lli = NULL;
}
chan->lli = pvPortMalloc(sizeof(struct dma_lli) * lli_num);
if (!chan->lli)
return -ENOMEM;
for (i = 0; i < lli_num - 1; i++) {
chan->lli[i].src_addr = config->src_addr + (si ? (i + 1) : 0) * (DMA_BLOCK_SIZE << src_width);
chan->lli[i].dst_addr = config->dst_addr + (di ? (i + 1) : 0) * (DMA_BLOCK_SIZE << src_width);
chan->lli[i].next_lli = (unsigned int)&chan->lli[i + 1];
chan->lli[i].control = ctl | DMA_BLOCK_SIZE;
if (!config->blkint_en)
chan->lli[i].control &= ~(1 << 31);
}
chan->lli[i].src_addr = config->src_addr + (si ? (i + 1) : 0) * (DMA_BLOCK_SIZE << src_width);
chan->lli[i].dst_addr = config->dst_addr + (di ? (i + 1) : 0) * (DMA_BLOCK_SIZE << src_width);
chan->lli[i].next_lli = 0;
chan->lli[i].control = ctl | (blk_size - DMA_BLOCK_SIZE * lli_num);
CP15_clean_dcache_for_dma((unsigned int)chan->lli,
(unsigned int)chan->lli + sizeof(struct dma_lli) * lli_num);
rDMACCxSrcAddr(chan->chan_id) = config->src_addr;
rDMACCxDestAddr(chan->chan_id) = config->dst_addr;
rDMACCxLLI(chan->chan_id) = (unsigned int)chan->lli | 1;
rDMACCxControl(chan->chan_id) = ctl & ~(1 << 31) | DMA_BLOCK_SIZE;
rDMACCxConfiguration(chan->chan_id) = cfg;
} else {
rDMACCxSrcAddr(chan->chan_id) = config->src_addr;
rDMACCxDestAddr(chan->chan_id) = config->dst_addr;
rDMACCxLLI(chan->chan_id) = 0;
rDMACCxControl(chan->chan_id) = ctl | (config->transfer_size >> src_width);
rDMACCxConfiguration(chan->chan_id) = cfg;
}
return 0;
}
int dma_register_complete_callback(struct dma_chan *chan,
void (*callback)(void *param, unsigned int mask),
void *callback_param)
{
chan->irq_callback = callback;
chan->callback_param = callback_param;
return 0;
}
int dma_start_channel(struct dma_chan *chan)
{
configASSERT(chan && chan->chan_id < DMA_CH_NUM);
rDMACCxConfiguration(chan->chan_id) |= (1 << 0);
return 0;
}
int dma_stop_channel(struct dma_chan *chan)
{
unsigned int timeout = xTaskGetTickCount() + 1000;
configASSERT(chan && chan->chan_id < DMA_CH_NUM);
xSemaphoreTake(dma_mutex, portMAX_DELAY);
if(!(rDMACEnbldChns & (1 << chan->chan_id))) {
xSemaphoreGive(dma_mutex);
return 0;
}
// A channel can be disabled by clearing the Enable bit.
rDMACCxConfiguration(chan->chan_id) &= ~1;
// waiting
while(rDMACEnbldChns & (1 << chan->chan_id)) {
if(xTaskGetTickCount() >= timeout) {
printf ("dma_stop_channel %d timeout\n", chan->chan_id);
xSemaphoreGive(dma_mutex);
return -1;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
if (chan->lli) {
vPortFree(chan->lli);
chan->lli = NULL;
}
xSemaphoreGive(dma_mutex);
return 0;
}
static void dma_m2m_callback(void *param, unsigned int mask)
{
if(dma_m2m_done)
xQueueSendFromISR(dma_m2m_done, NULL, 0);
}
int dma_m2mcpy(unsigned int dst_addr, unsigned int src_addr, int size)
{
struct dma_config cfg = {0};
int ret = -1;
struct dma_chan *dma_ch = dma_request_channel(0);
if (!dma_ch) {
printf("%s() dma_request_channel fail.\n", __func__);
return -1;
}
cfg.dst_addr_width = DMA_BUSWIDTH_4_BYTES;
cfg.dst_maxburst = 256;
cfg.src_addr_width = DMA_BUSWIDTH_4_BYTES;
cfg.src_maxburst = 256;
cfg.transfer_size = size;
cfg.src_addr = src_addr;
cfg.dst_addr = dst_addr;
cfg.direction = DMA_MEM_TO_MEM;
dma_clean_range(src_addr, src_addr + size);
dma_inv_range(dst_addr, dst_addr + size);
ret = dma_config_channel(dma_ch, &cfg);
if (ret) {
printf("%s, dma_config_channel failed.\n", __func__);
goto exit;
}
dma_register_complete_callback(dma_ch, dma_m2m_callback, NULL);
xQueueReset(dma_m2m_done);
dma_start_channel(dma_ch);
if (xQueueReceive(dma_m2m_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
printf("dma_m2mcpy wait timeout.\n");
ret = -ETIMEDOUT;
goto exit;
}
dma_stop_channel(dma_ch);
ret = 0;
exit:
if(dma_ch)
dma_release_channel(dma_ch);
return ret;
}
static void dma_int_handler(void *param)
{
unsigned int err_status, tfr_status;
struct dma_chan *chan;
unsigned int irqmask = 0;
int i;
err_status = rDMACIntErrorStatus;
tfr_status = rDMACIntTCStatus;
rDMACIntTCClear = tfr_status;
rDMACIntErrClr = err_status;
for(i= 0; i< DMA_CH_NUM; i++) {
irqmask = 0;
if (err_status & (1 << i)) {
irqmask |= DMA_INT_ERR;
}
if (tfr_status & (1 << i)) {
irqmask |= DMA_INT_TC;
}
if (!irqmask)
continue;
chan = &dma_ch[i];
if (chan->irq_callback)
chan->irq_callback(chan->callback_param, irqmask);
}
}
int dma_init(void)
{
dma_mutex = xSemaphoreCreateMutex();
dma_m2m_done = xQueueCreate(1, 0);
sys_soft_reset(softreset_dma);
request_irq(DMA_IRQn, 0, dma_int_handler, NULL);
/* Clear all interrupts on all channels. */
rDMACIntTCClear = 0xff;
rDMACIntErrClr = 0xff;
rDMACConfiguration |= (1<<0); // [0] E Read/write PrimeCell DMAC enable
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,859 @@
#include "FreeRTOS.h"
#include "chip.h"
#include <string.h>
#define SPI1_CS0_GPIO 23
#define USE_DMA_THRESHOLD 32
#define MALLOC_DMA_MEM_SIZE 0x1000
#define ARK_ECSPI_RXDATA 0x50
#define ARK_ECSPI_TXDATA 0x460
/* generic defines to abstract from the different register layouts */
#define ARK_INT_RR (1 << 0) /* Receive data ready interrupt */
#define ARK_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */
/* The maximum bytes that a sdma BD can transfer.*/
#define MAX_SDMA_BD_BYTES (1 << 15)
#define ARK_ECSPI_CTRL_MAX_BURST 512
struct ark_ecspi_data;
struct ark_spi_devtype_data {
void (*intctrl)(struct ark_ecspi_data *, int);
int (*config)(struct ark_ecspi_data *);
void (*trigger)(struct ark_ecspi_data *);
int (*rx_available)(struct ark_ecspi_data *);
void (*reset)(struct ark_ecspi_data *);
unsigned int fifo_size;
bool has_dmamode;
};
struct ark_ecspi_data {
struct spi_slave slave;
QueueHandle_t xfer_done;
unsigned int base;
unsigned int irq;
unsigned int spi_clk;
unsigned int spi_bus_clk;
unsigned int speed_hz;
unsigned int bits_per_word;
unsigned int spi_drctl;
unsigned int count, remainder;
void (*tx)(struct ark_ecspi_data *);
void (*rx)(struct ark_ecspi_data *);
unsigned char *rx_buf;
const unsigned char *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
unsigned int read_u32;
unsigned int word_mask;
unsigned int cs_gpio;
bool is_arke;
/* DMA */
bool usedma;
u32 wml;
QueueHandle_t dma_rx_completion;
QueueHandle_t dma_tx_completion;
struct spi_message dma_message;
struct spi_message pio_message;
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
char *rx_dummy_buffer;
char *tx_dummy_buffer;
const struct ark_spi_devtype_data *devtype_data;
};
static void ark_spi_buf_rx_u8(struct ark_ecspi_data *aspi)
{
unsigned int val = readl(aspi->base + ARK_ECSPI_RXDATA);
if (aspi->rx_buf) {
if (aspi->is_arke)
*(u8*)aspi->rx_buf = val & 0xff;
else
*(u8*)aspi->rx_buf = (val >> 24) & 0xff;
aspi->rx_buf += 1;
}
}
static void ark_spi_buf_rx_u16(struct ark_ecspi_data *aspi)
{
unsigned int val = readl(aspi->base + ARK_ECSPI_RXDATA);
if (aspi->rx_buf) {
if (aspi->is_arke)
*(u16*)aspi->rx_buf = val & 0xffff;
else
*(u16*)aspi->rx_buf = (val >> 16) & 0xffff;
aspi->rx_buf += 2;
}
}
static void ark_spi_buf_tx_u8(struct ark_ecspi_data *aspi)
{
u32 val = 0;
if (aspi->tx_buf) {
if (aspi->is_arke)
val = *(u8 *)aspi->tx_buf;
else
val = *(u8 *)aspi->tx_buf << 24;
aspi->tx_buf += 1;
}
aspi->count -= 1;
writel(val, aspi->base + ARK_ECSPI_TXDATA);
}
static void ark_spi_buf_tx_u16(struct ark_ecspi_data *aspi)
{
u32 val = 0;
if (aspi->tx_buf) {
if (aspi->is_arke)
val = *(u16 *)aspi->tx_buf;
else
val = *(u16 *)aspi->tx_buf << 16;
aspi->tx_buf += 2;
}
aspi->count -=2;
writel(val, aspi->base + ARK_ECSPI_TXDATA);
}
static int ark_spi_bytes_per_word(const int bits_per_word)
{
return DIV_ROUND_UP(bits_per_word, BITS_PER_BYTE);
}
#define ARK_ECSPI_CTRL 0x08
#define ARK_ECSPI_CTRL_ENABLE (1 << 0)
#define ARK_ECSPI_CTRL_XCH (1 << 2)
#define ARK_ECSPI_CTRL_SMC (1 << 3)
#define ARK_ECSPI_CTRL_MODE_MASK (0xf << 4)
#define ARK_ECSPI_CTRL_DRCTL(drctl) ((drctl) << 16)
#define ARK_ECSPI_CTRL_POSTDIV_OFFSET 8
#define ARK_ECSPI_CTRL_PREDIV_OFFSET 12
#define ARK_ECSPI_CTRL_CS(cs) ((cs) << 18)
#define ARK_ECSPI_CTRL_BL_OFFSET 20
#define ARK_ECSPI_CTRL_BL_MASK (0xfff << 20)
#define ARK_ECSPI_CONFIG 0x0c
#define ARK_ECSPI_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0))
#define ARK_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
#define ARK_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
#define ARK_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
#define ARK_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs) + 20))
#define ARK_ECSPI_INT 0x10
#define ARK_ECSPI_INT_TEEN (1 << 0)
#define ARK_ECSPI_INT_RREN (1 << 3)
#define ARK_ECSPI_DMA 0x14
#define ARK_ECSPI_DMA_TX_WML(wml) ((wml) & 0x3f)
#define ARK_ECSPI_DMA_RX_WML(wml) ((((wml) & 0x3f) - 1) << 16)
#define ARK_ECSPI_DMA_RXT_WML(wml) (((wml) & 0x3f) << 24)
#define ARK_ECSPI_DMA_TEDEN (1 << 7)
#define ARK_ECSPI_DMA_RXDEN (1 << 23)
#define ARK_ECSPI_DMA_RXTDEN (1UL << 31)
#define ARK_ECSPI_STAT 0x18
#define ARK_ECSPI_STAT_REN (1 << 8)
#define ARK_ECSPI_STAT_RR (1 << 3)
#define ARK_ECSPI_TESTREG 0x20
#define ARK_ECSPI_TESTREG_LBC BIT(31)
static void ark_spi_buf_rx_swap_u32(struct ark_ecspi_data *aspi)
{
unsigned int val = readl(aspi->base + ARK_ECSPI_RXDATA);
if (aspi->rx_buf) {
val &= aspi->word_mask;
*(u32 *)aspi->rx_buf = val;
aspi->rx_buf += sizeof(u32);
}
}
static void ark_spi_buf_rx_swap(struct ark_ecspi_data *aspi)
{
unsigned int bytes_per_word;
bytes_per_word = ark_spi_bytes_per_word(aspi->bits_per_word);
if (aspi->read_u32) {
ark_spi_buf_rx_swap_u32(aspi);
return;
}
if (bytes_per_word == 1)
ark_spi_buf_rx_u8(aspi);
else if (bytes_per_word == 2)
ark_spi_buf_rx_u16(aspi);
}
static void ark_spi_buf_tx_swap_u32(struct ark_ecspi_data *aspi)
{
u32 val = 0;
if (aspi->tx_buf) {
val = *(u32 *)aspi->tx_buf;
val &= aspi->word_mask;
aspi->tx_buf += sizeof(u32);
}
aspi->count -= sizeof(u32);
writel(val, aspi->base + ARK_ECSPI_TXDATA);
}
static void ark_spi_buf_tx_swap(struct ark_ecspi_data *aspi)
{
u32 ctrl, val;
unsigned int bytes_per_word;
if (aspi->count == aspi->remainder) {
ctrl = readl(aspi->base + ARK_ECSPI_CTRL);
ctrl &= ~ARK_ECSPI_CTRL_BL_MASK;
if (aspi->count > ARK_ECSPI_CTRL_MAX_BURST) {
aspi->remainder = aspi->count %
ARK_ECSPI_CTRL_MAX_BURST;
val = ARK_ECSPI_CTRL_MAX_BURST * 8 - 1;
} else if (aspi->count >= sizeof(u32)) {
aspi->remainder = aspi->count % sizeof(u32);
val = (aspi->count - aspi->remainder) * 8 - 1;
} else {
aspi->remainder = 0;
val = aspi->bits_per_word - 1;
aspi->read_u32 = 0;
}
ctrl |= (val << ARK_ECSPI_CTRL_BL_OFFSET);
writel(ctrl, aspi->base + ARK_ECSPI_CTRL);
}
if (aspi->count >= sizeof(u32)) {
ark_spi_buf_tx_swap_u32(aspi);
return;
}
bytes_per_word = ark_spi_bytes_per_word(aspi->bits_per_word);
if (bytes_per_word == 1)
ark_spi_buf_tx_u8(aspi);
else if (bytes_per_word == 2)
ark_spi_buf_tx_u16(aspi);
}
/* ARK eCSPI */
static unsigned int ark_ecspi_clkdiv(struct ark_ecspi_data *aspi,
unsigned int fspi, unsigned int *fres)
{
/*
* there are two 4-bit dividers, the pre-divider divides by
* $pre, the post-divider by 2^$post
*/
unsigned int pre, post;
unsigned int fin = aspi->spi_clk;
if (fspi > fin)
return 0;
post = fls(fin) - fls(fspi);
if (fin > fspi << post)
post++;
/* now we have: (fin <= fspi << post) with post being minimal */
post = configMAX(4U, post) - 4;
if (post > 0xf) {
TRACE_ERROR("cannot set clock freq: %u (base freq: %u)\n",
fspi, fin);
return 0xff;
}
pre = DIV_ROUND_UP(fin, fspi << post) - 1;
TRACE_DEBUG("%s: fin: %u, fspi: %u, post: %u, pre: %u\n",
__func__, fin, fspi, post, pre);
/* Resulting frequency for the SCLK line. */
*fres = (fin / (pre + 1)) >> post;
return (pre << ARK_ECSPI_CTRL_PREDIV_OFFSET) |
(post << ARK_ECSPI_CTRL_POSTDIV_OFFSET);
}
static void ark_ecspi_intctrl(struct ark_ecspi_data *aspi, int enable)
{
unsigned val = 0;
if (enable & ARK_INT_TE)
val |= ARK_ECSPI_INT_TEEN;
if (enable & ARK_INT_RR)
val |= ARK_ECSPI_INT_RREN;
writel(val, aspi->base + ARK_ECSPI_INT);
}
static void ark_ecspi_trigger(struct ark_ecspi_data *aspi)
{
u32 reg;
reg = readl(aspi->base + ARK_ECSPI_CTRL);
reg |= ARK_ECSPI_CTRL_XCH;
writel(reg, aspi->base + ARK_ECSPI_CTRL);
}
static int ark_ecspi_config(struct ark_ecspi_data *aspi)
{
unsigned int ctrl = ARK_ECSPI_CTRL_ENABLE;
unsigned int clk = aspi->speed_hz, delay, reg;
unsigned int cfg = readl(aspi->base + ARK_ECSPI_CONFIG);
unsigned int chip_select = aspi->slave.cs;
/*
* The hardware seems to have a race condition when changing modes. The
* current assumption is that the selection of the channel arrives
* earlier in the hardware than the mode bits when they are written at
* the same time.
* So set master mode for all channels as we do not support slave mode.
*/
ctrl |= ARK_ECSPI_CTRL_MODE_MASK;
/*
* Enable SPI_RDY handling (falling edge/level triggered).
*/
if (aspi->slave.mode & SPI_READY)
ctrl |= ARK_ECSPI_CTRL_DRCTL(aspi->spi_drctl);
/* set clock speed */
ctrl |= ark_ecspi_clkdiv(aspi, aspi->speed_hz, &clk);
aspi->spi_bus_clk = clk;
/* set chip select to use */
ctrl |= ARK_ECSPI_CTRL_CS(chip_select);
if (aspi->usedma)
ctrl |= (32 - 1) << ARK_ECSPI_CTRL_BL_OFFSET;
else
ctrl |= (aspi->bits_per_word - 1) << ARK_ECSPI_CTRL_BL_OFFSET;
cfg |= ARK_ECSPI_CONFIG_SBBCTRL(chip_select);
if (aspi->slave.mode & SPI_CPHA)
cfg |= ARK_ECSPI_CONFIG_SCLKPHA(chip_select);
else
cfg &= ~ARK_ECSPI_CONFIG_SCLKPHA(chip_select);
if (aspi->slave.mode & SPI_CPOL) {
cfg |= ARK_ECSPI_CONFIG_SCLKPOL(chip_select);
cfg |= ARK_ECSPI_CONFIG_SCLKCTL(chip_select);
} else {
cfg &= ~ARK_ECSPI_CONFIG_SCLKPOL(chip_select);
cfg &= ~ARK_ECSPI_CONFIG_SCLKCTL(chip_select);
}
if (aspi->slave.mode & SPI_CS_HIGH)
cfg |= ARK_ECSPI_CONFIG_SSBPOL(chip_select);
else
cfg &= ~ARK_ECSPI_CONFIG_SSBPOL(chip_select);
if (aspi->usedma) {
ctrl |= ARK_ECSPI_CTRL_SMC;
}
/* CTRL register always go first to bring out controller from reset */
writel(ctrl, aspi->base + ARK_ECSPI_CTRL);
reg = readl(aspi->base + ARK_ECSPI_TESTREG);
if (aspi->slave.mode & SPI_LOOP)
reg |= ARK_ECSPI_TESTREG_LBC;
else
reg &= ~ARK_ECSPI_TESTREG_LBC;
writel(reg, aspi->base + ARK_ECSPI_TESTREG);
writel(cfg, aspi->base + ARK_ECSPI_CONFIG);
/*
* Wait until the changes in the configuration register CONFIGREG
* propagate into the hardware. It takes exactly one tick of the
* SCLK clock, but we will wait two SCLK clock just to be sure. The
* effect of the delay it takes for the hardware to apply changes
* is noticable if the SCLK clock run very slow. In such a case, if
* the polarity of SCLK should be inverted, the GPIO ChipSelect might
* be asserted before the SCLK polarity changes, which would disrupt
* the SPI communication as the device on the other end would consider
* the change of SCLK polarity as a clock tick already.
*/
delay = (2 * 1000000) / clk;
if (delay < 10) /* SCLK is faster than 100 kHz */
udelay(delay);
else /* SCLK is _very_ slow */
udelay(delay + 10);
/* enable rx fifo */
writel(ARK_ECSPI_STAT_REN, aspi->base + ARK_ECSPI_STAT);
/*
* Configure the DMA register: setup the watermark
* and enable DMA request.
*/
if (aspi->usedma)
writel(ARK_ECSPI_DMA_RX_WML(aspi->wml) |
ARK_ECSPI_DMA_TX_WML(aspi->wml) |
ARK_ECSPI_DMA_RXT_WML(aspi->wml) |
ARK_ECSPI_DMA_TEDEN | ARK_ECSPI_DMA_RXDEN |
ARK_ECSPI_DMA_RXTDEN, aspi->base + ARK_ECSPI_DMA);
else writel(0, aspi->base + ARK_ECSPI_DMA);
return 0;
}
static int ark_ecspi_rx_available(struct ark_ecspi_data *aspi)
{
return readl(aspi->base + ARK_ECSPI_STAT) & ARK_ECSPI_STAT_RR;
}
static void ark_ecspi_reset(struct ark_ecspi_data *aspi)
{
/* drain receive buffer */
while (ark_ecspi_rx_available(aspi))
readl(aspi->base + ARK_ECSPI_RXDATA);
}
static struct ark_spi_devtype_data ark_ecspi_devtype_data = {
.intctrl = ark_ecspi_intctrl,
.config = ark_ecspi_config,
.trigger = ark_ecspi_trigger,
.rx_available = ark_ecspi_rx_available,
.reset = ark_ecspi_reset,
.fifo_size = 64,
.has_dmamode = false,
};
static void ark_spi_chipselect(struct ark_ecspi_data *aspi, int is_active)
{
int dev_is_lowactive = !(aspi->slave.mode & SPI_CS_HIGH);
if (aspi->slave.mode & SPI_NO_CS)
return;
gpio_direction_output(aspi->cs_gpio, is_active ^ dev_is_lowactive);
}
static void ark_spi_push(struct ark_ecspi_data *aspi)
{
while (aspi->txfifo < aspi->devtype_data->fifo_size) {
if (!aspi->count)
break;
if (aspi->txfifo && (aspi->count == aspi->remainder))
break;
aspi->tx(aspi);
aspi->txfifo++;
}
aspi->devtype_data->trigger(aspi);
}
static void ark_spi_isr(void *param)
{
struct ark_ecspi_data *aspi = param;
while (aspi->devtype_data->rx_available(aspi)) {
aspi->rx(aspi);
aspi->txfifo--;
}
if (aspi->count) {
ark_spi_push(aspi);
return;
}
if (aspi->txfifo) {
/* No data left to push, but still waiting for rx data,
* enable receive data available interrupt.
*/
aspi->devtype_data->intctrl(
aspi, ARK_INT_RR);
return;
}
aspi->devtype_data->intctrl(aspi, 0);
xQueueSendFromISR(aspi->xfer_done, NULL, 0);
}
static int ark_spi_setupxfer(struct ark_ecspi_data *aspi,
struct spi_configuration *configuration)
{
u32 mask;
if (!configuration)
return 0;
aspi->slave.mode = configuration->mode;
aspi->bits_per_word = configuration->data_width;
aspi->speed_hz = configuration->max_hz;
/* Initialize the functions for transfer */
aspi->remainder = 0;
aspi->read_u32 = 1;
mask = (1 << aspi->bits_per_word) - 1;
aspi->rx = ark_spi_buf_rx_swap;
aspi->tx = ark_spi_buf_tx_swap;
if (aspi->bits_per_word <= 8)
aspi->word_mask = mask << 24 | mask << 16
| mask << 8 | mask;
else if (aspi->bits_per_word <= 16)
aspi->word_mask = mask << 16 | mask;
else
aspi->word_mask = mask;
aspi->devtype_data->config(aspi);
return 0;
}
static int ark_spi_calculate_timeout(struct ark_ecspi_data *aspi, int size)
{
unsigned long timeout = 0;
/* Time with actual data transfer and CS change delay related to HW */
timeout = (8 + 4) * size / aspi->spi_bus_clk;
/* Add extra second for scheduler related activities */
timeout += 1;
/* Double calculated timeout */
return pdMS_TO_TICKS(2 * timeout * MSEC_PER_SEC);
}
static bool ark_spi_can_dma(struct ark_ecspi_data *aspi, struct spi_message *transfer)
{
const u32 mszs[] = {1, 4, 8, 16};
int idx = ARRAY_SIZE(mszs) - 1;
struct spi_message *dma_xfer = &aspi->dma_message;
struct spi_message *pio_xfer = &aspi->pio_message;
int len, remainder;
if (!aspi->dma_rx)
return false;
pio_xfer->length = 0;
memcpy(dma_xfer, transfer, sizeof(struct spi_message));
remainder = transfer->length & 3;
len = transfer->length - remainder;
if (len < USE_DMA_THRESHOLD)
return false;
if ((u32)transfer->send_buf & 3 || (u32)transfer->recv_buf & 3)
return false;
if (remainder) {
dma_xfer->length = len;
memcpy(pio_xfer, transfer, sizeof(struct spi_message));
pio_xfer->length = remainder;
if (pio_xfer->send_buf)
pio_xfer->send_buf = (u8*)pio_xfer->send_buf + len;
if (pio_xfer->recv_buf)
pio_xfer->recv_buf = (u8*)pio_xfer->recv_buf + len;
}
/* dw dma busrt should be 16,8,4,1 */
for (; idx >= 0; idx--) {
if (!(len % (mszs[idx] * 4)))
break;
}
aspi->wml = mszs[idx];
return true;
}
static void ark_spi_sdma_exit(struct ark_ecspi_data *aspi)
{
if (aspi->dma_rx) {
dma_release_channel(aspi->dma_rx);
aspi->dma_rx = NULL;
}
if (aspi->dma_tx) {
dma_release_channel(aspi->dma_tx);
aspi->dma_tx = NULL;
}
if (aspi->rx_dummy_buffer) {
vPortFree(aspi->rx_dummy_buffer);
aspi->rx_dummy_buffer = NULL;
}
if (aspi->tx_dummy_buffer) {
vPortFree(aspi->tx_dummy_buffer);
aspi->tx_dummy_buffer = NULL;
}
}
static int ark_spi_sdma_init(struct ark_ecspi_data *aspi)
{
int ret;
aspi->wml = aspi->devtype_data->fifo_size / 2;
/* Prepare for TX DMA: */
aspi->dma_tx = dma_request_channel(0);
if (IS_ERR(aspi->dma_tx)) {
ret = PTR_ERR(aspi->dma_tx);
TRACE_DEBUG("can't get the TX DMA channel, error %d!\n", ret);
aspi->dma_tx = NULL;
goto err;
}
/* Prepare for RX : */
aspi->dma_rx = dma_request_channel(0);
if (IS_ERR(aspi->dma_rx)) {
ret = PTR_ERR(aspi->dma_rx);
TRACE_DEBUG("can't get the RX DMA channel, error %d\n", ret);
aspi->dma_rx = NULL;
goto err;
}
aspi->rx_dummy_buffer = pvPortMalloc(MALLOC_DMA_MEM_SIZE);
if (!aspi->rx_dummy_buffer) {
ret = -ENOMEM;
goto err;
}
aspi->tx_dummy_buffer = pvPortMalloc(MALLOC_DMA_MEM_SIZE);
if (!aspi->tx_dummy_buffer) {
ret = -ENOMEM;
goto err;
}
aspi->dma_rx_completion = xQueueCreate(1, 0);
aspi->dma_tx_completion = xQueueCreate(1, 0);
return 0;
err:
ark_spi_sdma_exit(aspi);
return ret;
}
static void ark_spi_dma_rx_callback(void *cookie, unsigned mask)
{
struct ark_ecspi_data *aspi = (struct ark_ecspi_data *)cookie;
struct spi_message *dma_message = &aspi->dma_message;
/* Invalidate cache after read */
/* rx_dummy_buffer shoule align to CACHE_LINE_SIZE */
CP15_invalidate_dcache_for_dma((u32)aspi->rx_dummy_buffer,
(u32)aspi->rx_dummy_buffer + dma_message->length);
if (dma_message->recv_buf)
memcpy(dma_message->recv_buf, aspi->rx_dummy_buffer, dma_message->length);
xQueueSend(aspi->dma_rx_completion, NULL, 0);
}
static void ark_spi_dma_tx_callback(void *cookie, unsigned int mask)
{
struct ark_ecspi_data *aspi = (struct ark_ecspi_data *)cookie;
xQueueSend(aspi->dma_tx_completion, NULL, 0);
}
static int ark_spi_dma_transfer(struct ark_ecspi_data *aspi, struct spi_message *message)
{
struct dma_config rx = {0}, tx = {0};
unsigned long transfer_timeout;
rx.direction = DMA_DEV_TO_MEM;
rx.src_id = SPI1_RX;
rx.src_addr = aspi->base + ARK_ECSPI_RXDATA;
rx.dst_addr = (unsigned int)aspi->rx_dummy_buffer;
rx.dst_addr_width = rx.src_addr_width = DMA_BUSWIDTH_4_BYTES;
rx.src_maxburst = rx.dst_maxburst = aspi->wml;
rx.transfer_size = message->length;
dma_config_channel(aspi->dma_rx, &rx);
dma_register_complete_callback(aspi->dma_rx, ark_spi_dma_rx_callback, aspi);
tx.direction = DMA_MEM_TO_DEV;
tx.dst_id = SPI1_TX;
tx.src_addr = (unsigned int)aspi->tx_dummy_buffer;
tx.dst_addr = aspi->base + ARK_ECSPI_TXDATA;
tx.dst_addr_width = tx.src_addr_width = DMA_BUSWIDTH_4_BYTES;
tx.src_maxburst = tx.dst_maxburst = aspi->wml;
tx.transfer_size = message->length;
dma_config_channel(aspi->dma_tx, &tx);
dma_register_complete_callback(aspi->dma_tx, ark_spi_dma_tx_callback, aspi);
xQueueReset(aspi->dma_rx_completion);
dma_start_channel(aspi->dma_rx);
memset(aspi->tx_dummy_buffer, 0xff, message->length);
if (message->send_buf)
memcpy(aspi->tx_dummy_buffer, message->send_buf, message->length);
xQueueReset(aspi->dma_tx_completion);
/* Flush cache before write */
CP15_flush_dcache_for_dma((u32)aspi->tx_dummy_buffer,
(u32)aspi->tx_dummy_buffer + message->length);
dma_start_channel(aspi->dma_tx);
transfer_timeout = ark_spi_calculate_timeout(aspi, message->length);
/* Wait SDMA to finish the data transfer.*/
if (xQueueReceive(aspi->dma_tx_completion, NULL,
transfer_timeout) != pdTRUE) {
printf("I/O Error in DMA TX\n");
dma_stop_channel(aspi->dma_tx);
dma_stop_channel(aspi->dma_rx);
return -ETIMEDOUT;
}
if (xQueueReceive(aspi->dma_rx_completion, NULL,
transfer_timeout) != pdTRUE) {
printf("I/O Error in DMA RX\n");
aspi->devtype_data->reset(aspi);
dma_stop_channel(aspi->dma_rx);
return -ETIMEDOUT;
}
return message->length;
}
static int ark_spi_pio_xfer(struct ark_ecspi_data *aspi, struct spi_message *message)
{
unsigned long transfer_timeout;
int ret;
void *tx_dummy_buf = NULL;
void *rx_dummy_buf = NULL;
if ((unsigned int)message->send_buf & 3) {
tx_dummy_buf = pvPortMalloc(message->length);
if (!tx_dummy_buf) return -ENOMEM;
aspi->tx_buf = tx_dummy_buf;
memcpy(tx_dummy_buf, message->send_buf, message->length);
} else aspi->tx_buf = message->send_buf;
if ((unsigned int)message->recv_buf & 3) {
rx_dummy_buf = pvPortMalloc(message->length);
if (!rx_dummy_buf) return -ENOMEM;
aspi->rx_buf = rx_dummy_buf;
} else aspi->rx_buf = message->recv_buf;
aspi->remainder = aspi->count = message->length;
aspi->read_u32 = 1;
aspi->txfifo = 0;
xQueueReset(aspi->xfer_done);
ark_spi_push(aspi);
aspi->devtype_data->intctrl(aspi, ARK_INT_TE);
transfer_timeout = ark_spi_calculate_timeout(aspi, message->length);
if (xQueueReceive(aspi->xfer_done, NULL, transfer_timeout) != pdTRUE) {
TRACE_ERROR("I/O Error in PIO\n");
aspi->devtype_data->reset(aspi);
if (message->cs_release)
ark_spi_chipselect(aspi, 0);
ret = -ETIMEDOUT;
} else {
if (rx_dummy_buf)
memcpy(message->recv_buf, rx_dummy_buf, message->length);
ret = message->length;
}
if (rx_dummy_buf) vPortFree(rx_dummy_buf);
if (tx_dummy_buf) vPortFree(tx_dummy_buf);
return ret;
}
static int ecspi_configure(struct spi_slave *slave, struct spi_configuration *configuration)
{
struct ark_ecspi_data *aspi = (struct ark_ecspi_data *)slave;
return ark_spi_setupxfer(aspi, configuration);
}
static int ecspi_xfer(struct spi_slave *slave, struct spi_message *message)
{
struct ark_ecspi_data *aspi = (struct ark_ecspi_data *)slave;
int ret = 0;
if (message->cs_take)
ark_spi_chipselect(aspi, 1);
if (ark_spi_can_dma(aspi, message))
aspi->usedma = 1;
else
aspi->usedma = 0;
aspi->devtype_data->config(aspi);
if (aspi->usedma) {
if ((ret = ark_spi_dma_transfer(aspi, &aspi->dma_message)) < 0)
goto end;
if (aspi->pio_message.length > 0 &&
(ret = ark_spi_pio_xfer(aspi, &aspi->pio_message)) < 0)
goto end;
ret = message->length;
goto end;
}
ret = ark_spi_pio_xfer(aspi, message);
end:
if (message->cs_release)
ark_spi_chipselect(aspi, 0);
return ret;
};
static int ark_ecspi_probe(struct ark_ecspi_data *aspi, char *spi_bus_name)
{
int ret;
aspi->devtype_data = &ark_ecspi_devtype_data;
aspi->xfer_done = xQueueCreate(1, 0);
request_irq(aspi->irq, 0, ark_spi_isr, aspi);
if (aspi->devtype_data->has_dmamode) {
ret = ark_spi_sdma_init(aspi);
if (ret < 0)
TRACE_ERROR("dma setup error %d, use pio\n", ret);
}
aspi->devtype_data->reset(aspi);
aspi->devtype_data->intctrl(aspi, 0);
strncpy(aspi->slave.name, spi_bus_name, 16);
spi_add_slave(&aspi->slave);
return 0;
}
int ecspi_init(void)
{
struct ark_ecspi_data *aspi1 = pvPortMalloc(sizeof(struct ark_ecspi_data));
if (!aspi1)
return -ENOMEM;
memset(aspi1, 0, sizeof(struct ark_ecspi_data));
aspi1->base = REGS_SPI1_BASE;
aspi1->irq = SPI1_IRQn;
aspi1->spi_clk = ulClkGetRate(CLK_SPI1);
aspi1->cs_gpio = SPI1_CS0_GPIO;
aspi1->slave.mode = SPI_MODE_0;
aspi1->slave.cs = 0;
aspi1->slave.xfer = ecspi_xfer;
aspi1->slave.configure = ecspi_configure;
ark_ecspi_probe(aspi1, "spi1");
return 0;
}

View File

@ -0,0 +1,253 @@
#include "FreeRTOS.h"
#include "chip.h"
#define GPIO_SWPORTA_DR 0x00
#define GPIO_SWPORTA_DDR 0x04
#define GPIO_SWPORTA_CTL 0x08
#define GPIO_SWPORTA_INTEN 0x30
#define GPIO_SWPORTA_INTMASK 0x34
#define GPIO_SWPORTA_INTTYPE_LEVEL 0x38
#define GPIO_SWPORTA_INT_POLARITY 0x3c
#define GPIO_SWPORTA_INTSTATUS 0x40
#define GPIO_SWPORTA_RAW_INTSTATUS 0x44
#define GPIO_SWPORTA_DEBOUNCE 0x48
#define GPIO_SWPORTA_EOI 0x4c
#define GPIO_SWPORTA_EXT_PORTA 0x50
#define GPIO_SWPORTA_EXT_PORTB 0x54
#define GPIO_SWPORTA_EXT_PORTC 0x58
#define GPIO_SWPORTA_EXT_PORTD 0x5c
#define GPIO_SWPORTA_LS_SYNC 0x60
#define GPIO_SWPORTA_ID_CODE 0x64
#define GPIO_SWPORTA_INT_BOTHEDGE 0x68
#define GPIO_SWPORTA_VER_ID_CODE 0x6C
#define GPIO_SWPORTA_CONFIG_REG2 0x70
#define GPIO_SWPORTA_CONFIG_REG1 0x74
#define GPIO_BANK 4
#define GPIO_NUM 128
typedef struct {
ISRFunction_t handler;
void *handler_param;
int irq_type;
} GpioIrqDesc_t;
static GpioIrqDesc_t gpio_irq_descs[GPIO_NUM];
static __INLINE uint32_t gpio_get_regbase(int gpio)
{
int gpiox = (gpio >> 5) & 0x3;
return REGS_GPIO_BASE + 0x80 * gpiox;
}
/* static __INLINE int GPIO_BANK(unsigned gpio)
{
return gpio >> 5;
} */
static __INLINE int GPIO_OFFSET(unsigned gpio)
{
if (gpio == 96)
return 2;
else if (gpio == 97)
return 0;
else if (gpio == 98)
return 3;
else if (gpio == 99)
return 1;
else
return gpio & 0x1F;
}
static __INLINE void *GPIO_MODREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_DDR);
}
static __INLINE void *GPIO_WDATAREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_DR);
}
static __INLINE void *GPIO_RDATAREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_EXT_PORTA);
}
static __INLINE void *GPIO_INTENREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_INTEN);
}
static __INLINE void *GPIO_INTMASKREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_INTMASK);
}
static __INLINE void *GPIO_INTLVLREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_INTTYPE_LEVEL);
}
static __INLINE void *GPIO_INTPOLREG(unsigned gpio)
{
return (void*)(gpio_get_regbase(gpio) + GPIO_SWPORTA_INT_POLARITY);
}
void gpio_request(unsigned gpio)
{
pinctrl_gpio_request(gpio);
}
void gpio_direction_output(unsigned gpio, int value)
{
configASSERT(gpio < GPIO_NUM);
gpio_request(gpio);
writel(readl(GPIO_MODREG(gpio)) | (1 << GPIO_OFFSET(gpio)), GPIO_MODREG(gpio));
if (value)
writel(readl(GPIO_WDATAREG(gpio)) | (1 << GPIO_OFFSET(gpio)), GPIO_WDATAREG(gpio));
else
writel(readl(GPIO_WDATAREG(gpio)) & ~(1 << GPIO_OFFSET(gpio)), GPIO_WDATAREG(gpio));
}
void gpio_direction_input(unsigned gpio)
{
configASSERT(gpio < GPIO_NUM);
gpio_request(gpio);
writel(readl(GPIO_MODREG(gpio)) & ~(1 << GPIO_OFFSET(gpio)), GPIO_MODREG(gpio));
}
void gpio_set_value(unsigned gpio, int value)
{
configASSERT(gpio < GPIO_NUM);
if (value)
writel(readl(GPIO_WDATAREG(gpio)) | (1 << GPIO_OFFSET(gpio)), GPIO_WDATAREG(gpio));
else
writel(readl(GPIO_WDATAREG(gpio)) & ~(1 << GPIO_OFFSET(gpio)), GPIO_WDATAREG(gpio));
}
int gpio_get_value(unsigned gpio)
{
configASSERT(gpio < GPIO_NUM);
return !!(readl(GPIO_RDATAREG(gpio)) & (1 << GPIO_OFFSET(gpio)));
}
static void gpio_toggle_trigger(unsigned gpio)
{
u32 pol;
pol = readl(GPIO_INTPOLREG(gpio));
if (pol & (1 << GPIO_OFFSET(gpio)))
pol &= ~(1 << GPIO_OFFSET(gpio));
else
pol |= (1 << GPIO_OFFSET(gpio));
writel(pol, GPIO_INTPOLREG(gpio));
}
static void gpio_irq_enable(unsigned gpio)
{
writel(readl(GPIO_INTENREG(gpio)) | (1 << GPIO_OFFSET(gpio)), GPIO_INTENREG(gpio));
writel(readl(GPIO_INTMASKREG(gpio)) & ~(1 << GPIO_OFFSET(gpio)), GPIO_INTMASKREG(gpio));
}
static void gpio_irq_disable(unsigned gpio)
{
writel(readl(GPIO_INTENREG(gpio)) & ~(1 << GPIO_OFFSET(gpio)), GPIO_INTENREG(gpio));
writel(readl(GPIO_INTMASKREG(gpio)) | (1 << GPIO_OFFSET(gpio)), GPIO_INTMASKREG(gpio));
}
static int gpio_irq_set_irq_type(unsigned gpio, int type)
{
unsigned long level, polarity;
level = readl(GPIO_INTLVLREG(gpio));
polarity = readl(GPIO_INTPOLREG(gpio));
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
level |= (1 << GPIO_OFFSET(gpio));
gpio_toggle_trigger(gpio);
break;
case IRQ_TYPE_EDGE_RISING:
level |= (1 << GPIO_OFFSET(gpio));
polarity |= (1 << GPIO_OFFSET(gpio));
break;
case IRQ_TYPE_EDGE_FALLING:
level |= (1 << GPIO_OFFSET(gpio));
polarity &= ~(1 << GPIO_OFFSET(gpio));
break;
case IRQ_TYPE_LEVEL_HIGH:
level &= ~(1 << GPIO_OFFSET(gpio));
polarity |= (1 << GPIO_OFFSET(gpio));
break;
case IRQ_TYPE_LEVEL_LOW:
level &= ~(1 << GPIO_OFFSET(gpio));
polarity &= ~(1 << GPIO_OFFSET(gpio));
break;
}
writel(level, GPIO_INTLVLREG(gpio));
if (type != IRQ_TYPE_EDGE_BOTH)
writel(polarity, GPIO_INTPOLREG(gpio));
return 0;
}
static void gpio_irq_handler(void *param)
{
u32 status;
int i, j;
for (i = 0; i < GPIO_BANK; i++) {
status = readl(REGS_GPIO_BASE + 0x80 * i + GPIO_SWPORTA_INTSTATUS);
for (j = 0; j < 32; j++) {
if (status & (1 << j)) {
u32 gpio = i * 32 + j;
/* clear interrupt */
writel(1 << j, REGS_GPIO_BASE + 0x80 * i + GPIO_SWPORTA_EOI);
if (gpio_irq_descs[gpio].handler != NULL) {
gpio_irq_descs[gpio].handler(gpio_irq_descs[gpio].handler_param);
if (gpio_irq_descs[gpio].irq_type == IRQ_TYPE_EDGE_BOTH)
gpio_toggle_trigger(gpio);
}
}
}
}
}
int gpio_irq_request(unsigned gpio, int irq_type, ISRFunction_t irq_handler, void *param)
{
configASSERT(gpio < GPIO_NUM);
portENTER_CRITICAL();
gpio_request(gpio);
gpio_irq_descs[gpio].handler = irq_handler;
gpio_irq_descs[gpio].handler_param = param;
gpio_irq_descs[gpio].irq_type = irq_type;
gpio_irq_set_irq_type(gpio, irq_type);
request_irq(GPIOA_IRQn + ((gpio >> 5) & 0x3), 0, gpio_irq_handler, NULL);
gpio_irq_enable(gpio);
portEXIT_CRITICAL();
return 0;
}
int gpio_irq_free(unsigned gpio)
{
configASSERT(gpio < GPIO_NUM);
portENTER_CRITICAL();
gpio_irq_disable(gpio);
gpio_irq_descs[gpio].handler = NULL;
gpio_irq_descs[gpio].handler_param = NULL;
portEXIT_CRITICAL();
return 0;
}

View File

@ -0,0 +1,236 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "os_adapt.h"
#include "hx170dec.h"
#include "vdec.h"
#define REGS_VDEC_BASE 0x71200000
#define VDEC_MAX_CORES 1 /* number of cores of the hardware IP */
#define VDEC_NUM_REGS_DEC 60 /* number of registers of the Decoder part */
#define VDEC_NUM_REGS_PP 41 /* number of registers of the Post Processor part */
#define VDEC_DEC_FIRST_REG 0 /* first register (0-based) index */
#define VDEC_DEC_LAST_REG 59 /* last register (0-based) index */
#define VDEC_PP_FIRST_REG 60
#define VDEC_PP_LAST_REG 100
struct vdec_device {
unsigned int mmio_base;
struct device *dev;
int irq;
int num_cores;
unsigned long iobaseaddr;
unsigned long iosize;
QueueHandle_t dec_irq_done;
SemaphoreHandle_t dec_sem;
u32 regs[VDEC_NUM_REGS_DEC + VDEC_NUM_REGS_PP];
};
static struct vdec_device *vdec6731_global;
static inline void vdec_writel(const struct vdec_device *p, unsigned offset, u32 val)
{
writel(val, p->mmio_base + offset);
}
static inline u32 vdec_readl(const struct vdec_device *p, unsigned offset)
{
return readl(p->mmio_base + offset);
}
/**
* Write a range of registers. First register is assumed to be
* "Interrupt Register" and will be written last.
*/
static int vdec_regs_write(struct vdec_device *p, int begin, int end,
const struct core_desc *core)
{
int i;
memcpy(&p->regs[begin], core->regs, (end - begin + 1) * 4);
for (i = end; i >= begin; i--)
vdec_writel(p, 4 * i, p->regs[i]);
return 0;
}
/**
* Read a range of registers [begin..end]
*/
static int vdec_regs_read(struct vdec_device *p, int begin, int end,
const struct core_desc *core)
{
int i;
for (i = end; i >= begin; i--)
p->regs[i] = vdec_readl(p, 4 * i);
memcpy(core->regs, &p->regs[begin], (end - begin + 1) * 4);
return 0;
}
long vdec_ioctl(unsigned int cmd, void *arg)
{
int ret = 0;
void *argp = arg;
struct vdec_device *p = vdec6731_global;
struct core_desc core;
u32 reg;
switch (cmd) {
case HX170DEC_IOX_ASIC_ID:
reg = vdec_readl(p, VDEC_IDR);
memcpy(argp, &reg, sizeof(u32));
break;
case HX170DEC_IOC_MC_OFFSETS:
case HX170DEC_IOCGHWOFFSET:
memcpy(argp, &p->iobaseaddr, sizeof(p->iobaseaddr));
break;
case HX170DEC_IOCGHWIOSIZE: /* in bytes */
memcpy(argp, &p->iosize, sizeof(p->iosize));
break;
case HX170DEC_IOC_MC_CORES:
memcpy(argp, &p->num_cores, sizeof(p->num_cores));
break;
case HX170DEC_IOCS_DEC_PUSH_REG:
memcpy(&core, (void *)arg, sizeof(struct core_desc));
/* Skip VDEC_IDR (ID Register, ro) */
core.regs++; // core.size -= 4;
ret = vdec_regs_write(p, VDEC_DEC_FIRST_REG + 1, VDEC_DEC_LAST_REG, &core);
break;
case HX170DEC_IOCS_PP_PUSH_REG:
break;
case HX170DEC_IOCS_DEC_PULL_REG:
memcpy(&core, (void *)arg, sizeof(struct core_desc));
ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core);
break;
case HX170DEC_IOCS_PP_PULL_REG:
break;
case HX170DEC_IOCX_DEC_WAIT:
memcpy(&core, (void *)arg, sizeof(struct core_desc));
if (xQueueReceive(p->dec_irq_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
dev_err(p->dev, "wait_event_interruptible dec error %d\n", ret);
ret = -ETIMEDOUT;
} else {
/* Update dec registers */
ret = vdec_regs_read(p, VDEC_DEC_FIRST_REG, VDEC_DEC_LAST_REG, &core);
}
xQueueReset(p->dec_irq_done);
break;
case HX170DEC_IOCX_PP_WAIT:
break;
case HX170DEC_IOCH_DEC_RESERVE:
if (xSemaphoreTake(p->dec_sem, pdMS_TO_TICKS(100)) == pdTRUE) {
ret = 0; /* core id */
dev_dbg(p->dev, "down dec_sem (core id %d)\n", ret);
} else {
dev_err(p->dev, "down_interruptible dec error\n");
ret = -ETIMEDOUT;
}
break;
case HX170DEC_IOCT_DEC_RELEASE:
dev_dbg(p->dev, "up dec_sem\n");
xSemaphoreGive(p->dec_sem);
break;
case HX170DEC_IOCQ_PP_RESERVE:
break;
case HX170DEC_IOCT_PP_RELEASE:
break;
default:
dev_warn(p->dev, "unknown ioctl %x\n", cmd);
ret = -EINVAL;
}
return ret;
}
/*
* Platform driver related
*/
/* Should we use spin_lock_irqsave here? */
static void vdec_isr(void *dev_id)
{
struct vdec_device *p = dev_id;
u32 irq_status_dec;
int handled = 0;
/* interrupt status register read */
irq_status_dec = vdec_readl(p, VDEC_DIR);
//printf("irq_status=0x%x.\n", irq_status_dec);
if (irq_status_dec & VDEC_DIR_ISET) {
/* Clear IRQ */
vdec_writel(p, VDEC_DIR, irq_status_dec & ~VDEC_DIR_ISET);
xQueueSendFromISR(p->dec_irq_done, NULL, 0);
handled++;
}
if (handled == 0) {
dev_warn(p->dev, "Spurious IRQ (DIR=%08x)\n", irq_status_dec);
}
}
#if 0
static void vdec_iram_mode_select(void)
{
//0 MFC ram use as iram; 1 MFCram use MFC REF buffer
u32 val = readl(REGS_SYSCTL_BASE + 0x78);
val |= (1<<0);
writel(val, REGS_SYSCTL_BASE + 0x78);
}
#endif
int vdec_init(void)
{
struct vdec_device *p;
int hwid;
/* Allocate private data */
p = pvPortMalloc(sizeof(struct vdec_device));
if (!p) {
dev_dbg(&pdev->dev, "out of memory\n");
return -ENOMEM;
}
memset(p, 0, sizeof(struct vdec_device));
p->mmio_base = REGS_VDEC_BASE;
p->irq = JPG_IRQn;
p->num_cores = VDEC_MAX_CORES;
p->iosize = 0x200;
p->iobaseaddr = REGS_VDEC_BASE;
vdec6731_global = p;
request_irq(p->irq, 0, vdec_isr, p);
p->dec_irq_done = xQueueCreate(1, 0);
p->dec_sem = xSemaphoreCreateCounting(VDEC_MAX_CORES, 1);
dev_info(&pdev->dev, "VDEC controller at 0x%u, irq = %d\n",
p->mmio_base, p->irq);
/* Reset Asic (just in case..) */
vdec_writel(p, VDEC_DIR, VDEC_DIR_ID | VDEC_DIR_ABORT);
vdec_writel(p, VDEC_PPIR, VDEC_PPIR_ID);
hwid = vdec_readl(p, VDEC_IDR);
printf("Product ID: %#x (revision %d.%d.%d)\n", \
(hwid & VDEC_IDR_PROD_ID) >> 16,
(hwid & VDEC_IDR_MAJOR_VER) >> 12,
(hwid & VDEC_IDR_MINOR_VER) >> 4,
(hwid & VDEC_IDR_BUILD_VER));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,633 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#include "errno.h"
#include "timer.h"
#ifdef ANALOG_I2C_SUPPORT
struct i2c_gpio_private_data {
struct i2c_adapter adap;
struct i2c_algo_bit_data bit_data;
struct i2c_gpio_platform_data pdata;
};
/* Toggle SDA by changing the direction of the pin */
static void i2c_gpio_setsda_dir(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
if (state)
gpio_direction_input(pdata->sda_pin);
else
gpio_direction_output(pdata->sda_pin, 0);
}
/*
* Toggle SDA by changing the output value of the pin. This is only
* valid for pins configured as open drain (i.e. setting the value
* high effectively turns off the output driver.)
*/
static void i2c_gpio_setsda_val(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_direction_output(pdata->sda_pin, state);
gpio_set_value(pdata->sda_pin, state);
}
/* Toggle SCL by changing the direction of the pin. */
static void i2c_gpio_setscl_dir(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
if (state)
gpio_direction_input(pdata->scl_pin);
else
gpio_direction_output(pdata->scl_pin, 0);
}
/*
* Toggle SCL by changing the output value of the pin. This is used
* for pins that are configured as open drain and for output-only
* pins. The latter case will break the i2c protocol, but it will
* often work in practice.
*/
static void i2c_gpio_setscl_val(void *data, int state)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_direction_output(pdata->scl_pin, state);
gpio_set_value(pdata->scl_pin, state);
}
static int i2c_gpio_getsda(void *data)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_direction_input(pdata->sda_pin);
return gpio_get_value(pdata->sda_pin);
}
static int i2c_gpio_getscl(void *data)
{
struct i2c_gpio_platform_data *pdata = data;
gpio_direction_input(pdata->scl_pin);
return gpio_get_value(pdata->scl_pin);
}
/* --- setting states on the bus with the right timing: --------------- */
#define setsda(adap, val) adap->setsda(adap->data, val)
#define setscl(adap, val) adap->setscl(adap->data, val)
#define getsda(adap) adap->getsda(adap->data)
#define getscl(adap) adap->getscl(adap->data)
static __INLINE void sdalo(struct i2c_algo_bit_data *adap)
{
setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
}
static __INLINE void sdahi(struct i2c_algo_bit_data *adap)
{
setsda(adap, 1);
udelay((adap->udelay + 1) / 2);
}
static __INLINE void scllo(struct i2c_algo_bit_data *adap)
{
setscl(adap, 0);
udelay(adap->udelay / 2);
}
/*
* Raise scl line, and do checking for delays. This is necessary for slower
* devices.
*/
static int sclhi(struct i2c_algo_bit_data *adap)
{
unsigned long start;
setscl(adap, 1);
/* Not all adapters have scl sense line... */
if (!adap->getscl)
goto done;
start = xTaskGetTickCount();
while (!getscl(adap)) {
/* This hw knows how to read the clock line, so we wait
* until it actually gets high. This is safer as some
* chips may hold it low ("clock stretching") while they
* are processing data internally.
*/
if (xTaskGetTickCount() > start + adap->timeout) {
/* Test one last time, as we may have been preempted
* between last check and timeout test.
*/
if (getscl(adap))
break;
return -ETIMEDOUT;
}
taskYIELD();
}
done:
udelay(adap->udelay);
return 0;
}
/* --- other auxiliary functions -------------------------------------- */
static void i2c_start(struct i2c_algo_bit_data *adap)
{
/* assert: scl, sda are high */
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_repstart(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdahi(adap);
sclhi(adap);
setsda(adap, 0);
udelay(adap->udelay);
scllo(adap);
}
static void i2c_stop(struct i2c_algo_bit_data *adap)
{
/* assert: scl is low */
sdalo(adap);
sclhi(adap);
setsda(adap, 1);
udelay(adap->udelay);
}
/* send a byte without start cond., look for arbitration,
check ackn. from slave */
/* returns:
* 1 if the device acknowledged
* 0 if the device did not ack
* -ETIMEDOUT if an error occurred (while raising the scl line)
*/
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c)
{
int i;
int sb;
int ack;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
for (i = 7; i >= 0; i--) {
sb = (c >> i) & 1;
setsda(adap, sb);
udelay((adap->udelay + 1) / 2);
if (sclhi(adap) < 0) { /* timed out */
TRACE_DEBUG("i2c_outb: 0x%02x, timeout at bit #%d\n", (int)c, i);
return -ETIMEDOUT;
}
/* FIXME do arbitration here:
* if (sb && !getsda(adap)) -> ouch! Get out of here.
*
* Report a unique code, so higher level code can retry
* the whole (combined) message and *NOT* issue STOP.
*/
scllo(adap);
}
sdahi(adap); //---
//sdalo(adap); //+++
if (sclhi(adap) < 0) { /* timeout */
TRACE_DEBUG("i2c_outb: 0x%02x, timeout at ack\n", (int)c);
return -ETIMEDOUT;
}
/* read ack: SDA should be pulled down by slave, or it may
* NAK (usually to report problems with the data we wrote).
*/
ack = !getsda(adap); /* ack: sda is pulled low -> success */
TRACE_DEBUG("i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA");
scllo(adap);
//sdalo(adap); //+++
return ack;
/* assert: scl is low (sda undef) */
}
static int i2c_inb(struct i2c_adapter *i2c_adap)
{
/* read byte via i2c port, without start/stop sequence */
/* acknowledge is sent in i2c_read. */
int i;
unsigned char indata = 0;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: scl is low */
sdahi(adap); //---
for (i = 0; i < 8; i++) {
if (sclhi(adap) < 0) { /* timeout */
TRACE_DEBUG("i2c_inb: timeout at bit #%d\n", 7 - i);
return -ETIMEDOUT;
}
indata *= 2;
if (getsda(adap))
indata |= 0x01;
setscl(adap, 0);
udelay(i == 7 ? adap->udelay / 2 : adap->udelay);
}
/* assert: scl is low */
return indata;
}
/* try_address tries to contact a chip for a number of
* times before it gives up.
* return values:
* 1 chip answered
* 0 chip did not answer
* -x transmission error
*/
static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret = 0;
for (i = 0; i <= retries; i++) {
ret = i2c_outb(i2c_adap, addr);
if (ret == 1 || i == retries)
break;
TRACE_DEBUG("emitting stop condition\n");
i2c_stop(adap);
udelay(adap->udelay);
taskYIELD();
TRACE_DEBUG("emitting start condition\n");
i2c_start(adap);
}
if (i && ret)
TRACE_DEBUG("Used %d tries to %s client at "
"0x%02x: %s\n", i + 1,
addr & 1 ? "read from" : "write to", addr >> 1,
ret == 1 ? "success" : "failed, timeout?");
return ret;
}
static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
const unsigned char *temp = msg->buf;
int count = msg->len;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
int retval;
int wrcount = 0;
while (count > 0) {
retval = i2c_outb(i2c_adap, *temp);
/* OK/ACK; or ignored NAK */
if ((retval > 0) || (nak_ok && (retval == 0))) {
count--;
temp++;
wrcount++;
/* A slave NAKing the master means the slave didn't like
* something about the data it saw. For example, maybe
* the SMBus PEC was wrong.
*/
} else if (retval == 0) {
TRACE_ERROR("sendbytes: NAK bailout.\n");
return -EIO;
/* Timeout; or (someday) lost arbitration
*
* FIXME Lost ARB implies retrying the transaction from
* the first message, after the "winning" master issues
* its STOP. As a rule, upper layer code has no reason
* to know or care about this ... it is *NOT* an error.
*/
} else {
TRACE_ERROR("sendbytes: error %d\n", retval);
return retval;
}
}
return wrcount;
}
static int acknak(struct i2c_adapter *i2c_adap, int is_ack)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
/* assert: sda is high */
if (is_ack) /* send ack */
setsda(adap, 0);
udelay((adap->udelay + 1) / 2);
if (sclhi(adap) < 0) { /* timeout */
TRACE_ERROR("readbytes: ack/nak timeout\n");
return -ETIMEDOUT;
}
scllo(adap);
return 0;
}
static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
int inval;
int rdcount = 0; /* counts bytes read */
unsigned char *temp = msg->buf;
int count = msg->len;
const unsigned flags = msg->flags;
while (count > 0) {
inval = i2c_inb(i2c_adap);
if (inval >= 0) {
*temp = inval;
rdcount++;
} else { /* read timed out */
break;
}
temp++;
count--;
/* Some SMBus transactions require that we receive the
transaction length as the first read byte. */
if (rdcount == 1 && (flags & I2C_M_RECV_LEN)) {
if (inval <= 0 || inval > I2C_SMBUS_BLOCK_MAX) {
if (!(flags & I2C_M_NO_RD_ACK))
acknak(i2c_adap, 0);
TRACE_ERROR("readbytes: invalid block length (%d)\n", inval);
return -EPROTO;
}
/* The original count value accounts for the extra
bytes, that is, either 1 for a regular transaction,
or 2 for a PEC transaction. */
count += inval;
msg->len += inval;
}
TRACE_DEBUG( "readbytes: 0x%02x %s\n",
inval,
(flags & I2C_M_NO_RD_ACK)
? "(no ack/nak)"
: (count ? "A" : "NA"));
if (!(flags & I2C_M_NO_RD_ACK)) {
inval = acknak(i2c_adap, count);
if (inval < 0)
return inval;
}
}
return rdcount;
}
/* doAddress initiates the transfer by generating the start condition (in
* try_address) and transmits the address in the necessary format to handle
* reads, writes as well as 10bit-addresses.
* returns:
* 0 everything went okay, the chip ack'ed, or IGNORE_NAK flag was set
* -x an error occurred (like: -ENXIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
unsigned char addr;
int ret, retries;
retries = nak_ok ? 0 : i2c_adap->retries;
if (flags & I2C_M_TEN) {
/* a ten bit address */
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
TRACE_DEBUG("addr0: %d\n", addr);
/* try extended address code...*/
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
TRACE_ERROR("died at extended address code\n");
return -ENXIO;
}
/* the remaining 8 bit address */
ret = i2c_outb(i2c_adap, msg->addr & 0xff);
if ((ret != 1) && !nak_ok) {
/* the chip did not ack / xmission error occurred */
TRACE_ERROR("died at 2nd address code\n");
return -ENXIO;
}
if (flags & I2C_M_RD) {
TRACE_DEBUG("emitting repeated start condition\n");
i2c_repstart(adap);
/* okay, now switch into reading mode */
addr |= 0x01;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok) {
TRACE_ERROR("died at repeated address code\n");
return -EIO;
}
}
} else { /* normal 7bit address */
addr = msg->addr << 1;
if (flags & I2C_M_RD)
addr |= 1;
if (flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok)
return -ENXIO;
}
return 0;
}
static int bit_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg msgs[], int num)
{
struct i2c_msg *pmsg;
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
int i, ret;
unsigned short nak_ok;
TRACE_DEBUG("emitting start condition\n");
i2c_start(adap);
for (i = 0; i < num; i++) {
pmsg = &msgs[i];
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
if (!(pmsg->flags & I2C_M_NOSTART)) {
if (i) {
if (msgs[i - 1].flags & I2C_M_STOP) {
TRACE_DEBUG("emitting enforced stop/start condition\n");
i2c_stop(adap);
i2c_start(adap);
} else {
TRACE_DEBUG("emitting repeated start condition\n");
i2c_repstart(adap);
}
}
ret = bit_doAddress(i2c_adap, pmsg);
if ((ret != 0) && !nak_ok) {
TRACE_DEBUG("NAK from device addr 0x%02x msg #%d\n", msgs[i].addr, i);
goto bailout;
}
}
if (pmsg->flags & I2C_M_RD) {
/* read bytes into buffer*/
ret = readbytes(i2c_adap, pmsg);
if (ret >= 1)
TRACE_DEBUG("read %d byte%s\n", ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
} else {
/* write bytes from buffer */
ret = sendbytes(i2c_adap, pmsg);
if (ret >= 1)
TRACE_DEBUG("wrote %d byte%s\n", ret, ret == 1 ? "" : "s");
if (ret < pmsg->len) {
if (ret >= 0)
ret = -EIO;
goto bailout;
}
}
}
ret = i;
bailout:
TRACE_DEBUG("emitting stop condition\n");
i2c_stop(adap);
return ret;
}
struct i2c_gpio_platform_data i2c_gpio[] = {
{
.devid = 0,
.sda_pin = I2C_GPIO0_SDA_PIN,
.scl_pin = I2C_GPIO0_SCL_PIN,
.udelay = 5, /* clk freq 500/udelay kHz */
.timeout = configTICK_RATE_HZ / 10, /* 100ms */
.sda_is_open_drain = 0,
.scl_is_open_drain = 0,
.scl_is_output_only = 1,
}
};
const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
};
int i2c_bit_add_bus(struct i2c_adapter *adap)
{
int ret;
/* register new adapter to i2c module... */
adap->algo = &i2c_bit_algo;
adap->retries = 3;
ret = i2c_add_adapter(adap);
if (ret < 0)
return ret;
return 0;
}
static int i2c_gpio_add_device(struct i2c_gpio_platform_data *pdevdata)
{
struct i2c_gpio_private_data *priv;
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
int ret;
priv = pvPortMalloc(sizeof(*priv));
if (!priv) {
TRACE_ERROR("[%s] pvPortMalloc failed, devid:%d\n", __func__, pdevdata->devid);
return -ENOMEM;
}
memset(priv, 0, sizeof(*priv));
adap = &priv->adap;
bit_data = &priv->bit_data;
pdata = &priv->pdata;
memcpy(pdata, pdevdata, sizeof(*pdata));
gpio_request(pdata->sda_pin);
gpio_request(pdata->scl_pin);
if (pdata->sda_is_open_drain) {
gpio_direction_output(pdata->sda_pin, 1);
bit_data->setsda = i2c_gpio_setsda_val;
} else {
gpio_direction_input(pdata->sda_pin);
bit_data->setsda = i2c_gpio_setsda_dir;
}
if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
gpio_direction_output(pdata->scl_pin, 1);
bit_data->setscl = i2c_gpio_setscl_val;
} else {
gpio_direction_input(pdata->scl_pin);
bit_data->setscl = i2c_gpio_setscl_dir;
}
if (!pdata->scl_is_output_only)
bit_data->getscl = i2c_gpio_getscl;
bit_data->getsda = i2c_gpio_getsda;
if (pdata->udelay)
bit_data->udelay = pdata->udelay;
else if (pdata->scl_is_output_only)
bit_data->udelay = 50; /* 10 kHz */
else
bit_data->udelay = 5; /* 100 kHz */
if (pdata->timeout)
bit_data->timeout = pdata->timeout;
else
bit_data->timeout = configTICK_RATE_HZ / 10; /* 100 ms */
bit_data->data = pdata;
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdata->devid);
adap->algo_data = bit_data;
ret = i2c_bit_add_bus(adap);
if (ret) {
TRACE_ERROR("[%s] i2c_bit_add_bus failed, devid:%d\n", __func__, pdata->devid);
return ret;
}
TRACE_INFO("using pins %u (SDA) and %u (SCL%s)\n",
pdata->sda_pin, pdata->scl_pin,
pdata->scl_is_output_only
? ", no clock stretching" : "");
return 0;
}
void i2c_gpio_init(void)
{
int i;
for(i=0; i<sizeof(i2c_gpio)/sizeof(i2c_gpio[0]); i++) {
i2c_gpio_add_device(&i2c_gpio[i]);
}
}
#endif

View File

@ -0,0 +1,135 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "errno.h"
#define MAX_I2C_DEVICE_NUM 4
static struct i2c_adapter *i2c_devs[MAX_I2C_DEVICE_NUM] = {NULL};
static int i2c_devices_count = 0;
int i2c_add_adapter(struct i2c_adapter *adap)
{
if (i2c_devices_count >= MAX_I2C_DEVICE_NUM)
return -1;
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = configTICK_RATE_HZ;
i2c_devs[i2c_devices_count++] = adap;
return 0;
}
struct i2c_adapter *i2c_open(const char *i2cdev)
{
struct i2c_adapter *adap;
int i;
for (i = 0; i < i2c_devices_count; i++) {
adap = i2c_devs[i];
if (!strcmp(adap->name, i2cdev)) {
adap->open_count++;
if (adap->open_count == 1)
adap->xMutex = xSemaphoreCreateMutex();
return adap;
}
}
return NULL;
}
void i2c_close(struct i2c_adapter *adap)
{
if (adap && --adap->open_count == 0)
vSemaphoreDelete(adap->xMutex);
}
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
configASSERT(adap && msgs);
xSemaphoreTake(adap->xMutex, portMAX_DELAY);
/* Retry automatically on arbitration loss */
orig_jiffies = xTaskGetTickCount();
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (xTaskGetTickCount() > orig_jiffies + adap->timeout)
break;
}
xSemaphoreGive(adap->xMutex);
return ret;
}
int i2c_slave_register(struct i2c_adapter *adap, u8 addr, i2c_slave_cb_t slave_cb)
{
int ret;
if (!adap || !slave_cb)
return -EINVAL;
if (!(adap->flags & I2C_CLIENT_SLAVE))
TRACE_WARNING("%s: client slave flag not set.\n", __func__);
if (!adap->algo->reg_slave) {
printf("%s: not supported by adapter\n", __func__);
return -ENOTSUP;
}
adap->slave_cb = slave_cb;
adap->addr = addr;
ret = adap->algo->reg_slave(adap);
if (ret) {
adap->slave_cb = NULL;
printf("%s: adapter returned error %d\n", __func__, ret);
}
return ret;
}
int i2c_slave_unregister(struct i2c_adapter *adap)
{
int ret;
if (!adap)
return -EINVAL;
if (!adap->algo->unreg_slave) {
printf("%s: not supported by adapter\n", __func__);
return -ENOTSUP;
}
ret = adap->algo->unreg_slave(adap);
if (ret == 0)
adap->slave_cb = NULL;
else
printf("%s: adapter returned error %d\n", __func__, ret);
return ret;
}
void i2c_init(void)
{
#ifdef HW_I2C0_SUPPORT
i2c_dw_init(0);
#endif
#ifdef HW_I2C1_SUPPORT
i2c_dw_init(1);
#endif
#ifdef ANALOG_I2C_SUPPORT
i2c_gpio_init();
#endif
}

View File

@ -0,0 +1,473 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#include "audio/audio.h"
#include "i2s.h"
#include "soc-dai.h"
#define I2S_SLAVE 0
#define I2S_MASTER 1
typedef struct ark_i2s_private_data {
struct snd_soc_dai_ops adc_ops;
struct snd_soc_dai_ops dac_ops;
} ark_i2s_pd;
extern int audio_codec_adc_init(struct snd_soc_dai_ops *ops);
extern int audio_codec_dac_init(struct snd_soc_dai_ops *ops);
#if 0
static void ark_i2s_start_play(struct ark_i2s_data *i2s)
{
u32 val = readl(i2s->base + I2S_SACR1);
val &= ~SACR1_DRPL;
writel(val, i2s->base + I2S_SACR1);
}
static void ark_i2s_start_record(struct ark_i2s_data *i2s)
{
u32 val = readl(i2s->base + I2S_SACR1);
val &= ~SACR1_DREC;
writel(val, i2s->base + I2S_SACR1);
}
#endif
static void ark_i2s_stop_play(struct ark_i2s_data *i2s)
{
u32 val = readl(i2s->base + I2S_SACR1);
val |= SACR1_DRPL;
writel(val, i2s->base + I2S_SACR1);
}
static void ark_i2s_stop_record(struct ark_i2s_data *i2s)
{
u32 val = readl(i2s->base + I2S_SACR1);
val |= SACR1_DREC;
writel(val, i2s->base + I2S_SACR1);
}
static void ark_i2s_restart(struct ark_i2s_data *i2s, int stream)
{
u32 val = readl(i2s->base + I2S_SACR1);
/* close record and replay */
writel(val | SACR1_DREC | SACR1_DRPL, i2s->base + I2S_SACR1);
mdelay(1);
if(stream == AUDIO_STREAM_REPLAY)
val &= ~SACR1_DRPL;
else
val &= ~SACR1_DREC;
writel(val, i2s->base + I2S_SACR1);
}
int ark_i2s_startup(struct ark_i2s_data *i2s, int stream)
{
unsigned int val;
xSemaphoreTake(i2s->mutex, portMAX_DELAY);
if (stream == AUDIO_STREAM_REPLAY)
{
/* close play */
ark_i2s_stop_play(i2s);
val = readl(i2s->base + I2S_SACR0);
val |= SACR0_CHANLOCK | SACR0_RFTH(0x10) | SACR0_TFTH(0xF) | SACR0_ENB;
if(i2s->cfg[stream].lfirst)
val |= SACR0_TFIFOFIRSTBIT;
else
val &= ~SACR0_TFIFOFIRSTBIT;
if(i2s->cfg[stream].channels == 1)
val |= SACR0_SCBIT; /* single channel */
else
val &= ~SACR0_SCBIT;
if(i2s->cfg[stream].bits > 16)
val |= SACR0_BITS; /* 32 Bits */
else
val &= ~SACR0_BITS;
if(i2s->cfg[stream].master)
val |= SACR0_BCKD | SACR0_SYNCD;
else
val &= ~(SACR0_BCKD | SACR0_SYNCD);
if(i2s->dma_txch)
val |= SACR0_TDMAEN;
else
val &= ~SACR0_TDMAEN;
writel(val, i2s->base + I2S_SACR0);
/* interrupt clear */
val = readl(i2s->base + I2S_SAICR);
val |= (SAICR_TFS | SAICR_TUR);
writel(val, i2s->base + I2S_SAICR);
val &= ~(SAICR_TFS | SAICR_TUR);
writel(val, i2s->base + I2S_SAICR);
/* interrupt enable */
val = readl(i2s->base + I2S_SAIMR);
val |= (SAIMR_TFS | SAIMR_TUR);
writel(val, i2s->base + I2S_SAIMR);
/* start replay */
ark_i2s_restart(i2s, stream);
}
else if(stream == AUDIO_STREAM_RECORD)
{
ark_i2s_pd *pdata = i2s->extdata;
/* close record */
ark_i2s_stop_record(i2s);
if(pdata && pdata->adc_ops.hw_params)
{
struct snd_soc_hw_params params;
params.rates = i2s->cfg[stream].rates;
params.channels = i2s->cfg[stream].channels;
params.bits = i2s->cfg[stream].bits;
pdata->adc_ops.hw_params(&params);
}
val = readl(i2s->base + I2S_SACR0);
val |= SACR0_CHANLOCK| SACR0_RFTH(0x10) | SACR0_TFTH(0xF) | SACR0_ENB;
if(i2s->cfg[stream].lfirst)
val |= SACR0_RFIFIFIRSTBIT;
else
val &= ~SACR0_RFIFIFIRSTBIT;
if(i2s->cfg[stream].channels == 1)
val |= SACR0_SCBIT;
else
val &= ~SACR0_SCBIT;
if(i2s->cfg[stream].bits > 16)
val |= SACR0_BITS; //32 Bits.
else
val &= ~SACR0_BITS;
if(i2s->dma_rxch)
val |= SACR0_RDMAEN;
else
val &= ~SACR0_RDMAEN;
if(i2s->cfg[stream].master)
{
val |= SACR0_BCKD | SACR0_SYNCD;
writel(val, i2s->base + I2S_SACR0);
}
else
{
writel(val, i2s->base + I2S_SACR0);
#if 0
/* 630hv100 record work in slave mode, bclk need inverse */
if(i2s->id == 0) {
val = readl(REGS_SYSCTL_BASE + SYS_PER_CLK_CFG);
val |= (1<<18);
writel(val, REGS_SYSCTL_BASE + SYS_PER_CLK_CFG);
} else if(i2s->id == 1) {
val = readl(REGS_SYSCTL_BASE + SYS_BUS_CLK1_CFG);
val |= (1<<6); //1:bclk inverse; 0:normal.
writel(val, REGS_SYSCTL_BASE + SYS_BUS_CLK1_CFG);
}
#endif
}
/* interrupt clear */
val = readl(i2s->base + I2S_SAICR);
val |= (SAICR_ROR | SAICR_RFS);
writel(val, i2s->base + I2S_SAICR);
val &= ~(SAICR_ROR | SAICR_RFS);
writel(val, i2s->base + I2S_SAICR);
/* interrupt enable */
val = readl(i2s->base + I2S_SAIMR);
val |= (SAIMR_ROR | SAIMR_RFS);
writel(val, i2s->base + I2S_SAIMR);
if(pdata && pdata->adc_ops.startup)
{
pdata->adc_ops.startup(1);
}
/* start record */
ark_i2s_restart(i2s, stream);
}
xSemaphoreGive(i2s->mutex);
return 0;
}
void ark_i2s_set_volume(struct ark_i2s_data *i2s, int lvol, int rvol)
{
//writel(DACR0_LVOL(lvol) | DACR0_RVOL(rvol), i2s->base + I2S_DACR0);
}
int ark_i2s_set_rate(struct ark_i2s_data *i2s, int stream, unsigned int rate)
{
u32 step = 256 * 2, modulo;
u32 val, freq;
if (!i2s->nco_reg)
return 0;
xSemaphoreTake(i2s->mutex, portMAX_DELAY);
if(stream == AUDIO_STREAM_REPLAY)
{
if(i2s->cfg[stream].rates != rate)
i2s->cfg[stream].rates = rate;
}
else if(stream == AUDIO_STREAM_RECORD)
{
if(i2s->cfg[stream].rates != rate)
i2s->cfg[stream].rates = rate;
}
else
{
printf("%s, Invalid i2s stream:%d.\n", __func__, stream);
return -EINVAL;
}
/* mclk = rate * 256, mclk = freq * step / (2 * modulo) */
freq = ulClkGetRate(i2s->clkid);
modulo = freq / rate;
val = (step << 16) | modulo;
writel(val, i2s->nco_reg);
#if 0
writel(val, 0x6000006C); //set i2s0 rate.
#endif
xSemaphoreGive(i2s->mutex);
return 0;
}
int ark_i2s_set_params(struct ark_i2s_data *i2s, int stream, int rates, int channels, int bits)
{
int ret = 0;
xSemaphoreTake(i2s->mutex, portMAX_DELAY);
if(stream == AUDIO_STREAM_REPLAY)
{
i2s->cfg[stream].rates = rates;
i2s->cfg[stream].channels = channels;
i2s->cfg[stream].bits = bits;
}
else if(stream == AUDIO_STREAM_RECORD)
{
i2s->cfg[stream].rates = rates;
i2s->cfg[stream].channels = channels;
i2s->cfg[stream].bits = bits;
}
else
{
printf("%s, Invalid i2s stream:%d.\n", __func__, stream);
ret = -EINVAL;
}
xSemaphoreGive(i2s->mutex);
ark_i2s_set_rate(i2s, stream, rates);
return ret;
}
void ark_i2s_stop(struct ark_i2s_data *i2s, int stream)
{
ark_i2s_pd *pdata = i2s->extdata;
u32 val;
xSemaphoreTake(i2s->mutex, portMAX_DELAY);
if(stream == AUDIO_STREAM_REPLAY)
{
ark_i2s_stop_play(i2s);
if(i2s->dma_txch)
{
val = readl(i2s->base + I2S_SACR0);
val &= ~SACR0_TDMAEN;
writel(val, i2s->base + I2S_SACR0);
}
/* interrupt disable */
val = readl(i2s->base + I2S_SAIMR);
val &= ~(SAIMR_TFS | SAIMR_TUR);
writel(val, i2s->base + I2S_SAIMR);
}
else if(stream == AUDIO_STREAM_RECORD)
{
ark_i2s_stop_record(i2s);
if(i2s->dma_rxch)
{
val = readl(i2s->base + I2S_SACR0);
val &= ~SACR0_RDMAEN;
writel(val, i2s->base + I2S_SACR0);
}
/* interrupt disable */
val = readl(i2s->base + I2S_SAIMR);
val &= ~(SAIMR_ROR | SAIMR_RFS);
writel(val, i2s->base + I2S_SAIMR);
if(pdata && pdata->adc_ops.startup)
pdata->adc_ops.startup(0);
}
val = readl(i2s->base + I2S_SACR1);
if((val & (SACR1_DRPL | SACR1_DREC)) == (SACR1_DRPL | SACR1_DREC))
{
writel(readl(i2s->base + I2S_SACR0) & ~SACR0_ENB, i2s->base + I2S_SACR0);
}
xSemaphoreGive(i2s->mutex);
}
void i2s_interrupt_handler(void *param)
{
struct ark_i2s_data *i2s = (struct ark_i2s_data *)param;
unsigned int status;
//unsigned int val;
if(!i2s)
return;
status = readl(i2s->base + I2S_SASR0);
#if 1
writel(0xFF, i2s->base + I2S_SAICR);
if (status & SASR0_TUR) {
//printf("i2s txfifo underrun.\n");
}
#else
val = readl(i2s->base + I2S_SAICR);
if(status & SASR0_TFS)
val |= SAICR_TFS;
if(status & SASR0_TUR)
val |= SAICR_TUR;
if(status & SASR0_RFS)
val |= SAICR_RFS;
if(status & SASR0_ROR)
val |= SAICR_ROR;
writel(val, i2s->base + I2S_SAICR);
#endif
writel(0x0, i2s->base + I2S_SAICR);
}
static void i2s_sw_reset(struct ark_i2s_data *i2s)
{
writel(SACR0_RST, i2s->base + I2S_SACR0);
udelay(1);
writel(0, i2s->base + I2S_SACR0);
}
static int codec_init(struct ark_i2s_data *i2s, int flags)
{
ark_i2s_pd *pdata = NULL;
int ret = -1;
if(!i2s->extdata)
{
pdata = (ark_i2s_pd *)pvPortMalloc(sizeof(struct ark_i2s_private_data));
memset(&pdata->adc_ops, 0, sizeof(ark_i2s_pd));
i2s->extdata = (void *)pdata;
}
else
{
pdata = i2s->extdata;
}
if(pdata)
{
if((flags & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
#if AUDIO_CODEC_ADC_IC != AUDIO_CODEC_ADC_NONE
ret = audio_codec_adc_init(&pdata->adc_ops);
if(ret == 0)
{
if(pdata->adc_ops.init)
pdata->adc_ops.init(!i2s->cfg[AUDIO_STREAM_RECORD].master);
}
#endif
}
if((flags & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
#if AUDIO_CODEC_DAC_IC != AUDIO_CODEC_DAC_NONE
ret = audio_codec_dac_init(&pdata->dac_ops);
if(ret == 0)
{
if(pdata->dac_ops.init)
pdata->dac_ops.init(!i2s->cfg[AUDIO_STREAM_REPLAY].master);
}
#endif
}
}
return ret;
}
int ark_i2s_init(struct ark_i2s_data *i2s, int flags)
{
struct ark_i2s_cfg *cfg = NULL;
int softreset;
int irqn;
if(i2s->id == I2S_ID0)
{
softreset = softreset_i2s;
irqn = I2S_IRQn;
}
else if(i2s->id == I2S_ID1)
{
softreset = softreset_i2s1;
irqn = I2S1_IRQn;
}
else
{
printf("%s, Invalid i2s id:%d.\n", __func__, i2s->id);
return -EINVAL;
}
if(!(flags & AUDIO_FLAG_REPLAY_RECORD))
{
printf("%s, Invalid flags:0x%x.\n", __func__, flags);
return -EINVAL;
}
if((flags & AUDIO_FLAG_REPLAY_RECORD) == AUDIO_FLAG_REPLAY_RECORD)
i2s->full_duplex = 1;
else
i2s->full_duplex = 0;
if((flags & AUDIO_FLAG_REPLAY) == AUDIO_FLAG_REPLAY)
{
i2s->dma_txch = dma_request_channel(I2S_DMA_TXCH);
if (!i2s->dma_txch)
{
printf("%s() i2s replay dma_request_channel fail.\n", __func__);
return -ENODEV;
}
cfg = &i2s->cfg[AUDIO_STREAM_REPLAY];
memset(cfg, 0, sizeof(struct ark_i2s_cfg));
cfg->master = I2S_MASTER;
cfg->lfirst = 0;
}
if((flags & AUDIO_FLAG_RECORD) == AUDIO_FLAG_RECORD)
{
i2s->dma_rxch = dma_request_channel(I2S_DMA_RXCH);
if (!i2s->dma_rxch)
{
printf("%s() i2s record dma_request_channel fail.\n", __func__);
return -ENODEV;
}
cfg = &i2s->cfg[AUDIO_STREAM_RECORD];
memset(cfg, 0, sizeof(struct ark_i2s_cfg));
cfg->master = I2S_MASTER;
cfg->lfirst = 0;
}
i2s->mutex = xSemaphoreCreateMutex();
sys_soft_reset(softreset);
i2s_sw_reset(i2s);
request_irq(irqn, 0, i2s_interrupt_handler, i2s);
codec_init(i2s, flags);
return 0;
}

View File

@ -0,0 +1,342 @@
#include "FreeRTOS.h"
#include "board.h"
#include "chip.h"
#ifdef REVERSE_TRACK
#include "vg_driver.h"
#endif
#define ITU656IN_MODULE_EN 0x00
#define ITU656IN_IMR 0x04
#define ITU656IN_ICR 0x08
#define ITU656IN_ISR 0x0C
#define ITU656IN_LINE_NUM_PER_FIELD 0x10
#define ITU656IN_PIX_NUM_PER_LINE 0x14
#define ITU656IN_PIX_LINE_NUM_DELTA 0x18
#define ITU656IN_INPUT_SEL 0x1c
#define ITU656IN_SEP_MODE_SEL 0x20
#define ITU656IN_H_STRAT 0x24
#define ITU656IN_H_END 0x28
#define ITU656IN_H_WIDTH 0x2c
#define ITU656IN_V_START_0 0x30
#define ITU656IN_V_END_0 0x34
#define ITU656IN_V_START_1 0x38
#define ITU656IN_V_END_1 0x3C
#define ITU656IN_V_FIELD_0 0x40
#define ITU656IN_V_FIELD_1 0x44
#define ITU656IN_P_N_DETECT 0x48
#define ITU656IN_ENABLE_REG 0x4c
#define ITU656IN_HFZ 0x50
#define ITU656IN_SIZE 0x54
#define ITU656IN_TOTAL_PIX 0x58
#define ITU656IN_DRAM_DEST1 0x5c
#define ITU656IN_DRAM_DEST2 0x60
#define ITU656IN_TOTAL_PIX_OUT 0x64
#define ITU656IN_OUTLINE_NUM_PER_FIELD 0x68
#define ITU656IN_H_cut_num 0x6c
#define ITU656IN_V_cut_num 0x70
#define ITU656IN_DATA_ERROR_MODE 0x74
#define ITU656IN_MIRR_SET 0x78
#define ITU656IN_RESET 0x7c
#define ITU656IN_OUTPUT_TYPE 0x80
#define ITU656IN_PN_DETCET 0x84
#define ITU656IN_YUV_TYPESEL 0x88
#define ITU656IN_POP_ERR_INT (1 << 10)
#define ITU656IN_SLICE_INT (1 << 9)
#ifndef VIN_SMALL_MEM
#define ITU656IN_BUF_NUM 3
#else
#define ITU656IN_BUF_NUM 2
#endif
#define ITUFRAME_MAX_WIDTH VIN_WIDTH
#define ITUFRAME_MAX_HEIGHT VIN_HEIGHT
typedef struct {
uint32_t yaddr;
uint32_t uvaddr;
int status;
} ItuBufInfo;
static uint32_t itubase = REGS_ITU_BASE;
static ItuBufInfo itubuf_info[ITU656IN_BUF_NUM] = {0};
static ItuConfigPara itu_para;
static TaskHandle_t itu_task;
static SemaphoreHandle_t itu_mutex = NULL;
static void *itu_buf = NULL;
static int itu_enable = 0;
static int itu_take_video = 0;
static void itu_push_addr(void)
{
int i;
for (i = 0; i < ITU656IN_BUF_NUM; i++) {
if (!itubuf_info[i].status) {
writel(itubuf_info[i].yaddr, itubase + ITU656IN_DRAM_DEST1);
writel(itubuf_info[i].uvaddr, itubase + ITU656IN_DRAM_DEST1);
itubuf_info[i].status = 1;
break;
}
}
}
static void itu_free_addr(uint32_t yaddr)
{
int i;
for (i = 0; i < ITU656IN_BUF_NUM; i++) {
if (itubuf_info[i].yaddr == yaddr) {
itubuf_info[i].status = 0;
break;
}
}
}
static void itu_interupt_handler(void *param)
{
unsigned int status = readl(itubase + ITU656IN_ISR);
unsigned int yaddr;
//printf("itu int status = 0x%x.\n", status);
writel(status, itubase + ITU656IN_ICR);
if (status & ITU656IN_POP_ERR_INT) {
printf("itu pop err.\n");
itu_push_addr();
}
if (status & ITU656IN_SLICE_INT) {
yaddr = readl(itubase + ITU656IN_DRAM_DEST1);
//printf("yaddr 0x%x.\n", yaddr);
itu_free_addr(yaddr);
itu_push_addr();
xTaskNotifyFromISR(itu_task, yaddr, eSetValueWithOverwrite, 0);
}
}
int itu_config(ItuConfigPara *para)
{
uint32_t val;
int ret = 0;
int i;
configASSERT(para->in_width <= ITUFRAME_MAX_WIDTH &&
para->in_height <= ITUFRAME_MAX_HEIGHT);
xSemaphoreTake(itu_mutex, portMAX_DELAY);
writel(0xffffffff, itubase + ITU656IN_ICR);
writel(1 << 2, itubase + ITU656IN_MODULE_EN);
val = readl(itubase + ITU656IN_SEP_MODE_SEL);
writel(val | (1 << 3), itubase + ITU656IN_SEP_MODE_SEL); //控制YUV顺序
writel(0, itubase + ITU656IN_IMR);
if (para->itu601) {
val = readl(itubase + ITU656IN_SEP_MODE_SEL);
val &= ~(1 << 2);
writel(val, itubase + ITU656IN_SEP_MODE_SEL);
writel(0, itubase + ITU656IN_INPUT_SEL);
val = readl(itubase + ITU656IN_MODULE_EN);
writel(val | 1, itubase + ITU656IN_MODULE_EN);
} else {
writel(1, itubase + ITU656IN_INPUT_SEL);
}
writel((5 << 1), itubase + ITU656IN_ENABLE_REG);
writel(0, itubase + ITU656IN_MIRR_SET); //镜像
writel(para->yuv_type & 1, itubase + ITU656IN_YUV_TYPESEL); //0:y_uv 1:yuyv
#ifndef VIN_SMALL_MEM
writel(para->out_format & 1, itubase + ITU656IN_OUTPUT_TYPE); //0:420 1:422
#else
writel(ITU_YUV420, itubase + ITU656IN_OUTPUT_TYPE); //0:420 1:422
#endif
writel(para->in_width << 16, itubase + ITU656IN_SIZE);
//奇偶场中断(P:720*288,N:720*240)
writel(para->in_width * para->in_height, itubase + ITU656IN_TOTAL_PIX_OUT);
//slice 中断(P:720*288,N:720*240)
writel(para->in_width * para->in_height, itubase + ITU656IN_TOTAL_PIX);
writel(para->in_height, itubase + ITU656IN_OUTLINE_NUM_PER_FIELD);
//reset fifo
val = readl(itubase + ITU656IN_RESET);
val |= 1 << 1;
writel(val, itubase + ITU656IN_RESET);
udelay(10);
val &= ~(1 << 1);
writel(val, itubase + ITU656IN_RESET);
//push addr
for(i = 0; i < ITU656IN_BUF_NUM; i++) {
#ifndef VIN_SMALL_MEM
if (para->out_format == ITU_YUV422)
itubuf_info[i].yaddr = (uint32_t)itu_buf + para->in_width * para->in_height * 2 * i;
else
#endif
itubuf_info[i].yaddr = (uint32_t)itu_buf + para->in_width * para->in_height * 3 / 2 * i;
if (itubuf_info[i].yaddr == 0) {
printf("no enough memory for itu frames.\n");
ret = -ENOMEM;
goto end;
}
itubuf_info[i].uvaddr = itubuf_info[i].yaddr + para->in_width * para->in_height;
writel(itubuf_info[i].yaddr, itubase + ITU656IN_DRAM_DEST1);
writel(itubuf_info[i].uvaddr, itubase + ITU656IN_DRAM_DEST1);
itubuf_info[i].status = 1;
}
/* enable slice int */
writel(ITU656IN_POP_ERR_INT | ITU656IN_SLICE_INT, itubase + ITU656IN_IMR);
memcpy(&itu_para, para, sizeof(itu_para));
end:
xSemaphoreGive(itu_mutex);
return ret;
}
void itu_start(void)
{
uint32_t val;
//enable write data
xSemaphoreTake(itu_mutex, portMAX_DELAY);
val = readl(itubase + ITU656IN_MODULE_EN);
val &= ~(1 << 2);
writel(val, itubase + ITU656IN_MODULE_EN);
val = readl(itubase + ITU656IN_ENABLE_REG);
writel(val | 1, itubase + ITU656IN_ENABLE_REG);
itu_enable = 1;
xSemaphoreGive(itu_mutex);
}
void itu_stop(void)
{
uint32_t val;
//disable write data
xSemaphoreTake(itu_mutex, portMAX_DELAY);
val = readl(itubase + ITU656IN_MODULE_EN);
val |= (1 << 2);
writel(val, itubase + ITU656IN_MODULE_EN);
val = readl(itubase + ITU656IN_ENABLE_REG);
writel(val & ~1, itubase + ITU656IN_ENABLE_REG);
itu_enable = 0;
#ifndef REVERSE_UI
ark_lcd_osd_enable(LCD_VIDEO_LAYER, 0);
ark_lcd_set_osd_sync(LCD_VIDEO_LAYER);
ark_lcd_osd_enable(LCD_OSD1, 1);
ark_lcd_set_osd_sync(LCD_OSD1);
#endif
xTaskNotify(itu_task, 0, eSetValueWithOverwrite);
xSemaphoreGive(itu_mutex);
}
void itu_display_thread(void *param)
{
uint32_t ulNotifiedValue;
uint32_t yaddr, uvaddr, dstaddr;
int format;
for (;;) {
xTaskNotifyWait( 0x00, /* Don't clear any notification bits on entry. */
0xffffffff, /* Reset the notification value to 0 on exit. */
&ulNotifiedValue, /* Notified value pass out in ulNotifiedValue. */
portMAX_DELAY);
//printf("received address 0x%x.\n", ulNotifiedValue);
xSemaphoreTake(itu_mutex, portMAX_DELAY);
if (!itu_enable) {
xSemaphoreGive(itu_mutex);
vVideoDisplayBufFree(dstaddr);
/* 倒车显示优先级高,等倒车结束之后才释放视频显示资源 */
if (itu_take_video) {
vVideoDisplayBufGive();
itu_take_video = 0;
}
continue;
}
/* 获取视频显示资源 */
if (!itu_take_video) {
xVideoDisplayBufTake(portMAX_DELAY);
itu_take_video = 1;
}
if (!ark_lcd_get_osd_info_atomic_isactive(LCD_VIDEO_LAYER)) {
ark_lcd_wait_for_vsync();
}
/* 通过该接口获取视频显示共用的缓存可以避免切换过程中的黑屏、
花屏等问题 */
dstaddr = ulVideoDisplayBufGet();
yaddr = ulNotifiedValue;
uvaddr = yaddr + itu_para.in_width * itu_para.in_height;
#ifndef VIN_SMALL_MEM
if (itu_para.out_format == ITU_YUV422)
format = PXP_SRC_FMT_YUV2P422;
else
#endif
format = PXP_SRC_FMT_YUV2P420;
pxp_scaler_rotate(yaddr, uvaddr, 0, format, itu_para.in_width, itu_para.in_height,
dstaddr, 0, PXP_OUT_FMT_RGB565, itu_para.out_width, itu_para.out_height, LCD_ROTATE_ANGLE);
#ifdef REVERSE_TRACK
extern int get_reverse_track_index(void);
static int index = 58;
index = get_reverse_track_index();
xm_vg_set_gpu_fb_addr(dstaddr);
xm_vg_draw_prepare(&index);
xm_vg_draw_start();
#endif
LcdOsdInfo info = {0};
info.x = itu_para.out_x;
info.y = itu_para.out_y;
info.width = itu_para.out_width;
info.height = itu_para.out_height;
info.format = LCD_OSD_FORAMT_RGB565;
info.yaddr = dstaddr;
ark_lcd_set_osd_info_atomic(LCD_VIDEO_LAYER, &info);
ark_lcd_osd_enable(LCD_VIDEO_LAYER, 1);
ark_lcd_set_osd_sync(LCD_VIDEO_LAYER);
#ifndef REVERSE_UI
ark_lcd_osd_enable(LCD_OSD1, 0);
ark_lcd_set_osd_sync(LCD_OSD1);
#endif
vVideoDisplayBufRender(dstaddr);
xSemaphoreGive(itu_mutex);
vTaskDelay(pdMS_TO_TICKS(1));
}
}
int itu_init(void)
{
sys_soft_reset(softreset_itu);
itu_mutex = xSemaphoreCreateMutex();
#ifndef VIN_SMALL_MEM
itu_buf = pvPortMalloc(ITUFRAME_MAX_WIDTH * ITUFRAME_MAX_HEIGHT * 2 * ITU656IN_BUF_NUM);
#else
itu_buf = pvPortMalloc(ITUFRAME_MAX_WIDTH * ITUFRAME_MAX_HEIGHT * 3 / 2 * ITU656IN_BUF_NUM);
#endif
if (!itu_buf) {
printf("Itu malloc memory fail.\n");
return -ENOMEM;
}
if (xTaskCreate(itu_display_thread, "itudis", configMINIMAL_STACK_SIZE, NULL,
configMAX_PRIORITIES - 2, &itu_task) != pdPASS) {
printf("create itu display task fail.\n");
return -1;
}
request_irq(ITU_IRQn, 0, itu_interupt_handler, NULL);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,222 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#if LCD_INTERFACE_TYPE == LCD_INTERFACE_MIPI
#define ARK_MIPI_WRITEL(reg, val) writel(val, REGS_MIPI_BASE+reg)
#define ARK_MIPI_READL(reg) readl(REGS_MIPI_BASE+reg)
void mipi_init(void)
{
unsigned int val;
udelay(10000);
val = readl(REGS_SYSCTL_BASE + SYS_ANA1_CFG);
val |= (0x3 <<30)|(0x7 <<22);
writel(val, REGS_SYSCTL_BASE + SYS_ANA1_CFG);
val = readl(REGS_SYSCTL_BASE + SYS_PERCTL_CFG);
val |= (0x1<<8);
writel(val, REGS_SYSCTL_BASE + SYS_PERCTL_CFG);
ARK_MIPI_WRITEL(0x04, 0x0);
ARK_MIPI_WRITEL(0xa0, 0x0);
ARK_MIPI_WRITEL(0x08, 0x17);
ARK_MIPI_WRITEL(0x2c, 0x1c);
ARK_MIPI_WRITEL(0x9c, 0x09000c);
ARK_MIPI_WRITEL(0x94, 0x0);
ARK_MIPI_WRITEL(0x98, 0x180014);
ARK_MIPI_WRITEL(0xa4, 0x2803);
//ARK_MIPI_WRITEL(0x38, 0x1bf02);
ARK_MIPI_WRITEL(0x38, 0xbf02);
ARK_MIPI_WRITEL(0x0c, 0x0);
ARK_MIPI_WRITEL(0x68,0x000b4700);
// <20><>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD>
ARK_MIPI_WRITEL(0x10, 0x5);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ARK_MIPI_WRITEL(0x14, 0x0);// active high
// <20><>Ч<EFBFBD><D0A7><EFBFBD><EFBFBD>720
ARK_MIPI_WRITEL(0x3c, 0x1e0);
// HSAΪ12
ARK_MIPI_WRITEL(0x48, 29);
// Thbp = (Pixel clk period/Clklane byte period)*HBP
ARK_MIPI_WRITEL(0x4c, 18);
// Thline = (Pixel clk period/Clklane byte period)*HLINE
// HLINE = HSA+HBP+HACT+HFP
ARK_MIPI_WRITEL(0x50, 0x237);
ARK_MIPI_WRITEL(0x54, 0x10);// VSA <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ARK_MIPI_WRITEL(0x58, 0x6);// VBP <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
ARK_MIPI_WRITEL(0x5c, 0x6);// VFP <20><>ǰ<EFBFBD><C7B0>
ARK_MIPI_WRITEL(0x60, 0x500);// VACT <20><><EFBFBD><EFBFBD>Ч
ARK_MIPI_WRITEL(0x34, 0x1);
ARK_MIPI_WRITEL(0x18, 0xa000a);
ARK_MIPI_WRITEL(0xc4, 0xffffffff);
ARK_MIPI_WRITEL(0xc8, 0xffffffff);
ARK_MIPI_WRITEL(0x04, 0x1);
ARK_MIPI_WRITEL(0xa0, 0xf);
ARK_MIPI_WRITEL(0xb8, 0x44);
ARK_MIPI_WRITEL(0xb8, 0x10044);
ARK_MIPI_WRITEL(0xb4, 0x2);
ARK_MIPI_WRITEL(0xb4, 0x0);
ARK_MIPI_WRITEL(0xb8, 0x2c);
ARK_MIPI_WRITEL(0xb4, 0x2);
ARK_MIPI_WRITEL(0xb4, 0x0);
while(!(ARK_MIPI_READL(0xb0) & 0x5));
// <20><><EFBFBD><EFBFBD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0BBB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD>PHY<48><59><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɺ<EFBFBD>
ARK_MIPI_WRITEL(0x94, 0x1);
//udelay(200000);
//ARK_MIPI_WRITEL(0x180, 0x4);
ARK_MIPI_WRITEL(0x100, 0x1000000);
udelay(200000);
//panel init
ARK_MIPI_WRITEL(0x70, 0x9483ffb9);
ARK_MIPI_WRITEL(0x6c, 0x439);
ARK_MIPI_WRITEL(0x70, 0x721248b1);
ARK_MIPI_WRITEL(0x70, 0x71243209);
ARK_MIPI_WRITEL(0x70, 0x432f51);
ARK_MIPI_WRITEL(0x6c, 0xb39);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x680363ba);
ARK_MIPI_WRITEL(0x70, 0xc0b26b);
ARK_MIPI_WRITEL(0x6c, 0x739);
ARK_MIPI_WRITEL(0x70, 0x64a040b2);
ARK_MIPI_WRITEL(0x70, 0x2f0a0e);
ARK_MIPI_WRITEL(0x6c, 0x739);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x1c781cb4);
ARK_MIPI_WRITEL(0x70, 0x01781c78);
ARK_MIPI_WRITEL(0x70, 0x0055860c);
ARK_MIPI_WRITEL(0x70, 0x1c781c3f);
ARK_MIPI_WRITEL(0x70, 0x01781c78);
ARK_MIPI_WRITEL(0x70, 0x860c);
ARK_MIPI_WRITEL(0x6c, 0x1639);
ARK_MIPI_WRITEL(0x70, 0x000000d3);
ARK_MIPI_WRITEL(0x70, 0x08076400);
ARK_MIPI_WRITEL(0x70, 0x07103208);
ARK_MIPI_WRITEL(0x70, 0x15320700);
ARK_MIPI_WRITEL(0x70, 0x00150515);
ARK_MIPI_WRITEL(0x70, 0x00081032);
ARK_MIPI_WRITEL(0x70, 0x09093335);
ARK_MIPI_WRITEL(0x70, 0x37070d37);
ARK_MIPI_WRITEL(0x70, 0x080e);
ARK_MIPI_WRITEL(0x6c, 0x2239);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x241818d5);
ARK_MIPI_WRITEL(0x70, 0x1b1a1a24);
ARK_MIPI_WRITEL(0x70, 0x0605041b);
ARK_MIPI_WRITEL(0x70, 0x02010007);
ARK_MIPI_WRITEL(0x70, 0x19181803);
ARK_MIPI_WRITEL(0x70, 0x22212019);
ARK_MIPI_WRITEL(0x70, 0x18181823);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x070, 0x18);
ARK_MIPI_WRITEL(0x6c, 0x2d39);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x241919d6);
ARK_MIPI_WRITEL(0x70, 0x1b1a1a24);
ARK_MIPI_WRITEL(0x70, 0x0102031b);
ARK_MIPI_WRITEL(0x70, 0x05060700);
ARK_MIPI_WRITEL(0x70, 0x18181804);
ARK_MIPI_WRITEL(0x70, 0x21222318);
ARK_MIPI_WRITEL(0x70, 0x18181820);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18181818);
ARK_MIPI_WRITEL(0x70, 0x18);
ARK_MIPI_WRITEL(0x6c, 0x2d39);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x4848b6);
ARK_MIPI_WRITEL(0x6c, 0x339);
ARK_MIPI_WRITEL(0x70, 0x281e00e0);
ARK_MIPI_WRITEL(0x70, 0x3733302f);
ARK_MIPI_WRITEL(0x70, 0x887a6b34);
ARK_MIPI_WRITEL(0x70, 0x9e9b8b84);
ARK_MIPI_WRITEL(0x70, 0xa4aaaa9f);
ARK_MIPI_WRITEL(0x70, 0x5d5fc0b1);
ARK_MIPI_WRITEL(0x70, 0x70686561);
ARK_MIPI_WRITEL(0x70, 0x1e007f7e);
ARK_MIPI_WRITEL(0x70, 0x33302f28);
ARK_MIPI_WRITEL(0x70, 0x7a6b3437);
ARK_MIPI_WRITEL(0x70, 0x9b8b8488);
ARK_MIPI_WRITEL(0x70, 0xaaaa9f9e);
ARK_MIPI_WRITEL(0x70, 0x5fc0b1a4);
ARK_MIPI_WRITEL(0x70, 0x6865615d);
ARK_MIPI_WRITEL(0x70, 0x7f7e70);
ARK_MIPI_WRITEL(0x6c, 0x3b39);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x3cc);
ARK_MIPI_WRITEL(0x6c, 0x239);
ARK_MIPI_WRITEL(0x70, 0x311fc0);
ARK_MIPI_WRITEL(0x6c, 0x339);
ARK_MIPI_WRITEL(0x070, 0x2d4);
ARK_MIPI_WRITEL(0x6c, 0x239);
udelay(10000);
ARK_MIPI_WRITEL(0x70, 0x1bd);
ARK_MIPI_WRITEL(0x6c, 0x239);
ARK_MIPI_WRITEL(0x70, 0xb1);
ARK_MIPI_WRITEL(0x6c, 0x239);
ARK_MIPI_WRITEL(0x70, 0xbd);
ARK_MIPI_WRITEL(0x6c, 0x239);
ARK_MIPI_WRITEL(0x70, 0xedc6);
ARK_MIPI_WRITEL(0x6c, 0x239);
ARK_MIPI_WRITEL(0x6c, 0x1105);
udelay(250000);
ARK_MIPI_WRITEL(0x6c, 0x2905);
udelay(120000);
ARK_MIPI_WRITEL(0x38, 0x3f02);
ARK_MIPI_WRITEL(0x34, 0x0);
}
#endif

View File

@ -0,0 +1,154 @@
/* ----------------------------------------------------------------------------
* SAM Software Package License
* ----------------------------------------------------------------------------
* Copyright (c) 2013, Atmel Corporation
*
* All rights reserved.
*
* 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 disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
* ----------------------------------------------------------------------------
*/
/** \file */
/**
* \addtogroup mmu MMU Initialization
*
* \section Usage
*
* Translation Lookaside Buffers (TLBs) are an implementation technique that caches translations or
* translation table entries. TLBs avoid the requirement for every memory access to perform a translation table
* lookup. The ARM architecture does not specify the exact form of the TLB structures for any design. In a
* similar way to the requirements for caches, the architecture only defines certain principles for TLBs:
*
* The MMU supports memory accesses based on memory sections or pages:
* Supersections Consist of 16MB blocks of memory. Support for Supersections is optional.
* -# Sections Consist of 1MB blocks of memory.
* -# Large pages Consist of 64KB blocks of memory.
* -# Small pages Consist of 4KB blocks of memory.
*
* Access to a memory region is controlled by the access permission bits and the domain field in the TLB entry.
* Memory region attributes
* Each TLB entry has an associated set of memory region attributes. These control accesses to the caches,
* how the write buffer is used, and if the memory region is Shareable and therefore must be kept coherent.
*
* Related files:\n
* \ref mmu.c\n
* \ref mmu.h \n
*/
/*------------------------------------------------------------------------------ */
/* Headers */
/*------------------------------------------------------------------------------ */
#include <chip.h>
/*------------------------------------------------------------------------------ */
/* Exported functions */
/*------------------------------------------------------------------------------ */
/**
* \brief Initializes MMU.
* \param pTB Address of the translation table.
*/
void MMU_Initialize(uint32_t *pTB)
{
unsigned int index;
unsigned int addr;
/* Reset table entries */
for (index = 0; index < 4096; index++)
pTB[index] = 0;
/* interrupt vector address (after remap) 0x0000_0000 */
pTB[0x000] = (0x200 << 20)| // Physical Address
// ( 1 << 12)| // TEX[0]
( 3 << 10)| // Access in supervisor mode (AP)
( 0xF << 5)| // Domain 0xF
( 1 << 4)| // (XN)
( 0 << 3)| // C bit : cachable => YES
( 0 << 2)| // B bit : write-back => YES
( 2 << 0); // Set as 1 Mbyte section
/* SRAM address (after remap) 0x0030_0000 */
pTB[0x003] = (0x003 << 20)| // Physical Address
// ( 1 << 12)| // TEX[0]
( 3 << 10)| // Access in supervisor mode (AP)
( 0xF << 5)| // Domain 0xF
( 1 << 4)| // (XN)
( 0 << 3)| // C bit : cachable => YES
( 0 << 2)| // B bit : write-back => YES
( 2 << 0); // Set as 1 Mbyte section
/* DDRAM address (after remap) 0x2000_0000 */
for(addr = 0x200; addr < 0x240; addr++)
pTB[addr] = (addr << 20)| // Physical Address
( 1 << 18)| // 16MB Supersection
( 3 << 10)| // Access in supervisor mode (AP)
( 1 << 12)| // TEX[0]
( 0 << 5)| // Domain 0x0, Supersection only support domain 0
( 0 << 4)| // (XN)
( 1 << 3)| // C bit : cachable => YES
( 1 << 2)| // B bit : write-back => YES
( 2 << 0); // Set as 1 Mbyte section
/* DDRAM non-cache address (after remap) 0x3000_0000 */
for(addr = 0x300; addr < 0x340; addr++)
pTB[addr] = ((addr - 0x100) << 20)| // Physical Address
( 1 << 18)| // 16MB Supersection
( 3 << 10)| // Access in supervisor mode (AP)
( 1 << 12)| // TEX[0]
( 0 << 5)| // Domain 0x0, Supersection only support domain 0
( 0 << 4)| // (XN)
( 0 << 3)| // C bit : cachable => YES
( 0 << 2)| // B bit : write-back => YES
( 2 << 0); // Set as 1 Mbyte section
// periph address 0x60000000 ~ 0x80000000
for(addr = 0x600; addr < 0x800; addr++)
pTB[addr] = (addr << 20)| // Physical Address
( 3 << 10)| // Access in supervisor mode (AP)
( 0xF << 5)| // Domain 0xF
( 1 << 4)| // (XN)
( 0 << 3)| // C bit : cachable => NO
( 0 << 2)| // B bit : write-back => NO
( 2 << 0); // Set as 1 Mbyte section
CP15_WriteTTB((unsigned int)pTB);
/* Program the domain access register */
CP15_WriteDomainAccessControl(0xC0000003); // only domain 0 & 15: access are not checked
}
void dma_inv_range(UINT32 ulStart, UINT32 ulEnd)
{
CP15_invalidate_dcache_for_dma (ulStart, ulEnd);
}
void dma_clean_range(UINT32 ulStart, UINT32 ulEnd)
{
CP15_clean_dcache_for_dma (ulStart, ulEnd);
}
// flush<73><68>clean and invalidate
void dma_flush_range(UINT32 ulStart, UINT32 ulEnd)
{
CP15_flush_dcache_for_dma (ulStart, ulEnd);
}

View File

@ -0,0 +1,338 @@
#include "FreeRTOS.h"
#include "board.h"
#include "chip.h"
#define PINCTL_REG_BASE REGS_SYSCTL_BASE
#define MAX_PINS_PER_GROUP 32
typedef struct {
short muxio;
short pinval;
int drive;
} xPin_t;
typedef struct {
int groupid;
uint32_t mux_reg;
uint32_t mux_offset;
uint32_t mux_mask;
uint32_t mux_val;
short pins_num;
xPin_t pins[MAX_PINS_PER_GROUP];
} xPinGroup_t;
/* typedef struct {
} xPinFunction_t; */
typedef struct {
int reg;
int offset;
int mask;
} xPinmap_t;
static xPinmap_t amt630h_pin_map[] = {
{0xc0, 0, 0x3},
{0xc0, 2, 0x3},
{0xc0, 4, 0x3},
{0xc0, 6, 0x3},
{0xc0, 8, 0x3},
{0xc0, 10, 0x3},
{0xc0, 12, 0x3},
{0xc0, 14, 0x3},
{0xc0, 16, 0x3},
{0xc0, 18, 0x3},
{0xc0, 20, 0x3},
{0xc0, 22, 0x3},
{0xc0, 24, 0x3},
{0xc0, 26, 0x3},
{0xc0, 28, 0x3},
{0xc0, 30, 0x3},
{0xc4, 0, 0x3},
{0xc4, 2, 0x3},
{0xc4, 4, 0x3},
{0xc4, 6, 0x3},
{0xc4, 8, 0x3},
{0xc4, 10, 0x3},
{0xc4, 12, 0x3},
{0xc4, 14, 0x3},
{0xc4, 16, 0x3},
{0xc4, 18, 0x3},
{0xc4, 20, 0x3},
{0xc4, 22, 0x3},
{0xc4, 24, 0x3},
{0xc4, 26, 0x3},
{0xc4, 28, 0x3},
{0xc4, 30, 0x3},
{0xc8, 0, 0x3},
{0xc8, 2, 0x3},
{0xc8, 4, 0x3},
{0xc8, 6, 0x3},
{0xc8, 8, 0x3},
{0xc8, 10, 0x3},
{0xc8, 12, 0x3},
{0xc8, 14, 0x3},
{0xc8, 16, 0x3},
{0xc8, 18, 0x3},
{0xc8, 20, 0x3},
{0xc8, 22, 0x3},
{0xc8, 24, 0x3},
{0xc8, 26, 0x3},
{0xc8, 28, 0x3},
{0xc8, 30, 0x3},
{0xcc, 0, 0x3},
{0xcc, 2, 0x3},
{0xcc, 4, 0x3},
{0xcc, 6, 0x3},
{0xcc, 8, 0x3},
{0xcc, 10, 0x3},
{0xcc, 12, 0x3},
{0xcc, 14, 0x3},
{0xcc, 16, 0x3},
{0xcc, 18, 0x3},
{0xcc, 20, 0x3},
{0xcc, 22, 0x3},
{0xcc, 24, 0x3},
{0xcc, 26, 0x3},
{0xcc, 28, 0x3},
{0xcc, 30, 0x3},
{0xd0, 0, 0x3},
{0xd0, 2, 0x3},
{0xd0, 4, 0x3},
{0xd0, 6, 0x3},
{0xd0, 8, 0x3},
{0xd0, 10, 0x3},
{0xd0, 12, 0x3},
{0xd0, 14, 0x3},
{0xd0, 16, 0x3},
{0xd0, 18, 0x3},
{0xd0, 20, 0x3},
{0xd0, 22, 0x3},
{0xd0, 24, 0x3},
{0xd0, 26, 0x3},
{0xd0, 28, 0x3},
{0xd0, 30, 0x3},
{0xd4, 0, 0x3},
{0xd4, 2, 0x3},
{0xd4, 4, 0x3},
{0xd4, 6, 0x3},
{0xd4, 8, 0x3},
{0xd4, 10, 0x3},
{0xd4, 12, 0x3},
{0xd4, 14, 0x3},
{0xd4, 16, 0x3},
{0xd4, 18, 0x3},
{0xd4, 20, 0x3},
{0xd4, 22, 0x3},
{0xd4, 24, 0x3},
{0xd4, 26, 0x3},
{0xd4, 28, 0x3},
{0xd4, 30, 0x3},
{0xd8, 4, 0x3},
{0xd8, 0, 0x3},
{0xd8, 6, 0x3},
{0xd8, 2, 0x3},
{0xd8, 8, 0x3},
{0xd8, 10, 0x3},
/* pad not mux with gpio */
};
#define PIN_NUM ARRAY_SIZE(amt630h_pin_map)
static xPinGroup_t pin_groups[] = {
/* scl sda */
{.groupid = PGRP_I2C0, .pins_num = 2, .pins = {{48, 1}, {49, 1}}},
/* scl sda */
{.groupid = PGRP_I2C1, .pins_num = 2, .pins = {{50, 1}, {51, 1}}},
{.groupid = PGRP_LCD_TTL_CH0, .pins_num = 28,
/* de clk vynsc hsync */
.pins = {{88, 1}, {89, 1, PAD_DRIVE_2MA}, {90, 0}, {91, 0},
{64, 1}, {65, 1}, {66, 1}, {67, 1}, {68, 1}, {69, 1}, {70, 1}, {71, 1}, /* B0-B7 */
{72, 1}, {73, 1}, {74, 1}, {75, 1}, {76, 1}, {77, 1}, {78, 1}, {79, 1}, /* G0-G7 */
{80, 1}, {81, 1}, {82, 1}, {83, 1}, {84, 1}, {85, 1}, {86, 1}, {87, 1},}}, /* R0-R7 */
{.groupid = PGRP_LCD_TTL_CH1, .pins_num = 28,
/* de clk vynsc hsync */
.pins = {{12, 1}, {13, 1}, {14, 1}, {15, 1},
{64, 1}, {65, 1}, {66, 1}, {67, 1}, {68, 1}, {69, 1}, {70, 1}, {71, 1}, /* r0-r7 */
{72, 1}, {73, 1}, {74, 1}, {75, 1}, {84, 3}, {85, 3}, {86, 3}, {87, 3}, /* g0-g7 */
{88, 3}, {89, 3}, {90, 3}, {91, 3}, {8, 1}, {9, 1}, {10, 1}, {11, 1},}}, /* b0-b7 */
{.groupid = PGRP_LCD_LVDS, .pins_num = 10,
/* dp dn clkp clkn cp cn bp bn */
.pins = {{64, 2}, {65, 2}, {66, 2}, {67, 2}, {68, 2}, {69, 2}, {70, 2}, {71, 2},
/* ap an */
{72, 2}, {73, 2}, }},
{.groupid = PGRP_LCD_SRGB, .pins_num = 8,
.pins = {{74, 1}, {75, 1}, {76, 1}, {77, 1}, {78, 1}, {79, 1}, {80, 1}, {81, 1},}}, /* d0-d7 */
/* 由于uart0有些平台rx没有接上拉电阻需要将rx脚配置成gpio防止收到随机数据导致异常 */
{.groupid = PGRP_UART0, .pins_num = 2, .pins = {{38, 1}, {39, 1}}},
/* rx tx */
{.groupid = PGRP_UART1, .pins_num = 2, .pins = {{40, 1}, {41, 1}}},
/* rx tx */
{.groupid = PGRP_UART2, .pins_num = 2, .pins = {{42, 1}, {43, 1}}},
/* rx tx */
{.groupid = PGRP_UART3, .pins_num = 2, .pins = {{44, 1}, {45, 1}}},
/* cs clk d0 d1 d2 d3 */
{.groupid = PGRP_SPI0, .pins_num = 5, .pins = {/*{32, 1},*/ {33, 1}, {34, 1}, {35, 1}, {36, 1}, {37, 1}}},
/* cs clk txd rxd */
{.groupid = PGRP_SPI1, .pins_num = 3, .pins = {/*{23, 1},*/ {24, 1}, {25, 1}, {26, 1}}},
{.groupid = PGRP_SDMMC0, .pins_num = 7, .pins = {{16, 1}, {17, 1}, {18, 1}, {19, 1},
{20, 1}, {21, 1}, {22, 1}}},
{.groupid = PGRP_PWM0, .pins_num = 1, .pins = {{0, 1}}},
{.groupid = PGRP_PWM1, .pins_num = 1, .pins = {{1, 1}}},
{.groupid = PGRP_PWM2, .pins_num = 1, .pins = {{6, 1}}},
{.groupid = PGRP_PWM3, .pins_num = 1, .pins = {{47, 1}}},//47
{.groupid = PGRP_PWM0_IN, .pins_num = 1, .pins = {{4, 0}},//pin18 GPIO4
.mux_reg = 0x60000120, .mux_offset = 12, .mux_mask = 0x3, .mux_val = 0},
{.groupid = PGRP_PWM1_IN, .pins_num = 1, .pins = {{5, 0}},//pin19 GPIO5
.mux_reg = 0x60000120, .mux_offset = 14, .mux_mask = 0x3, .mux_val = 0},
{.groupid = PGRP_PWM2_IN, .pins_num = 1, .pins = {{6, 0}},//pin47 GPIO6
.mux_reg = 0x60000120, .mux_offset = 28, .mux_mask = 0x3, .mux_val = 0},
{.groupid = PGRP_PWM3_IN, .pins_num = 1, .pins = {{7, 0}},//pin48 GPIO7
.mux_reg = 0x60000120, .mux_offset = 30, .mux_mask = 0x3, .mux_val = 0},
{.groupid = PGRP_ITU_CH0, .pins_num = 11,
.pins = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {96, 0}, {97, 0}, {98, 0}, {99, 0}, /* d0-d7 */
{100, 0}, {101, 0}, {0, 0}}, /* hs, vs, clk */
.mux_reg = 0x600000dc, .mux_offset = 16, .mux_mask = 0x7, .mux_val = 2},
{.groupid = PGRP_ITU_CH0_INV, .pins_num = 11,
.pins = {{1, 0}, {2, 0}, {3, 0}, {4, 0}, {96, 0}, {97, 0}, {98, 0}, {99, 0}, /* d0-d7 */
{100, 0}, {101, 0}, {0, 0}}, /* hs, vs, clk */
.mux_reg = 0x600000dc, .mux_offset = 16, .mux_mask = 0x7, .mux_val = 6},
{.groupid = PGRP_ITU_CH1, .pins_num = 11,
.pins = {{8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}, /* d0-d7 */
{59, 0}, {60, 0}, {61, 0}}, /* hs, vs, clk */
.mux_reg = 0x600000dc, .mux_offset = 16, .mux_mask = 0x7, .mux_val = 3},
{.groupid = PGRP_ITU_CH1_INV, .pins_num = 11,
.pins = {{8, 0}, {9, 0}, {10, 0}, {11, 0}, {12, 0}, {13, 0}, {14, 0}, {15, 0}, /* d0-d7 */
{59, 0}, {60, 0}, {61, 0}}, /* hs, vs, clk */
.mux_reg = 0x600000dc, .mux_offset = 16, .mux_mask = 0x7, .mux_val = 7},
/* tx rx */
{.groupid = PGRP_CAN0_CH0, .pins_num = 2, .pins = {{0, 2}, {1, 2}},
.mux_reg = 0x600000dc, .mux_offset = 19, .mux_mask = 0x3, .mux_val = 1},
{.groupid = PGRP_CAN0_CH1, .pins_num = 2, .pins = {{12, 2}, {13, 2}},
.mux_reg = 0x600000dc, .mux_offset = 19, .mux_mask = 0x3, .mux_val = 2},
{.groupid = PGRP_CAN0_CH2, .pins_num = 2, .pins = {{96, 2}, {97, 2}},
.mux_reg = 0x600000dc, .mux_offset = 19, .mux_mask = 0x3, .mux_val = 3},
{.groupid = PGRP_CAN1_CH0, .pins_num = 2, .pins = {{2, 2}, {3, 2}},
.mux_reg = 0x600000dc, .mux_offset = 21, .mux_mask = 0x3, .mux_val = 1},
{.groupid = PGRP_CAN1_CH1, .pins_num = 2, .pins = {{14, 2}, {15, 2}},
.mux_reg = 0x600000dc, .mux_offset = 21, .mux_mask = 0x3, .mux_val = 2},
{.groupid = PGRP_CAN1_CH2, .pins_num = 2, .pins = {{98, 2}, {99, 2}},
.mux_reg = 0x600000dc, .mux_offset = 21, .mux_mask = 0x3, .mux_val = 3},
{.groupid = PGRP_I2S0_PLAY, .pins_num = 4, .pins = {{52, 1, PAD_DRIVE_2MA}, {54, 1, PAD_DRIVE_2MA}, {55, 1, PAD_DRIVE_2MA}, {56, 1, PAD_DRIVE_2MA}}},
{.groupid = PGRP_I2S0_RECORD, .pins_num = 4, .pins = {{52, 1, PAD_DRIVE_2MA}, {53, 1, PAD_DRIVE_2MA}, {55, 1, PAD_DRIVE_2MA}, {56, 1, PAD_DRIVE_2MA}}},
{.groupid = PGRP_I2S1_PLAY, .pins_num = 4, .pins = {{23, 1, PAD_DRIVE_2MA}, {24, 1, PAD_DRIVE_2MA}, {25, 1, PAD_DRIVE_2MA}, {26, 1, PAD_DRIVE_2MA}},
//bit2: select i2s1 function(bit[2] 0:ssp1_interface; 1:i2s1_interface).
//bit3: i2s1 data pin output(bit[3] 0:input, 1:output).
.mux_reg = REGS_SYSCTL_BASE + SYS_PAD_CTRL08, .mux_offset = 2, .mux_mask = 0x3, .mux_val = 3},
{.groupid = PGRP_I2S1_RECORD, .pins_num = 4, .pins = {{23, 1, PAD_DRIVE_2MA}, {24, 1, PAD_DRIVE_2MA}, {25, 1, PAD_DRIVE_2MA}, {26, 1, PAD_DRIVE_2MA}},
//bit2: select i2s1 function(bit[2] 0:ssp1_interface; 1:i2s1_interface).
//bit3: i2s1 data pin input(bit[3] 0:input, 1:output).
.mux_reg = REGS_SYSCTL_BASE + SYS_PAD_CTRL08, .mux_offset = 2, .mux_mask = 0x3, .mux_val = 1},
{.groupid = PGRP_RCRT, .pins_num = 1, .pins = {{57, 1}}},
};
#define GROUP_NUM ARRAY_SIZE(pin_groups)
static __INLINE void pinctrl_set_pin(int npin, int val, int drive)
{
xPinmap_t *pctrl;
uint32_t reg;
if (npin >= PIN_NUM)
return;
pctrl = &amt630h_pin_map[npin];
reg = readl(PINCTL_REG_BASE + pctrl->reg);
reg &= ~(pctrl->mask << pctrl->offset);
reg |= val << pctrl->offset;
writel(reg, PINCTL_REG_BASE + pctrl->reg);
if (drive != PAD_DRIVE_DEFAULT) {
uint32_t drv_reg = SYS_IO_DRIVER00 + npin / 16 * 4;
uint32_t offset = (npin % 16) * 2;
uint32_t drv_val = drive - 1;
vSysctlConfigure(drv_reg, offset, 3, drv_val);
}
}
void pinctrl_gpio_request(int gpio)
{
pinctrl_set_pin(gpio, 0, PAD_DRIVE_DEFAULT);
}
void pinctrl_set_group(int groupid)
{
int i, j;
xPinGroup_t *pgrp;
uint32_t reg;
for (i = 0; i < GROUP_NUM; i++) {
pgrp = &pin_groups[i];
if (pgrp->groupid == groupid) {
configASSERT(pgrp->pins_num <= MAX_PINS_PER_GROUP);
for (j = 0; j < pgrp->pins_num; j++)
pinctrl_set_pin(pgrp->pins[j].muxio, pgrp->pins[j].pinval,
pgrp->pins[j].drive);
if (pgrp->mux_reg) {
reg = readl(pgrp->mux_reg);
reg &= ~(pgrp->mux_mask << pgrp->mux_offset);
reg |= pgrp->mux_val << pgrp->mux_offset;
writel(reg, pgrp->mux_reg);
}
break;
}
}
}
void vPinctrlSetup(void)
{
#ifdef HW_I2C0_SUPPORT
pinctrl_set_group(PGRP_I2C0);
#endif
#ifdef HW_I2C1_SUPPORT
pinctrl_set_group(PGRP_I2C1);
#endif
pinctrl_set_group(PGRP_UART0);
pinctrl_set_group(PGRP_UART2);
#ifdef PWM_CAP_SUPPORT
pinctrl_set_group(PGRP_PWM2_IN);
#endif
#if 0
/* 高速串口先初始化再配置pad脚(放在vUartInit()函数中初始化),否则在初始化过程中收到数据会导致串口出错 */
pinctrl_set_group(PGRP_UART1);
pinctrl_set_group(PGRP_UART3);
#endif
pinctrl_set_group(PGRP_SPI0);
pinctrl_set_group(PGRP_SPI1);
pinctrl_set_group(PGRP_SDMMC0);
#ifdef REMOTE_SUPPORT
pinctrl_set_group(PGRP_RCRT);
#endif
#if LCD_INTERFACE_TYPE == LCD_INTERFACE_TTL
pinctrl_set_group(PGRP_LCD_TTL_CH0);
#elif LCD_INTERFACE_TYPE == LCD_INTERFACE_LVDS
pinctrl_set_group(PGRP_LCD_LVDS);
#endif
pinctrl_set_group(PGRP_ITU_CH1_INV);
pinctrl_set_group(PGRP_CAN0_CH0);
#ifdef AUDIO_REPLAY
#if (AUDIO_REPLAY_I2S == I2S_ID1)
pinctrl_set_group(PGRP_I2S1_PLAY);
#else
pinctrl_set_group(PGRP_I2S0_PLAY);
#endif
#endif
#ifdef AUDIO_RECORD
#if (AUDIO_RECORD_I2S == I2S_ID1)
pinctrl_set_group(PGRP_I2S1_RECORD);
#else
pinctrl_set_group(PGRP_I2S0_RECORD);
#endif
#endif
pinctrl_set_group(PGRP_PWM2);
pinctrl_set_group(PGRP_PWM3);
}

View File

@ -0,0 +1,42 @@
#include "FreeRTOS.h"
#include "chip.h"
#define PWM_EN 0x0
#define PWM_DUTY 0x4
#define PWM_CNTR 0x8
#define PWM_REG(x) (REGS_PWM_BASE + 0x10 * (x))
int pwm_config(int id, uint32_t duty_ns, uint32_t period_ns)
{
uint32_t clk_mhz = ulClkGetRate(CLK_PWM) / 1000000;
uint32_t duty = (unsigned long long)duty_ns * clk_mhz / 1000;
uint32_t period = (unsigned long long)period_ns * clk_mhz / 1000;
//writel(0, PWM_REG(id) + PWM_EN);
writel(duty, PWM_REG(id) + PWM_DUTY);
writel(period, PWM_REG(id) + PWM_CNTR);
return 0;
}
void pwm_enable(int id)
{
writel(1, PWM_REG(id) + PWM_EN);
}
void pwm_disable(int id)
{
writel(0, PWM_REG(id) + PWM_EN);
}
//240719 lj
void pwn_update_brightness(uint32_t duty_ns)
{
int id=2;
int led_id=3;
uint32_t clk_mhz = ulClkGetRate(CLK_PWM) / 1000000;
uint32_t duty = (unsigned long long)duty_ns * clk_mhz / 1000;
writel(duty, PWM_REG(id) + PWM_DUTY);
writel(duty, PWM_REG(led_id) + PWM_DUTY);
}

View File

@ -0,0 +1,206 @@
#include "FreeRTOS.h"
#include "board.h"
#ifdef PWM_CAP_SUPPORT
#include "chip.h"
#include "pwm_cap.h"
#define PWM_CAP_INT_CLEAR (0x10*4)
#define PWM_CAP_INT_EN (0x20*4)
#define PWM_CAP_INT_STA (0x21*4)
#define PWM_CAP_SYS_FRQ (0x00)
#define PWM_CAP_SETTING (0x04)
#define PWM_CAP_CYCLE_CAP (0x08)
#define PWM_FRE_CAP (0x0c)
#define PWM_CAP_CLK 198000000
#define PWM_CAP_REG(x) (REGS_PWM_BASE + 0x100 + 0x10 * (x))
void pwm_cap_Int_Handler(void *para);
static void pwm_cap_clk_config(UINT8 id,UINT32 clk)
{
writel(clk,PWM_CAP_REG(id) + PWM_CAP_SYS_FRQ);
}
static void pwm_cap_en(UINT8 id,UINT8 enable)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
if(enable)
reg |= (1UL<<31);
else
reg &= ~(1UL<<31);
writel(reg, PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_int_method(UINT8 id,UINT8 int_method)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0x3<<28);
reg |= (int_method<<28);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_set_glitch(UINT8 id,UINT8 glitch)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0xF<<24);
reg |= (glitch<<24);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_method(UINT8 id,UINT8 cap_method)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0x1<<30);
reg |= (cap_method<<30);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_times(UINT8 id,UINT8 cat_times)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0xFF<<16);
reg |= (cat_times<<16);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_based_unit(UINT8 id,UINT8 cap_based_unit)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0x7<<12);
reg |= (cap_based_unit<<12);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
static void pwm_cap_interval(UINT8 id,UINT8 cap_interval)
{
unsigned int reg;
reg = readl(PWM_CAP_REG(id) + PWM_CAP_SETTING);
reg &= ~(0xFF<<0);
reg |= (cap_interval<<0);
writel(reg,PWM_CAP_REG(id) + PWM_CAP_SETTING);
}
void pwm_Initial_Cap(UINT8 id)
{
pwm_cap_clk_config(id,PWM_CAP_CLK);
pwm_cap_int_method(id,PWM_CAP_ONCE_FINISH_INT);
pwm_cap_set_glitch(id,PWM_CAP_GLITCH);
pwm_cap_method(id,PWM_CAP_NUM);
pwm_cap_times(id,PWM_CAP_TIMES);
pwm_cap_based_unit(id,PWM_CAP_UINT_100MS);
pwm_cap_interval(id,PWM_CAP_INTERVAL);
request_irq(RCRT_IRQn, 0, pwm_cap_Int_Handler, NULL);
}
void pwm_cap_Int_Handler(void *para)
{
unsigned int val;
unsigned int Regval;
val = readl(PWM_CAP_REG(0) + PWM_CAP_INT_STA);
printf( "capture interrupt is valid\r\n") ;
if(val&1)
{
Regval = readl(PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
Regval |= (1<<0);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
printf("capture 0 interrupt is valid\r\n");
pwm_getCapVal(PWM_CAP_CH0);
Regval &=~(1<<0);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
writel(readl(PWM_CAP_REG(PWM_CAP_CH0) + PWM_CAP_SETTING)|(1UL<<31),PWM_CAP_REG(PWM_CAP_CH0) +PWM_CAP_SETTING);
}
if(val&(1<<1))
{
Regval = readl(PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
Regval |= (1<<1);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
printf( " capture 1 interrupt is valid\r\n");
pwm_getCapVal(1);
Regval &= ~(1<<1);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
}
if(val&(1<<2))
{
Regval = readl(PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
Regval |= (1<<2);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
printf( " capture 2 interrupt is valid\r\n");
pwm_getCapVal(2);
Regval &= ~(1<<2);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
}
if(val&(1<<3))
{
printf( " capture 3 interrupt is valid\r\n");
Regval = readl(PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
Regval |= (1<<3);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
pwm_getCapVal(3);
Regval &=~ (1<<3);
writel(Regval,PWM_CAP_REG(0) +PWM_CAP_INT_CLEAR);
}
}
double pwm_getCapVal(UINT8 id)
{
UINT32 reg,num;
double fre;
reg=readl(PWM_CAP_REG(id)+PWM_CAP_CYCLE_CAP);
writel(readl(PWM_CAP_REG(id)+PWM_CAP_SETTING)|(1UL<<31),PWM_CAP_REG(id)+PWM_CAP_SETTING);
num = reg>>8;
fre = ((reg&0xf0)>>4)/16.0+(reg&0xf)/256.0;
fre += num;
reg = readl(PWM_CAP_REG(id)+PWM_CAP_SETTING);
if(((reg >>12)&0x7)==4)
fre*=1;
else if(((reg >>12)&0x7)==2)
fre*=10;
else if(((reg >>12)&0x7)==1)
fre*=100;
else
fre*=1000;
printf("pwm cap value %lf\n",(fre+0.9));
return (fre + 0.9);
}
void pwm_enableCapIRQ(UINT8 id,unsigned char en)
{
unsigned int reg = 0;
reg = readl(PWM_CAP_REG(0)+ PWM_CAP_INT_EN);
reg &=~(1<<id);
if(en)
{
reg |=(1<<id);
writel(reg,PWM_CAP_REG(0)+ PWM_CAP_INT_EN);
}
else
writel(reg,PWM_CAP_REG(0)+ PWM_CAP_INT_EN);
}
void pwm_cap_init(UINT8 id)
{
// unsigned int irq_enable = 1;
pwm_Initial_Cap(id);
pwm_enableCapIRQ(id,1);
pwm_cap_en(id,PWM_CAP_ENABLE);
}
#endif

View File

@ -0,0 +1,267 @@
#include "FreeRTOS.h"
#include "board.h"
#include "chip.h"
#define PXP_CTRL 0x000
#define PXP_CTRL_SET 0x004
#define PXP_CTRL_CLR 0x008
#define PXP_CTRL_TOG 0x00C
#define PXP_STAT 0x010
#define PXP_STAT_SET 0x014
#define PXP_STAT_CLR 0x018
#define PXP_STAT_TOG 0x01c
#define PXP_OUTBUF 0x020
#define PXP_OUTBUF2 0x030
#define PXP_OUTSIZE 0x040
#define PXP_S0BUF 0x050
#define PXP_S0UBUF 0x060
#define PXP_S0VBUF 0x070
#define PXP_S0PARAM 0x080
#define PXP_S0BACKGROUND 0x090
#define PXP_S0CLIP 0x0A0
#define PXP_S0SCALE 0x0B0
#define PXP_S0OFFSET 0x0C0
#define PXP_CSCCOEFF0 0x0D0
#define PXP_CSCCOEFF1 0x0E0
#define PXP_CSCCOEFF2 0x0F0
#define PXP_NEXT 0x100
#define PXP_PAGETABLE 0x170
#define PXP_S0CKEYL 0x180
#define PXP_S0CKEYH 0x190
#define PXP_OLCKEYL 0x1A0
#define PXP_OLCKEYH 0x1B0
#define PXP_OL0BUF 0x200
#define PXP_OL0SIZE 0x210
#define PXP_OL0PARAM 0x220
#define PXP_OL1BUF 0x240
#define PXP_OL1SIZE 0x250
#define PXP_OL1PARAM 0x260
#define PXP_OL2BUF 0x280
#define PXP_OL2SIZE 0x290
#define PXP_OL2PARAM 0x2A0
#define PXP_OL3BUF 0x2C0
#define PXP_OL3SIZE 0x2D0
#define PXP_OL3PARAM 0x2E0
#define PXP_OL4BUF 0x300
#define PXP_OL4SIZE 0x310
#define PXP_OL4PARAM 0x320
#define PXP_OL5BUF 0x340
#define PXP_OL5SIZE 0x350
#define PXP_OL5PARAM 0x360
#define PXP_OL6BUF 0x380
#define PXP_OL6SIZE 0x390
#define PXP_OL6PARAM 0x3A0
#define PXP_OL7BUF 0x3C0
#define PXP_OL7SIZE 0x3D0
#define PXP_OL7PARAM 0x3E0
// offss for "NEXT" pointers
#define PXP_NEXTCTRL 0x000
#define PXP_NEXTRGBBUF 0x004
#define PXP_NEXTRGBBUF2 0x008
#define PXP_NEXTRGBSIZE 0x00C
#define PXP_NEXTS0BUF 0x010
#define PXP_NEXTS0UBUF 0x014
#define PXP_NEXTS0VBUF 0x018
#define PXP_NEXTS0PARAM 0x01C
#define PXP_NEXTS0BACKGROUND 0x020
#define PXP_NEXTS0CLIP 0x024
#define PXP_NEXTS0SCALE 0x028
#define PXP_NEXTS0OFFSET 0x02C
#define PXP_NEXTS0CKEYL 0x030
#define PXP_NEXTS0CKEYH 0x034
#define PXP_NEXTOLCKEYL 0x038
#define PXP_NEXTOLCKEYH 0x03C
#define PXP_NEXTOL0BUF 0x040
#define PXP_NEXTOL0SIZE 0x044
#define PXP_NEXTOL0PARAM 0x048
#define PXP_NEXTOL1BUF 0x050
#define PXP_NEXTOL1SIZE 0x054
#define PXP_NEXTOL1PARAM 0x058
#define PXP_NEXTOL2BUF 0x060
#define PXP_NEXTOL2SIZE 0x064
#define PXP_NEXTOL2PARAM 0x068
#define PXP_NEXTOL3BUF 0x070
#define PXP_NEXTOL3SIZE 0x074
#define PXP_NEXTOL3PARAM 0x078
#define PXP_NEXTOL4BUF 0x080
#define PXP_NEXTOL4SIZE 0x084
#define PXP_NEXTOL4PARAM 0x088
#define PXP_NEXTOL5BUF 0x090
#define PXP_NEXTOL5SIZE 0x094
#define PXP_NEXTOL5PARAM 0x098
#define PXP_NEXTOL6BUF 0x0A0
#define PXP_NEXTOL6SIZE 0x0A4
#define PXP_NEXTOL6PARAM 0x0A8
#define PXP_NEXTOL7BUF 0x0B0
#define PXP_NEXTOL7SIZE 0x0B4
#define PXP_NEXTOL7PARAM 0x0B8
#define abs(a, b) ((a > b) ? (a - b) : (b - a))
#define PXP_H_FILP 0
#define PXP_V_FILP 0
static uint32_t pxpbase = REGS_PXP_BASE;
static SemaphoreHandle_t pxp_mutex = NULL;
static QueueHandle_t pxp_done;
/* this function can not used in isr */
int pxp_scaler_rotate(uint32_t s0buf, uint32_t s0ubuf, uint32_t s0vbuf,
int s0format, uint32_t s0width, uint32_t s0height,
uint32_t outbuf, uint32_t outbuf2, int outformat,
uint32_t outwidth, uint32_t outheight, int outangle)
{
uint32_t ctrl;
int ret = 0;
int cutx = 2;
int cuty = 2;
int mirror = 0;
#if PXP_H_FILP
mirror = 1;
#endif
#if PXP_V_FILP
mirror |= 2;
#endif
configASSERT(outangle >= PXP_ROTATE_0 && outangle <= PXP_ROTATE_270);
if (outangle == PXP_ROTATE_90 || outangle == PXP_ROTATE_270) {
uint32_t tmp = outheight;
outheight = outwidth;
outwidth = tmp;
}
if(abs(s0width, outwidth) < 16)
cutx = 0;
if(abs(s0height, outheight) < 16)
cuty = 0;
xSemaphoreTake(pxp_mutex, portMAX_DELAY);
writel(1UL << 31, pxpbase + PXP_CTRL);
udelay(10);
writel(0, pxpbase + PXP_CTRL);
writel(outbuf, pxpbase + PXP_OUTBUF);
writel(outbuf2, pxpbase + PXP_OUTBUF2);
writel((0xffUL << 24) | (outwidth << 12) | outheight, pxpbase + PXP_OUTSIZE);
writel(s0buf, pxpbase + PXP_S0BUF);
writel(s0ubuf, pxpbase + PXP_S0UBUF);
writel(s0vbuf, pxpbase + PXP_S0VBUF);
writel((((s0width >> 3) & 0xff) << 8) | ((s0height >> 3) & 0xff), pxpbase + PXP_S0PARAM);
writel(0, pxpbase + PXP_S0BACKGROUND);
writel((((outwidth >> 3) & 0xff) << 8) | ((outheight >> 3) & 0xff), pxpbase + PXP_S0CLIP);
writel(((s0height * 0x1000 / (outheight + cuty)) << 16) | (s0width * 0x1000 / (outwidth + cutx)),
pxpbase + PXP_S0SCALE);
//YCbCr->RGB Coefficient Values bit31 ycbcr_mode
writel((1 << 31) | 0x1f0 | (0x180 << 9) | (0x12a << 18), pxpbase + PXP_CSCCOEFF0);
writel(0x204 | (0x198 <<16), pxpbase + PXP_CSCCOEFF1);
writel(0x79c | (0x730 <<16), pxpbase + PXP_CSCCOEFF2);
ctrl = (1 << 19) | (1 << 18) | ((s0format & 0xf) << 12) | (outangle << 8) |
(mirror << 10) | ((outformat & 0xf) << 4) | 3;
if(outformat == PXP_OUT_FMT_ARGB8888)
ctrl |=(1<<22);
xQueueReset(pxp_done);
writel(ctrl, pxpbase + PXP_CTRL);
if (xQueueReceive(pxp_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
printf("pxp timeout.\n");
ret = -ETIMEDOUT;
}
xSemaphoreGive(pxp_mutex);
return ret;
}
int pxp_rotate(uint32_t s0buf, uint32_t s0ubuf, uint32_t s0vbuf,
int s0format, uint32_t s0width, uint32_t s0height,
uint32_t outbuf, uint32_t outbuf2, int outformat,
int outangle)
{
uint32_t ctrl = 0;
int ret = 0;
int mirror = 0;
#if PXP_H_FILP
mirror = 1;
#endif
#if PXP_V_FILP
mirror |= 2;
#endif
configASSERT(outangle >= PXP_ROTATE_0 && outangle <= PXP_ROTATE_270);
xSemaphoreTake(pxp_mutex, portMAX_DELAY);
writel(1UL << 31, pxpbase + PXP_CTRL);
udelay(10);
writel(0, pxpbase + PXP_CTRL);
writel(outbuf, pxpbase + PXP_OUTBUF);
writel(outbuf2, pxpbase + PXP_OUTBUF2);
writel((0xffUL << 24) | (s0width << 12) | s0height, pxpbase + PXP_OUTSIZE);
writel(s0buf, pxpbase + PXP_S0BUF);
writel(s0ubuf, pxpbase + PXP_S0UBUF);
writel(s0vbuf, pxpbase + PXP_S0VBUF);
writel((((s0width >> 3) & 0xff) << 8) | ((s0height >> 3) & 0xff), pxpbase + PXP_S0PARAM);
writel(0, pxpbase + PXP_S0BACKGROUND);
//YCbCr->RGB Coefficient Values bit31 ycbcr_mode
writel((1 << 31) | 0x1f0 | (0x180 << 9) | (0x12a << 18), pxpbase + PXP_CSCCOEFF0);
writel(0x204 | (0x198 << 16), pxpbase + PXP_CSCCOEFF1);
writel(0x79c | (0x730 << 16), pxpbase + PXP_CSCCOEFF2);
if(outformat == PXP_OUT_FMT_ARGB8888) {
if(s0format == PXP_SRC_FMT_ARGB8888) {
writel(0, pxpbase + PXP_OLCKEYL);
writel(0, pxpbase + PXP_OLCKEYH);
writel(s0buf, pxpbase + PXP_OL0BUF);
writel((((s0width >> 3) & 0xff) << 8) | ((s0height >> 3) & 0xff), pxpbase + PXP_OL0SIZE);
writel((0x1<<0) | (0x3<<1) | (0x1<<3) | (s0format<<4) | (0x2<<16), pxpbase + PXP_OL0PARAM);
ctrl |= (1<<19);
s0format = PXP_SRC_FMT_RGB888;
} else {
ctrl |= (1<<22);
}
}
ctrl |= ((s0format & 0xf) << 12) | (outangle << 8) | (mirror<< 10) | ((outformat & 0xf) << 4) | 3;
xQueueReset(pxp_done);
writel(ctrl, pxpbase + PXP_CTRL);
if (xQueueReceive(pxp_done, NULL, pdMS_TO_TICKS(1000)) != pdTRUE) {
printf("pxp timeout.\n");
ret = -ETIMEDOUT;
}
xSemaphoreGive(pxp_mutex);
return ret;
}
static void pxp_interupt_handler(void *param)
{
uint32_t status = readl(pxpbase + PXP_STAT);
writel(status, pxpbase + PXP_STAT_CLR);
if (status & 0x01) {
xQueueSendFromISR(pxp_done, NULL, 0);
}
}
int pxp_init(void)
{
pxp_mutex = xSemaphoreCreateMutex();
pxp_done = xQueueCreate(1, 0);
request_irq(PXP_IRQn, 0, pxp_interupt_handler, NULL);
return 0;
}

View File

@ -0,0 +1,220 @@
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#ifdef REMOTE_SUPPORT
/* Remote */
#define rRC_DATA0 0x00
#define rRC_DATA1 0x04
#define rRC_DATA2 0x08
#define rRC_DATA3 0x0C
#define rRC_DATA4 0x10
#define rRC_DATA5 0x14
#define rRC_DATA6 0x18
#define rRC_DATA7 0x1C
#define rRC_CODEBUF 0x20
#define rRC_CODEVAL 0x24
#define rRC_KEYVAL 0x28
#define rRC_STATUS 0x2C
#define rRC_RT_USER_CODE_3_4 0x30
#define rRC_RT_INTER_REQ_CLR 0x34
#define NORMAL_KEY 0
#define RELEASE_KEY 1
#define REPEAT_KEY 2
#define REMOTE_NULL 0
#define REMOTE_SCAN 1 // scan state
#define REMOTE_CHECK 2 // check state
#define REMOTE_DELAY 3 // repeat delay state
#define REMOTE_REPEAT 4 // repeat state
#define REMOTE_STATE_IDLE 0
#define REMOTE_STATE_PRESS 1
#define REMOTE_STATE_REPEATE 2
#define REMOTE_PRESS_EVENT 0
#define REMOTE_RELEASE_EVENT 1
#define REMOTE_REPEATE_EVENT 2
//static UINT32 lg_ulRemoteStateMachine = REMOTE_STATE_IDLE;
//static unsigned short Remotekey_delay_time;
//static int Remotekey_state;
//static UINT32 lg_ulLastRepeatMs = 0;
static UINT32 lg_ulMaxRepeatMs = 0;
volatile UINT32 cRemoteKey; // <20><>ǰ<EFBFBD><C7B0>ֵ
volatile UINT32 cRemoteStatus; // <20><>ǰ<EFBFBD><C7B0>״̬
static void remote_config(void) //<2F><><EFBFBD><EFBFBD>ң<EFBFBD>ؽ<EFBFBD><D8BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>IJ<EFBFBD><C4B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
{
printf("remote_config\n");
#if 1
// nec
INT8 pulse_data_polarity = 0x01;//[3 :0 ]
INT8 valid_bitrange = 0x04;//[6 :4 ]
INT8 dispersion = 0x5;//[15:8 ]
INT8 prediv = 0x3f;//[23:16] 24M: 0x1f 48M: 0x3f
INT8 filtertime = 0x10;//[31:24]
INT8 start_valueh = 0x69;//[23:16]
INT8 start_valuel = 0x34;//[31:24]
INT8 one_valueh = 0x6; //0x6;//[7 : 0]
INT8 one_valuel = 0x13;//[15: 8]
INT8 zero_valueh = 0x6;//[23:16]
INT8 zero_valuel = 0x6;//[31:24]
INT8 rp0_valueh = 0x0f;//[7 : 0]
INT8 rp0_valuel = 0x0f;//[15: 8]
INT8 rp1_valueh = 0x0f;//[23:16]
INT8 rp1_valuel = 0x0f;//[31:24]
INT8 rp2_3_valueh = 0x69;//[7 : 0]
INT8 rp2_3_valuel = 0x1c;//[15: 8]
INT8 rp4_5_valueh =0x6;//[23:16]
INT8 rp4_5_valuel = 0xf5;//[31:24]
INT8 keyrelease_timeh = 0x00;//[7 : 0]
INT8 keyrelease_timel = 0x59;//[15: 8]
INT8 RP_5L_delta=0XF0;
INT8 int_num = 0x01;//[23:16]
INT8 mode_sel_reg = 0x03;//[25:24]
INT8 nec_release_int_en = 0x01;//[26]
UINT8 user_code1_l = 0x00;//[7 : 0]
UINT8 user_code1_h = 0xFF;//[15: 8]
UINT8 user_code2_l = 0x00;//[23:16]
UINT8 user_code2_h =0xFF;//[31:24]
INT32 NEC_bit_2_pulse = 0x2ff;//[15: 0]
// INT8 user_code_sel_reg = 0x01;//[23:16]
// INT8 user_code3_l = 0x02;//[7 : 0]
// INT8 user_code3_h = 0x00;//[15: 8]
// INT8 user_code4_l = 0xff;//[23:16]
// INT8 user_code4_h =0x00;//[31:24]
INT8 user_code_sel =0;
INT8 custom_jud_sel=1;
INT8 custom_not_jud_sel=1;
INT8 data_not_jud_sel=1;
uint32_t val;
uint32_t remote_param0, remote_param1, remote_param2, remote_param3 ;
uint32_t remote_param4, remote_param5, remote_param6, remote_param7 ;
#endif
remote_param0 = (pulse_data_polarity<<0)
+ (valid_bitrange<<4)
+ (dispersion<<7)
+ (prediv<<15)
+ (filtertime<<24);
remote_param1 = (RP_5L_delta<< 0)
+ (start_valueh<<16)
+ (start_valuel<<24);
remote_param2 = (one_valueh<<0)
+ (one_valuel<<8)
+ (zero_valueh<<16)
+ (zero_valuel<<24);
remote_param3 = (rp0_valueh<<0)
+ (rp0_valuel<<8)
+ (rp1_valueh<<16)
+ (rp1_valuel<<24);
remote_param4 = (rp2_3_valueh<<0)
+ (rp2_3_valuel<<8)
+ (rp4_5_valueh<<16)
+ (rp4_5_valuel<<24);
remote_param5 = (keyrelease_timel<<0)
+ (keyrelease_timeh<<8)
+ (int_num<<16)
+ (mode_sel_reg<<24)
+ (nec_release_int_en << 26);
remote_param6 = ((user_code1_l<<0)
+ (user_code1_h<<8)
+ (user_code2_l<<16)
+ (user_code2_h<<24));
/* rRC_RT_USER_CODE_3_4 = (user_code3_l<<0)
+ (user_code3_h<<8)
+ (user_code4_l<<16)
+ (user_code4_h<<24);
*/
remote_param7 =(NEC_bit_2_pulse<<0)
+ (user_code_sel<<16)
+(custom_jud_sel<<24)
+(custom_not_jud_sel<<25)
+(data_not_jud_sel<<26);
val = readl(REGS_SYSCTL_BASE + SYS_PER_CLK_CFG);
val &= ~0x0f;
writel(val, REGS_SYSCTL_BASE + SYS_PER_CLK_CFG);
writel(remote_param0, REGS_RCRT_BASE+rRC_DATA0);
writel(remote_param1, REGS_RCRT_BASE+rRC_DATA1);
writel(remote_param2, REGS_RCRT_BASE+rRC_DATA2);
writel(remote_param3, REGS_RCRT_BASE+rRC_DATA3);
writel(remote_param4, REGS_RCRT_BASE+rRC_DATA4);
writel(remote_param5, REGS_RCRT_BASE+rRC_DATA5);
writel(remote_param6, REGS_RCRT_BASE+rRC_DATA6);
writel(remote_param7, REGS_RCRT_BASE+rRC_DATA7);
}
/*********************************************************************
Set the report interval for remote repeating event
Parameter:
ulMillisecond: interval, unit as millisecond, this parameter should be the multiply of
10 millsecond.
*********************************************************************/
void SetRemoteKeyRepeateInterval(UINT32 ulMillisecond)
{
lg_ulMaxRepeatMs = ulMillisecond/10;
}
/*********************************************************************
Get the report interval for remote repeating event
Return:
millisendonds for interval
*********************************************************************/
UINT32 GetRemoteKeyRepeatInterval(void)
{
return lg_ulMaxRepeatMs * 10;
}
static void remote_int_handler(void *param)
{
// printf("Enter remote interrupt!\n");
cRemoteStatus=(readl(REGS_RCRT_BASE + rRC_STATUS))&0x0F;// ȡң<C8A1><D2A3><EFBFBD><EFBFBD>״̬
cRemoteKey=readl(REGS_RCRT_BASE + rRC_KEYVAL);
printf(" cRemoteKey is%x.\n",cRemoteKey);
writel(0xff, REGS_RCRT_BASE+rRC_RT_INTER_REQ_CLR);
if(cRemoteStatus & 0x01)
printf(" release detect\n");
else
printf(" cRemoteStatus: %x cRemoteKey: %x\n",cRemoteStatus,cRemoteKey);
}
void RemoteKeyInit(void)
{
cRemoteKey = REMOTE_NULL;
cRemoteStatus = NORMAL_KEY;
sys_soft_reset(softreset_rcrt);
remote_config();
request_irq(RCRT_IRQn,0,remote_int_handler,NULL);
printf("RemoteKeyInit\n");
}
#endif

View File

@ -0,0 +1,484 @@
#include <stdio.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "errno.h"
/* RTC registers */
#define RTC_CTL 0x00 /*control register*/
#define RTC_ANAWEN 0x04 /*analog block write enable register*/
#define RTC_ANACTL 0x08 /*analog block control register*/
#define RTC_IM 0x0c /*interrupt mode register*/
#define RTC_STA 0x10 /*rtc status register*/
#define RTC_ALMDAT 0x14 /*alarm data register*/
#define RTC_DONT 0x18 /*delay on timer register*/
#define RTC_RAM 0x1c /*ram bit register*/
#define RTC_CNTL 0x20 /*rtc counter register*/
#define RTC_CNTH 0x24 /*rtc sec counter register*/
//RTC_CTL register fields defination
#define CTL_CTL3_VALUE(x) (x<<23)
#define CTL_CTL2_VALUE(x) (x<<22)
#define CTL_BIAS_TRM_VALUE(x) (x<<21)
#define CTL_SOFT_SEL_VALUE(x) (x<<20)
#define CTL_CTL1_VALUE(x) (x<<19)
#define CTL_CTL0_VALUE(x) (x<<18)
#define CTL_SOFT_STR_VALUE(x) (x<<17)
#define CTL_OSC_EN_VALUE(x) (x<<16)
#define CTL_CTL3_SET (1<<15)
#define CTL_CTL2_SET (1<<14)
#define CTL_BIAS_TRM_SET (1<<13)
#define CTL_SOFT_SEL_SET (1<<12)
#define CTL_CTL1_SET (1<<11)
#define CTL_CTL0_SET (1<<10)
#define CTL_SOFT_STR_SET (1<<9)
#define CTL_OSC_EN_SET (1<<8)
#define CTL_ALM_DATA_WEN (1<<3)
#define CTL_PERIOD_INT_EN (1<<2)
#define CTL_ALARM_INT_EN (1<<1)
#define CTL_RESET (1<<0)
//RTC_ANAWEN register fields defination
#define ANA_CNT_WEN (1<<7)
#define ANA_RAM_WEN (1<<6)
#define ANA_DELAY_TIMER_WEN (1<<5)
#define ANA_CLR_PWR_DET_WEN (1<<4)
#define ANA_DELAY_POWER_ON_WEN (1<<3)
#define ANA_FORCE_POWER_OFF_WEN (1<<2)
#define ANA_FORCE_POWER_ON_WEN (1<<1)
#define ANA_RTC_WEN (1<<0)
//RTC_ANACTL register fields defination
#define ANACTL_CLR_PWR (1<<4)
#define ANACTL_DELAY_POWER_ON (1<<3)
#define ANACTL_FORCE_POWER_OFF (1<<2)
#define ANACTL_FORCE_POWER_ON (1<<1)
#define ANACTL_COUNTER_EN (1<<0)
/* STATUS_REG */
#define STA_PWR_DET (1<<6)
#define STA_DELAY_ON (1<<5)
#define STA_FORCE_OFF (1<<4)
#define STA_FORCE_ON (1<<3)
#define STA_RCT_BUSY (1<<2)
#define STA_PERIOD_INT (1<<1)
#define STA_ALARM_INT (1<<0)
/* 2020-01-01 Wednesday */
static struct rtc_time default_tm = {
.tm_sec = 0,
.tm_min = 0,
.tm_hour = 0,
.tm_mday = 1,
.tm_mon = 0,
.tm_year = 120,
.tm_wday = 3,
.tm_yday = 1,
};
static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const unsigned short rtc_ydays[2][13] = {
/* Normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
/*
* The number of days in the month.
*/
int rtc_month_days(unsigned int month, unsigned int year)
{
return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
}
/*
* The number of days since January 1. (0 to 365)
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
return rtc_ydays[is_leap_year(year)][month] + day-1;
}
/*
* rtc_time_to_tm - Converts time to rtc_time.
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
void rtc_time_to_tm(uint32_t time, struct rtc_time *tm)
{
unsigned int month, year;
unsigned long secs;
int days;
/* time must be positive */
days = time / 86400;
secs = time - (unsigned int) days * 86400;
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
year = 1970 + days / 365;
days -= (year - 1970) * 365
+ LEAPS_THRU_END_OF(year - 1)
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
days += 365 + is_leap_year(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
for (month = 0; month < 11; month++) {
int newdays;
newdays = days - rtc_month_days(month, year);
if (newdays < 0)
break;
days = newdays;
}
tm->tm_mon = month;
tm->tm_mday = days + 1;
tm->tm_hour = secs / 3600;
secs -= tm->tm_hour * 3600;
tm->tm_min = secs / 60;
tm->tm_sec = secs - tm->tm_min * 60;
}
/*
* Does the rtc_time represent a valid date/time?
*/
int rtc_valid_tm(struct rtc_time *tm)
{
if (tm->tm_year < 70
|| ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
|| ((unsigned)tm->tm_hour) >= 24
|| ((unsigned)tm->tm_min) >= 60
|| ((unsigned)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
}
/*
* mktime - Converts date to seconds.
* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* A leap second can be indicated by calling this function with sec as
* 60 (allowable under ISO 8601). The leap second is treated the same
* as the following second since they don't exist in UNIX time.
*
* An encoding of midnight at the end of the day as 24:00:00 - ie. midnight
* tomorrow - (allowable under ISO 8601) is supported.
*/
uint32_t mktime(const unsigned int year0, const unsigned int mon0,
const unsigned int day, const unsigned int hour,
const unsigned int min, const unsigned int sec)
{
unsigned int mon = mon0, year = year0;
/* 1..12 -> 11,12,1..10 */
if (0 >= (int) (mon -= 2)) {
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((uint32_t)
(year/4 - year/100 + year/400 + 367*mon/12 + day) +
year*365 - 719499
)*24 + hour /* now have hours - midnight tomorrow handled here */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
/*
* rtc_tm_to_time - Converts rtc_time to time.
* Convert Gregorian date to seconds since 01-01-1970 00:00:00.
*/
uint32_t rtc_tm_to_time(struct rtc_time *tm)
{
return mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
static __INLINE void rtc_clear_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_STA);
val &= ~CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_STA);
}
static __INLINE void rtc_enable_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_CTL);
if (!(val & CTL_ALARM_INT_EN)) {
rtc_clear_interrupt();
val |= CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_CTL);
}
}
static __INLINE void rtc_disable_interrupt(void)
{
unsigned int val;
val = readl(REGS_RTC_BASE + RTC_CTL);
if (val & CTL_ALARM_INT_EN) {
val &= ~CTL_ALARM_INT_EN;
writel(val, REGS_RTC_BASE + RTC_CTL);
}
}
static void rtc_wait_not_busy(void)
{
int status, count = 0;
/* Assuming BUSY may stay active for 80 msec) */
for (count = 0; count < 0x1000; count++) {
status = readl(REGS_RTC_BASE + RTC_STA);
if ((status & STA_RCT_BUSY) == 0)
break;
/* check status busy, after each msec */
vTaskDelay(pdMS_TO_TICKS(1));
}
}
static void rtc_isr(void *para)
{
unsigned int irq_data;
irq_data = readl(REGS_RTC_BASE + RTC_STA);
if ((irq_data & CTL_ALARM_INT_EN)) {
rtc_clear_interrupt();
return;
} else
return;
}
static void rtc_update_time(unsigned int time)
{
unsigned int val;
int timeout = 100000;
val = readl(REGS_RTC_BASE + RTC_ANAWEN);
writel(val | ANA_RTC_WEN, REGS_RTC_BASE + RTC_ANAWEN);
val = readl(REGS_RTC_BASE + RTC_ANACTL);
writel(val | ANACTL_COUNTER_EN, REGS_RTC_BASE + RTC_ANACTL);
//wait rtc_busy;
rtc_wait_not_busy();
val = readl(REGS_RTC_BASE + RTC_ANAWEN);
writel(val | ANA_CNT_WEN, REGS_RTC_BASE + RTC_ANAWEN);
writel(time, REGS_RTC_BASE + RTC_CNTH);
//wait rtc_busy;
rtc_wait_not_busy();
while(readl(REGS_RTC_BASE + RTC_CNTH) != time) {
if (timeout-- == 0)
break;
taskYIELD();
}
}
/*
* rtc_read_time - set the time
* @tm: holds date and time
*
* This function read time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_read_time(struct rtc_time *tm)
{
unsigned int time;
/* we don't report wday/yday/isdst ... */
rtc_wait_not_busy();
time = readl(REGS_RTC_BASE + RTC_CNTH);
rtc_time_to_tm(time, tm);
return 0;
}
/*
* rtc_set_time - set the time
* @tm: holds date and time
*
* This function set time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_set_time(struct rtc_time *tm)
{
long unsigned int time;
if (rtc_valid_tm(tm) < 0)
return -EINVAL;
/* convert tm to seconds. */
time = rtc_tm_to_time(tm);
rtc_update_time(time);
return 0;
}
#if 0
static void rtc_update_alarm_time(unsigned int time)
{
unsigned int val;
int timeout = 100000;
val = readl(REGS_RTC_BASE + RTC_CTL);
writel(val | CTL_ALM_DATA_WEN, REGS_RTC_BASE + RTC_CTL);
writel(time, REGS_RTC_BASE + RTC_ALMDAT);
//wait rtc_busy;
rtc_wait_not_busy();
while(readl(REGS_RTC_BASE + RTC_ALMDAT) != time) {
if (timeout-- == 0)
break;
taskYIELD();
}
}
/*
* rtc_read_alarm - read the alarm time
* @alm: holds alarm date and time
*
* This function read alarm time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_read_alarm(struct rtc_wkalrm *alm)
{
unsigned int time;
rtc_wait_not_busy();
time = readl(REGS_RTC_BASE + RTC_ALMDAT);
rtc_time_to_tm(time, &alm->time);
alm->enabled = readl(REGS_RTC_BASE + RTC_CTL) & CTL_ALARM_INT_EN;
return 0;
}
/*
* rtc_set_alarm - set the alarm time
* @alm: holds alarm date and time
*
* This function set alarm time and date. On success it will return 0
* otherwise -ve error is returned.
*/
static int rtc_set_alarm(struct rtc_wkalrm *alm)
{
long unsigned int time;
if (rtc_valid_tm(&alm->time) < 0)
return -EINVAL;
/* convert tm to seconds. */
time = rtc_tm_to_time(&alm->time);
rtc_update_alarm_time(time);
if (alm->enabled)
rtc_enable_interrupt();
else
rtc_disable_interrupt();
return 0;
}
#endif
static int alarm_irq_enable(unsigned int enabled)
{
int ret = 0;
rtc_clear_interrupt();
switch (enabled) {
case 0:
/* alarm off */
rtc_disable_interrupt();
break;
case 1:
/* alarm on */
rtc_enable_interrupt();
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
int rtc_init(void)
{
struct rtc_time tm;
writel(0, REGS_RTC_BASE + RTC_CTL);
writel(CTL_SOFT_STR_SET|CTL_SOFT_STR_VALUE(1), REGS_RTC_BASE + RTC_CTL);
writel(CTL_OSC_EN_SET|CTL_OSC_EN_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL0_SET|CTL_CTL0_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL1_SET|CTL_CTL1_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL2_SET|CTL_CTL2_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_CTL3_SET|CTL_CTL3_VALUE(0), REGS_RTC_BASE + RTC_CTL);
writel(CTL_BIAS_TRM_SET|CTL_BIAS_TRM_VALUE(1)|CTL_CTL3_VALUE(0), REGS_RTC_BASE + RTC_CTL);
udelay(1000);
writel(CTL_OSC_EN_SET|CTL_OSC_EN_VALUE(1), REGS_RTC_BASE + RTC_CTL);
udelay(1000);
request_irq(RTC_PRD_IRQn, 0, rtc_isr, NULL);
alarm_irq_enable(0);
rtc_read_time(&tm);
if (tm.tm_year == 70) {
rtc_set_time(&default_tm);
}
return 0;
}
int iGetLocalTime(SystemTime_t *tm)
{
rtc_read_time(tm);
tm->tm_year += 1900;
tm->tm_mon += 1;
return 0;
}
void vSetLocalTime(SystemTime_t *tm)
{
tm->tm_year -= 1900;
tm->tm_mon -= 1;
rtc_set_time(tm);
}

View File

@ -0,0 +1,197 @@
#include <string.h>
#include "FreeRTOS.h"
#include "trace.h"
#include "mmcsd_core.h"
#define SECTOR_SIZE 512
int32_t mmcsd_num_wr_blocks(struct mmcsd_card *card)
{
int32_t err;
uint32_t blocks;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
uint32_t timeout_us;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = APP_CMD;
cmd.arg = card->rca << 16;
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC;
err = mmcsd_send_cmd(card->host, &cmd, 0);
if (err)
return -1;
if (!controller_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
return -1;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_APP_SEND_NUM_WR_BLKS;
cmd.arg = 0;
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
memset(&data, 0, sizeof(struct mmcsd_data));
data.timeout_ns = card->tacc_ns * 100;
data.timeout_clks = card->tacc_clks * 100;
timeout_us = data.timeout_ns / 1000;
timeout_us += data.timeout_clks * 1000 /
(card->host->io_cfg.clock / 1000);
if (timeout_us > 100000)
{
data.timeout_ns = 100000000;
data.timeout_clks = 0;
}
data.blksize = 4;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = &blocks;
memset(&req, 0, sizeof(struct mmcsd_req));
req.cmd = &cmd;
req.data = &data;
mmcsd_send_request(card->host, &req);
if (cmd.err || data.err)
return -1;
return blocks;
}
int mmcsd_req_blk(struct mmcsd_card *card,
uint32_t sector,
void *buf,
size_t blks,
uint8_t dir)
{
struct mmcsd_cmd cmd, stop;
struct mmcsd_data data;
struct mmcsd_req req;
struct mmcsd_host *host = card->host;
uint32_t r_cmd, w_cmd;
mmcsd_host_lock(host);
memset(&req, 0, sizeof(struct mmcsd_req));
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
memset(&stop, 0, sizeof(struct mmcsd_cmd));
memset(&data, 0, sizeof(struct mmcsd_data));
req.cmd = &cmd;
req.data = &data;
cmd.arg = sector;
if (!(card->flags & CARD_FLAG_SDHC))
{
cmd.arg <<= 9;
}
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
data.blksize = SECTOR_SIZE;
data.blks = blks;
if (blks > 1)
{
if (!controller_is_spi(card->host) || !dir)
{
req.stop = &stop;
stop.cmd_code = STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = RESP_SPI_R1B | RESP_R1B | CMD_AC;
}
r_cmd = READ_MULTIPLE_BLOCK;
w_cmd = WRITE_MULTIPLE_BLOCK;
}
else
{
req.stop = NULL;
r_cmd = READ_SINGLE_BLOCK;
w_cmd = WRITE_BLOCK;
}
if (!dir)
{
cmd.cmd_code = r_cmd;
data.flags |= DATA_DIR_READ;
}
else
{
cmd.cmd_code = w_cmd;
data.flags |= DATA_DIR_WRITE;
}
mmcsd_set_data_timeout(&data, card);
data.buf = buf;
mmcsd_send_request(host, &req);
if (!controller_is_spi(card->host) && dir != 0)
{
do
{
int32_t err;
cmd.cmd_code = SEND_STATUS;
cmd.arg = card->rca << 16;
cmd.flags = RESP_R1 | CMD_AC;
err = mmcsd_send_cmd(card->host, &cmd, 5);
if (err)
{
TRACE_ERROR("error %d requesting status", err);
break;
}
/*
* Some cards mishandle the status bits,
* so make sure to check both the busy
* indication and the card state.
*/
} while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
(R1_CURRENT_STATE(cmd.resp[0]) == 7));
}
mmcsd_host_unlock(host);
if (cmd.err || data.err || stop.err)
{
TRACE_ERROR("mmcsd request blocks error");
TRACE_ERROR("%d,%d,%d, 0x%08x,0x%08x",
cmd.err, data.err, stop.err, data.flags, sector);
return -1;
}
return 0;
}
int32_t mmcsd_set_blksize(struct mmcsd_card *card)
{
struct mmcsd_cmd cmd;
int err;
/* Block-addressed cards ignore MMC_SET_BLOCKLEN. */
if (card->flags & CARD_FLAG_SDHC)
return 0;
mmcsd_host_lock(card->host);
cmd.cmd_code = SET_BLOCKLEN;
cmd.arg = 512;
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC;
err = mmcsd_send_cmd(card->host, &cmd, 5);
mmcsd_host_unlock(card->host);
if (err)
{
TRACE_ERROR("MMCSD: unable to set block size to %d: %d", cmd.arg, err);
return -1;
}
return 0;
}

View File

@ -0,0 +1,575 @@
#include <string.h>
#include "FreeRTOS.h"
#include "trace.h"
#include "errno.h"
#include "mmcsd_core.h"
#include "mmc.h"
static const uint32_t tran_unit[] =
{
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const uint8_t tran_value[] =
{
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const uint32_t tacc_uint[] =
{
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const uint8_t tacc_value[] =
{
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static inline uint32_t GET_BITS(uint32_t *resp,
uint32_t start,
uint32_t size)
{
const int32_t __size = size;
const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;
const int32_t __off = 3 - ((start) / 32);
const int32_t __shft = (start) & 31;
uint32_t __res;
__res = resp[__off] >> __shft;
if (__size + __shft > 32)
__res |= resp[__off-1] << ((32 - __shft) % 32);
return __res & __mask;
}
/*
* Given a 128-bit response, decode to our card CSD structure.
*/
static int32_t mmcsd_parse_csd(struct mmcsd_card *card)
{
uint32_t a, b;
struct mmcsd_csd *csd = &card->csd;
uint32_t *resp = card->resp_csd;
/*
* We only understand CSD structure v1.1 and v1.2.
* v1.2 has extra information in bits 15, 11 and 10.
* We also support eMMC v4.4 & v4.41.
*/
csd->csd_structure = GET_BITS(resp, 126, 2);
if (csd->csd_structure == 0) {
TRACE_ERROR("unrecognised CSD structure version %d!", csd->csd_structure);
return -1;
}
csd->taac = GET_BITS(resp, 112, 8);
csd->nsac = GET_BITS(resp, 104, 8);
csd->tran_speed = GET_BITS(resp, 96, 8);
csd->card_cmd_class = GET_BITS(resp, 84, 12);
csd->rd_blk_len = GET_BITS(resp, 80, 4);
csd->rd_blk_part = GET_BITS(resp, 79, 1);
csd->wr_blk_misalign = GET_BITS(resp, 78, 1);
csd->rd_blk_misalign = GET_BITS(resp, 77, 1);
csd->dsr_imp = GET_BITS(resp, 76, 1);
csd->c_size = GET_BITS(resp, 62, 12);
csd->c_size_mult = GET_BITS(resp, 47, 3);
csd->r2w_factor = GET_BITS(resp, 26, 3);
csd->wr_blk_len = GET_BITS(resp, 22, 4);
csd->wr_blk_partial = GET_BITS(resp, 21, 1);
csd->csd_crc = GET_BITS(resp, 1, 7);
card->card_blksize = 1 << csd->rd_blk_len;
card->card_blknr = (csd->c_size + 1) << (csd->c_size_mult + 2);
card->card_capacity = card->card_blknr * card->card_blksize;
card->card_capacity >>= 10; /* unit:KB */
card->tacc_clks = csd->nsac * 100;
card->tacc_ns = (tacc_uint[csd->taac&0x07] * tacc_value[(csd->taac&0x78)>>3] + 9) / 10;
card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3];
if (csd->wr_blk_len >= 9) {
a = GET_BITS(resp, 42, 5);
b = GET_BITS(resp, 37, 5);
card->erase_size = (a + 1) * (b + 1);
card->erase_size <<= csd->wr_blk_len - 9;
}
return 0;
}
/*
* Read extended CSD.
*/
static int mmc_get_ext_csd(struct mmcsd_card *card, uint8_t **new_ext_csd)
{
void *ext_csd;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
*new_ext_csd = NULL;
if (GET_BITS(card->resp_cid, 122, 4) < 4)
return 0;
/*
* As the ext_csd is so large and mostly unused, we don't store the
* raw block in mmc_card.
*/
ext_csd = pvPortMalloc(512);
if (!ext_csd) {
TRACE_ERROR("alloc memory failed when get ext csd!");
return -ENOMEM;
}
memset(&req, 0, sizeof(struct mmcsd_req));
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
memset(&data, 0, sizeof(struct mmcsd_data));
req.cmd = &cmd;
req.data = &data;
cmd.cmd_code = SEND_EXT_CSD;
cmd.arg = 0;
/* NOTE HACK: the RESP_SPI_R1 is always correct here, but we
* rely on callers to never use this with "native" calls for reading
* CSD or CID. Native versions of those commands use the R2 type,
* not R1 plus a data block.
*/
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
data.blksize = 512;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = ext_csd;
/*
* Some cards require longer data read timeout than indicated in CSD.
* Address this by setting the read timeout to a "reasonably high"
* value. For the cards tested, 300ms has proven enough. If necessary,
* this value can be increased if other problematic cards require this.
*/
data.timeout_ns = 300000000;
data.timeout_clks = 0;
mmcsd_send_request(card->host, &req);
if (cmd.err)
return cmd.err;
if (data.err)
return data.err;
*new_ext_csd = ext_csd;
return 0;
}
/*
* Decode extended CSD.
*/
static int mmc_parse_ext_csd(struct mmcsd_card *card, uint8_t *ext_csd)
{
uint64_t card_capacity = 0;
if(card == NULL || ext_csd == NULL)
{
TRACE_ERROR("emmc parse ext csd fail, invaild args");
return -1;
}
card->flags |= CARD_FLAG_HIGHSPEED;
card->hs_max_data_rate = 52000000;
card_capacity = *((uint32_t *)&ext_csd[EXT_CSD_SEC_CNT]);
card_capacity *= card->card_blksize;
card_capacity >>= 10; /* unit:KB */
card->card_capacity = card_capacity;
TRACE_INFO("emmc card capacity %d KB.", card->card_capacity);
return 0;
}
/**
* mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
* @set: cmd set values
* @index: EXT_CSD register index
* @value: value to program into EXT_CSD register
*
* Modifies the EXT_CSD register for selected card.
*/
static int mmc_switch(struct mmcsd_card *card, uint8_t set,
uint8_t index, uint8_t value)
{
int err;
struct mmcsd_host *host = card->host;
struct mmcsd_cmd cmd = {0};
cmd.cmd_code = SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
(index << 16) | (value << 8) | set;
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC;
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
return 0;
}
static int mmc_compare_ext_csds(struct mmcsd_card *card,
uint8_t *ext_csd, uint32_t bus_width)
{
uint8_t *bw_ext_csd;
int err;
if (bus_width == MMCSD_BUS_WIDTH_1)
return 0;
err = mmc_get_ext_csd(card, &bw_ext_csd);
if (err || bw_ext_csd == NULL) {
err = -1;
goto out;
}
/* only compare read only fields */
err = !((ext_csd[EXT_CSD_PARTITION_SUPPORT] == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
(ext_csd[EXT_CSD_ERASED_MEM_CONT] == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
(ext_csd[EXT_CSD_REV] == bw_ext_csd[EXT_CSD_REV]) &&
(ext_csd[EXT_CSD_STRUCTURE] == bw_ext_csd[EXT_CSD_STRUCTURE]) &&
(ext_csd[EXT_CSD_CARD_TYPE] == bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
(ext_csd[EXT_CSD_S_A_TIMEOUT] == bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
(ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
(ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
(ext_csd[EXT_CSD_SEC_TRIM_MULT] == bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
(ext_csd[EXT_CSD_SEC_ERASE_MULT] == bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
(ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
(ext_csd[EXT_CSD_TRIM_MULT] == bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
(ext_csd[EXT_CSD_SEC_CNT + 0] == bw_ext_csd[EXT_CSD_SEC_CNT + 0]) &&
(ext_csd[EXT_CSD_SEC_CNT + 1] == bw_ext_csd[EXT_CSD_SEC_CNT + 1]) &&
(ext_csd[EXT_CSD_SEC_CNT + 2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
(ext_csd[EXT_CSD_SEC_CNT + 3] == bw_ext_csd[EXT_CSD_SEC_CNT + 3]) &&
(ext_csd[EXT_CSD_PWR_CL_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_52_195]) &&
(ext_csd[EXT_CSD_PWR_CL_26_195] == bw_ext_csd[EXT_CSD_PWR_CL_26_195]) &&
(ext_csd[EXT_CSD_PWR_CL_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_52_360]) &&
(ext_csd[EXT_CSD_PWR_CL_26_360] == bw_ext_csd[EXT_CSD_PWR_CL_26_360]) &&
(ext_csd[EXT_CSD_PWR_CL_200_195] == bw_ext_csd[EXT_CSD_PWR_CL_200_195]) &&
(ext_csd[EXT_CSD_PWR_CL_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_200_360]) &&
(ext_csd[EXT_CSD_PWR_CL_DDR_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
(ext_csd[EXT_CSD_PWR_CL_DDR_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) &&
(ext_csd[EXT_CSD_PWR_CL_DDR_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
if (err)
err = -1;
out:
vPortFree(bw_ext_csd);
return err;
}
/*
* Select the bus width amoung 4-bit and 8-bit(SDR).
* If the bus width is changed successfully, return the selected width value.
* Zero is returned instead of error value if the wide width is not supported.
*/
static int mmc_select_bus_width(struct mmcsd_card *card, uint8_t *ext_csd)
{
uint32_t ext_csd_bits[] = {
EXT_CSD_BUS_WIDTH_8,
EXT_CSD_BUS_WIDTH_4,
EXT_CSD_BUS_WIDTH_1
};
uint32_t bus_widths[] = {
MMCSD_BUS_WIDTH_8,
MMCSD_BUS_WIDTH_4,
MMCSD_BUS_WIDTH_1
};
struct mmcsd_host *host = card->host;
unsigned idx, bus_width = 0;
int err = 0;
if (GET_BITS(card->resp_cid, 122, 4) < 4)
return 0;
/*
* Unlike SD, MMC cards dont have a configuration register to notify
* supported bus width. So bus test command should be run to identify
* the supported bus width or compare the ext csd values of current
* bus width and ext csd values of 1 bit mode read earlier.
*/
for (idx = 0; idx < sizeof(bus_widths)/sizeof(uint32_t); idx++) {
/*
* Host is capable of 8bit transfer, then switch
* the device to work in 8bit transfer mode. If the
* mmc switch command returns error then switch to
* 4bit transfer mode. On success set the corresponding
* bus width on the host. Meanwhile, mmc core would
* bail out early if corresponding bus capable wasn't
* set by drivers.
*/
if ((!(host->flags & MMCSD_BUSWIDTH_8) &&
ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_8) ||
(!(host->flags & MMCSD_BUSWIDTH_4) &&
(ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_4 ||
ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_8)))
continue;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
ext_csd_bits[idx]);
if (err)
continue;
bus_width = bus_widths[idx];
mmcsd_set_bus_width(host, bus_width);
mmcsd_delay_ms(20); //delay 10ms
err = mmc_compare_ext_csds(card, ext_csd, bus_width);
if (!err) {
err = bus_width;
break;
} else {
switch(ext_csd_bits[idx]){
case 0:
TRACE_ERROR("switch to bus width 1 bit failed!");
break;
case 1:
TRACE_ERROR("switch to bus width 4 bit failed!");
break;
case 2:
TRACE_ERROR("switch to bus width 8 bit failed!");
break;
default:
break;
}
}
}
return err;
}
int mmc_send_op_cond(struct mmcsd_host *host,
uint32_t ocr, uint32_t *rocr)
{
struct mmcsd_cmd cmd;
uint32_t i;
int err = 0;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SEND_OP_COND;
cmd.arg = controller_is_spi(host) ? 0 : ocr;
cmd.flags = RESP_SPI_R1 | RESP_R3 | CMD_BCR;
for (i = 100; i; i--) {
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (controller_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & CARD_BUSY)
break;
}
err = -1;
mmcsd_delay_ms(10); //delay 10ms
}
if (rocr && !controller_is_spi(host))
*rocr = cmd.resp[0];
return err;
}
static int mmc_set_card_addr(struct mmcsd_host *host, uint32_t rca)
{
int err;
struct mmcsd_cmd cmd;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SET_RELATIVE_ADDR;
cmd.arg = rca << 16;
cmd.flags = RESP_R1 | CMD_AC;
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
return 0;
}
static int32_t mmcsd_mmc_init_card(struct mmcsd_host *host,
uint32_t ocr)
{
int32_t err;
uint32_t resp[4];
uint32_t rocr = 0;
uint32_t max_data_rate;
uint8_t *ext_csd = NULL;
struct mmcsd_card *card = NULL;
mmcsd_go_idle(host);
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
if (err)
goto err;
if (controller_is_spi(host))
{
err = mmcsd_spi_use_crc(host, 1);
if (err)
goto err1;
}
if (controller_is_spi(host))
err = mmcsd_get_cid(host, resp);
else
err = mmcsd_all_get_cid(host, resp);
if (err)
goto err;
card = pvPortMalloc(sizeof(struct mmcsd_card));
if (!card)
{
TRACE_ERROR("malloc card failed!");
err = -ENOMEM;
goto err;
}
memset(card, 0, sizeof(struct mmcsd_card));
card->card_type = CARD_TYPE_MMC;
card->host = host;
card->rca = 1;
memcpy(card->resp_cid, resp, sizeof(card->resp_cid));
/*
* For native busses: get card RCA and quit open drain mode.
*/
if (!controller_is_spi(host))
{
err = mmc_set_card_addr(host, card->rca);
if (err)
goto err1;
mmcsd_set_bus_mode(host, MMCSD_BUSMODE_PUSHPULL);
}
err = mmcsd_get_csd(card, card->resp_csd);
if (err)
goto err1;
err = mmcsd_parse_csd(card);
if (err)
goto err1;
if (!controller_is_spi(host))
{
err = mmcsd_select_card(card);
if (err)
goto err1;
}
/*
* Fetch and process extended CSD.
*/
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
goto err1;
err = mmc_parse_ext_csd(card, ext_csd);
if (err)
goto err1;
/* If doing byte addressing, check if required to do sector
* addressing. Handle the case of <2GB cards needing sector
* addressing. See section 8.1 JEDEC Standard JED84-A441;
* ocr register has bit 30 set for sector addressing.
*/
if (!(card->flags & CARD_FLAG_SDHC) && (rocr & (1<<30)))
card->flags |= CARD_FLAG_SDHC;
/* set bus speed */
if (card->flags & CARD_FLAG_HIGHSPEED)
max_data_rate = card->hs_max_data_rate;
else
max_data_rate = card->max_data_rate;
mmcsd_set_clock(host, max_data_rate);
/*switch bus width*/
mmc_select_bus_width(card, ext_csd);
host->card = card;
vPortFree(ext_csd);
return 0;
err1:
vPortFree(card);
err:
return err;
}
/*
* Starting point for mmc card init.
*/
int32_t init_mmc(struct mmcsd_host *host, uint32_t ocr)
{
int32_t err;
uint32_t current_ocr;
/*
* We need to get OCR a different way for SPI.
*/
if (controller_is_spi(host))
{
err = mmcsd_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
current_ocr = mmcsd_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!current_ocr)
{
err = -1;
goto err;
}
/*
* Detect and init the card.
*/
err = mmcsd_mmc_init_card(host, current_ocr);
if (err)
goto err;
mmcsd_host_unlock(host);
mmcsd_host_lock(host);
return 0;
err:
TRACE_ERROR("init MMC card failed!");
return err;
}

View File

@ -0,0 +1,771 @@
#include "FreeRTOS.h"
#include "os_adapt.h"
#include "trace.h"
#include "errno.h"
#include "ff_sddisk.h"
#include "mmcsd_core.h"
#include "sd.h"
#include "mmc.h"
#include "sdio.h"
#define MMCSD_STACK_SIZE 2048
#define MMCSD_THREAD_PREORITY 0x16
#define SDMMC_MOUNT_PATH "/sd"
struct mmcsd_card *sdmmc_cardinfo = NULL;
FF_Disk_t *sdmmc_disk;
static TaskHandle_t mmcsd_detect_thread;
static QueueHandle_t mmcsd_detect_mb;
static QueueHandle_t mmcsd_hotpluge_mb;
static QueueHandle_t mmcsd_sdio_ready_mb;
static QueueHandle_t mmcsd_mmc_ready_mb;
void mmcsd_host_lock(struct mmcsd_host *host)
{
xSemaphoreTake(host->bus_lock, portMAX_DELAY);
}
void mmcsd_host_unlock(struct mmcsd_host *host)
{
xSemaphoreGive(host->bus_lock);
}
void mmcsd_req_complete(struct mmcsd_host *host)
{
xSemaphoreGive(host->sem_ack);
}
void mmcsd_send_request(struct mmcsd_host *host, struct mmcsd_req *req)
{
do {
req->cmd->retries--;
req->cmd->err = 0;
req->cmd->mrq = req;
if (req->data)
{
req->cmd->data = req->data;
req->data->err = 0;
req->data->mrq = req;
if (req->stop)
{
req->data->stop = req->stop;
req->stop->err = 0;
req->stop->mrq = req;
}
}
host->ops->request(host, req);
xSemaphoreTake(host->sem_ack, portMAX_DELAY);
} while(req->cmd->err && (req->cmd->retries > 0));
}
int32_t mmcsd_send_cmd(struct mmcsd_host *host,
struct mmcsd_cmd *cmd,
int retries)
{
struct mmcsd_req req;
memset(&req, 0, sizeof(struct mmcsd_req));
memset(cmd->resp, 0, sizeof(cmd->resp));
cmd->retries = retries;
req.cmd = cmd;
cmd->data = NULL;
mmcsd_send_request(host, &req);
return cmd->err;
}
int32_t mmcsd_go_idle(struct mmcsd_host *host)
{
int32_t err;
struct mmcsd_cmd cmd;
if (!controller_is_spi(host))
{
mmcsd_set_chip_select(host, MMCSD_CS_HIGH);
mmcsd_delay_ms(1);
}
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = GO_IDLE_STATE;
cmd.arg = 0;
cmd.flags = RESP_SPI_R1 | RESP_NONE | CMD_BC;
err = mmcsd_send_cmd(host, &cmd, 0);
mmcsd_delay_ms(1);
if (!controller_is_spi(host))
{
mmcsd_set_chip_select(host, MMCSD_CS_IGNORE);
mmcsd_delay_ms(1);
}
return err;
}
int32_t mmcsd_spi_read_ocr(struct mmcsd_host *host,
int32_t high_capacity,
uint32_t *ocr)
{
struct mmcsd_cmd cmd;
int32_t err;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SPI_READ_OCR;
cmd.arg = high_capacity ? (1 << 30) : 0;
cmd.flags = RESP_SPI_R3;
err = mmcsd_send_cmd(host, &cmd, 0);
*ocr = cmd.resp[1];
return err;
}
int32_t mmcsd_all_get_cid(struct mmcsd_host *host, uint32_t *cid)
{
int32_t err;
struct mmcsd_cmd cmd;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = ALL_SEND_CID;
cmd.arg = 0;
cmd.flags = RESP_R2 | CMD_BCR;
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
memcpy(cid, cmd.resp, sizeof(uint32_t) * 4);
return 0;
}
int32_t mmcsd_get_cid(struct mmcsd_host *host, uint32_t *cid)
{
int32_t err, i;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
uint32_t *buf = NULL;
if (!controller_is_spi(host))
{
if (!host->card)
return -1;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SEND_CID;
cmd.arg = host->card->rca << 16;
cmd.flags = RESP_R2 | CMD_AC;
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
memcpy(cid, cmd.resp, sizeof(uint32_t) * 4);
return 0;
}
buf = (uint32_t *)pvPortMalloc(16);
if (!buf)
{
TRACE_ERROR("allocate memory failed!");
return -ENOMEM;
}
memset(&req, 0, sizeof(struct mmcsd_req));
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
memset(&data, 0, sizeof(struct mmcsd_data));
req.cmd = &cmd;
req.data = &data;
cmd.cmd_code = SEND_CID;
cmd.arg = 0;
/* NOTE HACK: the RESP_SPI_R1 is always correct here, but we
* rely on callers to never use this with "native" calls for reading
* CSD or CID. Native versions of those commands use the R2 type,
* not R1 plus a data block.
*/
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
data.blksize = 16;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = buf;
/*
* The spec states that CSR and CID accesses have a timeout
* of 64 clock cycles.
*/
data.timeout_ns = 0;
data.timeout_clks = 64;
mmcsd_send_request(host, &req);
if (cmd.err || data.err)
{
vPortFree(buf);
return -1;
}
for (i = 0;i < 4;i++)
cid[i] = buf[i];
vPortFree(buf);
return 0;
}
int32_t mmcsd_get_csd(struct mmcsd_card *card, uint32_t *csd)
{
int32_t err, i;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
uint32_t *buf = NULL;
if (!controller_is_spi(card->host))
{
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SEND_CSD;
cmd.arg = card->rca << 16;
cmd.flags = RESP_R2 | CMD_AC;
err = mmcsd_send_cmd(card->host, &cmd, 3);
if (err)
return err;
memcpy(csd, cmd.resp, sizeof(uint32_t) * 4);
return 0;
}
buf = (uint32_t*)pvPortMalloc(16);
if (!buf)
{
TRACE_ERROR("allocate memory failed!");
return -ENOMEM;
}
memset(&req, 0, sizeof(struct mmcsd_req));
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
memset(&data, 0, sizeof(struct mmcsd_data));
req.cmd = &cmd;
req.data = &data;
cmd.cmd_code = SEND_CSD;
cmd.arg = 0;
/* NOTE HACK: the RESP_SPI_R1 is always correct here, but we
* rely on callers to never use this with "native" calls for reading
* CSD or CID. Native versions of those commands use the R2 type,
* not R1 plus a data block.
*/
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
data.blksize = 16;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = buf;
/*
* The spec states that CSR and CID accesses have a timeout
* of 64 clock cycles.
*/
data.timeout_ns = 0;
data.timeout_clks = 64;
mmcsd_send_request(card->host, &req);
if (cmd.err || data.err)
{
vPortFree(buf);
return -1;
}
for (i = 0;i < 4;i++)
csd[i] = buf[i];
vPortFree(buf);
return 0;
}
static int32_t _mmcsd_select_card(struct mmcsd_host *host,
struct mmcsd_card *card)
{
int32_t err;
struct mmcsd_cmd cmd;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SELECT_CARD;
if (card)
{
cmd.arg = card->rca << 16;
cmd.flags = RESP_R1 | CMD_AC;
}
else
{
cmd.arg = 0;
cmd.flags = RESP_NONE | CMD_AC;
}
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
return 0;
}
int32_t mmcsd_select_card(struct mmcsd_card *card)
{
return _mmcsd_select_card(card->host, card);
}
int32_t mmcsd_deselect_cards(struct mmcsd_card *card)
{
return _mmcsd_select_card(card->host, NULL);
}
int32_t mmcsd_spi_use_crc(struct mmcsd_host *host, int32_t use_crc)
{
struct mmcsd_cmd cmd;
int32_t err;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SPI_CRC_ON_OFF;
cmd.flags = RESP_SPI_R1;
cmd.arg = use_crc;
err = mmcsd_send_cmd(host, &cmd, 0);
if (!err)
host->spi_use_crc = use_crc;
return err;
}
static inline void mmcsd_set_iocfg(struct mmcsd_host *host)
{
struct mmcsd_io_cfg *io_cfg = &host->io_cfg;
mmcsd_dbg("clock %uHz busmode %u powermode %u cs %u Vdd %u "
"width %u \n",
io_cfg->clock, io_cfg->bus_mode,
io_cfg->power_mode, io_cfg->chip_select, io_cfg->vdd,
io_cfg->bus_width);
host->ops->set_iocfg(host, io_cfg);
}
/*
* Control chip select pin on a host.
*/
void mmcsd_set_chip_select(struct mmcsd_host *host, int32_t mode)
{
host->io_cfg.chip_select = mode;
mmcsd_set_iocfg(host);
}
/*
* Sets the host clock to the highest possible frequency that
* is below "hz".
*/
void mmcsd_set_clock(struct mmcsd_host *host, uint32_t clk)
{
if (clk < host->freq_min)
{
TRACE_WARNING("clock too low!");
}
host->io_cfg.clock = clk;
mmcsd_set_iocfg(host);
}
/*
* Change the bus mode (open drain/push-pull) of a host.
*/
void mmcsd_set_bus_mode(struct mmcsd_host *host, uint32_t mode)
{
host->io_cfg.bus_mode = mode;
mmcsd_set_iocfg(host);
}
/*
* Change data bus width of a host.
*/
void mmcsd_set_bus_width(struct mmcsd_host *host, uint32_t width)
{
host->io_cfg.bus_width = width;
mmcsd_set_iocfg(host);
}
void mmcsd_set_data_timeout(struct mmcsd_data *data,
const struct mmcsd_card *card)
{
uint32_t mult;
if (card->card_type == CARD_TYPE_SDIO)
{
data->timeout_ns = 1000000000; /* SDIO card 1s */
data->timeout_clks = 0;
return;
}
/*
* SD cards use a 100 multiplier rather than 10
*/
mult = (card->card_type == CARD_TYPE_SD) ? 100 : 10;
/*
* Scale up the multiplier (and therefore the timeout) by
* the r2w factor for writes.
*/
if (data->flags & DATA_DIR_WRITE)
mult <<= card->csd.r2w_factor;
data->timeout_ns = card->tacc_ns * mult;
data->timeout_clks = card->tacc_clks * mult;
/*
* SD cards also have an upper limit on the timeout.
*/
if (card->card_type == CARD_TYPE_SD)
{
uint32_t timeout_us, limit_us;
timeout_us = data->timeout_ns / 1000;
timeout_us += data->timeout_clks * 1000 /
(card->host->io_cfg.clock / 1000);
if (data->flags & DATA_DIR_WRITE)
/*
* The limit is really 250 ms, but that is
* insufficient for some crappy cards.
*/
limit_us = 300000;
else
limit_us = 100000;
/*
* SDHC cards always use these fixed values.
*/
if (timeout_us > limit_us || card->flags & CARD_FLAG_SDHC)
{
data->timeout_ns = limit_us * 1000; /* SDHC card fixed 250ms */
data->timeout_clks = 0;
}
}
if (controller_is_spi(card->host))
{
if (data->flags & DATA_DIR_WRITE)
{
if (data->timeout_ns < 1000000000)
data->timeout_ns = 1000000000; /* 1s */
}
else
{
if (data->timeout_ns < 100000000)
data->timeout_ns = 100000000; /* 100ms */
}
}
}
/*
* Mask off any voltages we don't support and select
* the lowest voltage
*/
uint32_t mmcsd_select_voltage(struct mmcsd_host *host, uint32_t ocr)
{
int bit;
ocr &= host->valid_ocr;
bit = ffs(ocr);
if (bit)
{
bit -= 1;
ocr &= 3 << bit;
host->io_cfg.vdd = bit;
mmcsd_set_iocfg(host);
}
else
{
TRACE_WARNING("host doesn't support card's voltages!");
ocr = 0;
}
return ocr;
}
static void mmcsd_power_up(struct mmcsd_host *host)
{
int bit = fls(host->valid_ocr) - 1;
host->io_cfg.vdd = bit;
if (controller_is_spi(host))
{
host->io_cfg.chip_select = MMCSD_CS_HIGH;
host->io_cfg.bus_mode = MMCSD_BUSMODE_PUSHPULL;
}
else
{
host->io_cfg.chip_select = MMCSD_CS_IGNORE;
host->io_cfg.bus_mode = MMCSD_BUSMODE_OPENDRAIN;
}
host->io_cfg.power_mode = MMCSD_POWER_UP;
host->io_cfg.bus_width = MMCSD_BUS_WIDTH_1;
mmcsd_set_iocfg(host);
/*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmcsd_delay_ms(10);
host->io_cfg.clock = host->freq_min;
host->io_cfg.power_mode = MMCSD_POWER_ON;
mmcsd_set_iocfg(host);
/*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
mmcsd_delay_ms(10);
}
static void mmcsd_power_off(struct mmcsd_host *host)
{
host->io_cfg.clock = 0;
host->io_cfg.vdd = 0;
if (!controller_is_spi(host))
{
host->io_cfg.bus_mode = MMCSD_BUSMODE_OPENDRAIN;
host->io_cfg.chip_select = MMCSD_CS_IGNORE;
}
host->io_cfg.power_mode = MMCSD_POWER_OFF;
host->io_cfg.bus_width = MMCSD_BUS_WIDTH_1;
mmcsd_set_iocfg(host);
}
int mmcsd_wait_cd_changed(uint32_t timeout)
{
struct mmcsd_host *host;
if (xQueueReceive(mmcsd_hotpluge_mb, &host, timeout) == pdPASS)
{
if(host->card == NULL)
{
return MMCSD_HOST_UNPLUGED;
}
else
{
return MMCSD_HOST_PLUGED;
}
}
return -1;
}
int mmcsd_wait_sdio_ready(int32_t timeout)
{
struct mmcsd_host *host;
if (xQueueReceive(mmcsd_sdio_ready_mb, &host, timeout) == pdPASS)
return MMCSD_HOST_PLUGED;
return -1;
}
int mmcsd_wait_mmc_ready(uint32_t timeout)
{
struct mmcsd_host *host;
if (xQueueReceive(mmcsd_mmc_ready_mb, &host, timeout) == pdPASS)
return MMCSD_HOST_PLUGED;
return -1;
}
void mmcsd_change(struct mmcsd_host *host)
{
xQueueSend(mmcsd_detect_mb, &host, 0);
}
void mmcsd_change_from_isr(struct mmcsd_host *host)
{
xQueueSendFromISR(mmcsd_detect_mb, &host, 0);
}
void mmcsd_detect(void *param)
{
struct mmcsd_host *host;
uint32_t ocr;
int32_t err;
while (1)
{
if (xQueueReceive(mmcsd_detect_mb, &host, portMAX_DELAY) == pdPASS)
{
if (host->card == NULL)
{
mmcsd_host_lock(host);
mmcsd_power_up(host);
mmcsd_go_idle(host);
mmcsd_send_if_cond(host, host->valid_ocr);
#if DEVICE_TYPE_SELECT != EMMC_FLASH
err = sdio_io_send_op_cond(host, 0, &ocr);
if (!err)
{
if (init_sdio(host, ocr))
mmcsd_power_off(host);
else
xQueueSend(mmcsd_sdio_ready_mb, &host, 0);
mmcsd_host_unlock(host);
continue;
}
/*
* detect SD card
*/
err = mmcsd_send_app_op_cond(host, 0, &ocr);
if (!err)
{
if (init_sd(host, ocr)) {
mmcsd_power_off(host);
} else {
sdmmc_cardinfo = host->card;
mmcsd_host_unlock(host);
sdmmc_disk = FF_SDDiskInit(SDMMC_MOUNT_PATH);
xQueueSend(mmcsd_hotpluge_mb, &host, 0);
}
continue;
}
#else
/*
* detect mmc card
*/
err = mmc_send_op_cond(host, 0, &ocr);
if (!err)
{
if (init_mmc(host, ocr)) {
mmcsd_power_off(host);
} else {
sdmmc_cardinfo = host->card;
mmcsd_host_unlock(host);
sdmmc_disk = FF_SDDiskInit(SDMMC_MOUNT_PATH);
xQueueSend(mmcsd_mmc_ready_mb, &host, 0);
}
continue;
}
#endif
mmcsd_host_unlock(host);
}
else
{
/* card removed */
mmcsd_host_lock(host);
if (host->card->sdio_function_num != 0)
{
TRACE_WARNING("unsupport sdio card plug out!\r\n");
}
else
{
if (host->card->card_type == CARD_TYPE_SD || host->card->card_type == CARD_TYPE_MMC) {
FF_SDDiskDelete(sdmmc_disk);
sdmmc_cardinfo = NULL;
}
vPortFree(host->card);
host->card = NULL;
}
mmcsd_host_unlock(host);
xQueueSend(mmcsd_hotpluge_mb, &host, 0);
}
}
}
}
struct mmcsd_host *mmcsd_alloc_host(void)
{
struct mmcsd_host *host;
host = pvPortMalloc(sizeof(struct mmcsd_host));
if (!host)
{
TRACE_ERROR("alloc host failed");
return NULL;
}
memset(host, 0, sizeof(struct mmcsd_host));
host->max_seg_size = 65535;
host->max_dma_segs = 1;
host->max_blk_size = 512;
host->max_blk_count = 4096;
host->bus_lock = xSemaphoreCreateMutex();
host->sem_ack= xSemaphoreCreateBinary();
return host;
}
void mmcsd_free_host(struct mmcsd_host *host)
{
vSemaphoreDelete(host->bus_lock);
vSemaphoreDelete(host->sem_ack);
vPortFree(host);
}
int mmcsd_core_init(void)
{
/* initialize detect SD cart thread */
/* initialize mailbox and create detect SD card thread */
mmcsd_detect_mb = xQueueCreate(4, sizeof(uint32_t));
configASSERT(mmcsd_detect_mb);
mmcsd_hotpluge_mb = xQueueCreate(4, sizeof(uint32_t));
configASSERT(mmcsd_hotpluge_mb);
mmcsd_sdio_ready_mb = xQueueCreate(4, sizeof(uint32_t));
configASSERT(mmcsd_sdio_ready_mb);
mmcsd_mmc_ready_mb = xQueueCreate(4, sizeof(uint32_t));
configASSERT(mmcsd_mmc_ready_mb);
xTaskCreate(mmcsd_detect, "mmcsd_detect", MMCSD_STACK_SIZE,
NULL, MMCSD_THREAD_PREORITY, &mmcsd_detect_thread);
#if DEVICE_TYPE_SELECT != EMMC_FLASH
sdio_init();
#endif
return 0;
}
struct mmcsd_card *mmcsd_get_sdmmc_card_info(void)
{
return sdmmc_cardinfo;
}

View File

@ -0,0 +1,687 @@
#include <string.h>
#include "FreeRTOS.h"
#include "trace.h"
#include "errno.h"
#include "mmcsd_core.h"
#include "sd.h"
static const uint32_t tran_unit[] =
{
10000, 100000, 1000000, 10000000,
0, 0, 0, 0
};
static const uint8_t tran_value[] =
{
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static const uint32_t tacc_uint[] =
{
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
};
static const uint8_t tacc_value[] =
{
0, 10, 12, 13, 15, 20, 25, 30,
35, 40, 45, 50, 55, 60, 70, 80,
};
static inline uint32_t GET_BITS(uint32_t *resp,
uint32_t start,
uint32_t size)
{
const int32_t __size = size;
const uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;
const int32_t __off = 3 - ((start) / 32);
const int32_t __shft = (start) & 31;
uint32_t __res;
__res = resp[__off] >> __shft;
if (__size + __shft > 32)
__res |= resp[__off-1] << ((32 - __shft) % 32);
return __res & __mask;
}
static int32_t mmcsd_parse_csd(struct mmcsd_card *card)
{
struct mmcsd_csd *csd = &card->csd;
uint32_t *resp = card->resp_csd;
csd->csd_structure = GET_BITS(resp, 126, 2);
switch (csd->csd_structure)
{
case 0:
csd->taac = GET_BITS(resp, 112, 8);
csd->nsac = GET_BITS(resp, 104, 8);
csd->tran_speed = GET_BITS(resp, 96, 8);
csd->card_cmd_class = GET_BITS(resp, 84, 12);
csd->rd_blk_len = GET_BITS(resp, 80, 4);
csd->rd_blk_part = GET_BITS(resp, 79, 1);
csd->wr_blk_misalign = GET_BITS(resp, 78, 1);
csd->rd_blk_misalign = GET_BITS(resp, 77, 1);
csd->dsr_imp = GET_BITS(resp, 76, 1);
csd->c_size = GET_BITS(resp, 62, 12);
csd->c_size_mult = GET_BITS(resp, 47, 3);
csd->r2w_factor = GET_BITS(resp, 26, 3);
csd->wr_blk_len = GET_BITS(resp, 22, 4);
csd->wr_blk_partial = GET_BITS(resp, 21, 1);
csd->csd_crc = GET_BITS(resp, 1, 7);
card->card_blksize = 1 << csd->rd_blk_len;
card->card_blknr = (csd->c_size + 1) << (csd->c_size_mult + 2);
card->card_capacity = card->card_blknr * card->card_blksize;
card->card_capacity >>= 10; /* unit:KB */
card->tacc_clks = csd->nsac * 100;
card->tacc_ns = (tacc_uint[csd->taac&0x07] * tacc_value[(csd->taac&0x78)>>3] + 9) / 10;
card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3];
break;
case 1:
card->flags |= CARD_FLAG_SDHC;
/*This field is fixed to 0Eh, which indicates 1 ms.
The host should not use TAAC, NSAC, and R2W_FACTOR
to calculate timeout and should uses fixed timeout
values for read and write operations*/
csd->taac = GET_BITS(resp, 112, 8);
csd->nsac = GET_BITS(resp, 104, 8);
csd->tran_speed = GET_BITS(resp, 96, 8);
csd->card_cmd_class = GET_BITS(resp, 84, 12);
csd->rd_blk_len = GET_BITS(resp, 80, 4);
csd->rd_blk_part = GET_BITS(resp, 79, 1);
csd->wr_blk_misalign = GET_BITS(resp, 78, 1);
csd->rd_blk_misalign = GET_BITS(resp, 77, 1);
csd->dsr_imp = GET_BITS(resp, 76, 1);
csd->c_size = GET_BITS(resp, 48, 22);
csd->r2w_factor = GET_BITS(resp, 26, 3);
csd->wr_blk_len = GET_BITS(resp, 22, 4);
csd->wr_blk_partial = GET_BITS(resp, 21, 1);
csd->csd_crc = GET_BITS(resp, 1, 7);
card->card_blknr = (csd->c_size + 1) << 10;
card->card_blksize = 512;
card->card_capacity = (csd->c_size + 1) * 512; /* unit:KB */
card->tacc_clks = 0;
card->tacc_ns = 0;
card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3];
break;
default:
TRACE_ERROR("unrecognised CSD structure version %d!", csd->csd_structure);
return -1;
}
TRACE_INFO("SD card capacity %d KB.", card->card_capacity);
return 0;
}
static int32_t mmcsd_parse_scr(struct mmcsd_card *card)
{
struct sd_scr *scr = &card->scr;
uint32_t resp[4];
resp[3] = card->resp_scr[1];
resp[2] = card->resp_scr[0];
scr->sd_version = GET_BITS(resp, 56, 4);
scr->sd_bus_widths = GET_BITS(resp, 48, 4);
return 0;
}
static int32_t mmcsd_switch(struct mmcsd_card *card)
{
int32_t err;
struct mmcsd_host *host = card->host;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
uint8_t *buf;
buf = (uint8_t*)pvPortMalloc(64);
if (!buf)
{
TRACE_ERROR("alloc memory failed!");
return -ENOMEM;
}
if (card->card_type != CARD_TYPE_SD)
goto err;
if (card->scr.sd_version < SCR_SPEC_VER_1)
goto err;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_SWITCH;
cmd.arg = 0x00FFFFF1;
cmd.flags = RESP_R1 | CMD_ADTC;
memset(&data, 0, sizeof(struct mmcsd_data));
mmcsd_set_data_timeout(&data, card);
data.blksize = 64;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = (uint32_t *)buf;
memset(&req, 0, sizeof(struct mmcsd_req));
req.cmd = &cmd;
req.data = &data;
mmcsd_send_request(host, &req);
if (cmd.err || data.err)
{
goto err1;
}
if (buf[13] & 0x02)
card->hs_max_data_rate = 50000000;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_SWITCH;
cmd.arg = 0x80FFFFF1;
cmd.flags = RESP_R1 | CMD_ADTC;
memset(&data, 0, sizeof(struct mmcsd_data));
mmcsd_set_data_timeout(&data, card);
data.blksize = 64;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = (uint32_t *)buf;
memset(&req, 0, sizeof(struct mmcsd_req));
req.cmd = &cmd;
req.data = &data;
mmcsd_send_request(host, &req);
if (cmd.err || data.err)
{
goto err1;
}
if ((buf[16] & 0xF) != 1)
{
TRACE_INFO("switching card to high speed failed!");
goto err;
}
card->flags |= CARD_FLAG_HIGHSPEED;
err:
vPortFree(buf);
return 0;
err1:
if (cmd.err)
err = cmd.err;
if (data.err)
err = data.err;
return err;
}
static int mmcsd_app_cmd(struct mmcsd_host *host,
struct mmcsd_card *card)
{
int err;
struct mmcsd_cmd cmd = {0};
cmd.cmd_code = APP_CMD;
if (card)
{
cmd.arg = card->rca << 16;
cmd.flags = RESP_R1 | CMD_AC;
}
else
{
cmd.arg = 0;
cmd.flags = RESP_R1 | CMD_BCR;
}
err = mmcsd_send_cmd(host, &cmd, 0);
if (err)
return err;
/* Check that card supported application commands */
if (!controller_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
return -1;
return 0;
}
int mmcsd_send_app_cmd(struct mmcsd_host *host,
struct mmcsd_card *card,
struct mmcsd_cmd *cmd,
int retry)
{
struct mmcsd_req req;
uint32_t i;
int err;
err = -1;
/*
* We have to resend MMC_APP_CMD for each attempt so
* we cannot use the retries field in mmc_command.
*/
for (i = 0;i <= retry;i++)
{
memset(&req, 0, sizeof(struct mmcsd_req));
err = mmcsd_app_cmd(host, card);
if (err)
{
/* no point in retrying; no APP commands allowed */
if (controller_is_spi(host))
{
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
continue;
}
memset(&req, 0, sizeof(struct mmcsd_req));
memset(cmd->resp, 0, sizeof(cmd->resp));
req.cmd = cmd;
//cmd->data = NULL;
mmcsd_send_request(host, &req);
err = cmd->err;
if (!cmd->err)
break;
/* no point in retrying illegal APP commands */
if (controller_is_spi(host))
{
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
}
return err;
}
int mmcsd_app_set_bus_width(struct mmcsd_card *card, int32_t width)
{
int err;
struct mmcsd_cmd cmd;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_APP_SET_BUS_WIDTH;
cmd.flags = RESP_R1 | CMD_AC;
switch (width)
{
case MMCSD_BUS_WIDTH_1:
cmd.arg = MMCSD_BUS_WIDTH_1;
break;
case MMCSD_BUS_WIDTH_4:
cmd.arg = MMCSD_BUS_WIDTH_4;
break;
default:
return -1;
}
err = mmcsd_send_app_cmd(card->host, card, &cmd, 3);
if (err)
return err;
return 0;
}
int mmcsd_send_app_op_cond(struct mmcsd_host *host,
uint32_t ocr,
uint32_t *rocr)
{
struct mmcsd_cmd cmd;
uint32_t i;
int err = 0;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_APP_OP_COND;
if (controller_is_spi(host))
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
else
cmd.arg = ocr;
cmd.flags = RESP_SPI_R1 | RESP_R3 | CMD_BCR;
for (i = 100; i; i--)
{
err = mmcsd_send_app_cmd(host, NULL, &cmd, 3);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (controller_is_spi(host))
{
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
}
else
{
if (cmd.resp[0] & CARD_BUSY)
break;
}
err = -1;
mmcsd_delay_ms(10); //delay 10ms
}
if (rocr && !controller_is_spi(host))
*rocr = cmd.resp[0];
return err;
}
/*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
* before SD_APP_OP_COND. This command will harmlessly fail for
* SD 1.0 cards.
*/
int mmcsd_send_if_cond(struct mmcsd_host *host, uint32_t ocr)
{
struct mmcsd_cmd cmd;
int err;
uint8_t pattern;
cmd.cmd_code = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | 0xAA;
cmd.flags = RESP_SPI_R7 | RESP_R7 | CMD_BCR;
err = mmcsd_send_cmd(host, &cmd, 0);
if (err)
return err;
if (controller_is_spi(host))
pattern = cmd.resp[1] & 0xFF;
else
pattern = cmd.resp[0] & 0xFF;
if (pattern != 0xAA)
return -1;
return 0;
}
int mmcsd_get_card_addr(struct mmcsd_host *host, uint32_t *rca)
{
int err;
struct mmcsd_cmd cmd;
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
cmd.cmd_code = SD_SEND_RELATIVE_ADDR;
cmd.arg = 0;
cmd.flags = RESP_R6 | CMD_BCR;
err = mmcsd_send_cmd(host, &cmd, 3);
if (err)
return err;
*rca = cmd.resp[0] >> 16;
return 0;
}
#define be32_to_cpu(x) ((uint32_t)( \
(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
int32_t mmcsd_get_scr(struct mmcsd_card *card, uint32_t *scr)
{
int32_t err;
struct mmcsd_req req;
struct mmcsd_cmd cmd;
struct mmcsd_data data;
err = mmcsd_app_cmd(card->host, card);
if (err)
return err;
memset(&req, 0, sizeof(struct mmcsd_req));
memset(&cmd, 0, sizeof(struct mmcsd_cmd));
memset(&data, 0, sizeof(struct mmcsd_data));
req.cmd = &cmd;
req.data = &data;
cmd.cmd_code = SD_APP_SEND_SCR;
cmd.arg = 0;
cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
data.blksize = 8;
data.blks = 1;
data.flags = DATA_DIR_READ;
data.buf = scr;
mmcsd_set_data_timeout(&data, card);
mmcsd_send_request(card->host, &req);
if (cmd.err)
return cmd.err;
if (data.err)
return data.err;
scr[0] = be32_to_cpu(scr[0]);
scr[1] = be32_to_cpu(scr[1]);
return 0;
}
static int32_t mmcsd_sd_init_card(struct mmcsd_host *host,
uint32_t ocr)
{
struct mmcsd_card *card;
int32_t err;
uint32_t resp[4];
uint32_t max_data_rate;
mmcsd_go_idle(host);
/*
* If SD_SEND_IF_COND indicates an SD 2.0
* compliant card and we should set bit 30
* of the ocr to indicate that we can handle
* block-addressed SDHC cards.
*/
err = mmcsd_send_if_cond(host, ocr);
if (!err)
ocr |= 1 << 30;
err = mmcsd_send_app_op_cond(host, ocr, NULL);
if (err)
goto err;
if (controller_is_spi(host))
err = mmcsd_get_cid(host, resp);
else
err = mmcsd_all_get_cid(host, resp);
if (err)
goto err;
card = pvPortMalloc(sizeof(struct mmcsd_card));
if (!card)
{
TRACE_ERROR("malloc card failed!");
err = -ENOMEM;
goto err;
}
memset(card, 0, sizeof(struct mmcsd_card));
card->card_type = CARD_TYPE_SD;
card->host = host;
memcpy(card->resp_cid, resp, sizeof(card->resp_cid));
/*
* For native busses: get card RCA and quit open drain mode.
*/
if (!controller_is_spi(host))
{
err = mmcsd_get_card_addr(host, &card->rca);
if (err)
goto err1;
mmcsd_set_bus_mode(host, MMCSD_BUSMODE_PUSHPULL);
}
err = mmcsd_get_csd(card, card->resp_csd);
if (err)
goto err1;
err = mmcsd_parse_csd(card);
if (err)
goto err1;
if (!controller_is_spi(host))
{
err = mmcsd_select_card(card);
if (err)
goto err1;
}
err = mmcsd_get_scr(card, card->resp_scr);
if (err)
goto err1;
mmcsd_parse_scr(card);
if (controller_is_spi(host))
{
err = mmcsd_spi_use_crc(host, 1);
if (err)
goto err1;
}
/*
* change SD card to high-speed, only SD2.0 spec
*/
err = mmcsd_switch(card);
if (err)
goto err1;
/* set bus speed */
max_data_rate = (unsigned int)-1;
if (card->flags & CARD_FLAG_HIGHSPEED)
{
if (max_data_rate > card->hs_max_data_rate)
max_data_rate = card->hs_max_data_rate;
}
else if (max_data_rate > card->max_data_rate)
{
max_data_rate = card->max_data_rate;
}
mmcsd_set_clock(host, max_data_rate);
/*switch bus width*/
if ((host->flags & MMCSD_BUSWIDTH_4) &&
(card->scr.sd_bus_widths & SD_SCR_BUS_WIDTH_4))
{
err = mmcsd_app_set_bus_width(card, MMCSD_BUS_WIDTH_4);
if (err)
goto err1;
mmcsd_set_bus_width(host, MMCSD_BUS_WIDTH_4);
}
host->card = card;
return 0;
err1:
vPortFree(card);
err:
return err;
}
/*
* Starting point for SD card init.
*/
int32_t init_sd(struct mmcsd_host *host, uint32_t ocr)
{
int32_t err;
uint32_t current_ocr;
/*
* We need to get OCR a different way for SPI.
*/
if (controller_is_spi(host))
{
mmcsd_go_idle(host);
err = mmcsd_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
if (ocr & VDD_165_195)
{
TRACE_INFO(" SD card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.");
ocr &= ~VDD_165_195;
}
current_ocr = mmcsd_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!current_ocr)
{
err = -1;
goto err;
}
/*
* Detect and init the card.
*/
err = mmcsd_sd_init_card(host, current_ocr);
if (err)
goto err;
mmcsd_host_unlock(host);
mmcsd_host_lock(host);
return 0;
err:
TRACE_DEBUG("init SD card failed!");
return err;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,196 @@
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "board.h"
#include "sfud.h"
#include "sysinfo.h"
#include "sfud_data.h"
#include "pwm.h"
#ifndef WITH_MVVM
SfudData_t sfud_data_saved ={0};
#define SFUD_DATA_FLASH_OFFSET 0x1fff000 //32Mflash
SfudMileData_t sfud_mile_data ={0};
static uint32_t save_addr = 0;
static uint16_t User_data_index = 0;
#define SFUD_MILE_DATA_FLASH_OFFSET 0xffe000//0xffe000
#define MILE_FLASH_OFFSET 0x1000
/*
void read_mileage(uint32_t trip_data,uint32_t total_data)
{
uint32_t i;
sfud_flash *sflash = sfud_get_device(0);
save_addr = SFUD_MILE_DATA_FLASH_OFFSET;
sfud_read(sflash, save_addr, sizeof(SfudMileData_t), (void*)&sfud_mile_data);
//生产时初始化为0
if(sfud_mile_data.TOTAL_mileage == 0XFFFFFFFF)
{
sfud_mile_data.TOTAL_mileage = 0;
sfud_mile_data.TRIP_mileage = 0;
User_data_index = 0;
sfud_write(sflash,save_addr,sizeof(SfudMileData_t),(void*)&sfud_mile_data);
printf("Init mileage\r\n");
}
else
{
for(i=0;i<256;i++) //数据区长度4k
{
sfud_read(sflash, save_addr, sizeof(SfudMileData_t), (void*)&sfud_mile_data);
if(sfud_mile_data.TOTAL_mileage == 0XFFFFFFFF)
{
break;
}
save_addr += 8;
}
save_addr -= 8;
sfud_read(sflash, save_addr, sizeof(SfudMileData_t), (void*)&sfud_mile_data);
if(sfud_mile_data.TRIP_mileage > sfud_mile_data.TOTAL_mileage)
sfud_mile_data.TRIP_mileage = sfud_mile_data.TOTAL_mileage;
Set_sys_trip_mileage(sfud_mile_data.TRIP_mileage);
Set_sys_total_mileage(sfud_mile_data.TOTAL_mileage);
User_data_index = i;
printf("Read mileage\r\n");
}
}
// void ReadMileageData(void){
// SfudMileData_t flash_data = {0};
// sfud_flash *sflash = sfud_get_device(0);
// if(sfud_read(sflash, SFUD_MILE_DATA_FLASH_OFFSET, sizeof(SfudMileData_t), (void*)&flash_data)==SFUD_SUCCESS){
// printf("sfud_read >>>>>>>>>>>>>>>>> OK .add = %08X , %08X , %08X .\r\n",SFUD_MILE_DATA_FLASH_OFFSET,flash_data.TOTAL_mileage,flash_data.TRIP_mileage);
// }
// }
void SaveMileageData(uint32_t trip_data,uint32_t total_data){
SfudMileData_t flash_data = {0};
sfud_flash *sflash = sfud_get_device(0);
uint8_t buff[sizeof(SfudMileData_t)];//定义buff用于保存结构体
memset(buff,0,sizeof(buff));//将buff内存清零
for(uint8_t i = 0;i<8;i++){
if(User_data_index < 255)
{
User_data_index++;
save_addr += 8;
}else{
User_data_index = 0;
save_addr = SFUD_MILE_DATA_FLASH_OFFSET;
sfud_erase(sflash, save_addr, MILE_FLASH_OFFSET);
printf("erase data\r\n");
}
((SfudMileData_t *)buff)->TRIP_mileage = trip_data;
((SfudMileData_t *)buff)->TOTAL_mileage = total_data;
printf("User_data_index = %d ,save_addr =%08X .\r\n",User_data_index,save_addr);
if (sfud_write(sflash,save_addr,sizeof(buff),buff)!=SFUD_SUCCESS){
printf("i = %d .save sfud mile error.\r\n",i);
}else{
break;
}
}
}
*/
void Classify_SaveDataToFlash(SfudData_t user_data,uint8_t type){
uint8_t buff[sizeof(SfudData_t)];//定义buff用于保存结构体
memset(buff,0,sizeof(buff));//将buff内存清零
switch(type){
case 0:
((SfudData_t *)buff)->language = user_data.language;
break;
case 1:
((SfudData_t *)buff)->theme = user_data.theme;
break;
case 2:
((SfudData_t *)buff)->theme_state = user_data.theme_state;
break;
case 3:
((SfudData_t *)buff)->light_value = user_data.light_value;
break;
case 4:
((SfudData_t *)buff)->bt_on_off = user_data.bt_on_off;
break;
case 5:
strcpy(((SfudData_t *)buff)->f_mac_address,user_data.f_mac_address);
break;
case 6:
strcpy(((SfudData_t *)buff)->r_mac_address,user_data.r_mac_address);
break;
default:
break;
}
sfud_flash *sflash = sfud_get_device(0);
if (sfud_erase_write(sflash,SFUD_DATA_FLASH_OFFSET,sizeof(buff),buff)!=SFUD_SUCCESS){
printf("sfud data write error.\r\n");
}
}
void SaveDataToFlash(SfudData_t user_data){
uint8_t buff[sizeof(SfudData_t)];//定义buff用于保存结构体
memset(buff,0,sizeof(buff));//将buff内存清零
// printf("**************save flash***********************\r\n");
// printf("user_data==> %d-%d-%d-%d-%d,%d",user_data.language,user_data.display_unit,user_data.theme
// ,user_data.theme_state,user_data.light_value,user_data.bt_on_off);
// printf("mac:%02X%02X%02X%02X%02X%02X , %02X%02X%02X%02X%02X%02X end\r\n",user_data.f_mac_address[0],user_data.f_mac_address[1],user_data.f_mac_address[2]
// ,user_data.f_mac_address[3],user_data.f_mac_address[4],user_data.f_mac_address[5]
// ,user_data.r_mac_address[0],user_data.r_mac_address[1],user_data.r_mac_address[2]
// ,user_data.r_mac_address[3],user_data.r_mac_address[4],user_data.r_mac_address[5]);
// printf("data:%02X %02X%02X , %02X %02X%02X end\r\n"
// ,user_data.f_mac_address[6],user_data.f_mac_address[7],user_data.f_mac_address[8]
// ,user_data.r_mac_address[6],user_data.r_mac_address[7],user_data.r_mac_address[8]);
// printf("*************************************\r\n");
// printf("mile data: %02X,%02X end\r\n"
// ,user_data.maintenance_mileage,user_data.mileage_flag);
// printf("*************************************\r\n");
if(user_data.light_value==0)
user_data.light_value=1;
else if(user_data.light_value>6)
user_data.light_value=5;
((SfudData_t *)buff)->factory_reset = 1;
((SfudData_t *)buff)->language = user_data.language;
((SfudData_t *)buff)->display_unit = user_data.display_unit;
((SfudData_t *)buff)->theme = user_data.theme;
((SfudData_t *)buff)->theme_state = user_data.theme_state;
((SfudData_t *)buff)->light_value = user_data.light_value;
((SfudData_t *)buff)->bt_on_off = user_data.bt_on_off;
//((SfudData_t *)buff)->trip_uint = user_data.trip_uint;
strcpy(((SfudData_t *)buff)->f_mac_address,user_data.f_mac_address);
strcpy(((SfudData_t *)buff)->r_mac_address,user_data.r_mac_address);
((SfudData_t *)buff)->maintenance_mileage = user_data.maintenance_mileage;
((SfudData_t *)buff)->mileage_flag = user_data.mileage_flag;
((SfudData_t *)buff)->display_mileage = user_data.display_mileage;
((SfudData_t *)buff)->tcs_on_off = user_data.tcs_on_off;
((SfudData_t *)buff)->mile_state = user_data.mile_state;
((SfudData_t *)buff)->uuid_state = user_data.uuid_state;
((SfudData_t *)buff)->tp_state = user_data.tp_state;
sfud_flash *sflash = sfud_get_device(0);
if (sfud_erase_write(sflash,SFUD_DATA_FLASH_OFFSET,sizeof(buff),buff)!=SFUD_SUCCESS){
printf("sfud data write error.\r\n");
}
}
void ReadDataToFlash(void){
sfud_flash *sflash = sfud_get_device(0);
sfud_read(sflash, SFUD_DATA_FLASH_OFFSET, sizeof(SfudData_t), (void*)&sfud_data_saved);
}
SfudData_t* userData_getSfudSaved(void)
{
/*printf("userData_getSfudSaved: %d-%d-%d-%d-%d-%d \r\n",sfud_data_saved.language,
sfud_data_saved.display_unit,sfud_data_saved.theme,sfud_data_saved.theme_state,
sfud_data_saved.light_value,sfud_data_saved.bt_on_off);*/
return &sfud_data_saved;
}
#endif /*WITH_MVVM*/

View File

@ -0,0 +1,152 @@
#include <string.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "errno.h"
#define MAX_SPI_DEVICE_NUM 2
static struct spi_slave *spi_devs[MAX_SPI_DEVICE_NUM] = {NULL};
static int spi_devices_count = 0;
int spi_add_slave(struct spi_slave *slave)
{
if (spi_devices_count >= MAX_SPI_DEVICE_NUM)
return -1;
spi_devs[spi_devices_count++] = slave;
return 0;
}
struct spi_slave *spi_open(const char *spidev)
{
struct spi_slave *slave;
int i;
for (i = 0; i < spi_devices_count; i++) {
slave = spi_devs[i];
if (!strcmp(slave->name, spidev)) {
slave->open_count++;
if (slave->open_count == 1)
slave->xMutex = xSemaphoreCreateMutex();
return slave;
}
}
return NULL;
}
void spi_close(struct spi_slave *slave)
{
if (slave && --slave->open_count == 0)
vSemaphoreDelete(slave->xMutex);
}
int spi_send_then_recv(struct spi_slave *slave, const void *send_buf,
size_t send_length, void *recv_buf,
size_t recv_length)
{
int result;
struct spi_message message = {0};
/* send data */
message.send_buf = send_buf;
message.recv_buf = NULL;
message.length = send_length;
message.cs_take = 1;
message.cs_release = 0;
message.next = NULL;
result = slave->xfer(slave, &message);
if (result < 0)
{
result = -EIO;
goto __exit;
}
/* recv data */
message.send_buf = NULL;
message.recv_buf = recv_buf;
message.length = recv_length;
message.cs_take = 0;
message.cs_release = 1;
message.next = NULL;
result = slave->xfer(slave, &message);
if (result < 0)
{
result = -EIO;
goto __exit;
}
result = ENOERR;
__exit:
return result;
}
int spi_transfer(struct spi_slave *slave, const void *send_buf,
void *recv_buf, size_t length)
{
int result;
struct spi_message message = {0};
configASSERT(slave != NULL);
xSemaphoreTake(slave->xMutex, portMAX_DELAY);
/* initial message */
message.send_buf = send_buf;
message.recv_buf = recv_buf;
message.length = length;
message.cs_take = 1;
message.cs_release = 1;
message.next = NULL;
/* transfer message */
result = slave->xfer(slave, &message);
if (result < 0)
{
result = -EIO;
goto __exit;
}
__exit:
xSemaphoreGive(slave->xMutex);
return result;
}
int spi_configure(struct spi_slave *slave, struct spi_configuration *cfg)
{
int ret;
configASSERT(slave && cfg);
xSemaphoreTake(slave->xMutex, portMAX_DELAY);
ret = slave->configure(slave, cfg);
xSemaphoreGive(slave->xMutex);
return ret;
}
int spi_recv(struct spi_slave *slave, void *recv_buf, size_t length)
{
return spi_transfer(slave, NULL, recv_buf, length);
}
int spi_send(struct spi_slave *slave, const void *send_buf, size_t length)
{
return spi_transfer(slave, send_buf, NULL, length);
}
void spi_init(void)
{
ecspi_init();
dwspi_init();
}

View File

@ -0,0 +1,366 @@
#include "chip.h"
void vSysctlConfigure(uint32_t regoffset, uint32_t bitoffset, uint32_t mask, uint32_t val)
{
uint32_t tmp = readl(REGS_SYSCTL_BASE + regoffset);
tmp &= ~(mask << bitoffset);
tmp |= val << bitoffset;
writel(tmp, REGS_SYSCTL_BASE + regoffset);
}
void sys_soft_reset (int reset_dev)
{
unsigned int mask;
volatile unsigned int* reg;
switch (reset_dev)
{
case softreset_imc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 31;
break;
case softreset_usbphy:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 30;
break;
case softreset_ddr:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 29;
break;
case softreset_icu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 28;
break;
case softreset_aes:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 27;
break;
case softreset_rcrt:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 26;
break;
case softreset_adc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 25;
break;
case softreset_rtc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 24;
break;
case softreset_i2s:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 23;
break;
case softreset_wdt:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 22;
break;
case softreset_pwm:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 21;
break;
case softreset_timer3:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 20;
break;
case softreset_timer2:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 19;
break;
case softreset_timer1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 18;
break;
case softreset_timer0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 17;
break;
case softreset_gpio:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 16;
break;
case softreset_uart3:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 15;
break;
case softreset_uart2:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 14;
break;
case softreset_uart1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 13;
break;
case softreset_uart0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 12;
break;
case softreset_i2c1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 11;
break;
case softreset_i2c:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 10;
break;
case softreset_ssp1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 9;
break;
case softreset_ssp:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 8;
break;
case softreset_pxp:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 7;
break;
case softreset_gpu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 6;
break;
case softreset_itu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 5;
break;
case softreset_card:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 4;
break;
case softreset_usb:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 3;
break;
case softreset_jpeg:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 2;
break;
case softreset_dma:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 1;
break;
case softreset_lcd:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 0;
break;
case softreset_can0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 0;
break;
case softreset_can1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 1;
break;
case softreset_h2xdma:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 2;
break;
case softreset_h2xusb:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 3;
break;
case softreset_mipi:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 4;
break;
case softreset_usb_utmi:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 5;
break;
case softreset_vpu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 6;
break;
case softreset_i2s1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 8;
break;
default:
printf ("illegal reset device (%d)\n", reset_dev);
return;
}
portENTER_CRITICAL();
*reg &= ~(1 << mask);
udelay (100);
*reg |= (1 << mask);
portEXIT_CRITICAL();
}
void sys_soft_reset_from_isr (int reset_dev)
{
unsigned int mask;
volatile unsigned int* reg;
switch (reset_dev)
{
case softreset_imc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 31;
break;
case softreset_usbphy:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 30;
break;
case softreset_ddr:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 29;
break;
case softreset_icu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 28;
break;
case softreset_aes:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 27;
break;
case softreset_rcrt:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 26;
break;
case softreset_adc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 25;
break;
case softreset_rtc:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 24;
break;
case softreset_i2s:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 23;
break;
case softreset_wdt:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 22;
break;
case softreset_pwm:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 21;
break;
case softreset_timer3:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 20;
break;
case softreset_timer2:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 19;
break;
case softreset_timer1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 18;
break;
case softreset_timer0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 17;
break;
case softreset_gpio:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 16;
break;
case softreset_uart3:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 15;
break;
case softreset_uart2:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 14;
break;
case softreset_uart1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 13;
break;
case softreset_uart0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 12;
break;
case softreset_i2c1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 11;
break;
case softreset_i2c:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 10;
break;
case softreset_ssp1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 9;
break;
case softreset_ssp:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 8;
break;
case softreset_pxp:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 7;
break;
case softreset_gpu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 6;
break;
case softreset_itu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 5;
break;
case softreset_card:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 4;
break;
case softreset_usb:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 3;
break;
case softreset_jpeg:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 2;
break;
case softreset_dma:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 1;
break;
case softreset_lcd:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x5c));
mask = 0;
break;
case softreset_can0:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 0;
break;
case softreset_can1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 1;
break;
case softreset_h2xdma:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 2;
break;
case softreset_h2xusb:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 3;
break;
case softreset_mipi:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 4;
break;
case softreset_usb_utmi:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 5;
break;
case softreset_vpu:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 6;
break;
case softreset_i2s1:
reg = ((volatile unsigned int *)(REGS_SYSCTL_BASE+0x60));
mask = 8;
break;
default:
printf ("illegal reset device (%d)\n", reset_dev);
return;
}
*reg &= ~(1 << mask);
udelay (100);
*reg |= (1 << mask);
}

View File

@ -0,0 +1,148 @@
#include <stdint.h>
#include "amt630hv100.h"
#include "clock.h"
#include "timer.h"
#include "chip.h"
#define TIMER_PERIOD 1000000
#define TIMER_LOAD_VAL 0xFFFFFFFF
/* For convenience the high frequency timer increments a variable that is then
used as the time base for the run time stats. */
volatile uint32_t ulHighFrequencyTimerCounts = 0;
void vTimerInit(uint32_t id, int32_t inten, int32_t periodic, uint32_t rate)
{
uint32_t regbase = REGS_TIMER_BASE;
uint32_t ctrl = TIMER_CTRL_INT_MASK;
if (inten) ctrl &= ~TIMER_CTRL_INT_MASK;
if (periodic) ctrl |= TIMER_CTRL_PERIODIC;
writel(0, regbase + TIMER_CTRL(id));
writel(ulClkGetRate(CLK_TIMER) / rate, regbase + TIMER_LOAD_COUNT(id));
writel(ctrl, regbase + TIMER_CTRL(id));
}
void vTimerEnable(uint32_t id)
{
uint32_t regbase = REGS_TIMER_BASE;
writel(readl(regbase + TIMER_CTRL(id)) | TIMER_CTRL_ENABLE, regbase + TIMER_CTRL(id));
}
void vTimerDisable(uint32_t id)
{
uint32_t regbase = REGS_TIMER_BASE;
writel(readl(regbase + TIMER_CTRL(id)) & ~TIMER_CTRL_ENABLE, regbase + TIMER_CTRL(id));
}
void vTimerClrInt(uint32_t id)
{
uint32_t regbase = REGS_TIMER_BASE;
volatile uint32_t val;
val = readl(regbase + TIMER_EOI(id));
}
/*-----------------------------------------------------------*/
static void prvRunTimer_Handler( void *para )
{
vTimerClrInt((uint32_t)para);
ulHighFrequencyTimerCounts++;
}
void vInitialiseTimerForRunTimeState(void)
{
vTimerInit(TIMER_ID1, 1, 1, configTICK_RATE_HZ * 20);
request_irq(TIMER1_IRQn, 1, prvRunTimer_Handler, (void*)TIMER_ID1);
vTimerEnable(TIMER_ID1);
}
uint32_t ulGetRunTimeCountValue(void)
{
uint32_t regbase = REGS_TIMER_BASE;
return TIMER_LOAD_VAL - readl(regbase + TIMER_CURRENT_VAL(TIMER_ID1));
}
void vInitialiseTimerForDelay(void)
{
uint32_t regbase = REGS_TIMER_BASE;
writel(0, regbase + TIMER_CTRL(TIMER_ID2));
writel(TIMER_LOAD_VAL, regbase + TIMER_LOAD_COUNT(TIMER_ID2));
writel(TIMER_CTRL_INT_MASK | TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE,
regbase + TIMER_CTRL(TIMER_ID2));
}
void udelay(uint32_t usec)
{
uint32_t regbase = REGS_TIMER_BASE;
long tmo = usec * (ulClkGetRate(CLK_TIMER) / TIMER_PERIOD);
unsigned long last = readl(regbase + TIMER_CURRENT_VAL(TIMER_ID2));
unsigned long now;
while (tmo > 0) {
now = readl(regbase + TIMER_CURRENT_VAL(TIMER_ID2));
if (last >= now)
tmo -= last - now;
else
tmo -= last + (TIMER_LOAD_VAL - now);
last = now;
}
}
void mdelay(uint32_t msec)
{
udelay(msec * 1000);
}
void vTimerUdelay(uint32_t usec)
{
udelay(usec);
}
void vTimerMdelay(uint32_t msec)
{
vTimerUdelay(msec * 1000);
}
static unsigned long long timestamp;
static unsigned long lastdec;
uint32_t get_timer_masked(void)
{
uint32_t regbase = REGS_TIMER_BASE;
unsigned long now = readl(regbase + TIMER_CURRENT_VAL(TIMER_ID2));
if (lastdec >= now) { /* normal mode (non roll) */
/* normal mode */
timestamp += lastdec - now; /* move stamp fordward with absoulte diff ticks */
} else { /* we have overflow of the count down timer */
/* nts = ts + ld + (TLV - now)
* ts=old stamp, ld=time that passed before passing through -1
* (TLV-now) amount of time after passing though -1
* nts = new "advancing time stamp"...it could also roll and cause problems.
*/
timestamp += lastdec + (TIMER_LOAD_VAL - now);
}
lastdec = now;
return timestamp / (ulClkGetRate(CLK_TIMER) / TIMER_PERIOD);
}
uint32_t get_timer(uint32_t base)
{
uint32_t now = get_timer_masked();
if (now >= base) {
return now - base;
} else {
return 0xffffffff - base + now;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,538 @@
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#include "serial.h"
#include "sysinfo.h"
#include "data_port.h"
#include "moto_config.h"
uint8_t bt_upgrade_flag = 0;
extern uint8_t wifi_ota_state;
extern uint8_t wifi_file_state;
#define BUFFER_LEN 512//26
/************************* 蓝牙协议 ****************************/
#define UUP_PACKET_SIZE 128
#define UUP_MAX_FRAME_LEN (UUP_PACKET_SIZE + 16)
#define UUP_PACKET_A27_SIZE 4096
#define UUP_MAX_FRAME_A27_LEN (UUP_PACKET_A27_SIZE + 16)
#define UUP_RX_FRAME_NUM 16
#define BYTESPERPAGE 256
#define PAGESPERSECTORS 16
#define UUP_BUF_SIZE (BYTESPERPAGE * PAGESPERSECTORS)
static unsigned char uup_rx_buf[UUP_RX_FRAME_NUM][4096];
static unsigned char *uup_rx_ptr;
static int uup_rx_rev_len = 0;
static int uup_rx_head = 0;
static int uup_rx_tail = 0;
static int uup_rx_state = 0;
static int upgrade_state = 0;
static int uup_rx_data_len = 0;
int timeout = 0;
unsigned char uart_time_request[13] = {0};
static uint8_t uart_tx_flag = 0;
static uint32_t send_idletick = 0;//发送时间检测
extern uint8_t flash_flag;//检测是否收到device信息
uint8_t uart_state = 0;//串口状态
uint8_t uart_state_flag = 0;//串口当前状态标识
uint8_t bt_communication_heartbeat = 10;//时间检测
uint8_t uart_flag =0;//串口变化标志
void TXCMD_Other_Set(unsigned char time_buffer[],uint8_t len);
void UART3_Modification_Type(void);
void UART3_Type_regression(void);
//配置为GPIO
void UART3_Modification_Type(void){
uart_state_flag = 1;
vSysctlConfigure(SYS_PAD_CTRL02, 24, 0xf, 0);
}
//配置为UART 正常接收数据
void UART3_Type_regression(void){
if(uart_state_flag){//防止多次操作串口
vSysctlConfigure(SYS_PAD_CTRL02, 24, 0xf, 5);
uart_state_flag = 0;
}
}
extern uint8_t data_error_flag;
static void protocol_uart_tx_thread(void *param)
{
UartPort_t *uap = param;
uint8_t uarttx[32] = "hello, i am amt630h";
uint8_t heartbeat_data[32] = {0};
//char* cmd = "AT\r\n";
for (;;)
{
if(uart_tx_flag){
if (uart_time_request[0] != 0 && uart_time_request[0] == 0xAA)
{
// printf("UART3 tx-->>> ");
for (uint8_t i = 0; i < uart_tx_flag; i++)
{
// printf("%02x ", uart_time_request[i]);
uarttx[i] = uart_time_request[i];
}
// printf("\r\n");
iUartWrite(uap, uarttx, uart_tx_flag, pdMS_TO_TICKS(100)); // 发送数据
}else if(uart_time_request[0] != 0 && uart_time_request[0] == 0x7E){
// printf("UART3 tx-->>> ");
for (uint8_t i = 0; i < 8; i++)
{
// printf("%02x ", uart_time_request[i]);
uarttx[i] = uart_time_request[i];
}
// printf("\r\n");
iUartWrite(uap, uarttx, 8, pdMS_TO_TICKS(100)); // 发送数据
uart_time_request[0] = 0;
}
uart_tx_flag = 0;
}
else{
//1s调用一次
if(xTaskGetTickCount() - send_idletick > configTICK_RATE_HZ){
//确认仪表完成自检 + 确认仪表收到里程信息 + 确认数据无误(小计总计大小问题)
if((!flash_flag) && (!data_error_flag)){
//确认蓝牙不处于升级 + 确认630不处于升级 + 确认串口收到通讯信息
if(Get_sys_bt_upgrade() == 0 && Get_sys_upgrade_Flag() == 0 && uart_state){//蓝牙升级 不发送数据 630升级 不发送数据
#ifndef DATA_CAN
SendDataToBT(heartbeat_data);
// printf("tx>");
// for (uint8_t i = 0; i < 6; i++)
// {
// printf("%02X ", heartbeat_data[i]);
// }
// printf("\r\n");
// printf("tx .\r\n");
iUartWrite(uap, heartbeat_data, 6, pdMS_TO_TICKS(20));
#else
SendDataToBT(heartbeat_data);
//确认数据无误(小计总计大小问题)
if(data_error_flag == 0){
// printf("tx .\r\n");
iUartWrite(uap, heartbeat_data, 21, pdMS_TO_TICKS(20));
}
#endif
}
}
send_idletick = xTaskGetTickCount();
}
}
vTaskDelay(30);
}
}
//计算数据校验和
static uint16_t calculate_cumulative_sum(uint8_t *buf)
{
uint32_t sum = 0;
uint8_t len, i;
len = buf[1];
for (i = 2; i < len + 1; i++)
{
sum += buf[i];
}
sum &= 0xFF;
return sum;
}
static void protocol_uart_rx_thread(void *param)
{
UartPort_t *uap = xUartOpen(UART_BT_PORT);
uint8_t uartrx[BUFFER_LEN];
int len;
int str_len = 0;
int i,j;
uint8_t tlv_data_value[128]={0};
int data_len =0;
uint8_t equipment_data = 0;
uint8_t mac_data = 0;
uint8_t bt_state = 0;
uint8_t flag = 0;
uint32_t sum = 0;
// static uint32_t uartidletick = 0;
bt_communication_heartbeat = 10;
if (!uap) {
printf("open uart %d fail.\n", UART_BT_PORT);
vTaskDelete(NULL);
return;
}
printf("MXC => UART3 115200.\n");
vUartInit(uap, 115200, 0);
if (xTaskCreate(protocol_uart_tx_thread, "uartsend", configMINIMAL_STACK_SIZE, uap,
configMAX_PRIORITIES / 3, NULL) != pdPASS) {
printf("create uart tx demo task fail.\n");
vTaskDelete(NULL);
return;
}
extern uint8_t system_flag;
system_flag++;
if(system_flag>=2)
Send_software_version();
for (;;) {
len = iUartRead(uap, uartrx, BUFFER_LEN, pdMS_TO_TICKS(10));
// if(len){
// printf("uart2>");
// for(i=0;i<len;i++){
// printf("%02X ",uartrx[i]);
// }
// printf("\r\n");
// }
if(Get_sys_power_on_self_test() != 150 ){
for(i=0;i<len;i++){
switch (upgrade_state) {
case 0:
if (uartrx[i] == 0xAA){
upgrade_state++;
tlv_data_value[str_len] = uartrx[i];
str_len++;
equipment_data = 0;
mac_data = 0;
}else
str_len = 0;
break;
case 1:
if (uartrx[i] == 0x11 || uartrx[i] == 0x19){
tlv_data_value[str_len] = uartrx[i];
str_len++;
data_len = uartrx[i];
upgrade_state++;
}else if(uartrx[i] == 0x1b){
tlv_data_value[str_len] = uartrx[i];
str_len++;
data_len = uartrx[i];
upgrade_state++;
mac_data = 1;
}else if(uartrx[i] == 0x55){
tlv_data_value[str_len] = uartrx[i];
str_len++;
data_len = uartrx[i];
upgrade_state++;
equipment_data = 1;
}else if(uartrx[i] == 0x05){
tlv_data_value[str_len] = uartrx[i];
str_len++;
data_len = uartrx[i];
upgrade_state++;
bt_state = 1;
}else{
upgrade_state = 0;
str_len = 0;
}
break;
case 2:
tlv_data_value[str_len] = uartrx[i];
if(uartrx[i] == 0x03){
str_len++;
upgrade_state++;
}else if(equipment_data && uartrx[i] == 0x01){
str_len++;
upgrade_state++;
}else{
upgrade_state = 0;
str_len = 0;
}
break;
case 3:
tlv_data_value[str_len] = uartrx[i];
if(uartrx[i] == 0x50){
str_len++;
upgrade_state++;
}else if(equipment_data && uartrx[i] == 0x1c){
str_len++;
upgrade_state++;
}else if(mac_data && uartrx[i] == 0x51){
str_len++;
upgrade_state++;
}else if(bt_state && uartrx[i] == 0x54){
str_len++;
upgrade_state++;
}else{
upgrade_state = 0;
str_len = 0;
}
break;
case 4:
tlv_data_value[str_len] = uartrx[i];
if(uartrx[i] == 0x0D ||uartrx[i] == 0x15){
str_len++;
upgrade_state++;
}else if(equipment_data){
str_len++;
upgrade_state++;
}else if(mac_data && uartrx[i] == 0x17){
str_len++;
upgrade_state++;
}else if(bt_state && uartrx[i] == 0x1){
str_len++;
upgrade_state++;
}else{
upgrade_state = 0;
str_len = 0;
}
break;
case 5:
tlv_data_value[str_len] = uartrx[i];
str_len++;
if(str_len > data_len+1){
upgrade_state++;
}
break;
case 6:
if (uartrx[i] == 0x0a){
sum = calculate_cumulative_sum(tlv_data_value);
if(sum != tlv_data_value[str_len-1]){
printf("CHECKSUM ERROR ,sum = %x ,tlv_data_value=%x \n",sum,tlv_data_value[str_len-1]);
if(flag <5){
flag ++;
printf("str_len=%d data_len =%d ,",str_len,data_len);
for(j=0;j<str_len;j++){
printf("%2x ",tlv_data_value[j]);
}
printf("\n");
}
}else{
uart_state = 3;
if(equipment_data){
device_data_analysis(tlv_data_value);
equipment_data = 0;
}else if(mac_data){
// printf("tire.\r\n");
// printf("\r\n------------------------------.\r\n");
// for(j=0;j<str_len;j++){
// printf("%02x ",tlv_data_value[j]);
// }
// tire_pressure_data_analysis(tlv_data_value);
mac_data = 0;
}else if(bt_state){//存在呼吸 若呼吸不存在
bt_communication_heartbeat = 3;
// printf("bt_state = %d .\r\n",tlv_data_value[5]);
bt_state = 0;
}else{
data_analysis(tlv_data_value);
}
}
}else{
printf("\nuartrx[i] = %x .\n",uartrx[i]);
}
upgrade_state=0;
str_len = 0;
data_len = 0;
break;
}
#if 1
switch (uup_rx_state) {
case 0:
if (uartrx[i] == 0x7e)
uup_rx_state++;
break;
case 1:
if (uartrx[i] == 0x05)
uup_rx_state++;
else
uup_rx_state = 0;
break;
case 2:
if (uartrx[i] == 0x02)
uup_rx_state++;
else
uup_rx_state = 0;
break;
case 3:
if (uartrx[i] == 0x03)
uup_rx_state++;
else
uup_rx_state = 0;
break;
case 4:
if (uartrx[i] == 0x01)
uup_rx_state++;
else
uup_rx_state = 0;
break;
case 5:
if (uartrx[i] == Get_sys_softwar_host())
uup_rx_state++;
else
uup_rx_state = 0;
break;
case 6:
if (uartrx[i] == Get_sys_softwar_order()){
if(Get_sys_veer_velocity()){
printf("error ota sj. Speed present\n");
Send_ota_state(1);
}else{
printf("enter ota sj.\n");
Set_sys_power_on_self_test(150);
Set_sys_upgrade_Flag(1);//进入ota界面
Send_ota_state(0);
if(timeout)
timeout=0;
}
}
uup_rx_state = 0;
break;
}
#endif
}
#if 1
}else{//升级逻辑
timeout ++;
uart_state = 3;
for (i = 0; i < len; i++) {
switch (uup_rx_state) {
case 0:
if (uartrx[i] == 0x55) {
uup_rx_state++;
uup_rx_rev_len = 0;
uup_rx_ptr = &uup_rx_buf[uup_rx_head][0];
}
break;
case 1:
if (uartrx[i] == 0x81)
uup_rx_state++;
else
uup_rx_state = 0;
*uup_rx_ptr++ = uartrx[i];
break;
case 2:
if (uartrx[i] == 0xc6)
uup_rx_state++;
else
uup_rx_state = 0;
*uup_rx_ptr++ = uartrx[i];
break;
case 3:
uup_rx_data_len = uartrx[i];
//uup_rx_data_len = (uartrx[i]<<8);
uup_rx_state++;
*uup_rx_ptr++ = uartrx[i];
break;
case 4:
//uup_rx_data_len |= uartrx[i];
uup_rx_data_len = (uartrx[i]<<8) | uup_rx_data_len;
if((uup_rx_data_len > UUP_PACKET_A27_SIZE + 2)) { //4096 + 2
printf("Invalid uup_rx_data_len %d\n", uup_rx_data_len);
uup_rx_state = 0;
} else {
uup_rx_state++;
*uup_rx_ptr++ = uartrx[i];
}
break;
case 5:
*uup_rx_ptr++ = uartrx[i];
if (++uup_rx_rev_len == uup_rx_data_len)
uup_rx_state++;
break;
case 6:
*uup_rx_ptr++ = uartrx[i];
uup_rx_head = (uup_rx_head + 1) % UUP_RX_FRAME_NUM;
uup_rx_state = 0;
break;
}
}
if (uup_rx_tail != uup_rx_head) {
unsigned char *buf;
unsigned char checksum = 0;
buf = &uup_rx_buf[uup_rx_tail][0];
//len = (buf[2]<<8)+buf[3];
len = buf[2];
len = buf[3]<<8 | len;
for (i = 0; i < len + 4; i++)
checksum ^= buf[i];
//printf("checksum = 0x%x , buf[len + 4] = 0x%x\n",checksum,buf[len + 4]);
if (checksum == buf[len + 4]) {
timeout =0;
uup_ota_update(uap, buf + 4, len);
} else {
printf("rev frame checksum err.\r\n");
}
uup_rx_tail = (uup_rx_tail + 1) % UUP_RX_FRAME_NUM;
}
if(timeout >= 3000){//超时退出升级 大约40s
printf("exit ota sj.\n");
Set_sys_power_on_self_test(100);
Set_sys_upgrade_Flag(2);
printf("UART3_Type_regression .\n");
extern void UART3_Type_regression(void);
UART3_Type_regression();
timeout = 0;
}
#endif
}
//收不到蓝牙心跳信息 复位串口
if(uart_flag==1){
uart_flag = 2;
printf("reset uart.\r\n");
UART3_Modification_Type();
vUartClose(uap);
uap = xUartOpen(UART_BT_PORT);
UART3_Type_regression();
vUartInit(uap,115200,0);
bt_communication_heartbeat = 10;
}
}
}
int uart_communicates_with_bt(void)
{
/* Create a task to process uart rx data */
if (xTaskCreate(protocol_uart_rx_thread, "uart3rx", configMINIMAL_STACK_SIZE, NULL,
configMAX_PRIORITIES / 3, NULL) != pdPASS)
{
//printf("create uart rx demo task fail.\n");
return -1;
}
return 0;
}
void TXCMD_Other_Set(unsigned char time_buffer[],uint8_t len)
{
if(uart_tx_flag == 0)
uart_tx_flag = len;
for (uint8_t i = 0; i < len; i++)
{
uart_time_request[i] = time_buffer[i];
}
}

View File

@ -0,0 +1,840 @@
#include "usb_os_adapter.h"
#include "trace.h"
#include <asm/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "hcd.h"
#include "ark_dwc2.h"
#include "semphr.h"
#include "timers.h"
#include "usbroothubdes.h"
#include "sysctl.h"
#include "board.h"
struct dwc2_host_data {
struct dwc2_hsotg *host;
struct usb_hcd hcd;
enum usb_device_speed host_speed;
int root_hub_devnum;
struct usb_host_endpoint hep_in[16];
struct usb_host_endpoint hep_out[16];
struct hc_driver *dw2_hc_driver;
List_t free_urb_list;
spinlock_t lock;
};
struct urb_user_ctx {
struct usb_device* dev;
struct usb_host_endpoint *hep;
unsigned long pipe;
void *buffer;
int length;
int num_iso_packets;
int interval;
int alloc_flag;
};
int dwc2_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags);
int dwc2_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status);
extern int dwc2_driver_init(struct dwc2_hsotg **dev, struct usb_hcd *hcd);
extern int dwc2_driver_uninit(struct dwc2_hsotg *hsotg);
extern struct hc_driver* dwc2_get_driver();
static int dwc_otg_submit_rh_msg(struct usb_device *dev,
unsigned long pipe, void *buffer, int txlen,
struct devrequest *cmd);
struct dwc2_host_data dwc2_host;
struct urb_user_ctx urb_user_handle[8];
void *alloc_urb_user_ctx()
{
int i;
struct urb_user_ctx* handle = NULL;
for (i = 0; i < 8; i++) {
handle = &urb_user_handle[i];
if (handle->alloc_flag == 0) {
handle->alloc_flag = 1;
break;
}
}
return handle;
}
void free_urb_user_ctx(void *ctx)
{
struct urb_user_ctx* handle = (struct urb_user_ctx*)ctx;
handle->alloc_flag = 0;
}
static void dwc2_host_complete_urb(struct urb *urb)
{
if (urb->status != 0) {
printf("#urb transfer err:%d\r\n", urb->status);
}
urb->dev->status &= ~USB_ST_NOT_PROC;
urb->dev->act_len = urb->actual_length;
//printf("actual_length:%d\r\n", urb->actual_length);
xQueueSendFromISR((QueueHandle_t)urb->queueHandle, NULL, 0);
}
static void usb_urb_release(struct urb *urb)
{
if (NULL == urb)
return;
spin_lock(&dwc2_host.lock);
list_add_tail(&urb->urb_list, &dwc2_host.free_urb_list);
spin_unlock(&dwc2_host.lock);
}
static struct urb *usb_urb_alloc(struct dwc2_host_data *ctx,
int iso_desc_count,
gfp_t mem_flags)
{
struct urb *urb = NULL;
QueueHandle_t complete;
u32 size = sizeof(*urb) + iso_desc_count *
sizeof(struct usb_iso_packet_descriptor);
ListItem_t *pxListItem = NULL;
int found = 0, flags;
spin_lock_irqsave(&ctx->lock, flags);
list_for_each_entry(pxListItem, urb, &ctx->free_urb_list) {
if (urb->number_of_packets == iso_desc_count) {
found = 1;
break;
}
}
if (found) {
void *queueHandle = urb->queueHandle;
list_del_init(&urb->urb_list);
spin_unlock_irqrestore(&ctx->lock, flags);
memset(urb, 0, sizeof(struct urb));
urb->number_of_packets = iso_desc_count;
INIT_LIST_ITEM(&urb->urb_list);
listSET_LIST_ITEM_OWNER(&urb->urb_list, urb);
urb->queueHandle = queueHandle;
xQueueReset(urb->queueHandle);
return urb;
}
spin_unlock_irqrestore(&ctx->lock, flags);
urb = (struct urb *)kzalloc(size, mem_flags);
if (urb) {
urb->number_of_packets = iso_desc_count;
INIT_LIST_ITEM(&urb->urb_list);
listSET_LIST_ITEM_OWNER(&urb->urb_list, urb);
complete = xQueueCreate(1, 0);
urb->queueHandle = complete;
}
return urb;
}
static void construct_urb(struct urb *urb, struct usb_host_endpoint *hep,
struct usb_device *dev, int endpoint_type,
unsigned long pipe, void *buffer, int len,
struct devrequest *setup, int interval)
{
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
INIT_LIST_HEAD(&hep->urb_list);
#ifndef NO_GNU
INIT_LIST_HEAD(&urb->urb_list);
#else
INIT_LIST_ITEM(&urb->urb_list);
urb->urb_list.pvOwner = (void *)urb;
#endif
urb->ep = hep;
urb->complete = dwc2_host_complete_urb;
urb->status = -EINPROGRESS;
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = buffer;
urb->transfer_dma = (dma_addr_t)buffer;
urb->transfer_buffer_length = len;
urb->setup_packet = (unsigned char *)setup;
urb->setup_dma = (dma_addr_t)setup;
urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
URB_DMA_SG_COMBINED);
urb->transfer_flags |= (is_in ? URB_DIR_IN : URB_DIR_OUT);
urb->ep->desc.wMaxPacketSize =
__cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] :
dev->epmaxpacketout[epnum]);
urb->ep->desc.bmAttributes = endpoint_type;
urb->ep->desc.bEndpointAddress =
(is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum;
urb->ep->desc.bInterval = interval;
}
static int submit_urb(struct usb_hcd *hcd, struct urb *urb)
{
int ret;
ret = dwc2_urb_enqueue(hcd, urb, 0);
if (ret < 0) {
printf("Failed to enqueue URB to controller ret=%d \r\n", ret);
return ret;
}
return ret;
}
static int _dwc2_submit_control_msg(struct dwc2_host_data *host,
struct usb_device *dev, unsigned long pipe,
void *buffer, int len, struct devrequest *setup, int timeout)
{
struct urb *urb = NULL;
struct usb_host_endpoint *hep = NULL;
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
int ret = 0;
if (epnum > 8 || epnum < 0)
return -EINVAL;
if (is_in) {
hep = &host->hep_in[epnum];
} else {
hep = &host->hep_out[epnum];
}
urb = usb_urb_alloc(host, 0, IRQ_NONE);
if (urb == NULL)
return -ENOMEM;
construct_urb(urb, hep, dev, USB_ENDPOINT_XFER_CONTROL,
pipe, buffer, len, setup, 0);
/* Fix speed for non hub-attached devices */
if (!usb_dev_get_parent(dev))
dev->speed = host->host_speed;
//unsigned char *a = urb->setup_packet;
//if (a)
//printf("setup-->%02x %02x %02x %02x %02x %02x %02x %02x\r\n", a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
ret = submit_urb(&host->hcd, urb);
if (ret < 0) {
printf("%s:%d\r\n", __func__, __LINE__);
//dwc2_urb_dequeue(&host->hcd, urb, 0);
//printf("%s:%d\r\n", __func__, __LINE__);
goto exit;
}
ret = xQueueReceive((QueueHandle_t)urb->queueHandle, NULL, timeout);
if (ret != pdTRUE || 0 != urb->status) {//timeout
printf("%s:%d\r\n", __func__, __LINE__);
dwc2_urb_dequeue(&host->hcd, urb, 0);
printf("%s:%d\r\n", __func__, __LINE__);
if (0 != urb->status)
printf("usb control urb error:%d\r\n", urb->status);
ret = urb->status;
} else {
ret = urb->actual_length;
}
exit:
usb_urb_release(urb);
return ret;
}
int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int *actual_length, int timeout)
{
struct urb *urb = NULL;
struct usb_host_endpoint *hep = NULL;
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
int ret = 0;
struct dwc2_host_data *host = &dwc2_host;
if (epnum > 8 || epnum < 0)
return -EINVAL;
if (is_in) {
hep = &host->hep_in[epnum];
} else {
hep = &host->hep_out[epnum];
}
urb = usb_urb_alloc(host, 0, IRQ_NONE);
if (urb == NULL)
return -ENOMEM;
construct_urb(urb, hep, dev, USB_ENDPOINT_XFER_BULK,
pipe, buffer, transfer_len, NULL, 0);
ret = submit_urb(&host->hcd, urb);
if (ret < 0) {
printf("%s:%d\r\n", __func__, __LINE__);
//dwc2_urb_dequeue(&host->hcd, urb, 0);
//printf("%s:%d\r\n", __func__, __LINE__);
goto exit;
}
ret = xQueueReceive((QueueHandle_t)urb->queueHandle, NULL, timeout);
if (ret != pdTRUE || 0 != urb->status) {//timeout
printf("%s:%d\r\n", __func__, __LINE__);
dwc2_urb_dequeue(&host->hcd, urb, 0);
printf("%s:%d\r\n", __func__, __LINE__);
if (actual_length)
*actual_length = 0;
if (0 != urb->status)
printf("usb bulk urb error:%d\r\n", urb->status);
ret = urb->status;
} else {
if (actual_length)
*actual_length = urb->actual_length;
}
exit:
usb_urb_release(urb);
return ret;
}
static int _dwc2_submit_int_msg(struct dwc2_host_data *host, struct usb_device *dev, unsigned long pipe, void *buffer, int len, int interval, int timeout)
{
struct urb *urb = NULL;
struct usb_host_endpoint *hep = NULL;
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
int ret = 0;
if (epnum > 8 || epnum < 0)
return -EINVAL;
if (is_in) {
hep = &host->hep_in[epnum];
} else {
hep = &host->hep_out[epnum];
}
urb = usb_urb_alloc(host, 0, IRQ_NONE);
if (urb == NULL)
return -ENOMEM;
construct_urb(urb, hep, dev, USB_ENDPOINT_XFER_INT,
pipe, buffer, len, NULL, interval);
ret = submit_urb(&host->hcd, urb);
if (ret < 0) {
dwc2_urb_dequeue(&host->hcd, urb, 0);
goto exit;
}
ret = xQueueReceive((QueueHandle_t)urb->queueHandle, NULL, timeout);
if (ret != pdTRUE) {//timeout
dwc2_urb_dequeue(&host->hcd, urb, 0);
}
exit:
usb_urb_release(urb);
return ret;
}
#if 0
static int _dwc2_reset_root_port(struct dwc2_host_data *host,
struct usb_device *dev)
{
mdelay(50);
host->host_speed = USB_SPEED_HIGH;
mdelay((host->host_speed == USB_SPEED_LOW) ? 200 : 50);
return 0;
}
#endif
void usb_reset_endpoint()
{
int i;
struct dwc2_host_data *host = &dwc2_host;
if ((NULL == host->dw2_hc_driver) || (NULL == host->dw2_hc_driver->endpoint_reset))
return;
for (i = 0; i < 16; i++) {
host->dw2_hc_driver->endpoint_reset(&(host->hcd), &host->hep_in[i]);
host->dw2_hc_driver->endpoint_reset(&(host->hcd), &host->hep_out[i]);
}
}
void usb_disable_endpoint()
{
int i;
struct dwc2_host_data *host = &dwc2_host;
if ((NULL == host->dw2_hc_driver) || (NULL == host->dw2_hc_driver->endpoint_disable))
return;
for (i = 0; i < 16; i++) {
host->dw2_hc_driver->endpoint_disable(&(host->hcd), &host->hep_in[i]);
host->dw2_hc_driver->endpoint_disable(&(host->hcd), &host->hep_out[i]);
}
}
void reset_usb_phy()
{
vSysctlConfigure(SYS_SOFT_RST, 30, 1, 0);//phy reset
mdelay(10);
vSysctlConfigure(SYS_SOFT_RST, 30, 1, 1);//phy reset
}
void usb_sysctrl_init()
{
vSysctlConfigure(SYS_ANA_CFG, 8, 0xff, 4);
vSysctlConfigure(SYS_SOFT_RST, 30, 1, 0);//phy reset
vSysctlConfigure(SYS_SOFT_RST, 3, 1, 0);
vSysctlConfigure(SYS_SOFT1_RST, 5, 1, 0);
mdelay(10);
vSysctlConfigure(SYS_SOFT_RST, 30, 1, 1);//phy reset
vSysctlConfigure(SYS_SOFT_RST, 3, 1, 1);
vSysctlConfigure(SYS_SOFT1_RST, 5, 1, 1);
if (!get_usb_mode())
vSysctlConfigure(SYS_ANA_CFG, 24, 3, 1);// usb host
else
vSysctlConfigure(SYS_ANA_CFG, 24, 3, 3);// usb dev
mdelay(10);
}
int hub_status_data(char buf[8])
{
int ret = 1;
struct dwc2_host_data *host = &dwc2_host;
//printf("prvUsbPortScanTimerCallback\r\n");
ret = host->dw2_hc_driver->hub_status_data(&(host->hcd), buf);
if (ret != 0) {
//printf("state:%d usb state changed now!\r\n", buf[0]);
}
return ret;
}
void usb_dwc2_lowlevel_restart()
{
struct dwc2_host_data *host = &dwc2_host;
if (host->dw2_hc_driver && host->dw2_hc_driver->stop) {
host->dw2_hc_driver->stop(&(host->hcd));
}
msleep(50);
if (host->dw2_hc_driver && host->dw2_hc_driver->start) {
host->dw2_hc_driver->start(&(host->hcd));
}
}
int usb_dwc2_reset(int inIrq, int isDev)
{
struct dwc2_hsotg *hsotg = dwc2_host.host;
struct wq_msg *pmsg = &hsotg->xmsg;
if (isDev)
pmsg->id = OTG_WQ_MSG_ID_DEV_RESET;
else
pmsg->id = OTG_WQ_MSG_ID_HOST_RESET;
pmsg->delay = 50;
if (inIrq) {
xQueueSendFromISR(hsotg->wq_otg, (void*)pmsg, 0);
} else {
xQueueSend(hsotg->wq_otg, (void*)pmsg, 0);
}
return 0;
}
//#define mainTIMER_SCAN_FREQUENCY_MS pdMS_TO_TICKS( 1000UL )
int usb_dwc2_lowlevel_init()
{
struct dwc2_host_data *host = &dwc2_host;
int ret;
INIT_LIST_HEAD(&host->free_urb_list);
spin_lock_init(&host->lock);
ret = dwc2_driver_init(&host->host, &(host->hcd));
if (!host->host) {
printf("MUSB host is not registered\n");
return -ENODEV;
}
host->dw2_hc_driver = dwc2_get_driver();
return ret;
}
int usb_dwc2_lowlevel_uninit()
{
if (!dwc2_host.host) {
printf("MUSB host is not registered\n");
return -ENODEV;
}
dwc2_driver_uninit(dwc2_host.host);
return 0;
}
int submit_control_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length, struct devrequest *setup, int timeout)
{
if (dev->parent == NULL) {
return dwc_otg_submit_rh_msg(dev, pipe, buffer, length, setup);
} else {
return _dwc2_submit_control_msg(&dwc2_host, dev, pipe, buffer, length, setup, timeout);
}
}
int submit_int_msg(struct usb_device *dev, unsigned long pipe,
void *buffer, int length, int interval)
{
return _dwc2_submit_int_msg(&dwc2_host, dev, pipe, buffer, length, interval, 0);
}
static void construct_iso_urb(struct urb *urb, struct usb_host_endpoint *hep,
struct usb_device *dev, int endpoint_type,
unsigned long pipe, void *buffer, int len,
int num_iso_packets, int interval)
{
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
int i;
int psize = 512;
INIT_LIST_HEAD(&hep->urb_list);
#ifndef NO_GNU
INIT_LIST_HEAD(&urb->urb_list);
#else
INIT_LIST_ITEM(&urb->urb_list);
urb->urb_list.pvOwner = (void *)urb;
#endif
urb->ep = hep;
urb->complete = dwc2_host_complete_urb;
urb->status = -EINPROGRESS;
urb->dev = dev;
urb->pipe = pipe;
urb->transfer_buffer = buffer;
urb->transfer_dma = (dma_addr_t)buffer;
urb->transfer_buffer_length = len;
urb->transfer_flags &= ~(URB_DIR_MASK | URB_DMA_MAP_SINGLE |
URB_DMA_MAP_PAGE | URB_DMA_MAP_SG | URB_MAP_LOCAL |
URB_SETUP_MAP_SINGLE | URB_SETUP_MAP_LOCAL |
URB_DMA_SG_COMBINED);
urb->transfer_flags |= (is_in ? URB_DIR_IN : URB_DIR_OUT);
urb->ep->desc.wMaxPacketSize =
__cpu_to_le16(is_in ? dev->epmaxpacketin[epnum] :
dev->epmaxpacketout[epnum]);
urb->ep->desc.bmAttributes = endpoint_type;
urb->ep->desc.bEndpointAddress =
(is_in ? USB_DIR_IN : USB_DIR_OUT) | epnum;
urb->ep->desc.bInterval = interval;
if (num_iso_packets > 0)
psize = len / num_iso_packets;
for (i = 0; i < num_iso_packets; ++i) {
urb->iso_frame_desc[i].offset = i * psize;
urb->iso_frame_desc[i].length = psize;
}
}
int submit_iso_msg(struct usb_device *dev, unsigned long pipe, void *buffer, int transfer_len, int *actual_length, int timeout)
{
struct urb *urb = NULL;
struct usb_host_endpoint *hep = NULL;
int epnum = usb_pipeendpoint(pipe);
int is_in = usb_pipein(pipe);
int ret = 0;
struct dwc2_host_data *host = &dwc2_host;
int num_iso_packets = 1;
if (epnum > 8 || epnum < 0)
return -EINVAL;
if (is_in) {
hep = &host->hep_in[epnum];
} else {
hep = &host->hep_out[epnum];
}
urb = usb_urb_alloc(host, num_iso_packets, IRQ_NONE);
if (urb == NULL)
return -ENOMEM;
construct_iso_urb(urb, hep, dev, USB_ENDPOINT_XFER_BULK,
pipe, buffer, transfer_len, num_iso_packets, 0);
ret = submit_urb(&host->hcd, urb);
if (ret < 0) {
dwc2_urb_dequeue(&host->hcd, urb, 0);
if (actual_length)
*actual_length = 0;
goto exit;
}
ret = xQueueReceive((QueueHandle_t)urb->queueHandle, NULL, timeout);
if (ret != pdTRUE || 0 != urb->status) {
dwc2_urb_dequeue(&host->hcd, urb, 0);
if (actual_length)
*actual_length = 0;
if (0 != urb->status)
printf("usb iso urb error:%d\r\n", urb->status);
ret = urb->status;
} else {
if (actual_length) {
int i, tmp_len = 0;
for (i = 0; i < urb->number_of_packets; i++) {
tmp_len += urb->iso_frame_desc[i].actual_length;
}
*actual_length = tmp_len;
}
}
exit:
usb_urb_release(urb);
return ret;
}
/*
* DWC2 to USB API interface
*/
/* Direction: In ; Request: Status */
static int dwc_otg_submit_rh_msg_in_status(struct usb_device *dev, void *buffer,
int txlen, struct devrequest *cmd)
{
int len = 0;
int stat = 0;
switch (cmd->requesttype & ~USB_DIR_IN) {
case 0:
*(uint16_t *)buffer = cpu_to_le16(1);
len = 2;
break;
case USB_RECIP_INTERFACE:
case USB_RECIP_ENDPOINT:
*(uint16_t *)buffer = cpu_to_le16(0);
len = 2;
break;
case USB_TYPE_CLASS:
*(uint32_t *)buffer = cpu_to_le32(0);
len = 4;
break;
case USB_RECIP_OTHER | USB_TYPE_CLASS:
len = 4;
stat = dwc2_host.dw2_hc_driver->hub_control(&dwc2_host.hcd, (cmd->requesttype << 8) | (cmd->request),
cmd->value, cmd->index, (char*)buffer, len);
break;
default:
//puts("unsupported root hub command\n");
stat = USB_ST_STALLED;
}
dev->act_len = min(len, txlen);
dev->status = stat;
return stat;
}
/* Direction: In ; Request: Descriptor */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0) /* number of downstream ports */
#define RH_A_PSM (1 << 8) /* power switching mode */
#define RH_A_NPS (1 << 9) /* no power switching */
#define RH_A_DT (1 << 10) /* device type (mbz) */
#define RH_A_OCPM (1 << 11) /* over current protection mode */
#define RH_A_NOCP (1 << 12) /* no over current protection */
#define RH_A_POTPGT (0xffUL << 24) /* power on to power good time */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff /* device removable flags */
#define RH_B_PPCM 0xffff0000 /* port power control mask */
static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
{
unsigned char data[32];
uint32_t dsc;
int len = 0;
int stat = 0;
uint16_t wValue = cpu_to_le16(cmd->value);
uint16_t wLength = cpu_to_le16(cmd->length);
switch (cmd->requesttype & ~USB_DIR_IN) {
case 0:
{
switch (wValue & 0xff00) {
case 0x0100: /* device descriptor */
len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength);
memcpy(buffer, root_hub_dev_des, len);
break;
case 0x0200: /* configuration descriptor */
len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength);
memcpy(buffer, root_hub_config_des, len);
break;
case 0x0300: /* string descriptors */
switch (wValue & 0xff) {
case 0x00:
len = min3(txlen, (int)sizeof(root_hub_str_index0),
(int)wLength);
memcpy(buffer, root_hub_str_index0, len);
break;
case 0x01:
len = min3(txlen, (int)sizeof(root_hub_str_index1),
(int)wLength);
memcpy(buffer, root_hub_str_index1, len);
break;
}
break;
default:
stat = USB_ST_STALLED;
}
break;
}
case USB_TYPE_CLASS:
/* Root port config, set 1 port and nothing else. */
dsc = 0x00000001;
data[0] = 9; /* min length; */
data[1] = 0x29;
data[2] = dsc & RH_A_NDP;
data[3] = 0;
if (dsc & RH_A_PSM)
data[3] |= 0x1;
if (dsc & RH_A_NOCP)
data[3] |= 0x10;
else if (dsc & RH_A_OCPM)
data[3] |= 0x8;
/* corresponds to data[4-7] */
data[5] = (dsc & RH_A_POTPGT) >> 24;
data[7] = dsc & RH_B_DR;
if (data[2] < 7) {
data[8] = 0xff;
} else {
data[0] += 2;
data[8] = (dsc & RH_B_DR) >> 8;
data[9] = 0xff;
data[10] = data[9];
}
len = min3(txlen, (int)data[0], (int)wLength);
memcpy(buffer, data, len);
break;
default:
//puts("unsupported root hub command\n");
stat = USB_ST_STALLED;
}
dev->act_len = min(len, txlen);
dev->status = stat;
return stat;
}
/* Direction: In ; Request: Configuration */
static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
{
int len = 0;
int stat = 0;
switch (cmd->requesttype & ~USB_DIR_IN) {
case 0:
*(uint8_t *)buffer = 0x01;
len = 1;
break;
default:
//puts("unsupported root hub command\n");
stat = USB_ST_STALLED;
}
dev->act_len = min(len, txlen);
dev->status = stat;
return stat;
}
/* Direction: In */
static int dwc_otg_submit_rh_msg_in(
struct usb_device *dev, void *buffer,
int txlen, struct devrequest *cmd)
{
switch (cmd->request) {
case USB_REQ_GET_STATUS:
return dwc_otg_submit_rh_msg_in_status(dev, buffer,
txlen, cmd);
case USB_REQ_GET_DESCRIPTOR:
return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer,
txlen, cmd);
case USB_REQ_GET_CONFIGURATION:
return dwc_otg_submit_rh_msg_in_configuration(dev, buffer,
txlen, cmd);
default:
//puts("unsupported root hub command\n");
return USB_ST_STALLED;
}
}
/* Direction: Out */
static int dwc_otg_submit_rh_msg_out(
struct usb_device *dev,
void *buffer, int txlen,
struct devrequest *cmd)
{
int len = 0;
int stat = 0;
uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8);
uint16_t wValue = cpu_to_le16(cmd->value);
switch (bmrtype_breq & ~USB_DIR_IN) {
case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT:
case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS:
break;
case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS:
case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS:
dwc2_host.dw2_hc_driver->hub_control(&dwc2_host.hcd, (cmd->requesttype << 8) | (cmd->request),
cmd->value, cmd->index, (char*)buffer, txlen);
break;
case (USB_REQ_SET_ADDRESS << 8):
dwc2_host.root_hub_devnum = wValue;
dwc2_host.dw2_hc_driver->hub_control(&dwc2_host.hcd, (cmd->requesttype << 8) | (cmd->request),
cmd->value, cmd->index, (char*)buffer, txlen);
break;
case (USB_REQ_SET_CONFIGURATION << 8):
break;
default:
//puts("unsupported root hub command\n");
stat = USB_ST_STALLED;
}
len = min(len, txlen);
dev->act_len = len;
dev->status = stat;
return stat;
}
static int dwc_otg_submit_rh_msg(struct usb_device *dev,
unsigned long pipe, void *buffer, int txlen,
struct devrequest *cmd)
{
int stat = 0;
if (usb_pipeint(pipe)) {
//puts("Root-Hub submit IRQ: NOT implemented\n");
return 0;
}
if (cmd->requesttype & USB_DIR_IN)
stat = dwc_otg_submit_rh_msg_in(dev, buffer, txlen, cmd);
else
stat = dwc_otg_submit_rh_msg_out(dev, buffer, txlen, cmd);
stat = dev->act_len;
return stat;
}

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* MUSB OTG driver u-boot specific functions
*
* Copyright © 2015 Hans de Goede <hdegoede@redhat.com>
*/
#ifndef __ARK_USB_DWC2_H__
#define __ARK_USB_DWC2_H__
int usb_dwc2_lowlevel_init();
int usb_dwc2_lowlevel_uninit();
void usb_sysctrl_init();
void reset_usb_phy();
void usb_disable_endpoint();
void usb_reset_endpoint();
void usb_dwc2_lowlevel_restart();
int usb_dwc2_reset(int inIrq, int isDev);
#endif

View File

@ -0,0 +1,843 @@
/*
* core.c - DesignWare HS OTG Controller common routines
*
* Copyright (C) 2004-2013 Synopsys, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
/*
* The Core code provides basic services for accessing and managing the
* DWC_otg hardware. These services are used by both the Host Controller
* Driver and the Peripheral Controller Driver.
*/
#define VERBOSE_DEBUG
#include "usb_os_adapter.h"
#include "trace.h"
#include <asm/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "hcd.h"
/**
* dwc2_backup_global_registers() - Backup global controller registers.
* When suspending usb bus, registers needs to be backuped
* if controller power is disabled once suspended.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
/* Backup global regs */
gr = &hsotg->gr_backup;
gr->gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
gr->gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gr->gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i));
gr->valid = true;
return 0;
}
/**
* dwc2_restore_global_registers() - Restore controller global registers.
* When resuming usb bus, device registers needs to be restored
* if controller power were disabled.
*
* @hsotg: Programming view of the DWC_otg controller
*/
static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
{
struct dwc2_gregs_backup *gr;
int i;
dev_dbg(hsotg->dev, "%s\n", __func__);
/* Restore global regs */
gr = &hsotg->gr_backup;
if (!gr->valid) {
dev_err(hsotg->dev, "%s: no global registers to restore\n",
__func__);
return -EINVAL;
}
gr->valid = false;
dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
dwc2_writel(gr->gotgctl, hsotg->regs + GOTGCTL);
dwc2_writel(gr->gintmsk, hsotg->regs + GINTMSK);
dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
for (i = 0; i < MAX_EPS_CHANNELS; i++)
dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
return 0;
}
/**
* dwc2_exit_hibernation() - Exit controller from Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
* @restore: Controller registers need to be restored
*/
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->params.hibernation)
return -ENOTSUPP;
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_STOPPCLK;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_PWRCLMP;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
udelay(100);
if (restore) {
ret = dwc2_restore_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_restore_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_restore_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
__func__);
return ret;
}
}
}
return ret;
}
/**
* dwc2_enter_hibernation() - Put controller in Partial Power Down.
*
* @hsotg: Programming view of the DWC_otg controller
*/
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
{
u32 pcgcctl;
int ret = 0;
if (!hsotg->params.hibernation)
return -ENOTSUPP;
/* Backup all registers */
ret = dwc2_backup_global_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
__func__);
return ret;
}
if (dwc2_is_host_mode(hsotg)) {
ret = dwc2_backup_host_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
__func__);
return ret;
}
} else {
ret = dwc2_backup_device_registers(hsotg);
if (ret) {
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
__func__);
return ret;
}
}
/*
* Clear any pending interrupts since dwc2 will not be able to
* clear them after entering hibernation.
*/
dwc2_writel(0xffffffff, hsotg->regs + GINTSTS);
/* Put the controller in low power state */
pcgcctl = dwc2_readl(hsotg->regs + PCGCTL);
pcgcctl |= PCGCTL_PWRCLMP;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
udelay(20);
pcgcctl |= PCGCTL_RSTPDWNMODULE;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
udelay(20);
pcgcctl |= PCGCTL_STOPPCLK;
dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
return ret;
}
/**
* dwc2_wait_for_mode() - Waits for the controller mode.
* @hsotg: Programming view of the DWC_otg controller.
* @host_mode: If true, waits for host mode, otherwise device mode.
*/
static void dwc2_wait_for_mode(struct dwc2_hsotg *hsotg,
bool host_mode)
{
uint32_t tick = xTaskGetTickCount();
unsigned int timeout = 110;//ms
dev_vdbg(hsotg->dev, "Waiting for %s mode\n",
host_mode ? "host" : "device");
//timeout = tick + configTICK_RATE_HZ / 10;
timeout += tick;
while (1) {
__s64 ms;
if (dwc2_is_host_mode(hsotg) == host_mode) {
dev_vdbg(hsotg->dev, "%s mode set\n",
host_mode ? "Host" : "Device");
break;
}
ms = (__s64)xTaskGetTickCount();
if (ms >= (__s64)timeout) {
dev_warn(hsotg->dev, "%s: Couldn't set %s mode\n",
__func__, host_mode ? "host" : "device");
break;
}
vTaskDelay(2000 / portTICK_RATE_MS);
}
}
/**
* dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
* filter is enabled.
*/
static bool dwc2_iddig_filter_enabled(struct dwc2_hsotg *hsotg)
{
u32 gsnpsid;
u32 ghwcfg4;
if (!dwc2_hw_is_otg(hsotg))
return false;
/* Check if core configuration includes the IDDIG filter. */
ghwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
return false;
/*
* Check if the IDDIG debounce filter is bypassed. Available
* in core version >= 3.10a.
*/
gsnpsid = dwc2_readl(hsotg->regs + GSNPSID);
if (gsnpsid >= DWC2_CORE_REV_3_10a) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
return false;
}
return true;
}
/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*/
int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
{
u32 greset;
int count = 0;
bool wait_for_host_mode = false;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
/*
* If the current mode is host, either due to the force mode
* bit being set (which persists after core reset) or the
* connector id pin, a core soft reset will temporarily reset
* the mode to device. A delay from the IDDIG debounce filter
* will occur before going back to host mode.
*
* Determine whether we will go back into host mode after a
* reset and account for this delay after the reset.
*/
if (dwc2_iddig_filter_enabled(hsotg)) {
u32 gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
u32 gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
if (!(gotgctl & GOTGCTL_CONID_B) ||
(gusbcfg & GUSBCFG_FORCEHOSTMODE)) {
wait_for_host_mode = true;
}
}
/* Core Soft Reset */
greset = dwc2_readl(hsotg->regs + GRSTCTL);
greset |= GRSTCTL_CSFTRST;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
do {
udelay(1);
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
"%s() HANG! Soft Reset GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
} while (greset & GRSTCTL_CSFTRST);
/* Wait for AHB master IDLE state */
count = 0;
do {
udelay(1);
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 50) {
dev_warn(hsotg->dev,
"%s() HANG! AHB Idle GRSTCTL=%0x\n",
__func__, greset);
return -EBUSY;
}
} while (!(greset & GRSTCTL_AHBIDLE));
if (wait_for_host_mode && !skip_wait)
dwc2_wait_for_mode(hsotg, true);
return 0;
}
/*
* Force the mode of the controller.
*
* Forcing the mode is needed for two cases:
*
* 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
* controller to stay in a particular mode regardless of ID pin
* changes. We do this usually after a core reset.
*
* 2) During probe we want to read reset values of the hw
* configuration registers that are only available in either host or
* device mode. We may need to force the mode if the current mode does
* not allow us to access the register in the mode that we want.
*
* In either case it only makes sense to force the mode if the
* controller hardware is OTG capable.
*
* Checks are done in this function to determine whether doing a force
* would be valid or not.
*
* If a force is done, it requires a IDDIG debounce filter delay if
* the filter is configured and enabled. We poll the current mode of
* the controller to account for this delay.
*/
static bool dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host)
{
u32 gusbcfg;
u32 set;
u32 clear;
dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device");
/*
* Force mode has no effect if the hardware is not OTG.
*/
if (!dwc2_hw_is_otg(hsotg))
return false;
/*
* If dr_mode is either peripheral or host only, there is no
* need to ever force the mode to the opposite mode.
*/
#ifndef NO_GNU
if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
return false;
if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST))
return false;
#else
WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL);
WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST);
#endif
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~clear;
gusbcfg |= set;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
dwc2_wait_for_mode(hsotg, host);
return true;
}
/**
* dwc2_clear_force_mode() - Clears the force mode bits.
*
* After clearing the bits, wait up to 100 ms to account for any
* potential IDDIG filter delay. We can't know if we expect this delay
* or not because the value of the connector ID status is affected by
* the force mode. We only need to call this once during probe if
* dr_mode == OTG.
*/
void dwc2_clear_force_mode(struct dwc2_hsotg *hsotg)
{
u32 gusbcfg;
gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG);
gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG);
if (dwc2_iddig_filter_enabled(hsotg))
msleep(100);
}
/*
* Sets or clears force mode based on the dr_mode parameter.
*/
void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg)
{
bool ret;
switch (hsotg->dr_mode) {
case USB_DR_MODE_HOST:
ret = dwc2_force_mode(hsotg, true);
/*
* NOTE: This is required for some rockchip soc based
* platforms on their host-only dwc2.
*/
if (!ret)
msleep(50);
break;
case USB_DR_MODE_PERIPHERAL:
dwc2_force_mode(hsotg, false);
break;
case USB_DR_MODE_OTG:
dwc2_clear_force_mode(hsotg);
break;
default:
dev_warn(hsotg->dev, "%s() Invalid dr_mode=%d\n",
__func__, hsotg->dr_mode);
break;
}
}
/*
* Do core a soft reset of the core. Be careful with this because it
* resets all the internal state machines of the core.
*
* Additionally this will apply force mode as per the hsotg->dr_mode
* parameter.
*/
int dwc2_core_reset_and_force_dr_mode(struct dwc2_hsotg *hsotg)
{
int retval;
retval = dwc2_core_reset(hsotg, false);
if (retval)
return retval;
dwc2_force_dr_mode(hsotg);
return 0;
}
/**
* dwc2_dump_host_registers() - Prints the host registers
*
* @hsotg: Programming view of DWC_otg controller
*
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg)
{
#ifdef DEBUG
u32 addr;//u32 __iomem *addr;
u32 value;
int i;
dev_dbg(hsotg->dev, "Host Global Registers\n");
addr = hsotg->regs + HCFG;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCFG @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HFIR;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HFIR @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HFNUM;
dev_dbg(hsotg->dev, "HFNUM @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HPTXSTS;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HPTXSTS @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HAINT;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HAINT @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HAINTMSK;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
if (hsotg->params.dma_desc_enable) {
addr = hsotg->regs + HFLBADDR;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
}
addr = hsotg->regs + HPRT0;
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
for (i = 0; i < hsotg->params.host_channels; i++) {
dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i);
addr = hsotg->regs + HCCHAR(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCCHAR @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HCSPLT(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCSPLT @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HCINT(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCINT @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HCINTMSK(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCINTMSK @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HCTSIZ(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCTSIZ @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HCDMA(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
if (hsotg->params.dma_desc_enable) {
addr = hsotg->regs + HCDMAB(i);
value = dwc2_readl(addr);
dev_dbg(hsotg->dev, "HCDMAB @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
}
}
USB_UNUSED(value);
#endif
}
/**
* dwc2_dump_global_registers() - Prints the core global registers
*
* @hsotg: Programming view of DWC_otg controller
*
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg)
{
#ifdef DEBUG
//u32 __iomem *addr;
u32 addr;
dev_dbg(hsotg->dev, "Core Global Registers\n");
addr = hsotg->regs + GOTGCTL;
dev_dbg(hsotg->dev, "GOTGCTL @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GOTGINT;
dev_dbg(hsotg->dev, "GOTGINT @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GAHBCFG;
dev_dbg(hsotg->dev, "GAHBCFG @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GUSBCFG;
dev_dbg(hsotg->dev, "GUSBCFG @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GRSTCTL;
dev_dbg(hsotg->dev, "GRSTCTL @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GINTSTS;
dev_dbg(hsotg->dev, "GINTSTS @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GINTMSK;
dev_dbg(hsotg->dev, "GINTMSK @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GRXSTSR;
dev_dbg(hsotg->dev, "GRXSTSR @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GRXFSIZ;
dev_dbg(hsotg->dev, "GRXFSIZ @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GNPTXFSIZ;
dev_dbg(hsotg->dev, "GNPTXFSIZ @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GNPTXSTS;
dev_dbg(hsotg->dev, "GNPTXSTS @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GI2CCTL;
dev_dbg(hsotg->dev, "GI2CCTL @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GPVNDCTL;
dev_dbg(hsotg->dev, "GPVNDCTL @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GGPIO;
dev_dbg(hsotg->dev, "GGPIO @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GUID;
dev_dbg(hsotg->dev, "GUID @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GSNPSID;
dev_dbg(hsotg->dev, "GSNPSID @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GHWCFG1;
dev_dbg(hsotg->dev, "GHWCFG1 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GHWCFG2;
dev_dbg(hsotg->dev, "GHWCFG2 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GHWCFG3;
dev_dbg(hsotg->dev, "GHWCFG3 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GHWCFG4;
dev_dbg(hsotg->dev, "GHWCFG4 @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GLPMCFG;
dev_dbg(hsotg->dev, "GLPMCFG @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GPWRDN;
dev_dbg(hsotg->dev, "GPWRDN @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + GDFIFOCFG;
dev_dbg(hsotg->dev, "GDFIFOCFG @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + HPTXFSIZ;
dev_dbg(hsotg->dev, "HPTXFSIZ @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
addr = hsotg->regs + PCGCTL;
dev_dbg(hsotg->dev, "PCGCTL @0x%08lX : 0x%08X\n",
(unsigned long)addr, dwc2_readl(addr));
USB_UNUSED(addr);
#endif
}
/**
* dwc2_flush_tx_fifo() - Flushes a Tx FIFO
*
* @hsotg: Programming view of DWC_otg controller
* @num: Tx FIFO to flush
*/
void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num)
{
u32 greset;
int count = 0;
dev_vdbg(hsotg->dev, "Flush Tx FIFO %d\n", num);
greset = GRSTCTL_TXFFLSH;
greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
do {
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 10000) {
dev_warn(hsotg->dev,
"%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n",
__func__, greset,
dwc2_readl(hsotg->regs + GNPTXSTS));
break;
}
udelay(1);
} while (greset & GRSTCTL_TXFFLSH);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
/**
* dwc2_flush_rx_fifo() - Flushes the Rx FIFO
*
* @hsotg: Programming view of DWC_otg controller
*/
void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg)
{
u32 greset;
int count = 0;
dev_vdbg(hsotg->dev, "%s()\n", __func__);
greset = GRSTCTL_RXFFLSH;
dwc2_writel(greset, hsotg->regs + GRSTCTL);
do {
greset = dwc2_readl(hsotg->regs + GRSTCTL);
if (++count > 10000) {
dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n",
__func__, greset);
break;
}
udelay(1);
} while (greset & GRSTCTL_RXFFLSH);
/* Wait for at least 3 PHY Clocks */
udelay(1);
}
/*
* Forces either host or device mode if the controller is not
* currently in that mode.
*
* Returns true if the mode was forced.
*/
bool dwc2_force_mode_if_needed(struct dwc2_hsotg *hsotg, bool host)
{
if (host && dwc2_is_host_mode(hsotg))
return false;
else if (!host && dwc2_is_device_mode(hsotg))
return false;
return dwc2_force_mode(hsotg, host);
}
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg)
{
if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff)
return false;
else
return true;
}
/**
* dwc2_enable_global_interrupts() - Enables the controller's Global
* Interrupt in the AHB Config register
*
* @hsotg: Programming view of DWC_otg controller
*/
void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg)
{
u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
ahbcfg |= GAHBCFG_GLBL_INTR_EN;
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
}
/**
* dwc2_disable_global_interrupts() - Disables the controller's Global
* Interrupt in the AHB Config register
*
* @hsotg: Programming view of DWC_otg controller
*/
void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg)
{
u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
ahbcfg &= ~GAHBCFG_GLBL_INTR_EN;
dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG);
}
/* Returns the controller's GHWCFG2.OTG_MODE. */
unsigned int dwc2_op_mode(struct dwc2_hsotg *hsotg)
{
u32 ghwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >>
GHWCFG2_OP_MODE_SHIFT;
}
/* Returns true if the controller is capable of DRD. */
bool dwc2_hw_is_otg(struct dwc2_hsotg *hsotg)
{
unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_HNP_SRP_CAPABLE) ||
(op_mode == GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE) ||
(op_mode == GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE);
}
/* Returns true if the controller is host-only. */
bool dwc2_hw_is_host(struct dwc2_hsotg *hsotg)
{
unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) ||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST);
}
/* Returns true if the controller is device-only. */
bool dwc2_hw_is_device(struct dwc2_hsotg *hsotg)
{
unsigned int op_mode = dwc2_op_mode(hsotg);
return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ||
(op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE);
}
#ifndef NO_GNU
MODULE_DESCRIPTION("DESIGNWARE HS OTG Core");
MODULE_AUTHOR("Synopsys, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,518 @@
/*
* core_intr.c - DesignWare HS OTG Controller common interrupt handling
*
* Copyright (C) 2004-2013 Synopsys, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 file contains the common interrupt handlers
*/
#include "usb_os_adapter.h"
#include "trace.h"
#include <asm/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "hcd.h"
const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
{
switch (hsotg->op_state) {
case OTG_STATE_A_HOST:
return "a_host";
case OTG_STATE_A_SUSPEND:
return "a_suspend";
case OTG_STATE_A_PERIPHERAL:
return "a_peripheral";
case OTG_STATE_B_PERIPHERAL:
return "b_peripheral";
case OTG_STATE_B_HOST:
return "b_host";
default:
return "unknown";
}
}
/**
* dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
* When the PRTINT interrupt fires, there are certain status bits in the Host
* Port that needs to get cleared.
*
* @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
{
u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
if (hprt0 & HPRT0_ENACHG) {
hprt0 &= ~HPRT0_ENA;
dwc2_writel(hprt0, hsotg->regs + HPRT0);
}
}
/**
* dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
*
* @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg)
{
/* Clear interrupt */
dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
dev_warn(hsotg->dev, "Mode Mismatch Interrupt: currently in %s mode\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
}
/**
* dwc2_handle_otg_intr() - Handles the OTG Interrupts. It reads the OTG
* Interrupt Register (GOTGINT) to determine what interrupt has occurred.
*
* @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
{
u32 gotgint;
u32 gotgctl;
u32 gintmsk;
gotgint = dwc2_readl(hsotg->regs + GOTGINT);
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint,
dwc2_op_state_str(hsotg));
if (gotgint & GOTGINT_SES_END_DET) {
dev_dbg(hsotg->dev,
" ++OTG Interrupt: Session End Detected++ (%s)\n",
dwc2_op_state_str(hsotg));
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (dwc2_is_device_mode(hsotg))
dwc2_hsotg_disconnect(hsotg);
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
} else {
/*
* If not B_HOST and Device HNP still set, HNP did
* not succeed!
*/
if (gotgctl & GOTGCTL_DEVHNPEN) {
dev_dbg(hsotg->dev, "Session End Detected\n");
dev_err(hsotg->dev,
"Device Not Connected/Responding!\n");
}
/*
* If Session End Detected the B-Cable has been
* disconnected
*/
/* Reset to a clean state */
hsotg->lx_state = DWC2_L0;
}
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
gotgctl &= ~GOTGCTL_DEVHNPEN;
dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
}
if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) {
dev_dbg(hsotg->dev,
" ++OTG Interrupt: Session Request Success Status Change++\n");
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
if (gotgctl & GOTGCTL_SESREQSCS) {
if (hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS &&
hsotg->params.i2c_enable) {
hsotg->srp_success = 1;
} else {
/* Clear Session Request */
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
gotgctl &= ~GOTGCTL_SESREQ;
dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
}
}
}
if (gotgint & GOTGINT_HST_NEG_SUC_STS_CHNG) {
/*
* Print statements during the HNP interrupt handling
* can cause it to fail
*/
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
/*
* WA for 3.00a- HW is not setting cur_mode, even sometimes
* this does not help
*/
if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a)
udelay(100);
if (gotgctl & GOTGCTL_HSTNEGSCS) {
if (dwc2_is_host_mode(hsotg)) {
hsotg->op_state = OTG_STATE_B_HOST;
/*
* Need to disable SOF interrupt immediately.
* When switching from device to host, the PCD
* interrupt handler won't handle the interrupt
* if host mode is already set. The HCD
* interrupt handler won't get called if the
* HCD state is HALT. This means that the
* interrupt does not get handled and Linux
* complains loudly.
*/
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_SOF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
/*
* Call callback function with spin lock
* released
*/
spin_unlock(&hsotg->lock);
/* Initialize the Core for Host mode */
dwc2_hcd_start_isr(hsotg);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_B_HOST;
}
} else {
gotgctl = dwc2_readl(hsotg->regs + GOTGCTL);
gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN);
dwc2_writel(gotgctl, hsotg->regs + GOTGCTL);
dev_dbg(hsotg->dev, "HNP Failed\n");
dev_err(hsotg->dev,
"Device Not Connected/Responding\n");
}
}
if (gotgint & GOTGINT_HST_NEG_DET) {
/*
* The disconnect interrupt is set at the same time as
* Host Negotiation Detected. During the mode switch all
* interrupts are cleared so the disconnect interrupt
* handler will not get executed.
*/
dev_dbg(hsotg->dev,
" ++OTG Interrupt: Host Negotiation Detected++ (%s)\n",
(dwc2_is_host_mode(hsotg) ? "Host" : "Device"));
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, "a_suspend->a_peripheral (%d)\n",
hsotg->op_state);
spin_unlock(&hsotg->lock);
dwc2_hcd_disconnect(hsotg, false);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_PERIPHERAL;
} else {
/* Need to disable SOF interrupt immediately */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_SOF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
spin_unlock(&hsotg->lock);
dwc2_hcd_start_isr(hsotg);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_HOST;
}
}
if (gotgint & GOTGINT_A_DEV_TOUT_CHG)
dev_dbg(hsotg->dev,
" ++OTG Interrupt: A-Device Timeout Change++\n");
if (gotgint & GOTGINT_DBNCE_DONE)
dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n");
/* Clear GOTGINT */
dwc2_writel(gotgint, hsotg->regs + GOTGINT);
}
/**
* dwc2_handle_conn_id_status_change_intr() - Handles the Connector ID Status
* Change Interrupt
*
* @hsotg: Programming view of DWC_otg controller
*
* Reads the OTG Interrupt Register (GOTCTL) to determine whether this is a
* Device to Host Mode transition or a Host to Device Mode transition. This only
* occurs when the cable is connected/removed from the PHY connector.
*/
static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
{
u32 gintmsk;
/* Clear interrupt */
dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
/* Need to disable SOF interrupt immediately */
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gintmsk &= ~GINTSTS_SOF;
dwc2_writel(gintmsk, hsotg->regs + GINTMSK);
dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device");
/*
* Need to schedule a work, as there are possible DELAY function calls.
* Release lock before scheduling workq as it holds spinlock during
* scheduling.
*/
if (hsotg->wq_otg) {
spin_unlock(&hsotg->lock);
struct wq_msg *pmsg = &hsotg->xmsg;
pmsg->id = OTG_WQ_MSG_ID_STATE_CHANGE;
pmsg->delay = 0;
xQueueSendFromISR(hsotg->wq_otg, (void*)pmsg, 0);
spin_lock(&hsotg->lock);
}
}
/**
* dwc2_handle_session_req_intr() - This interrupt indicates that a device is
* initiating the Session Request Protocol to request the host to turn on bus
* power so a new session can begin
*
* @hsotg: Programming view of DWC_otg controller
*
* This handler responds by turning on bus power. If the DWC_otg controller is
* in low power mode, this handler brings the controller out of low power mode
* before turning on bus power.
*/
static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
{
int ret;
/* Clear interrupt */
dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n",
hsotg->lx_state);
if (dwc2_is_device_mode(hsotg)) {
if (hsotg->lx_state == DWC2_L2) {
ret = dwc2_exit_hibernation(hsotg, true);
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev,
"exit hibernation failed\n");
}
/*
* Report disconnect if there is any previous session
* established
*/
dwc2_hsotg_disconnect(hsotg);
}
}
/*
* This interrupt indicates that a device has been disconnected from the
* root port
*/
int dwc2_disconnect_flag;
void usb_stor_disconnect_isr();
static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
{
usb_stor_disconnect_isr();
dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "++Disconnect Detected Interrupt++ (%s) %s\n",
dwc2_is_host_mode(hsotg) ? "Host" : "Device",
dwc2_op_state_str(hsotg));
if (hsotg->op_state == OTG_STATE_A_HOST)
dwc2_hcd_disconnect(hsotg, false);
dwc2_disconnect_flag = 1;
}
/*
* This interrupt indicates that SUSPEND state has been detected on the USB.
*
* For HNP the USB Suspend interrupt signals the change from "a_peripheral"
* to "a_host".
*
* When power management is enabled the core will be put in low power mode.
*/
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
{
u32 dsts;
int ret;
/* Clear interrupt */
dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
dev_dbg(hsotg->dev, "USB SUSPEND\n");
if (dwc2_is_device_mode(hsotg)) {
/*
* Check the Device status register to determine if the Suspend
* state is active
*/
dsts = dwc2_readl(hsotg->regs + DSTS);
dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts);
dev_dbg(hsotg->dev,
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
!!(dsts & DSTS_SUSPSTS),
hsotg->hw_params.power_optimized);
if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
/* Ignore suspend request before enumeration */
if (!dwc2_is_device_connected(hsotg)) {
dev_dbg(hsotg->dev,
"ignore suspend request before enumeration\n");
return;
}
ret = dwc2_enter_hibernation(hsotg);
if (ret) {
if (ret != -ENOTSUPP)
dev_err(hsotg->dev,
"enter hibernation failed\n");
goto skip_power_saving;
}
udelay(100);
skip_power_saving:
/*
* Change to L2 (suspend) state before releasing
* spinlock
*/
hsotg->lx_state = DWC2_L2;
/* Call gadget suspend callback */
call_gadget(hsotg, suspend);
} else if (dsts & DSTS_SUSPSTS) {
/* Call gadget disconnect callback */
call_gadget(hsotg, disconnect);
}
} else {
if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
/* Change to L2 (suspend) state */
hsotg->lx_state = DWC2_L2;
/* Clear the a_peripheral flag, back to a_host */
spin_unlock(&hsotg->lock);
dwc2_hcd_start_isr(hsotg);
spin_lock(&hsotg->lock);
hsotg->op_state = OTG_STATE_A_HOST;
}
}
}
#define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \
GINTSTS_CONIDSTSCHNG | GINTSTS_OTGINT | \
GINTSTS_MODEMIS | GINTSTS_DISCONNINT | \
GINTSTS_USBSUSP | GINTSTS_PRTINT)
/*
* This function returns the Core Interrupt register
*/
static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg)
{
u32 gintsts;
u32 gintmsk;
u32 gahbcfg;
u32 gintmsk_common = GINTMSK_COMMON;
gintsts = dwc2_readl(hsotg->regs + GINTSTS);
gintmsk = dwc2_readl(hsotg->regs + GINTMSK);
gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG);
/* If any common interrupts set */
if (gintsts & gintmsk_common)
dev_dbg(hsotg->dev, "gintsts=%08x gintmsk=%08x\n",
gintsts, gintmsk);
if (gahbcfg & GAHBCFG_GLBL_INTR_EN)
return gintsts & gintmsk & gintmsk_common;
else
return 0;
}
/*
* Common interrupt handler
*
* The common interrupts are those that occur in both Host and Device mode.
* This handler handles the following interrupts:
* - Mode Mismatch Interrupt
* - OTG Interrupt
* - Connector ID Status Change Interrupt
* - Disconnect Interrupt
* - Session Request Interrupt
* - Resume / Remote Wakeup Detected Interrupt
* - Suspend Interrupt
*/
irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
{
struct dwc2_hsotg *hsotg = dev;
u32 gintsts;
irqreturn_t retval = IRQ_NONE;
if (!dwc2_is_controller_alive(hsotg)) {
dev_warn(hsotg->dev, "Controller is dead\n");
goto out;
}
gintsts = dwc2_read_common_intr(hsotg);
if (gintsts & ~GINTSTS_PRTINT)
retval = IRQ_HANDLED;
if (gintsts & GINTSTS_MODEMIS)
dwc2_handle_mode_mismatch_intr(hsotg);
if (gintsts & GINTSTS_OTGINT)
dwc2_handle_otg_intr(hsotg);
if (gintsts & GINTSTS_CONIDSTSCHNG)
dwc2_handle_conn_id_status_change_intr(hsotg);
if (gintsts & GINTSTS_DISCONNINT)
dwc2_handle_disconnect_intr(hsotg);
if (gintsts & GINTSTS_SESSREQINT)
dwc2_handle_session_req_intr(hsotg);
if (gintsts & GINTSTS_WKUPINT)
dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
if (gintsts & GINTSTS_USBSUSP)
dwc2_handle_usb_suspend_intr(hsotg);
if (gintsts & GINTSTS_PRTINT) {
/*
* The port interrupt occurs while in device mode with HPRT0
* Port Enable/Disable
*/
if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev,
" --Port interrupt received in Device mode--\n");
dwc2_handle_usb_port_intr(hsotg);
retval = IRQ_HANDLED;
}
}
out:
return retval;
}

View File

@ -0,0 +1,138 @@
#ifndef _DWC2_COMPAT_H
#define _DWC2_COMPAT_H
/* OTG defines lots of enumeration states before device reset */
enum usb_otg_state {
OTG_STATE_UNDEFINED = 0,
/* single-role peripheral, and dual-role default-b */
OTG_STATE_B_IDLE,
OTG_STATE_B_SRP_INIT,
OTG_STATE_B_PERIPHERAL,
/* extra dual-role default-b states */
OTG_STATE_B_WAIT_ACON,
OTG_STATE_B_HOST,
/* dual-role default-a */
OTG_STATE_A_IDLE,
OTG_STATE_A_WAIT_VRISE,
OTG_STATE_A_WAIT_BCON,
OTG_STATE_A_HOST,
OTG_STATE_A_SUSPEND,
OTG_STATE_A_PERIPHERAL,
OTG_STATE_A_WAIT_VFALL,
OTG_STATE_A_VBUS_ERR,
};
enum usb_dr_mode {
USB_DR_MODE_UNKNOWN,
USB_DR_MODE_HOST,
USB_DR_MODE_PERIPHERAL,
USB_DR_MODE_OTG,
};
#define URB_DIR_IN 0x0200 /* Transfer from device to host */
#define URB_DIR_OUT 0
#define URB_DIR_MASK URB_DIR_IN
#define URB_DMA_MAP_SINGLE 0x00010000 /* Non-scatter-gather mapping */
#define URB_DMA_MAP_PAGE 0x00020000 /* HCD-unsupported S-G */
#define URB_DMA_MAP_SG 0x00040000 /* HCD-supported S-G */
#define URB_MAP_LOCAL 0x00080000 /* HCD-local-memory mapping */
#define URB_SETUP_MAP_SINGLE 0x00100000 /* Setup packet DMA mapped */
#define URB_SETUP_MAP_LOCAL 0x00200000 /* HCD-local setup packet */
#define URB_DMA_SG_COMBINED 0x00400000 /* S-G entries were combined */
#define URB_ALIGNED_TEMP_BUFFER 0x00800000 /* Temp buffer was alloc'd */
#define USB_RESUME_TIMEOUT 40 /* ms */
/* class requests from the USB 2.0 hub spec, table 11-15 */
#define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request))
/* GetBusState and SetHubDescriptor are optional, omitted */
#define ClearHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_CLEAR_FEATURE)
#define ClearPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_CLEAR_FEATURE)
#define GetHubDescriptor HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_DESCRIPTOR)
#define GetHubStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_HUB, USB_REQ_GET_STATUS)
#define GetPortStatus HUB_CLASS_REQ(USB_DIR_IN, USB_RT_PORT, USB_REQ_GET_STATUS)
#define SetHubFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_HUB, USB_REQ_SET_FEATURE)
#define SetPortFeature HUB_CLASS_REQ(USB_DIR_OUT, USB_RT_PORT, USB_REQ_SET_FEATURE)
/*
* Port feature numbers
* See USB 2.0 spec Table 11-17
*/
#define USB_PORT_FEAT_CONNECTION 0
#define USB_PORT_FEAT_ENABLE 1
#define USB_PORT_FEAT_SUSPEND 2 /* L2 suspend */
#define USB_PORT_FEAT_OVER_CURRENT 3
#define USB_PORT_FEAT_RESET 4
#define USB_PORT_FEAT_L1 5 /* L1 suspend */
#define USB_PORT_FEAT_POWER 8
#define USB_PORT_FEAT_LOWSPEED 9 /* Should never be used */
#define USB_PORT_FEAT_C_CONNECTION 16
#define USB_PORT_FEAT_C_ENABLE 17
#define USB_PORT_FEAT_C_SUSPEND 18
#define USB_PORT_FEAT_C_OVER_CURRENT 19
#define USB_PORT_FEAT_C_RESET 20
#define USB_PORT_FEAT_TEST 21
#define USB_PORT_FEAT_INDICATOR 22
#define USB_PORT_FEAT_C_PORT_L1 23
/*
* wPortChange bit field
* See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10
* Bits 0 to 5 shown, bits 6 to 15 are reserved
*/
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
#define USB_PORT_STAT_C_L1 0x0020
/*
* wPortStatus bit field
* See USB 2.0 spec Table 11-21
*/
#define USB_PORT_STAT_CONNECTION 0x0001
#define USB_PORT_STAT_ENABLE 0x0002
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
#define USB_PORT_STAT_L1 0x0020
/* bits 6 to 7 are reserved */
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400
#define USB_PORT_STAT_TEST 0x0800
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
/*
* wHubCharacteristics (masks)
* See USB 2.0 spec Table 11-13, offset 3
*/
#define HUB_CHAR_LPSM 0x0003 /* Logical Power Switching Mode mask */
#define HUB_CHAR_COMMON_LPSM 0x0000 /* All ports power control at once */
#define HUB_CHAR_INDV_PORT_LPSM 0x0001 /* per-port power control */
#define HUB_CHAR_NO_LPSM 0x0002 /* no power switching */
#define HUB_CHAR_COMPOUND 0x0004 /* hub is part of a compound device */
#define HUB_CHAR_OCPM 0x0018 /* Over-Current Protection Mode mask */
#define HUB_CHAR_COMMON_OCPM 0x0000 /* All ports Over-Current reporting */
#define HUB_CHAR_INDV_PORT_OCPM 0x0008 /* per-port Over-current reporting */
#define HUB_CHAR_NO_OCPM 0x0010 /* No Over-current Protection support */
#define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */
#define HUB_CHAR_PORTIND 0x0080 /* per-port indicators (LEDs) */
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,893 @@
/*
* hcd.h - DesignWare HS OTG Controller host-mode declarations
*
* Copyright (C) 2004-2013 Synopsys, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
#ifndef __DWC2_HCD_H__
#define __DWC2_HCD_H__
#include "usb-compat.h"
/*
* This file contains the structures, constants, and interfaces for the
* Host Contoller Driver (HCD)
*
* The Host Controller Driver (HCD) is responsible for translating requests
* from the USB Driver into the appropriate actions on the DWC_otg controller.
* It isolates the USBD from the specifics of the controller by providing an
* API to the USBD.
*/
struct dwc2_qh;
/**
* struct dwc2_host_chan - Software host channel descriptor
*
* @hc_num: Host channel number, used for register address lookup
* @dev_addr: Address of the device
* @ep_num: Endpoint of the device
* @ep_is_in: Endpoint direction
* @speed: Device speed. One of the following values:
* - USB_SPEED_LOW
* - USB_SPEED_FULL
* - USB_SPEED_HIGH
* @ep_type: Endpoint type. One of the following values:
* - USB_ENDPOINT_XFER_CONTROL: 0
* - USB_ENDPOINT_XFER_ISOC: 1
* - USB_ENDPOINT_XFER_BULK: 2
* - USB_ENDPOINT_XFER_INTR: 3
* @max_packet: Max packet size in bytes
* @data_pid_start: PID for initial transaction.
* 0: DATA0
* 1: DATA2
* 2: DATA1
* 3: MDATA (non-Control EP),
* SETUP (Control EP)
* @multi_count: Number of additional periodic transactions per
* (micro)frame
* @xfer_buf: Pointer to current transfer buffer position
* @xfer_dma: DMA address of xfer_buf
* @align_buf: In Buffer DMA mode this will be used if xfer_buf is not
* DWORD aligned
* @xfer_len: Total number of bytes to transfer
* @xfer_count: Number of bytes transferred so far
* @start_pkt_count: Packet count at start of transfer
* @xfer_started: True if the transfer has been started
* @ping: True if a PING request should be issued on this channel
* @error_state: True if the error count for this transaction is non-zero
* @halt_on_queue: True if this channel should be halted the next time a
* request is queued for the channel. This is necessary in
* slave mode if no request queue space is available when
* an attempt is made to halt the channel.
* @halt_pending: True if the host channel has been halted, but the core
* is not finished flushing queued requests
* @do_split: Enable split for the channel
* @complete_split: Enable complete split
* @hub_addr: Address of high speed hub for the split
* @hub_port: Port of the low/full speed device for the split
* @xact_pos: Split transaction position. One of the following values:
* - DWC2_HCSPLT_XACTPOS_MID
* - DWC2_HCSPLT_XACTPOS_BEGIN
* - DWC2_HCSPLT_XACTPOS_END
* - DWC2_HCSPLT_XACTPOS_ALL
* @requests: Number of requests issued for this channel since it was
* assigned to the current transfer (not counting PINGs)
* @schinfo: Scheduling micro-frame bitmap
* @ntd: Number of transfer descriptors for the transfer
* @halt_status: Reason for halting the host channel
* @hcint Contents of the HCINT register when the interrupt came
* @qh: QH for the transfer being processed by this channel
* @hc_list_entry: For linking to list of host channels
* @desc_list_addr: Current QH's descriptor list DMA address
* @desc_list_sz: Current QH's descriptor list size
* @split_order_list_entry: List entry for keeping track of the order of splits
*
* This structure represents the state of a single host channel when acting in
* host mode. It contains the data items needed to transfer packets to an
* endpoint via a host channel.
*/
struct dwc2_host_chan {
u8 hc_num;
unsigned dev_addr:7;
unsigned ep_num:4;
unsigned ep_is_in:1;
unsigned speed:4;
unsigned ep_type:2;
unsigned max_packet:11;
unsigned data_pid_start:2;
#define DWC2_HC_PID_DATA0 TSIZ_SC_MC_PID_DATA0
#define DWC2_HC_PID_DATA2 TSIZ_SC_MC_PID_DATA2
#define DWC2_HC_PID_DATA1 TSIZ_SC_MC_PID_DATA1
#define DWC2_HC_PID_MDATA TSIZ_SC_MC_PID_MDATA
#define DWC2_HC_PID_SETUP TSIZ_SC_MC_PID_SETUP
unsigned multi_count:2;
u8 *xfer_buf;
dma_addr_t xfer_dma;
dma_addr_t align_buf;
u32 xfer_len;
u32 xfer_count;
u16 start_pkt_count;
u8 xfer_started;
u8 do_ping;
u8 error_state;
u8 halt_on_queue;
u8 halt_pending;
u8 do_split;
u8 complete_split;
u8 hub_addr;
u8 hub_port;
u8 xact_pos;
#define DWC2_HCSPLT_XACTPOS_MID HCSPLT_XACTPOS_MID
#define DWC2_HCSPLT_XACTPOS_END HCSPLT_XACTPOS_END
#define DWC2_HCSPLT_XACTPOS_BEGIN HCSPLT_XACTPOS_BEGIN
#define DWC2_HCSPLT_XACTPOS_ALL HCSPLT_XACTPOS_ALL
u8 requests;
u8 schinfo;
u16 ntd;
enum dwc2_halt_status halt_status;
u32 hcint;
struct dwc2_qh *qh;
#ifndef NO_GNU
struct list_head hc_list_entry;
#else
ListItem_t hc_list_entry;
#endif
dma_addr_t desc_list_addr;
u32 desc_list_sz;
#ifndef NO_GNU
struct list_head split_order_list_entry;
#else
ListItem_t split_order_list_entry;
#endif
};
struct dwc2_hcd_pipe_info {
u8 dev_addr;
u8 ep_num;
u8 pipe_type;
u8 pipe_dir;
u16 mps;
};
struct dwc2_hcd_iso_packet_desc {
u32 offset;
u32 length;
u32 actual_length;
int status;
};
struct dwc2_qtd;
struct dwc2_hcd_urb {
void *priv;
struct dwc2_qtd *qtd;
void *buf;
dma_addr_t dma;
void *setup_packet;
dma_addr_t setup_dma;
u32 length;
u32 actual_length;
int status;
u32 error_count;
u32 packet_count;
u32 flags;
u16 interval;
#ifdef NO_GNU
ListItem_t free_list_entry;
#endif
struct dwc2_hcd_pipe_info pipe_info;
struct dwc2_hcd_iso_packet_desc iso_descs[0];
};
/* Phases for control transfers */
enum dwc2_control_phase {
DWC2_CONTROL_SETUP,
DWC2_CONTROL_DATA,
DWC2_CONTROL_STATUS,
};
/* Transaction types */
enum dwc2_transaction_type {
DWC2_TRANSACTION_NONE,
DWC2_TRANSACTION_PERIODIC,
DWC2_TRANSACTION_NON_PERIODIC,
DWC2_TRANSACTION_ALL,
};
/* The number of elements per LS bitmap (per port on multi_tt) */
#define DWC2_ELEMENTS_PER_LS_BITMAP DIV_ROUND_UP(DWC2_LS_SCHEDULE_SLICES, \
BITS_PER_LONG)
/**
* struct dwc2_tt - dwc2 data associated with a usb_tt
*
* @refcount: Number of Queue Heads (QHs) holding a reference.
* @usb_tt: Pointer back to the official usb_tt.
* @periodic_bitmaps: Bitmap for which parts of the 1ms frame are accounted
* for already. Each is DWC2_ELEMENTS_PER_LS_BITMAP
* elements (so sizeof(long) times that in bytes).
*
* This structure is stored in the hcpriv of the official usb_tt.
*/
struct dwc2_tt {
int refcount;
struct usb_tt *usb_tt;
unsigned long periodic_bitmaps[];
};
/**
* struct dwc2_hs_transfer_time - Info about a transfer on the high speed bus.
*
* @start_schedule_usecs: The start time on the main bus schedule. Note that
* the main bus schedule is tightly packed and this
* time should be interpreted as tightly packed (so
* uFrame 0 starts at 0 us, uFrame 1 starts at 100 us
* instead of 125 us).
* @duration_us: How long this transfer goes.
*/
struct dwc2_hs_transfer_time {
u32 start_schedule_us;
u16 duration_us;
};
/**
* struct dwc2_qh - Software queue head structure
*
* @hsotg: The HCD state structure for the DWC OTG controller
* @ep_type: Endpoint type. One of the following values:
* - USB_ENDPOINT_XFER_CONTROL
* - USB_ENDPOINT_XFER_BULK
* - USB_ENDPOINT_XFER_INT
* - USB_ENDPOINT_XFER_ISOC
* @ep_is_in: Endpoint direction
* @maxp: Value from wMaxPacketSize field of Endpoint Descriptor
* @dev_speed: Device speed. One of the following values:
* - USB_SPEED_LOW
* - USB_SPEED_FULL
* - USB_SPEED_HIGH
* @data_toggle: Determines the PID of the next data packet for
* non-controltransfers. Ignored for control transfers.
* One of the following values:
* - DWC2_HC_PID_DATA0
* - DWC2_HC_PID_DATA1
* @ping_state: Ping state
* @do_split: Full/low speed endpoint on high-speed hub requires split
* @td_first: Index of first activated isochronous transfer descriptor
* @td_last: Index of last activated isochronous transfer descriptor
* @host_us: Bandwidth in microseconds per transfer as seen by host
* @device_us: Bandwidth in microseconds per transfer as seen by device
* @host_interval: Interval between transfers as seen by the host. If
* the host is high speed and the device is low speed this
* will be 8 times device interval.
* @device_interval: Interval between transfers as seen by the device.
* interval.
* @next_active_frame: (Micro)frame _before_ we next need to put something on
* the bus. We'll move the qh to active here. If the
* host is in high speed mode this will be a uframe. If
* the host is in low speed mode this will be a full frame.
* @start_active_frame: If we are partway through a split transfer, this will be
* what next_active_frame was when we started. Otherwise
* it should always be the same as next_active_frame.
* @num_hs_transfers: Number of transfers in hs_transfers.
* Normally this is 1 but can be more than one for splits.
* Always >= 1 unless the host is in low/full speed mode.
* @hs_transfers: Transfers that are scheduled as seen by the high speed
* bus. Not used if host is in low or full speed mode (but
* note that it IS USED if the device is low or full speed
* as long as the HOST is in high speed mode).
* @ls_start_schedule_slice: Start time (in slices) on the low speed bus
* schedule that's being used by this device. This
* will be on the periodic_bitmap in a
* "struct dwc2_tt". Not used if this device is high
* speed. Note that this is in "schedule slice" which
* is tightly packed.
* @ls_duration_us: Duration on the low speed bus schedule.
* @ntd: Actual number of transfer descriptors in a list
* @dw_align_buf: Used instead of original buffer if its physical address
* is not dword-aligned
* @dw_align_buf_dma: DMA address for dw_align_buf
* @qtd_list: List of QTDs for this QH
* @channel: Host channel currently processing transfers for this QH
* @qh_list_entry: Entry for QH in either the periodic or non-periodic
* schedule
* @desc_list: List of transfer descriptors
* @desc_list_dma: Physical address of desc_list
* @desc_list_sz: Size of descriptors list
* @n_bytes: Xfer Bytes array. Each element corresponds to a transfer
* descriptor and indicates original XferSize value for the
* descriptor
* @unreserve_timer: Timer for releasing periodic reservation.
* @dwc2_tt: Pointer to our tt info (or NULL if no tt).
* @ttport: Port number within our tt.
* @tt_buffer_dirty True if clear_tt_buffer_complete is pending
* @unreserve_pending: True if we planned to unreserve but haven't yet.
* @schedule_low_speed: True if we have a low/full speed component (either the
* host is in low/full speed mode or do_split).
*
* A Queue Head (QH) holds the static characteristics of an endpoint and
* maintains a list of transfers (QTDs) for that endpoint. A QH structure may
* be entered in either the non-periodic or periodic schedule.
*/
struct dwc2_qh {
struct dwc2_hsotg *hsotg;
u8 ep_type;
u8 ep_is_in;
u16 maxp;
u8 dev_speed;
u8 data_toggle;
u8 ping_state;
u8 do_split;
u8 td_first;
u8 td_last;
u16 host_us;
u16 device_us;
u16 host_interval;
u16 device_interval;
u16 next_active_frame;
u16 start_active_frame;
s16 num_hs_transfers;
struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES];
u32 ls_start_schedule_slice;
u16 ntd;
u8 *dw_align_buf;
dma_addr_t dw_align_buf_dma;
#ifndef NO_GNU
struct list_head qtd_list;
#else
List_t qtd_list;
#endif
struct dwc2_host_chan *channel;
#ifndef NO_GNU
struct list_head qh_list_entry;
#else
ListItem_t qh_list_entry;
#endif
struct dwc2_dma_desc *desc_list;
dma_addr_t desc_list_dma;
u32 desc_list_sz;
u32 *n_bytes;
struct timer_list unreserve_timer;
struct dwc2_tt *dwc_tt;
int ttport;
unsigned tt_buffer_dirty:1;
unsigned unreserve_pending:1;
unsigned schedule_low_speed:1;
};
/**
* struct dwc2_qtd - Software queue transfer descriptor (QTD)
*
* @control_phase: Current phase for control transfers (Setup, Data, or
* Status)
* @in_process: Indicates if this QTD is currently processed by HW
* @data_toggle: Determines the PID of the next data packet for the
* data phase of control transfers. Ignored for other
* transfer types. One of the following values:
* - DWC2_HC_PID_DATA0
* - DWC2_HC_PID_DATA1
* @complete_split: Keeps track of the current split type for FS/LS
* endpoints on a HS Hub
* @isoc_split_pos: Position of the ISOC split in full/low speed
* @isoc_frame_index: Index of the next frame descriptor for an isochronous
* transfer. A frame descriptor describes the buffer
* position and length of the data to be transferred in the
* next scheduled (micro)frame of an isochronous transfer.
* It also holds status for that transaction. The frame
* index starts at 0.
* @isoc_split_offset: Position of the ISOC split in the buffer for the
* current frame
* @ssplit_out_xfer_count: How many bytes transferred during SSPLIT OUT
* @error_count: Holds the number of bus errors that have occurred for
* a transaction within this transfer
* @n_desc: Number of DMA descriptors for this QTD
* @isoc_frame_index_last: Last activated frame (packet) index, used in
* descriptor DMA mode only
* @urb: URB for this transfer
* @qh: Queue head for this QTD
* @qtd_list_entry: For linking to the QH's list of QTDs
*
* A Queue Transfer Descriptor (QTD) holds the state of a bulk, control,
* interrupt, or isochronous transfer. A single QTD is created for each URB
* (of one of these types) submitted to the HCD. The transfer associated with
* a QTD may require one or multiple transactions.
*
* A QTD is linked to a Queue Head, which is entered in either the
* non-periodic or periodic schedule for execution. When a QTD is chosen for
* execution, some or all of its transactions may be executed. After
* execution, the state of the QTD is updated. The QTD may be retired if all
* its transactions are complete or if an error occurred. Otherwise, it
* remains in the schedule so more transactions can be executed later.
*/
struct dwc2_qtd {
enum dwc2_control_phase control_phase;
u8 in_process;
u8 data_toggle;
u8 complete_split;
u8 isoc_split_pos;
u16 isoc_frame_index;
u16 isoc_split_offset;
u16 isoc_td_last;
u16 isoc_td_first;
u32 ssplit_out_xfer_count;
u8 error_count;
u8 n_desc;
u16 isoc_frame_index_last;
struct dwc2_hcd_urb *urb;
struct dwc2_qh *qh;
#ifndef NO_GNU
struct list_head qtd_list_entry;
#else
ListItem_t qtd_list_entry;
#endif
};
#ifdef DEBUG
struct hc_xfer_info {
struct dwc2_hsotg *hsotg;
struct dwc2_host_chan *chan;
};
#endif
u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg);
/* Gets the struct usb_hcd that contains a struct dwc2_hsotg */
static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg)
{
return (struct usb_hcd *)hsotg->priv;
}
/*
* Inline used to disable one channel interrupt. Channel interrupts are
* disabled when the channel is halted or released by the interrupt handler.
* There is no need to handle further interrupts of that type until the
* channel is re-assigned. In fact, subsequent handling may cause crashes
* because the channel structures are cleaned up when the channel is released.
*/
static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr)
{
u32 mask = dwc2_readl(hsotg->regs + HCINTMSK(chnum));
mask &= ~intr;
dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum));
}
void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan);
void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
enum dwc2_halt_status halt_status);
void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan);
/*
* Reads HPRT0 in preparation to modify. It keeps the WC bits 0 so that if they
* are read as 1, they won't clear when written back.
*/
static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg)
{
u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0);
hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG);
return hprt0;
}
static inline u8 dwc2_hcd_get_ep_num(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->ep_num;
}
static inline u8 dwc2_hcd_get_pipe_type(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_type;
}
static inline u16 dwc2_hcd_get_mps(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->mps;
}
static inline u8 dwc2_hcd_get_dev_addr(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->dev_addr;
}
static inline u8 dwc2_hcd_is_pipe_isoc(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_type == USB_ENDPOINT_XFER_ISOC;
}
static inline u8 dwc2_hcd_is_pipe_int(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_type == USB_ENDPOINT_XFER_INT;
}
static inline u8 dwc2_hcd_is_pipe_bulk(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_type == USB_ENDPOINT_XFER_BULK;
}
static inline u8 dwc2_hcd_is_pipe_control(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_type == USB_ENDPOINT_XFER_CONTROL;
}
static inline u8 dwc2_hcd_is_pipe_in(struct dwc2_hcd_pipe_info *pipe)
{
return pipe->pipe_dir == USB_DIR_IN;
}
static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
{
return !dwc2_hcd_is_pipe_in(pipe);
}
void dwc2_hcd_irq(struct dwc2_hsotg *hsotg);
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, struct usb_hcd *hcd);
void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
/* Transaction Execution Functions */
enum dwc2_transaction_type dwc2_hcd_select_transactions(
struct dwc2_hsotg *hsotg);
void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg,
enum dwc2_transaction_type tr_type);
/* Schedule Queue Functions */
/* Implemented in hcd_queue.c */
struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
struct dwc2_hcd_urb *urb,
gfp_t mem_flags);
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
void dwc2_hcd_qh_deactivate(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
int sched_csplit);
void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb);
int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
struct dwc2_qh *qh);
/* Unlinks and frees a QTD */
static inline void dwc2_hcd_qtd_unlink_and_free(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd,
struct dwc2_qh *qh)
{
list_del(&qtd->qtd_list_entry);
#ifndef NO_GNU
kfree(qtd);
#else
list_add_tail(&qtd->qtd_list_entry, &hsotg->free_qtd_list);
#endif
qtd = NULL;
}
/* Descriptor DMA support functions */
void dwc2_hcd_start_xfer_ddma(struct dwc2_hsotg *hsotg,
struct dwc2_qh *qh);
void dwc2_hcd_complete_xfer_ddma(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan, int chnum,
enum dwc2_halt_status halt_status);
int dwc2_hcd_qh_init_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
gfp_t mem_flags);
void dwc2_hcd_qh_free_ddma(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh);
/* Check if QH is non-periodic */
#define dwc2_qh_is_non_per(_qh_ptr_) \
((_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_BULK || \
(_qh_ptr_)->ep_type == USB_ENDPOINT_XFER_CONTROL)
#ifdef CONFIG_USB_DWC2_DEBUG_PERIODIC
static inline bool dbg_hc(struct dwc2_host_chan *hc) { return true; }
static inline bool dbg_qh(struct dwc2_qh *qh) { return true; }
static inline bool dbg_urb(struct urb *urb) { return true; }
static inline bool dbg_perio(void) { return true; }
#else /* !CONFIG_USB_DWC2_DEBUG_PERIODIC */
static inline bool dbg_hc(struct dwc2_host_chan *hc)
{
return hc->ep_type == USB_ENDPOINT_XFER_BULK ||
hc->ep_type == USB_ENDPOINT_XFER_CONTROL;
}
static inline bool dbg_qh(struct dwc2_qh *qh)
{
return qh->ep_type == USB_ENDPOINT_XFER_BULK ||
qh->ep_type == USB_ENDPOINT_XFER_CONTROL;
}
static inline bool dbg_urb(struct urb *urb)
{
return true;
/*return usb_pipetype(urb->pipe) == PIPE_BULK ||
usb_pipetype(urb->pipe) == PIPE_CONTROL;*/
}
static inline bool dbg_perio(void) { return false; }
#endif
/* High bandwidth multiplier as encoded in highspeed endpoint descriptors */
#define dwc2_hb_mult(wmaxpacketsize) (1 + (((wmaxpacketsize) >> 11) & 0x03))
/* Packet size for any kind of endpoint descriptor */
#define dwc2_max_packet(wmaxpacketsize) ((wmaxpacketsize) & 0x07ff)
/*
* Returns true if frame1 index is greater than frame2 index. The comparison
* is done modulo FRLISTEN_64_SIZE. This accounts for the rollover of the
* frame number when the max index frame number is reached.
*/
static inline bool dwc2_frame_idx_num_gt(u16 fr_idx1, u16 fr_idx2)
{
u16 diff = fr_idx1 - fr_idx2;
u16 sign = diff & (FRLISTEN_64_SIZE >> 1);
return diff && !sign;
}
/*
* Returns true if frame1 is less than or equal to frame2. The comparison is
* done modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the
* frame number when the max frame number is reached.
*/
static inline int dwc2_frame_num_le(u16 frame1, u16 frame2)
{
return ((frame2 - frame1) & HFNUM_MAX_FRNUM) <= (HFNUM_MAX_FRNUM >> 1);
}
/*
* Returns true if frame1 is greater than frame2. The comparison is done
* modulo HFNUM_MAX_FRNUM. This accounts for the rollover of the frame
* number when the max frame number is reached.
*/
static inline int dwc2_frame_num_gt(u16 frame1, u16 frame2)
{
return (frame1 != frame2) &&
((frame1 - frame2) & HFNUM_MAX_FRNUM) < (HFNUM_MAX_FRNUM >> 1);
}
/*
* Increments frame by the amount specified by inc. The addition is done
* modulo HFNUM_MAX_FRNUM. Returns the incremented value.
*/
static inline u16 dwc2_frame_num_inc(u16 frame, u16 inc)
{
return (frame + inc) & HFNUM_MAX_FRNUM;
}
static inline u16 dwc2_frame_num_dec(u16 frame, u16 dec)
{
return (frame + HFNUM_MAX_FRNUM + 1 - dec) & HFNUM_MAX_FRNUM;
}
static inline u16 dwc2_full_frame_num(u16 frame)
{
return (frame & HFNUM_MAX_FRNUM) >> 3;
}
static inline u16 dwc2_micro_frame_num(u16 frame)
{
return frame & 0x7;
}
/*
* Returns the Core Interrupt Status register contents, ANDed with the Core
* Interrupt Mask register contents
*/
static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg)
{
return dwc2_readl(hsotg->regs + GINTSTS) &
dwc2_readl(hsotg->regs + GINTMSK);
}
static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb)
{
return dwc2_urb->status;
}
static inline u32 dwc2_hcd_urb_get_actual_length(
struct dwc2_hcd_urb *dwc2_urb)
{
return dwc2_urb->actual_length;
}
static inline u32 dwc2_hcd_urb_get_error_count(struct dwc2_hcd_urb *dwc2_urb)
{
return dwc2_urb->error_count;
}
static inline void dwc2_hcd_urb_set_iso_desc_params(
struct dwc2_hcd_urb *dwc2_urb, int desc_num, u32 offset,
u32 length)
{
dwc2_urb->iso_descs[desc_num].offset = offset;
dwc2_urb->iso_descs[desc_num].length = length;
}
static inline u32 dwc2_hcd_urb_get_iso_desc_status(
struct dwc2_hcd_urb *dwc2_urb, int desc_num)
{
return dwc2_urb->iso_descs[desc_num].status;
}
static inline u32 dwc2_hcd_urb_get_iso_desc_actual_length(
struct dwc2_hcd_urb *dwc2_urb, int desc_num)
{
return dwc2_urb->iso_descs[desc_num].actual_length;
}
static inline int dwc2_hcd_is_bandwidth_allocated(struct dwc2_hsotg *hsotg,
struct usb_host_endpoint *ep)
{
struct dwc2_qh *qh = (struct dwc2_qh *)ep->hcpriv;
#ifndef NO_GNU
if (qh && !list_empty(&qh->qh_list_entry))
return 1;
#else
if (qh && !list_item_empty(&qh->qh_list_entry))
return 1;
#endif
return 0;
}
static inline u16 dwc2_hcd_get_ep_bandwidth(struct dwc2_hsotg *hsotg,
struct usb_host_endpoint *ep)
{
struct dwc2_qh *qh = (struct dwc2_qh *)ep->hcpriv;
if (!qh) {
WARN_ON(1);
return 0;
}
return qh->host_us;
}
void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
struct dwc2_host_chan *chan, int chnum,
struct dwc2_qtd *qtd);
/* HCD Core API */
/**
* dwc2_handle_hcd_intr() - Called on every hardware interrupt
*
* @hsotg: The DWC2 HCD
*
* Returns IRQ_HANDLED if interrupt is handled
* Return IRQ_NONE if interrupt is not handled
*/
irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_stop() - Halts the DWC_otg host mode operation
*
* @hsotg: The DWC2 HCD
*/
void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
* and 0 otherwise
*
* @hsotg: The DWC2 HCD
*/
int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_state() - Dumps hsotg state
*
* @hsotg: The DWC2 HCD
*
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg);
/**
* dwc2_hcd_dump_frrem() - Dumps the average frame remaining at SOF
*
* @hsotg: The DWC2 HCD
*
* This can be used to determine average interrupt latency. Frame remaining is
* also shown for start transfer and two additional sample points.
*
* NOTE: This function will be removed once the peripheral controller code
* is integrated and the driver is stable
*/
void dwc2_hcd_dump_frrem(struct dwc2_hsotg *hsotg);
/* URB interface */
/* Transfer flags */
#define URB_GIVEBACK_ASAP 0x1
#define URB_SEND_ZERO_PACKET 0x2
/* Host driver callbacks */
struct dwc2_tt *dwc2_host_get_tt_info(struct dwc2_hsotg *hsotg,
void *context, gfp_t mem_flags,
int *ttport);
void dwc2_host_put_tt_info(struct dwc2_hsotg *hsotg,
struct dwc2_tt *dwc_tt);
int dwc2_host_get_speed(struct dwc2_hsotg *hsotg, void *context);
void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
int status);
#ifdef DEBUG
/*
* Macro to sample the remaining PHY clocks left in the current frame. This
* may be used during debugging to determine the average time it takes to
* execute sections of code. There are two possible sample points, "a" and
* "b", so the _letter_ argument must be one of these values.
*
* To dump the average sample times, read the "hcd_frrem" sysfs attribute. For
* example, "cat /sys/devices/lm0/hcd_frrem".
*/
#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) \
do { \
struct hfnum_data _hfnum_; \
struct dwc2_qtd *_qtd_; \
\
_qtd_ = list_entry((_qh_)->qtd_list.next, struct dwc2_qtd, \
qtd_list_entry); \
if (usb_pipeint(_qtd_->urb->pipe) && \
(_qh_)->start_active_frame != 0 && !_qtd_->complete_split) { \
_hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \
switch (_hfnum_.b.frnum & 0x7) { \
case 7: \
(_hcd_)->hfnum_7_samples_##_letter_++; \
(_hcd_)->hfnum_7_frrem_accum_##_letter_ += \
_hfnum_.b.frrem; \
break; \
case 0: \
(_hcd_)->hfnum_0_samples_##_letter_++; \
(_hcd_)->hfnum_0_frrem_accum_##_letter_ += \
_hfnum_.b.frrem; \
break; \
default: \
(_hcd_)->hfnum_other_samples_##_letter_++; \
(_hcd_)->hfnum_other_frrem_accum_##_letter_ += \
_hfnum_.b.frrem; \
break; \
} \
} \
} while (0)
#else
#define dwc2_sample_frrem(_hcd_, _qh_, _letter_) do {} while (0)
#endif
void dwc2_hcd_start_isr(struct dwc2_hsotg *hsotg);
#endif /* __DWC2_HCD_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,867 @@
/*
* hw.h - DesignWare HS OTG Controller hardware definitions
*
* Copyright 2004-2013 Synopsys, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
#ifndef __DWC2_HW_H__
#define __DWC2_HW_H__
#define HSOTG_REG(x) (x)
#define GOTGCTL HSOTG_REG(0x000)
#define GOTGCTL_CHIRPEN BIT(27)
#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22)
#define GOTGCTL_MULT_VALID_BC_SHIFT 22
#define GOTGCTL_OTGVER BIT(20)
#define GOTGCTL_BSESVLD BIT(19)
#define GOTGCTL_ASESVLD BIT(18)
#define GOTGCTL_DBNC_SHORT BIT(17)
#define GOTGCTL_CONID_B BIT(16)
#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15)
#define GOTGCTL_DEVHNPEN BIT(11)
#define GOTGCTL_HSTSETHNPEN BIT(10)
#define GOTGCTL_HNPREQ BIT(9)
#define GOTGCTL_HSTNEGSCS BIT(8)
#define GOTGCTL_SESREQ BIT(1)
#define GOTGCTL_SESREQSCS BIT(0)
#define GOTGINT HSOTG_REG(0x004)
#define GOTGINT_DBNCE_DONE BIT(19)
#define GOTGINT_A_DEV_TOUT_CHG BIT(18)
#define GOTGINT_HST_NEG_DET BIT(17)
#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9)
#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8)
#define GOTGINT_SES_END_DET BIT(2)
#define GAHBCFG HSOTG_REG(0x008)
#define GAHBCFG_AHB_SINGLE BIT(23)
#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22)
#define GAHBCFG_REM_MEM_SUPP BIT(21)
#define GAHBCFG_P_TXF_EMP_LVL BIT(8)
#define GAHBCFG_NP_TXF_EMP_LVL BIT(7)
#define GAHBCFG_DMA_EN BIT(5)
#define GAHBCFG_HBSTLEN_MASK (0xf << 1)
#define GAHBCFG_HBSTLEN_SHIFT 1
#define GAHBCFG_HBSTLEN_SINGLE 0
#define GAHBCFG_HBSTLEN_INCR 1
#define GAHBCFG_HBSTLEN_INCR4 3
#define GAHBCFG_HBSTLEN_INCR8 5
#define GAHBCFG_HBSTLEN_INCR16 7
#define GAHBCFG_GLBL_INTR_EN BIT(0)
#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \
GAHBCFG_NP_TXF_EMP_LVL | \
GAHBCFG_DMA_EN | \
GAHBCFG_GLBL_INTR_EN)
#define GUSBCFG HSOTG_REG(0x00C)
#define GUSBCFG_FORCEDEVMODE BIT(30)
#define GUSBCFG_FORCEHOSTMODE BIT(29)
#define GUSBCFG_TXENDDELAY BIT(28)
#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27)
#define GUSBCFG_ICUSBCAP BIT(26)
#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25)
#define GUSBCFG_INDICATORPASSTHROUGH BIT(24)
#define GUSBCFG_INDICATORCOMPLEMENT BIT(23)
#define GUSBCFG_TERMSELDLPULSE BIT(22)
#define GUSBCFG_ULPI_INT_VBUS_IND BIT(21)
#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20)
#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19)
#define GUSBCFG_ULPI_AUTO_RES BIT(18)
#define GUSBCFG_ULPI_FS_LS BIT(17)
#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16)
#define GUSBCFG_PHY_LP_CLK_SEL BIT(15)
#define GUSBCFG_USBTRDTIM_MASK (0xf << 10)
#define GUSBCFG_USBTRDTIM_SHIFT 10
#define GUSBCFG_HNPCAP BIT(9)
#define GUSBCFG_SRPCAP BIT(8)
#define GUSBCFG_DDRSEL BIT(7)
#define GUSBCFG_PHYSEL BIT(6)
#define GUSBCFG_FSINTF BIT(5)
#define GUSBCFG_ULPI_UTMI_SEL BIT(4)
#define GUSBCFG_PHYIF16 BIT(3)
#define GUSBCFG_PHYIF8 (0 << 3)
#define GUSBCFG_TOUTCAL_MASK (0x7 << 0)
#define GUSBCFG_TOUTCAL_SHIFT 0
#define GUSBCFG_TOUTCAL_LIMIT 0x7
#define GUSBCFG_TOUTCAL(_x) ((_x) << 0)
#define GRSTCTL HSOTG_REG(0x010)
#define GRSTCTL_AHBIDLE BIT(31)
#define GRSTCTL_DMAREQ BIT(30)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f
#define GRSTCTL_TXFNUM(_x) ((_x) << 6)
#define GRSTCTL_TXFFLSH BIT(5)
#define GRSTCTL_RXFFLSH BIT(4)
#define GRSTCTL_IN_TKNQ_FLSH BIT(3)
#define GRSTCTL_FRMCNTRRST BIT(2)
#define GRSTCTL_HSFTRST BIT(1)
#define GRSTCTL_CSFTRST BIT(0)
#define GINTSTS HSOTG_REG(0x014)
#define GINTMSK HSOTG_REG(0x018)
#define GINTSTS_WKUPINT BIT(31)
#define GINTSTS_SESSREQINT BIT(30)
#define GINTSTS_DISCONNINT BIT(29)
#define GINTSTS_CONIDSTSCHNG BIT(28)
#define GINTSTS_LPMTRANRCVD BIT(27)
#define GINTSTS_PTXFEMP BIT(26)
#define GINTSTS_HCHINT BIT(25)
#define GINTSTS_PRTINT BIT(24)
#define GINTSTS_RESETDET BIT(23)
#define GINTSTS_FET_SUSP BIT(22)
#define GINTSTS_INCOMPL_IP BIT(21)
#define GINTSTS_INCOMPL_SOOUT BIT(21)
#define GINTSTS_INCOMPL_SOIN BIT(20)
#define GINTSTS_OEPINT BIT(19)
#define GINTSTS_IEPINT BIT(18)
#define GINTSTS_EPMIS BIT(17)
#define GINTSTS_RESTOREDONE BIT(16)
#define GINTSTS_EOPF BIT(15)
#define GINTSTS_ISOUTDROP BIT(14)
#define GINTSTS_ENUMDONE BIT(13)
#define GINTSTS_USBRST BIT(12)
#define GINTSTS_USBSUSP BIT(11)
#define GINTSTS_ERLYSUSP BIT(10)
#define GINTSTS_I2CINT BIT(9)
#define GINTSTS_ULPI_CK_INT BIT(8)
#define GINTSTS_GOUTNAKEFF BIT(7)
#define GINTSTS_GINNAKEFF BIT(6)
#define GINTSTS_NPTXFEMP BIT(5)
#define GINTSTS_RXFLVL BIT(4)
#define GINTSTS_SOF BIT(3)
#define GINTSTS_OTGINT BIT(2)
#define GINTSTS_MODEMIS BIT(1)
#define GINTSTS_CURMODE_HOST BIT(0)
#define GRXSTSR HSOTG_REG(0x01C)
#define GRXSTSP HSOTG_REG(0x020)
#define GRXSTS_FN_MASK (0x7f << 25)
#define GRXSTS_FN_SHIFT 25
#define GRXSTS_PKTSTS_MASK (0xf << 17)
#define GRXSTS_PKTSTS_SHIFT 17
#define GRXSTS_PKTSTS_GLOBALOUTNAK 1
#define GRXSTS_PKTSTS_OUTRX 2
#define GRXSTS_PKTSTS_HCHIN 2
#define GRXSTS_PKTSTS_OUTDONE 3
#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3
#define GRXSTS_PKTSTS_SETUPDONE 4
#define GRXSTS_PKTSTS_DATATOGGLEERR 5
#define GRXSTS_PKTSTS_SETUPRX 6
#define GRXSTS_PKTSTS_HCHHALTED 7
#define GRXSTS_HCHNUM_MASK (0xf << 0)
#define GRXSTS_HCHNUM_SHIFT 0
#define GRXSTS_DPID_MASK (0x3 << 15)
#define GRXSTS_DPID_SHIFT 15
#define GRXSTS_BYTECNT_MASK (0x7ff << 4)
#define GRXSTS_BYTECNT_SHIFT 4
#define GRXSTS_EPNUM_MASK (0xf << 0)
#define GRXSTS_EPNUM_SHIFT 0
#define GRXFSIZ HSOTG_REG(0x024)
#define GRXFSIZ_DEPTH_MASK (0xffff << 0)
#define GRXFSIZ_DEPTH_SHIFT 0
#define GNPTXFSIZ HSOTG_REG(0x028)
/* Use FIFOSIZE_* constants to access this register */
#define GNPTXSTS HSOTG_REG(0x02C)
#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24)
#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24
#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16)
#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16
#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff)
#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0)
#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0
#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff)
#define GI2CCTL HSOTG_REG(0x0030)
#define GI2CCTL_BSYDNE BIT(31)
#define GI2CCTL_RW BIT(30)
#define GI2CCTL_I2CDATSE0 BIT(28)
#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26)
#define GI2CCTL_I2CDEVADDR_SHIFT 26
#define GI2CCTL_I2CSUSPCTL BIT(25)
#define GI2CCTL_ACK BIT(24)
#define GI2CCTL_I2CEN BIT(23)
#define GI2CCTL_ADDR_MASK (0x7f << 16)
#define GI2CCTL_ADDR_SHIFT 16
#define GI2CCTL_REGADDR_MASK (0xff << 8)
#define GI2CCTL_REGADDR_SHIFT 8
#define GI2CCTL_RWDATA_MASK (0xff << 0)
#define GI2CCTL_RWDATA_SHIFT 0
#define GPVNDCTL HSOTG_REG(0x0034)
#define GGPIO HSOTG_REG(0x0038)
#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16)
#define GUID HSOTG_REG(0x003c)
#define GSNPSID HSOTG_REG(0x0040)
#define GHWCFG1 HSOTG_REG(0x0044)
#define GHWCFG2 HSOTG_REG(0x0048)
#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31)
#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26)
#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26
#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24)
#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24
#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22)
#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22
#define GHWCFG2_MULTI_PROC_INT BIT(20)
#define GHWCFG2_DYNAMIC_FIFO BIT(19)
#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18)
#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14)
#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14
#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10)
#define GHWCFG2_NUM_DEV_EP_SHIFT 10
#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8)
#define GHWCFG2_FS_PHY_TYPE_SHIFT 8
#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0
#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1
#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2
#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3
#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6)
#define GHWCFG2_HS_PHY_TYPE_SHIFT 6
#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0
#define GHWCFG2_HS_PHY_TYPE_UTMI 1
#define GHWCFG2_HS_PHY_TYPE_ULPI 2
#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3
#define GHWCFG2_POINT2POINT BIT(5)
#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3)
#define GHWCFG2_ARCHITECTURE_SHIFT 3
#define GHWCFG2_SLAVE_ONLY_ARCH 0
#define GHWCFG2_EXT_DMA_ARCH 1
#define GHWCFG2_INT_DMA_ARCH 2
#define GHWCFG2_OP_MODE_MASK (0x7 << 0)
#define GHWCFG2_OP_MODE_SHIFT 0
#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0
#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1
#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2
#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3
#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4
#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5
#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6
#define GHWCFG2_OP_MODE_UNDEFINED 7
#define GHWCFG3 HSOTG_REG(0x004c)
#define GHWCFG3_DFIFO_DEPTH_MASK (0xffffUL << 16)
#define GHWCFG3_DFIFO_DEPTH_SHIFT 16
#define GHWCFG3_OTG_LPM_EN BIT(15)
#define GHWCFG3_BC_SUPPORT BIT(14)
#define GHWCFG3_OTG_ENABLE_HSIC BIT(13)
#define GHWCFG3_ADP_SUPP BIT(12)
#define GHWCFG3_SYNCH_RESET_TYPE BIT(11)
#define GHWCFG3_OPTIONAL_FEATURES BIT(10)
#define GHWCFG3_VENDOR_CTRL_IF BIT(9)
#define GHWCFG3_I2C BIT(8)
#define GHWCFG3_OTG_FUNC BIT(7)
#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4)
#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4
#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0)
#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0
#define GHWCFG4 HSOTG_REG(0x0050)
#define GHWCFG4_DESC_DMA_DYN BIT(31)
#define GHWCFG4_DESC_DMA BIT(30)
#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26)
#define GHWCFG4_NUM_IN_EPS_SHIFT 26
#define GHWCFG4_DED_FIFO_EN BIT(25)
#define GHWCFG4_DED_FIFO_SHIFT 25
#define GHWCFG4_SESSION_END_FILT_EN BIT(24)
#define GHWCFG4_B_VALID_FILT_EN BIT(23)
#define GHWCFG4_A_VALID_FILT_EN BIT(22)
#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21)
#define GHWCFG4_IDDIG_FILT_EN BIT(20)
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16)
#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14)
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1
#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2
#define GHWCFG4_XHIBER BIT(7)
#define GHWCFG4_HIBER BIT(6)
#define GHWCFG4_MIN_AHB_FREQ BIT(5)
#define GHWCFG4_POWER_OPTIMIZ BIT(4)
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0)
#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0
#define GLPMCFG HSOTG_REG(0x0054)
#define GLPMCFG_INV_SEL_HSIC BIT(31)
#define GLPMCFG_HSIC_CONNECT BIT(30)
#define GLPMCFG_RETRY_COUNT_STS_MASK (0x7 << 25)
#define GLPMCFG_RETRY_COUNT_STS_SHIFT 25
#define GLPMCFG_SEND_LPM BIT(24)
#define GLPMCFG_RETRY_COUNT_MASK (0x7 << 21)
#define GLPMCFG_RETRY_COUNT_SHIFT 21
#define GLPMCFG_LPM_CHAN_INDEX_MASK (0xf << 17)
#define GLPMCFG_LPM_CHAN_INDEX_SHIFT 17
#define GLPMCFG_SLEEP_STATE_RESUMEOK BIT(16)
#define GLPMCFG_PRT_SLEEP_STS BIT(15)
#define GLPMCFG_LPM_RESP_MASK (0x3 << 13)
#define GLPMCFG_LPM_RESP_SHIFT 13
#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8)
#define GLPMCFG_HIRD_THRES_SHIFT 8
#define GLPMCFG_HIRD_THRES_EN (0x10 << 8)
#define GLPMCFG_EN_UTMI_SLEEP BIT(7)
#define GLPMCFG_REM_WKUP_EN BIT(6)
#define GLPMCFG_HIRD_MASK (0xf << 2)
#define GLPMCFG_HIRD_SHIFT 2
#define GLPMCFG_APPL_RESP BIT(1)
#define GLPMCFG_LPM_CAP_EN BIT(0)
#define GPWRDN HSOTG_REG(0x0058)
#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24)
#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24
#define GPWRDN_ADP_INT BIT(23)
#define GPWRDN_BSESSVLD BIT(22)
#define GPWRDN_IDSTS BIT(21)
#define GPWRDN_LINESTATE_MASK (0x3 << 19)
#define GPWRDN_LINESTATE_SHIFT 19
#define GPWRDN_STS_CHGINT_MSK BIT(18)
#define GPWRDN_STS_CHGINT BIT(17)
#define GPWRDN_SRP_DET_MSK BIT(16)
#define GPWRDN_SRP_DET BIT(15)
#define GPWRDN_CONNECT_DET_MSK BIT(14)
#define GPWRDN_CONNECT_DET BIT(13)
#define GPWRDN_DISCONN_DET_MSK BIT(12)
#define GPWRDN_DISCONN_DET BIT(11)
#define GPWRDN_RST_DET_MSK BIT(10)
#define GPWRDN_RST_DET BIT(9)
#define GPWRDN_LNSTSCHG_MSK BIT(8)
#define GPWRDN_LNSTSCHG BIT(7)
#define GPWRDN_DIS_VBUS BIT(6)
#define GPWRDN_PWRDNSWTCH BIT(5)
#define GPWRDN_PWRDNRSTN BIT(4)
#define GPWRDN_PWRDNCLMP BIT(3)
#define GPWRDN_RESTORE BIT(2)
#define GPWRDN_PMUACTV BIT(1)
#define GPWRDN_PMUINTSEL BIT(0)
#define GDFIFOCFG HSOTG_REG(0x005c)
#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16)
#define GDFIFOCFG_EPINFOBASE_SHIFT 16
#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0)
#define GDFIFOCFG_GDFIFOCFG_SHIFT 0
#define ADPCTL HSOTG_REG(0x0060)
#define ADPCTL_AR_MASK (0x3 << 27)
#define ADPCTL_AR_SHIFT 27
#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26)
#define ADPCTL_ADP_SNS_INT_MSK BIT(25)
#define ADPCTL_ADP_PRB_INT_MSK BIT(24)
#define ADPCTL_ADP_TMOUT_INT BIT(23)
#define ADPCTL_ADP_SNS_INT BIT(22)
#define ADPCTL_ADP_PRB_INT BIT(21)
#define ADPCTL_ADPENA BIT(20)
#define ADPCTL_ADPRES BIT(19)
#define ADPCTL_ENASNS BIT(18)
#define ADPCTL_ENAPRB BIT(17)
#define ADPCTL_RTIM_MASK (0x7ff << 6)
#define ADPCTL_RTIM_SHIFT 6
#define ADPCTL_PRB_PER_MASK (0x3 << 4)
#define ADPCTL_PRB_PER_SHIFT 4
#define ADPCTL_PRB_DELTA_MASK (0x3 << 2)
#define ADPCTL_PRB_DELTA_SHIFT 2
#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0)
#define ADPCTL_PRB_DSCHRG_SHIFT 0
#define HPTXFSIZ HSOTG_REG(0x100)
/* Use FIFOSIZE_* constants to access this register */
#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4))
/* Use FIFOSIZE_* constants to access this register */
/* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */
#define FIFOSIZE_DEPTH_MASK (0xffffUL << 16)
#define FIFOSIZE_DEPTH_SHIFT 16
#define FIFOSIZE_STARTADDR_MASK (0xffff << 0)
#define FIFOSIZE_STARTADDR_SHIFT 0
#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff)
/* Device mode registers */
#define DCFG HSOTG_REG(0x800)
#define DCFG_DESCDMA_EN BIT(23)
#define DCFG_EPMISCNT_MASK (0x1f << 18)
#define DCFG_EPMISCNT_SHIFT 18
#define DCFG_EPMISCNT_LIMIT 0x1f
#define DCFG_EPMISCNT(_x) ((_x) << 18)
#define DCFG_PERFRINT_MASK (0x3 << 11)
#define DCFG_PERFRINT_SHIFT 11
#define DCFG_PERFRINT_LIMIT 0x3
#define DCFG_PERFRINT(_x) ((_x) << 11)
#define DCFG_DEVADDR_MASK (0x7f << 4)
#define DCFG_DEVADDR_SHIFT 4
#define DCFG_DEVADDR_LIMIT 0x7f
#define DCFG_DEVADDR(_x) ((_x) << 4)
#define DCFG_NZ_STS_OUT_HSHK BIT(2)
#define DCFG_DEVSPD_MASK (0x3 << 0)
#define DCFG_DEVSPD_SHIFT 0
#define DCFG_DEVSPD_HS 0
#define DCFG_DEVSPD_FS 1
#define DCFG_DEVSPD_LS 2
#define DCFG_DEVSPD_FS48 3
#define DCTL HSOTG_REG(0x804)
#define DCTL_PWRONPRGDONE BIT(11)
#define DCTL_CGOUTNAK BIT(10)
#define DCTL_SGOUTNAK BIT(9)
#define DCTL_CGNPINNAK BIT(8)
#define DCTL_SGNPINNAK BIT(7)
#define DCTL_TSTCTL_MASK (0x7 << 4)
#define DCTL_TSTCTL_SHIFT 4
#define DCTL_GOUTNAKSTS BIT(3)
#define DCTL_GNPINNAKSTS BIT(2)
#define DCTL_SFTDISCON BIT(1)
#define DCTL_RMTWKUPSIG BIT(0)
#define DSTS HSOTG_REG(0x808)
#define DSTS_SOFFN_MASK (0x3fff << 8)
#define DSTS_SOFFN_SHIFT 8
#define DSTS_SOFFN_LIMIT 0x3fff
#define DSTS_SOFFN(_x) ((_x) << 8)
#define DSTS_ERRATICERR BIT(3)
#define DSTS_ENUMSPD_MASK (0x3 << 1)
#define DSTS_ENUMSPD_SHIFT 1
#define DSTS_ENUMSPD_HS 0
#define DSTS_ENUMSPD_FS 1
#define DSTS_ENUMSPD_LS 2
#define DSTS_ENUMSPD_FS48 3
#define DSTS_SUSPSTS BIT(0)
#define DIEPMSK HSOTG_REG(0x810)
#define DIEPMSK_NAKMSK BIT(13)
#define DIEPMSK_BNAININTRMSK BIT(9)
#define DIEPMSK_TXFIFOUNDRNMSK BIT(8)
#define DIEPMSK_TXFIFOEMPTY BIT(7)
#define DIEPMSK_INEPNAKEFFMSK BIT(6)
#define DIEPMSK_INTKNEPMISMSK BIT(5)
#define DIEPMSK_INTKNTXFEMPMSK BIT(4)
#define DIEPMSK_TIMEOUTMSK BIT(3)
#define DIEPMSK_AHBERRMSK BIT(2)
#define DIEPMSK_EPDISBLDMSK BIT(1)
#define DIEPMSK_XFERCOMPLMSK BIT(0)
#define DOEPMSK HSOTG_REG(0x814)
#define DOEPMSK_BNAMSK BIT(9)
#define DOEPMSK_BACK2BACKSETUP BIT(6)
#define DOEPMSK_STSPHSERCVDMSK BIT(5)
#define DOEPMSK_OUTTKNEPDISMSK BIT(4)
#define DOEPMSK_SETUPMSK BIT(3)
#define DOEPMSK_AHBERRMSK BIT(2)
#define DOEPMSK_EPDISBLDMSK BIT(1)
#define DOEPMSK_XFERCOMPLMSK BIT(0)
#define DAINT HSOTG_REG(0x818)
#define DAINTMSK HSOTG_REG(0x81C)
#define DAINT_OUTEP_SHIFT 16
#define DAINT_OUTEP(_x) (1 << ((_x) + 16))
#define DAINT_INEP(_x) (1 << (_x))
#define DTKNQR1 HSOTG_REG(0x820)
#define DTKNQR2 HSOTG_REG(0x824)
#define DTKNQR3 HSOTG_REG(0x830)
#define DTKNQR4 HSOTG_REG(0x834)
#define DIEPEMPMSK HSOTG_REG(0x834)
#define DVBUSDIS HSOTG_REG(0x828)
#define DVBUSPULSE HSOTG_REG(0x82C)
#define DIEPCTL0 HSOTG_REG(0x900)
#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20))
#define DOEPCTL0 HSOTG_REG(0xB00)
#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20))
/* EP0 specialness:
* bits[29..28] - reserved (no SetD0PID, SetD1PID)
* bits[25..22] - should always be zero, this isn't a periodic endpoint
* bits[10..0] - MPS setting different for EP0
*/
#define D0EPCTL_MPS_MASK (0x3 << 0)
#define D0EPCTL_MPS_SHIFT 0
#define D0EPCTL_MPS_64 0
#define D0EPCTL_MPS_32 1
#define D0EPCTL_MPS_16 2
#define D0EPCTL_MPS_8 3
#define DXEPCTL_EPENA BIT(31)
#define DXEPCTL_EPDIS BIT(30)
#define DXEPCTL_SETD1PID BIT(29)
#define DXEPCTL_SETODDFR BIT(29)
#define DXEPCTL_SETD0PID BIT(28)
#define DXEPCTL_SETEVENFR BIT(28)
#define DXEPCTL_SNAK BIT(27)
#define DXEPCTL_CNAK BIT(26)
#define DXEPCTL_TXFNUM_MASK (0xf << 22)
#define DXEPCTL_TXFNUM_SHIFT 22
#define DXEPCTL_TXFNUM_LIMIT 0xf
#define DXEPCTL_TXFNUM(_x) ((_x) << 22)
#define DXEPCTL_STALL BIT(21)
#define DXEPCTL_SNP BIT(20)
#define DXEPCTL_EPTYPE_MASK (0x3 << 18)
#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18)
#define DXEPCTL_EPTYPE_ISO (0x1 << 18)
#define DXEPCTL_EPTYPE_BULK (0x2 << 18)
#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18)
#define DXEPCTL_NAKSTS BIT(17)
#define DXEPCTL_DPID BIT(16)
#define DXEPCTL_EOFRNUM BIT(16)
#define DXEPCTL_USBACTEP BIT(15)
#define DXEPCTL_NEXTEP_MASK (0xf << 11)
#define DXEPCTL_NEXTEP_SHIFT 11
#define DXEPCTL_NEXTEP_LIMIT 0xf
#define DXEPCTL_NEXTEP(_x) ((_x) << 11)
#define DXEPCTL_MPS_MASK (0x7ff << 0)
#define DXEPCTL_MPS_SHIFT 0
#define DXEPCTL_MPS_LIMIT 0x7ff
#define DXEPCTL_MPS(_x) ((_x) << 0)
#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20))
#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20))
#define DXEPINT_SETUP_RCVD BIT(15)
#define DXEPINT_NYETINTRPT BIT(14)
#define DXEPINT_NAKINTRPT BIT(13)
#define DXEPINT_BBLEERRINTRPT BIT(12)
#define DXEPINT_PKTDRPSTS BIT(11)
#define DXEPINT_BNAINTR BIT(9)
#define DXEPINT_TXFIFOUNDRN BIT(8)
#define DXEPINT_OUTPKTERR BIT(8)
#define DXEPINT_TXFEMP BIT(7)
#define DXEPINT_INEPNAKEFF BIT(6)
#define DXEPINT_BACK2BACKSETUP BIT(6)
#define DXEPINT_INTKNEPMIS BIT(5)
#define DXEPINT_STSPHSERCVD BIT(5)
#define DXEPINT_INTKNTXFEMP BIT(4)
#define DXEPINT_OUTTKNEPDIS BIT(4)
#define DXEPINT_TIMEOUT BIT(3)
#define DXEPINT_SETUP BIT(3)
#define DXEPINT_AHBERR BIT(2)
#define DXEPINT_EPDISBLD BIT(1)
#define DXEPINT_XFERCOMPL BIT(0)
#define DIEPTSIZ0 HSOTG_REG(0x910)
#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19)
#define DIEPTSIZ0_PKTCNT_SHIFT 19
#define DIEPTSIZ0_PKTCNT_LIMIT 0x3
#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19)
#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0)
#define DIEPTSIZ0_XFERSIZE_SHIFT 0
#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f
#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0)
#define DOEPTSIZ0 HSOTG_REG(0xB10)
#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29)
#define DOEPTSIZ0_SUPCNT_SHIFT 29
#define DOEPTSIZ0_SUPCNT_LIMIT 0x3
#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29)
#define DOEPTSIZ0_PKTCNT BIT(19)
#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0)
#define DOEPTSIZ0_XFERSIZE_SHIFT 0
#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20))
#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20))
#define DXEPTSIZ_MC_MASK (0x3 << 29)
#define DXEPTSIZ_MC_SHIFT 29
#define DXEPTSIZ_MC_LIMIT 0x3
#define DXEPTSIZ_MC(_x) ((_x) << 29)
#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19)
#define DXEPTSIZ_PKTCNT_SHIFT 19
#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff
#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff)
#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19)
#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0)
#define DXEPTSIZ_XFERSIZE_SHIFT 0
#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff
#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff)
#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0)
#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20))
#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20))
#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20))
#define PCGCTL HSOTG_REG(0x0e00)
#define PCGCTL_IF_DEV_MODE BIT(31)
#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29)
#define PCGCTL_P2HD_PRT_SPD_SHIFT 29
#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27)
#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27
#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20)
#define PCGCTL_MAC_DEV_ADDR_SHIFT 20
#define PCGCTL_MAX_TERMSEL BIT(19)
#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17)
#define PCGCTL_MAX_XCVRSELECT_SHIFT 17
#define PCGCTL_PORT_POWER BIT(16)
#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14)
#define PCGCTL_PRT_CLK_SEL_SHIFT 14
#define PCGCTL_ESS_REG_RESTORED BIT(13)
#define PCGCTL_EXTND_HIBER_SWITCH BIT(12)
#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11)
#define PCGCTL_ENBL_EXTND_HIBER BIT(10)
#define PCGCTL_RESTOREMODE BIT(9)
#define PCGCTL_RESETAFTSUSP BIT(8)
#define PCGCTL_DEEP_SLEEP BIT(7)
#define PCGCTL_PHY_IN_SLEEP BIT(6)
#define PCGCTL_ENBL_SLEEP_GATING BIT(5)
#define PCGCTL_RSTPDWNMODULE BIT(3)
#define PCGCTL_PWRCLMP BIT(2)
#define PCGCTL_GATEHCLK BIT(1)
#define PCGCTL_STOPPCLK BIT(0)
#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000))
/* Host Mode Registers */
#define HCFG HSOTG_REG(0x0400)
#define HCFG_MODECHTIMEN BIT(31)
#define HCFG_PERSCHEDENA BIT(26)
#define HCFG_FRLISTEN_MASK (0x3 << 24)
#define HCFG_FRLISTEN_SHIFT 24
#define HCFG_FRLISTEN_8 (0 << 24)
#define FRLISTEN_8_SIZE 8
#define HCFG_FRLISTEN_16 BIT(24)
#define FRLISTEN_16_SIZE 16
#define HCFG_FRLISTEN_32 (2 << 24)
#define FRLISTEN_32_SIZE 32
#define HCFG_FRLISTEN_64 (3 << 24)
#define FRLISTEN_64_SIZE 64
#define HCFG_DESCDMA BIT(23)
#define HCFG_RESVALID_MASK (0xff << 8)
#define HCFG_RESVALID_SHIFT 8
#define HCFG_ENA32KHZ BIT(7)
#define HCFG_FSLSSUPP BIT(2)
#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0)
#define HCFG_FSLSPCLKSEL_SHIFT 0
#define HCFG_FSLSPCLKSEL_30_60_MHZ 0
#define HCFG_FSLSPCLKSEL_48_MHZ 1
#define HCFG_FSLSPCLKSEL_6_MHZ 2
#define HFIR HSOTG_REG(0x0404)
#define HFIR_FRINT_MASK (0xffff << 0)
#define HFIR_FRINT_SHIFT 0
#define HFIR_RLDCTRL BIT(16)
#define HFNUM HSOTG_REG(0x0408)
#define HFNUM_FRREM_MASK (0xffffUL << 16)
#define HFNUM_FRREM_SHIFT 16
#define HFNUM_FRNUM_MASK (0xffff << 0)
#define HFNUM_FRNUM_SHIFT 0
#define HFNUM_MAX_FRNUM 0x3fff
#define HPTXSTS HSOTG_REG(0x0410)
#define TXSTS_QTOP_ODD BIT(31)
#define TXSTS_QTOP_CHNEP_MASK (0xf << 27)
#define TXSTS_QTOP_CHNEP_SHIFT 27
#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25)
#define TXSTS_QTOP_TOKEN_SHIFT 25
#define TXSTS_QTOP_TERMINATE BIT(24)
#define TXSTS_QSPCAVAIL_MASK (0xff << 16)
#define TXSTS_QSPCAVAIL_SHIFT 16
#define TXSTS_FSPCAVAIL_MASK (0xffff << 0)
#define TXSTS_FSPCAVAIL_SHIFT 0
#define HAINT HSOTG_REG(0x0414)
#define HAINTMSK HSOTG_REG(0x0418)
#define HFLBADDR HSOTG_REG(0x041c)
#define HPRT0 HSOTG_REG(0x0440)
#define HPRT0_SPD_MASK (0x3 << 17)
#define HPRT0_SPD_SHIFT 17
#define HPRT0_SPD_HIGH_SPEED 0
#define HPRT0_SPD_FULL_SPEED 1
#define HPRT0_SPD_LOW_SPEED 2
#define HPRT0_TSTCTL_MASK (0xf << 13)
#define HPRT0_TSTCTL_SHIFT 13
#define HPRT0_PWR BIT(12)
#define HPRT0_LNSTS_MASK (0x3 << 10)
#define HPRT0_LNSTS_SHIFT 10
#define HPRT0_RST BIT(8)
#define HPRT0_SUSP BIT(7)
#define HPRT0_RES BIT(6)
#define HPRT0_OVRCURRCHG BIT(5)
#define HPRT0_OVRCURRACT BIT(4)
#define HPRT0_ENACHG BIT(3)
#define HPRT0_ENA BIT(2)
#define HPRT0_CONNDET BIT(1)
#define HPRT0_CONNSTS BIT(0)
#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch))
#define HCCHAR_CHENA BIT(31)
#define HCCHAR_CHDIS BIT(30)
#define HCCHAR_ODDFRM BIT(29)
#define HCCHAR_DEVADDR_MASK (0x7f << 22)
#define HCCHAR_DEVADDR_SHIFT 22
#define HCCHAR_MULTICNT_MASK (0x3 << 20)
#define HCCHAR_MULTICNT_SHIFT 20
#define HCCHAR_EPTYPE_MASK (0x3 << 18)
#define HCCHAR_EPTYPE_SHIFT 18
#define HCCHAR_LSPDDEV BIT(17)
#define HCCHAR_EPDIR BIT(15)
#define HCCHAR_EPNUM_MASK (0xf << 11)
#define HCCHAR_EPNUM_SHIFT 11
#define HCCHAR_MPS_MASK (0x7ff << 0)
#define HCCHAR_MPS_SHIFT 0
#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch))
#define HCSPLT_SPLTENA BIT(31)
#define HCSPLT_COMPSPLT BIT(16)
#define HCSPLT_XACTPOS_MASK (0x3 << 14)
#define HCSPLT_XACTPOS_SHIFT 14
#define HCSPLT_XACTPOS_MID 0
#define HCSPLT_XACTPOS_END 1
#define HCSPLT_XACTPOS_BEGIN 2
#define HCSPLT_XACTPOS_ALL 3
#define HCSPLT_HUBADDR_MASK (0x7f << 7)
#define HCSPLT_HUBADDR_SHIFT 7
#define HCSPLT_PRTADDR_MASK (0x7f << 0)
#define HCSPLT_PRTADDR_SHIFT 0
#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch))
#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch))
#define HCINTMSK_RESERVED14_31 (0x3ffff << 14)
#define HCINTMSK_FRM_LIST_ROLL BIT(13)
#define HCINTMSK_XCS_XACT BIT(12)
#define HCINTMSK_BNA BIT(11)
#define HCINTMSK_DATATGLERR BIT(10)
#define HCINTMSK_FRMOVRUN BIT(9)
#define HCINTMSK_BBLERR BIT(8)
#define HCINTMSK_XACTERR BIT(7)
#define HCINTMSK_NYET BIT(6)
#define HCINTMSK_ACK BIT(5)
#define HCINTMSK_NAK BIT(4)
#define HCINTMSK_STALL BIT(3)
#define HCINTMSK_AHBERR BIT(2)
#define HCINTMSK_CHHLTD BIT(1)
#define HCINTMSK_XFERCOMPL BIT(0)
#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch))
#define TSIZ_DOPNG BIT(31)
#define TSIZ_SC_MC_PID_MASK (0x3 << 29)
#define TSIZ_SC_MC_PID_SHIFT 29
#define TSIZ_SC_MC_PID_DATA0 0
#define TSIZ_SC_MC_PID_DATA2 1
#define TSIZ_SC_MC_PID_DATA1 2
#define TSIZ_SC_MC_PID_MDATA 3
#define TSIZ_SC_MC_PID_SETUP 3
#define TSIZ_PKTCNT_MASK (0x3ff << 19)
#define TSIZ_PKTCNT_SHIFT 19
#define TSIZ_NTD_MASK (0xff << 8)
#define TSIZ_NTD_SHIFT 8
#define TSIZ_SCHINFO_MASK (0xff << 0)
#define TSIZ_SCHINFO_SHIFT 0
#define TSIZ_XFERSIZE_MASK (0x7ffff << 0)
#define TSIZ_XFERSIZE_SHIFT 0
#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch))
#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch))
#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch))
/**
* struct dwc2_dma_desc - DMA descriptor structure,
* used for both host and gadget modes
*
* @status: DMA descriptor status quadlet
* @buf: DMA descriptor data buffer pointer
*
* DMA Descriptor structure contains two quadlets:
* Status quadlet and Data buffer pointer.
*/
struct dwc2_dma_desc {
u32 status;
u32 buf;
};//__packed
/* Host Mode DMA descriptor status quadlet */
#define HOST_DMA_A BIT(31)
#define HOST_DMA_STS_MASK (0x3 << 28)
#define HOST_DMA_STS_SHIFT 28
#define HOST_DMA_STS_PKTERR BIT(28)
#define HOST_DMA_EOL BIT(26)
#define HOST_DMA_IOC BIT(25)
#define HOST_DMA_SUP BIT(24)
#define HOST_DMA_ALT_QTD BIT(23)
#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17)
#define HOST_DMA_QTD_OFFSET_SHIFT 17
#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0)
#define HOST_DMA_ISOC_NBYTES_SHIFT 0
#define HOST_DMA_NBYTES_MASK (0x1ffff << 0)
#define HOST_DMA_NBYTES_SHIFT 0
#define HOST_DMA_NBYTES_LIMIT 131071
/* Device Mode DMA descriptor status quadlet */
#define DEV_DMA_BUFF_STS_MASK (0x3 << 30)
#define DEV_DMA_BUFF_STS_SHIFT 30
#define DEV_DMA_BUFF_STS_HREADY 0
#define DEV_DMA_BUFF_STS_DMABUSY 1
#define DEV_DMA_BUFF_STS_DMADONE 2
#define DEV_DMA_BUFF_STS_HBUSY 3UL
#define DEV_DMA_STS_MASK (0x3 << 28)
#define DEV_DMA_STS_SHIFT 28
#define DEV_DMA_STS_SUCC 0
#define DEV_DMA_STS_BUFF_FLUSH 1
#define DEV_DMA_STS_BUFF_ERR 3
#define DEV_DMA_L BIT(27)
#define DEV_DMA_SHORT BIT(26)
#define DEV_DMA_IOC BIT(25)
#define DEV_DMA_SR BIT(24)
#define DEV_DMA_MTRF BIT(23)
#define DEV_DMA_ISOC_PID_MASK (0x3 << 23)
#define DEV_DMA_ISOC_PID_SHIFT 23
#define DEV_DMA_ISOC_PID_DATA0 0
#define DEV_DMA_ISOC_PID_DATA2 1
#define DEV_DMA_ISOC_PID_DATA1 2
#define DEV_DMA_ISOC_PID_MDATA 3
#define DEV_DMA_ISOC_FRNUM_MASK (0x7ff << 12)
#define DEV_DMA_ISOC_FRNUM_SHIFT 12
#define DEV_DMA_ISOC_TX_NBYTES_MASK (0xfff << 0)
#define DEV_DMA_ISOC_TX_NBYTES_LIMIT 0xfff
#define DEV_DMA_ISOC_RX_NBYTES_MASK (0x7ff << 0)
#define DEV_DMA_ISOC_RX_NBYTES_LIMIT 0x7ff
#define DEV_DMA_ISOC_NBYTES_SHIFT 0
#define DEV_DMA_NBYTES_MASK (0xffff << 0)
#define DEV_DMA_NBYTES_SHIFT 0
#define DEV_DMA_NBYTES_LIMIT 0xffff
#define MAX_DMA_DESC_NUM_GENERIC 64
#define MAX_DMA_DESC_NUM_HS_ISOC 256
#endif /* __DWC2_HW_H__ */

View File

@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2007
* Stelian Pop <stelian@popies.net>
* Lead Tech Design <www.leadtechdesign.com>
*/
#ifndef __ASM_ARM_DMA_MAPPING_H
#define __ASM_ARM_DMA_MAPPING_H
#include <linux/dma-direction.h>
#define dma_mapping_error(x, y) 0
struct device;
static inline void *dma_alloc_coherent(struct device *dev, size_t len, dma_addr_t *handle, gfp_t flag)
{
(void)flag;
//*handle = (unsigned long)memalign(ARCH_DMA_MINALIGN, len);
*handle = (unsigned long)malloc(len + ARCH_DMA_MINALIGN);
return (void *)*handle;
}
static inline void dma_free_coherent(struct device *dev, size_t size, void *addr, dma_addr_t handle)
{
free(addr);
}
static inline void *dmam_alloc_coherent(struct device *dev, size_t len, dma_addr_t *handle, gfp_t flag)
{
(void)flag;
(void)dev;
*handle = (dma_addr_t)malloc(len + ARCH_DMA_MINALIGN);
return (void *)*handle;
}
static inline void dmam_free_coherent(struct device *dev, size_t len, void *vaddr, dma_addr_t handle)
{
free(vaddr);
}
static inline unsigned long dma_map_single(volatile void *vaddr, size_t len,
enum dma_data_direction dir)
{
return (unsigned long)vaddr;
}
static inline void dma_unmap_single(volatile void *vaddr, size_t len,
unsigned long paddr)
{
}
static inline void dma_sync_single_for_device(struct device *dev,
dma_addr_t addr, size_t size, enum dma_data_direction dir)
{
}
static inline void dma_sync_single_for_cpu(struct device *dev,
dma_addr_t addr, size_t size, enum dma_data_direction dir)
{
}
#endif /* __ASM_ARM_DMA_MAPPING_H */

View File

@ -0,0 +1,13 @@
#ifndef _LINUX_DMA_DIRECTION_H
#define _LINUX_DMA_DIRECTION_H
/*
* These definitions mirror those in pci.h, so they can be used
* interchangeably with their PCI_ counterparts.
*/
enum dma_data_direction {
DMA_BIDIRECTIONAL = 0,
DMA_TO_DEVICE = 1,
DMA_FROM_DEVICE = 2,
DMA_NONE = 3,
};
#endif

View File

@ -0,0 +1,168 @@
#ifndef _LINUX_ERRNO_H
#define _LINUX_ERRNO_H
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Invalid system call number */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
/* for robust mutexes */
#define EOWNERDEAD 130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
#define ERFKILL 132 /* Operation not possible due to RF-kill */
#define EHWPOISON 133 /* Memory page has hardware error */
#define ERESTARTSYS 512
#define ERESTARTNOINTR 513
#define ERESTARTNOHAND 514 /* restart if no handler.. */
#define ENOIOCTLCMD 515 /* No ioctl command */
#define ERESTART_RESTARTBLOCK 516 /* restart by calling sys_restart_syscall */
#define EPROBE_DEFER 517 /* Driver requests probe retry */
#define EOPENSTALE 518 /* open found a stale dentry */
/* Defined for the NFSv3 protocol */
#define EBADHANDLE 521 /* Illegal NFS file handle */
#define ENOTSYNC 522 /* Update synchronization mismatch */
#define EBADCOOKIE 523 /* Cookie is stale */
#define ENOTSUPP 524 /* Operation is not supported */
#define ETOOSMALL 525 /* Buffer or request is too small */
#define ESERVERFAULT 526 /* An untranslatable error occurred */
#define EBADTYPE 527 /* Type not supported by server */
#define EJUKEBOX 528 /* Request initiated, but will not complete before timeout */
#define EIOCBQUEUED 529 /* iocb queued, will get completion event */
#define ERECALLCONFLICT 530 /* conflict with recalled state */
#endif

View File

@ -0,0 +1,454 @@
/*
* USB Communications Device Class (CDC) definitions
*
* CDC says how to talk to lots of different types of network adapters,
* notably ethernet adapters and various modems. It's used mostly with
* firmware based USB peripherals.
*/
#ifndef __LINUX_USB_CDC_H
#define __LINUX_USB_CDC_H
#include "usb_os_adapter.h"
#define USB_CDC_SUBCLASS_ACM 0x02
#define USB_CDC_SUBCLASS_ETHERNET 0x06
#define USB_CDC_SUBCLASS_WHCM 0x08
#define USB_CDC_SUBCLASS_DMM 0x09
#define USB_CDC_SUBCLASS_MDLM 0x0a
#define USB_CDC_SUBCLASS_OBEX 0x0b
#define USB_CDC_SUBCLASS_EEM 0x0c
#define USB_CDC_SUBCLASS_NCM 0x0d
#define USB_CDC_PROTO_NONE 0
#define USB_CDC_ACM_PROTO_AT_V25TER 1
#define USB_CDC_ACM_PROTO_AT_PCCA101 2
#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3
#define USB_CDC_ACM_PROTO_AT_GSM 4
#define USB_CDC_ACM_PROTO_AT_3G 5
#define USB_CDC_ACM_PROTO_AT_CDMA 6
#define USB_CDC_ACM_PROTO_VENDOR 0xff
#define USB_CDC_PROTO_EEM 7
#define USB_CDC_NCM_PROTO_NTB 1
/*-------------------------------------------------------------------------*/
/*
* Class-Specific descriptors ... there are a couple dozen of them
*/
#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */
#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */
#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */
#define USB_CDC_UNION_TYPE 0x06 /* union_desc */
#define USB_CDC_COUNTRY_TYPE 0x07
#define USB_CDC_NETWORK_TERMINAL_TYPE 0x0a /* network_terminal_desc */
#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */
#define USB_CDC_WHCM_TYPE 0x11
#define USB_CDC_MDLM_TYPE 0x12 /* mdlm_desc */
#define USB_CDC_MDLM_DETAIL_TYPE 0x13 /* mdlm_detail_desc */
#define USB_CDC_DMM_TYPE 0x14
#define USB_CDC_OBEX_TYPE 0x15
#define USB_CDC_NCM_TYPE 0x1a
/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */
#include "pack_struct_start.h"
struct usb_cdc_header_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__le16 bcdCDC;
};
#include "pack_struct_end.h"
#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01
#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02
/* "Call Management Descriptor" from CDC spec 5.2.3.2 */
#include "pack_struct_start.h"
struct usb_cdc_call_mgmt_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 bmCapabilities;
__u8 bDataInterface;
};
#include "pack_struct_end.h"
/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */
#include "pack_struct_start.h"
struct usb_cdc_acm_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 bmCapabilities;
};
#include "pack_struct_end.h"
/* capabilities from 5.2.3.3 */
#define USB_CDC_COMM_FEATURE 0x01
#define USB_CDC_CAP_LINE 0x02
#define USB_CDC_CAP_BRK 0x04
#define USB_CDC_CAP_NOTIFY 0x08
/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */
#include "pack_struct_start.h"
struct usb_cdc_union_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 bMasterInterface0;
__u8 bSlaveInterface0;
/* ... and there could be other slave interfaces */
};
#include "pack_struct_end.h"
/* "Country Selection Functional Descriptor" from CDC spec 5.2.3.9 */
#include "pack_struct_start.h"
struct usb_cdc_country_functional_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 iCountryCodeRelDate;
__le16 wCountyCode0;
/* ... and there can be a lot of country codes */
};
#include "pack_struct_end.h"
/* "Network Channel Terminal Functional Descriptor" from CDC spec 5.2.3.11 */
#include "pack_struct_start.h"
struct usb_cdc_network_terminal_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 bEntityId;
__u8 iName;
__u8 bChannelIndex;
__u8 bPhysicalInterface;
};
#include "pack_struct_end.h"
/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */
#include "pack_struct_start.h"
struct usb_cdc_ether_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__u8 iMACAddress;
__le32 bmEthernetStatistics;
__le16 wMaxSegmentSize;
__le16 wNumberMCFilters;
__u8 bNumberPowerFilters;
};
#include "pack_struct_end.h"
/* "Telephone Control Model Functional Descriptor" from CDC WMC spec 6.3..3 */
#include "pack_struct_start.h"
struct usb_cdc_dmm_desc {
__u8 bFunctionLength;
__u8 bDescriptorType;
__u8 bDescriptorSubtype;
__u16 bcdVersion;
__le16 wMaxCommand;
};
#include "pack_struct_end.h"
/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */
#include "pack_struct_start.h"
struct usb_cdc_mdlm_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__le16 bcdVersion;
__u8 bGUID[16];
};
#include "pack_struct_end.h"
/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */
#include "pack_struct_start.h"
struct usb_cdc_mdlm_detail_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
/* type is associated with mdlm_desc.bGUID */
__u8 bGuidDescriptorType;
__u8 bDetailData[0];
};
#include "pack_struct_end.h"
/* "OBEX Control Model Functional Descriptor" */
#include "pack_struct_start.h"
struct usb_cdc_obex_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__le16 bcdVersion;
};
#include "pack_struct_end.h"
/* "NCM Control Model Functional Descriptor" */
#include "pack_struct_start.h"
struct usb_cdc_ncm_desc {
__u8 bLength;
__u8 bDescriptorType;
__u8 bDescriptorSubType;
__le16 bcdNcmVersion;
__u8 bmNetworkCapabilities;
};
#include "pack_struct_end.h"
/*-------------------------------------------------------------------------*/
/*
* Class-Specific Control Requests (6.2)
*
* section 3.6.2.1 table 4 has the ACM profile, for modems.
* section 3.8.2 table 10 has the ethernet profile.
*
* Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant,
* heavily dependent on the encapsulated (proprietary) command mechanism.
*/
#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00
#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01
#define USB_CDC_REQ_SET_LINE_CODING 0x20
#define USB_CDC_REQ_GET_LINE_CODING 0x21
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
#define USB_CDC_REQ_SEND_BREAK 0x23
#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40
#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41
#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42
#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43
#define USB_CDC_GET_ETHERNET_STATISTIC 0x44
#define USB_CDC_GET_NTB_PARAMETERS 0x80
#define USB_CDC_GET_NET_ADDRESS 0x81
#define USB_CDC_SET_NET_ADDRESS 0x82
#define USB_CDC_GET_NTB_FORMAT 0x83
#define USB_CDC_SET_NTB_FORMAT 0x84
#define USB_CDC_GET_NTB_INPUT_SIZE 0x85
#define USB_CDC_SET_NTB_INPUT_SIZE 0x86
#define USB_CDC_GET_MAX_DATAGRAM_SIZE 0x87
#define USB_CDC_SET_MAX_DATAGRAM_SIZE 0x88
#define USB_CDC_GET_CRC_MODE 0x89
#define USB_CDC_SET_CRC_MODE 0x8a
/* Line Coding Structure from CDC spec 6.2.13 */
#include "pack_struct_start.h"
struct usb_cdc_line_coding {
__le32 dwDTERate;
__u8 bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
__u8 bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
__u8 bDataBits;
};
#include "pack_struct_end.h"
/* table 62; bits in multicast filter */
#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0)
#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */
#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2)
#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3)
#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */
/*-------------------------------------------------------------------------*/
/*
* Class-Specific Notifications (6.3) sent by interrupt transfers
*
* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications
* section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS
* RNDIS also defines its own bit-incompatible notifications
*/
#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00
#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01
#define USB_CDC_NOTIFY_SERIAL_STATE 0x20
#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a
#include "pack_struct_start.h"
struct usb_cdc_notification {
__u8 bmRequestType;
__u8 bNotificationType;
__le16 wValue;
__le16 wIndex;
__le16 wLength;
};
#include "pack_struct_end.h"
#include "pack_struct_start.h"
struct usb_cdc_speed_change {
__le32 DLBitRRate; /* contains the downlink bit rate (IN pipe) */
__le32 ULBitRate; /* contains the uplink bit rate (OUT pipe) */
};
#include "pack_struct_end.h"
/*-------------------------------------------------------------------------*/
/*
* Class Specific structures and constants
*
* CDC NCM NTB parameters structure, CDC NCM subclass 6.2.1
*
*/
#include "pack_struct_start.h"
struct usb_cdc_ncm_ntb_parameters {
__le16 wLength;
__le16 bmNtbFormatsSupported;
__le32 dwNtbInMaxSize;
__le16 wNdpInDivisor;
__le16 wNdpInPayloadRemainder;
__le16 wNdpInAlignment;
__le16 wPadding1;
__le32 dwNtbOutMaxSize;
__le16 wNdpOutDivisor;
__le16 wNdpOutPayloadRemainder;
__le16 wNdpOutAlignment;
__le16 wNtbOutMaxDatagrams;
};
#include "pack_struct_end.h"
/*
* CDC NCM transfer headers, CDC NCM subclass 3.2
*/
#define USB_CDC_NCM_NTH16_SIGN 0x484D434E /* NCMH */
#define USB_CDC_NCM_NTH32_SIGN 0x686D636E /* ncmh */
#include "pack_struct_start.h"
struct usb_cdc_ncm_nth16 {
__le32 dwSignature;
__le16 wHeaderLength;
__le16 wSequence;
__le16 wBlockLength;
__le16 wNdpIndex;
};
#include "pack_struct_end.h"
#include "pack_struct_start.h"
struct usb_cdc_ncm_nth32 {
__le32 dwSignature;
__le16 wHeaderLength;
__le16 wSequence;
__le32 dwBlockLength;
__le32 dwNdpIndex;
};
#include "pack_struct_end.h"
/*
* CDC NCM datagram pointers, CDC NCM subclass 3.3
*/
#define USB_CDC_NCM_NDP16_CRC_SIGN 0x314D434E /* NCM1 */
#define USB_CDC_NCM_NDP16_NOCRC_SIGN 0x304D434E /* NCM0 */
#define USB_CDC_NCM_NDP32_CRC_SIGN 0x316D636E /* ncm1 */
#define USB_CDC_NCM_NDP32_NOCRC_SIGN 0x306D636E /* ncm0 */
/* 16-bit NCM Datagram Pointer Entry */
#include "pack_struct_start.h"
struct usb_cdc_ncm_dpe16 {
__le16 wDatagramIndex;
__le16 wDatagramLength;
};
#include "pack_struct_end.h"
/* 16-bit NCM Datagram Pointer Table */
#include "pack_struct_start.h"
struct usb_cdc_ncm_ndp16 {
__le32 dwSignature;
__le16 wLength;
__le16 wNextNdpIndex;
struct usb_cdc_ncm_dpe16 dpe16[0];
};
#include "pack_struct_end.h"
/* 32-bit NCM Datagram Pointer Entry */
#include "pack_struct_start.h"
struct usb_cdc_ncm_dpe32 {
__le32 dwDatagramIndex;
__le32 dwDatagramLength;
};
#include "pack_struct_end.h"
/* 32-bit NCM Datagram Pointer Table */
#include "pack_struct_start.h"
struct usb_cdc_ncm_ndp32 {
__le32 dwSignature;
__le16 wLength;
__le16 wReserved6;
__le32 dwNextNdpIndex;
__le32 dwReserved12;
struct usb_cdc_ncm_dpe32 dpe32[0];
};
#include "pack_struct_end.h"
/* CDC NCM subclass 3.2.1 and 3.2.2 */
#define USB_CDC_NCM_NDP16_INDEX_MIN 0x000C
#define USB_CDC_NCM_NDP32_INDEX_MIN 0x0010
/* CDC NCM subclass 3.3.3 Datagram Formatting */
#define USB_CDC_NCM_DATAGRAM_FORMAT_CRC 0x30
#define USB_CDC_NCM_DATAGRAM_FORMAT_NOCRC 0X31
/* CDC NCM subclass 4.2 NCM Communications Interface Protocol Code */
#define USB_CDC_NCM_PROTO_CODE_NO_ENCAP_COMMANDS 0x00
#define USB_CDC_NCM_PROTO_CODE_EXTERN_PROTO 0xFE
/* CDC NCM subclass 5.2.1 NCM Functional Descriptor, bmNetworkCapabilities */
#define USB_CDC_NCM_NCAP_ETH_FILTER (1 << 0)
#define USB_CDC_NCM_NCAP_NET_ADDRESS (1 << 1)
#define USB_CDC_NCM_NCAP_ENCAP_COMMAND (1 << 2)
#define USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE (1 << 3)
#define USB_CDC_NCM_NCAP_CRC_MODE (1 << 4)
#define USB_CDC_NCM_NCAP_NTB_INPUT_SIZE (1 << 5)
/* CDC NCM subclass Table 6-3: NTB Parameter Structure */
#define USB_CDC_NCM_NTB16_SUPPORTED (1 << 0)
#define USB_CDC_NCM_NTB32_SUPPORTED (1 << 1)
/* CDC NCM subclass Table 6-3: NTB Parameter Structure */
#define USB_CDC_NCM_NDP_ALIGN_MIN_SIZE 0x04
#define USB_CDC_NCM_NTB_MAX_LENGTH 0x1C
/* CDC NCM subclass 6.2.5 SetNtbFormat */
#define USB_CDC_NCM_NTB16_FORMAT 0x00
#define USB_CDC_NCM_NTB32_FORMAT 0x01
/* CDC NCM subclass 6.2.7 SetNtbInputSize */
#define USB_CDC_NCM_NTB_MIN_IN_SIZE 2048
#define USB_CDC_NCM_NTB_MIN_OUT_SIZE 2048
/* NTB Input Size Structure */
#include "pack_struct_start.h"
struct usb_cdc_ncm_ndp_input_size {
__le32 dwNtbInMaxSize;
__le16 wNtbInMaxDatagrams;
__le16 wReserved;
};
#include "pack_struct_end.h"
/* CDC NCM subclass 6.2.11 SetCrcMode */
#define USB_CDC_NCM_CRC_NOT_APPENDED 0x00
#define USB_CDC_NCM_CRC_APPENDED 0x01
#endif /* __LINUX_USB_CDC_H */

View File

@ -0,0 +1,352 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* composite.h -- framework for usb gadgets which are composite devices
*
* Copyright (C) 2006-2008 David Brownell
*/
#ifndef __LINUX_USB_COMPOSITE_H
#define __LINUX_USB_COMPOSITE_H
/*
* This framework is an optional layer on top of the USB Gadget interface,
* making it easier to build (a) Composite devices, supporting multiple
* functions within any single configuration, and (b) Multi-configuration
* devices, also supporting multiple functions but without necessarily
* having more than one function per configuration.
*
* Example: a device with a single configuration supporting both network
* link and mass storage functions is a composite device. Those functions
* might alternatively be packaged in individual configurations, but in
* the composite model the host can use both functions at the same time.
*/
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
//#include <usb/lin_gadget_compat.h>
/*
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
* wish to delay the data/status stages of the control transfer till they
* are ready. The control transfer will then be kept from completing till
* all the function drivers that requested for USB_GADGET_DELAYED_STAUS
* invoke usb_composite_setup_continue().
*/
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
struct usb_configuration;
/**
* struct usb_function - describes one function of a configuration
* @name: For diagnostics, identifies the function.
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and by language IDs provided in control requests
* @descriptors: Table of full (or low) speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at full speed (or at low speed).
* @hs_descriptors: Table of high speed descriptors, using interface and
* string identifiers assigned during @bind(). If this pointer is null,
* the function will not be available at high speed.
* @config: assigned when @usb_add_function() is called; this is the
* configuration with which this function is associated.
* @bind: Before the gadget can register, all of its functions bind() to the
* available resources including string and interface identifiers used
* in interface or class descriptors; endpoints; I/O buffers; and so on.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this function.
* @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
* initialize usb_ep.driver data at this time (when it is used).
* Note that setting an interface to its current altsetting resets
* interface state, and that all interfaces have a disabled state.
* @get_alt: Returns the active altsetting. If this is not provided,
* then only altsetting zero is supported.
* @disable: (REQUIRED) Indicates the function should be disabled. Reasons
* include host resetting or reconfiguring the gadget, and disconnection.
* @setup: Used for interface-specific control requests.
* @suspend: Notifies functions when the host stops sending USB traffic.
* @resume: Notifies functions when the host restarts USB traffic.
*
* A single USB function uses one or more interfaces, and should in most
* cases support operation at both full and high speeds. Each function is
* associated by @usb_add_function() with a one configuration; that function
* causes @bind() to be called so resources can be allocated as part of
* setting up a gadget driver. Those resources include endpoints, which
* should be allocated using @usb_ep_autoconfig().
*
* To support dual speed operation, a function driver provides descriptors
* for both high and full speed operation. Except in rare cases that don't
* involve bulk endpoints, each speed needs different endpoint descriptors.
*
* Function drivers choose their own strategies for managing instance data.
* The simplest strategy just declares it "static', which means the function
* can only be activated once. If the function needs to be exposed in more
* than one configuration at a given speed, it needs to support multiple
* usb_function structures (one for each configuration).
*
* A more complex strategy might encapsulate a @usb_function structure inside
* a driver-specific instance structure to allows multiple activations. An
* example of multiple activations might be a CDC ACM function that supports
* two or more distinct instances within the same configuration, providing
* several independent logical data links to a USB host.
*/
struct usb_function {
const char *name;
struct usb_gadget_strings **strings;
struct usb_descriptor_header **descriptors;
struct usb_descriptor_header **hs_descriptors;
struct usb_configuration *config;
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching.
* Related: unbind() may kfree() but bind() won't...
*/
/* configuration management: bind/unbind */
int (*bind)(struct usb_configuration *,
struct usb_function *);
void (*unbind)(struct usb_configuration *,
struct usb_function *);
/* runtime state management */
int (*set_alt)(struct usb_function *,
unsigned interface, unsigned alt);
int (*get_alt)(struct usb_function *,
unsigned interface);
void (*disable)(struct usb_function *);
int (*setup)(struct usb_function *,
const struct usb_ctrlrequest *);
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
/* private: */
/* internals */
ListItem_t list;
void* powner;
DECLARE_BITMAP(endpoints, 32);
};
int usb_add_function(struct usb_configuration *, struct usb_function *);
int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);
int usb_interface_id(struct usb_configuration *, struct usb_function *);
/**
* ep_choose - select descriptor endpoint at current device speed
* @g: gadget, connected and running at some speed
* @hs: descriptor to use for high speed operation
* @fs: descriptor to use for full or low speed operation
*/
static inline struct usb_endpoint_descriptor *
ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
struct usb_endpoint_descriptor *fs)
{
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return hs;
return fs;
}
#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
/**
* struct usb_configuration - represents one gadget configuration
* @label: For diagnostics, describes the configuration.
* @strings: Tables of strings, keyed by identifiers assigned during @bind()
* and by language IDs provided in control requests.
* @descriptors: Table of descriptors preceding all function descriptors.
* Examples include OTG and vendor-specific descriptors.
* @bind: Called from @usb_add_config() to allocate resources unique to this
* configuration and to call @usb_add_function() for each function used.
* @unbind: Reverses @bind; called as a side effect of unregistering the
* driver which added this configuration.
* @setup: Used to delegate control requests that aren't handled by standard
* device infrastructure or directed at a specific interface.
* @bConfigurationValue: Copied into configuration descriptor.
* @iConfiguration: Copied into configuration descriptor.
* @bmAttributes: Copied into configuration descriptor.
* @bMaxPower: Copied into configuration descriptor.
* @cdev: assigned by @usb_add_config() before calling @bind(); this is
* the device associated with this configuration.
*
* Configurations are building blocks for gadget drivers structured around
* function drivers. Simple USB gadgets require only one function and one
* configuration, and handle dual-speed hardware by always providing the same
* functionality. Slightly more complex gadgets may have more than one
* single-function configuration at a given speed; or have configurations
* that only work at one speed.
*
* Composite devices are, by definition, ones with configurations which
* include more than one function.
*
* The lifecycle of a usb_configuration includes allocation, initialization
* of the fields described above, and calling @usb_add_config() to set up
* internal data and bind it to a specific device. The configuration's
* @bind() method is then used to initialize all the functions and then
* call @usb_add_function() for them.
*
* Those functions would normally be independant of each other, but that's
* not mandatory. CDC WMC devices are an example where functions often
* depend on other functions, with some functions subsidiary to others.
* Such interdependency may be managed in any way, so long as all of the
* descriptors complete by the time the composite driver returns from
* its bind() routine.
*/
struct usb_configuration {
const char *label;
struct usb_gadget_strings **strings;
const struct usb_descriptor_header **descriptors;
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
/* configuration management: bind/unbind */
int (*bind)(struct usb_configuration *);
void (*unbind)(struct usb_configuration *);
int (*setup)(struct usb_configuration *,
const struct usb_ctrlrequest *);
/* fields in the config descriptor */
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
struct usb_composite_dev *cdev;
/* private: */
/* internals */
ListItem_t list;
List_t functions;
u8 next_interface_id;
unsigned highspeed:1;
unsigned fullspeed:1;
struct usb_function *interface[MAX_CONFIG_INTERFACES];
};
int usb_add_config(struct usb_composite_dev *,
struct usb_configuration *);
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.
* @dev: Template descriptor for the device, including default device
* identifiers.
* @strings: tables of strings, keyed by identifiers assigned during bind()
* and language IDs provided in control requests
* @bind: (REQUIRED) Used to allocate resources that are shared across the
* whole device, such as string IDs, and add its configurations using
* @usb_add_config(). This may fail by returning a negative errno
* value; it should return zero on successful initialization.
* @unbind: Reverses @bind(); called as a side effect of unregistering
* this driver.
* @disconnect: optional driver disconnect method
* @suspend: Notifies when the host stops sending USB traffic,
* after function notifications
* @resume: Notifies configuration when the host restarts USB traffic,
* before function notifications
*
* Devices default to reporting self powered operation. Devices which rely
* on bus powered operation should report this in their @bind() method.
*
* Before returning from @bind, various fields in the template descriptor
* may be overridden. These include the idVendor/idProduct/bcdDevice values
* normally to bind the appropriate host side driver, and the three strings
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
* meaningful device identifiers. (The strings will not be defined unless
* they are defined in @dev and @strings.) The correct ep0 maxpacket size
* is also reported, as defined by the underlying controller driver.
*/
struct usb_composite_driver {
const char *name;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
int (*bind)(struct usb_composite_dev *);
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
};
extern int usb_composite_register(struct usb_composite_driver *);
extern void usb_composite_unregister(struct usb_composite_driver *);
/**
* struct usb_composite_device - represents one composite usb gadget
* @gadget: read-only, abstracts the gadget's usb peripheral controller
* @req: used for control responses; buffer is pre-allocated
* @bufsiz: size of buffer pre-allocated in @req
* @config: the currently active configuration
*
* One of these devices is allocated and initialized before the
* associated device driver's bind() is called.
*
* OPEN ISSUE: it appears that some WUSB devices will need to be
* built by combining a normal (wired) gadget with a wireless one.
* This revision of the gadget framework should probably try to make
* sure doing that won't hurt too much.
*
* One notion for how to handle Wireless USB devices involves:
* (a) a second gadget here, discovery mechanism TBD, but likely
* needing separate "register/unregister WUSB gadget" calls;
* (b) updates to usb_gadget to include flags "is it wireless",
* "is it wired", plus (presumably in a wrapper structure)
* bandgroup and PHY info;
* (c) presumably a wireless_ep wrapping a usb_ep, and reporting
* wireless-specific parameters like maxburst and maxsequence;
* (d) configurations that are specific to wireless links;
* (e) function drivers that understand wireless configs and will
* support wireless for (additional) function instances;
* (f) a function to support association setup (like CBAF), not
* necessarily requiring a wireless adapter;
* (g) composite device setup that can create one or more wireless
* configs, including appropriate association setup support;
* (h) more, TBD.
*/
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
unsigned bufsiz;
struct usb_configuration *config;
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc;
List_t configs;
struct usb_composite_driver *driver;
u8 next_string_id;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
};
extern int usb_string_id(struct usb_composite_dev *c);
extern int usb_string_ids_tab(struct usb_composite_dev *c,
struct usb_string *str);
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
struct usb_endpoint_descriptor *
get_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
struct usb_endpoint_descriptor *hs);
struct usb_descriptor_header **
usb_copy_descriptors(struct usb_descriptor_header **src);
void usb_free_descriptors(struct usb_descriptor_header **v);
#endif /* __LINUX_USB_COMPOSITE_H */

View File

@ -0,0 +1,56 @@
#ifndef __ETHER_H
#define __ETHER_H
#include "FreeRTOS_IP.h"
//typedef struct NetworkBufferDescriptor_t *(*ncm_wrap)(struct gether *port, NetworkBufferDescriptor_t* pxBufferDescriptor);
//typedef int (*ncm_unwrap)(struct gether *port, NetworkBufferDescriptor_t* pxBufferDescriptor, List_t *frames);
struct gether {
struct usb_function func;
/* endpoints handle full and/or high speeds */
struct usb_ep *in_ep;
struct usb_ep *out_ep;
const struct usb_endpoint_descriptor *in, *out;
bool connected;
bool is_zlp_ok;
u16 cdc_filter;
/* hooks for added framing, as needed for RNDIS and EEM. */
u32 header_len;
/* NCM requires fixed size bundles */
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
NetworkBufferDescriptor_t *(*wrap)(struct gether *port, NetworkBufferDescriptor_t* pxBufferDescriptor);
void *(*wrap_ext)(struct gether *port, void* bufferDescHandle);
int (*unwrap)(struct gether *port, uint8_t* data_buf, int len, List_t *frames);
void (*disconnect_cb)(struct gether *port);
void (*open)(struct gether *);
void (*close)(struct gether *);
void* ctx;
};
#ifndef ETH_FRAME_LEN
#define ETH_FRAME_LEN 1514
#endif
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
#ifndef NET_IP_ALIGN
#define NET_IP_ALIGN 4
#endif
void gether_send(NetworkBufferDescriptor_t * const pxDescriptor);
void gether_send_ext(void * const pxDescriptor);
void gether_disconnect(struct gether *link);
int gether_connect(struct gether *link);
void gether_cleanup(void);
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
#endif

View File

@ -0,0 +1,948 @@
/*
* <linux/usb/gadget.h>
*
* We call the USB code inside a Linux-based peripheral device a "gadget"
* driver, except for the hardware-specific bus glue. One USB host can
* master many USB gadgets, but the gadgets are only slaved to one host.
*
*
* (C) Copyright 2002-2004 by David Brownell
* All Rights Reserved.
*
* This software is licensed under the GNU GPL version 2.
*
* Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#ifndef __LINUX_USB_GADGET_H
#define __LINUX_USB_GADGET_H
/*
#include <errno.h>
#include <linux/compat.h>
#include <linux/list.h>*/
#include "usb_os_adapter.h"
#include <linux/errno.h>
#include <stdbool.h>
#define CONFIG_USB_GADGET_DUALSPEED 1
struct usb_ep;
/**
* struct usb_request - describes one i/o request
* @buf: Buffer used for data. Always provide this; some controllers
* only use PIO, or don't use DMA for some endpoints.
* @dma: DMA address corresponding to 'buf'. If you don't set this
* field, and the usb controller needs one, it is responsible
* for mapping and unmapping the buffer.
* @stream_id: The stream id, when USB3.0 bulk streams are being used
* @length: Length of that data
* @no_interrupt: If true, hints that no completion irq is needed.
* Helpful sometimes with deep request queues that are handled
* directly by DMA controllers.
* @zero: If true, when writing data, makes the last packet be "short"
* by adding a zero length packet as needed;
* @short_not_ok: When reading data, makes short packets be
* treated as errors (queue stops advancing till cleanup).
* @complete: Function called when request completes, so this request and
* its buffer may be re-used.
* Reads terminate with a short packet, or when the buffer fills,
* whichever comes first. When writes terminate, some data bytes
* will usually still be in flight (often in a hardware fifo).
* Errors (for reads or writes) stop the queue from advancing
* until the completion function returns, so that any transfers
* invalidated by the error may first be dequeued.
* @context: For use by the completion callback
* @list: For use by the gadget driver.
* @status: Reports completion code, zero or a negative errno.
* Normally, faults block the transfer queue from advancing until
* the completion callback returns.
* Code "-ESHUTDOWN" indicates completion caused by device disconnect,
* or when the driver disabled the endpoint.
* @actual: Reports bytes transferred to/from the buffer. For reads (OUT
* transfers) this may be less than the requested length. If the
* short_not_ok flag is set, short reads are treated as errors
* even when status otherwise indicates successful completion.
* Note that for writes (IN transfers) some data bytes may still
* reside in a device-side FIFO when the request is reported as
* complete.
*
* These are allocated/freed through the endpoint they're used with. The
* hardware's driver can add extra per-request data to the memory it returns,
* which often avoids separate memory allocations (potential failures),
* later when the request is queued.
*
* Request flags affect request handling, such as whether a zero length
* packet is written (the "zero" flag), whether a short read should be
* treated as an error (blocking request queue advance, the "short_not_ok"
* flag), or hinting that an interrupt is not required (the "no_interrupt"
* flag, for use with deep request queues).
*
* Bulk endpoints can use any size buffers, and can also be used for interrupt
* transfers. interrupt-only endpoints can be much less functional.
*
* NOTE: this is analagous to 'struct urb' on the host side, except that
* it's thinner and promotes more pre-allocation.
*/
struct usb_request {
void *buf;
unsigned length;
dma_addr_t dma;
unsigned stream_id:16;
unsigned no_interrupt:1;
unsigned zero:1;
unsigned short_not_ok:1;
void (*complete)(struct usb_ep *ep,
struct usb_request *req);
void *context;
#ifndef NO_GNU
struct list_head list;
#else
ListItem_t list;
void* powner;
#endif
int status;
unsigned actual;
};
/*-------------------------------------------------------------------------*/
/* endpoint-specific parts of the api to the usb controller hardware.
* unlike the urb model, (de)multiplexing layers are not required.
* (so this api could slash overhead if used on the host side...)
*
* note that device side usb controllers commonly differ in how many
* endpoints they support, as well as their capabilities.
*/
struct usb_ep_ops {
int (*enable) (struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc);
int (*disable) (struct usb_ep *ep);
struct usb_request *(*alloc_request) (struct usb_ep *ep,
gfp_t gfp_flags);
void (*free_request) (struct usb_ep *ep, struct usb_request *req);
int (*queue) (struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags);
int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
int (*set_halt) (struct usb_ep *ep, int value);
int (*set_wedge)(struct usb_ep *ep);
int (*fifo_status) (struct usb_ep *ep);
void (*fifo_flush) (struct usb_ep *ep);
};
/**
* struct usb_ep - device side representation of USB endpoint
* @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk"
* @ops: Function pointers used to access hardware-specific operations.
* @ep_list:the gadget's ep_list holds all of its endpoints
* @maxpacket:The maximum packet size used on this endpoint. The initial
* value can sometimes be reduced (hardware allowing), according to
* the endpoint descriptor used to configure the endpoint.
* @maxpacket_limit:The maximum packet size value which can be handled by this
* endpoint. It's set once by UDC driver when endpoint is initialized, and
* should not be changed. Should not be confused with maxpacket.
* @max_streams: The maximum number of streams supported
* by this EP (0 - 16, actual number is 2^n)
* @maxburst: the maximum number of bursts supported by this EP (for usb3)
* @driver_data:for use by the gadget driver. all other fields are
* read-only to gadget drivers.
* @desc: endpoint descriptor. This pointer is set before the endpoint is
* enabled and remains valid until the endpoint is disabled.
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
* descriptor that is used to configure the endpoint
*
* the bus controller driver lists all the general purpose endpoints in
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
* and is accessed only in response to a driver setup() callback.
*/
struct usb_ep {
void *driver_data;
const char *name;
const struct usb_ep_ops *ops;
#ifndef NO_GNU
struct list_head ep_list;
#else
ListItem_t ep_list;
void* powner;
#endif
unsigned maxpacket:16;
unsigned maxpacket_limit:16;
unsigned max_streams:16;
unsigned maxburst:5;
const struct usb_endpoint_descriptor *desc;
const struct usb_ss_ep_comp_descriptor *comp_desc;
};
/*-------------------------------------------------------------------------*/
/**
* usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint
* @ep:the endpoint being configured
* @maxpacket_limit:value of maximum packet size limit
*
* This function shoud be used only in UDC drivers to initialize endpoint
* (usually in probe function).
*/
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
unsigned maxpacket_limit)
{
ep->maxpacket_limit = maxpacket_limit;
ep->maxpacket = maxpacket_limit;
}
/**
* usb_ep_enable - configure endpoint, making it usable
* @ep:the endpoint being configured. may not be the endpoint named "ep0".
* drivers discover endpoints through the ep_list of a usb_gadget.
* @desc:descriptor for desired behavior. caller guarantees this pointer
* remains valid until the endpoint is disabled; the data byte order
* is little-endian (usb-standard).
*
* when configurations are set, or when interface settings change, the driver
* will enable or disable the relevant endpoints. while it is enabled, an
* endpoint may be used for i/o until the driver receives a disconnect() from
* the host or until the endpoint is disabled.
*
* the ep0 implementation (which calls this routine) must ensure that the
* hardware capabilities of each endpoint match the descriptor provided
* for it. for example, an endpoint named "ep2in-bulk" would be usable
* for interrupt transfers as well as bulk, but it likely couldn't be used
* for iso transfers or for endpoint 14. some endpoints are fully
* configurable, with more generic names like "ep-a". (remember that for
* USB, "in" means "towards the USB master".)
*
* returns zero, or a negative error code.
*/
static inline int usb_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
return ep->ops->enable(ep, desc);
}
/**
* usb_ep_disable - endpoint is no longer usable
* @ep:the endpoint being unconfigured. may not be the endpoint named "ep0".
*
* no other task may be using this endpoint when this is called.
* any pending and uncompleted requests will complete with status
* indicating disconnect (-ESHUTDOWN) before this call returns.
* gadget drivers must call usb_ep_enable() again before queueing
* requests to the endpoint.
*
* returns zero, or a negative error code.
*/
static inline int usb_ep_disable(struct usb_ep *ep)
{
return ep->ops->disable(ep);
}
/**
* usb_ep_alloc_request - allocate a request object to use with this endpoint
* @ep:the endpoint to be used with with the request
* @gfp_flags:GFP_* flags to use
*
* Request objects must be allocated with this call, since they normally
* need controller-specific setup and may even need endpoint-specific
* resources such as allocation of DMA descriptors.
* Requests may be submitted with usb_ep_queue(), and receive a single
* completion callback. Free requests with usb_ep_free_request(), when
* they are no longer needed.
*
* Returns the request, or null if one could not be allocated.
*/
static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags)
{
return ep->ops->alloc_request(ep, gfp_flags);
}
/**
* usb_ep_free_request - frees a request object
* @ep:the endpoint associated with the request
* @req:the request being freed
*
* Reverses the effect of usb_ep_alloc_request().
* Caller guarantees the request is not queued, and that it will
* no longer be requeued (or otherwise used).
*/
static inline void usb_ep_free_request(struct usb_ep *ep,
struct usb_request *req)
{
ep->ops->free_request(ep, req);
}
/**
* usb_ep_queue - queues (submits) an I/O request to an endpoint.
* @ep:the endpoint associated with the request
* @req:the request being submitted
* @gfp_flags: GFP_* flags to use in case the lower level driver couldn't
* pre-allocate all necessary memory with the request.
*
* This tells the device controller to perform the specified request through
* that endpoint (reading or writing a buffer). When the request completes,
* including being canceled by usb_ep_dequeue(), the request's completion
* routine is called to return the request to the driver. Any endpoint
* (except control endpoints like ep0) may have more than one transfer
* request queued; they complete in FIFO order. Once a gadget driver
* submits a request, that request may not be examined or modified until it
* is given back to that driver through the completion callback.
*
* Each request is turned into one or more packets. The controller driver
* never merges adjacent requests into the same packet. OUT transfers
* will sometimes use data that's already buffered in the hardware.
* Drivers can rely on the fact that the first byte of the request's buffer
* always corresponds to the first byte of some USB packet, for both
* IN and OUT transfers.
*
* Bulk endpoints can queue any amount of data; the transfer is packetized
* automatically. The last packet will be short if the request doesn't fill it
* out completely. Zero length packets (ZLPs) should be avoided in portable
* protocols since not all usb hardware can successfully handle zero length
* packets. (ZLPs may be explicitly written, and may be implicitly written if
* the request 'zero' flag is set.) Bulk endpoints may also be used
* for interrupt transfers; but the reverse is not true, and some endpoints
* won't support every interrupt transfer. (Such as 768 byte packets.)
*
* Interrupt-only endpoints are less functional than bulk endpoints, for
* example by not supporting queueing or not handling buffers that are
* larger than the endpoint's maxpacket size. They may also treat data
* toggle differently.
*
* Control endpoints ... after getting a setup() callback, the driver queues
* one response (even if it would be zero length). That enables the
* status ack, after transfering data as specified in the response. Setup
* functions may return negative error codes to generate protocol stalls.
* (Note that some USB device controllers disallow protocol stall responses
* in some cases.) When control responses are deferred (the response is
* written after the setup callback returns), then usb_ep_set_halt() may be
* used on ep0 to trigger protocol stalls.
*
* For periodic endpoints, like interrupt or isochronous ones, the usb host
* arranges to poll once per interval, and the gadget driver usually will
* have queued some data to transfer at that time.
*
* Returns zero, or a negative error code. Endpoints that are not enabled
* report errors; errors will also be
* reported when the usb peripheral is disconnected.
*/
static inline int usb_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags)
{
return ep->ops->queue(ep, req, gfp_flags);
}
/**
* usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint
* @ep:the endpoint associated with the request
* @req:the request being canceled
*
* if the request is still active on the endpoint, it is dequeued and its
* completion routine is called (with status -ECONNRESET); else a negative
* error code is returned.
*
* note that some hardware can't clear out write fifos (to unlink the request
* at the head of the queue) except as part of disconnecting from usb. such
* restrictions prevent drivers from supporting configuration changes,
* even to configuration zero (a "chapter 9" requirement).
*/
static inline int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
return ep->ops->dequeue(ep, req);
}
/**
* usb_ep_set_halt - sets the endpoint halt feature.
* @ep: the non-isochronous endpoint being stalled
*
* Use this to stall an endpoint, perhaps as an error report.
* Except for control endpoints,
* the endpoint stays halted (will not stream any data) until the host
* clears this feature; drivers may need to empty the endpoint's request
* queue first, to make sure no inappropriate transfers happen.
*
* Note that while an endpoint CLEAR_FEATURE will be invisible to the
* gadget driver, a SET_INTERFACE will not be. To reset endpoints for the
* current altsetting, see usb_ep_clear_halt(). When switching altsettings,
* it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints.
*
* Returns zero, or a negative error code. On success, this call sets
* underlying hardware state that blocks data transfers.
* Attempts to halt IN endpoints will fail (returning -EAGAIN) if any
* transfer requests are still queued, or if the controller hardware
* (usually a FIFO) still holds bytes that the host hasn't collected.
*/
static inline int usb_ep_set_halt(struct usb_ep *ep)
{
return ep->ops->set_halt(ep, 1);
}
/**
* usb_ep_clear_halt - clears endpoint halt, and resets toggle
* @ep:the bulk or interrupt endpoint being reset
*
* Use this when responding to the standard usb "set interface" request,
* for endpoints that aren't reconfigured, after clearing any other state
* in the endpoint's i/o queue.
*
* Returns zero, or a negative error code. On success, this call clears
* the underlying hardware state reflecting endpoint halt and data toggle.
* Note that some hardware can't support this request (like pxa2xx_udc),
* and accordingly can't correctly implement interface altsettings.
*/
static inline int usb_ep_clear_halt(struct usb_ep *ep)
{
return ep->ops->set_halt(ep, 0);
}
/**
* usb_ep_fifo_status - returns number of bytes in fifo, or error
* @ep: the endpoint whose fifo status is being checked.
*
* FIFO endpoints may have "unclaimed data" in them in certain cases,
* such as after aborted transfers. Hosts may not have collected all
* the IN data written by the gadget driver (and reported by a request
* completion). The gadget driver may not have collected all the data
* written OUT to it by the host. Drivers that need precise handling for
* fault reporting or recovery may need to use this call.
*
* This returns the number of such bytes in the fifo, or a negative
* errno if the endpoint doesn't use a FIFO or doesn't support such
* precise handling.
*/
static inline int usb_ep_fifo_status(struct usb_ep *ep)
{
if (ep->ops->fifo_status)
return ep->ops->fifo_status(ep);
else
return -EOPNOTSUPP;
}
/**
* usb_ep_fifo_flush - flushes contents of a fifo
* @ep: the endpoint whose fifo is being flushed.
*
* This call may be used to flush the "unclaimed data" that may exist in
* an endpoint fifo after abnormal transaction terminations. The call
* must never be used except when endpoint is not being used for any
* protocol translation.
*/
static inline void usb_ep_fifo_flush(struct usb_ep *ep)
{
if (ep->ops->fifo_flush)
ep->ops->fifo_flush(ep);
}
/*-------------------------------------------------------------------------*/
struct usb_gadget;
struct usb_gadget_driver;
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
*/
struct usb_gadget_ops {
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *,
unsigned code, unsigned long param);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
int (*udc_stop)(struct usb_gadget *);
};
/**
* struct usb_gadget - represents a usb slave device
* @ops: Function pointers used to access hardware-specific operations.
* @ep0: Endpoint zero, used when reading or writing responses to
* driver setup() requests
* @ep_list: List of other endpoints supported by the device.
* @speed: Speed of current connection to USB host.
* @max_speed: Maximal speed the UDC can handle. UDC must support this
* and all slower speeds.
* @is_dualspeed: true if the controller supports both high and full speed
* operation. If it does, the gadget driver must also support both.
* @is_otg: true if the USB device port uses a Mini-AB jack, so that the
* gadget driver must provide a USB OTG descriptor.
* @is_a_peripheral: false unless is_otg, the "A" end of a USB cable
* is in the Mini-AB jack, and HNP has been used to switch roles
* so that the "A" device currently acts as A-Peripheral, not A-Host.
* @a_hnp_support: OTG device feature flag, indicating that the A-Host
* supports HNP at this port.
* @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host
* only supports HNP on a different root port.
* @b_hnp_enable: OTG device feature flag, indicating that the A-Host
* enabled HNP support.
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
* @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
* MaxPacketSize.
*
* Gadgets have a mostly-portable "gadget driver" implementing device
* functions, handling all usb configurations and interfaces. Gadget
* drivers talk to hardware-specific code indirectly, through ops vectors.
* That insulates the gadget driver from hardware details, and packages
* the hardware endpoints through generic i/o queues. The "usb_gadget"
* and "usb_ep" interfaces provide that insulation from the hardware.
*
* Except for the driver data, all fields in this structure are
* read-only to the gadget driver. That driver data is part of the
* "driver model" infrastructure in 2.6 (and later) kernels, and for
* earlier systems is grouped in a similar structure that's not known
* to the rest of the kernel.
*
* Values of the three OTG device feature flags are updated before the
* setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before
* driver suspend() calls. They are valid only when is_otg, and when the
* device is acting as a B-Peripheral (so is_a_peripheral is false).
*/
struct usb_gadget {
/* readonly to gadget driver */
const struct usb_gadget_ops *ops;
struct usb_ep *ep0;
#ifndef NO_GNU
struct list_head ep_list; /* of usb_ep */
#else
List_t ep_list;
void* powner;
void* dev_pri;
#endif
enum usb_device_speed speed;
enum usb_device_speed max_speed;
enum usb_device_state state;
unsigned is_dualspeed:1;
unsigned is_otg:1;
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
const char *name;
//struct device dev;
unsigned quirk_ep_out_aligned_size:1;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
{
gadget->dev_pri = data;
}
static inline void *get_gadget_data(struct usb_gadget *gadget)
{
return gadget->dev_pri;
}
#if 0
static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
{
return container_of(dev, struct usb_gadget, dev);
}
#endif
/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */
#define gadget_for_each_ep(tmp, gadget) \
list_for_each_entry(tmp, &(gadget)->ep_list, ep_list)
/**
* gadget_is_dualspeed - return true iff the hardware handles high speed
* @g: controller that might support both high and full speeds
*/
static inline int gadget_is_dualspeed(struct usb_gadget *g)
{
#ifdef CONFIG_USB_GADGET_DUALSPEED
/* runtime test would check "g->is_dualspeed" ... that might be
* useful to work around hardware bugs, but is mostly pointless
*/
return 1;
#else
return 0;
#endif
}
/**
* gadget_is_otg - return true iff the hardware is OTG-ready
* @g: controller that might have a Mini-AB connector
*
* This is a runtime test, since kernels with a USB-OTG stack sometimes
* run on boards which only have a Mini-B (or Mini-A) connector.
*/
static inline int gadget_is_otg(struct usb_gadget *g)
{
#ifdef CONFIG_USB_OTG
return g->is_otg;
#else
return 0;
#endif
}
/**
* usb_gadget_frame_number - returns the current frame number
* @gadget: controller that reports the frame number
*
* Returns the usb frame number, normally eleven bits from a SOF packet,
* or negative errno if this device doesn't support this capability.
*/
static inline int usb_gadget_frame_number(struct usb_gadget *gadget)
{
return gadget->ops->get_frame(gadget);
}
/**
* usb_gadget_wakeup - tries to wake up the host connected to this gadget
* @gadget: controller used to wake up the host
*
* Returns zero on success, else negative error code if the hardware
* doesn't support such attempts, or its support has not been enabled
* by the usb host. Drivers must return device descriptors that report
* their ability to support this, or hosts won't enable it.
*
* This may also try to use SRP to wake the host and start enumeration,
* even if OTG isn't otherwise in use. OTG devices may also start
* remote wakeup even when hosts don't explicitly enable it.
*/
static inline int usb_gadget_wakeup(struct usb_gadget *gadget)
{
if (!gadget->ops->wakeup)
return -EOPNOTSUPP;
return gadget->ops->wakeup(gadget);
}
/**
* usb_gadget_set_selfpowered - sets the device selfpowered feature.
* @gadget:the device being declared as self-powered
*
* this affects the device status reported by the hardware driver
* to reflect that it now has a local power supply.
*
* returns zero on success, else negative errno.
*/
static inline int usb_gadget_set_selfpowered(struct usb_gadget *gadget)
{
if (!gadget->ops->set_selfpowered)
return -EOPNOTSUPP;
return gadget->ops->set_selfpowered(gadget, 1);
}
/**
* usb_gadget_clear_selfpowered - clear the device selfpowered feature.
* @gadget:the device being declared as bus-powered
*
* this affects the device status reported by the hardware driver.
* some hardware may not support bus-powered operation, in which
* case this feature's value can never change.
*
* returns zero on success, else negative errno.
*/
static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget)
{
if (!gadget->ops->set_selfpowered)
return -EOPNOTSUPP;
return gadget->ops->set_selfpowered(gadget, 0);
}
/**
* usb_gadget_vbus_connect - Notify controller that VBUS is powered
* @gadget:The device which now has VBUS power.
*
* This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session starting. Common responses include
* resuming the controller, activating the D+ (or D-) pullup to let the
* host detect that a USB device is attached, and starting to draw power
* (8mA or possibly more, especially after SET_CONFIGURATION).
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_connect(struct usb_gadget *gadget)
{
if (!gadget->ops->vbus_session)
return -EOPNOTSUPP;
return gadget->ops->vbus_session(gadget, 1);
}
/**
* usb_gadget_vbus_draw - constrain controller's VBUS power usage
* @gadget:The device whose VBUS usage is being described
* @mA:How much current to draw, in milliAmperes. This should be twice
* the value listed in the configuration descriptor bMaxPower field.
*
* This call is used by gadget drivers during SET_CONFIGURATION calls,
* reporting how much power the device may consume. For example, this
* could affect how quickly batteries are recharged.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA)
{
if (!gadget->ops->vbus_draw)
return -EOPNOTSUPP;
return gadget->ops->vbus_draw(gadget, mA);
}
/**
* usb_gadget_vbus_disconnect - notify controller about VBUS session end
* @gadget:the device whose VBUS supply is being described
*
* This call is used by a driver for an external transceiver (or GPIO)
* that detects a VBUS power session ending. Common responses include
* reversing everything done in usb_gadget_vbus_connect().
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_vbus_disconnect(struct usb_gadget *gadget)
{
if (!gadget->ops->vbus_session)
return -EOPNOTSUPP;
return gadget->ops->vbus_session(gadget, 0);
}
/**
* usb_gadget_connect - software-controlled connect to USB host
* @gadget:the peripheral being connected
*
* Enables the D+ (or potentially D-) pullup. The host will start
* enumerating this gadget when the pullup is active and a VBUS session
* is active (the link is powered). This pullup is always enabled unless
* usb_gadget_disconnect() has been used to disable it.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_connect(struct usb_gadget *gadget)
{
if (!gadget->ops->pullup)
return -EOPNOTSUPP;
return gadget->ops->pullup(gadget, 1);
}
/**
* usb_gadget_disconnect - software-controlled disconnect from USB host
* @gadget:the peripheral being disconnected
*
* Disables the D+ (or potentially D-) pullup, which the host may see
* as a disconnect (when a VBUS session is active). Not all systems
* support software pullup controls.
*
* This routine may be used during the gadget driver bind() call to prevent
* the peripheral from ever being visible to the USB host, unless later
* usb_gadget_connect() is called. For example, user mode components may
* need to be activated before the system can talk to hosts.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
{
if (!gadget->ops->pullup)
return -EOPNOTSUPP;
return gadget->ops->pullup(gadget, 0);
}
/*-------------------------------------------------------------------------*/
/**
* struct usb_gadget_driver - driver for usb 'slave' devices
* @function: String describing the gadget's function
* @speed: Highest speed the driver handles.
* @bind: Invoked when the driver is bound to a gadget, usually
* after registering the driver.
* At that point, ep0 is fully initialized, and ep_list holds
* the currently-available endpoints.
* Called in a context that permits sleeping.
* @setup: Invoked for ep0 control requests that aren't handled by
* the hardware level driver. Most calls must be handled by
* the gadget driver, including descriptor and configuration
* management. The 16 bit members of the setup data are in
* USB byte order. Called in_interrupt; this may not sleep. Driver
* queues a response to ep0, or returns negative to stall.
* @disconnect: Invoked after all transfers have been stopped,
* when the host is disconnected. May be called in_interrupt; this
* may not sleep. Some devices can't detect disconnect, so this might
* not be called except as part of controller shutdown.
* @unbind: Invoked when the driver is unbound from a gadget,
* usually from rmmod (after a disconnect is reported).
* Called in a context that permits sleeping.
* @suspend: Invoked on USB suspend. May be called in_interrupt.
* @resume: Invoked on USB resume. May be called in_interrupt.
* @reset: Invoked on USB bus reset. It is mandatory for all gadget drivers
* and should be called in_interrupt.
*
* Devices are disabled till a gadget driver successfully bind()s, which
* means the driver will handle setup() requests needed to enumerate (and
* meet "chapter 9" requirements) then do some useful work.
*
* If gadget->is_otg is true, the gadget driver must provide an OTG
* descriptor during enumeration, or else fail the bind() call. In such
* cases, no USB traffic may flow until both bind() returns without
* having called usb_gadget_disconnect(), and the USB host stack has
* initialized.
*
* Drivers use hardware-specific knowledge to configure the usb hardware.
* endpoint addressing is only one of several hardware characteristics that
* are in descriptors the ep0 implementation returns from setup() calls.
*
* Except for ep0 implementation, most driver code shouldn't need change to
* run on top of different usb controllers. It'll use endpoints set up by
* that ep0 implementation.
*
* The usb controller driver handles a few standard usb requests. Those
* include set_address, and feature flags for devices, interfaces, and
* endpoints (the get_status, set_feature, and clear_feature requests).
*
* Accordingly, the driver's setup() callback must always implement all
* get_descriptor requests, returning at least a device descriptor and
* a configuration descriptor. Drivers must make sure the endpoint
* descriptors match any hardware constraints. Some hardware also constrains
* other descriptors. (The pxa250 allows only configurations 1, 2, or 3).
*
* The driver's setup() callback must also implement set_configuration,
* and should also implement set_interface, get_configuration, and
* get_interface. Setting a configuration (or interface) is where
* endpoints should be activated or (config 0) shut down.
*
* (Note that only the default control endpoint is supported. Neither
* hosts nor devices generally support control traffic except to ep0.)
*
* Most devices will ignore USB suspend/resume operations, and so will
* not provide those callbacks. However, some may need to change modes
* when the host is not longer directing those activities. For example,
* local controls (buttons, dials, etc) may need to be re-enabled since
* the (remote) host can't do that any longer; or an error state might
* be cleared, to make the device behave identically whether or not
* power is maintained.
*/
struct usb_gadget_driver {
char *function;
enum usb_device_speed speed;
int (*bind)(struct usb_gadget *);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
void (*disconnect)(struct usb_gadget *);
void (*suspend)(struct usb_gadget *);
void (*resume)(struct usb_gadget *);
void (*reset)(struct usb_gadget *);
};
/*-------------------------------------------------------------------------*/
/* driver modules register and unregister, as usual.
* these calls must be made in a context that can sleep.
*
* these will usually be implemented directly by the hardware-dependent
* usb bus interface driver, which will only support a single driver.
*/
/**
* usb_gadget_register_driver - register a gadget driver
* @driver:the driver being registered
*
* Call this in your gadget driver's module initialization function,
* to tell the underlying usb controller driver about your driver.
* The driver's bind() function will be called to bind it to a
* gadget before this registration call returns. It's expected that
* the bind() functions will be in init sections.
* This function must be called in a context that can sleep.
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver);
/**
* usb_gadget_unregister_driver - unregister a gadget driver
* @driver:the driver being unregistered
*
* Call this in your gadget driver's module cleanup function,
* to tell the underlying usb controller that your driver is
* going away. If the controller is connected to a USB host,
* it will first disconnect(). The driver is also requested
* to unbind() and clean up any device state, before this procedure
* finally returns. It's expected that the unbind() functions
* will in in exit sections, so may not be linked in some kernels.
* This function must be called in a context that can sleep.
*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
int usb_add_gadget_udc_release(struct device *parent,
struct usb_gadget *gadget, void (*release)(struct device *dev));
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
void usb_del_gadget_udc(struct usb_gadget *gadget);
/*-------------------------------------------------------------------------*/
/* utility to simplify dealing with string descriptors */
/**
* struct usb_gadget_strings - a set of USB strings in a given language
* @language:identifies the strings' language (0x0409 for en-us)
* @strings:array of strings with their ids
*
* If you're using usb_gadget_get_string(), use this to wrap all the
* strings for a given language.
*/
struct usb_gadget_strings {
u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
/* put descriptor for string with that id into buf (buflen >= 256) */
int usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf);
/*-------------------------------------------------------------------------*/
/* utility to simplify managing config descriptors */
/* write vector of descriptors into buffer */
int usb_descriptor_fillbuf(void *, unsigned,
const struct usb_descriptor_header **);
/* build config descriptor from single descriptor vector */
int usb_gadget_config_buf(const struct usb_config_descriptor *config,
void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
/*-------------------------------------------------------------------------*/
/* utility to simplify map/unmap of usb_requests to/from DMA */
extern int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
extern void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
/*-------------------------------------------------------------------------*/
/* utility to set gadget state properly */
extern void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state);
/*-------------------------------------------------------------------------*/
/* utility to tell udc core that the bus reset occurs */
extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
/*-------------------------------------------------------------------------*/
/* utility to give requests back to the gadget layer */
extern void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req);
/*-------------------------------------------------------------------------*/
/* utility wrapping a simple endpoint selection policy */
extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
struct usb_endpoint_descriptor *);
extern void usb_ep_autoconfig_reset(struct usb_gadget *);
extern int usb_gadget_handle_interrupts(int index);
#endif /* __LINUX_USB_GADGET_H */

View File

@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/* include/linux/usb/otg.h
*
* Copyright (c) 2015 Texas Instruments Incorporated - http://www.ti.com
*
* USB OTG (On The Go) defines
*/
#ifndef __LINUX_USB_OTG_H
#define __LINUX_USB_OTG_H
enum usb_dr_mode {
USB_DR_MODE_UNKNOWN,
USB_DR_MODE_HOST,
USB_DR_MODE_PERIPHERAL,
USB_DR_MODE_OTG,
};
/**
* usb_get_dr_mode() - Get dual role mode for given device
* @node: Node offset to the given device
*
* The function gets phy interface string from property 'dr_mode',
* and returns the correspondig enum usb_dr_mode
*/
enum usb_dr_mode usb_get_dr_mode(int node);
/**
* usb_get_maximum_speed() - Get maximum speed for given device
* @node: Node offset to the given device
*
* The function gets phy interface string from property 'maximum-speed',
* and returns the correspondig enum usb_device_speed
*/
enum usb_device_speed usb_get_maximum_speed(int node);
#endif /* __LINUX_USB_OTG_H */

View File

@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2001
* Denis Peter, MPL AG Switzerland
*/
#ifndef _SCSI_H
#define _SCSI_H
struct scsi_cmd {
unsigned char cmd[16]; /* command */
/* for request sense */
#pragma pack(ARCH_DMA_MINALIGN)
unsigned char sense_buf[64];
unsigned char status; /* SCSI Status */
unsigned char target; /* Target ID */
unsigned char lun; /* Target LUN */
unsigned char cmdlen; /* command len */
unsigned long datalen; /* Total data length */
unsigned char * pdata; /* pointer to data */
unsigned char msgout[12]; /* Messge out buffer (NOT USED) */
unsigned char msgin[12]; /* Message in buffer */
unsigned char sensecmdlen; /* Sense command len */
unsigned long sensedatalen; /* Sense data len */
unsigned char sensecmd[6]; /* Sense command */
unsigned long contr_stat; /* Controller Status */
unsigned long trans_bytes; /* tranfered bytes */
unsigned int priv;
};
/*-----------------------------------------------------------
**
** SCSI constants.
**
**-----------------------------------------------------------
*/
/*
** Messages
*/
#define M_COMPLETE (0x00)
#define M_EXTENDED (0x01)
#define M_SAVE_DP (0x02)
#define M_RESTORE_DP (0x03)
#define M_DISCONNECT (0x04)
#define M_ID_ERROR (0x05)
#define M_ABORT (0x06)
#define M_REJECT (0x07)
#define M_NOOP (0x08)
#define M_PARITY (0x09)
#define M_LCOMPLETE (0x0a)
#define M_FCOMPLETE (0x0b)
#define M_RESET (0x0c)
#define M_ABORT_TAG (0x0d)
#define M_CLEAR_QUEUE (0x0e)
#define M_INIT_REC (0x0f)
#define M_REL_REC (0x10)
#define M_TERMINATE (0x11)
#define M_SIMPLE_TAG (0x20)
#define M_HEAD_TAG (0x21)
#define M_ORDERED_TAG (0x22)
#define M_IGN_RESIDUE (0x23)
#define M_IDENTIFY (0x80)
#define M_X_MODIFY_DP (0x00)
#define M_X_SYNC_REQ (0x01)
#define M_X_WIDE_REQ (0x03)
#define M_X_PPR_REQ (0x04)
/*
** Status
*/
#define S_GOOD (0x00)
#define S_CHECK_COND (0x02)
#define S_COND_MET (0x04)
#define S_BUSY (0x08)
#define S_INT (0x10)
#define S_INT_COND_MET (0x14)
#define S_CONFLICT (0x18)
#define S_TERMINATED (0x20)
#define S_QUEUE_FULL (0x28)
#define S_ILLEGAL (0xff)
#define S_SENSE (0x80)
/*
* Sense_keys
*/
#define SENSE_NO_SENSE 0x0
#define SENSE_RECOVERED_ERROR 0x1
#define SENSE_NOT_READY 0x2
#define SENSE_MEDIUM_ERROR 0x3
#define SENSE_HARDWARE_ERROR 0x4
#define SENSE_ILLEGAL_REQUEST 0x5
#define SENSE_UNIT_ATTENTION 0x6
#define SENSE_DATA_PROTECT 0x7
#define SENSE_BLANK_CHECK 0x8
#define SENSE_VENDOR_SPECIFIC 0x9
#define SENSE_COPY_ABORTED 0xA
#define SENSE_ABORTED_COMMAND 0xB
#define SENSE_VOLUME_OVERFLOW 0xD
#define SENSE_MISCOMPARE 0xE
#define SCSI_CHANGE_DEF 0x40 /* Change Definition (Optional) */
#define SCSI_COMPARE 0x39 /* Compare (O) */
#define SCSI_COPY 0x18 /* Copy (O) */
#define SCSI_COP_VERIFY 0x3A /* Copy and Verify (O) */
#define SCSI_INQUIRY 0x12 /* Inquiry (MANDATORY) */
#define SCSI_LOG_SELECT 0x4C /* Log Select (O) */
#define SCSI_LOG_SENSE 0x4D /* Log Sense (O) */
#define SCSI_MODE_SEL6 0x15 /* Mode Select 6-byte (Device Specific) */
#define SCSI_MODE_SEL10 0x55 /* Mode Select 10-byte (Device Specific) */
#define SCSI_MODE_SEN6 0x1A /* Mode Sense 6-byte (Device Specific) */
#define SCSI_MODE_SEN10 0x5A /* Mode Sense 10-byte (Device Specific) */
#define SCSI_READ_BUFF 0x3C /* Read Buffer (O) */
#define SCSI_REQ_SENSE 0x03 /* Request Sense (MANDATORY) */
#define SCSI_SEND_DIAG 0x1D /* Send Diagnostic (O) */
#define SCSI_TST_U_RDY 0x00 /* Test Unit Ready (MANDATORY) */
#define SCSI_WRITE_BUFF 0x3B /* Write Buffer (O) */
/***************************************************************************
* %%% Commands Unique to Direct Access Devices %%%
***************************************************************************/
#define SCSI_COMPARE 0x39 /* Compare (O) */
#define SCSI_FORMAT 0x04 /* Format Unit (MANDATORY) */
#define SCSI_LCK_UN_CAC 0x36 /* Lock Unlock Cache (O) */
#define SCSI_PREFETCH 0x34 /* Prefetch (O) */
#define SCSI_MED_REMOVL 0x1E /* Prevent/Allow medium Removal (O) */
#define SCSI_READ6 0x08 /* Read 6-byte (MANDATORY) */
#define SCSI_READ10 0x28 /* Read 10-byte (MANDATORY) */
#define SCSI_READ16 0x48
#define SCSI_RD_CAPAC 0x25 /* Read Capacity (MANDATORY) */
#define SCSI_RD_CAPAC10 SCSI_RD_CAPAC /* Read Capacity (10) */
#define SCSI_RD_CAPAC16 0x9e /* Read Capacity (16) */
#define SCSI_RD_DEFECT 0x37 /* Read Defect Data (O) */
#define SCSI_READ_LONG 0x3E /* Read Long (O) */
#define SCSI_REASS_BLK 0x07 /* Reassign Blocks (O) */
#define SCSI_RCV_DIAG 0x1C /* Receive Diagnostic Results (O) */
#define SCSI_RELEASE 0x17 /* Release Unit (MANDATORY) */
#define SCSI_REZERO 0x01 /* Rezero Unit (O) */
#define SCSI_SRCH_DAT_E 0x31 /* Search Data Equal (O) */
#define SCSI_SRCH_DAT_H 0x30 /* Search Data High (O) */
#define SCSI_SRCH_DAT_L 0x32 /* Search Data Low (O) */
#define SCSI_SEEK6 0x0B /* Seek 6-Byte (O) */
#define SCSI_SEEK10 0x2B /* Seek 10-Byte (O) */
#define SCSI_SEND_DIAG 0x1D /* Send Diagnostics (MANDATORY) */
#define SCSI_SET_LIMIT 0x33 /* Set Limits (O) */
#define SCSI_START_STP 0x1B /* Start/Stop Unit (O) */
#define SCSI_SYNC_CACHE 0x35 /* Synchronize Cache (O) */
#define SCSI_VERIFY 0x2F /* Verify (O) */
#define SCSI_WRITE6 0x0A /* Write 6-Byte (MANDATORY) */
#define SCSI_WRITE10 0x2A /* Write 10-Byte (MANDATORY) */
#define SCSI_WRT_VERIFY 0x2E /* Write and Verify (O) */
#define SCSI_WRITE_LONG 0x3F /* Write Long (O) */
#define SCSI_WRITE_SAME 0x41 /* Write Same (O) */
/**
* struct scsi_platdata - stores information about SCSI controller
*
* @base: Controller base address
* @max_lun: Maximum number of logical units
* @max_id: Maximum number of target ids
*/
struct scsi_platdata {
unsigned long base;
unsigned long max_lun;
unsigned long max_id;
};
#if 0
/* Operations for SCSI */
struct scsi_ops {
/**
* exec() - execute a command
*
* @dev: SCSI bus
* @cmd: Command to execute
* @return 0 if OK, -ve on error
*/
int (*exec)(struct udevice *dev, struct scsi_cmd *cmd);
/**
* bus_reset() - reset the bus
*
* @dev: SCSI bus to reset
* @return 0 if OK, -ve on error
*/
int (*bus_reset)(struct udevice *dev);
};
#define scsi_get_ops(dev) ((struct scsi_ops *)(dev)->driver->ops)
extern struct scsi_ops scsi_ops;
/**
* scsi_exec() - execute a command
*
* @dev: SCSI bus
* @cmd: Command to execute
* @return 0 if OK, -ve on error
*/
int scsi_exec(struct udevice *dev, struct scsi_cmd *cmd);
/**
* scsi_bus_reset() - reset the bus
*
* @dev: SCSI bus to reset
* @return 0 if OK, -ve on error
*/
int scsi_bus_reset(struct udevice *dev);
/**
* scsi_scan() - Scan all SCSI controllers for available devices
*
* @vebose: true to show information about each device found
*/
int scsi_scan(bool verbose);
/**
* scsi_scan_dev() - scan a SCSI bus and create devices
*
* @dev: SCSI bus
* @verbose: true to show information about each device found
*/
int scsi_scan_dev(struct udevice *dev, bool verbose);
#endif
void scsi_low_level_init(int busdevfunc);
void scsi_init(void);
#define SCSI_IDENTIFY 0xC0 /* not used */
/* Hardware errors */
#define SCSI_SEL_TIME_OUT 0x00000101 /* Selection time out */
#define SCSI_HNS_TIME_OUT 0x00000102 /* Handshake */
#define SCSI_MA_TIME_OUT 0x00000103 /* Phase error */
#define SCSI_UNEXP_DIS 0x00000104 /* unexpected disconnect */
#define SCSI_INT_STATE 0x00010000 /* unknown Interrupt number is stored in 16 LSB */
#endif /* _SCSI_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,380 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2001
* Denis Peter, MPL AG Switzerland
*
* Note: Part of this code has been derived from linux
*/
#ifndef _USB_DEFS_H_
#define _USB_DEFS_H_
/* USB constants */
/* Device and/or Interface Class codes */
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
#define USB_CLASS_DATA 10
#define USB_CLASS_VENDOR_SPEC 0xff
/* some HID sub classes */
#define USB_SUB_HID_NONE 0
#define USB_SUB_HID_BOOT 1
/* some UID Protocols */
#define USB_PROT_HID_NONE 0
#define USB_PROT_HID_KEYBOARD 1
#define USB_PROT_HID_MOUSE 2
/* Sub STORAGE Classes */
#define US_SC_RBC 1 /* Typically, flash devices */
#define US_SC_8020 2 /* CD-ROM */
#define US_SC_QIC 3 /* QIC-157 Tapes */
#define US_SC_UFI 4 /* Floppy */
#define US_SC_8070 5 /* Removable media */
#define US_SC_SCSI 6 /* Transparent */
#define US_SC_MIN US_SC_RBC
#define US_SC_MAX US_SC_SCSI
/* STORAGE Protocols */
#define US_PR_CB 1 /* Control/Bulk w/o interrupt */
#define US_PR_CBI 0 /* Control/Bulk/Interrupt */
#define US_PR_BULK 0x50 /* bulk only */
/* USB types */
#define USB_TYPE_STANDARD (0x00 << 5)
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_TYPE_VENDOR (0x02 << 5)
#define USB_TYPE_RESERVED (0x03 << 5)
/* USB recipients */
#define USB_RECIP_DEVICE 0x00
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_RECIP_OTHER 0x03
/* USB directions */
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
/*
* bmRequestType: USB Device Requests, table 9.2 USB 2.0 spec.
* (shifted) direction/type/recipient.
*/
#define DeviceRequest \
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
#define DeviceOutRequest \
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE) << 8)
#define InterfaceRequest \
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
#define EndpointRequest \
((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
#define EndpointOutRequest \
((USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8)
/* Descriptor types */
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_DT_HID (USB_TYPE_CLASS | 0x01)
#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02)
#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
#define USB_DT_SS_HUB (USB_TYPE_CLASS | 0x0a)
/* Descriptor sizes per descriptor type */
#define USB_DT_DEVICE_SIZE 18
#define USB_DT_CONFIG_SIZE 9
#define USB_DT_INTERFACE_SIZE 9
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
#define USB_DT_HUB_NONVAR_SIZE 7
#define USB_DT_HID_SIZE 9
/* Endpoints */
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
/* USB Packet IDs (PIDs) */
#define USB_PID_UNDEF_0 0xf0
#define USB_PID_OUT 0xe1
#define USB_PID_ACK 0xd2
#define USB_PID_DATA0 0xc3
#define USB_PID_UNDEF_4 0xb4
#define USB_PID_SOF 0xa5
#define USB_PID_UNDEF_6 0x96
#define USB_PID_UNDEF_7 0x87
#define USB_PID_UNDEF_8 0x78
#define USB_PID_IN 0x69
#define USB_PID_NAK 0x5a
#define USB_PID_DATA1 0x4b
#define USB_PID_PREAMBLE 0x3c
#define USB_PID_SETUP 0x2d
#define USB_PID_STALL 0x1e
#define USB_PID_UNDEF_F 0x0f
/* Standard requests */
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
/* HID requests */
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_GET_IDLE 0x02
#define USB_REQ_GET_PROTOCOL 0x03
#define USB_REQ_SET_REPORT 0x09
#define USB_REQ_SET_IDLE 0x0A
#define USB_REQ_SET_PROTOCOL 0x0B
/* Device features */
#define USB_FEAT_HALT 0x00
#define USB_FEAT_WAKEUP 0x01
#define USB_FEAT_TEST 0x02
/* Test modes */
#define USB_TEST_MODE_J 0x01
#define USB_TEST_MODE_K 0x02
#define USB_TEST_MODE_SE0_NAK 0x03
#define USB_TEST_MODE_PACKET 0x04
#define USB_TEST_MODE_FORCE_ENABLE 0x05
/*
* "pipe" definitions, use unsigned so we can compare reliably, since this
* value is shifted up to bits 30/31.
*/
#define PIPE_ISOCHRONOUS 0U
#define PIPE_INTERRUPT 1U
#define PIPE_CONTROL 2U
#define PIPE_BULK 3U
#define PIPE_DEVEP_MASK 0x0007ff00
#define USB_ISOCHRONOUS 0
#define USB_INTERRUPT 1
#define USB_CONTROL 2
#define USB_BULK 3
#define USB_PIPE_TYPE_SHIFT 30
#define USB_PIPE_TYPE_MASK (3 << USB_PIPE_TYPE_SHIFT)
#define USB_PIPE_DEV_SHIFT 8
#define USB_PIPE_DEV_MASK (0x7f << USB_PIPE_DEV_SHIFT)
#define USB_PIPE_EP_SHIFT 15
#define USB_PIPE_EP_MASK (0xf << USB_PIPE_EP_SHIFT)
/* USB-status codes: */
#define USB_ST_ACTIVE 0x1 /* TD is active */
#define USB_ST_STALLED 0x2 /* TD is stalled */
#define USB_ST_BUF_ERR 0x4 /* buffer error */
#define USB_ST_BABBLE_DET 0x8 /* Babble detected */
#define USB_ST_NAK_REC 0x10 /* NAK Received*/
#define USB_ST_CRC_ERR 0x20 /* CRC/timeout Error */
#define USB_ST_BIT_ERR 0x40 /* Bitstuff error */
#define USB_ST_NOT_PROC 0x80000000L /* Not yet processed */
/*************************************************************************
* Hub defines
*/
/*
* Hub request types
*/
#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE)
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
/*
* Hub Class feature numbers
*/
#define C_HUB_LOCAL_POWER 0
#define C_HUB_OVER_CURRENT 1
/*
* Port feature numbers
*/
#define USB_PORT_FEAT_CONNECTION 0
#define USB_PORT_FEAT_ENABLE 1
#define USB_PORT_FEAT_SUSPEND 2
#define USB_PORT_FEAT_OVER_CURRENT 3
#define USB_PORT_FEAT_RESET 4
#define USB_PORT_FEAT_POWER 8
#define USB_PORT_FEAT_LOWSPEED 9
#define USB_PORT_FEAT_HIGHSPEED 10
#define USB_PORT_FEAT_C_CONNECTION 16
#define USB_PORT_FEAT_C_ENABLE 17
#define USB_PORT_FEAT_C_SUSPEND 18
#define USB_PORT_FEAT_C_OVER_CURRENT 19
#define USB_PORT_FEAT_C_RESET 20
#define USB_PORT_FEAT_TEST 21
/*
* Changes to Port feature numbers for Super speed,
* from USB 3.0 spec Table 10-8
*/
#define USB_SS_PORT_FEAT_U1_TIMEOUT 23
#define USB_SS_PORT_FEAT_U2_TIMEOUT 24
#define USB_SS_PORT_FEAT_C_LINK_STATE 25
#define USB_SS_PORT_FEAT_C_CONFIG_ERROR 26
#define USB_SS_PORT_FEAT_BH_RESET 28
#define USB_SS_PORT_FEAT_C_BH_RESET 29
/* wPortStatus bits */
#define USB_PORT_STAT_CONNECTION 0x0001
#define USB_PORT_STAT_ENABLE 0x0002
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400 /* support for EHCI */
#define USB_PORT_STAT_SUPER_SPEED 0x0600 /* faking support to XHCI */
#define USB_PORT_STAT_SPEED_MASK \
(USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED)
/*
* Changes to wPortStatus bit field in USB 3.0
* See USB 3.0 spec Table 10-10
*/
#define USB_SS_PORT_STAT_LINK_STATE 0x01e0
#define USB_SS_PORT_STAT_POWER 0x0200
#define USB_SS_PORT_STAT_SPEED 0x1c00
#define USB_SS_PORT_STAT_SPEED_5GBPS 0x0000
/* Bits that are the same from USB 2.0 */
#define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \
USB_PORT_STAT_ENABLE | \
USB_PORT_STAT_OVERCURRENT | \
USB_PORT_STAT_RESET)
/* wPortChange bits */
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
/*
* Changes to wPortChange bit fields in USB 3.0
* See USB 3.0 spec Table 10-12
*/
#define USB_SS_PORT_STAT_C_BH_RESET 0x0020
#define USB_SS_PORT_STAT_C_LINK_STATE 0x0040
#define USB_SS_PORT_STAT_C_CONFIG_ERROR 0x0080
/* wHubCharacteristics (masks) */
#define HUB_CHAR_LPSM 0x0003
#define HUB_CHAR_COMPOUND 0x0004
#define HUB_CHAR_OCPM 0x0018
#define HUB_CHAR_TTTT 0x0060 /* TT Think Time mask */
/*
* Hub Status & Hub Change bit masks
*/
#define HUB_STATUS_LOCAL_POWER 0x0001
#define HUB_STATUS_OVERCURRENT 0x0002
#define HUB_CHANGE_LOCAL_POWER 0x0001
#define HUB_CHANGE_OVERCURRENT 0x0002
/* Mask for wIndex in get/set port feature */
#define USB_HUB_PORT_MASK 0xf
/* Hub class request codes */
#define USB_REQ_SET_HUB_DEPTH 0x0c
/*
* As of USB 2.0, full/low speed devices are segregated into trees.
* One type grows from USB 1.1 host controllers (OHCI, UHCI etc).
* The other type grows from high speed hubs when they connect to
* full/low speed devices using "Transaction Translators" (TTs).
*/
#if 0
struct usb_tt {
bool multi; /* true means one TT per port */
unsigned think_time; /* think time in ns */
};
#else
struct usb_device;
struct usb_tt {
struct usb_device *hub; /* upstream highspeed hub */
int multi; /* true means one TT per port */
unsigned think_time; /* think time in ns */
void *hcpriv; /* HCD private data */
/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
spinlock_t lock;
//struct list_head clear_list; /* of usb_tt_clear */
};
#endif
/*
* CBI style
*/
#define US_CBI_ADSC 0
/* Command Block Wrapper */
struct umass_bbb_cbw {
__u32 dCBWSignature;
# define CBWSIGNATURE 0x43425355
__u32 dCBWTag;
__u32 dCBWDataTransferLength;
__u8 bCBWFlags;
# define CBWFLAGS_OUT 0x00
# define CBWFLAGS_IN 0x80
# define CBWFLAGS_SBZ 0x7f
__u8 bCBWLUN;
__u8 bCDBLength;
# define CBWCDBLENGTH 16
__u8 CBWCDB[CBWCDBLENGTH];
};
#define UMASS_BBB_CBW_SIZE 31
/* Command Status Wrapper */
struct umass_bbb_csw {
__u32 dCSWSignature;
# define CSWSIGNATURE 0x53425355
__u32 dCSWTag;
__u32 dCSWDataResidue;
__u8 bCSWStatus;
# define CSWSTATUS_GOOD 0x0
# define CSWSTATUS_FAILED 0x1
# define CSWSTATUS_PHASE 0x2
};
#define UMASS_BBB_CSW_SIZE 13
/*
* BULK only
*/
#define US_BBB_RESET 0xff
#define US_BBB_GET_MAX_LUN 0xfe
#endif /*_USB_DEFS_H_ */

View File

@ -0,0 +1,534 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
* Based on
* linux/drivers/usbd/usb-function.h - USB Function
*
* Copyright (c) 2000, 2001, 2002 Lineo
* Copyright (c) 2001 Hewlett Packard
*
* By:
* Stuart Lynne <sl@lineo.com>,
* Tom Rushworth <tbr@lineo.com>,
* Bruce Balden <balden@lineo.com>
*/
/* USB Descriptors - Create a complete description of all of the
* function driver capabilities. These map directly to the USB descriptors.
*
* This heirarchy is created by the functions drivers and is passed to the
* usb-device driver when the function driver is registered.
*
* device
* configuration
* interface
* alternate
* class
* class
* alternate
* endpoint
* endpoint
* interface
* alternate
* endpoint
* endpoint
* configuration
* interface
* alternate
* endpoint
* endpoint
*
*
* The configuration structures refer to the USB Configurations that will be
* made available to a USB HOST during the enumeration process.
*
* The USB HOST will select a configuration and optionally an interface with
* the usb set configuration and set interface commands.
*
* The selected interface (or the default interface if not specifically
* selected) will define the list of endpoints that will be used.
*
* The configuration and interfaces are stored in an array that is indexed
* by the specified configuratin or interface number minus one.
*
* A configuration number of zero is used to specify a return to the unconfigured
* state.
*
*/
#ifndef __USBDESCRIPTORS_H__
#define __USBDESCRIPTORS_H__
#include <asm/types.h>
/*
* communications class types
*
* c.f. CDC USB Class Definitions for Communications Devices
* c.f. WMCD USB CDC Subclass Specification for Wireless Mobile Communications Devices
*
*/
#define CLASS_BCD_VERSION 0x0110
/* c.f. CDC 4.1 Table 14 */
#define COMMUNICATIONS_DEVICE_CLASS 0x02
/* c.f. CDC 4.2 Table 15 */
#define COMMUNICATIONS_INTERFACE_CLASS_CONTROL 0x02
#define COMMUNICATIONS_INTERFACE_CLASS_DATA 0x0A
#define COMMUNICATIONS_INTERFACE_CLASS_VENDOR 0x0FF
/* c.f. CDC 4.3 Table 16 */
#define COMMUNICATIONS_NO_SUBCLASS 0x00
#define COMMUNICATIONS_DLCM_SUBCLASS 0x01
#define COMMUNICATIONS_ACM_SUBCLASS 0x02
#define COMMUNICATIONS_TCM_SUBCLASS 0x03
#define COMMUNICATIONS_MCCM_SUBCLASS 0x04
#define COMMUNICATIONS_CCM_SUBCLASS 0x05
#define COMMUNICATIONS_ENCM_SUBCLASS 0x06
#define COMMUNICATIONS_ANCM_SUBCLASS 0x07
/* c.f. WMCD 5.1 */
#define COMMUNICATIONS_WHCM_SUBCLASS 0x08
#define COMMUNICATIONS_DMM_SUBCLASS 0x09
#define COMMUNICATIONS_MDLM_SUBCLASS 0x0a
#define COMMUNICATIONS_OBEX_SUBCLASS 0x0b
/* c.f. CDC 4.4 Table 17 */
#define COMMUNICATIONS_NO_PROTOCOL 0x00
#define COMMUNICATIONS_V25TER_PROTOCOL 0x01 /*Common AT Hayes compatible*/
/* c.f. CDC 4.5 Table 18 */
#define DATA_INTERFACE_CLASS 0x0a
/* c.f. CDC 4.6 No Table */
#define DATA_INTERFACE_SUBCLASS_NONE 0x00 /* No subclass pertinent */
/* c.f. CDC 4.7 Table 19 */
#define DATA_INTERFACE_PROTOCOL_NONE 0x00 /* No class protcol required */
/* c.f. CDC 5.2.3 Table 24 */
#define CS_INTERFACE 0x24
#define CS_ENDPOINT 0x25
/*
* bDescriptorSubtypes
*
* c.f. CDC 5.2.3 Table 25
* c.f. WMCD 5.3 Table 5.3
*/
#define USB_ST_HEADER 0x00
#define USB_ST_CMF 0x01
#define USB_ST_ACMF 0x02
#define USB_ST_DLMF 0x03
#define USB_ST_TRF 0x04
#define USB_ST_TCLF 0x05
#define USB_ST_UF 0x06
#define USB_ST_CSF 0x07
#define USB_ST_TOMF 0x08
#define USB_ST_USBTF 0x09
#define USB_ST_NCT 0x0a
#define USB_ST_PUF 0x0b
#define USB_ST_EUF 0x0c
#define USB_ST_MCMF 0x0d
#define USB_ST_CCMF 0x0e
#define USB_ST_ENF 0x0f
#define USB_ST_ATMNF 0x10
#define USB_ST_WHCM 0x11
#define USB_ST_MDLM 0x12
#define USB_ST_MDLMD 0x13
#define USB_ST_DMM 0x14
#define USB_ST_OBEX 0x15
#define USB_ST_CS 0x16
#define USB_ST_CSD 0x17
#define USB_ST_TCM 0x18
/* endpoint modifiers
* static struct usb_endpoint_description function_default_A_1[] = {
*
* {this_endpoint: 0, attributes: CONTROL, max_size: 8, polling_interval: 0 },
* {this_endpoint: 1, attributes: BULK, max_size: 64, polling_interval: 0, direction: IN},
* {this_endpoint: 2, attributes: BULK, max_size: 64, polling_interval: 0, direction: OUT},
* {this_endpoint: 3, attributes: INTERRUPT, max_size: 8, polling_interval: 0},
*
*
*/
#define OUT 0x00
#define IN 0x80
#define CONTROL 0x00
#define ISOCHRONOUS 0x01
#define BULK 0x02
#define INTERRUPT 0x03
/* configuration modifiers
*/
#define BMATTRIBUTE_RESERVED 0x80
#define BMATTRIBUTE_SELF_POWERED 0x40
/*
* standard usb descriptor structures
*/
struct usb_endpoint_descriptor {
u8 bLength;
u8 bDescriptorType; /* 0x5 */
u8 bEndpointAddress;
u8 bmAttributes;
u16 wMaxPacketSize;
u8 bInterval;
} __attribute__ ((packed));
struct usb_interface_descriptor {
u8 bLength;
u8 bDescriptorType; /* 0x04 */
u8 bInterfaceNumber;
u8 bAlternateSetting;
u8 bNumEndpoints;
u8 bInterfaceClass;
u8 bInterfaceSubClass;
u8 bInterfaceProtocol;
u8 iInterface;
} __attribute__ ((packed));
struct usb_configuration_descriptor {
u8 bLength;
u8 bDescriptorType; /* 0x2 */
u16 wTotalLength;
u8 bNumInterfaces;
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
} __attribute__ ((packed));
struct usb_device_descriptor {
u8 bLength;
u8 bDescriptorType; /* 0x01 */
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u16 idVendor;
u16 idProduct;
u16 bcdDevice;
u8 iManufacturer;
u8 iProduct;
u8 iSerialNumber;
u8 bNumConfigurations;
} __attribute__ ((packed));
#if defined(CONFIG_USBD_HS)
struct usb_qualifier_descriptor {
u8 bLength;
u8 bDescriptorType;
u16 bcdUSB;
u8 bDeviceClass;
u8 bDeviceSubClass;
u8 bDeviceProtocol;
u8 bMaxPacketSize0;
u8 bNumConfigurations;
u8 breserved;
} __attribute__ ((packed));
#endif
struct usb_string_descriptor {
u8 bLength;
u8 bDescriptorType; /* 0x03 */
u16 wData[0];
} __attribute__ ((packed));
struct usb_generic_descriptor {
u8 bLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
} __attribute__ ((packed));
/*
* communications class descriptor structures
*
* c.f. CDC 5.2 Table 25c
*/
struct usb_class_function_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
} __attribute__ ((packed));
struct usb_class_function_descriptor_generic {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype;
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_header_function_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x00 */
u16 bcdCDC;
} __attribute__ ((packed));
struct usb_class_call_management_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x01 */
u8 bmCapabilities;
u8 bDataInterface;
} __attribute__ ((packed));
struct usb_class_abstract_control_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x02 */
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_direct_line_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x03 */
} __attribute__ ((packed));
struct usb_class_telephone_ringer_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x04 */
u8 bRingerVolSeps;
u8 bNumRingerPatterns;
} __attribute__ ((packed));
struct usb_class_telephone_call_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x05 */
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_union_function_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x06 */
u8 bMasterInterface;
/* u8 bSlaveInterface0[0]; */
u8 bSlaveInterface0;
} __attribute__ ((packed));
struct usb_class_country_selection_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x07 */
u8 iCountryCodeRelDate;
u16 wCountryCode0[0];
} __attribute__ ((packed));
struct usb_class_telephone_operational_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x08 */
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_usb_terminal_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x09 */
u8 bEntityId;
u8 bInterfaceNo;
u8 bOutInterfaceNo;
u8 bmOptions;
u8 bChild0[0];
} __attribute__ ((packed));
struct usb_class_network_channel_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0a */
u8 bEntityId;
u8 iName;
u8 bChannelIndex;
u8 bPhysicalInterface;
} __attribute__ ((packed));
struct usb_class_protocol_unit_function_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0b */
u8 bEntityId;
u8 bProtocol;
u8 bChild0[0];
} __attribute__ ((packed));
struct usb_class_extension_unit_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0c */
u8 bEntityId;
u8 bExtensionCode;
u8 iName;
u8 bChild0[0];
} __attribute__ ((packed));
struct usb_class_multi_channel_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0d */
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_capi_control_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0e */
u8 bmCapabilities;
} __attribute__ ((packed));
struct usb_class_ethernet_networking_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x0f */
u8 iMACAddress;
u32 bmEthernetStatistics;
u16 wMaxSegmentSize;
u16 wNumberMCFilters;
u8 bNumberPowerFilters;
} __attribute__ ((packed));
struct usb_class_atm_networking_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x10 */
u8 iEndSystermIdentifier;
u8 bmDataCapabilities;
u8 bmATMDeviceStatistics;
u16 wType2MaxSegmentSize;
u16 wType3MaxSegmentSize;
u16 wMaxVC;
} __attribute__ ((packed));
struct usb_class_mdlm_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x12 */
u16 bcdVersion;
u8 bGUID[16];
} __attribute__ ((packed));
struct usb_class_mdlmd_descriptor {
u8 bFunctionLength;
u8 bDescriptorType;
u8 bDescriptorSubtype; /* 0x13 */
u8 bGuidDescriptorType;
u8 bDetailData[0];
} __attribute__ ((packed));
/*
* HID class descriptor structures
*
* c.f. HID 6.2.1
*/
struct usb_class_hid_descriptor {
u8 bLength;
u8 bDescriptorType;
u16 bcdCDC;
u8 bCountryCode;
u8 bNumDescriptors; /* 0x01 */
u8 bDescriptorType0;
u16 wDescriptorLength0;
/* optional descriptors are not supported. */
} __attribute__((packed));
struct usb_class_report_descriptor {
u8 bLength; /* dummy */
u8 bDescriptorType;
u16 wLength;
u8 bData[0];
} __attribute__((packed));
/*
* descriptor union structures
*/
struct usb_descriptor {
union {
struct usb_generic_descriptor generic;
struct usb_endpoint_descriptor endpoint;
struct usb_interface_descriptor interface;
struct usb_configuration_descriptor configuration;
struct usb_device_descriptor device;
struct usb_string_descriptor string;
} descriptor;
} __attribute__ ((packed));
struct usb_class_descriptor {
union {
struct usb_class_function_descriptor function;
struct usb_class_function_descriptor_generic generic;
struct usb_class_header_function_descriptor header_function;
struct usb_class_call_management_descriptor call_management;
struct usb_class_abstract_control_descriptor abstract_control;
struct usb_class_direct_line_descriptor direct_line;
struct usb_class_telephone_ringer_descriptor telephone_ringer;
struct usb_class_telephone_operational_descriptor telephone_operational;
struct usb_class_telephone_call_descriptor telephone_call;
struct usb_class_union_function_descriptor union_function;
struct usb_class_country_selection_descriptor country_selection;
struct usb_class_usb_terminal_descriptor usb_terminal;
struct usb_class_network_channel_descriptor network_channel;
struct usb_class_extension_unit_descriptor extension_unit;
struct usb_class_multi_channel_descriptor multi_channel;
struct usb_class_capi_control_descriptor capi_control;
struct usb_class_ethernet_networking_descriptor ethernet_networking;
struct usb_class_atm_networking_descriptor atm_networking;
struct usb_class_mdlm_descriptor mobile_direct;
struct usb_class_mdlmd_descriptor mobile_direct_detail;
struct usb_class_hid_descriptor hid;
} descriptor;
} __attribute__ ((packed));
#ifdef DEBUG
static inline void print_device_descriptor(struct usb_device_descriptor *d)
{
serial_printf("usb device descriptor \n");
serial_printf("\tbLength %2.2x\n", d->bLength);
serial_printf("\tbDescriptorType %2.2x\n", d->bDescriptorType);
serial_printf("\tbcdUSB %4.4x\n", d->bcdUSB);
serial_printf("\tbDeviceClass %2.2x\n", d->bDeviceClass);
serial_printf("\tbDeviceSubClass %2.2x\n", d->bDeviceSubClass);
serial_printf("\tbDeviceProtocol %2.2x\n", d->bDeviceProtocol);
serial_printf("\tbMaxPacketSize0 %2.2x\n", d->bMaxPacketSize0);
serial_printf("\tidVendor %4.4x\n", d->idVendor);
serial_printf("\tidProduct %4.4x\n", d->idProduct);
serial_printf("\tbcdDevice %4.4x\n", d->bcdDevice);
serial_printf("\tiManufacturer %2.2x\n", d->iManufacturer);
serial_printf("\tiProduct %2.2x\n", d->iProduct);
serial_printf("\tiSerialNumber %2.2x\n", d->iSerialNumber);
serial_printf("\tbNumConfigurations %2.2x\n", d->bNumConfigurations);
}
#else
/* stubs */
#define print_device_descriptor(d)
#endif /* DEBUG */
#endif

View File

@ -0,0 +1,777 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2003
* Gerry Hamel, geh@ti.com, Texas Instruments
*
* Based on linux/drivers/usbd/usbd.h
*
* Copyright (c) 2000, 2001, 2002 Lineo
* Copyright (c) 2001 Hewlett Packard
*
* By:
* Stuart Lynne <sl@lineo.com>,
* Tom Rushworth <tbr@lineo.com>,
* Bruce Balden <balden@lineo.com>
*/
#ifndef __USBDCORE_H__
#define __USBDCORE_H__
#include <common.h>
#include "usbdescriptors.h"
#define MAX_URBS_QUEUED 5
#if 1
#define usberr(fmt,args...) serial_printf("ERROR: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args)
#else
#define usberr(fmt,args...) do{}while(0)
#endif
#if 0
#define usbdbg(fmt,args...) serial_printf("debug: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args)
#else
#define usbdbg(fmt,args...) do{}while(0)
#endif
#if 0
#define usbinfo(fmt,args...) serial_printf("info: %s(), %d: "fmt"\n",__FUNCTION__,__LINE__,##args)
#else
#define usbinfo(fmt,args...) do{}while(0)
#endif
#ifndef le16_to_cpu
#define le16_to_cpu(x) (x)
#endif
#ifndef inb
#define inb(p) (*(volatile u8*)(p))
#endif
#ifndef outb
#define outb(val,p) (*(volatile u8*)(p) = (val))
#endif
#ifndef inw
#define inw(p) (*(volatile u16*)(p))
#endif
#ifndef outw
#define outw(val,p) (*(volatile u16*)(p) = (val))
#endif
#ifndef inl
#define inl(p) (*(volatile u32*)(p))
#endif
#ifndef outl
#define outl(val,p) (*(volatile u32*)(p) = (val))
#endif
#ifndef insw
#define insw(p,to,len) mmio_insw(p,to,len)
#endif
#ifndef outsw
#define outsw(p,from,len) mmio_outsw(p,from,len)
#endif
#ifndef insb
#define insb(p,to,len) mmio_insb(p,to,len)
#endif
#ifndef mmio_insw
#define mmio_insw(r,b,l) ({ int __i ; \
u16 *__b2; \
__b2 = (u16 *) b; \
for (__i = 0; __i < l; __i++) { \
*(__b2 + __i) = inw(r); \
}; \
})
#endif
#ifndef mmio_outsw
#define mmio_outsw(r,b,l) ({ int __i; \
u16 *__b2; \
__b2 = (u16 *) b; \
for (__i = 0; __i < l; __i++) { \
outw( *(__b2 + __i), r); \
} \
})
#endif
#ifndef mmio_insb
#define mmio_insb(r,b,l) ({ int __i ; \
u8 *__b2; \
__b2 = (u8 *) b; \
for (__i = 0; __i < l; __i++) { \
*(__b2 + __i) = inb(r); \
}; \
})
#endif
/*
* Structure member address manipulation macros.
* These are used by client code (code using the urb_link routines), since
* the urb_link structure is embedded in the client data structures.
*
* Note: a macro offsetof equivalent to member_offset is defined in stddef.h
* but this is kept here for the sake of portability.
*
* p2surround returns a pointer to the surrounding structure given
* type of the surrounding structure, the name memb of the structure
* member pointed at by ptr. For example, if you have:
*
* struct foo {
* int x;
* float y;
* char z;
* } thingy;
*
* char *cp = &thingy.z;
*
* then
*
* &thingy == p2surround(struct foo, z, cp)
*
* Clear?
*/
#define _cv_(ptr) ((char*)(void*)(ptr))
#define member_offset(type,memb) (_cv_(&(((type*)0)->memb))-(char*)0)
#define p2surround(type,memb,ptr) ((type*)(void*)(_cv_(ptr)-member_offset(type,memb)))
struct urb;
struct usb_endpoint_instance;
struct usb_interface_instance;
struct usb_configuration_instance;
struct usb_device_instance;
struct usb_bus_instance;
/*
* Device and/or Interface Class codes
*/
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
#define USB_CLASS_DATA 10
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
/*
* USB types
*/
#define USB_TYPE_STANDARD (0x00 << 5)
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_TYPE_VENDOR (0x02 << 5)
#define USB_TYPE_RESERVED (0x03 << 5)
/*
* USB recipients
*/
#define USB_RECIP_DEVICE 0x00
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_RECIP_OTHER 0x03
/*
* USB directions
*/
#define USB_DIR_OUT 0
#define USB_DIR_IN 0x80
/*
* Descriptor types
*/
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#if defined(CONFIG_USBD_HS)
#define USB_DT_QUAL 0x06
#endif
#define USB_DT_HID (USB_TYPE_CLASS | 0x01)
#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02)
#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03)
#define USB_DT_HUB (USB_TYPE_CLASS | 0x09)
/*
* Descriptor sizes per descriptor type
*/
#define USB_DT_DEVICE_SIZE 18
#define USB_DT_CONFIG_SIZE 9
#define USB_DT_INTERFACE_SIZE 9
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
#define USB_DT_HUB_NONVAR_SIZE 7
#define USB_DT_HID_SIZE 9
/*
* Endpoints
*/
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
/*
* USB Packet IDs (PIDs)
*/
#define USB_PID_UNDEF_0 0xf0
#define USB_PID_OUT 0xe1
#define USB_PID_ACK 0xd2
#define USB_PID_DATA0 0xc3
#define USB_PID_PING 0xb4 /* USB 2.0 */
#define USB_PID_SOF 0xa5
#define USB_PID_NYET 0x96 /* USB 2.0 */
#define USB_PID_DATA2 0x87 /* USB 2.0 */
#define USB_PID_SPLIT 0x78 /* USB 2.0 */
#define USB_PID_IN 0x69
#define USB_PID_NAK 0x5a
#define USB_PID_DATA1 0x4b
#define USB_PID_PREAMBLE 0x3c /* Token mode */
#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */
#define USB_PID_SETUP 0x2d
#define USB_PID_STALL 0x1e
#define USB_PID_MDATA 0x0f /* USB 2.0 */
/*
* Standard requests
*/
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
#define USBD_DEVICE_REQUESTS(x) (((unsigned int)x <= USB_REQ_SYNCH_FRAME) ? usbd_device_requests[x] : "UNKNOWN")
/*
* HID requests
*/
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_GET_IDLE 0x02
#define USB_REQ_GET_PROTOCOL 0x03
#define USB_REQ_SET_REPORT 0x09
#define USB_REQ_SET_IDLE 0x0A
#define USB_REQ_SET_PROTOCOL 0x0B
/*
* USB Spec Release number
*/
#if defined(CONFIG_USBD_HS)
#define USB_BCD_VERSION 0x0200
#else
#define USB_BCD_VERSION 0x0110
#endif
/*
* Device Requests (c.f Table 9-2)
*/
#define USB_REQ_DIRECTION_MASK 0x80
#define USB_REQ_TYPE_MASK 0x60
#define USB_REQ_RECIPIENT_MASK 0x1f
#define USB_REQ_DEVICE2HOST 0x80
#define USB_REQ_HOST2DEVICE 0x00
#define USB_REQ_TYPE_STANDARD 0x00
#define USB_REQ_TYPE_CLASS 0x20
#define USB_REQ_TYPE_VENDOR 0x40
#define USB_REQ_RECIPIENT_DEVICE 0x00
#define USB_REQ_RECIPIENT_INTERFACE 0x01
#define USB_REQ_RECIPIENT_ENDPOINT 0x02
#define USB_REQ_RECIPIENT_OTHER 0x03
/*
* get status bits
*/
#define USB_STATUS_SELFPOWERED 0x01
#define USB_STATUS_REMOTEWAKEUP 0x02
#define USB_STATUS_HALT 0x01
/*
* descriptor types
*/
#define USB_DESCRIPTOR_TYPE_DEVICE 0x01
#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02
#define USB_DESCRIPTOR_TYPE_STRING 0x03
#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04
#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05
#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06
#define USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION 0x07
#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08
#define USB_DESCRIPTOR_TYPE_HID 0x21
#define USB_DESCRIPTOR_TYPE_REPORT 0x22
#define USBD_DEVICE_DESCRIPTORS(x) (((unsigned int)x <= USB_DESCRIPTOR_TYPE_INTERFACE_POWER) ? \
usbd_device_descriptors[x] : "UNKNOWN")
/*
* standard feature selectors
*/
#define USB_ENDPOINT_HALT 0x00
#define USB_DEVICE_REMOTE_WAKEUP 0x01
#define USB_TEST_MODE 0x02
/* USB Requests
*
*/
struct usb_device_request {
u8 bmRequestType;
u8 bRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
} __attribute__ ((packed));
/* USB Status
*
*/
typedef enum urb_send_status {
SEND_IN_PROGRESS,
SEND_FINISHED_OK,
SEND_FINISHED_ERROR,
RECV_READY,
RECV_OK,
RECV_ERROR
} urb_send_status_t;
/*
* Device State (c.f USB Spec 2.0 Figure 9-1)
*
* What state the usb device is in.
*
* Note the state does not change if the device is suspended, we simply set a
* flag to show that it is suspended.
*
*/
typedef enum usb_device_state {
STATE_INIT, /* just initialized */
STATE_CREATED, /* just created */
STATE_ATTACHED, /* we are attached */
STATE_POWERED, /* we have seen power indication (electrical bus signal) */
STATE_DEFAULT, /* we been reset */
STATE_ADDRESSED, /* we have been addressed (in default configuration) */
STATE_CONFIGURED, /* we have seen a set configuration device command */
STATE_UNKNOWN, /* destroyed */
} usb_device_state_t;
#define USBD_DEVICE_STATE(x) (((unsigned int)x <= STATE_UNKNOWN) ? usbd_device_states[x] : "UNKNOWN")
/*
* Device status
*
* Overall state
*/
typedef enum usb_device_status {
USBD_OPENING, /* we are currently opening */
USBD_OK, /* ok to use */
USBD_SUSPENDED, /* we are currently suspended */
USBD_CLOSING, /* we are currently closing */
} usb_device_status_t;
#define USBD_DEVICE_STATUS(x) (((unsigned int)x <= USBD_CLOSING) ? usbd_device_status[x] : "UNKNOWN")
/*
* Device Events
*
* These are defined in the USB Spec (c.f USB Spec 2.0 Figure 9-1).
*
* There are additional events defined to handle some extra actions we need
* to have handled.
*
*/
typedef enum usb_device_event {
DEVICE_UNKNOWN, /* bi - unknown event */
DEVICE_INIT, /* bi - initialize */
DEVICE_CREATE, /* bi - */
DEVICE_HUB_CONFIGURED, /* bi - bus has been plugged int */
DEVICE_RESET, /* bi - hub has powered our port */
DEVICE_ADDRESS_ASSIGNED, /* ep0 - set address setup received */
DEVICE_CONFIGURED, /* ep0 - set configure setup received */
DEVICE_SET_INTERFACE, /* ep0 - set interface setup received */
DEVICE_SET_FEATURE, /* ep0 - set feature setup received */
DEVICE_CLEAR_FEATURE, /* ep0 - clear feature setup received */
DEVICE_DE_CONFIGURED, /* ep0 - set configure setup received for ?? */
DEVICE_BUS_INACTIVE, /* bi - bus in inactive (no SOF packets) */
DEVICE_BUS_ACTIVITY, /* bi - bus is active again */
DEVICE_POWER_INTERRUPTION, /* bi - hub has depowered our port */
DEVICE_HUB_RESET, /* bi - bus has been unplugged */
DEVICE_DESTROY, /* bi - device instance should be destroyed */
DEVICE_HOTPLUG, /* bi - a hotplug event has occurred */
DEVICE_FUNCTION_PRIVATE, /* function - private */
} usb_device_event_t;
typedef struct urb_link {
struct urb_link *next;
struct urb_link *prev;
} urb_link;
/* USB Data structure - for passing data around.
*
* This is used for both sending and receiving data.
*
* The callback function is used to let the function driver know when
* transmitted data has been sent.
*
* The callback function is set by the alloc_recv function when an urb is
* allocated for receiving data for an endpoint and used to call the
* function driver to inform it that data has arrived.
*/
/* in linux we'd malloc this, but in u-boot we prefer static data */
#define URB_BUF_SIZE 512
struct urb {
struct usb_endpoint_instance *endpoint;
struct usb_device_instance *device;
struct usb_device_request device_request; /* contents of received SETUP packet */
struct urb_link link; /* embedded struct for circular doubly linked list of urbs */
u8* buffer;
unsigned int buffer_length;
unsigned int actual_length;
urb_send_status_t status;
int data;
u16 buffer_data[URB_BUF_SIZE]; /* data received (OUT) or being sent (IN) */
};
/* Endpoint configuration
*
* Per endpoint configuration data. Used to track which function driver owns
* an endpoint.
*
*/
struct usb_endpoint_instance {
int endpoint_address; /* logical endpoint address */
/* control */
int status; /* halted */
int state; /* available for use by bus interface driver */
/* receive side */
struct urb_link rcv; /* received urbs */
struct urb_link rdy; /* empty urbs ready to receive */
struct urb *rcv_urb; /* active urb */
int rcv_attributes; /* copy of bmAttributes from endpoint descriptor */
int rcv_packetSize; /* maximum packet size from endpoint descriptor */
int rcv_transferSize; /* maximum transfer size from function driver */
int rcv_queue;
/* transmit side */
struct urb_link tx; /* urbs ready to transmit */
struct urb_link done; /* transmitted urbs */
struct urb *tx_urb; /* active urb */
int tx_attributes; /* copy of bmAttributes from endpoint descriptor */
int tx_packetSize; /* maximum packet size from endpoint descriptor */
int tx_transferSize; /* maximum transfer size from function driver */
int tx_queue;
int sent; /* data already sent */
int last; /* data sent in last packet XXX do we need this */
};
struct usb_alternate_instance {
struct usb_interface_descriptor *interface_descriptor;
int endpoints;
int *endpoint_transfersize_array;
struct usb_endpoint_descriptor **endpoints_descriptor_array;
};
struct usb_interface_instance {
int alternates;
struct usb_alternate_instance *alternates_instance_array;
};
struct usb_configuration_instance {
int interfaces;
struct usb_configuration_descriptor *configuration_descriptor;
struct usb_interface_instance *interface_instance_array;
};
/* USB Device Instance
*
* For each physical bus interface we create a logical device structure. This
* tracks all of the required state to track the USB HOST's view of the device.
*
* Keep track of the device configuration for a real physical bus interface,
* this includes the bus interface, multiple function drivers, the current
* configuration and the current state.
*
* This will show:
* the specific bus interface driver
* the default endpoint 0 driver
* the configured function driver
* device state
* device status
* endpoint list
*/
struct usb_device_instance {
/* generic */
char *name;
struct usb_device_descriptor *device_descriptor; /* per device descriptor */
#if defined(CONFIG_USBD_HS)
struct usb_qualifier_descriptor *qualifier_descriptor;
#endif
void (*event) (struct usb_device_instance *device, usb_device_event_t event, int data);
/* Do cdc device specific control requests */
int (*cdc_recv_setup)(struct usb_device_request *request, struct urb *urb);
/* bus interface */
struct usb_bus_instance *bus; /* which bus interface driver */
/* configuration descriptors */
int configurations;
struct usb_configuration_instance *configuration_instance_array;
/* device state */
usb_device_state_t device_state; /* current USB Device state */
usb_device_state_t device_previous_state; /* current USB Device state */
u8 address; /* current address (zero is default) */
u8 configuration; /* current show configuration (zero is default) */
u8 interface; /* current interface (zero is default) */
u8 alternate; /* alternate flag */
usb_device_status_t status; /* device status */
int urbs_queued; /* number of submitted urbs */
/* Shouldn't need to make this atomic, all we need is a change indicator */
unsigned long usbd_rxtx_timestamp;
unsigned long usbd_last_rxtx_timestamp;
};
/* Bus Interface configuration structure
*
* This is allocated for each configured instance of a bus interface driver.
*
* The privdata pointer may be used by the bus interface driver to store private
* per instance state information.
*/
struct usb_bus_instance {
struct usb_device_instance *device;
struct usb_endpoint_instance *endpoint_array; /* array of available configured endpoints */
int max_endpoints; /* maximimum number of rx enpoints */
unsigned char maxpacketsize;
unsigned int serial_number;
char *serial_number_str;
void *privdata; /* private data for the bus interface */
};
extern char *usbd_device_events[];
extern char *usbd_device_states[];
extern char *usbd_device_status[];
extern char *usbd_device_requests[];
extern char *usbd_device_descriptors[];
void urb_link_init (urb_link * ul);
void urb_detach (struct urb *urb);
urb_link *first_urb_link (urb_link * hd);
struct urb *first_urb (urb_link * hd);
struct urb *first_urb_detached (urb_link * hd);
void urb_append (urb_link * hd, struct urb *urb);
struct urb *usbd_alloc_urb (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint);
void usbd_dealloc_urb (struct urb *urb);
/*
* usbd_device_event is used by bus interface drivers to tell the higher layers that
* certain events have taken place.
*/
void usbd_device_event_irq (struct usb_device_instance *conf, usb_device_event_t, int);
void usbd_device_event (struct usb_device_instance *conf, usb_device_event_t, int);
/* descriptors
*
* Various ways of finding descriptors based on the current device and any
* possible configuration / interface / endpoint for it.
*/
struct usb_configuration_descriptor *usbd_device_configuration_descriptor (struct usb_device_instance *, int, int);
struct usb_function_instance *usbd_device_function_instance (struct usb_device_instance *, unsigned int);
struct usb_interface_instance *usbd_device_interface_instance (struct usb_device_instance *, int, int, int);
struct usb_alternate_instance *usbd_device_alternate_instance (struct usb_device_instance *, int, int, int, int);
struct usb_interface_descriptor *usbd_device_interface_descriptor (struct usb_device_instance *, int, int, int, int);
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor_index (struct usb_device_instance *, int, int, int, int, int);
struct usb_class_descriptor *usbd_device_class_descriptor_index (struct usb_device_instance *, int, int, int, int, int);
struct usb_class_report_descriptor *usbd_device_class_report_descriptor_index( struct usb_device_instance *, int , int , int , int , int );
struct usb_endpoint_descriptor *usbd_device_endpoint_descriptor (struct usb_device_instance *, int, int, int, int, int);
int usbd_device_endpoint_transfersize (struct usb_device_instance *, int, int, int, int, int);
struct usb_string_descriptor *usbd_get_string (u8);
struct usb_device_descriptor *usbd_device_device_descriptor(struct
usb_device_instance *, int);
#if defined(CONFIG_USBD_HS)
/*
* is_usbd_high_speed routine needs to be defined by specific gadget driver
* It returns true if device enumerates at High speed
* Retuns false otherwise
*/
int is_usbd_high_speed(void);
#endif
int usbd_endpoint_halted (struct usb_device_instance *device, int endpoint);
void usbd_rcv_complete(struct usb_endpoint_instance *endpoint, int len, int urb_bad);
void usbd_tx_complete (struct usb_endpoint_instance *endpoint);
/* These are macros used in debugging */
#ifdef DEBUG
static inline void print_urb(struct urb *u)
{
serial_printf("urb %p\n", (u));
serial_printf("\tendpoint %p\n", u->endpoint);
serial_printf("\tdevice %p\n", u->device);
serial_printf("\tbuffer %p\n", u->buffer);
serial_printf("\tbuffer_length %d\n", u->buffer_length);
serial_printf("\tactual_length %d\n", u->actual_length);
serial_printf("\tstatus %d\n", u->status);
serial_printf("\tdata %d\n", u->data);
}
static inline void print_usb_device_request(struct usb_device_request *r)
{
serial_printf("usb request\n");
serial_printf("\tbmRequestType 0x%2.2x\n", r->bmRequestType);
if ((r->bmRequestType & USB_REQ_DIRECTION_MASK) == 0)
serial_printf("\t\tDirection : To device\n");
else
serial_printf("\t\tDirection : To host\n");
if ((r->bmRequestType & USB_TYPE_STANDARD) == USB_TYPE_STANDARD)
serial_printf("\t\tType : Standard\n");
if ((r->bmRequestType & USB_TYPE_CLASS) == USB_TYPE_CLASS)
serial_printf("\t\tType : Standard\n");
if ((r->bmRequestType & USB_TYPE_VENDOR) == USB_TYPE_VENDOR)
serial_printf("\t\tType : Standard\n");
if ((r->bmRequestType & USB_TYPE_RESERVED) == USB_TYPE_RESERVED)
serial_printf("\t\tType : Standard\n");
if ((r->bmRequestType & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
serial_printf("\t\tRecipient : Device\n");
if ((r->bmRequestType & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_INTERFACE)
serial_printf("\t\tRecipient : Interface\n");
if ((r->bmRequestType & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_ENDPOINT)
serial_printf("\t\tRecipient : Endpoint\n");
if ((r->bmRequestType & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_OTHER)
serial_printf("\t\tRecipient : Other\n");
serial_printf("\tbRequest 0x%2.2x\n", r->bRequest);
if (r->bRequest == USB_REQ_GET_STATUS)
serial_printf("\t\tGET_STATUS\n");
else if (r->bRequest == USB_REQ_SET_ADDRESS)
serial_printf("\t\tSET_ADDRESS\n");
else if (r->bRequest == USB_REQ_SET_FEATURE)
serial_printf("\t\tSET_FEATURE\n");
else if (r->bRequest == USB_REQ_GET_DESCRIPTOR)
serial_printf("\t\tGET_DESCRIPTOR\n");
else if (r->bRequest == USB_REQ_SET_CONFIGURATION)
serial_printf("\t\tSET_CONFIGURATION\n");
else if (r->bRequest == USB_REQ_SET_INTERFACE)
serial_printf("\t\tUSB_REQ_SET_INTERFACE\n");
else
serial_printf("\tUNKNOWN %d\n", r->bRequest);
serial_printf("\twValue 0x%4.4x\n", r->wValue);
if (r->bRequest == USB_REQ_GET_DESCRIPTOR) {
switch (r->wValue >> 8) {
case USB_DESCRIPTOR_TYPE_DEVICE:
serial_printf("\tDEVICE\n");
break;
case USB_DESCRIPTOR_TYPE_CONFIGURATION:
serial_printf("\tCONFIGURATION\n");
break;
case USB_DESCRIPTOR_TYPE_STRING:
serial_printf("\tSTRING\n");
break;
case USB_DESCRIPTOR_TYPE_INTERFACE:
serial_printf("\tINTERFACE\n");
break;
case USB_DESCRIPTOR_TYPE_ENDPOINT:
serial_printf("\tENDPOINT\n");
break;
case USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
serial_printf("\tDEVICE_QUALIFIER\n");
break;
case USB_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
serial_printf("\tOTHER_SPEED_CONFIGURATION\n");
break;
case USB_DESCRIPTOR_TYPE_INTERFACE_POWER:
serial_printf("\tINTERFACE_POWER\n");
break;
case USB_DESCRIPTOR_TYPE_HID:
serial_printf("\tHID\n");
break;
case USB_DESCRIPTOR_TYPE_REPORT:
serial_printf("\tREPORT\n");
break;
default:
serial_printf("\tUNKNOWN TYPE\n");
break;
}
}
serial_printf("\twIndex 0x%4.4x\n", r->wIndex);
serial_printf("\twLength 0x%4.4x\n", r->wLength);
}
#else
/* stubs */
#define print_urb(u)
#define print_usb_device_request(r)
#endif /* DEBUG */
#endif

View File

@ -0,0 +1,128 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* USB virtual root hub descriptors
*
* (C) Copyright 2014
* Stephen Warren swarren@wwwdotorg.org
*
* Based on ohci-hcd.c
*/
#ifndef __USBROOTHUBDES_H__
#define __USBROOTHUBDES_H__
/* Device descriptor */
static __u8 root_hub_dev_des[] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, /* __u16 bcdUSB; v1.1 */
0x01,
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; */
0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
0x00, /* __u16 idVendor; */
0x00,
0x00, /* __u16 idProduct; */
0x00,
0x00, /* __u16 bcdDevice; */
0x00,
0x00, /* __u8 iManufacturer; */
0x01, /* __u8 iProduct; */
0x00, /* __u8 iSerialNumber; */
0x01, /* __u8 bNumConfigurations; */
};
/* Configuration descriptor */
static __u8 root_hub_config_des[] = {
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, /* __u16 wTotalLength; */
0x00,
0x01, /* __u8 bNumInterfaces; */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0x40, /* __u8 bmAttributes;
* Bit 7: Bus-powered
* 6: Self-powered,
* 5 Remote-wakwup,
* 4..0: resvd
*/
0x00, /* __u8 MaxPower; */
/* interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* endpoint */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
0x00,
0xff, /* __u8 ep_bInterval; 255 ms */
};
#ifdef WANT_USB_ROOT_HUB_HUB_DES
static unsigned char root_hub_hub_des[] = {
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */
0x02, /* __u8 bNbrPorts; */
0x00, /* __u16 wHubCharacteristics; */
0x00,
0x01, /* __u8 bPwrOn2pwrGood; 2ms */
0x00, /* __u8 bHubContrCurrent; 0 mA */
0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
0xff, /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
};
#endif
static unsigned char root_hub_str_index0[] = {
0x04, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
0x09, /* __u8 lang ID */
0x04, /* __u8 lang ID */
};
static unsigned char root_hub_str_index1[] = {
32, /* __u8 bLength; */
0x03, /* __u8 bDescriptorType; String-descriptor */
'U', /* __u8 Unicode */
0, /* __u8 Unicode */
'-', /* __u8 Unicode */
0, /* __u8 Unicode */
'B', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
't', /* __u8 Unicode */
0, /* __u8 Unicode */
' ', /* __u8 Unicode */
0, /* __u8 Unicode */
'R', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
'o', /* __u8 Unicode */
0, /* __u8 Unicode */
't', /* __u8 Unicode */
0, /* __u8 Unicode */
' ', /* __u8 Unicode */
0, /* __u8 Unicode */
'H', /* __u8 Unicode */
0, /* __u8 Unicode */
'u', /* __u8 Unicode */
0, /* __u8 Unicode */
'b', /* __u8 Unicode */
0, /* __u8 Unicode */
};
#endif

View File

@ -0,0 +1,698 @@
/*
* Copyright (C) 2004-2016 Synopsys, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
#include "usb_os_adapter.h"
#include "trace.h"
#include <asm/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "board.h"
//#define USB_DMA
/* static void dwc2_set_bcm_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->host_rx_fifo_size = 774;
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = 0x10;
p->uframe_sched = false;
}
static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->speed = DWC2_SPEED_PARAM_HIGH;
p->host_rx_fifo_size = 512;
p->host_nperio_tx_fifo_size = 512;
p->host_perio_tx_fifo_size = 512;
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->host_channels = 16;
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
p->phy_utmi_width = 8;
p->i2c_enable = false;
p->reload_ctl = false;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
p->uframe_sched = false;
p->change_speed_quirk = true;
}
static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->host_rx_fifo_size = 525;
p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 256;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
}
static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = 2;
p->host_rx_fifo_size = 288;
p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 96;
p->max_transfer_size = 65535;
p->max_packet_count = 511;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
}
static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->speed = DWC2_SPEED_PARAM_HIGH;
p->host_rx_fifo_size = 512;
p->host_nperio_tx_fifo_size = 500;
p->host_perio_tx_fifo_size = 500;
p->host_channels = 16;
p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 <<
GAHBCFG_HBSTLEN_SHIFT;
p->uframe_sched = false;
}
static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
}
static void dwc2_set_stm32f4x9_fsotg_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->speed = DWC2_SPEED_PARAM_FULL;
p->host_rx_fifo_size = 128;
p->host_nperio_tx_fifo_size = 96;
p->host_perio_tx_fifo_size = 96;
p->max_packet_count = 256;
p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
p->i2c_enable = false;
p->uframe_sched = false;
p->activate_stm_fs_transceiver = true;
} */
static void dwc2_set_param_otg_cap(struct dwc2_hsotg *hsotg)
{
u8 val;
switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE;
break;
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE;
break;
default:
val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
break;
}
hsotg->params.otg_cap = val;
}
static void dwc2_set_param_phy_type(struct dwc2_hsotg *hsotg)
{
int val;
u32 hs_phy_type = hsotg->hw_params.hs_phy_type;
val = DWC2_PHY_TYPE_PARAM_FS;
if (hs_phy_type != GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED) {
if (hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI ||
hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI)
val = DWC2_PHY_TYPE_PARAM_UTMI;
else
val = DWC2_PHY_TYPE_PARAM_ULPI;
}
if (dwc2_is_fs_iot(hsotg))
hsotg->params.phy_type = DWC2_PHY_TYPE_PARAM_FS;
hsotg->params.phy_type = val;
}
static void dwc2_set_param_speed(struct dwc2_hsotg *hsotg)
{
int val;
val = hsotg->params.phy_type == DWC2_PHY_TYPE_PARAM_FS ?
DWC2_SPEED_PARAM_FULL : DWC2_SPEED_PARAM_HIGH;
if (dwc2_is_fs_iot(hsotg))
val = DWC2_SPEED_PARAM_FULL;
if (dwc2_is_hs_iot(hsotg))
val = DWC2_SPEED_PARAM_HIGH;
hsotg->params.speed = val;
}
static void dwc2_set_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
{
int val;
val = (hsotg->hw_params.utmi_phy_data_width ==
GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16;
hsotg->params.phy_utmi_width = val;
}
static void dwc2_set_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
int depth_average;
int fifo_count;
int i;
fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
memset(p->g_tx_fifo_size, 0, sizeof(p->g_tx_fifo_size));
depth_average = dwc2_hsotg_tx_fifo_average_depth(hsotg);
for (i = 1; i <= fifo_count; i++)
p->g_tx_fifo_size[i] = depth_average;
}
/**
* dwc2_set_default_params() - Set all core parameters to their
* auto-detected default values.
*/
static void dwc2_set_default_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
struct dwc2_core_params *p = &hsotg->params;
#ifdef USB_DMA
bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
#endif
dwc2_set_param_otg_cap(hsotg);
dwc2_set_param_phy_type(hsotg);
dwc2_set_param_speed(hsotg);
dwc2_set_param_phy_utmi_width(hsotg);
p->phy_ulpi_ddr = false;
p->phy_ulpi_ext_vbus = false;
p->enable_dynamic_fifo = hw->enable_dynamic_fifo;
p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo;
p->i2c_enable = hw->i2c_enable;
p->ulpi_fs_ls = false;
p->ts_dline = false;
p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a);
p->uframe_sched = true;
p->external_id_pin_ctl = false;
p->hibernation = false;
p->max_packet_count = hw->max_packet_count;
p->max_transfer_size = hw->max_transfer_size;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT;
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
#ifdef USB_DMA
p->host_dma = dma_capable;
#else
p->host_dma = false;
#endif
p->dma_desc_enable = false;
p->dma_desc_fs_enable = false;
p->host_support_fs_ls_low_power = false;
p->host_ls_low_power_phy_clk = false;
p->host_channels = hw->host_channels;
p->host_rx_fifo_size = hw->rx_fifo_size;
p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size;
p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size;
}
if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
#ifdef USB_DMA
p->g_dma = dma_capable;
#else
p->g_dma = false;
#endif
p->g_dma_desc = hw->dma_desc_enable;
/*
* The values for g_rx_fifo_size (2048) and
* g_np_tx_fifo_size (1024) come from the legacy s3c
* gadget driver. These defaults have been hard-coded
* for some time so many platforms depend on these
* values. Leave them as defaults for now and only
* auto-detect if the hardware does not support the
* default.
*/
p->g_rx_fifo_size = 2048;
p->g_np_tx_fifo_size = 1024;
dwc2_set_param_tx_fifo_sizes(hsotg);
}
}
/**
* dwc2_get_device_properties() - Read in device properties.
*
* Read in the device properties and adjust core parameters if needed.
*/
static void dwc2_get_device_properties(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
p->g_rx_fifo_size = 1588;
p->g_np_tx_fifo_size = 384;
p->g_tx_fifo_size[0] = 1024;
p->g_tx_fifo_size[1] = 1024;
p->g_tx_fifo_size[2] = 1024;
p->g_tx_fifo_size[3] = 1024;
/*p->g_tx_fifo_size[4] = 1024;
p->g_tx_fifo_size[5] = 1024;
p->g_tx_fifo_size[6] = 1024;
p->g_tx_fifo_size[7] = 1024;*/
}
}
static void dwc2_check_param_otg_cap(struct dwc2_hsotg *hsotg)
{
int valid = 1;
switch (hsotg->params.otg_cap) {
case DWC2_CAP_PARAM_HNP_SRP_CAPABLE:
if (hsotg->hw_params.op_mode != GHWCFG2_OP_MODE_HNP_SRP_CAPABLE)
valid = 0;
break;
case DWC2_CAP_PARAM_SRP_ONLY_CAPABLE:
switch (hsotg->hw_params.op_mode) {
case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE:
case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE:
case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST:
break;
default:
valid = 0;
break;
}
break;
case DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE:
/* always valid */
break;
default:
valid = 0;
break;
}
if (!valid)
dwc2_set_param_otg_cap(hsotg);
}
static void dwc2_check_param_phy_type(struct dwc2_hsotg *hsotg)
{
int valid = 0;
u32 hs_phy_type;
u32 fs_phy_type;
hs_phy_type = hsotg->hw_params.hs_phy_type;
fs_phy_type = hsotg->hw_params.fs_phy_type;
switch (hsotg->params.phy_type) {
case DWC2_PHY_TYPE_PARAM_FS:
if (fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED)
valid = 1;
break;
case DWC2_PHY_TYPE_PARAM_UTMI:
if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
(hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
valid = 1;
break;
case DWC2_PHY_TYPE_PARAM_ULPI:
if ((hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI) ||
(hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI_ULPI))
valid = 1;
break;
default:
break;
}
if (!valid)
dwc2_set_param_phy_type(hsotg);
}
static void dwc2_check_param_speed(struct dwc2_hsotg *hsotg)
{
int valid = 1;
int phy_type = hsotg->params.phy_type;
int speed = hsotg->params.speed;
switch (speed) {
case DWC2_SPEED_PARAM_HIGH:
if ((hsotg->params.speed == DWC2_SPEED_PARAM_HIGH) &&
(phy_type == DWC2_PHY_TYPE_PARAM_FS))
valid = 0;
break;
case DWC2_SPEED_PARAM_FULL:
case DWC2_SPEED_PARAM_LOW:
break;
default:
valid = 0;
break;
}
if (!valid)
dwc2_set_param_speed(hsotg);
}
static void dwc2_check_param_phy_utmi_width(struct dwc2_hsotg *hsotg)
{
int valid = 0;
int param = hsotg->params.phy_utmi_width;
int width = hsotg->hw_params.utmi_phy_data_width;
switch (width) {
case GHWCFG4_UTMI_PHY_DATA_WIDTH_8:
valid = (param == 8);
break;
case GHWCFG4_UTMI_PHY_DATA_WIDTH_16:
valid = (param == 16);
break;
case GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16:
valid = (param == 8 || param == 16);
break;
}
if (!valid)
dwc2_set_param_phy_utmi_width(hsotg);
}
static void dwc2_check_param_tx_fifo_sizes(struct dwc2_hsotg *hsotg)
{
int fifo_count;
int fifo;
int min;
u32 total = 0;
u32 dptxfszn;
fifo_count = dwc2_hsotg_tx_fifo_count(hsotg);
min = hsotg->hw_params.en_multiple_tx_fifo ? 16 : 4;
for (fifo = 1; fifo <= fifo_count; fifo++)
total += hsotg->params.g_tx_fifo_size[fifo];
if (total > dwc2_hsotg_tx_fifo_total_depth(hsotg) || !total) {
dev_warn(hsotg->dev, "%s: Invalid parameter g-tx-fifo-size, setting to default average\n",
__func__);
dwc2_set_param_tx_fifo_sizes(hsotg);
}
for (fifo = 1; fifo <= fifo_count; fifo++) {
dptxfszn = (dwc2_readl(hsotg->regs + DPTXFSIZN(fifo)) &
FIFOSIZE_DEPTH_MASK) >> FIFOSIZE_DEPTH_SHIFT;
if (hsotg->params.g_tx_fifo_size[fifo] < min ||
hsotg->params.g_tx_fifo_size[fifo] > dptxfszn) {
dev_warn(hsotg->dev, "%s: Invalid parameter g_tx_fifo_size[%d]=%d\n",
__func__, fifo,
hsotg->params.g_tx_fifo_size[fifo]);
hsotg->params.g_tx_fifo_size[fifo] = dptxfszn;
}
}
}
#define CHECK_RANGE(_param, _min, _max, _def) do { \
if ((hsotg->params._param) < (_min) || \
(hsotg->params._param) > (_max)) { \
dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
__func__, #_param, hsotg->params._param); \
hsotg->params._param = (_def); \
} \
} while (0)
#define CHECK_BOOL(_param, _check) do { \
if (hsotg->params._param && !(_check)) { \
dev_warn(hsotg->dev, "%s: Invalid parameter %s=%d\n", \
__func__, #_param, hsotg->params._param); \
hsotg->params._param = false; \
} \
} while (0)
static void dwc2_check_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
struct dwc2_core_params *p = &hsotg->params;
bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH);
dwc2_check_param_otg_cap(hsotg);
dwc2_check_param_phy_type(hsotg);
dwc2_check_param_speed(hsotg);
dwc2_check_param_phy_utmi_width(hsotg);
CHECK_BOOL(enable_dynamic_fifo, hw->enable_dynamic_fifo);
CHECK_BOOL(en_multiple_tx_fifo, hw->en_multiple_tx_fifo);
CHECK_BOOL(i2c_enable, hw->i2c_enable);
CHECK_BOOL(reload_ctl, (hsotg->hw_params.snpsid > DWC2_CORE_REV_2_92a));
CHECK_RANGE(max_packet_count,
15, hw->max_packet_count,
hw->max_packet_count);
CHECK_RANGE(max_transfer_size,
2047, hw->max_transfer_size,
hw->max_transfer_size);
if ((hsotg->dr_mode == USB_DR_MODE_HOST) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
CHECK_BOOL(host_dma, dma_capable);
CHECK_BOOL(dma_desc_enable, p->host_dma);
CHECK_BOOL(dma_desc_fs_enable, p->dma_desc_enable);
CHECK_BOOL(host_ls_low_power_phy_clk,
p->phy_type == DWC2_PHY_TYPE_PARAM_FS);
CHECK_RANGE(host_channels,
1, hw->host_channels,
hw->host_channels);
CHECK_RANGE(host_rx_fifo_size,
16, hw->rx_fifo_size,
hw->rx_fifo_size);
CHECK_RANGE(host_nperio_tx_fifo_size,
16, hw->host_nperio_tx_fifo_size,
hw->host_nperio_tx_fifo_size);
CHECK_RANGE(host_perio_tx_fifo_size,
16, hw->host_perio_tx_fifo_size,
hw->host_perio_tx_fifo_size);
}
if ((hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) ||
(hsotg->dr_mode == USB_DR_MODE_OTG)) {
CHECK_BOOL(g_dma, dma_capable);
CHECK_BOOL(g_dma_desc, (p->g_dma && hw->dma_desc_enable));
CHECK_RANGE(g_rx_fifo_size,
16, hw->rx_fifo_size,
hw->rx_fifo_size);
CHECK_RANGE(g_np_tx_fifo_size,
16, hw->dev_nperio_tx_fifo_size,
hw->dev_nperio_tx_fifo_size);
dwc2_check_param_tx_fifo_sizes(hsotg);
}
}
/*
* Gets host hardware parameters. Forces host mode if not currently in
* host mode. Should be called immediately after a core soft reset in
* order to get the reset values.
*/
static void dwc2_get_host_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
u32 gnptxfsiz;
u32 hptxfsiz;
bool forced;
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
return;
forced = dwc2_force_mode_if_needed(hsotg, true);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ);
if (forced)
dwc2_clear_force_mode(hsotg);
hw->host_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
hw->host_perio_tx_fifo_size = (hptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
}
/*
* Gets device hardware parameters. Forces device mode if not
* currently in device mode. Should be called immediately after a core
* soft reset in order to get the reset values.
*/
static void dwc2_get_dev_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
bool forced;
u32 gnptxfsiz;
if (hsotg->dr_mode == USB_DR_MODE_HOST)
return;
forced = dwc2_force_mode_if_needed(hsotg, false);
gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ);
if (forced)
dwc2_clear_force_mode(hsotg);
hw->dev_nperio_tx_fifo_size = (gnptxfsiz & FIFOSIZE_DEPTH_MASK) >>
FIFOSIZE_DEPTH_SHIFT;
}
/**
* During device initialization, read various hardware configuration
* registers and interpret the contents.
*/
int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
unsigned int width;
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
u32 grxfsiz;
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the GSNPSID register contents. The value should be
* 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3",
* as in "OTG version 2.xx" or "OTG version 3.xx".
*/
hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID);
if ((hw->snpsid & 0xfffff000) != 0x4f542000 &&
(hw->snpsid & 0xfffff000) != 0x4f543000 &&
(hw->snpsid & 0xffff0000) != 0x55310000 &&
(hw->snpsid & 0xffff0000) != 0x55320000) {
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
hw->snpsid);
return -ENODEV;
}
dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1);
hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2);
hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3);
hwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4);
grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ);
/*
* Host specific hardware parameters. Reading these parameters
* requires the controller to be in host mode. The mode will
* be forced, if necessary, to read these values.
*/
dwc2_get_host_hwparams(hsotg);
dwc2_get_dev_hwparams(hsotg);
/* hwcfg1 */
hw->dev_ep_dirs = hwcfg1;
/* hwcfg2 */
hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >>
GHWCFG2_OP_MODE_SHIFT;
hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >>
GHWCFG2_ARCHITECTURE_SHIFT;
hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO);
hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >>
GHWCFG2_NUM_HOST_CHAN_SHIFT);
hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >>
GHWCFG2_HS_PHY_TYPE_SHIFT;
hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >>
GHWCFG2_FS_PHY_TYPE_SHIFT;
hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >>
GHWCFG2_NUM_DEV_EP_SHIFT;
hw->nperio_tx_q_depth =
(hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >>
GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1;
hw->host_perio_tx_q_depth =
(hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >>
GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1;
hw->dev_token_q_depth =
(hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >>
GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT;
/* hwcfg3 */
width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT;
hw->max_transfer_size = (1 << (width + 11)) - 1;
width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >>
GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT;
hw->max_packet_count = (1 << (width + 4)) - 1;
hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C);
hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >>
GHWCFG3_DFIFO_DEPTH_SHIFT;
/* hwcfg4 */
hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN);
hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >>
GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT;
hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA);
hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ);
hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >>
GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT;
/* fifo sizes */
hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >>
GRXFSIZ_DEPTH_SHIFT;
return 0;
}
int dwc2_init_params(struct dwc2_hsotg *hsotg)
{
dwc2_set_default_params(hsotg);
dwc2_get_device_properties(hsotg);
dwc2_check_params(hsotg);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,94 @@
#include "usb_os_adapter.h"
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
* @buf: Buffer to be filled
* @buflen: Size of buf
* @src: Array of descriptor pointers, terminated by null pointer.
*
* Copies descriptors into the buffer, returning the length or a
* negative error code if they can't all be copied. Useful when
* assembling descriptors for an associated set of interfaces used
* as part of configuring a composite device; or in other cases where
* sets of descriptors need to be marshaled.
*/
int
usb_descriptor_fillbuf(void *buf, unsigned buflen,
const struct usb_descriptor_header **src)
{
u8 *dest = buf;
if (!src)
return -EINVAL;
/* fill buffer from src[] until null descriptor ptr */
for (; NULL != *src; src++) {
unsigned len = (*src)->bLength;
if (len > buflen)
return -EINVAL;
memcpy(dest, *src, len);
buflen -= len;
dest += len;
}
return dest - (u8 *)buf;
}
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
/* config need not be aligned */
memcpy(cp, config, sizeof(*cp));
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
put_unaligned_le16(len, &cp->wTotalLength);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}

View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
*
* Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and
* Remy Bohmer <linux@bohmer.net>
*/
#include "usb_os_adapter.h"
#include <linux/usb/ch9.h>
#include <linux/errno.h>
#include <linux/usb/gadget.h>
#include <stdlib.h>
#define isdigit(c) ('0' <= (c) && (c) <= '9')
/* we must assign addresses for configurable endpoints (like net2280) */
static unsigned epnum;
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
*
* - ep1, ep2, ... address is fixed, not direction or type
* - ep1in, ep2out, ... address and direction are fixed, not type
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
* - ep1in-bulk, ep2out-iso, ... all three are fixed
* - ep-* ... no functionality restrictions
*
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
* Less common restrictions are implied by gadget_is_*().
*
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int ep_matches(
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc
)
{
u8 type;
const char *tmp;
u16 max;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr(ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) /* == "-iso" */
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) /* != "-bulk" */
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) /* != "-iso" */
return 0;
}
} else {
tmp = ep->name + strlen(ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit(*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high speed */
if (!gadget->is_dualspeed && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high speed */
if (ep->maxpacket < max)
return 0;
if (!gadget->is_dualspeed && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((get_unaligned(&desc->wMaxPacketSize) &
cpu_to_le16(3<<11))) {
if (!gadget->is_dualspeed)
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
if (isdigit(ep->name[2])) {
u8 num = strtol(&ep->name[2], NULL, 10);
desc->bEndpointAddress |= num;
} else {
if (++epnum > 15)
return 0;
desc->bEndpointAddress |= epnum;
}
/* report (variable) full speed bulk maxpacket */
if (USB_ENDPOINT_XFER_BULK == type) {
int size = ep->maxpacket;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
//put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
desc->wMaxPacketSize = (uint16_t)size;
}
return 1;
}
#if 0
static struct usb_ep *
find_ep(struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
ListItem_t *pxListItem = NULL;
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
if (0 == strcmp(ep->name, name))
return ep;
}
return NULL;
}
#endif
/**
* usb_ep_autoconfig - choose an endpoint matching the descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on success.
*
* By choosing an endpoint to use with the specified descriptor, this
* routine simplifies writing gadget drivers that work with multiple
* USB device controllers. The endpoint would be passed later to
* usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration on your
* hardware. This code may not make the best choices about how to use the
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent
* the endpoint from being returned by a later autoconfig call, claim it
* by assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
struct usb_ep *ep = NULL;
ListItem_t *pxListItem = NULL;
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
if (ep_matches(gadget, ep, desc))
return ep;
}
/* Fail */
return NULL;
}
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
{
struct usb_ep *ep;
ListItem_t *pxListItem = NULL;
list_for_each_entry(pxListItem, ep, &gadget->ep_list) {
ep->driver_data = NULL;
}
epnum = 0;
}

View File

@ -0,0 +1,971 @@
#include "usb_os_adapter.h"
#include "board.h"
#include <linux/usb/cdc.h>
#include "linux/usb/composite.h"
#include "linux/usb/ether.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "trace.h"
#include "list.h"
#include "task.h"
#include "ark_dwc2.h"
#if !USE_LWIP
#include "FreeRTOS_IP.h"
#include "FreeRTOS_IP_Private.h"
#include "NetworkBufferManagement.h"
#include "NetworkInterface.h"
#else
#include "queue.h"
#include "lwip/pbuf.h"
#include "ethernet.h"
struct netif *ncm_netif = NULL;
void ncm_net_set_intf(void* intf)
{
ncm_netif = (struct netif *)intf;
}
extern void ncm_ethernetif_input(void *h, struct pbuf* p);
#endif
#define UETH__VERSION "29-May-2008"
#define WORK_RX_MEMORY 0
struct eth_dev {
spinlock_t lock;
struct gether *port_usb;
struct usb_gadget *gadget;
spinlock_t req_lock;
List_t tx_reqs;
atomic_t tx_qlen;
#if !USE_LWIP
List_t rx_frames;
#else
struct pbuf rx_frames;
#endif
struct usb_request *out_req;
unsigned header_len;
bool zlp;
u8 host_mac[ETH_ALEN];
TaskHandle_t usb_ether_task;
QueueHandle_t usb_ether_event_queue;
int start;
int tx_err_count;
};
struct eth_event
{
int type;
void* priv;
};
#define USB_ETH_EVENT_USB_CONNECT 0
#define USB_ETH_EVENT_USB_DISCONNECT 1
#define USB_ETH_EVENT_USB_DATA_RX 2 // usb recv data
#define USB_ETH_EVENT_USB_DATA_TX 3 // usb data tx ok notify
#define USB_ETH_EVENT_NET_DATA_TX 4 // data from tcpip
/*-------------------------------------------------------------------------*/
#undef atomic_read
#define atomic_read(v) ((v)->counter)
#undef atomic_set
#define atomic_set(v,i) ((v)->counter = (i))
#undef atomic_inc
#define atomic_inc(v) ((v)->counter++)
#define RX_EXTRA 20 /* bytes guarding against rx overflows */
#define DEFAULT_QLEN 4/* double buffering by default */
static unsigned qmult = 2;
static struct eth_dev *the_dev;
/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
gadget->speed == USB_SPEED_SUPER))
return qmult * DEFAULT_QLEN;
else
return DEFAULT_QLEN * 8;
}
/*-------------------------------------------------------------------------*/
#undef DBG
#undef VDBG
#undef ERROR
#undef INFO
#define xprintk(d, level, fmt, args...) \
printk(level "%s: " fmt , (d)->net->name , ## args)
#ifdef DEBUG
#undef DEBUG
#define DBG(dev, fmt, args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE_DEBUG
#define VDBG DBG
#else
#define VDBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#define ERROR(dev, fmt, args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define INFO(dev, fmt, args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
static void ether_disconnect(struct gether *link);
static int ether_connect(struct eth_dev *dev, struct gether *link);
static int rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags);
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
int ncm_get_mac_address(char mac[6])
{
if (the_dev)
memcpy(mac, the_dev->host_mac, 6);
return 0;
}
static int prealloc(List_t *list, struct usb_ep *ep, unsigned n)
{
unsigned i;
struct usb_request *req;
ListItem_t *pxListItem = NULL;
if (!n)
return -ENOMEM;
i = n;
list_for_each_entry(pxListItem, req, list) {
if (i-- == 0) {
printf("prealloc out!\r\n");
goto extra;
}
}
while (i--) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (!req)
return list_empty(list) ? -ENOMEM : 0;
vListInitialiseItem(&req->list);
listSET_LIST_ITEM_OWNER(&(req->list), req);
printf("req:%x is alloc\n", req);
req->buf = NULL;
req->length = 0;
list_add_tail(&req->list, list);
}
return 0;
extra:
/* free extras */
for (;;) {
ListItem_t *next;
next = listGET_NEXT(&req->list);
uxListRemove(&req->list);
usb_ep_free_request(ep, req);
if (next == listGET_END_MARKER(list))
break;
req = listGET_LIST_ITEM_OWNER(&req->list);
}
return 0;
}
static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
{
int status;
spin_lock(&dev->req_lock);
status = prealloc(&dev->tx_reqs, link->in_ep, n);
if (status < 0)
goto fail;
/*status = prealloc(&dev->rx_reqs, link->out_ep, n);
if (status < 0)
goto fail;*/
goto done;
fail:
DBG(dev, "can't alloc requests\n");
done:
spin_unlock(&dev->req_lock);
return status;
}
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
//struct usb_request *req;
//unsigned long flags;
/* fill unused rxq slots with some skb */
/*spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) {
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->rx_reqs));
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
rx_submit(dev, req, 0);
spin_lock_irqsave(&dev->req_lock, flags);
}
spin_unlock_irqrestore(&dev->req_lock, flags);*/
rx_submit(dev, dev->out_req, 0);
}
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct eth_dev *dev = ep->driver_data;
struct eth_event ev;
ev.type = USB_ETH_EVENT_USB_DATA_TX;
ev.priv = (void*)req;
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct eth_event ev;
struct eth_dev *dev = ep->driver_data;
if (req)
//printf("%s:%d req->actual:%d\r\n", __func__, __LINE__, req->actual);
ev.type = USB_ETH_EVENT_USB_DATA_RX;
ev.priv = (void*)req;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
static inline int is_promisc(u16 cdc_filter)
{
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
}
static int rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
{
int retval = -ENOMEM;
size_t size = 0;
struct usb_ep *out;
unsigned long flags;
void *buf = NULL;
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb)
out = dev->port_usb->out_ep;
else
out = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
if (!out)
return -ENOTCONN;
size += ETH_ALEN + ipconfigNETWORK_MTU + RX_EXTRA;
size += dev->port_usb->header_len;
if (out->maxpacket <= 0) {
printf("maxpacket err\r\n");
}
size += out->maxpacket - 1;
size -= size % out->maxpacket;
if (dev->port_usb->is_fixed)
size = max_t(size_t, size, dev->port_usb->fixed_out_len);
buf = req->buf;
if (NULL == req->buf || req->length < size) {
if (NULL != req->buf)
vPortFree(req->buf);
buf = pvPortMalloc(size);
if (buf == NULL) {
return -ENOMEM;
}
}
//memset(req->buf, 0, req->length);
req->buf = buf;
req->length = size;
req->complete = rx_complete;
req->context = NULL;
retval = usb_ep_queue(out, req, 0);
if (retval) {
printf("rx submit failed--> %d\r\n", retval);
/*spin_lock_irqsave(&dev->req_lock, flags);
list_add_tail(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);*/
}
return retval;
}
int usb_data_rx_proc(struct eth_dev* dev, struct usb_request *req)
{
int ret = -1;
if (!dev->port_usb->connected) {
printf("%s:%d usb is disconnected\r\n", __func__, __LINE__);
return -1;
}
if (req->status != 0) {
usb_ep_queue(dev->port_usb->out_ep, req, 0);
return -1;
}
if (dev->port_usb && dev->port_usb->unwrap) {
#if !USE_LWIP
dev->port_usb->unwrap(dev->port_usb, req->buf, req->actual, &dev->rx_frames);
#else
dev->port_usb->unwrap(dev->port_usb, req->buf, req->actual, (List_t *)&dev->rx_frames);
#endif
}
if (dev->port_usb->connected) {
#if !USE_LWIP
NetworkBufferDescriptor_t* pxBufferDescriptor = NULL;
IPStackEvent_t xRxEvent;
ListItem_t *pxListItem, *pxListItem1;
list_for_each_entry_safe(pxListItem, pxListItem1, pxBufferDescriptor, &dev->rx_frames) {
list_del_init(&pxBufferDescriptor->xBufferListItem);
xRxEvent.eEventType = eNetworkRxEvent;
xRxEvent.pvData = ( void * ) pxBufferDescriptor;
if (0) {
int i, dump_len = pxBufferDescriptor->xDataLength;
printf("recv len:%d\r\n", dump_len);
for (i = 0; i < dump_len; i++) {
printf("%02x ", pxBufferDescriptor->pucEthernetBuffer[i]);
if ((i + 1) % 16 == 0)
printf("\r\n");
}printf("\r\n");
}
if( xSendEventStructToIPTask( &xRxEvent, 0 ) == pdFALSE ) {
vReleaseNetworkBufferAndDescriptor( pxBufferDescriptor );
iptraceETHERNET_RX_EVENT_LOST();
} else {
iptraceNETWORK_INTERFACE_RECEIVE();
}
}
#else
if (dev->rx_frames.next) {
struct pbuf *header = dev->rx_frames.next;
while(header != NULL) {
struct pbuf *current = header;
header = header->next;
ncm_ethernetif_input(ncm_netif, current);
}
dev->rx_frames.next = NULL;
}
#endif
}
if (req) {
ret = rx_submit(dev, req, 0);
if (ret != 0) {
rx_fill(dev, 0);
}
}
return 0;
}
void usb_data_tx_proc(struct eth_dev* dev, struct usb_request *req)
{
if (1) {
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t *)req->context;
if (pxBufferDescriptor) {
if (pxBufferDescriptor->pucEthernetBuffer) {
vPortFree(pxBufferDescriptor->pucEthernetBuffer);
pxBufferDescriptor->pucEthernetBuffer = NULL;
}
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
}
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)req->context;//the buf is alloc by ncm, it must be freed.
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
}
spin_lock(&dev->req_lock);
if (!dev->port_usb->connected) {
req->buf = NULL;
req->length = 0;
printf("%s #############.\n", __func__);
}
list_add_tail(&req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
}
static void free_net_buffer(void* desc_handle)
{
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t*)desc_handle;
if (NULL != pxBufferDescriptor)
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)desc_handle;
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
}
int net_data_tx_proc(struct eth_dev* dev, void *pxBufferDescriptorHandle)
{
int length = 0;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
struct usb_ep *in;
u16 cdc_filter;
#if !USE_LWIP
NetworkBufferDescriptor_t *pxBufferDescriptor = (NetworkBufferDescriptor_t*)pxBufferDescriptorHandle;
#else
struct pbuf* pxBufferDescriptor = (struct pbuf*)pxBufferDescriptorHandle;
#endif
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
in = dev->port_usb->in_ep;
cdc_filter = dev->port_usb->cdc_filter;
} else {
in = NULL;
cdc_filter = 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
if (!in || !dev->port_usb->connected) {
free_net_buffer((void*)pxBufferDescriptor);
return 0;
}
/* apply outgoing CDC or RNDIS filters */
if (!is_promisc(cdc_filter)) {
#if !USE_LWIP
u8 *dest = pxBufferDescriptor->pucEthernetBuffer;
#else
u8 *dest = pxBufferDescriptor->payload;
#endif
if (is_multicast_ether_addr(dest)) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
if (is_broadcast_ether_addr(dest))
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
if (!(cdc_filter & type)) {
free_net_buffer((void*)pxBufferDescriptor);
return 0;
}
}
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
spin_lock_irqsave(&dev->req_lock, flags);
/*
* this freelist can be empty if an interrupt triggered disconnect()
* and reconfigured the gadget (shutting down this queue) after the
* network stack decided to xmit but before we got the spinlock.
*/
if (list_empty(&dev->tx_reqs)) {
spin_unlock_irqrestore(&dev->req_lock, flags);
printf("tx reqs empty\r\n");
free_net_buffer((void*)pxBufferDescriptor);
if (dev->tx_err_count++ > 3) {
usb_dwc2_reset(0, 1);
dev->tx_err_count = 0;
}
return -1;
}
dev->tx_err_count = 0;
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->tx_reqs));
list_del(&req->list);
/* temporarily stop TX queue when the freelist empties */
if (list_empty(&dev->tx_reqs)) {
}
spin_unlock_irqrestore(&dev->req_lock, flags);
/* no buffer copies needed, unless the network stack did it
* or the hardware can't use skb buffers.
* or there's not enough space for extra headers we need
*/
#if !USE_LWIP
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb && dev->port_usb->wrap)
pxBufferDescriptor = dev->port_usb->wrap(dev->port_usb, pxBufferDescriptor);
spin_unlock_irqrestore(&dev->lock, flags);
if (!pxBufferDescriptor)
goto drop;
length = pxBufferDescriptor->xDataLength;
req->buf = pxBufferDescriptor->pucEthernetBuffer;
req->context = pxBufferDescriptor;
req->complete = tx_complete;
#else
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb && dev->port_usb->wrap_ext)
pxBufferDescriptor = (struct pbuf*)dev->port_usb->wrap_ext(dev->port_usb, (void*)pxBufferDescriptor);
spin_unlock_irqrestore(&dev->lock, flags);
if (!pxBufferDescriptor)
goto drop;
length = pxBufferDescriptor->len;
req->buf = pxBufferDescriptor->payload;
req->context = pxBufferDescriptor;
req->complete = tx_complete;
#endif
/* NCM requires no zlp if transfer is dwNtbInMaxSize */
if (dev->port_usb->is_fixed &&
length == dev->port_usb->fixed_in_len &&
(length % in->maxpacket) == 0)
req->zero = 0;
else
req->zero = 1;
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
* and some hardware doesn't like to write zlps.
*/
if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
length++;
req->length = length;
retval = usb_ep_queue(in, req, GFP_ATOMIC);
switch (retval) {
default:
DBG(dev, "tx queue err %d\n", retval);
break;
case 0:
atomic_inc(&dev->tx_qlen);
}
if (retval) {
#if !USE_LWIP
pxBufferDescriptor = (NetworkBufferDescriptor_t *)req->context;
if (pxBufferDescriptor) {
if (pxBufferDescriptor->pucEthernetBuffer) {
vPortFree(pxBufferDescriptor->pucEthernetBuffer);
pxBufferDescriptor->pucEthernetBuffer = NULL;
}
vReleaseNetworkBufferAndDescriptor(pxBufferDescriptor);
}
#else
pxBufferDescriptor = (struct pbuf*)req->context;
if (pxBufferDescriptor) {
pbuf_free(pxBufferDescriptor);
}
#endif
drop:
spin_lock_irqsave(&dev->req_lock, flags);
list_add_tail(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
return 0;
}
static void usb_ether_task_proc(void* arg)
{
struct eth_event ev;
struct eth_dev *dev = (struct eth_dev *)arg;
while(dev->start) {
ev.type = -1;
if (xQueueReceive(dev->usb_ether_event_queue, &ev, portMAX_DELAY) != pdPASS) {
printf("%s xQueueReceive err!\r\n", __func__);
continue;
}
if (ev.type == -1)
continue;
switch(ev.type) {
case USB_ETH_EVENT_USB_DATA_RX:
if (NULL != ev.priv)
usb_data_rx_proc(dev, (struct usb_request *)ev.priv);
break;
case USB_ETH_EVENT_USB_DATA_TX:
if (NULL != ev.priv)
usb_data_tx_proc(dev, (struct usb_request *)ev.priv);
break;
case USB_ETH_EVENT_NET_DATA_TX:
if (NULL != ev.priv)
net_data_tx_proc(dev, ev.priv);
break;
case USB_ETH_EVENT_USB_DISCONNECT:
if (NULL != ev.priv)
ether_disconnect((struct gether *)ev.priv);
break;
case USB_ETH_EVENT_USB_CONNECT:
if (NULL != ev.priv)
ether_connect(dev, (struct gether *)ev.priv);
break;
default:
break;
}
}
vTaskDelete(NULL);
}
/**
* gether_setup - initialize one ethernet-over-usb link
* @g: gadget to associated with these links
* @ethaddr: NULL, or a buffer in which the ethernet address of the
* host side of the link is recorded
* Context: may sleep
*
* This sets up the single network link that may be exported by a
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
* Returns negative errno, or zero on success
*/
#if 0
UBaseType_t uxRand( void );
static void random_ether_addr(u8 *addr)
{
UBaseType_t val = uxRand();
if (NULL == addr)
return;
addr [0] = ((val >> 0) & 0xff);
addr [1] = ((val >> 8) & 0xff);
addr [2] = ((val >> 16) & 0xff);
addr [3] = ((val >> 24) & 0xff);
val = uxRand();
addr [4] = ((val >> 0) & 0xff);
addr [5] = ((val >> 8) & 0xff);
addr [0] &= 0xfe; /* clear multicast bit */
addr [0] |= 0x02; /* set local assignment bit (IEEE802) */
}
#endif
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
{
struct eth_dev *dev;
BaseType_t ret = pdFAIL;
if (the_dev)
return -EBUSY;
dev = pvPortMalloc(sizeof(struct eth_dev));
if (dev == NULL)
return -ENOMEM;
memset((void*)dev, 0, sizeof(struct eth_dev));
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_LIST_HEAD(&dev->tx_reqs);
//INIT_LIST_HEAD(&dev->rx_reqs);
#if !USE_LWIP
INIT_LIST_HEAD(&dev->rx_frames);
#else
dev->rx_frames.next = NULL;
dev->rx_frames.len = 0;
dev->rx_frames.tot_len = 0;
#endif
#if 1
dev->host_mac[0] = 0x00;//00:0c:29:53:02:09
dev->host_mac[1] = 0x0c;
dev->host_mac[2] = 0x29;
dev->host_mac[3] = 0x53;
dev->host_mac[4] = 0x02;
dev->host_mac[5] = 0x09;
#else
random_ether_addr(dev->host_mac);
#endif
//FreeRTOS_UpdateMACAddress(dev->host_mac);
printf("\r\nncm mac: %02x %02x %02x %02x %02x %02x\r\n", dev->host_mac[0], dev->host_mac[1],
dev->host_mac[2], dev->host_mac[3], dev->host_mac[4], dev->host_mac[5]);
if (ethaddr)
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
dev->usb_ether_event_queue = xQueueCreate(16, sizeof(struct eth_event));
if (NULL == dev->usb_ether_event_queue) {
goto exit;
}
dev->start = 1;
ret = xTaskCreate(usb_ether_task_proc, "usb_ether_task", 2048, (void*)dev, configMAX_PRIORITIES - 3, &dev->usb_ether_task);
if (ret != pdPASS) {
goto exit;
}
dev->gadget = g;
the_dev = dev;
return 0;
exit:
if (ret != pdPASS) {
if (dev) {
if (dev->usb_ether_event_queue) {
vQueueDelete(dev->usb_ether_event_queue);
}
vPortFree(dev);
}
}
return -1;
}
/**
* gether_cleanup - remove Ethernet-over-USB device
* Context: may sleep
*
* This is called to free all resources allocated by @gether_setup().
*/
void gether_cleanup(void)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (!dev)
return;
dev->start = 0;
if (dev->usb_ether_event_queue) {
ev.type = -1;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
vQueueDelete(dev->usb_ether_event_queue);
}//maybe exist bug
vPortFree(dev);
the_dev = NULL;
}
static int ether_connect(struct eth_dev *dev, struct gether *link)
{
int result = 0;
if (!dev)
return -EINVAL;
dev->tx_err_count = 0;
if (link && link->connected)
return 0;
link->in_ep->driver_data = dev;
result = usb_ep_enable(link->in_ep, link->in);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->in_ep->name, result);
goto fail0;
}
link->out_ep->driver_data = dev;
result = usb_ep_enable(link->out_ep, link->out);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->out_ep->name, result);
goto fail1;
}
if (result == 0) {
result = alloc_requests(dev, link, qlen(dev->gadget));
dev->out_req = NULL;
dev->out_req = usb_ep_alloc_request(link->out_ep, GFP_ATOMIC);
if (!dev->out_req) {
printf("usb_ep_alloc_request %s --> %d\r\n",
link->out_ep->name, result);
goto fail1;
}
dev->out_req->buf = NULL;
dev->out_req->length = 0;
}
if (result == 0 && dev->out_req) {
dev->zlp = link->is_zlp_ok;
DBG(dev, "qlen %d\n", qlen(dev->gadget));
dev->header_len = link->header_len;
spin_lock(&dev->lock);
dev->port_usb = link;
link->ctx = (void *)dev;
spin_unlock(&dev->lock);
rx_fill(dev, 0);
atomic_set(&dev->tx_qlen, 0);
link->connected = true;
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);
fail1:
(void) usb_ep_disable(link->in_ep);
}
fail0:
/* caller is responsible for cleanup on error */
if (result < 0)
return result;
return result;
}
/**
* gether_disconnect - notify network layer that USB link is inactive
* @link: the USB link, on which gether_connect() was called
* Context: irqs blocked
*
* This is called to deactivate endpoints and let the network layer know
* the connection went inactive ("no carrier").
*
* On return, the state is as if gether_connect() had never been called.
* The endpoints are inactive, and accordingly without active USB I/O.
* Pointers to endpoint descriptors and endpoint private data are nulled.
*/
static void ether_disconnect(struct gether *link)
{
struct eth_dev *dev = (struct eth_dev *)link->ctx;
struct usb_request *req;
if (!dev || !link->connected)
return;
dev->tx_err_count = 0;
link->connected = false;
printf("%s:%d start\r\n", __func__, __LINE__);
usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->tx_reqs)) {
req = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->tx_reqs));
list_del(&req->list);
spin_unlock(&dev->req_lock);
req->buf = NULL;
req->length = 0;
usb_ep_free_request(link->in_ep, req);
printf("req:%x is released at disconnect\n", req);
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
link->in_ep->driver_data = NULL;
link->in_ep->desc = NULL;
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
#if !USE_LWIP
while (!list_empty(&dev->rx_frames)) {
NetworkBufferDescriptor_t * pxNetworkBuffer = listGET_LIST_ITEM_OWNER(listGET_HEAD_ENTRY(&dev->rx_frames));
list_del_init(&pxNetworkBuffer->xBufferListItem);
vReleaseNetworkBufferAndDescriptor(pxNetworkBuffer);
}
#else
if (dev->rx_frames.next) {
struct pbuf *header = dev->rx_frames.next;
while(header != NULL) {
struct pbuf *current = header;
header = header->next;
pbuf_free(current);
}
dev->rx_frames.next = NULL;
}
#endif
req = dev->out_req;
if (req && req->buf) {
vPortFree(req->buf);
req->buf = NULL;
req->length = 0;
}
usb_ep_free_request(link->out_ep, dev->out_req);
spin_unlock(&dev->req_lock);
link->out_ep->driver_data = NULL;
link->out_ep->desc = NULL;
/* finish forgetting about this USB link episode */
dev->header_len = 0;
spin_lock(&dev->lock);
dev->port_usb = NULL;
spin_unlock(&dev->lock);
if (link && link->disconnect_cb)
link->disconnect_cb(link);
printf("%s:%d end\r\n", __func__, __LINE__);
}
int gether_connect(struct gether *link)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_USB_CONNECT;
ev.priv = (void*)link;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
return 0;
}
void gether_disconnect(struct gether *link)
{
struct eth_event ev;
struct eth_dev *dev = (struct eth_dev *)link->ctx;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_USB_DISCONNECT;
ev.priv = (void*)link;
if (!xPortIsInInterrupt())
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
else
xQueueSendFromISR(dev->usb_ether_event_queue, &ev, 0);
}
}
void gether_send(NetworkBufferDescriptor_t * const pxDescriptor)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_NET_DATA_TX;
ev.priv = (void*)pxDescriptor;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
} else {
#if !USE_LWIP
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
#endif
}
}
void gether_send_ext(void * const pxDescriptor)
{
struct eth_event ev;
struct eth_dev *dev = the_dev;
if (dev && dev->usb_ether_event_queue) {
ev.type = USB_ETH_EVENT_NET_DATA_TX;
ev.priv = (void*)pxDescriptor;
xQueueSend(dev->usb_ether_event_queue, &ev, 0);
} else {
#if !USE_LWIP
vReleaseNetworkBufferAndDescriptor( pxDescriptor );
#endif
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
/*
* ncm
*
*/
#include "usb_os_adapter.h"
#include <stdio.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
#include <linux/usb/ether.h>
#define STRING_MANUFACTURER 25
#define STRING_PRODUCT 2
#define STRING_USBDOWN 2
#define STRING_SERIAL 3
#define MAX_STRING_SERIAL 256
#define CONFIGURATION_NUMBER 1
#undef DEBUG
#define DRIVER_VERSION "usb_ncm"
#define CONFIG_USB_GADGET_MANUFACTURER "arkmicro"
#if (DEBUG)
#define debug(s, ...) printf("%s: " s "\n", "UsbNcm", ## __VA_ARGS__)
#else
#define debug(s, ...)
#endif
#define CONFIG_USB_GADGET_VENDOR_NUM 0x5001
#define CONFIG_USB_GADGET_PRODUCT_NUM 0x2003
static const char product[] = "USB ncm gadget";
static char g_ncm_serial[MAX_STRING_SERIAL];
static const char manufacturer[] = CONFIG_USB_GADGET_MANUFACTURER;
static uint8_t hostaddr[ETH_ALEN];
void g_ncm_set_serialnumber(char *s)
{
memset(g_ncm_serial, 0, MAX_STRING_SERIAL);
strncpy(g_ncm_serial, s, MAX_STRING_SERIAL - 1);
}
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0, /*0x02:CDC-modem , 0x00:CDC-serial*/
.bDeviceProtocol = 0x00,
.idVendor = cpu_to_le16(CONFIG_USB_GADGET_VENDOR_NUM),
.idProduct = cpu_to_le16(CONFIG_USB_GADGET_PRODUCT_NUM),
/* .iProduct = DYNAMIC */
/* .iSerialNumber = DYNAMIC */
.bNumConfigurations = 1,
};
/*
* static strings, in UTF-8
* IDs for those strings are assigned dynamically at g_ncm_bind()
*/
static struct usb_string g_ncm_string_defs[] = {
{.s = manufacturer},
{.s = product},
{.s = g_ncm_serial},
};
static struct usb_gadget_strings g_ncm_string_tab = {
.language = 0x0409, /* en-us */
.strings = g_ncm_string_defs,
};
static struct usb_gadget_strings *g_ncm_composite_strings[] = {
&g_ncm_string_tab,
NULL,
};
static int usb_gadget_controller_number(struct usb_gadget *gadget)
{
(void)gadget;
return 0x02;
}
static int g_ncm_unbind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
usb_gadget_disconnect(gadget);
return 0;
}
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
static int g_ncm_do_config(struct usb_configuration *c)
{
return ncm_bind_config(c, hostaddr);
}
static int g_ncm_config_register(struct usb_composite_dev *cdev)
{
struct usb_configuration *config;
const char *name = "usb_ncm";
config = kmalloc(sizeof(*config), 0);
if (!config)
return -ENOMEM;
memset(config, 0, sizeof(*config));
config->label = name;
config->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER;
config->bConfigurationValue = CONFIGURATION_NUMBER;
config->iConfiguration = STRING_USBDOWN;
config->bind = g_ncm_do_config;
listSET_LIST_ITEM_OWNER(&config->list, config);
return usb_add_config(cdev, config);
}
static int g_ncm_get_bcd_device_number(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int gcnum;
gcnum = usb_gadget_controller_number(gadget);
if (gcnum > 0)
gcnum += 0x200;
return gcnum;
}
static int g_ncm_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int id, ret;
int gcnum;
ret = gether_setup(cdev->gadget, hostaddr);
debug("%s: gadget: 0x%p cdev: 0x%p\n", __func__, gadget, cdev);
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[0].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[1].id = id;
device_desc.iProduct = id;
if (strlen(g_ncm_serial)) {
id = usb_string_id(cdev);
if (id < 0)
return id;
g_ncm_string_defs[2].id = id;
device_desc.iSerialNumber = id;
}
ret = g_ncm_config_register(cdev);
if (ret)
goto error;
gcnum = g_ncm_get_bcd_device_number(cdev);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(gcnum);
else {
debug("%s: controller '%s' not recognized\n",
__func__, gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x9999);
}
debug("%s: calling usb_gadget_connect for "
"controller '%s'\n", __func__, gadget->name);
usb_gadget_connect(gadget);
return 0;
error:
g_ncm_unbind(cdev);
return -ENOMEM;
}
static struct usb_composite_driver g_ncm_driver = {
.name = NULL,
.dev = &device_desc,
.strings = g_ncm_composite_strings,
.bind = g_ncm_bind,
.unbind = g_ncm_unbind,
};
int g_ncm_register(const char *name)
{
int ret;
debug("%s: g_ncm_driver.name = %s\n", __func__, name);
g_ncm_driver.name = name;
ret = usb_composite_register(&g_ncm_driver);
if (ret) {
printf("%s: failed!, error: %d\n", __func__, ret);
return ret;
}
return 0;
}
void g_ncm_unregister(void)
{
usb_composite_unregister(&g_ncm_driver);
}

View File

@ -0,0 +1,333 @@
#include "usb_os_adapter.h"
#include "FreeRTOS_POSIX.h"
#include "pthread.h"
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "chip.h"
#include "dma-direction.h"
#include "dma-mapping.h"
/**
* struct usb_udc - describes one usb device controller
* @driver - the gadget driver pointer. For use by the class code
* @dev - the child device to the actual controller
* @gadget - the gadget. For use by the class code
* @list - for use by the udc class driver
*
* This represents the internal data structure which is used by the UDC-class
* to hold information about udc driver and gadget together.
*/
struct usb_udc {
struct usb_gadget_driver *driver;
struct usb_gadget *gadget;
struct device dev;
ListItem_t list;
};
static List_t udc_list;
static SemaphoreHandle_t udc_lock;
static int g_udc_core_init_flag = 0;
int usb_udc_core_init()
{
if (g_udc_core_init_flag)
return 0;
g_udc_core_init_flag = 1;
udc_lock = xSemaphoreCreateMutex();
vListInitialise(&udc_list);
return 0;
}
int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
req->dma = dma_map_single(req->buf, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
return 0;
}
void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return;
dma_unmap_single((void *)req->dma, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_giveback_request - give the request back to the gadget layer
* Context: in_interrupt()
*
* This is called by device controller drivers in order to return the
* completed request back to the gadget layer.
*/
void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req)
{
req->complete(ep, req);
}
/* ------------------------------------------------------------------------- */
void usb_gadget_set_state(struct usb_gadget *gadget,
enum usb_device_state state)
{
gadget->state = state;
}
/* ------------------------------------------------------------------------- */
/**
* usb_gadget_udc_reset - notifies the udc core that bus reset occurs
* @gadget: The gadget which bus reset occurs
* @driver: The gadget driver we want to notify
*
* If the udc driver has bus reset handler, it needs to call this when the bus
* reset occurs, it notifies the gadget driver that the bus reset occurs as
* well as updates gadget state.
*/
void usb_gadget_udc_reset(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
driver->reset(gadget);
usb_gadget_set_state(gadget, USB_STATE_DEFAULT);
}
/**
* usb_gadget_udc_start - tells usb device controller to start up
* @udc: The UDC to be started
*
* This call is issued by the UDC Class driver when it's about
* to register a gadget driver to the device controller, before
* calling gadget driver's bind() method.
*
* It allows the controller to be powered off until strictly
* necessary to have it powered on.
*
* Returns zero on success, else negative errno.
*/
static inline int usb_gadget_udc_start(struct usb_udc *udc)
{
return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}
/**
* usb_gadget_udc_stop - tells usb device controller we don't need it anymore
* @gadget: The device we want to stop activity
* @driver: The driver to unbind from @gadget
*
* This call is issued by the UDC Class driver after calling
* gadget driver's unbind() method.
*
* The details are implementation specific, but it can go as
* far as powering off UDC completely and disable its data
* line pullups.
*/
static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{
udc->gadget->ops->udc_stop(udc->gadget);
}
#if 0
/**
* usb_udc_release - release the usb_udc struct
* @dev: the dev member within usb_udc
*
* This is called by driver's core in order to free memory once the last
* reference is released.
*/
static void usb_udc_release(struct device *dev)
{
//struct usb_udc *udc;
(void)dev;
//udc = container_of(dev, struct usb_udc, dev);
//kfree(udc);
}
#endif
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller driver's
* device.
* @gadget: the gadget to be added to the list.
* @release: a gadget release function.
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
void (*release)(struct device *dev))
{
struct usb_udc *udc;
int ret = -ENOMEM;
udc = kzalloc(sizeof(*udc), GFP_KERNEL);
if (!udc)
goto err1;
udc->gadget = gadget;
INIT_LIST_ITEM(&udc->list);
listSET_LIST_ITEM_OWNER(&udc->list, udc);
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_add_tail(&udc->list, &udc_list);
usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
xSemaphoreGive(udc_lock);
return 0;
err1:
return ret;
}
/**
* usb_add_gadget_udc - adds a new gadget to the udc class driver list
* @parent: the parent device to this udc. Usually the controller
* driver's device.
* @gadget: the gadget to be added to the list
*
* Returns zero on success, negative errno otherwise.
*/
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{
usb_udc_core_init();
return usb_add_gadget_udc_release(parent, gadget, NULL);
}
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
udc->driver->function);
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc);
udc->driver = NULL;
}
/**
* usb_del_gadget_udc - deletes @udc from udc_list
* @gadget: the gadget to be removed.
*
* This, will call usb_gadget_unregister_driver() if
* the @udc is still busy.
*/
void usb_del_gadget_udc(struct usb_gadget *gadget)
{
struct usb_udc *udc = NULL;
ListItem_t *pxListItem = NULL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list)
if (udc->gadget == gadget)
goto found;
dev_err(gadget->dev.parent, "gadget not registered.\n");
xSemaphoreGive(udc_lock);
return;
found:
dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
list_del(&udc->list);
xSemaphoreGive(udc_lock);
if (udc->driver)
usb_gadget_remove_driver(udc);
}
/* ------------------------------------------------------------------------- */
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
int ret;
dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
driver->function);
udc->driver = driver;
ret = driver->bind(udc->gadget);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc);
if (ret) {
driver->unbind(udc->gadget);
goto err1;
}
usb_gadget_connect(udc->gadget);
return 0;
err1:
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
udc->driver->function, ret);
udc->driver = NULL;
return ret;
}
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret;
ListItem_t *pxListItem = NULL;
if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list) {
/* For now we take the first one */
if (!udc->driver)
goto found;
}
printf("couldn't find an available UDC\n");
xSemaphoreGive(udc_lock);
return -ENODEV;
found:
ret = udc_bind_to_driver(udc, driver);
xSemaphoreGive(udc_lock);
return ret;
}
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
usb_udc_core_init();
return usb_gadget_probe_driver(driver);
}
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret = -ENODEV;
ListItem_t *pxListItem = NULL;
if (!driver || !driver->unbind)
return -EINVAL;
xSemaphoreTake(udc_lock, portMAX_DELAY);
list_for_each_entry(pxListItem, udc, &udc_list)
if (udc->driver == driver) {
usb_gadget_remove_driver(udc);
usb_gadget_set_state(udc->gadget,
USB_STATE_NOTATTACHED);
ret = 0;
break;
}
xSemaphoreGive(udc_lock);
return ret;
}

View File

@ -0,0 +1,108 @@
#include "usb_os_adapter.h"
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
static int utf8_to_utf16le(const char *s, __le16 *cp, unsigned len)
{
int count = 0;
u8 c;
u16 uchar;
/*
* this insists on correct encodings, though not minimal ones.
* BUT it currently rejects legit 4-byte UTF-8 code points,
* which need surrogate pairs. (Unicode 3.1 can use them.)
*/
while (len != 0 && (c = (u8) *s++) != 0) {
if ((c & 0x80)) {
/*
* 2-byte sequence:
* 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx
*/
if ((c & 0xe0) == 0xc0) {
uchar = (c & 0x1f) << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/*
* 3-byte sequence (most CJKV characters):
* zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx
*/
} else if ((c & 0xf0) == 0xe0) {
uchar = (c & 0x0f) << 12;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c << 6;
c = (u8) *s++;
if ((c & 0xc0) != 0x80)
goto fail;
c &= 0x3f;
uchar |= c;
/* no bogus surrogates */
if (0xd800 <= uchar && uchar <= 0xdfff)
goto fail;
/*
* 4-byte sequence (surrogate pairs, currently rare):
* 11101110wwwwzzzzyy + 110111yyyyxxxxxx
* = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
* (uuuuu = wwww + 1)
* FIXME accept the surrogate code points (only)
*/
} else
goto fail;
} else
uchar = c;
put_unaligned_le16(uchar, cp++);
count++;
len--;
}
return count;
fail:
return -1;
}
int usb_gadget_get_string(struct usb_gadget_strings *table, int id, u8 *buf)
{
struct usb_string *s;
int len;
if (!table)
return -EINVAL;
/* descriptor 0 has the language id */
if (id == 0) {
buf[0] = 4;
buf[1] = USB_DT_STRING;
buf[2] = (u8) table->language;
buf[3] = (u8) (table->language >> 8);
return 4;
}
for (s = table->strings; s && s->s; s++)
if (s->id == id)
break;
/* unrecognized: stall. */
if (!s || !s->s)
return -EINVAL;
/* string descriptors have length, tag, then UTF16-LE text */
len = min((size_t) 126, strlen(s->s));
memset(buf + 2, 0, 2 * len); /* zero all the bytes */
len = utf8_to_utf16le(s->s, (__le16 *)&buf[2], len);
if (len < 0)
return -EINVAL;
buf[0] = (len + 1) * 2;
buf[1] = USB_DT_STRING;
return buf[0];
}

View File

@ -0,0 +1,449 @@
#ifndef __USB_COMPAT_H__
#define __USB_COMPAT_H__
//#include <dm.h>
#include "usb.h"
#include "timer.h"
struct usb_bus {
int busnum; /* Bus number (in order of reg) */
const char *bus_name; /* stable id (PCI slot_name etc) */
u8 uses_dma; /* Does the host controller use DMA? */
u8 uses_pio_for_control; /*
* Does the host controller use PIO
* for control transfers?
*/
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
unsigned no_stop_on_short:1; /*
* Quirk: some controllers don't stop
* the ep queue on a short transfer
* with the URB_SHORT_NOT_OK flag set.
*/
unsigned no_sg_constraint:1; /* no sg constraint */
unsigned sg_tablesize; /* 0 or largest number of sg list entries */
int devnum_next; /* Next open device number in
* round-robin allocation */
int bandwidth_allocated; /* on this bus: how much of the time
* reserved for periodic (intr/iso)
* requests is used, on average?
* Units: microseconds/frame.
* Limits: Full/low speed reserve 90%,
* while high speed reserves 80%.
*/
int bandwidth_int_reqs; /* number of Interrupt requests */
int bandwidth_isoc_reqs; /* number of Isoc. requests */
unsigned resuming_ports; /* bit array: resuming root-hub ports */
};
struct usb_hcd {
struct usb_bus self;
int has_tt;
void *hcd_priv;
};
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
#ifndef NO_GNU
struct list_head urb_list;
#else
List_t urb_list;
#endif
void *hcpriv;
};
/*
* urb->transfer_flags:
*
* Note: URB_DIR_IN/OUT is automatically set in usb_submit_urb().
*/
#define URB_SHORT_NOT_OK 0x0001 /* report short reads as errors */
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUT with short packet */
#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt*/
struct urb;
typedef void (*usb_complete_t)(struct urb *);
struct urb {
void *hcpriv; /* private data for host controller */
#ifndef NO_GNU
struct list_head urb_list; /* list head for use by the urb's
* current owner */
#else
ListItem_t urb_list;
void *queueHandle;
#endif
struct usb_device *dev; /* (in) pointer to associated device */
struct usb_host_endpoint *ep; /* (internal) pointer to endpoint */
unsigned int pipe; /* (in) pipe information */
int status; /* (return) non-ISO status */
unsigned int transfer_flags; /* (in) URB_SHORT_NOT_OK | ...*/
void *transfer_buffer; /* (in) associated data buffer */
dma_addr_t transfer_dma; /* (in) dma addr for transfer_buffer */
u32 transfer_buffer_length; /* (in) data buffer length */
u32 actual_length; /* (return) actual transfer length */
unsigned char *setup_packet; /* (in) setup packet (control only) */
dma_addr_t setup_dma;
int start_frame; /* (modify) start frame (ISO) */
void *context;
usb_complete_t complete; /* (in) completion routine */
int interval;
int error_count; /* (return) number of ISO errors */
int number_of_packets; /* (in) number of ISO packets */
struct usb_iso_packet_descriptor iso_frame_desc[0];
};
struct hc_driver {
const char *description; /* "ehci-hcd" etc */
const char *product_desc; /* product/vendor string */
size_t hcd_priv_size; /* size of private data */
/* irq handler */
irqreturn_t (*irq) (struct usb_hcd *hcd);
int flags;
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
#define HCD_SHARED 0x0004 /* Two (or more) usb_hcds share HW */
#define HCD_USB11 0x0010 /* USB 1.1 */
#define HCD_USB2 0x0020 /* USB 2.0 */
#define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/
#define HCD_USB3 0x0040 /* USB 3.0 */
#define HCD_USB31 0x0050 /* USB 3.1 */
#define HCD_MASK 0x0070
#define HCD_BH 0x0100 /* URB complete in BH context */
/* called to init HCD and root hub */
int (*reset) (struct usb_hcd *hcd);
int (*start) (struct usb_hcd *hcd);
/* NOTE: these suspend/resume calls relate to the HC as
* a whole, not just the root hub; they're for PCI bus glue.
*/
/* called after suspending the hub, before entering D3 etc */
int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup);
/* called after entering D0 (etc), before resuming the hub */
int (*pci_resume)(struct usb_hcd *hcd, bool hibernated);
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
/* shutdown HCD */
void (*shutdown) (struct usb_hcd *hcd);
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
/* manage i/o requests, device state */
int (*urb_enqueue)(struct usb_hcd *hcd,
struct urb *urb, gfp_t mem_flags);
int (*urb_dequeue)(struct usb_hcd *hcd,
struct urb *urb, int status);
/*
* (optional) these hooks allow an HCD to override the default DMA
* mapping and unmapping routines. In general, they shouldn't be
* necessary unless the host controller has special DMA requirements,
* such as alignment contraints. If these are not specified, the
* general usb_hcd_(un)?map_urb_for_dma functions will be used instead
* (and it may be a good idea to call these functions in your HCD
* implementation)
*/
int (*map_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags);
void (*unmap_urb_for_dma)(struct usb_hcd *hcd, struct urb *urb);
/* hw synch, freeing endpoint resources that urb_dequeue can't */
void (*endpoint_disable)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* (optional) reset any endpoint state such as sequence number
and current window */
void (*endpoint_reset)(struct usb_hcd *hcd,
struct usb_host_endpoint *ep);
/* root hub support */
int (*hub_status_data) (struct usb_hcd *hcd, char *buf);
int (*hub_control) (struct usb_hcd *hcd,
u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
/* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int);
/* has a port been handed over to a companion? */
int (*port_handed_over)(struct usb_hcd *, int);
/* CLEAR_TT_BUFFER completion callback */
void (*clear_tt_buffer_complete)(struct usb_hcd *,
struct usb_host_endpoint *);
/* xHCI specific functions */
/* Called by usb_alloc_dev to alloc HC device structures */
int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
/* Called by usb_disconnect to free HC device structures */
void (*free_dev)(struct usb_hcd *, struct usb_device *);
/* Change a group of bulk endpoints to support multiple stream IDs */
int (*alloc_streams)(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
unsigned int num_streams, gfp_t mem_flags);
/* Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we run out of memory.
*/
int (*free_streams)(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags);
/* Bandwidth computation functions */
/* Note that add_endpoint() can only be called once per endpoint before
* check_bandwidth() or reset_bandwidth() must be called.
* drop_endpoint() can only be called once per endpoint also.
* A call to xhci_drop_endpoint() followed by a call to
* xhci_add_endpoint() will add the endpoint to the schedule with
* possibly new parameters denoted by a different endpoint descriptor
* in usb_host_endpoint. A call to xhci_add_endpoint() followed by a
* call to xhci_drop_endpoint() is not allowed.
*/
/* Allocate endpoint resources and add them to a new schedule */
int (*add_endpoint)(struct usb_hcd *, struct usb_device *,
struct usb_host_endpoint *);
/* Drop an endpoint from a new schedule */
int (*drop_endpoint)(struct usb_hcd *, struct usb_device *,
struct usb_host_endpoint *);
/* Check that a new hardware configuration, set using
* endpoint_enable and endpoint_disable, does not exceed bus
* bandwidth. This must be called before any set configuration
* or set interface requests are sent to the device.
*/
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Reset the device schedule to the last known good schedule,
* which was set from a previous successful call to
* check_bandwidth(). This reverts any add_endpoint() and
* drop_endpoint() calls since that last successful call.
* Used for when a check_bandwidth() call fails due to resource
* or bandwidth constraints.
*/
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
/* Returns the hardware-chosen device address */
int (*address_device)(struct usb_hcd *, struct usb_device *udev);
/* prepares the hardware to send commands to the device */
int (*enable_device)(struct usb_hcd *, struct usb_device *udev);
/* Notifies the HCD after a hub descriptor is fetched.
* Will block.
*/
int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags);
int (*reset_device)(struct usb_hcd *, struct usb_device *);
/* Notifies the HCD after a device is connected and its
* address is set
*/
int (*update_device)(struct usb_hcd *, struct usb_device *);
int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int);
int (*find_raw_port_number)(struct usb_hcd *, int);
/* Call for power on/off the port if necessary */
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
};
#ifndef NO_GNU
#define usb_hcd_link_urb_to_ep(hcd, urb) ({ \
int ret = 0; \
list_add_tail(&urb->urb_list, &urb->ep->urb_list); \
ret; })
#else
#define usb_hcd_link_urb_to_ep(hcd, urb) do { \
list_add_tail(&urb->urb_list, &urb->ep->urb_list); \
} while(0)
#endif
#define usb_hcd_unlink_urb_from_ep(hcd, urb) list_del_init(&urb->urb_list)
#define usb_hcd_check_unlink_urb(hdc, urb, status) 0
static inline void usb_hcd_giveback_urb(struct usb_hcd *hcd,
struct urb *urb,
int status)
{
urb->status = status;
if (urb->complete)
urb->complete(urb);
}
static inline int usb_hcd_unmap_urb_for_dma(struct usb_hcd *hcd,
struct urb *urb)
{
/* TODO: add cache invalidation here */
return 0;
}
static inline struct usb_bus *hcd_to_bus(struct usb_hcd *hcd)
{
return &hcd->self;
}
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
{
(void)hcd;
return;
}
/*
* Generic bandwidth allocation constants/support
*/
#define FRAME_TIME_USECS 1000L
#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */
/* Trying not to use worst-case bit-stuffing
* of (7/6 * 8 * bytecount) = 9.33 * bytecount */
/* bytecount = data payload byte count */
#define NS_TO_US(ns) DIV_ROUND_UP(ns, 1000L)
/* convert nanoseconds to microseconds, rounding up */
/*
* Full/low speed bandwidth allocation constants/support.
*/
#define BW_HOST_DELAY 1000L /* nanoseconds */
#define BW_HUB_LS_SETUP 333L /* nanoseconds */
/* 4 full-speed bit times (est.) */
#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */
#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L)
#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L)
/*
* Ceiling [nano/micro]seconds (typical) for that many bytes at high speed
* ISO is a bit less, no ACK ... from USB 2.0 spec, 5.11.3 (and needed
* to preallocate bandwidth)
*/
#define USB2_HOST_DELAY 5 /* nsec, guess */
#define HS_NSECS(bytes) (((55 * 8 * 2083) \
+ (2083UL * (3 + BitTime(bytes))))/1000 \
+ USB2_HOST_DELAY)
#define HS_NSECS_ISO(bytes) (((38 * 8 * 2083) \
+ (2083UL * (3 + BitTime(bytes))))/1000 \
+ USB2_HOST_DELAY)
#define HS_USECS(bytes) NS_TO_US(HS_NSECS(bytes))
#define HS_USECS_ISO(bytes) NS_TO_US(HS_NSECS_ISO(bytes))
static inline long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
{
unsigned long tmp;
switch (speed) {
case USB_SPEED_LOW: /* INTR only */
if (is_input) {
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
} else {
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
}
case USB_SPEED_FULL: /* ISOC or INTR */
if (isoc) {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
} else {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return 9107L + BW_HOST_DELAY + tmp;
}
case USB_SPEED_HIGH: /* ISOC or INTR */
/* FIXME adjust for input vs output */
if (isoc)
tmp = HS_NSECS_ISO (bytecount);
else
tmp = HS_NSECS (bytecount);
return tmp;
default:
//pr_debug ("%s: bogus device speed!\n", usbcore_name);
return -1;
}
}
#ifdef CONFIG_DM_USB
static inline struct usb_device *usb_dev_get_parent(struct usb_device *udev)
{
struct udevice *parent = udev->dev->parent;
/*
* When called from usb-uclass.c: usb_scan_device() udev->dev points
* to the parent udevice, not the actual udevice belonging to the
* udev as the device is not instantiated yet.
*
* If dev is an usb-bus, then we are called from usb_scan_device() for
* an usb-device plugged directly into the root port, return NULL.
*/
if (device_get_uclass_id(udev->dev) == UCLASS_USB)
return NULL;
/*
* If these 2 are not the same we are being called from
* usb_scan_device() and udev itself is the parent.
*/
if (dev_get_parent_priv(udev->dev) != udev)
return udev;
/* We are being called normally, use the parent pointer */
if (device_get_uclass_id(parent) == UCLASS_USB_HUB)
return dev_get_parent_priv(parent);
return NULL;
}
#else
static inline struct usb_device *usb_dev_get_parent(struct usb_device *dev)
{
return dev->parent;
}
#endif
#if 0
static inline void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req)
{
req->complete(ep, req);
}
static inline int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return 0;
req->dma = dma_map_single(req->buf, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
return 0;
}
static inline void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in)
{
if (req->length == 0)
return;
dma_unmap_single((void *)req->dma, req->length,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
}
#else
void usb_gadget_giveback_request(struct usb_ep *ep,
struct usb_request *req);
int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
#endif
#endif /* __USB_COMPAT_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,110 @@
#ifndef __USB_MASS_STORAGE_H
#define __USB_MASS_STORAGE_H
/* Part types */
#define PART_TYPE_UNKNOWN 0x00
#define PART_TYPE_MAC 0x01
#define PART_TYPE_DOS 0x02
#define PART_TYPE_ISO 0x03
#define PART_TYPE_AMIGA 0x04
#define PART_TYPE_EFI 0x05
/* device types */
#define DEV_TYPE_UNKNOWN 0xff /* not connected */
#define DEV_TYPE_HARDDISK 0x00 /* harddisk */
#define DEV_TYPE_TAPE 0x01 /* Tape */
#define DEV_TYPE_CDROM 0x05 /* CD-ROM */
#define DEV_TYPE_OPDISK 0x07 /* optical disk */
#define PART_NAME_LEN 32
#define PART_TYPE_LEN 32
#define MAX_SEARCH_PARTITIONS 64
typedef unsigned long lbaint_t;
typedef struct {
u8 b[16];
} efi_guid_t;
/* Interface types: */
enum if_type {
IF_TYPE_UNKNOWN = 0,
IF_TYPE_IDE,
IF_TYPE_SCSI,
IF_TYPE_ATAPI,
IF_TYPE_USB,
IF_TYPE_DOC,
IF_TYPE_MMC,
IF_TYPE_SD,
IF_TYPE_SATA,
IF_TYPE_HOST,
IF_TYPE_NVME,
IF_TYPE_EFI,
IF_TYPE_COUNT, /* Number of interface types */
};
#define BLK_VEN_SIZE 40
#define BLK_PRD_SIZE 20
#define BLK_REV_SIZE 8
/*
* Identifies the partition table type (ie. MBR vs GPT GUID) signature
*/
enum sig_type {
SIG_TYPE_NONE,
SIG_TYPE_MBR,
SIG_TYPE_GUID,
SIG_TYPE_COUNT /* Number of signature types */
};
/*
* With driver model (CONFIG_BLK) this is uclass platform data, accessible
* with dev_get_uclass_platdata(dev)
*/
struct blk_desc {
/*
* TODO: With driver model we should be able to use the parent
* device's uclass instead.
*/
enum if_type if_type; /* type of the interface */
int devnum; /* device number */
unsigned char part_type; /* partition type */
unsigned char target; /* target SCSI ID */
unsigned char lun; /* target LUN */
unsigned char hwpart; /* HW partition, e.g. for eMMC */
unsigned char type; /* device type */
unsigned char removable; /* removable device */
lbaint_t lba; /* number of blocks */
unsigned long blksz; /* block size */
int log2blksz; /* for convenience: log2(blksz) */
char vendor[BLK_VEN_SIZE + 1]; /* device vendor string */
char product[BLK_PRD_SIZE + 1]; /* device product number */
char revision[BLK_REV_SIZE + 1]; /* firmware revision */
enum sig_type sig_type; /* Partition table signature type */
union {
u32 mbr_sig; /* MBR integer signature */
efi_guid_t guid_sig; /* GPT GUID Signature */
};
unsigned long (*block_read)(struct blk_desc *block_dev,
lbaint_t start,
lbaint_t blkcnt,
void *buffer);
unsigned long (*block_write)(struct blk_desc *block_dev,
lbaint_t start,
lbaint_t blkcnt,
const void *buffer);
unsigned long (*block_erase)(struct blk_desc *block_dev,
lbaint_t start,
lbaint_t blkcnt);
void *priv; /* driver private struct pointer */
void *disk_priv;
};
extern struct blk_desc usb_dev_desc[6];
#endif

View File

@ -0,0 +1,306 @@
#include "usb_os_adapter.h"
void *kmem_cache_alloc(struct kmem_cache *obj, int flag)
{
(void)flag;
return pvPortMalloc(obj->sz);
}
void kmem_cache_free(struct kmem_cache *cachep, void *obj)
{
(void)cachep;
vPortFree(obj);
}
void kmem_cache_destroy(struct kmem_cache *cachep)
{
free(cachep);
}
static void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
void* ptr = NULL;
if (size != 0 && n > SIZE_MAX / size)
return NULL;
ptr = pvPortMalloc(n * size);
if (flags & __GFP_ZERO) {
memset(ptr, 0, n * size);
}
return ptr;
}
void *kcalloc(size_t n, size_t size, gfp_t flags)
{
return kmalloc_array(n, size, flags | __GFP_ZERO);
}
void *kmalloc(size_t size, int flags)
{
void *p;
p = pvPortMalloc(size);
if (flags & __GFP_ZERO)
memset(p, 0, size);
return p;
}
void *kzalloc(size_t size, gfp_t flags)
{
return kmalloc(size, flags | __GFP_ZERO);
}
//struct device;
void *devm_kzalloc(struct device *dev, size_t size, gfp_t flags)
{
return kmalloc(size, flags | __GFP_ZERO);
}
void kfree(void* addr)
{
vPortFree(addr);
}
struct kmem_cache *get_mem(int element_sz)
{
struct kmem_cache *ret;
ret = pvPortMalloc(sizeof(struct kmem_cache));
ret->sz = element_sz;
return ret;
}
static unsigned long _find_next_bit(const unsigned long *addr1,
const unsigned long *addr2, unsigned long nbits,
unsigned long start, unsigned long invert)
{
unsigned long tmp;
if (unlikely(start >= nbits))
return nbits;
tmp = addr1[start / BITS_PER_LONG];
if (addr2)
tmp &= addr2[start / BITS_PER_LONG];
tmp ^= invert;
/* Handle 1st word. */
tmp &= BITMAP_FIRST_WORD_MASK(start);
start = round_down(start, BITS_PER_LONG);
while (!tmp) {
start += BITS_PER_LONG;
if (start >= nbits)
return nbits;
tmp = addr1[start / BITS_PER_LONG];
if (addr2)
tmp &= addr2[start / BITS_PER_LONG];
tmp ^= invert;
}
return min(start + __ffs(tmp), nbits);
}
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
return _find_next_bit(addr, NULL, size, offset, 0UL);
}
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
return _find_next_bit(addr, NULL, size, offset, ~0UL);
}
unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask,
unsigned long align_offset)
{
unsigned long index, end, i;
again:
index = find_next_zero_bit(map, size, start);
/* Align allocation */
index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset;
end = index + nr;
if (end > size)
return end;
i = find_next_bit(map, end, index);
if (i < end) {
start = i + 1;
goto again;
}
return index;
}
unsigned long
bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask)
{
return bitmap_find_next_zero_area_off(map, size, start, nr, align_mask, 0);
}
void bitmap_set(unsigned long *map, unsigned int start, int len)
{
unsigned long *p = map + BIT_WORD(start);
const unsigned int size = start + len;
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
while (len - bits_to_set >= 0) {
*p |= mask_to_set;
len -= bits_to_set;
bits_to_set = BITS_PER_LONG;
mask_to_set = ~0UL;
p++;
}
if (len) {
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
*p |= mask_to_set;
}
}
void bitmap_clear(unsigned long *map, unsigned int start, int len)
{
unsigned long *p = map + BIT_WORD(start);
const unsigned int size = start + len;
int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
while (len - bits_to_clear >= 0) {
*p &= ~mask_to_clear;
len -= bits_to_clear;
bits_to_clear = BITS_PER_LONG;
mask_to_clear = ~0UL;
p++;
}
if (len) {
mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
*p &= ~mask_to_clear;
}
}
void writesl(u32 addr, const void *buffer, unsigned int count)
{
if (count) {
const u32 *buf = (u32 *)buffer;
do {
writel(*buf++, addr);
} while (--count);
}
}
void readsl(u32 addr, void *buffer, unsigned int count)
{
if (count) {
u32 *buf = (u32 *)buffer;
do {
u32 x = readl(addr);
*buf++ = x;
} while (--count);
}
}
void iowrite32_rep(u32 addr, const void *buffer, unsigned int count)
{
writesl(addr, buffer, count);
}
void ioread32_rep(u32 addr, void *buffer, unsigned int count)
{
readsl(addr, buffer, count);
}
void put_unaligned_le16(u16 val, void *p)
{
// *((__le16 *)p) = cpu_to_le16(val);
u8 *tmp = (u8*)p;
tmp[0] = (val & 0xff);
tmp[1] = ((val >> 8) & 0xff);
}
void put_unaligned_le32(u32 val, void *p)
{
// *((__le32 *)p) = cpu_to_le32(val);
u8 *tmp = (u8*)p;
tmp[0] = (val & 0xff);
tmp[1] = ((val >> 8) & 0xff);
tmp[2] = ((val >> 16) & 0xff);
tmp[3] = ((val >> 24) & 0xff);
}
int get_unaligned_le16(void *p)
{
u8 *tmp = (u8*)p;
return (tmp[1] << 8) | tmp[0];
}
int get_unaligned_le32(void *p)
{
u8 *tmp = (u8*)p;
return (tmp[3] << 24) | (tmp[2] << 16) | (tmp[1] << 8) | tmp[0];
}
void generic_set_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
*p |= mask;
}
int is_zero_ether_addr(const u8 *addr)
{
return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]);
}
int is_multicast_ether_addr(const u8 *addr)
{
return 0x01 & addr[0];
}
int is_local_ether_addr(const u8 *addr)
{
return 0x02 & addr[0];
}
#if 0
int is_broadcast_ether_addr(const u8 *addr)
{
return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff;
}
#endif
int is_unicast_ether_addr(const u8 *addr)
{
return !is_multicast_ether_addr(addr);
}
int is_valid_ether_addr(const u8 *addr)
{
return !is_multicast_ether_addr(addr) && !is_zero_ether_addr(addr);
}
#if 0
static inline void random_ether_addr(u8 *addr)
{
get_random_bytes (addr, ETH_ALEN);
addr [0] &= 0xfe; /* clear multicast bit */
addr [0] |= 0x02; /* set local assignment bit (IEEE802) */
}
#endif

View File

@ -0,0 +1,351 @@
#ifndef _USB_OS_ADAPTER_H
#define _USB_OS_ADAPTER_H
#include "os_adapt.h"
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
#define NO_GNU
#define pr_err(...) TRACE_ERROR(__VA_ARGS__)
#define WARN_ON_ONCE(condition) WARN_ON(condition)
#define USB_UNUSED(x) ((void) x)
typedef INT8 __s8;
typedef UINT8 __u8;
typedef INT16 __s16;
typedef UINT16 __u16;
typedef INT32 __s32;
typedef UINT32 __u32;
typedef long long __s64;
typedef unsigned long long __u64;
typedef __u16 __le16;
typedef __u16 __be16;
//typedef __u32 __le32;
typedef __u32 __be32;
typedef __u64 __le64;
typedef __u64 __be64;
typedef unsigned long ulong;
typedef __u32 dev_t;
#define __cpu_to_le16(x) (x)
#define cpu_to_le16(x) (x)
#define __le16_to_cpu le16_to_cpu
#define get_unaligned(x) (*x)
void iowrite32_rep(u32 addr, const void *buffer, unsigned int count);
void ioread32_rep(u32 addr, void *buffer, unsigned int count);
unsigned long bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask);
void bitmap_set(unsigned long *map, unsigned int start, int len);
void bitmap_clear(unsigned long *map, unsigned int start, int len);
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(ListItem_t *item, List_t *list)
{
if (!listIS_CONTAINED_WITHIN(NULL, item))
uxListRemove(item);
vListInsertEnd(list, item);
}
static inline void list_del_init(ListItem_t *item)
{
if (!listIS_CONTAINED_WITHIN(NULL, item)) {//maybe item has removed from list
uxListRemove(item);
vListInitialiseItem(item);
}
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(ListItem_t *item, List_t *list)
{
vListInsertEnd(list, item);
}
static inline void INIT_LIST_HEAD(List_t *list)
{
vListInitialise(list);
}
static inline void INIT_LIST_ITEM(ListItem_t *item)
{
vListInitialiseItem(item);
}
#define list_for_each_entry_safe(pxListItem, nListItem, pvOwner, list) \
for (pxListItem = listGET_HEAD_ENTRY(list), \
nListItem = listGET_NEXT(pxListItem), \
pvOwner = listGET_LIST_ITEM_OWNER(pxListItem); \
pxListItem != listGET_END_MARKER(list); \
pxListItem = nListItem, \
nListItem = listGET_NEXT(pxListItem), \
pvOwner = listGET_LIST_ITEM_OWNER(pxListItem))
#define list_for_each_safe(pxListItem, nListItem, list) \
for (pxListItem = listGET_HEAD_ENTRY(list), \
nListItem = listGET_NEXT(pxListItem); \
pxListItem != listGET_END_MARKER(list); \
pxListItem = nListItem, \
nListItem = listGET_NEXT(pxListItem))
#define list_del(pxListItem) uxListRemove(pxListItem)
#define list_empty(pxList) listLIST_IS_EMPTY(pxList)
#define list_item_empty(pxListItem) ((pxListItem)->pxContainer == NULL)
#define __ARG_PLACEHOLDER_1 0,
#define config_enabled(cfg) _config_enabled(cfg)
#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value)
#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0)
#define ___config_enabled(__ignored, val, ...) val
/*
* IS_ENABLED(CONFIG_FOO) evaluates to 1 if CONFIG_FOO is set to 'y' or 'm',
* 0 otherwise.
*
*/
#define IS_ENABLED(option) \
(config_enabled(option) || config_enabled(option##_MODULE))
struct timer_list
{
int a;
};
struct unused {
int a;
};
typedef struct unused unused_t;
#define task_pid_nr(x) 0
#define set_freezable(...) do { } while (0)
#define try_to_freeze(...) 0
#define set_current_state(...) do { } while (0)
#define kthread_should_stop(...) 0
#define schedule() do { } while (0)
#define setup_timer(timer, func, data) do {} while (0)
#define del_timer_sync(timer) do {} while (0)
#define schedule_work(work) do {} while (0)
#define INIT_WORK(work, fun) do {} while (0)
//#define local_irq_save(flag) do {} while (0)
//#define local_irq_restore(flag) do {} while (0)
#define local_irq_save(flag) do {portENTER_CRITICAL(); (void)flag;} while(0)
#define local_irq_restore(flag) do {portEXIT_CRITICAL(); (void)flag;} while(0)
struct work_struct {
int a;
};
struct kmem_cache {
int sz;
};
typedef int wait_queue_head_t;
typedef struct {
volatile unsigned int slock;
} spinlock_t;
static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
{
//TODO
unsigned volatile long y;
switch(size){
case 1:
y = (*(char *)ptr) & 0x000000ff;
*((char *)ptr) = (char)x;
break;
case 2:
y = (*(short *)ptr) & 0x0000ffff;
*((short *)ptr) = (short)x;
break;
default: // 4
y = (*(unsigned long *)ptr) & 0xffffffff;
*((unsigned long *)ptr) = x;
break;
}
return y;
}
#define ARCH_SPIN_LOCK_UNLOCKED 1
#define arch_spin_is_locked(x) (*(volatile signed char *)(&(x)->slock) <= 0)
#define arch_spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x))
#define xchg(ptr,v) ((unsigned int)__xchg((unsigned long)(v),(ptr),sizeof(*(ptr))))
#define __xg(x) ((volatile long *)(x))
static inline void _raw_spin_unlock(spinlock_t *lock)
{
xchg(&lock->slock, 1);
}
static inline int _raw_spin_trylock(spinlock_t *lock)
{
return xchg(&lock->slock, 0) != 0 ? 1 : 0;
}
static inline void _raw_spin_lock(spinlock_t *lock)
{
volatile int was_locked;
do {
was_locked = xchg(&lock->slock, 0) == 0 ? 1 : 0;
} while(was_locked);
}
#define SPINLOCK_MAGIC 0xdead4ead
#define SPIN_LOCK_UNLOCKED ARCH_SPIN_LOCK_UNLOCKED
#define spin_lock_init(x) do { (x)->slock = SPIN_LOCK_UNLOCKED; } while(0)
#define spin_is_locked(x) arch_spin_is_locked(x)
#define spin_unlock_wait(x) arch_spin_unlock_wait(x)
#define _spin_trylock(lock) ({_raw_spin_trylock(lock) ? \
1 : ({ 0;});})
#define _spin_lock(lock) \
do { \
if (!xPortIsInInterrupt()) \
_raw_spin_lock(lock); \
} while(0)
#define _spin_unlock(lock) \
do { \
if (!xPortIsInInterrupt()) \
_raw_spin_unlock(lock); \
} while (0)
#define spin_lock(lock) _spin_lock(lock)
#define spin_unlock(lock) _spin_unlock(lock)
#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED
#define spin_lock_irqsave(lock, flags) \
do { \
(void)lock; flags = __get_interrupt_state(); __disable_irq(); \
} while (0)
#define spin_unlock_irqrestore(lock, flags) \
do { \
(void)lock; __set_interrupt_state(flags); \
} while (0)
#define assert_spin_locked(lock) do {} while (0)
#define irqreturn_t int
#define IRQ_NONE 0
#define IRQ_HANDLED 1
#define IRQ_WAKE_THREAD 2
#define GFP_ATOMIC ((gfp_t) 0)
#define GFP_KERNEL ((gfp_t) 0)
#define GFP_NOFS ((gfp_t) 0)
#define GFP_USER ((gfp_t) 0)
#define __GFP_NOWARN ((gfp_t) 0)
#define __GFP_ZERO ((gfp_t)0x8000u)
#define UINT_MAX (~0U)
void *kmem_cache_alloc(struct kmem_cache *obj, int flag);
void kmem_cache_free(struct kmem_cache *cachep, void *obj);
void kmem_cache_destroy(struct kmem_cache *cachep);
void *kcalloc(size_t n, size_t size, gfp_t flags);
void *kmalloc(size_t size, int flags);
void *kzalloc(size_t size, gfp_t flags);
void kfree(void* addr);
struct device;
void *devm_kzalloc(struct device *dev, size_t size, gfp_t flags);
struct kmem_cache *get_mem(int element_sz);
#define kmem_cache_create(a, sz, c, d, e) get_mem(sz)
#define min_t(type, x, y) (x < y ? x: y)
#define max_t(type, x, y) (x > y ? x: y)
//#define msleep mdelay
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
#define __round_mask(x, y) ((unsigned long)((y)-1))
#define round_down(x, y) ((x) & ~(__round_mask((x), (y))))
#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
//#define min(x,y) ((x) < (y) ? x : y)
#define max(x,y) ((x) > (y) ? x : y)
#define min3(x, y, z) min(min(x, y), z)
#define max3(x, y, z) max(max(x, y), z)
#define ROUND(a,b) (((a) + (b) - 1) & ~((b) - 1))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define ALIGN(x,a) __ALIGN_MASK((x),(uintptr_t)(a)-1)
//#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
//typedef unsigned long uintptr_t;
#define PAD_COUNT(s, pad) (((s) - 1) / (pad) + 1)
#define PAD_SIZE(s, pad) (PAD_COUNT(s, pad) * pad)
#define ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad) \
char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align) \
+ (align - 1)]; \
\
type *name = (type *)ALIGN((uintptr_t)__##name, align)
#define ALLOC_ALIGN_BUFFER(type, name, size, align) \
ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
#define ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad) \
ALLOC_ALIGN_BUFFER_PAD(type, name, size, ARCH_DMA_MINALIGN, pad)
#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \
ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
#define be32_to_cpu(x) ((uint32_t)( \
(((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \
(((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \
(((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \
(((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24)))
#define LOG2(x) (((x & 0xaaaaaaaa) ? 1 : 0) + ((x & 0xcccccccc) ? 2 : 0) + \
((x & 0xf0f0f0f0) ? 4 : 0) + ((x & 0xff00ff00) ? 8 : 0) + \
((x & 0xffff0000) ? 16 : 0))
void put_unaligned_le16(u16 val, void *p);
void put_unaligned_le32(u32 val, void *p);
int get_unaligned_le16(void *p);
int get_unaligned_le32(void *p);
void generic_set_bit(int nr, volatile unsigned long *addr);
int is_zero_ether_addr(const u8 *addr);
int is_multicast_ether_addr(const u8 *addr);
int is_local_ether_addr(const u8 *addr);
int is_broadcast_ether_addr(const u8 *addr);
int is_unicast_ether_addr(const u8 *addr);
int is_valid_ether_addr(const u8 *addr);
int get_usb_mode(void);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,263 @@
/*
* platform.c - DesignWare HS OTG Controller platform driver
*
* Copyright (C) Matthijs Kooijman <matthijs@stdin.nl>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. 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.
* 3. The names of the above-listed copyright holders may not be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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.
*/
#include "usb_os_adapter.h"
#include "trace.h"
#include <asm/dma-mapping.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "core.h"
#include "hcd.h"
#include "chip.h"
#include "board.h"
int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
{
int ret = 0;
if (ret == 0)
hsotg->ll_hw_enabled = false;
return ret;
}
int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
{
int ret = 0;
if (ret == 0)
hsotg->ll_hw_enabled = true;
return ret;
}
static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
{
if (get_usb_mode()) {
hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;//usb_get_dr_mode(hsotg->dev);
if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
hsotg->dr_mode = USB_DR_MODE_OTG;
} else {
enum usb_dr_mode mode;
hsotg->dr_mode = USB_DR_MODE_HOST;//usb_get_dr_mode(hsotg->dev);
if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
hsotg->dr_mode = USB_DR_MODE_OTG;
mode = hsotg->dr_mode;
if (dwc2_hw_is_device(hsotg)) {
if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
dev_err(hsotg->dev,
"Controller does not support host mode.\n");
return -EINVAL;
}
mode = USB_DR_MODE_PERIPHERAL;
} else if (dwc2_hw_is_host(hsotg)) {
if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
dev_err(hsotg->dev,
"Controller does not support device mode.\n");
return -EINVAL;
}
mode = USB_DR_MODE_HOST;
} else {
if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
mode = USB_DR_MODE_HOST;
else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
mode = USB_DR_MODE_PERIPHERAL;
}
if (mode != hsotg->dr_mode) {
dev_warn(hsotg->dev,
"Configuration mismatch. dr_mode forced to %s\n",
mode == USB_DR_MODE_HOST ? "host" : "device");
hsotg->dr_mode = mode;
}
}
return 0;
}
//static QueueHandle_t usb_irq_queue;
//static TaskHandle_t usb_irq_task;
//static char first_irq = 1;
irqreturn_t dwc2_hsotg_irq(int irq, void *pw);
static void ark_usb_interrupt(void *param)
{
struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)param;
//printf("ark_usb_interrupt\r\n");
dwc2_handle_common_intr(hsotg->irq, param);
dwc2_hcd_irq(hsotg);
dwc2_hsotg_irq(hsotg->irq, param);
/*if (first_irq) {
dwc2_hcd_irq(hsotg);
first_irq = 0;
} else {
portDISABLE_INTERRUPTS();
xQueueSendFromISR(usb_irq_queue, NULL, 0);
}*/
}
#if 0
static void usb_irq_task_proc(void *pvParameters)
{
struct dwc2_hsotg *hsotg = (struct dwc2_hsotg *)pvParameters;
int ret = -1;
while(1) {
ret = xQueueReceive(usb_irq_queue, NULL, portMAX_DELAY);
if (pdFALSE == ret) {
continue;
}
//portENTER_CRITICAL();
dwc2_hcd_irq(hsotg);
portENABLE_INTERRUPTS();
//portEXIT_CRITICAL();
}
}
#endif
int dwc2_driver_init(struct dwc2_hsotg **dev, struct usb_hcd *hcd)
{
struct dwc2_hsotg *hsotg;
int retval = -1;
u32 size = sizeof(struct device);
hsotg = devm_kzalloc(NULL, sizeof(*hsotg), GFP_KERNEL);
if (!hsotg)
return -ENOMEM;
hsotg->dev = (struct device *)kzalloc(size, (__GFP_ZERO | GFP_KERNEL));
if (NULL == hsotg->dev)
goto error;
hsotg->phyif = GUSBCFG_PHYIF16;
hsotg->regs = REGS_USB_BASE;
spin_lock_init(&(hsotg->lock));
hsotg->irq = USB_IRQn;
retval = request_irq(hsotg->irq, 0, ark_usb_interrupt, (void*)hsotg);
if (retval)
goto error;
retval = dwc2_get_dr_mode(hsotg);
if (retval)
goto error;
/*
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.
*/
dwc2_core_reset_and_force_dr_mode(hsotg);
/* Detect config values from hardware */
retval = dwc2_get_hwparams(hsotg);
if (retval)
goto error;
dwc2_force_dr_mode(hsotg);
retval = dwc2_init_params(hsotg);
if (retval)
goto error;
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg, hsotg->irq);
if (retval)
goto error;
hsotg->gadget_enabled = 1;
}
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
//usb_irq_queue = xQueueCreate(1, 0);
//xTaskCreate(usb_irq_task_proc, "usb_irq_proc", configMINIMAL_STACK_SIZE * 5, (void*)dev, configMAX_PRIORITIES, &usb_irq_task);
retval = dwc2_hcd_init(hsotg, hcd);
if (retval) {
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
goto error;
}
hsotg->hcd_enabled = 1;
}
/* Gadget code manages lowlevel hw on its own */
if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
dwc2_lowlevel_hw_disable(hsotg);
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
/* Postponed adding a new gadget to the udc class driver list */
if (hsotg->gadget_enabled) {
retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget);
if (retval) {
//hsotg->gadget.udc = NULL;
dwc2_hsotg_remove(hsotg);
goto error;
}
}
#endif
if (dev) {
*dev = hsotg;
}
return 0;
error:
if (hsotg && hsotg->dev) {
kfree(hsotg->dev);
}
if (hsotg) {
kfree(hsotg);
}
return retval;
}
int dwc2_driver_uninit(struct dwc2_hsotg *hsotg)
{
if (NULL == hsotg)
return 0;
if (hsotg->hcd_enabled)
dwc2_hcd_remove(hsotg);
if (hsotg->gadget_enabled)
dwc2_hsotg_remove(hsotg);
if (hsotg->dev)
kfree(hsotg->dev);
kfree(hsotg);
return 0;
}

View File

@ -0,0 +1,703 @@
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
#include "usb_uvc.h"
#include "usb_os_adapter.h"
#include "trace.h"
#include <linux/usb/ch9.h>
#include "scsi.h"
#include "usb.h"
#include "dwc2_compat.h"
#include "ark_dwc2.h"
#include "list.h"
#include "trace.h"
#include "semphr.h"
#include "timers.h"
#include "sysctl.h"
#include "itu.h"
#include "board.h"
#include "ff_stdio.h"
#include "board.h"
#include "lcd.h"
#include "pxp.h"
#include "timer.h"
#include "cp15/cp15.h"
#include "usb.h"
#ifdef USB_UVC_SUPPORT
#pragma data_alignment=64
static char iso_buf[800 * 8] = {0};
#define UVC_VIDEO_WIDTH 1280
#define UVC_VIDEO_HEIGHT 720
//#define UVC_VIDEO_WIDTH 640
//#define UVC_VIDEO_HEIGHT 480
#define UVC0_JPEG_FIFO_COUNT (2)
#define UVC0_JPEG_BUF_SIZE 0x80000
#define UVC0_JPEG_YUV_BUF_SIZE ((UVC_VIDEO_WIDTH * UVC_VIDEO_HEIGHT) * 2 + 2048)
#define SAVE_UVC_FRAME 0
typedef struct _jpeg_frame_s {
char *buf; //ͼƬ<CDBC><C6AC><EFBFBD>ŵ<EFBFBD><C5B5>׵<EFBFBD>ַ
char *cur; //ÿ<><C3BF>payload<61><64><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݷ<EFBFBD><DDB7><EFBFBD>buf<75><66>ƫ<EFBFBD>ƺ<EFBFBD><C6BA>ĵ<EFBFBD>ַ
char *yuv_buf;
unsigned int yuv_len;
unsigned int len; //ͼƬ<CDBC>ܳ<EFBFBD><DCB3><EFBFBD>
unsigned int lock;
unsigned int frame_id;
ListItem_t entry;
} video_frame_s;
typedef struct _MGC_UVC_PROBE_PARAM_ {
uint16_t bmHint;
uint8_t bFormatIndex;
uint8_t bFrameIndex;
uint32_t dwFrameInterval;
uint16_t wKeyFrameRate;
uint16_t wPFrameRate;
uint16_t wCompQuality;
uint16_t wCompWindowSize;
uint16_t wDelay;
uint32_t dwMaxVideoFrameSize;
uint32_t dwMaxPayloadTransferSize;
} MGC_UVC_PROBE_PARAM;
static List_t frame_free_list;
static video_frame_s jpeg_fifos[UVC0_JPEG_FIFO_COUNT];
static char* jpeg_buf = NULL;
static char* jpeg_yuv_buf = NULL;
static SemaphoreHandle_t frame_list_mutex = NULL;
static TaskHandle_t dec_jpeg_handle;
static TaskHandle_t iso_read_handle;
static QueueHandle_t jpeg_frame_queue;
static QueueHandle_t iso_exit;
static int uvc_tast_start = 0;
static int fid;
//static int g_frame_err = 0;
static int g_uvc_dev_ready = 0;
void usb_set_maxpacket_ep_ex(struct usb_device *dev, int if_idx, int alt_idx, int ep_idx);
/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
#define UVC_STREAM_EOH (1 << 7)
#define UVC_STREAM_ERR (1 << 6)
#define UVC_STREAM_STI (1 << 5)
#define UVC_STREAM_RES (1 << 4)
#define UVC_STREAM_SCR (1 << 3)
#define UVC_STREAM_PTS (1 << 2)
#define UVC_STREAM_EOF (1 << 1)
#define UVC_STREAM_FID (1 << 0)
static void uvc_dump_probe_param(MGC_UVC_PROBE_PARAM *ProbeParam)
{
printf("=== MGC_UvcGetProbeControl ===\r\n");
printf("bmHint = 0x%x\r\n", ProbeParam->bmHint);
printf("bFormatIndex = 0x%x\r\n", ProbeParam->bFormatIndex);
printf("bFrameIndex = 0x%x\r\n", ProbeParam->bFrameIndex);
printf("dwFrameInterval = 0x%x\r\n", ProbeParam->dwFrameInterval);
printf("wKeyFrameRate = 0x%x\r\n", ProbeParam->wKeyFrameRate);
printf("wPFrameRate = 0x%x\r\n", ProbeParam->wPFrameRate);
printf("wCompQuality = 0x%x\r\n", ProbeParam->wCompQuality);
printf("wCompWindowSize = 0x%x\r\n", ProbeParam->wCompWindowSize);
printf("wDelay = 0x%x\r\n", ProbeParam->wDelay);
printf("dwMaxVideoFrameSize = 0x%x\r\n", ProbeParam->dwMaxVideoFrameSize);
printf("dwMaxPayloadTransferSize = 0x%x\r\n", ProbeParam->dwMaxPayloadTransferSize);
}
#if 1
static int usb_camera_init(struct usb_device *dev, int *iso_pipe)
{
struct usb_interface *iface;
struct usb_interface *video_stream_iface;
struct usb_endpoint_descriptor *ep_desc;
int i, ret = -1;
char flag_video_ctrl = false;
char flag_video_stream = false;
unsigned char ep_in = 0;
MGC_UVC_PROBE_PARAM ProbeParam = {0};
int alt_if_num = 5;
char data_rcv[128] = {0};
char *pdata = NULL;
for (i = 0; i < USB_MAXINTERFACES; i++) {
iface = &dev->config.if_desc[i];
if (iface->desc.bInterfaceClass == USB_CLASS_VIDEO) {
if (iface->desc.bInterfaceSubClass == 0x01) {
flag_video_ctrl = true;
} else if (iface->desc.bInterfaceSubClass == 0x02) {
flag_video_stream = true;
video_stream_iface = iface;
}
}
}
if (!flag_video_ctrl || !flag_video_stream) {
goto exit;
}
ep_desc = &video_stream_iface->alt_intf[alt_if_num].ep_desc[0];
if ((ep_desc->bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) {
if (ep_desc->bEndpointAddress & USB_DIR_IN) {
ep_in = ep_desc->bEndpointAddress;
usb_set_maxpacket_ep_ex(dev, 1, alt_if_num, 0);
}
}
ret = usb_set_interface(dev, 1, 0);
if (ret < 0) {
goto exit;
}
mdelay(10);
ret= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x81,
0xA1,
0x100,
0x1, data_rcv,
0x1A, 3000);
if (ret < 0) {
goto exit;
} else {
pdata = data_rcv;
ProbeParam.bmHint = (pdata[1]<<8) | pdata[0];
ProbeParam.bFormatIndex = pdata[2];
ProbeParam.bFrameIndex = pdata[3];
ProbeParam.dwFrameInterval = (pdata[7]<<24)|(pdata[6]<<16)|(pdata[5]<<8) | pdata[4];
ProbeParam.wKeyFrameRate = (pdata[9]<<8) | pdata[8];
ProbeParam.wPFrameRate = (pdata[11]<<8) | pdata[10];
ProbeParam.wCompQuality = (pdata[13]<<8) | pdata[12];
ProbeParam.wCompWindowSize = (pdata[15]<<8) | pdata[14];
ProbeParam.wDelay = (pdata[17]<<8) | pdata[16];
ProbeParam.dwMaxVideoFrameSize = (pdata[21]<<24)|(pdata[20]<<16)|(pdata[19]<<8) | pdata[18];
ProbeParam.dwMaxPayloadTransferSize = (pdata[21]<<25)|(pdata[24]<<16)|(pdata[23]<<8) | pdata[22];
uvc_dump_probe_param(&ProbeParam);
}
ProbeParam.bFormatIndex = 2; //1:mpgj; 2:yuy2 [Depend On Format Type Descriptor ]
#if UVC_VIDEO_WIDTH == 1280 && UVC_VIDEO_HEIGHT == 720
ProbeParam.bFrameIndex = 2; //1:1280X720; 5:640*480
#else
ProbeParam.bFrameIndex = 1;
#endif
ProbeParam.dwMaxVideoFrameSize = 0;//720p:0x001C224D; 640*480:0x0009624D
ProbeParam.dwFrameInterval = 0x051615;
pdata = (char *)&ProbeParam;
ret= usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x1,
0x21,
0x100,
0x1, pdata,
0x1A, 3000);
if (ret < 0) {
goto exit;
}
ret= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x81,
0xA1,
0x100,
0x1, data_rcv,
0x1A, 3000);
if (ret < 0) {
} else {
pdata = data_rcv;
ProbeParam.bmHint = (pdata[1]<<8) | pdata[0];
ProbeParam.bFormatIndex = pdata[2];
ProbeParam.bFrameIndex = pdata[3];
ProbeParam.dwFrameInterval = (pdata[7]<<24)|(pdata[6]<<16)|(pdata[5]<<8) | pdata[4];
ProbeParam.wKeyFrameRate = (pdata[9]<<8) | pdata[8];
ProbeParam.wPFrameRate = (pdata[11]<<8) | pdata[10];
ProbeParam.wCompQuality = (pdata[13]<<8) | pdata[12];
ProbeParam.wCompWindowSize = (pdata[15]<<8) | pdata[14];
ProbeParam.wDelay = (pdata[17]<<8) | pdata[16];
ProbeParam.dwMaxVideoFrameSize = (pdata[21]<<24)|(pdata[20]<<16)|(pdata[19]<<8) | pdata[18];
ProbeParam.dwMaxPayloadTransferSize = (pdata[21]<<25)|(pdata[24]<<16)|(pdata[23]<<8) | pdata[22];
uvc_dump_probe_param(&ProbeParam);
}
pdata = (char *)&ProbeParam;
ret= usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x1,
0x21,
0x200,
0x1, pdata,
0x1A, 3000);
if (ret < 0) {
goto exit;
}
mdelay(10);
ret = usb_set_interface(dev, 1, alt_if_num);
if (ret < 0) {
goto exit;
}
if (iso_pipe)
*iso_pipe = usb_rcvisocpipe(dev, ep_in);
exit:
return ret;
}
#else
static void uvc_get_probe_param(MGC_UVC_PROBE_PARAM *ProbeParam, char *pdata)
{
ProbeParam->bmHint = (pdata[1]<<8) | pdata[0];
ProbeParam->bFormatIndex = pdata[2];
ProbeParam->bFrameIndex = pdata[3];
ProbeParam->dwFrameInterval = (pdata[7]<<24)|(pdata[6]<<16)|(pdata[5]<<8) | pdata[4];
ProbeParam->wKeyFrameRate = (pdata[9]<<8) | pdata[8];
ProbeParam->wPFrameRate = (pdata[11]<<8) | pdata[10];
ProbeParam->wCompQuality = (pdata[13]<<8) | pdata[12];
ProbeParam->wCompWindowSize = (pdata[15]<<8) | pdata[14];
ProbeParam->wDelay = (pdata[17]<<8) | pdata[16];
ProbeParam->dwMaxVideoFrameSize = (pdata[21]<<24)|(pdata[20]<<16)|(pdata[19]<<8) | pdata[18];
ProbeParam->dwMaxPayloadTransferSize = (pdata[21]<<25)|(pdata[24]<<16)|(pdata[23]<<8) | pdata[22];
}
static int usb_camera_init(struct usb_device *dev, int *iso_pipe)
{
struct usb_interface *iface;
struct usb_interface *video_stream_iface;
struct usb_endpoint_descriptor *ep_desc;
int i, ret = -1;
char flag_video_ctrl = false;
char flag_video_stream = false;
unsigned char ep_in = 0;
MGC_UVC_PROBE_PARAM ProbeParam = {0};
int alt_if_num = 5;
char data_rcv[128] = {0};
char *pdata = NULL;
for (i = 0; i < USB_MAXINTERFACES; i++) {
iface = &dev->config.if_desc[i];
if (iface->desc.bInterfaceClass == USB_CLASS_VIDEO) {
if (iface->desc.bInterfaceSubClass == 0x01) {
flag_video_ctrl = true;
} else if (iface->desc.bInterfaceSubClass == 0x02) {
flag_video_stream = true;
video_stream_iface = iface;
}
}
}
if (!flag_video_ctrl || !flag_video_stream) {
goto exit;
}
ep_desc = &video_stream_iface->alt_intf[alt_if_num].ep_desc[0];
if ((ep_desc->bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) {
if (ep_desc->bEndpointAddress & USB_DIR_IN) {
ep_in = ep_desc->bEndpointAddress;
usb_set_maxpacket_ep_ex(dev, 1, alt_if_num, 0);
}
}
ret = usb_set_interface(dev, 1, 0);
if (ret < 0) {
goto exit;
}
mdelay(10);
ret= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x81,
0xA1,
0x100,
0x1, data_rcv,
0x1A, 3000);
if (ret < 0) {
goto exit;
} else {
uvc_get_probe_param(&ProbeParam, data_rcv);
uvc_dump_probe_param(&ProbeParam);
}
ProbeParam.bFormatIndex = 1; //1:mpgj; 2:yuy2 [Depend On Format Type Descriptor ]
#if UVC_VIDEO_WIDTH == 1280 && UVC_VIDEO_HEIGHT == 720
ProbeParam.bFrameIndex = 9; //1:1280X720; 5:640*480
#else
ProbeParam.bFrameIndex = 1;
#endif
ProbeParam.dwMaxVideoFrameSize = 0;//720p:0x001C224D; 640*480:0x0009624D
ProbeParam.dwFrameInterval = 0x051615;
pdata = (char *)&ProbeParam;
ret= usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x1,
0x21,
0x100,
0x1, pdata,
0x1A, 3000);
if (ret < 0) {
goto exit;
}
ret= usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x81,
0xA1,
0x100,
0x1, data_rcv,
0x1A, 3000);
if (ret < 0) {
} else {
uvc_get_probe_param(&ProbeParam, data_rcv);
uvc_dump_probe_param(&ProbeParam);
}
pdata = (char *)&ProbeParam;
ret= usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 0x1,
0x21,
0x200,
0x1, pdata,
0x1A, 3000);
if (ret < 0) {
goto exit;
}
mdelay(10);
ret = usb_set_interface(dev, 1, alt_if_num);
if (ret < 0) {
goto exit;
}
if (iso_pipe)
*iso_pipe = usb_rcvisocpipe(dev, ep_in);
exit:
return ret;
}
#endif
static void uvc_frame_list_reset()
{
video_frame_s* frame = NULL;
ListItem_t *pxListItem;
list_for_each_entry(pxListItem, frame, &frame_free_list) {
frame->cur = frame->buf;
frame->len = 0;
}
}
static void uvc_data_receive_proc(void *pvParameters)
{
int ret = -1;
int iso_buf_len = sizeof(iso_buf);
int header_len = 0, payload_len = 0, frame_len = 0;
uint8_t header_info = 0;
uint8_t* payload = NULL;
uint8_t* buf = (uint8_t*)iso_buf;
uint8_t* frame_buf = (uint8_t*)iso_buf;
video_frame_s* frame = NULL;
ListItem_t* item = NULL;
struct usb_device *dev = (struct usb_device *)pvParameters;
int iso_pipe = -1;
if (NULL == dev)
goto exit;
ret = usb_camera_init(dev, &iso_pipe);
if (ret < 0)
goto exit;
while(1) {
if (!g_uvc_dev_ready)
break;
if (list_empty(&frame_free_list)) {
continue;
}
ret = usb_iso_msg(dev, iso_pipe, (void*)buf, iso_buf_len, &payload_len, (int)portMAX_DELAY);
if (ret < 0)
break;
if (payload_len <= 0)
continue;
frame_buf = buf;
payload = (uint8_t*)frame_buf;
header_len = payload[0];
if (header_len > payload_len) {
printf("err pack\r\n");
}
if (header_len < 2) {
header_info = 0;
} else {
header_info = payload[1];
if (header_info & 0x40) {
printf("bad pack: error bit set\r\n");
continue;
}
if ((header_info & UVC_STREAM_FID) != fid) {
fid = (header_info & UVC_STREAM_FID);
//printf("new video frame start\r\n");
}
frame_len = (payload_len - header_len);
frame = list_first_entry(&frame_free_list);
frame_buf += header_len;
memcpy((void *)frame->cur, frame_buf, frame_len);
frame->cur += frame_len;
frame->len += frame_len;
if(header_info & UVC_STREAM_EOF) {
xSemaphoreTake(frame_list_mutex, portMAX_DELAY);
item = listGET_HEAD_ENTRY(&frame_free_list);
uxListRemove(item);
xSemaphoreGive(frame_list_mutex);
xQueueSend(jpeg_frame_queue, (void*)&frame, 0);
//printf("video frame end\r\n");
}
}
}
exit:
frame = NULL;
xQueueSend(jpeg_frame_queue, (void*)&frame, 0);
xQueueSend(iso_exit, NULL, 0);
vTaskDelete(NULL);
}
static void dec_jpeg_task_proc(void *pvParameters)
{
video_frame_s* frame = NULL;
uint32_t yaddr, uvaddr, vaddr, dstaddr;
int format;
uint32_t ts = get_timer(0);
int uvc_lcd_take = 0;
char *yuv_buf = jpeg_yuv_buf;
#if SAVE_UVC_FRAME
int i = 0;
char filename[32] = {0};
#endif
while(uvc_tast_start) {
xQueueReceive(jpeg_frame_queue, (void*)&frame, portMAX_DELAY);
if (frame == NULL) {
if (uvc_lcd_take) {
ark_lcd_osd_enable(LCD_VIDEO_LAYER, 0);
ark_lcd_set_osd_sync(LCD_VIDEO_LAYER);
vVideoDisplayBufGive();
ark_lcd_osd_enable(LCD_UI_LAYER, 1);
ark_lcd_set_osd_sync(LCD_UI_LAYER);
uvc_lcd_take = 0;
}
xSemaphoreTake(frame_list_mutex, portMAX_DELAY);
uvc_frame_list_reset();
xSemaphoreGive(frame_list_mutex);
xQueueReset(jpeg_frame_queue);
continue;
}
if (uvc_tast_start == 0)
break;
CP15_clean_dcache_for_dma((uint32_t)frame->buf, (uint32_t)(frame->buf + frame->len));
#if SAVE_UVC_FRAME
sprintf(filename, "/sd/uvc/%05d.jpg", i);
if (i++ % 50 == 0) {
FF_FILE *fp = ff_fopen(filename, "w+");
if (fp) {
int ret = ff_fwrite((void*)frame->buf, 1, frame->len, fp);
if (ret < 0){
printf("write failed\r\n");
}
ff_fclose(fp);
} else {
printf("open %s failed\r\n", filename);
}
}
#endif
JpegHeaderInfo jpginfo = {0};
jpginfo.jpg_addr = (uint32_t)frame->buf;
jpginfo.dec_addry = (uint32_t)yuv_buf;
jpginfo.dec_size = (uint32_t)frame->yuv_len;
ts = get_timer(0);
if (jpeg_dec(&jpginfo) < 0) {
printf("dec jpeg failed use %ld\r\n", get_timer(ts));
} else {
yaddr = jpginfo.dec_addry;
uvaddr = jpginfo.dec_addru;
vaddr = jpginfo.dec_addrv;
format = PXP_SRC_FMT_YUV422;
if (uvc_lcd_take == 0) {
uvc_lcd_take = 1;
xVideoDisplayBufTake(portMAX_DELAY);
}
if (uvc_lcd_take) {
if (!ark_lcd_get_osd_info_atomic_isactive(LCD_VIDEO_LAYER)) {
ark_lcd_wait_for_vsync();
}
dstaddr = ulVideoDisplayBufGet();
pxp_scaler_rotate(yaddr, uvaddr, vaddr, format, jpginfo.width, jpginfo.height,
dstaddr, 0, PXP_OUT_FMT_RGB565, LCD_WIDTH, LCD_HEIGHT, 0);
LcdOsdInfo info = {0};
info.x = 0;
info.y = 0;
info.width = LCD_WIDTH;
info.height = LCD_HEIGHT;
info.format = LCD_OSD_FORAMT_RGB565;
info.yaddr = dstaddr;
ark_lcd_set_osd_info_atomic(LCD_VIDEO_LAYER, &info);
ark_lcd_osd_enable(LCD_VIDEO_LAYER, 1);
ark_lcd_set_osd_sync(LCD_VIDEO_LAYER);
ark_lcd_osd_enable(LCD_UI_LAYER, 0);
ark_lcd_set_osd_sync(LCD_UI_LAYER);
vVideoDisplayBufRender(dstaddr);
}
//printf("proc frame:%d use %ld \r\n", frame->len, get_timer(ts));
}
xSemaphoreTake(frame_list_mutex, portMAX_DELAY);
frame->cur = frame->buf;
frame->len = 0;
vListInsertEnd(&frame_free_list, &frame->entry);
xSemaphoreGive(frame_list_mutex);
}
}
int usb_uvc_probe(struct usb_device *dev)
{
struct usb_interface *iface;
int i;
char flag_video_ctrl = false;
char flag_video_stream = false;
for (i = 0; i < USB_MAXINTERFACES; i++) {
iface = &dev->config.if_desc[i];
if (iface->desc.bInterfaceClass == USB_CLASS_VIDEO) {
if (iface->desc.bInterfaceSubClass == 0x01) {
flag_video_ctrl = true;
} else if (iface->desc.bInterfaceSubClass == 0x02) {
flag_video_stream = true;
}
}
}
if (!flag_video_ctrl || !flag_video_stream) {
return 0;
}
printf("found UVC device!\r\n");
g_uvc_dev_ready = 1;
xTaskCreate(uvc_data_receive_proc, "uvc_data_receive_proc", configMINIMAL_STACK_SIZE * 1, (void*)dev, configMAX_PRIORITIES, &iso_read_handle);
return 1;
}
/* <20><>ʼ<EFBFBD><CABC>jpeg <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
static int g_uvc_init_flag = 0;
void uvc_jpeg_buf_init(void)
{
video_frame_s *buffer;
int i;
if (g_uvc_init_flag)
return;
vListInitialise(&frame_free_list);
buffer = (video_frame_s *)jpeg_fifos;
jpeg_buf = pvPortMalloc(UVC0_JPEG_FIFO_COUNT * UVC0_JPEG_BUF_SIZE);
if (jpeg_buf == NULL) {
printf("malloc jpeg buf failed\r\n");
return;
}
jpeg_yuv_buf = pvPortMalloc(UVC0_JPEG_YUV_BUF_SIZE);
memset((void *)jpeg_yuv_buf, 0, UVC0_JPEG_YUV_BUF_SIZE);
if (jpeg_yuv_buf == NULL) {
printf("malloc jpeg yuv buf failed\r\n");
vPortFree(jpeg_buf);
return;
}
for(i = 0; i < UVC0_JPEG_FIFO_COUNT; i++) {
buffer->buf = (char *)(((unsigned int)jpeg_buf + i * UVC0_JPEG_BUF_SIZE + 1023)&(~1023));
buffer->cur = buffer->buf;
//buffer->yuv_buf = (char *)(((unsigned int)jpeg_yuv_buf + i * UVC0_JPEG_YUV_BUF_SIZE + 1023)&(~1023));
buffer->yuv_len = UVC_VIDEO_WIDTH * UVC_VIDEO_HEIGHT * 2;
buffer->lock = 0;
buffer->frame_id = i;
buffer->len = 0;
vListInitialiseItem(&buffer->entry);
vListInsertEnd(&frame_free_list, &buffer->entry);
listSET_LIST_ITEM_OWNER(&buffer->entry, buffer);
buffer++;
}
g_uvc_init_flag = 0;
}
int usb_uvc_scan()
{
unsigned char i = 0;
struct usb_device *dev = NULL;
int ret = 0;
for (i = 0; i < USB_MAX_DEVICE; i++) {
dev = usb_get_dev_index(i);
if (usb_uvc_probe(dev)) {
ret = 1;
break;
}
}
return ret;
}
void usb_uvc_disconnect()
{
if (g_uvc_dev_ready != 0) {
g_uvc_dev_ready = 0;
xQueueReceive(iso_exit, NULL, portMAX_DELAY);
}
xQueueReset(iso_exit);
}
int usb_uvc_init()
{
BaseType_t ret = -1;
uvc_jpeg_buf_init();
frame_list_mutex = xSemaphoreCreateMutex();
jpeg_frame_queue = xQueueCreate(UVC0_JPEG_FIFO_COUNT, sizeof(uint32_t));
iso_exit = xQueueCreate(1, 0);
uvc_tast_start = 1;
ret = xTaskCreate(dec_jpeg_task_proc, "dec_jpeg_task_task", configMINIMAL_STACK_SIZE * 1, NULL, configMAX_PRIORITIES, &dec_jpeg_handle);
return (int)ret;
}
#else
int usb_uvc_init()
{
return 0;
}
int usb_uvc_scan()
{
return 0;
}
void usb_uvc_disconnect()
{
}
#endif

View File

@ -0,0 +1,8 @@
#ifndef __USB_UVC_H
#define __USB_UVC_H
int usb_uvc_scan(void);
void usb_uvc_disconnect(void);
int usb_uvc_init(void);
#endif

View File

@ -0,0 +1,155 @@
#include <stdio.h>
#include "FreeRTOS.h"
#include "chip.h"
#include "errno.h"
#define WTCON 0x00
#define WTPSR 0x04
#define WTCNT 0x08
#define WTCLRINT 0x10
#define WTRCR 0x14
#define WTCNT_MAXCNT 0xffff
#define WTCON_MAXDIV 0x80
#define WTCON_ENABLE (1 << 0)
#define WTCON_RSTEN (1 << 1)
#define WTCON_INTEN (1 << 2)
#define WTCON_DIV16 (0 << 4)
#define WTCON_DIV32 (1 << 4)
#define WTCON_DIV64 (2 << 4)
#define WTCON_DIV128 (3 << 4)
#define WTCON_DIVMASK (0x3 << 4)
#define WTCON_PRESCALE_MAX 0x10000
#define WATCHDOG_DEFAULT_TIME (15)
static int wdt_timeout = WATCHDOG_DEFAULT_TIME;
static int soft_noboot = 1; /* 1: not reboot when watchdog timer expired */
static int wdt_count;
static __INLINE unsigned int wdt_max_timeout()
{
unsigned long freq = ulClkGetRate(CLK_APB);
return WTCNT_MAXCNT / (freq / WTCON_PRESCALE_MAX
/ WTCON_MAXDIV);
}
int wdt_set_heartbeat(unsigned int timeout)
{
unsigned long freq = ulClkGetRate(CLK_APB);
unsigned int count;
unsigned int divisor = 1;
if (timeout < 1 || timeout > wdt_max_timeout())
return -EINVAL;
freq = DIV_ROUND_UP(freq, 128);
count = timeout * freq;
TRACE_DEBUG("Heartbeat: count=%d, timeout=%d, freq=%lu\n",
count, timeout, freq);
/* if the count is bigger than the watchdog register,
then work out what we need to do (and if) we can
actually make this value
*/
if (count >= 0x10000) {
divisor = DIV_ROUND_UP(count, 0xffff);
if (divisor > WTCON_PRESCALE_MAX) {
TRACE_ERROR("timeout %d too big\n", timeout);
return -EINVAL;
}
}
TRACE_DEBUG("Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
timeout, divisor, count, DIV_ROUND_UP(count, divisor));
count = DIV_ROUND_UP(count, divisor);
wdt_count = count;
/* update the pre-scaler */
writel(divisor - 1, REGS_WDT_BASE + WTPSR);
writel(count, REGS_WDT_BASE + WTCNT);
wdt_timeout = timeout;
return 0;
}
void wdt_stop(void)
{
unsigned long wtcon;
wtcon = readl(REGS_WDT_BASE + WTCON);
wtcon &= ~(WTCON_ENABLE | WTCON_RSTEN);
writel(wtcon, REGS_WDT_BASE + WTCON);
}
void wdt_start(void)
{
unsigned long wtcon;
wdt_stop();
wtcon = readl(REGS_WDT_BASE + WTCON);
wtcon &= ~WTCON_DIVMASK;
wtcon |= WTCON_ENABLE | WTCON_DIV128;
if (soft_noboot) {
wtcon |= WTCON_INTEN;
wtcon &= ~WTCON_RSTEN;
} else {
wtcon &= ~WTCON_INTEN;
wtcon |= WTCON_RSTEN;
}
TRACE_DEBUG("Starting watchdog: count=0x%08x, wtcon=%08lx\n",
wdt_count, wtcon);
writel(wdt_count, REGS_WDT_BASE + WTCNT);
writel(wtcon, REGS_WDT_BASE + WTCON);
}
void ark_wdt_keepalive(void)
{
writel(wdt_count, REGS_WDT_BASE + WTCNT);
}
void wdt_isr(void *para)
{
TRACE_DEBUG("watchdog timer expired (irq)\n");
ark_wdt_keepalive();
writel(0x1, REGS_WDT_BASE + WTCLRINT);
}
int wdt_init(void)
{
int ret;
ret = wdt_set_heartbeat(wdt_timeout);
if (ret) {
TRACE_ERROR("failed to set timeout value\n");
}
request_irq(WDT_IRQn, 0, wdt_isr, NULL);
wdt_start();
return 0;
}
void wdt_cpu_reboot(void)
{
printf("cpu reboot...\n");
soft_noboot = 0;
wdt_count = 300;
wdt_start();
while(1);
}