import RT-Thread@9217865c without bsp, libcpu and components/net

This commit is contained in:
Zihao Yu 2023-05-20 16:23:33 +08:00
commit e2376a3709
1414 changed files with 390370 additions and 0 deletions

View file

@ -0,0 +1,41 @@
from building import *
import rtconfig
cwd = GetCurrentDir()
src = ['spi_core.c', 'spi_dev.c']
CPPPATH = [cwd, cwd + '/../include']
LOCAL_CFLAGS = ''
if GetDepend('RT_USING_SPI_BITOPS'):
src += ['spi-bit-ops.c']
if GetDepend('RT_USING_QSPI'):
src += ['qspi_core.c']
src_device = []
if GetDepend('RT_USING_SPI_WIFI'):
src_device += ['spi_wifi_rw009.c']
if GetDepend('RT_USING_ENC28J60'):
src_device += ['enc28j60.c']
if GetDepend('RT_USING_SPI_MSD'):
src_device += ['spi_msd.c']
if GetDepend('RT_USING_SFUD'):
src_device += ['spi_flash_sfud.c', 'sfud/src/sfud.c']
CPPPATH += [cwd + '/sfud/inc']
if GetDepend('RT_SFUD_USING_SFDP'):
src_device += ['sfud/src/sfud_sfdp.c']
if rtconfig.PLATFORM in ['gcc', 'armclang', 'llvm-arm']:
LOCAL_CFLAGS += ' -std=c99'
elif rtconfig.PLATFORM in ['armcc']:
LOCAL_CFLAGS += ' --c99'
src += src_device
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_SPI'], CPPPATH = CPPPATH, LOCAL_CFLAGS = LOCAL_CFLAGS)
Return('group')

View file

@ -0,0 +1,899 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include "enc28j60.h"
/* #define NET_TRACE */
/* #define ETH_RX_DUMP */
/* #define ETH_TX_DUMP */
#ifdef NET_TRACE
#define NET_DEBUG rt_kprintf
#else
#define NET_DEBUG(...)
#endif /* #ifdef NET_TRACE */
struct enc28j60_tx_list_typedef
{
struct enc28j60_tx_list_typedef *prev;
struct enc28j60_tx_list_typedef *next;
rt_uint32_t addr; /* pkt addr in buffer */
rt_uint32_t len; /* pkt len */
volatile rt_bool_t free; /* 0:busy, 1:free */
};
static struct enc28j60_tx_list_typedef enc28j60_tx_list[2];
static volatile struct enc28j60_tx_list_typedef *tx_current;
static volatile struct enc28j60_tx_list_typedef *tx_ack;
static struct rt_event tx_event;
/* private enc28j60 define */
/* enc28j60 spi interface function */
static uint8_t spi_read_op(struct rt_spi_device *spi_device, uint8_t op, uint8_t address);
static void spi_write_op(struct rt_spi_device *spi_device, uint8_t op, uint8_t address, uint8_t data);
static uint8_t spi_read(struct rt_spi_device *spi_device, uint8_t address);
static void spi_write(struct rt_spi_device *spi_device, rt_uint8_t address, rt_uint8_t data);
static void enc28j60_clkout(struct rt_spi_device *spi_device, rt_uint8_t clk);
static void enc28j60_set_bank(struct rt_spi_device *spi_device, uint8_t address);
static uint32_t enc28j60_interrupt_disable(struct rt_spi_device *spi_device);
static void enc28j60_interrupt_enable(struct rt_spi_device *spi_device, uint32_t level);
static uint16_t enc28j60_phy_read(struct rt_spi_device *spi_device, rt_uint8_t address);
static void enc28j60_phy_write(struct rt_spi_device *spi_device, rt_uint8_t address, uint16_t data);
static rt_bool_t enc28j60_check_link_status(struct rt_spi_device *spi_device);
#define enc28j60_lock(dev) rt_mutex_take(&((struct net_device*)dev)->lock, RT_WAITING_FOREVER);
#define enc28j60_unlock(dev) rt_mutex_release(&((struct net_device*)dev)->lock);
static struct net_device enc28j60_dev;
static uint8_t Enc28j60Bank;
//struct rt_spi_device * spi_device;
static uint16_t NextPacketPtr;
static void _delay_us(uint32_t us)
{
volatile uint32_t len;
for (; us > 0; us --)
for (len = 0; len < 20; len++);
}
/* enc28j60 spi interface function */
static uint8_t spi_read_op(struct rt_spi_device *spi_device, uint8_t op, uint8_t address)
{
uint8_t send_buffer[2];
uint8_t recv_buffer[1];
uint32_t send_size = 1;
send_buffer[0] = op | (address & ADDR_MASK);
send_buffer[1] = 0xFF;
/* do dummy read if needed (for mac and mii, see datasheet page 29). */
if (address & 0x80)
{
send_size = 2;
}
rt_spi_send_then_recv(spi_device, send_buffer, send_size, recv_buffer, 1);
return (recv_buffer[0]);
}
static void spi_write_op(struct rt_spi_device *spi_device, uint8_t op, uint8_t address, uint8_t data)
{
rt_base_t level;
uint8_t buffer[2];
level = rt_hw_interrupt_disable();
buffer[0] = op | (address & ADDR_MASK);
buffer[1] = data;
rt_spi_send(spi_device, buffer, 2);
rt_hw_interrupt_enable(level);
}
/* enc28j60 function */
static void enc28j60_clkout(struct rt_spi_device *spi_device, rt_uint8_t clk)
{
/* setup clkout: 2 is 12.5MHz: */
spi_write(spi_device, ECOCON, clk & 0x7);
}
static void enc28j60_set_bank(struct rt_spi_device *spi_device, uint8_t address)
{
/* set the bank (if needed) .*/
if ((address & BANK_MASK) != Enc28j60Bank)
{
/* set the bank. */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1 | ECON1_BSEL0));
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK) >> 5);
Enc28j60Bank = (address & BANK_MASK);
}
}
static uint8_t spi_read(struct rt_spi_device *spi_device, uint8_t address)
{
/* set the bank. */
enc28j60_set_bank(spi_device, address);
/* do the read. */
return spi_read_op(spi_device, ENC28J60_READ_CTRL_REG, address);
}
static void spi_write(struct rt_spi_device *spi_device, rt_uint8_t address, rt_uint8_t data)
{
/* set the bank. */
enc28j60_set_bank(spi_device, address);
/* do the write. */
spi_write_op(spi_device, ENC28J60_WRITE_CTRL_REG, address, data);
}
static uint16_t enc28j60_phy_read(struct rt_spi_device *spi_device, rt_uint8_t address)
{
uint16_t value;
/* Set the right address and start the register read operation. */
spi_write(spi_device, MIREGADR, address);
spi_write(spi_device, MICMD, MICMD_MIIRD);
_delay_us(15);
/* wait until the PHY read completes. */
while (spi_read(spi_device, MISTAT) & MISTAT_BUSY);
/* reset reading bit */
spi_write(spi_device, MICMD, 0x00);
value = spi_read(spi_device, MIRDL) | spi_read(spi_device, MIRDH) << 8;
return (value);
}
static void enc28j60_phy_write(struct rt_spi_device *spi_device, rt_uint8_t address, uint16_t data)
{
/* set the PHY register address. */
spi_write(spi_device, MIREGADR, address);
/* write the PHY data. */
spi_write(spi_device, MIWRL, data);
spi_write(spi_device, MIWRH, data >> 8);
/* wait until the PHY write completes. */
while (spi_read(spi_device, MISTAT) & MISTAT_BUSY)
{
_delay_us(15);
}
}
static uint32_t enc28j60_interrupt_disable(struct rt_spi_device *spi_device)
{
uint32_t level;
/* switch to bank 0 */
enc28j60_set_bank(spi_device, EIE);
/* get last interrupt level */
level = spi_read(spi_device, EIE);
/* disable interrutps */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIE, level);
return level;
}
static void enc28j60_interrupt_enable(struct rt_spi_device *spi_device, uint32_t level)
{
/* switch to bank 0 */
enc28j60_set_bank(spi_device, EIE);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, EIE, level);
}
/*
* Access the PHY to determine link status
*/
static rt_bool_t enc28j60_check_link_status(struct rt_spi_device *spi_device)
{
uint16_t reg;
reg = enc28j60_phy_read(spi_device, PHSTAT2);
if (reg & PHSTAT2_LSTAT)
{
/* on */
return RT_TRUE;
}
else
{
/* off */
return RT_FALSE;
}
}
/************************* RT-Thread Device Interface *************************/
void enc28j60_isr(void)
{
eth_device_ready(&enc28j60_dev.parent);
NET_DEBUG("enc28j60_isr\r\n");
}
static void _tx_chain_init(void)
{
enc28j60_tx_list[0].next = &enc28j60_tx_list[1];
enc28j60_tx_list[1].next = &enc28j60_tx_list[0];
enc28j60_tx_list[0].prev = &enc28j60_tx_list[1];
enc28j60_tx_list[1].prev = &enc28j60_tx_list[0];
enc28j60_tx_list[0].addr = TXSTART_INIT;
enc28j60_tx_list[1].addr = TXSTART_INIT + MAX_TX_PACKAGE_SIZE;
enc28j60_tx_list[0].free = RT_TRUE;
enc28j60_tx_list[1].free = RT_TRUE;
tx_current = &enc28j60_tx_list[0];
tx_ack = tx_current;
}
/* initialize the interface */
static rt_err_t enc28j60_init(rt_device_t dev)
{
struct net_device *enc28j60 = (struct net_device *)dev;
struct rt_spi_device *spi_device = enc28j60->spi_device;
enc28j60_lock(dev);
_tx_chain_init();
// perform system reset
spi_write_op(spi_device, ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
rt_thread_delay(RT_TICK_PER_SECOND / 50); /* delay 20ms */
NextPacketPtr = RXSTART_INIT;
// Rx start
spi_write(spi_device, ERXSTL, RXSTART_INIT & 0xFF);
spi_write(spi_device, ERXSTH, RXSTART_INIT >> 8);
// set receive pointer address
spi_write(spi_device, ERXRDPTL, RXSTOP_INIT & 0xFF);
spi_write(spi_device, ERXRDPTH, RXSTOP_INIT >> 8);
// RX end
spi_write(spi_device, ERXNDL, RXSTOP_INIT & 0xFF);
spi_write(spi_device, ERXNDH, RXSTOP_INIT >> 8);
// TX start
spi_write(spi_device, ETXSTL, TXSTART_INIT & 0xFF);
spi_write(spi_device, ETXSTH, TXSTART_INIT >> 8);
// set transmission pointer address
spi_write(spi_device, EWRPTL, TXSTART_INIT & 0xFF);
spi_write(spi_device, EWRPTH, TXSTART_INIT >> 8);
// TX end
spi_write(spi_device, ETXNDL, TXSTOP_INIT & 0xFF);
spi_write(spi_device, ETXNDH, TXSTOP_INIT >> 8);
// do bank 1 stuff, packet filter:
// For broadcast packets we allow only ARP packtets
// All other packets should be unicast only for our mac (MAADR)
//
// The pattern to match on is therefore
// Type ETH.DST
// ARP BROADCAST
// 06 08 -- ff ff ff ff ff ff -> ip checksum for theses bytes=f7f9
// in binary these poitions are:11 0000 0011 1111
// This is hex 303F->EPMM0=0x3f,EPMM1=0x30
spi_write(spi_device, ERXFCON, ERXFCON_UCEN | ERXFCON_CRCEN | ERXFCON_BCEN);
// do bank 2 stuff
// enable MAC receive
spi_write(spi_device, MACON1, MACON1_MARXEN | MACON1_TXPAUS | MACON1_RXPAUS);
// enable automatic padding to 60bytes and CRC operations
// spi_write_op(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN | MACON3_FULDPX);
// bring MAC out of reset
// set inter-frame gap (back-to-back)
// spi_write(MABBIPG, 0x12);
spi_write(spi_device, MABBIPG, 0x15);
spi_write(spi_device, MACON4, MACON4_DEFER);
spi_write(spi_device, MACLCON2, 63);
// set inter-frame gap (non-back-to-back)
spi_write(spi_device, MAIPGL, 0x12);
spi_write(spi_device, MAIPGH, 0x0C);
// Set the maximum packet size which the controller will accept
// Do not send packets longer than MAX_FRAMELEN:
spi_write(spi_device, MAMXFLL, MAX_FRAMELEN & 0xFF);
spi_write(spi_device, MAMXFLH, MAX_FRAMELEN >> 8);
// do bank 3 stuff
// write MAC address
// NOTE: MAC address in ENC28J60 is byte-backward
spi_write(spi_device, MAADR0, enc28j60->dev_addr[5]);
spi_write(spi_device, MAADR1, enc28j60->dev_addr[4]);
spi_write(spi_device, MAADR2, enc28j60->dev_addr[3]);
spi_write(spi_device, MAADR3, enc28j60->dev_addr[2]);
spi_write(spi_device, MAADR4, enc28j60->dev_addr[1]);
spi_write(spi_device, MAADR5, enc28j60->dev_addr[0]);
/* output off */
spi_write(spi_device, ECOCON, 0x00);
// enc28j60_phy_write(PHCON1, 0x00);
enc28j60_phy_write(spi_device, PHCON1, PHCON1_PDPXMD); // full duplex
// no loopback of transmitted frames
enc28j60_phy_write(spi_device, PHCON2, PHCON2_HDLDIS);
/* enable PHY link changed interrupt. */
enc28j60_phy_write(spi_device, PHIE, PHIE_PGEIE | PHIE_PLNKIE);
enc28j60_set_bank(spi_device, ECON2);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON2, ECON2_AUTOINC);
// switch to bank 0
enc28j60_set_bank(spi_device, ECON1);
// enable all interrutps
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, EIE, 0xFF);
// enable packet reception
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
/* clock out */
enc28j60_clkout(spi_device, 2);
enc28j60_phy_write(spi_device, PHLCON, 0xD76); //0x476
rt_thread_delay(RT_TICK_PER_SECOND / 50); /* delay 20ms */
enc28j60_unlock(dev);
return RT_EOK;
}
/* control the interface */
static rt_err_t enc28j60_control(rt_device_t dev, int cmd, void *args)
{
struct net_device *enc28j60 = (struct net_device *)dev;
switch (cmd)
{
case NIOCTL_GADDR:
/* get mac address */
if (args) rt_memcpy(args, enc28j60->dev_addr, 6);
else return -RT_ERROR;
break;
default :
break;
}
return RT_EOK;
}
/* Open the ethernet interface */
static rt_err_t enc28j60_open(rt_device_t dev, uint16_t oflag)
{
return RT_EOK;
}
/* Close the interface */
static rt_err_t enc28j60_close(rt_device_t dev)
{
return RT_EOK;
}
/* Read */
static rt_ssize_t enc28j60_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return RT_EOK;
}
/* Write */
static rt_ssize_t enc28j60_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return 0;
}
/* ethernet device interface */
/* Transmit packet. */
static rt_err_t enc28j60_tx(rt_device_t dev, struct pbuf *p)
{
struct net_device *enc28j60 = (struct net_device *)dev;
struct rt_spi_device *spi_device = enc28j60->spi_device;
struct pbuf *q;
rt_uint32_t level;
#ifdef ETH_TX_DUMP
rt_size_t dump_count = 0;
rt_uint8_t *dump_ptr;
rt_size_t dump_i;
#endif
if (tx_current->free == RT_FALSE)
{
NET_DEBUG("[Tx] no empty buffer!\r\n");
while (tx_current->free == RT_FALSE)
{
rt_err_t result;
rt_uint32_t recved;
/* there is no block yet, wait a flag */
result = rt_event_recv(&tx_event, 0x01,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recved);
RT_ASSERT(result == RT_EOK);
}
NET_DEBUG("[Tx] wait empty buffer done!\r\n");
}
enc28j60_lock(dev);
/* disable enc28j60 interrupt */
level = enc28j60_interrupt_disable(spi_device);
// Set the write pointer to start of transmit buffer area
// spi_write(EWRPTL, TXSTART_INIT&0xFF);
// spi_write(EWRPTH, TXSTART_INIT>>8);
spi_write(spi_device, EWRPTL, (tx_current->addr) & 0xFF);
spi_write(spi_device, EWRPTH, (tx_current->addr) >> 8);
// Set the TXND pointer to correspond to the packet size given
tx_current->len = p->tot_len;
// spi_write(ETXNDL, (TXSTART_INIT+ p->tot_len + 1)&0xFF);
// spi_write(ETXNDH, (TXSTART_INIT+ p->tot_len + 1)>>8);
// write per-packet control byte (0x00 means use macon3 settings)
spi_write_op(spi_device, ENC28J60_WRITE_BUF_MEM, 0, 0x00);
#ifdef ETH_TX_DUMP
NET_DEBUG("tx_dump, size:%d\r\n", p->tot_len);
#endif
for (q = p; q != NULL; q = q->next)
{
uint8_t cmd = ENC28J60_WRITE_BUF_MEM;
rt_spi_send_then_send(enc28j60->spi_device, &cmd, 1, q->payload, q->len);
#ifdef ETH_RX_DUMP
dump_ptr = q->payload;
for (dump_i = 0; dump_i < q->len; dump_i++)
{
NET_DEBUG("%02x ", *dump_ptr);
if (((dump_count + 1) % 8) == 0)
{
NET_DEBUG(" ");
}
if (((dump_count + 1) % 16) == 0)
{
NET_DEBUG("\r\n");
}
dump_count++;
dump_ptr++;
}
#endif
}
#ifdef ETH_RX_DUMP
NET_DEBUG("\r\n");
#endif
// send the contents of the transmit buffer onto the network
if (tx_current == tx_ack)
{
NET_DEBUG("[Tx] stop, restart!\r\n");
// TX start
spi_write(spi_device, ETXSTL, (tx_current->addr) & 0xFF);
spi_write(spi_device, ETXSTH, (tx_current->addr) >> 8);
// TX end
spi_write(spi_device, ETXNDL, (tx_current->addr + tx_current->len) & 0xFF);
spi_write(spi_device, ETXNDH, (tx_current->addr + tx_current->len) >> 8);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
else
{
NET_DEBUG("[Tx] busy, add to chain!\r\n");
}
tx_current->free = RT_FALSE;
tx_current = tx_current->next;
/* Reset the transmit logic problem. See Rev. B4 Silicon Errata point 12. */
if ((spi_read(spi_device, EIR) & EIR_TXERIF))
{
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
}
/* enable enc28j60 interrupt */
enc28j60_interrupt_enable(spi_device, level);
enc28j60_unlock(dev);
return RT_EOK;
}
/* recv packet. */
static struct pbuf *enc28j60_rx(rt_device_t dev)
{
struct net_device *enc28j60 = (struct net_device *)dev;
struct rt_spi_device *spi_device = enc28j60->spi_device;
struct pbuf *p = RT_NULL;
uint8_t eir, eir_clr;
uint32_t pk_counter;
rt_uint32_t level;
rt_uint32_t len;
rt_uint16_t rxstat;
enc28j60_lock(dev);
/* disable enc28j60 interrupt */
level = enc28j60_interrupt_disable(spi_device);
/* get EIR */
eir = spi_read(spi_device, EIR);
while (eir & ~EIR_PKTIF)
{
eir_clr = 0;
/* clear PKTIF */
if (eir & EIR_PKTIF)
{
NET_DEBUG("EIR_PKTIF\r\n");
/* switch to bank 0. */
enc28j60_set_bank(spi_device, EIE);
/* disable rx interrutps. */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIE, EIE_PKTIE);
eir_clr |= EIR_PKTIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_PKTIF);
}
/* clear DMAIF */
if (eir & EIR_DMAIF)
{
NET_DEBUG("EIR_DMAIF\r\n");
eir_clr |= EIR_DMAIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_DMAIF);
}
/* LINK changed handler */
if (eir & EIR_LINKIF)
{
rt_bool_t link_status;
NET_DEBUG("EIR_LINKIF\r\n");
link_status = enc28j60_check_link_status(spi_device);
/* read PHIR to clear the flag */
enc28j60_phy_read(spi_device, PHIR);
eir_clr |= EIR_LINKIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_LINKIF);
eth_device_linkchange(&(enc28j60->parent), link_status);
}
if (eir & EIR_TXIF)
{
/* A frame has been transmitted. */
enc28j60_set_bank(spi_device, EIR);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXIF);
tx_ack->free = RT_TRUE;
tx_ack = tx_ack->next;
if (tx_ack->free == RT_FALSE)
{
NET_DEBUG("[tx isr] Tx chain not empty, continue send the next pkt!\r\n");
// TX start
spi_write(spi_device, ETXSTL, (tx_ack->addr) & 0xFF);
spi_write(spi_device, ETXSTH, (tx_ack->addr) >> 8);
// TX end
spi_write(spi_device, ETXNDL, (tx_ack->addr + tx_ack->len) & 0xFF);
spi_write(spi_device, ETXNDH, (tx_ack->addr + tx_ack->len) >> 8);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
else
{
NET_DEBUG("[tx isr] Tx chain empty, stop!\r\n");
}
/* set event */
rt_event_send(&tx_event, 0x01);
}
/* wake up handler */
if (eir & EIR_WOLIF)
{
NET_DEBUG("EIR_WOLIF\r\n");
eir_clr |= EIR_WOLIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_WOLIF);
}
/* TX Error handler */
if ((eir & EIR_TXERIF) != 0)
{
NET_DEBUG("EIR_TXERIF re-start tx chain!\r\n");
enc28j60_set_bank(spi_device, ECON1);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);
eir_clr |= EIR_TXERIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF);
/* re-init tx chain */
_tx_chain_init();
}
/* RX Error handler */
if ((eir & EIR_RXERIF) != 0)
{
NET_DEBUG("EIR_RXERIF re-start rx!\r\n");
NextPacketPtr = RXSTART_INIT;
enc28j60_set_bank(spi_device, ECON1);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXRST);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXRST);
/* switch to bank 0. */
enc28j60_set_bank(spi_device, ECON1);
/* enable packet reception. */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
eir_clr |= EIR_RXERIF;
// enc28j60_set_bank(spi_device, EIR);
// spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, EIR_RXERIF);
}
enc28j60_set_bank(spi_device, EIR);
spi_write_op(spi_device, ENC28J60_BIT_FIELD_CLR, EIR, eir_clr);
eir = spi_read(spi_device, EIR);
}
/* read pkt */
pk_counter = spi_read(spi_device, EPKTCNT);
if (pk_counter)
{
/* Set the read pointer to the start of the received packet. */
spi_write(spi_device, ERDPTL, (NextPacketPtr));
spi_write(spi_device, ERDPTH, (NextPacketPtr) >> 8);
/* read the next packet pointer. */
NextPacketPtr = spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0);
NextPacketPtr |= spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0) << 8;
/* read the packet length (see datasheet page 43). */
len = spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0); //0x54
len |= spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0) << 8; //5554
len -= 4; //remove the CRC count
// read the receive status (see datasheet page 43)
rxstat = spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0);
rxstat |= ((rt_uint16_t)spi_read_op(spi_device, ENC28J60_READ_BUF_MEM, 0)) << 8;
// check CRC and symbol errors (see datasheet page 44, table 7-3):
// The ERXFCON.CRCEN is set by default. Normally we should not
// need to check this.
if ((rxstat & 0x80) == 0)
{
// invalid
len = 0;
}
else
{
/* allocation pbuf */
p = pbuf_alloc(PBUF_LINK, len, PBUF_POOL);
if (p != RT_NULL)
{
struct pbuf *q;
#ifdef ETH_RX_DUMP
rt_size_t dump_count = 0;
rt_uint8_t *dump_ptr;
rt_size_t dump_i;
NET_DEBUG("rx_dump, size:%d\r\n", len);
#endif
for (q = p; q != RT_NULL; q = q->next)
{
uint8_t cmd = ENC28J60_READ_BUF_MEM;
rt_spi_send_then_recv(spi_device, &cmd, 1, q->payload, q->len);
#ifdef ETH_RX_DUMP
dump_ptr = q->payload;
for (dump_i = 0; dump_i < q->len; dump_i++)
{
NET_DEBUG("%02x ", *dump_ptr);
if (((dump_count + 1) % 8) == 0)
{
NET_DEBUG(" ");
}
if (((dump_count + 1) % 16) == 0)
{
NET_DEBUG("\r\n");
}
dump_count++;
dump_ptr++;
}
#endif
}
#ifdef ETH_RX_DUMP
NET_DEBUG("\r\n");
#endif
}
}
/* Move the RX read pointer to the start of the next received packet. */
/* This frees the memory we just read out. */
spi_write(spi_device, ERXRDPTL, (NextPacketPtr));
spi_write(spi_device, ERXRDPTH, (NextPacketPtr) >> 8);
/* decrement the packet counter indicate we are done with this packet. */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}
else
{
/* switch to bank 0. */
enc28j60_set_bank(spi_device, ECON1);
/* enable packet reception. */
spi_write_op(spi_device, ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
level |= EIE_PKTIE;
}
/* enable enc28j60 interrupt */
enc28j60_interrupt_enable(spi_device, level);
enc28j60_unlock(dev);
return p;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops enc28j60_ops =
{
enc28j60_init,
enc28j60_open,
enc28j60_close,
enc28j60_read,
enc28j60_write,
enc28j60_control
};
#endif
rt_err_t enc28j60_attach(const char *spi_device_name)
{
struct rt_spi_device *spi_device;
spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name);
if (spi_device == RT_NULL)
{
NET_DEBUG("spi device %s not found!\r\n", spi_device_name);
return -RT_ENOSYS;
}
/* config spi */
{
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible Modes 0 */
cfg.max_hz = 20 * 1000 * 1000; /* SPI Interface with Clock Speeds Up to 20 MHz */
rt_spi_configure(spi_device, &cfg);
} /* config spi */
rt_memset(&enc28j60_dev, 0, sizeof(enc28j60_dev));
rt_event_init(&tx_event, "eth_tx", RT_IPC_FLAG_FIFO);
enc28j60_dev.spi_device = spi_device;
/* detect device */
{
uint16_t value;
/* perform system reset. */
spi_write_op(spi_device, ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
rt_thread_delay(1); /* delay 20ms */
enc28j60_dev.emac_rev = spi_read(spi_device, EREVID);
value = enc28j60_phy_read(spi_device, PHHID2);
enc28j60_dev.phy_rev = value & 0x0F;
enc28j60_dev.phy_pn = (value >> 4) & 0x3F;
enc28j60_dev.phy_id = (enc28j60_phy_read(spi_device, PHHID1) | ((value >> 10) << 16)) << 3;
if (enc28j60_dev.phy_id != 0x00280418)
{
NET_DEBUG("ENC28J60 PHY ID not correct!\r\n");
NET_DEBUG("emac_rev:%d\r\n", enc28j60_dev.emac_rev);
NET_DEBUG("phy_rev:%02X\r\n", enc28j60_dev.phy_rev);
NET_DEBUG("phy_pn:%02X\r\n", enc28j60_dev.phy_pn);
NET_DEBUG("phy_id:%08X\r\n", enc28j60_dev.phy_id);
return -RT_EIO;
}
}
/* OUI 00-04-A3 (hex): Microchip Technology, Inc. */
enc28j60_dev.dev_addr[0] = 0x00;
enc28j60_dev.dev_addr[1] = 0x04;
enc28j60_dev.dev_addr[2] = 0xA3;
/* set MAC address, only for test */
enc28j60_dev.dev_addr[3] = 0x12;
enc28j60_dev.dev_addr[4] = 0x34;
enc28j60_dev.dev_addr[5] = 0x56;
/* init rt-thread device struct */
enc28j60_dev.parent.parent.type = RT_Device_Class_NetIf;
#ifdef RT_USING_DEVICE_OPS
enc28j60_dev.parent.parent.ops = &enc28j60_ops;
#else
enc28j60_dev.parent.parent.init = enc28j60_init;
enc28j60_dev.parent.parent.open = enc28j60_open;
enc28j60_dev.parent.parent.close = enc28j60_close;
enc28j60_dev.parent.parent.read = enc28j60_read;
enc28j60_dev.parent.parent.write = enc28j60_write;
enc28j60_dev.parent.parent.control = enc28j60_control;
#endif
/* init rt-thread ethernet device struct */
enc28j60_dev.parent.eth_rx = enc28j60_rx;
enc28j60_dev.parent.eth_tx = enc28j60_tx;
rt_mutex_init(&enc28j60_dev.lock, "enc28j60", RT_IPC_FLAG_PRIO);
eth_device_init(&(enc28j60_dev.parent), "e0");
return RT_EOK;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
/*
* Debug routine to dump useful register contents
*/
static void enc28j60(void)
{
struct rt_spi_device *spi_device = enc28j60_dev.spi_device;
enc28j60_lock(&enc28j60_dev);
rt_kprintf("-- enc28j60 registers:\n");
rt_kprintf("HwRevID: 0x%02X\n", spi_read(spi_device, EREVID));
rt_kprintf("Cntrl: ECON1 ECON2 ESTAT EIR EIE\n");
rt_kprintf(" 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X\n",
spi_read(spi_device, ECON1),
spi_read(spi_device, ECON2),
spi_read(spi_device, ESTAT),
spi_read(spi_device, EIR),
spi_read(spi_device, EIE));
rt_kprintf("MAC : MACON1 MACON3 MACON4\n");
rt_kprintf(" 0x%02X 0x%02X 0x%02X\n",
spi_read(spi_device, MACON1),
spi_read(spi_device, MACON3),
spi_read(spi_device, MACON4));
rt_kprintf("Rx : ERXST ERXND ERXWRPT ERXRDPT ERXFCON EPKTCNT MAMXFL\n");
rt_kprintf(" 0x%04X 0x%04X 0x%04X 0x%04X ",
(spi_read(spi_device, ERXSTH) << 8) | spi_read(spi_device, ERXSTL),
(spi_read(spi_device, ERXNDH) << 8) | spi_read(spi_device, ERXNDL),
(spi_read(spi_device, ERXWRPTH) << 8) | spi_read(spi_device, ERXWRPTL),
(spi_read(spi_device, ERXRDPTH) << 8) | spi_read(spi_device, ERXRDPTL));
rt_kprintf("0x%02X 0x%02X 0x%04X\n",
spi_read(spi_device, ERXFCON),
spi_read(spi_device, EPKTCNT),
(spi_read(spi_device, MAMXFLH) << 8) | spi_read(spi_device, MAMXFLL));
rt_kprintf("Tx : ETXST ETXND MACLCON1 MACLCON2 MAPHSUP\n");
rt_kprintf(" 0x%04X 0x%04X 0x%02X 0x%02X 0x%02X\n",
(spi_read(spi_device, ETXSTH) << 8) | spi_read(spi_device, ETXSTL),
(spi_read(spi_device, ETXNDH) << 8) | spi_read(spi_device, ETXNDL),
spi_read(spi_device, MACLCON1),
spi_read(spi_device, MACLCON2),
spi_read(spi_device, MAPHSUP));
rt_kprintf("PHY : PHCON1 PHSTAT1\r\n");
rt_kprintf(" 0x%04X 0x%04X\r\n",
enc28j60_phy_read(spi_device, PHCON1),
enc28j60_phy_read(spi_device, PHSTAT1));
enc28j60_unlock(&enc28j60_dev);
}
FINSH_FUNCTION_EXPORT(enc28j60, dump enc28j60 registers);
#endif

View file

@ -0,0 +1,343 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#ifndef EN28J60_H_INCLUDED
#define EN28J60_H_INCLUDED
#include <stdint.h>
#include <rtthread.h>
#include <drivers/spi.h>
#include <netif/ethernetif.h>
// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address (bits 0-4)
// - Bank number (bits 5-6)
// - MAC/PHY indicator (bit 7)
#define ADDR_MASK 0x1F
#define BANK_MASK 0x60
#define SPRD_MASK 0x80
// All-bank registers
#define EIE 0x1B
#define EIR 0x1C
#define ESTAT 0x1D
#define ECON2 0x1E
#define ECON1 0x1F
// Bank 0 registers
#define ERDPTL (0x00|0x00)
#define ERDPTH (0x01|0x00)
#define EWRPTL (0x02|0x00)
#define EWRPTH (0x03|0x00)
#define ETXSTL (0x04|0x00)
#define ETXSTH (0x05|0x00)
#define ETXNDL (0x06|0x00)
#define ETXNDH (0x07|0x00)
#define ERXSTL (0x08|0x00)
#define ERXSTH (0x09|0x00)
#define ERXNDL (0x0A|0x00)
#define ERXNDH (0x0B|0x00)
#define ERXRDPTL (0x0C|0x00)
#define ERXRDPTH (0x0D|0x00)
#define ERXWRPTL (0x0E|0x00)
#define ERXWRPTH (0x0F|0x00)
#define EDMASTL (0x10|0x00)
#define EDMASTH (0x11|0x00)
#define EDMANDL (0x12|0x00)
#define EDMANDH (0x13|0x00)
#define EDMADSTL (0x14|0x00)
#define EDMADSTH (0x15|0x00)
#define EDMACSL (0x16|0x00)
#define EDMACSH (0x17|0x00)
// Bank 1 registers
#define EHT0 (0x00|0x20)
#define EHT1 (0x01|0x20)
#define EHT2 (0x02|0x20)
#define EHT3 (0x03|0x20)
#define EHT4 (0x04|0x20)
#define EHT5 (0x05|0x20)
#define EHT6 (0x06|0x20)
#define EHT7 (0x07|0x20)
#define EPMM0 (0x08|0x20)
#define EPMM1 (0x09|0x20)
#define EPMM2 (0x0A|0x20)
#define EPMM3 (0x0B|0x20)
#define EPMM4 (0x0C|0x20)
#define EPMM5 (0x0D|0x20)
#define EPMM6 (0x0E|0x20)
#define EPMM7 (0x0F|0x20)
#define EPMCSL (0x10|0x20)
#define EPMCSH (0x11|0x20)
#define EPMOL (0x14|0x20)
#define EPMOH (0x15|0x20)
#define EWOLIE (0x16|0x20)
#define EWOLIR (0x17|0x20)
#define ERXFCON (0x18|0x20)
#define EPKTCNT (0x19|0x20)
// Bank 2 registers
#define MACON1 (0x00|0x40|0x80)
#define MACON2 (0x01|0x40|0x80)
#define MACON3 (0x02|0x40|0x80)
#define MACON4 (0x03|0x40|0x80)
#define MABBIPG (0x04|0x40|0x80)
#define MAIPGL (0x06|0x40|0x80)
#define MAIPGH (0x07|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFLL (0x0A|0x40|0x80)
#define MAMXFLH (0x0B|0x40|0x80)
#define MAPHSUP (0x0D|0x40|0x80)
#define MICON (0x11|0x40|0x80)
#define MICMD (0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWRL (0x16|0x40|0x80)
#define MIWRH (0x17|0x40|0x80)
#define MIRDL (0x18|0x40|0x80)
#define MIRDH (0x19|0x40|0x80)
// Bank 3 registers
#define MAADR1 (0x00|0x60|0x80)
#define MAADR0 (0x01|0x60|0x80)
#define MAADR3 (0x02|0x60|0x80)
#define MAADR2 (0x03|0x60|0x80)
#define MAADR5 (0x04|0x60|0x80)
#define MAADR4 (0x05|0x60|0x80)
#define EBSTSD (0x06|0x60)
#define EBSTCON (0x07|0x60)
#define EBSTCSL (0x08|0x60)
#define EBSTCSH (0x09|0x60)
#define MISTAT (0x0A|0x60|0x80)
#define EREVID (0x12|0x60)
#define ECOCON (0x15|0x60)
#define EFLOCON (0x17|0x60)
#define EPAUSL (0x18|0x60)
#define EPAUSH (0x19|0x60)
// PHY registers
#define PHCON1 0x00
#define PHSTAT1 0x01
#define PHHID1 0x02
#define PHHID2 0x03
#define PHCON2 0x10
#define PHSTAT2 0x11
#define PHIE 0x12
#define PHIR 0x13
#define PHLCON 0x14
// ENC28J60 ERXFCON Register Bit Definitions
#define ERXFCON_UCEN 0x80
#define ERXFCON_ANDOR 0x40
#define ERXFCON_CRCEN 0x20
#define ERXFCON_PMEN 0x10
#define ERXFCON_MPEN 0x08
#define ERXFCON_HTEN 0x04
#define ERXFCON_MCEN 0x02
#define ERXFCON_BCEN 0x01
// ENC28J60 EIE Register Bit Definitions
#define EIE_INTIE 0x80
#define EIE_PKTIE 0x40
#define EIE_DMAIE 0x20
#define EIE_LINKIE 0x10
#define EIE_TXIE 0x08
#define EIE_WOLIE 0x04
#define EIE_TXERIE 0x02
#define EIE_RXERIE 0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF 0x40
#define EIR_DMAIF 0x20
#define EIR_LINKIF 0x10
#define EIR_TXIF 0x08
#define EIR_WOLIF 0x04
#define EIR_TXERIF 0x02
#define EIR_RXERIF 0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT 0x80
#define ESTAT_LATECOL 0x10
#define ESTAT_RXBUSY 0x04
#define ESTAT_TXABRT 0x02
#define ESTAT_CLKRDY 0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC 0x80
#define ECON2_PKTDEC 0x40
#define ECON2_PWRSV 0x20
#define ECON2_VRPS 0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST 0x80
#define ECON1_RXRST 0x40
#define ECON1_DMAST 0x20
#define ECON1_CSUMEN 0x10
#define ECON1_TXRTS 0x08
#define ECON1_RXEN 0x04
#define ECON1_BSEL1 0x02
#define ECON1_BSEL0 0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK 0x10
#define MACON1_TXPAUS 0x08
#define MACON1_RXPAUS 0x04
#define MACON1_PASSALL 0x02
#define MACON1_MARXEN 0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST 0x80
#define MACON2_RNDRST 0x40
#define MACON2_MARXRST 0x08
#define MACON2_RFUNRST 0x04
#define MACON2_MATXRST 0x02
#define MACON2_TFUNRST 0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2 0x80
#define MACON3_PADCFG1 0x40
#define MACON3_PADCFG0 0x20
#define MACON3_TXCRCEN 0x10
#define MACON3_PHDRLEN 0x08
#define MACON3_HFRMLEN 0x04
#define MACON3_FRMLNEN 0x02
#define MACON3_FULDPX 0x01
// ENC28J60 MACON4 Register Bit Definitions
#define MACON4_DEFER (1<<6)
#define MACON4_BPEN (1<<5)
#define MACON4_NOBKOFF (1<<4)
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN 0x02
#define MICMD_MIIRD 0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID 0x04
#define MISTAT_SCAN 0x02
#define MISTAT_BUSY 0x01
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST 0x8000
#define PHCON1_PLOOPBK 0x4000
#define PHCON1_PPWRSV 0x0800
#define PHCON1_PDPXMD 0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX 0x1000
#define PHSTAT1_PHDPX 0x0800
#define PHSTAT1_LLSTAT 0x0004
#define PHSTAT1_JBSTAT 0x0002
/* ENC28J60 PHY PHSTAT2 Register Bit Definitions */
#define PHSTAT2_TXSTAT (1 << 13)
#define PHSTAT2_RXSTAT (1 << 12)
#define PHSTAT2_COLSTAT (1 << 11)
#define PHSTAT2_LSTAT (1 << 10)
#define PHSTAT2_DPXSTAT (1 << 9)
#define PHSTAT2_PLRITY (1 << 5)
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK 0x4000
#define PHCON2_TXDIS 0x2000
#define PHCON2_JABBER 0x0400
#define PHCON2_HDLDIS 0x0100
/* ENC28J60 PHY PHIE Register Bit Definitions */
#define PHIE_PLNKIE (1 << 4)
#define PHIE_PGEIE (1 << 1)
/* ENC28J60 PHY PHIR Register Bit Definitions */
#define PHIR_PLNKIF (1 << 4)
#define PHIR_PGEIF (1 << 1)
// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN 0x04
#define PKTCTRL_PCRCEN 0x02
#define PKTCTRL_POVERRIDE 0x01
/* ENC28J60 Transmit Status Vector */
#define TSV_TXBYTECNT 0
#define TSV_TXCOLLISIONCNT 16
#define TSV_TXCRCERROR 20
#define TSV_TXLENCHKERROR 21
#define TSV_TXLENOUTOFRANGE 22
#define TSV_TXDONE 23
#define TSV_TXMULTICAST 24
#define TSV_TXBROADCAST 25
#define TSV_TXPACKETDEFER 26
#define TSV_TXEXDEFER 27
#define TSV_TXEXCOLLISION 28
#define TSV_TXLATECOLLISION 29
#define TSV_TXGIANT 30
#define TSV_TXUNDERRUN 31
#define TSV_TOTBYTETXONWIRE 32
#define TSV_TXCONTROLFRAME 48
#define TSV_TXPAUSEFRAME 49
#define TSV_BACKPRESSUREAPP 50
#define TSV_TXVLANTAGFRAME 51
#define TSV_SIZE 7
#define TSV_BYTEOF(x) ((x) / 8)
#define TSV_BITMASK(x) (1 << ((x) % 8))
#define TSV_GETBIT(x, y) (((x)[TSV_BYTEOF(y)] & TSV_BITMASK(y)) ? 1 : 0)
/* ENC28J60 Receive Status Vector */
#define RSV_RXLONGEVDROPEV 16
#define RSV_CARRIEREV 18
#define RSV_CRCERROR 20
#define RSV_LENCHECKERR 21
#define RSV_LENOUTOFRANGE 22
#define RSV_RXOK 23
#define RSV_RXMULTICAST 24
#define RSV_RXBROADCAST 25
#define RSV_DRIBBLENIBBLE 26
#define RSV_RXCONTROLFRAME 27
#define RSV_RXPAUSEFRAME 28
#define RSV_RXUNKNOWNOPCODE 29
#define RSV_RXTYPEVLAN 30
#define RSV_SIZE 6
#define RSV_BITMASK(x) (1 << ((x) - 16))
#define RSV_GETBIT(x, y) (((x) & RSV_BITMASK(y)) ? 1 : 0)
// SPI operation codes
#define ENC28J60_READ_CTRL_REG 0x00
#define ENC28J60_READ_BUF_MEM 0x3A
#define ENC28J60_WRITE_CTRL_REG 0x40
#define ENC28J60_WRITE_BUF_MEM 0x7A
#define ENC28J60_BIT_FIELD_SET 0x80
#define ENC28J60_BIT_FIELD_CLR 0xA0
#define ENC28J60_SOFT_RESET 0xFF
// The RXSTART_INIT should be zero. See Rev. B4 Silicon Errata
// buffer boundaries applied to internal 8K ram
// the entire available packet buffer space is allocated
//
#define MAX_TX_PACKAGE_SIZE (1536)
// start with recbuf at 0/
#define RXSTART_INIT 0x0
// receive buffer end
#define RXSTOP_INIT (0x1FFF - MAX_TX_PACKAGE_SIZE*2) - 1
// start TX buffer at 0x1FFF-0x0600, pace for one full ethernet frame (~1500 bytes)
#define TXSTART_INIT (0x1FFF - MAX_TX_PACKAGE_SIZE*2)
// stp TX buffer at end of mem
#define TXSTOP_INIT 0x1FFF
// max frame length which the conroller will accept:
#define MAX_FRAMELEN 1518
#define MAX_ADDR_LEN 6
struct net_device
{
/* inherit from ethernet device */
struct eth_device parent;
/* interface address info. */
rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */
rt_uint8_t emac_rev;
rt_uint8_t phy_rev;
rt_uint8_t phy_pn;
rt_uint32_t phy_id;
/* spi device */
struct rt_spi_device *spi_device;
struct rt_mutex lock;
};
/* export function */
extern rt_err_t enc28j60_attach(const char *spi_device_name);
extern void enc28j60_isr(void);
#endif // EN28J60_H_INCLUDED

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-11-16 zylx first version.
*/
#include <drivers/spi.h>
rt_err_t rt_qspi_configure(struct rt_qspi_device *device, struct rt_qspi_configuration *cfg)
{
RT_ASSERT(device != RT_NULL);
RT_ASSERT(cfg != RT_NULL);
struct rt_qspi_device *qspi_device = (struct rt_qspi_device *)device;
rt_err_t result = RT_EOK;
/* copy configuration items */
qspi_device->config.parent.mode = cfg->parent.mode;
qspi_device->config.parent.max_hz = cfg->parent.max_hz;
qspi_device->config.parent.data_width = cfg->parent.data_width;
qspi_device->config.parent.reserved = cfg->parent.reserved;
qspi_device->config.medium_size = cfg->medium_size;
qspi_device->config.ddr_mode = cfg->ddr_mode;
qspi_device->config.qspi_dl_width = cfg->qspi_dl_width;
result = rt_spi_configure(&device->parent, &cfg->parent);
return result;
}
rt_err_t rt_qspi_bus_register(struct rt_spi_bus *bus, const char *name, const struct rt_spi_ops *ops)
{
rt_err_t result = RT_EOK;
result = rt_spi_bus_register(bus, name, ops);
if(result == RT_EOK)
{
/* set SPI bus to qspi modes */
bus->mode = RT_SPI_BUS_MODE_QSPI;
}
return result;
}
rt_size_t rt_qspi_transfer_message(struct rt_qspi_device *device, struct rt_qspi_message *message)
{
rt_err_t result;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(message != RT_NULL);
result = rt_mutex_take(&(device->parent.bus->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(-RT_EBUSY);
return 0;
}
/* reset errno */
rt_set_errno(RT_EOK);
/* configure SPI bus */
if (device->parent.bus->owner != &device->parent)
{
/* not the same owner as current, re-configure SPI bus */
result = device->parent.bus->ops->configure(&device->parent, &device->parent.config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->parent.bus->owner = &device->parent;
}
else
{
/* configure SPI bus failed */
rt_set_errno(-RT_EIO);
goto __exit;
}
}
/* transmit each SPI message */
result = device->parent.bus->ops->xfer(&device->parent, &message->parent);
if (result == 0)
{
rt_set_errno(-RT_EIO);
}
__exit:
/* release bus lock */
rt_mutex_release(&(device->parent.bus->lock));
return result;
}
rt_err_t rt_qspi_send_then_recv(struct rt_qspi_device *device, const void *send_buf, rt_size_t send_length, void *recv_buf, rt_size_t recv_length)
{
RT_ASSERT(send_buf);
RT_ASSERT(recv_buf);
RT_ASSERT(send_length != 0);
struct rt_qspi_message message;
unsigned char *ptr = (unsigned char *)send_buf;
rt_size_t count = 0;
rt_err_t result = 0;
message.instruction.content = ptr[0];
message.instruction.qspi_lines = 1;
count++;
/* get address */
if (send_length > 1)
{
if (device->config.medium_size > 0x1000000 && send_length >= 5)
{
/* medium size greater than 16Mb, address size is 4 Byte */
message.address.content = (ptr[1] << 24) | (ptr[2] << 16) | (ptr[3] << 8) | (ptr[4]);
message.address.size = 32;
count += 4;
}
else if (send_length >= 4)
{
/* address size is 3 Byte */
message.address.content = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
message.address.size = 24;
count += 3;
}
else
{
return -RT_ERROR;
}
message.address.qspi_lines = 1;
}
else
{
/* no address stage */
message.address.content = 0 ;
message.address.qspi_lines = 0;
message.address.size = 0;
}
message.alternate_bytes.content = 0;
message.alternate_bytes.size = 0;
message.alternate_bytes.qspi_lines = 0;
/* set dummy cycles */
if (count != send_length)
{
message.dummy_cycles = (send_length - count) * 8;
}
else
{
message.dummy_cycles = 0;
}
/* set recv buf and recv size */
message.parent.recv_buf = recv_buf;
message.parent.send_buf = RT_NULL;
message.parent.length = recv_length;
message.parent.cs_take = 1;
message.parent.cs_release = 1;
message.qspi_data_lines = 1;
result = rt_qspi_transfer_message(device, &message);
if (result == 0)
{
result = -RT_EIO;
}
else
{
result = recv_length;
}
return result;
}
rt_err_t rt_qspi_send(struct rt_qspi_device *device, const void *send_buf, rt_size_t length)
{
RT_ASSERT(send_buf);
RT_ASSERT(length != 0);
struct rt_qspi_message message;
unsigned char *ptr = (unsigned char *)send_buf;
rt_size_t count = 0;
rt_err_t result = 0;
message.instruction.content = ptr[0];
message.instruction.qspi_lines = 1;
count++;
/* get address */
if (length > 1)
{
if (device->config.medium_size > 0x1000000 && length >= 5)
{
/* medium size greater than 16Mb, address size is 4 Byte */
message.address.content = (ptr[1] << 24) | (ptr[2] << 16) | (ptr[3] << 8) | (ptr[4]);
message.address.size = 32;
message.address.qspi_lines = 1;
count += 4;
}
else if (length >= 4)
{
/* address size is 3 Byte */
message.address.content = (ptr[1] << 16) | (ptr[2] << 8) | (ptr[3]);
message.address.size = 24;
message.address.qspi_lines = 1;
count += 3;
}
else
{
return -RT_ERROR;
}
}
else
{
/* no address stage */
message.address.content = 0 ;
message.address.qspi_lines = 0;
message.address.size = 0;
}
message.alternate_bytes.content = 0;
message.alternate_bytes.size = 0;
message.alternate_bytes.qspi_lines = 0;
message.dummy_cycles = 0;
/* determine if there is data to send */
if (length - count > 0)
{
message.qspi_data_lines = 1;
}
else
{
message.qspi_data_lines = 0;
}
/* set send buf and send size */
message.parent.send_buf = ptr + count;
message.parent.recv_buf = RT_NULL;
message.parent.length = length - count;
message.parent.cs_take = 1;
message.parent.cs_release = 1;
result = rt_qspi_transfer_message(device, &message);
if (result == 0)
{
result = -RT_EIO;
}
else
{
result = length;
}
return result;
}

View file

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Armink (armink.ztl@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,297 @@
# SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库
---
## 0、SFUD 是什么
[SFUD](https://github.com/armink/SFUD) 是一款开源的串行 SPI Flash 通用驱动库。由于现有市面的串行 Flash 种类居多,各个 Flash 的规格及命令存在差异, SFUD 就是为了解决这些 Flash 的差异现状而设计,让我们的产品能够支持不同品牌及规格的 Flash提高了涉及到 Flash 功能的软件的可重用性及可扩展性,同时也可以规避 Flash 缺货或停产给产品所带来的风险。
- 主要特点:支持 SPI/QSPI 接口、面向对象(同时支持多个 Flash 对象)、可灵活裁剪、扩展性强、支持 4 字节地址
- 资源占用
- 标准占用RAM:0.2KB ROM:5.5KB
- 最小占用RAM:0.1KB ROM:3.6KB
- 设计思路:
- **什么是 SFDP** :它是 JEDEC (固态技术协会)制定的串行 Flash 功能的参数表标准,最新版 V1.6B [点击这里查看](https://www.jedec.org/standards-documents/docs/jesd216b))。该标准规定了,每个 Flash 中会存在一个参数表,该表中会存放 Flash 容量、写粒度、擦除命令、地址模式等 Flash 规格参数。目前,除了部分厂家旧款 Flash 型号会不支持该标准,其他绝大多数新出厂的 Flash 均已支持 SFDP 标准。所以该库在初始化时会优先读取 SFDP 表参数。
- **不支持 SFDP 怎么办** :如果该 Flash 不支持 SFDP 标准SFUD 会查询配置文件 ( [`/sfud/inc/sfud_flash_def.h`](https://github.com/armink/SFUD/blob/4bee2d0417a7ce853cc7aa3639b03fe825611fd9/sfud/inc/sfud_flash_def.h#L116-L142) ) 中提供的 **Flash 参数信息表** 中是否支持该款 Flash。如果不支持则可以在配置文件中添加该款 Flash 的参数信息(添加方法详细见 [2.5 添加库目前不支持的 Flash](#25-添加库目前不支持的-flash))。获取到了 Flash 的规格参数后,就可以实现对 Flash 的全部操作。
## 1、为什么选择 SFUD
- 避免项目因 Flash 缺货、Flash 停产或产品扩容而带来的风险;
- 越来越多的项目将固件存储到串行 Flash 中例如ESP8266 的固件、主板中的 BIOS 及其他常见电子产品中的固件等等,但是各种 Flash 规格及命令不统一。使用 SFUD 即可避免,在相同功能的软件平台基础下,无法适配不同 Flash 种类的硬件平台的问题,提高软件的可重用性;
- 简化软件流程,降低开发难度。现在只需要配置好 SPI 通信,即可畅快的开始玩串行 Flash 了;
- 可以用来制作 Flash 编程器/烧写器
## 2、SFUD 如何使用
### 2.1 已支持 Flash
下表为所有已在 Demo 平台上进行过真机测试过的 Flash。显示为 **不支持** SFDP 标准的 Flash 已经在 Flash 参数信息表中定义,更多不支持 SFDP 标准的 Flash 需要大家以后 **共同来完善和维护** **([Github](https://github.com/armink/SFUD)|[OSChina](http://git.oschina.net/armink/SFUD)|[Coding](https://coding.net/u/armink/p/SFUD/git))** 。
如果觉得这个开源项目很赞,可以点击 [项目主页](https://github.com/armink/SFUD) 右上角的 **Star** ,同时把它推荐给更多有需要的朋友。
|型号|制造商|容量|最高速度|SFDP 标准|QSPI 模式|备注|
|:--:|:----:|:--:|:--:|:--:|:--:|----|
|[W25Q40BV](http://microchip.ua/esp8266/W25Q40BV(EOL).pdf)|Winbond|4Mb|50Mhz|不支持|双线|已停产|
|[W25Q80DV](http://www.winbond.com/resource-files/w25q80dv_revg_07212015.pdf)|Winbond|8Mb|104Mhz|支持|双线||
|[W25Q16BV](https://media.digikey.com/pdf/Data%20Sheets/Winbond%20PDFs/W25Q16BV.pdf)|Winbond|16Mb|104Mhz|不支持|双线| by [slipperstree](https://github.com/slipperstree)|
|[W25Q16CV](http://www.winbond.com/resource-files/da00-w25q16cvf1.pdf)|Winbond|16Mb|104Mhz|支持|未测试||
|[W25Q16DV](http://www.winbond.com/resource-files/w25q16dv%20revk%2005232016%20doc.pdf)|Winbond|16Mb|104Mhz|支持|未测试| by [slipperstree](https://github.com/slipperstree)|
|[W25Q32BV](http://www.winbond.com/resource-files/w25q32bv_revi_100413_wo_automotive.pdf)|Winbond|32Mb|104Mhz|支持|双线||
|[W25Q64CV](http://www.winbond.com/resource-files/w25q64cv_revh_052214[2].pdf)|Winbond|64Mb|80Mhz|支持|四线||
|[W25Q128BV](http://www.winbond.com/resource-files/w25q128bv_revh_100313_wo_automotive.pdf)|Winbond|128Mb|104Mhz|支持|四线||
|[W25Q256FV](http://www.winbond.com/resource-files/w25q256fv%20revi%2002262016%20kms.pdf)|Winbond|256Mb|104Mhz|支持|四线||
|[MX25L3206E](http://www.macronix.com/Lists/DataSheet/Attachments/3199/MX25L3206E,%203V,%2032Mb,%20v1.5.pdf)|Macronix|32Mb|86MHz|支持|双线||
|[MX25L3233F](https://www.macronix.com/Lists/Datasheet/Attachments/8754/MX25L3233F,%203V,%2032Mb,%20v1.7.pdf)|Macronix|32Mb|133MHz|支持|未测试|by [JiapengLi](https://github.com/JiapengLi)|
|[KH25L4006E](http://www.macronix.com.hk/Lists/Datasheet/Attachments/117/KH25L4006E.pdf)|Macronix|4Mb|86Mhz|支持|未测试| by [JiapengLi](https://github.com/JiapengLi)|
|[KH25L3206E](http://www.macronix.com.hk/Lists/Datasheet/Attachments/131/KH25L3206E.pdf)|Macronix|32Mb|86Mhz|支持|双线||
|[SST25VF016B](http://ww1.microchip.com/downloads/en/DeviceDoc/20005044C.pdf)|Microchip|16Mb|50MHz|不支持|不支持| SST 已被 Microchip 收购|
|[M25P40](https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p40.pdf)|Micron|4Mb|75Mhz|不支持|未测试| by [redocCheng](https://github.com/redocCheng)|
|[M25P80](https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p80.pdf)|Micron|8Mb|75Mhz|不支持|未测试| by [redocCheng](https://github.com/redocCheng)|
|[M25P32](https://www.micron.com/~/media/documents/products/data-sheet/nor-flash/serial-nor/m25p/m25p32.pdf)|Micron|32Mb|75Mhz|不支持|不支持||
|[EN25Q32B](http://www.kean.com.au/oshw/WR703N/teardown/EN25Q32B%2032Mbit%20SPI%20Flash.pdf)|EON|32Mb|104MHz|不支持|未测试||
|[GD25Q16B](http://www.gigadevice.com/product/detail/5/410.html)|GigaDevice|16Mb|120Mhz|不支持|未测试| by [Liang Yongxiang](https://github.com/liangyongxiang) |
|[GD25Q64B](http://www.gigadevice.com/product/detail/5/364.html)|GigaDevice|64Mb|120Mhz|不支持|双线||
|[S25FL216K](http://www.cypress.com/file/197346/download)|Cypress|16Mb|65Mhz|不支持|双线||
|[S25FL032P](http://www.cypress.com/file/196861/download)|Cypress|32Mb|104Mhz|不支持|未测试| by [yc_911](https://gitee.com/yc_911) |
|[S25FL164K](http://www.cypress.com/file/196886/download)|Cypress|64Mb|108Mhz|支持|未测试||
|[A25L080](http://www.amictechnology.com/datasheets/A25L080.pdf)|AMIC|8Mb|100Mhz|不支持|双线||
|[A25LQ64](http://www.amictechnology.com/datasheets/A25LQ64.pdf)|AMIC|64Mb|104Mhz|支持|支持||
|[F25L004](http://www.esmt.com.tw/db/manager/upload/f25l004.pdf)|ESMT|4Mb|100Mhz|不支持|不支持||
|[PCT25VF016B](http://pctgroup.com.tw/attachments/files/files/248_25VF016B-P.pdf)|PCT|16Mb|80Mhz|不支持|不支持|SST 授权许可,会被识别为 SST25VF016B|
|[AT45DB161E](http://www.adestotech.com/wp-content/uploads/doc8782.pdf)|ADESTO|16Mb|85MHz|不支持|不支持|ADESTO 收购 Atmel 串行闪存产品线|
> 注QSPI 模式中,双线表示支持双线快读,四线表示支持四线快读。
>
> 一般情况下,支持四线快读的 FLASH 也支持双线快读。
### 2.2 API 说明
先说明下本库主要使用的一个结构体 `sfud_flash` 。其定义位于 `/sfud/inc/sfud_def.h`。每个 SPI Flash 会对应一个该结构体,该结构体指针下面统称为 Flash 设备对象。初始化成功后在 `sfud_flash->chip` 结构体中会存放 SPI Flash 的常见参数。如果 SPI Flash 还支持 SFDP ,还可以通过 `sfud_flash->sfdp` 看到更加全面的参数信息。以下很多函数都将使用 Flash 设备对象作为第一个入参,实现对指定 SPI Flash 的操作。
#### 2.2.1 初始化 SFUD 库
将会调用 `sfud_device_init` ,初始化 Flash 设备表中的全部设备。如果只有一个 Flash 也可以只使用 `sfud_device_init` 进行单一初始化。
> **注意**:初始化完的 SPI Flash 默认都 **已取消写保护** 状态,如需开启写保护,请使用 sfud_write_status 函数修改 SPI Flash 状态。
```C
sfud_err sfud_init(void)
```
#### 2.2.2 初始化指定的 Flash 设备
```C
sfud_err sfud_device_init(sfud_flash *flash)
```
|参数 |描述|
|:----- |:----|
|flash |待初始化的 Flash 设备|
#### 2.2.3 使能快速读模式(仅当 SFUD 开启 QSPI 模式后可用)
当 SFUD 开启 QSPI 模式后SFUD 中的 Flash 驱动支持使用 QSPI 总线进行通信。相比传统的 SPI 模式,使用 QSPI 能够加速 Flash 数据的读取,但当数据需要写入时,由于 Flash 本身的数据写入速度慢于 SPI 传输速度,所以 QSPI 模式下的数据写入速度提升并不明显。
所以 SFUD 对于 QSPI 模式的支持仅限于快速读命令。通过该函数可以配置 Flash 所使用的 QSPI 总线的实际支持的数据线最大宽度例如1 线(默认值,即传统的 SPI 模式、2 线、4 线。
设置后SFUD 会去结合当前设定的 QSPI 总线数据线宽度,去 [QSPI Flash 扩展信息表](https://github.com/armink/SFUD/blob/069d2b409ec239f84d675b2c3d37894e908829e6/sfud/inc/sfud_flash_def.h#L149-L177) 中匹配最合适的、速度最快的快速读命令,之后用户在调用 sfud_read() 时,会使用 QSPI 模式的传输函数发送该命令。
```C
sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width)
```
| 参数 | 描述 |
| :-------------- | :------------------------------------------- |
| flash | Flash 设备 |
| data_line_width | QSPI 总线支持的数据线最大宽度例如1、2、4 |
#### 2.2.4 获取 Flash 设备对象
在 SFUD 配置文件中会定义 Flash 设备表,负责存放所有将要使用的 Flash 设备对象,所以 SFUD 支持多个 Flash 设备同时驱动。设备表的配置在 `/sfud/inc/sfud_cfg.h``SFUD_FLASH_DEVICE_TABLE` 宏定义,详细配置方法参照 [2.3 配置方法 Flash](#23-配置方法))。本方法通过 Flash 设备位于设备表中索引值来返回 Flash 设备对象,超出设备表范围返回 `NULL`
```C
sfud_flash *sfud_get_device(size_t index)
```
|参数 |描述|
|:----- |:----|
|index |Flash 设备位于 FLash 设备表中的索引值|
#### 2.2.5 读取 Flash 数据
```C
sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|addr |起始地址|
|size |从起始地址开始读取数据的总大小|
|data |读取到的数据|
#### 2.2.6 擦除 Flash 数据
> 注意:擦除操作将会按照 Flash 芯片的擦除粒度(详见 Flash 数据手册,一般为 block 大小。初始化完成后,可以通过 `sfud_flash->chip.erase_gran` 查看)对齐,请注意保证起始地址和擦除数据大小按照 Flash 芯片的擦除粒度对齐,否则执行擦除操作后,将会导致其他数据丢失。
```C
sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|addr |起始地址|
|size |从起始地址开始擦除数据的总大小|
#### 2.2.7 擦除 Flash 全部数据
```C
sfud_err sfud_chip_erase(const sfud_flash *flash)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
#### 2.2.8 往 Flash 写数据
```C
sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|addr |起始地址|
|size |从起始地址开始写入数据的总大小|
|data |待写入的数据|
#### 2.2.9 先擦除再往 Flash 写数据
> 注意:擦除操作将会按照 Flash 芯片的擦除粒度(详见 Flash 数据手册,一般为 block 大小。初始化完成后,可以通过 `sfud_flash->chip.erase_gran` 查看)对齐,请注意保证起始地址和擦除数据大小按照 Flash 芯片的擦除粒度对齐,否则执行擦除操作后,将会导致其他数据丢失。
```C
sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|addr |起始地址|
|size |从起始地址开始写入数据的总大小|
|data |待写入的数据|
#### 2.2.10 读取 Flash 状态
```C
sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|status |当前状态寄存器值|
#### 2.2.11 写(修改) Flash 状态
```C
sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status)
```
|参数 |描述|
|:----- |:----|
|flash |Flash 设备对象|
|is_volatile |是否为易闪失的true: 易闪失的,及断电后会丢失|
|status |当前状态寄存器值|
### 2.3 配置方法
所有配置位于 `/sfud/inc/sfud_cfg.h` ,请参考下面的配置介绍,选择适合自己项目的配置。
#### 2.3.1 调试模式
打开/关闭 `SFUD_DEBUG_MODE` 宏定义
#### 2.3.2 是否使用 SFDP 参数功能
打开/关闭 `SFUD_USING_SFDP` 宏定义
> 注意:关闭后只会查询该库在 `/sfud/inc/sfud_flash_def.h` 中提供的 Flash 信息表。这样虽然会降低软件的适配性,但减少代码量。
#### 2.3.3 是否使用该库自带的 Flash 参数信息表
打开/关闭 `SFUD_USING_FLASH_INFO_TABLE` 宏定义
> 注意:关闭后该库只驱动支持 SFDP 规范的 Flash也会适当的降低部分代码量。另外 2.3.2 及 2.3.3 这两个宏定义至少定义一种,也可以两种方式都选择。
#### 2.3.4 既不使用 SFDP ,也不使用 Flash 参数信息表
为了进一步降低代码量,`SFUD_USING_SFDP``SFUD_USING_FLASH_INFO_TABLE` 也可以 **都不定义**
此时,只要在定义 Flash 设备时,指定好 Flash 参数,之后再调用 `sfud_device_init` 对该设备进行初始化。参考如下代码:
```C
sfud_flash sfud_norflash0 = {
.name = "norflash0",
.spi.name = "SPI1",
.chip = { "W25Q64FV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L * 1024L * 1024L, SFUD_WM_PAGE_256B, 4096, 0x20 } };
......
sfud_device_init(&sfud_norflash0);
......
```
#### 2.3.5 Flash 设备表
如果产品中存在多个 Flash ,可以添加 Flash 设备表。修改 `SFUD_FLASH_DEVICE_TABLE` 这个宏定义,示例如下:
```C
enum {
SFUD_W25Q64CV_DEVICE_INDEX = 0,
SFUD_GD25Q64B_DEVICE_INDEX = 1,
};
#define SFUD_FLASH_DEVICE_TABLE \
{ \
[SFUD_W25Q64CV_DEVICE_INDEX] = {.name = "W25Q64CV", .spi.name = "SPI1"}, \
[SFUD_GD25Q64B_DEVICE_INDEX] = {.name = "GD25Q64B", .spi.name = "SPI3"}, \
}
```
上面定义了两个 Flash 设备(大部分产品一个足以),两个设备的名称为 `"W25Q64CV"``"GD25Q64B"` ,分别对应 `"SPI1"``"SPI3"` 这两个 SPI 设备名称(在移植 SPI 接口时会用到,位于 `/sfud/port/sfud_port.c` `SFUD_W25Q16CV_DEVICE_INDEX``SFUD_GD25Q64B_DEVICE_INDEX` 这两个枚举定义了两个设备位于设备表中的索引,可以通过 `sfud_get_device_table()` 方法获取到设备表,再配合这个索引值来访问指定的设备。
#### 2.3.6 QSPI 模式
打开/关闭 `SFUD_USING_QSPI` 宏定义
开启后SFUD 也将支持使用 QSPI 总线连接的 Flash。
### 2.4 移植说明
移植文件位于 `/sfud/port/sfud_port.c` ,文件中的 `sfud_err sfud_spi_port_init(sfud_flash *flash)` 方法是库提供的移植方法,在里面完成各个设备 SPI 读写驱动(必选)、重试次数(必选)、重试接口(可选)及 SPI 锁(可选)的配置。更加详细的移植内容,可以参考 demo 中的各个平台的移植文件。
### 2.5 添加库目前不支持的 Flash
这里需要修改 `/sfud/inc/sfdu_flash_def.h` ,所有已经支持的 Flash 见 `SFUD_FLASH_CHIP_TABLE` 宏定义,需要提前准备的 Flash 参数内容分别为:| 名称 | 制造商 ID | 类型 ID | 容量 ID | 容量 | 写模式 | 擦除粒度(擦除的最小单位) | 擦除粒度对应的命令 | 。这里以添加 兆易创新 ( GigaDevice ) 的 `GD25Q64B` Flash 来举例。
此款 Flash 为兆易创新的早期生产的型号,所以不支持 SFDP 标准。首先需要下载其数据手册,找到 0x9F 命令返回的 3 种 ID 这里需要最后面两字节 ID ,即 `type id``capacity id``GD25Q64B` 对应这两个 ID 分别为 `0x40``0x17` 。上面要求的其他 Flash 参数都可以在数据手册中找到,这里要重点说明下 **写模式** 这个参数,库本身提供的写模式共计有 4 种,详见文件顶部的 `sfud_write_mode` 枚举类型,同一款 Flash 可以同时支持多种写模式,视情况而定。对于 `GD25Q64B` 而言,其支持的写模式应该为 `SFUD_WM_PAGE_256B` ,即写 1-256 字节每页。结合上述 `GD25Q64B` 的 Flash 参数应如下:
```
{"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8*1024*1024, SFUD_WM_PAGE_256B, 4096, 0x20},
```
再将其增加到 `SFUD_FLASH_CHIP_TABLE` 宏定义末尾,即可完成该库对 `GD25Q64B` 的支持。
### 2.6 Demo
目前已支持如下平台下的 Demo
|路径 |平台描述|
|:----- |:----|
|[/demo/stm32f10x_non_os](https://github.com/armink/SFUD/tree/master/demo/stm32f10x_non_os) |STM32F10X 裸机平台|
|[/demo/stm32f2xx_rtt](https://github.com/armink/SFUD/tree/master/demo/stm32f2xx_rtt) |STM32F2XX + [RT-Thread](http://www.rt-thread.org/) 操作系统平台|
|[/demo/stm32l475_non_os_qspi](https://github.com/armink/SFUD/tree/master/demo/stm32l475_non_os_qspi) |STM32L475 + QSPI 模式 裸机平台|
### 2.7 许可
采用 MIT 开源协议,细节请阅读项目中的 LICENSE 文件内容。

View file

@ -0,0 +1,178 @@
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is an head file for this library. You can see all of the functions which can be called by user.
* Created on: 2016-04-23
*/
#ifndef _SFUD_H_
#define _SFUD_H_
#include "sfud_def.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ../src/sfup.c */
/**
* SFUD library initialize.
*
* @return result
*/
sfud_err sfud_init(void);
/**
* SFUD initialize by flash device
*
* @param flash flash device
*
* @return result
*/
sfud_err sfud_device_init(sfud_flash *flash);
/**
* get flash device by its index which in the flash information table
*
* @param index the index which in the flash information table @see flash_table
*
* @return flash device
*/
sfud_flash *sfud_get_device(size_t index);
/**
* get flash device total number on flash device information table @see flash_table
*
* @return flash device total number
*/
size_t sfud_get_device_num(void);
/**
* get flash device information table @see flash_table
*
* @return flash device table pointer
*/
const sfud_flash *sfud_get_device_table(void);
#ifdef SFUD_USING_QSPI
/**
* Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
*
* it will find the appropriate fast-read instruction to replace the read instruction(0x03)
* fast-read instruction @see SFUD_FLASH_EXT_INFO_TABLE
*
* @note When Flash is in QSPI mode, the method must be called after sfud_device_init().
*
* @param flash flash device
* @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
*
* @return result
*/
sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width);
#endif /* SFUD_USING_QSPI */
/**
* read flash data
*
* @param flash flash device
* @param addr start address
* @param size read size
* @param data read data pointer
*
* @return result
*/
sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data);
/**
* erase flash data
*
* @note It will erase align by erase granularity.
*
* @param flash flash device
* @param addr start address
* @param size erase size
*
* @return result
*/
sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size);
/**
* write flash data (no erase operate)
*
* @param flash flash device
* @param addr start address
* @param data write data
* @param size write size
*
* @return result
*/
sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
/**
* erase and write flash data
*
* @param flash flash device
* @param addr start address
* @param size write size
* @param data write data
*
* @return result
*/
sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
/**
* erase all flash data
*
* @param flash flash device
*
* @return result
*/
sfud_err sfud_chip_erase(const sfud_flash *flash);
/**
* read flash register status
*
* @param flash flash device
* @param status register status
*
* @return result
*/
sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status);
/**
* write status register
*
* @param flash flash device
* @param is_volatile true: volatile mode, false: non-volatile mode
* @param status register status
*
* @return result
*/
sfud_err sfud_write_status(const sfud_flash *flash, bool is_volatile, uint8_t status);
#ifdef __cplusplus
}
#endif
#endif /* _SFUD_H_ */

View file

@ -0,0 +1,75 @@
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the configure head file for this library.
* Created on: 2016-04-23
*/
#ifndef _SFUD_CFG_H_
#define _SFUD_CFG_H_
#include <rtthread.h>
/**
* It will print more information on debug mode.
* #define RT_DEBUG_SFUD open debug mode */
#ifdef RT_DEBUG_SFUD
#define SFUD_DEBUG_MODE
#endif
#ifdef RT_DEBUG_SFUD
#define DBG_LVL DBG_LOG
#define SFUD_DEBUG(fmt, ...) LOG_D("(%s:%ld) "fmt"", __FILE__, __LINE__, ##__VA_ARGS__)
#else
#define DBG_LVL DBG_INFO
#endif /* RT_DEBUG_SFUD */
#define DBG_TAG "SFUD"
#include <rtdbg.h>
#define SFUD_INFO(...) LOG_I(__VA_ARGS__)
/**
* Using probe flash JEDEC SFDP parameter.
*/
#ifdef RT_SFUD_USING_SFDP
#define SFUD_USING_SFDP
#endif
/**
* SFUD will support QSPI mode.
*/
#ifdef RT_SFUD_USING_QSPI
#define SFUD_USING_QSPI
#endif
/**
* Using probe flash JEDEC ID then query defined supported flash chip information table. @see SFUD_FLASH_CHIP_TABLE
*/
#ifdef RT_SFUD_USING_FLASH_INFO_TABLE
#define SFUD_USING_FLASH_INFO_TABLE
#endif
#define SFUD_FLASH_DEVICE_TABLE {{0}}
#endif /* _SFUD_CFG_H_ */

View file

@ -0,0 +1,296 @@
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the macro definition head file for this library.
* Created on: 2016-04-23
*/
#ifndef _SFUD_DEF_H_
#define _SFUD_DEF_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <sfud_cfg.h>
#include "sfud_flash_def.h"
#ifdef __cplusplus
extern "C" {
#endif
/* debug print function. Must be implement by user. */
#ifdef SFUD_DEBUG_MODE
#ifndef SFUD_DEBUG
#define SFUD_DEBUG(...) sfud_log_debug(__FILE__, __LINE__, __VA_ARGS__)
#endif /* SFUD_DEBUG */
#else
#define SFUD_DEBUG(...)
#endif /* SFUD_DEBUG_MODE */
#ifndef SFUD_INFO
#define SFUD_INFO(...) sfud_log_info(__VA_ARGS__)
#endif
/* assert for developer. */
#ifdef SFUD_DEBUG_MODE
#define SFUD_ASSERT(EXPR) \
if (!(EXPR)) \
{ \
SFUD_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__); \
while (1); \
}
#else
#define SFUD_ASSERT(EXPR)
#endif
/**
* retry process
*
* @param delay delay function for every retry. NULL will not delay for every retry.
* @param retry retry counts
* @param result SFUD_ERR_TIMEOUT: retry timeout
*/
#define SFUD_RETRY_PROCESS(delay, retry, result) \
void (*__delay_temp)(void) = (void (*)(void))delay; \
if (retry == 0) {result = SFUD_ERR_TIMEOUT;break;} \
else {if (__delay_temp) {__delay_temp();} retry --;}
/* software version number */
#define SFUD_SW_VERSION "1.1.0"
/*
* all defined supported command
*/
#ifndef SFUD_CMD_WRITE_ENABLE
#define SFUD_CMD_WRITE_ENABLE 0x06
#endif
#ifndef SFUD_CMD_WRITE_DISABLE
#define SFUD_CMD_WRITE_DISABLE 0x04
#endif
#ifndef SFUD_CMD_READ_STATUS_REGISTER
#define SFUD_CMD_READ_STATUS_REGISTER 0x05
#endif
#ifndef SFUD_VOLATILE_SR_WRITE_ENABLE
#define SFUD_VOLATILE_SR_WRITE_ENABLE 0x50
#endif
#ifndef SFUD_CMD_WRITE_STATUS_REGISTER
#define SFUD_CMD_WRITE_STATUS_REGISTER 0x01
#endif
#ifndef SFUD_CMD_PAGE_PROGRAM
#define SFUD_CMD_PAGE_PROGRAM 0x02
#endif
#ifndef SFUD_CMD_AAI_WORD_PROGRAM
#define SFUD_CMD_AAI_WORD_PROGRAM 0xAD
#endif
#ifndef SFUD_CMD_ERASE_CHIP
#define SFUD_CMD_ERASE_CHIP 0xC7
#endif
#ifndef SFUD_CMD_READ_DATA
#define SFUD_CMD_READ_DATA 0x03
#endif
#ifndef SFUD_CMD_DUAL_OUTPUT_READ_DATA
#define SFUD_CMD_DUAL_OUTPUT_READ_DATA 0x3B
#endif
#ifndef SFUD_CMD_DUAL_IO_READ_DATA
#define SFUD_CMD_DUAL_IO_READ_DATA 0xBB
#endif
#ifndef SFUD_CMD_QUAD_IO_READ_DATA
#define SFUD_CMD_QUAD_IO_READ_DATA 0xEB
#endif
#ifndef SFUD_CMD_QUAD_OUTPUT_READ_DATA
#define SFUD_CMD_QUAD_OUTPUT_READ_DATA 0x6B
#endif
#ifndef SFUD_CMD_MANUFACTURER_DEVICE_ID
#define SFUD_CMD_MANUFACTURER_DEVICE_ID 0x90
#endif
#ifndef SFUD_CMD_JEDEC_ID
#define SFUD_CMD_JEDEC_ID 0x9F
#endif
#ifndef SFUD_CMD_READ_UNIQUE_ID
#define SFUD_CMD_READ_UNIQUE_ID 0x4B
#endif
#ifndef SFUD_CMD_READ_SFDP_REGISTER
#define SFUD_CMD_READ_SFDP_REGISTER 0x5A
#endif
#ifndef SFUD_CMD_ENABLE_RESET
#define SFUD_CMD_ENABLE_RESET 0x66
#endif
#ifndef SFUD_CMD_RESET
#define SFUD_CMD_RESET 0x99
#endif
#ifndef SFUD_CMD_ENTER_4B_ADDRESS_MODE
#define SFUD_CMD_ENTER_4B_ADDRESS_MODE 0xB7
#endif
#ifndef SFUD_CMD_EXIT_4B_ADDRESS_MODE
#define SFUD_CMD_EXIT_4B_ADDRESS_MODE 0xE9
#endif
#ifndef SFUD_WRITE_MAX_PAGE_SIZE
#define SFUD_WRITE_MAX_PAGE_SIZE 256
#endif
/* send dummy data for read data */
#ifndef SFUD_DUMMY_DATA
#define SFUD_DUMMY_DATA 0xFF
#endif
/* maximum number of erase type support on JESD216 (V1.0) */
#define SFUD_SFDP_ERASE_TYPE_MAX_NUM 4
/**
* status register bits
*/
enum {
SFUD_STATUS_REGISTER_BUSY = (1 << 0), /**< busing */
SFUD_STATUS_REGISTER_WEL = (1 << 1), /**< write enable latch */
SFUD_STATUS_REGISTER_SRP = (1 << 7), /**< status register protect */
};
/**
* error code
*/
typedef enum {
SFUD_SUCCESS = 0, /**< success */
SFUD_ERR_NOT_FOUND = 1, /**< not found or not supported */
SFUD_ERR_WRITE = 2, /**< write error */
SFUD_ERR_READ = 3, /**< read error */
SFUD_ERR_TIMEOUT = 4, /**< timeout error */
SFUD_ERR_ADDR_OUT_OF_BOUND = 5, /**< address is out of flash bound */
} sfud_err;
#ifdef SFUD_USING_QSPI
/**
* QSPI flash read cmd format
*/
typedef struct {
uint8_t instruction;
uint8_t instruction_lines;
uint8_t address_size;
uint8_t address_lines;
uint8_t alternate_bytes_lines;
uint8_t dummy_cycles;
uint8_t data_lines;
} sfud_qspi_read_cmd_format;
#endif /* SFUD_USING_QSPI */
/* SPI bus write read data function type */
typedef sfud_err (*spi_write_read_func)(const uint8_t *write_buf, size_t write_size, uint8_t *read_buf, size_t read_size);
#ifdef SFUD_USING_SFDP
/**
* the SFDP (Serial Flash Discoverable Parameters) parameter info which used on this library
*/
typedef struct {
bool available; /**< available when read SFDP OK */
uint8_t major_rev; /**< SFDP Major Revision */
uint8_t minor_rev; /**< SFDP Minor Revision */
uint16_t write_gran; /**< write granularity (bytes) */
uint8_t erase_4k; /**< 4 kilobyte erase is supported throughout the device */
uint8_t erase_4k_cmd; /**< 4 Kilobyte erase command */
bool sr_is_non_vola; /**< status register is supports non-volatile */
uint8_t vola_sr_we_cmd; /**< volatile status register write enable command */
bool addr_3_byte; /**< supports 3-Byte addressing */
bool addr_4_byte; /**< supports 4-Byte addressing */
uint32_t capacity; /**< flash capacity (bytes) */
struct {
uint32_t size; /**< erase sector size (bytes). 0x00: not available */
uint8_t cmd; /**< erase command */
} eraser[SFUD_SFDP_ERASE_TYPE_MAX_NUM]; /**< supported eraser types table */
//TODO lots of fast read-related stuff (like modes supported and number of wait states/dummy cycles needed in each)
} sfud_sfdp, *sfud_sfdp_t;
#endif
/**
* SPI device
*/
typedef struct __sfud_spi {
/* SPI device name */
char *name;
/* SPI bus write read data function */
sfud_err (*wr)(const struct __sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size);
#ifdef SFUD_USING_QSPI
/* QSPI fast read function */
sfud_err (*qspi_read)(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format,
uint8_t *read_buf, size_t read_size);
#endif
/* lock SPI bus */
void (*lock)(const struct __sfud_spi *spi);
/* unlock SPI bus */
void (*unlock)(const struct __sfud_spi *spi);
/* some user data */
void *user_data;
} sfud_spi, *sfud_spi_t;
/**
* serial flash device
*/
typedef struct {
char *name; /**< serial flash name */
size_t index; /**< index of flash device information table @see flash_table */
sfud_flash_chip chip; /**< flash chip information */
sfud_spi spi; /**< SPI device */
bool init_ok; /**< initialize OK flag */
bool addr_in_4_byte; /**< flash is in 4-Byte addressing */
struct {
void (*delay)(void); /**< every retry's delay */
size_t times; /**< default times for error retry */
} retry;
void *user_data; /**< some user data */
#ifdef SFUD_USING_QSPI
sfud_qspi_read_cmd_format read_cmd_format; /**< fast read cmd format */
#endif
#ifdef SFUD_USING_SFDP
sfud_sfdp sfdp; /**< serial flash discoverable parameters by JEDEC standard */
#endif
} sfud_flash, *sfud_flash_t;
#ifdef __cplusplus
}
#endif
#endif /* _SFUD_DEF_H_ */

View file

@ -0,0 +1,198 @@
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: It is the flash types and specification macro definition head file for this library.
* Created on: 2016-06-09
*/
#ifndef _SFUD_FLASH_DEF_H_
#define _SFUD_FLASH_DEF_H_
#include <stdint.h>
#include <sfud_cfg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* flash program(write) data mode
*/
enum sfud_write_mode {
SFUD_WM_PAGE_256B = 1 << 0, /**< write 1 to 256 bytes per page */
SFUD_WM_BYTE = 1 << 1, /**< byte write */
SFUD_WM_AAI = 1 << 2, /**< auto address increment */
SFUD_WM_DUAL_BUFFER = 1 << 3, /**< dual-buffer write, like AT45DB series */
};
/* manufacturer information */
typedef struct {
char *name;
uint8_t id;
} sfud_mf;
/* flash chip information */
typedef struct {
char *name; /**< flash chip name */
uint8_t mf_id; /**< manufacturer ID */
uint8_t type_id; /**< memory type ID */
uint8_t capacity_id; /**< capacity ID */
uint32_t capacity; /**< flash capacity (bytes) */
uint16_t write_mode; /**< write mode @see sfud_write_mode */
uint32_t erase_gran; /**< erase granularity (bytes) */
uint8_t erase_gran_cmd; /**< erase granularity size block command */
} sfud_flash_chip;
#ifdef SFUD_USING_QSPI
/* QSPI flash chip's extended information compared with SPI flash */
typedef struct {
uint8_t mf_id; /**< manufacturer ID */
uint8_t type_id; /**< memory type ID */
uint8_t capacity_id; /**< capacity ID */
uint8_t read_mode; /**< supported read mode on this qspi flash chip */
} sfud_qspi_flash_ext_info;
#endif
/* SFUD support manufacturer JEDEC ID */
#define SFUD_MF_ID_CYPRESS 0x01
#define SFUD_MF_ID_FUJITSU 0x04
#define SFUD_MF_ID_EON 0x1C
#define SFUD_MF_ID_ATMEL 0x1F
#define SFUD_MF_ID_MICRON 0x20
#define SFUD_MF_ID_AMIC 0x37
#define SFUD_MF_ID_NOR_MEM 0x52
#define SFUD_MF_ID_SANYO 0x62
#define SFUD_MF_ID_INTEL 0x89
#define SFUD_MF_ID_ESMT 0x8C
#define SFUD_MF_ID_FUDAN 0xA1
#define SFUD_MF_ID_HYUNDAI 0xAD
#define SFUD_MF_ID_SST 0xBF
#define SFUD_MF_ID_MACRONIX 0xC2
#define SFUD_MF_ID_GIGADEVICE 0xC8
#define SFUD_MF_ID_ISSI 0xD5
#define SFUD_MF_ID_WINBOND 0xEF
/* SFUD supported manufacturer information table */
#define SFUD_MF_TABLE \
{ \
{"Cypress", SFUD_MF_ID_CYPRESS}, \
{"Fujitsu", SFUD_MF_ID_FUJITSU}, \
{"EON", SFUD_MF_ID_EON}, \
{"Atmel", SFUD_MF_ID_ATMEL}, \
{"Micron", SFUD_MF_ID_MICRON}, \
{"AMIC", SFUD_MF_ID_AMIC}, \
{"Sanyo", SFUD_MF_ID_SANYO}, \
{"Intel", SFUD_MF_ID_INTEL}, \
{"ESMT", SFUD_MF_ID_ESMT}, \
{"Fudan", SFUD_MF_ID_FUDAN}, \
{"Hyundai", SFUD_MF_ID_HYUNDAI}, \
{"SST", SFUD_MF_ID_SST}, \
{"GigaDevice", SFUD_MF_ID_GIGADEVICE}, \
{"ISSI", SFUD_MF_ID_ISSI}, \
{"Winbond", SFUD_MF_ID_WINBOND}, \
{"Macronix", SFUD_MF_ID_MACRONIX}, \
{"NOR-MEM", SFUD_MF_ID_NOR_MEM}, \
}
#ifdef SFUD_USING_FLASH_INFO_TABLE
/* SFUD supported flash chip information table. If the flash not support JEDEC JESD216 standard,
* then the SFUD will find the flash chip information by this table. You can add other flash to here then
* notice me for update it. The configuration information name and index reference the sfud_flash_chip structure.
* | name | mf_id | type_id | capacity_id | capacity | write_mode | erase_gran | erase_gran_cmd |
*/
#define SFUD_FLASH_CHIP_TABLE \
{ \
{"AT45DB161E", SFUD_MF_ID_ATMEL, 0x26, 0x00, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_DUAL_BUFFER, 512, 0x81}, \
{"W25Q40BV", SFUD_MF_ID_WINBOND, 0x40, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25X40CL", SFUD_MF_ID_WINBOND, 0x30, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25X16AV", SFUD_MF_ID_WINBOND, 0x30, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q16BV", SFUD_MF_ID_WINBOND, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q32BV", SFUD_MF_ID_WINBOND, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q64CV", SFUD_MF_ID_WINBOND, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q64DW", SFUD_MF_ID_WINBOND, 0x60, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q128BV", SFUD_MF_ID_WINBOND, 0x40, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"W25Q256FV", SFUD_MF_ID_WINBOND, 0x40, 0x19, 32L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"SST25VF080B", SFUD_MF_ID_SST, 0x25, 0x8E, 1L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
{"SST25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
{"M25P32", SFUD_MF_ID_MICRON, 0x20, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
{"M25P80", SFUD_MF_ID_MICRON, 0x20, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
{"M25P40", SFUD_MF_ID_MICRON, 0x20, 0x13, 512L*1024L, SFUD_WM_PAGE_256B, 64L*1024L, 0xD8}, \
{"EN25Q32B", SFUD_MF_ID_EON, 0x30, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"GD25Q64B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, 8L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"GD25Q16B", SFUD_MF_ID_GIGADEVICE, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"GD25Q32C", SFUD_MF_ID_GIGADEVICE, 0x40, 0x16, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"S25FL216K", SFUD_MF_ID_CYPRESS, 0x40, 0x15, 2L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"S25FL032P", SFUD_MF_ID_CYPRESS, 0x02, 0x15, 4L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"A25L080", SFUD_MF_ID_AMIC, 0x30, 0x14, 1L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
{"F25L004", SFUD_MF_ID_ESMT, 0x20, 0x13, 512L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
{"PCT25VF016B", SFUD_MF_ID_SST, 0x25, 0x41, 2L*1024L*1024L, SFUD_WM_BYTE|SFUD_WM_AAI, 4096, 0x20}, \
{"NM25Q128EVB", SFUD_MF_ID_NOR_MEM, 0x21, 0x18, 16L*1024L*1024L, SFUD_WM_PAGE_256B, 4096, 0x20}, \
}
#endif /* SFUD_USING_FLASH_INFO_TABLE */
#ifdef SFUD_USING_QSPI
/* This table saves flash read-fast instructions in QSPI mode,
* SFUD can use this table to select the most appropriate read instruction for flash.
* | mf_id | type_id | capacity_id | qspi_read_mode |
*/
#define SFUD_FLASH_EXT_INFO_TABLE \
{ \
/* W25Q40BV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x13, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* W25Q80JV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* W25Q16BV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* W25Q32BV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_OUTPUT|QUAD_IO}, \
/* W25Q64JV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \
/* W25Q128JV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \
/* W25Q256FV */ \
{SFUD_MF_ID_WINBOND, 0x40, 0x19, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \
/* EN25Q32B */ \
{SFUD_MF_ID_EON, 0x30, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT|QUAD_IO}, \
/* S25FL216K */ \
{SFUD_MF_ID_CYPRESS, 0x40, 0x15, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* A25L080 */ \
{SFUD_MF_ID_AMIC, 0x30, 0x14, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO}, \
/* A25LQ64 */ \
{SFUD_MF_ID_AMIC, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_IO}, \
/* MX25L3206E and KH25L3206E */ \
{SFUD_MF_ID_MACRONIX, 0x20, 0x16, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* MX25L51245G */ \
{SFUD_MF_ID_MACRONIX, 0x20, 0x1A, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \
/* GD25Q64B */ \
{SFUD_MF_ID_GIGADEVICE, 0x40, 0x17, NORMAL_SPI_READ|DUAL_OUTPUT}, \
/* NM25Q128EVB */ \
{SFUD_MF_ID_NOR_MEM, 0x21, 0x18, NORMAL_SPI_READ|DUAL_OUTPUT|DUAL_IO|QUAD_OUTPUT|QUAD_IO}, \
}
#endif /* SFUD_USING_QSPI */
#ifdef __cplusplus
}
#endif
#endif /* _SFUD_FLASH_DEF_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,387 @@
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: Analyze the SFDP (Serial Flash Discoverable Parameters) which from JESD216/A/B (V1.X) standard.
* JESD216 (V1.0) document: http://www.jedec.org/sites/default/files/docs/JESD216.pdf
* JESD216A (V1.5) document: http://www.jedec.org/sites/default/files/docs/JESD216A.pdf
* JESD216B (V1.6) document: http://www.jedec.org/sites/default/files/docs/JESD216B.pdf
*
* Created on: 2016-05-26
*/
#include "../inc/sfud.h"
/**
* JEDEC Standard JESD216 Terms and definitions:
*
* DWORD: Four consecutive 8-bit bytes used as the basic 32-bit building block for headers and parameter tables.
*
* Sector: The minimum granularity - size and alignment - of an area that can be erased in the data array
* of a flash memory device. Different areas within the address range of the data array may have a different
* minimum erase granularity (sector size).
*/
#ifdef SFUD_USING_SFDP
/* support maximum SFDP major revision by driver */
#define SUPPORT_MAX_SFDP_MAJOR_REV 1
/* the JEDEC basic flash parameter table length is 9 DWORDs (288-bit) on JESD216 (V1.0) initial release standard */
#define BASIC_TABLE_LEN 9
/* the smallest eraser in SFDP eraser table */
#define SMALLEST_ERASER_INDEX 0
/**
* SFDP parameter header structure
*/
typedef struct {
uint8_t id; /**< Parameter ID LSB */
uint8_t minor_rev; /**< Parameter minor revision */
uint8_t major_rev; /**< Parameter major revision */
uint8_t len; /**< Parameter table length(in double words) */
uint32_t ptp; /**< Parameter table 24bit pointer (byte address) */
} sfdp_para_header;
static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size);
static bool read_sfdp_header(sfud_flash *flash);
static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header);
static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header);
/* ../port/sfup_port.c */
extern void sfud_log_debug(const char *file, const long line, const char *format, ...);
extern void sfud_log_info(const char *format, ...);
/**
* Read SFDP parameter information
*
* @param flash flash device
*
* @return true: read OK
*/
bool sfud_read_sfdp(sfud_flash *flash) {
SFUD_ASSERT(flash);
/* JEDEC basic flash parameter header */
sfdp_para_header basic_header;
if (read_sfdp_header(flash) && read_basic_header(flash, &basic_header)) {
return read_basic_table(flash, &basic_header);
} else {
SFUD_INFO("Warning: Read SFDP parameter header information failed. The %s does not support JEDEC SFDP.", flash->name);
return false;
}
}
/**
* Read SFDP parameter header
*
* @param flash flash device
*
* @return true: read OK
*/
static bool read_sfdp_header(sfud_flash *flash) {
sfud_sfdp *sfdp = &flash->sfdp;
/* The SFDP header is located at address 000000h of the SFDP data structure.
* It identifies the SFDP Signature, the number of parameter headers, and the SFDP revision numbers. */
/* sfdp parameter header address */
uint32_t header_addr = 0;
/* each parameter header being 2 DWORDs (64-bit) */
uint8_t header[2 * 4] = { 0 };
SFUD_ASSERT(flash);
sfdp->available = false;
/* read SFDP header */
if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) {
SFUD_INFO("Error: Can't read SFDP header.");
return false;
}
/* check SFDP header */
if (!(header[0] == 'S' &&
header[1] == 'F' &&
header[2] == 'D' &&
header[3] == 'P')) {
SFUD_DEBUG("Error: Check SFDP signature error. It's must be 50444653h('S' 'F' 'D' 'P').");
return false;
}
sfdp->minor_rev = header[4];
sfdp->major_rev = header[5];
if (sfdp->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) {
SFUD_INFO("Error: This reversion(V%d.%d) of SFDP is not supported.", sfdp->major_rev, sfdp->minor_rev);
return false;
}
SFUD_DEBUG("Check SFDP header is OK. The reversion is V%d.%d, NPN is %d.", sfdp->major_rev, sfdp->minor_rev,
header[6]);
return true;
}
/**
* Read JEDEC basic parameter header
*
* @param flash flash device
*
* @return true: read OK
*/
static bool read_basic_header(const sfud_flash *flash, sfdp_para_header *basic_header) {
/* The basic parameter header is mandatory, is defined by this standard, and starts at byte offset 08h. */
uint32_t header_addr = 8;
/* each parameter header being 2 DWORDs (64-bit) */
uint8_t header[2 * 4] = { 0 };
SFUD_ASSERT(flash);
SFUD_ASSERT(basic_header);
/* read JEDEC basic flash parameter header */
if (read_sfdp_data(flash, header_addr, header, sizeof(header)) != SFUD_SUCCESS) {
SFUD_INFO("Error: Can't read JEDEC basic flash parameter header.");
return false;
}
basic_header->id = header[0];
basic_header->minor_rev = header[1];
basic_header->major_rev = header[2];
basic_header->len = header[3];
basic_header->ptp = (long)header[4] | (long)header[5] << 8 | (long)header[6] << 16;
/* check JEDEC basic flash parameter header */
if (basic_header->major_rev > SUPPORT_MAX_SFDP_MAJOR_REV) {
SFUD_INFO("Error: This reversion(V%d.%d) of JEDEC basic flash parameter header is not supported.",
basic_header->major_rev, basic_header->minor_rev);
return false;
}
if (basic_header->len < BASIC_TABLE_LEN) {
SFUD_INFO("Error: The JEDEC basic flash parameter table length (now is %d) error.", basic_header->len);
return false;
}
SFUD_DEBUG("Check JEDEC basic flash parameter header is OK. The table id is %d, reversion is V%d.%d,"
" length is %d, parameter table pointer is 0x%06lX.", basic_header->id, basic_header->major_rev,
basic_header->minor_rev, basic_header->len, basic_header->ptp);
return true;
}
/**
* Read JEDEC basic parameter table
*
* @param flash flash device
*
* @return true: read OK
*/
static bool read_basic_table(sfud_flash *flash, sfdp_para_header *basic_header) {
sfud_sfdp *sfdp = &flash->sfdp;
/* parameter table address */
uint32_t table_addr = basic_header->ptp;
/* parameter table */
uint8_t table[BASIC_TABLE_LEN * 4] = { 0 }, i, j;
SFUD_ASSERT(flash);
SFUD_ASSERT(basic_header);
/* read JEDEC basic flash parameter table */
if (read_sfdp_data(flash, table_addr, table, sizeof(table)) != SFUD_SUCCESS) {
SFUD_INFO("Warning: Can't read JEDEC basic flash parameter table.");
return false;
}
/* print JEDEC basic flash parameter table info */
SFUD_DEBUG("JEDEC basic flash parameter table info:");
SFUD_DEBUG("MSB-LSB 3 2 1 0");
for (i = 0; i < BASIC_TABLE_LEN; i++) {
SFUD_DEBUG("[%04d] 0x%02X 0x%02X 0x%02X 0x%02X", i + 1, table[i * 4 + 3], table[i * 4 + 2], table[i * 4 + 1],
table[i * 4]);
}
/* get block/sector 4 KB erase supported and command */
sfdp->erase_4k_cmd = table[1];
switch (table[0] & 0x03) {
case 1:
sfdp->erase_4k = true;
SFUD_DEBUG("4 KB Erase is supported throughout the device. Command is 0x%02X.", sfdp->erase_4k_cmd);
break;
case 3:
sfdp->erase_4k = false;
SFUD_DEBUG("Uniform 4 KB erase is unavailable for this device.");
break;
default:
SFUD_INFO("Error: Uniform 4 KB erase supported information error.");
return false;
}
/* get write granularity */
//TODO 目前为 1.0 所提供的方式,后期支持 V1.5 及以上的方式读取 page size
switch ((table[0] & (0x01 << 2)) >> 2) {
case 0:
sfdp->write_gran = 1;
SFUD_DEBUG("Write granularity is 1 byte.");
break;
case 1:
sfdp->write_gran = 256;
SFUD_DEBUG("Write granularity is 64 bytes or larger.");
break;
}
/* volatile status register block protect bits */
switch ((table[0] & (0x01 << 3)) >> 3) {
case 0:
/* Block Protect bits in device's status register are solely non-volatile or may be
* programmed either as volatile using the 50h instruction for write enable or non-volatile
* using the 06h instruction for write enable.
*/
sfdp->sr_is_non_vola = true;
SFUD_DEBUG("Target flash status register is non-volatile.");
break;
case 1:
/* block protect bits in device's status register are solely volatile. */
sfdp->sr_is_non_vola = false;
SFUD_DEBUG("Block Protect bits in device's status register are solely volatile.");
/* write enable instruction select for writing to volatile status register */
switch ((table[0] & (0x01 << 4)) >> 4) {
case 0:
sfdp->vola_sr_we_cmd = SFUD_VOLATILE_SR_WRITE_ENABLE;
SFUD_DEBUG("Flash device requires instruction 50h as the write enable prior "
"to performing a volatile write to the status register.");
break;
case 1:
sfdp->vola_sr_we_cmd = SFUD_CMD_WRITE_ENABLE;
SFUD_DEBUG("Flash device requires instruction 06h as the write enable prior "
"to performing a volatile write to the status register.");
break;
}
break;
}
/* get address bytes, number of bytes used in addressing flash array read, write and erase. */
switch ((table[2] & (0x03 << 1)) >> 1) {
case 0:
sfdp->addr_3_byte = true;
sfdp->addr_4_byte = false;
SFUD_DEBUG("3-Byte only addressing.");
break;
case 1:
sfdp->addr_3_byte = true;
sfdp->addr_4_byte = true;
SFUD_DEBUG("3- or 4-Byte addressing.");
break;
case 2:
sfdp->addr_3_byte = false;
sfdp->addr_4_byte = true;
SFUD_DEBUG("4-Byte only addressing.");
break;
default:
sfdp->addr_3_byte = false;
sfdp->addr_4_byte = false;
SFUD_INFO("Error: Read address bytes error!");
return false;
}
/* get flash memory capacity */
uint32_t table2_temp = ((long)table[7] << 24) | ((long)table[6] << 16) | ((long)table[5] << 8) | (long)table[4];
switch ((table[7] & (0x01 << 7)) >> 7) {
case 0:
sfdp->capacity = 1 + (table2_temp >> 3);
break;
case 1:
table2_temp &= 0x7FFFFFFF;
if (table2_temp > sizeof(sfdp->capacity) * 8 + 3) {
sfdp->capacity = 0;
SFUD_INFO("Error: The flash capacity is grater than 32 Gb/ 4 GB! Not Supported.");
return false;
}
sfdp->capacity = 1L << (table2_temp - 3);
break;
}
SFUD_DEBUG("Capacity is %ld Bytes.", sfdp->capacity);
/* get erase size and erase command */
for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
if (table[28 + 2 * i] != 0x00) {
sfdp->eraser[j].size = 1L << table[28 + 2 * i];
sfdp->eraser[j].cmd = table[28 + 2 * i + 1];
SFUD_DEBUG("Flash device supports %ldKB block erase. Command is 0x%02X.", sfdp->eraser[j].size / 1024,
sfdp->eraser[j].cmd);
j++;
}
}
/* sort the eraser size from small to large */
for (i = 0, j = 0; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++) {
if (sfdp->eraser[i].size) {
for (j = i + 1; j < SFUD_SFDP_ERASE_TYPE_MAX_NUM; j++) {
if (sfdp->eraser[j].size != 0 && sfdp->eraser[i].size > sfdp->eraser[j].size) {
/* swap the small eraser */
uint32_t temp_size = sfdp->eraser[i].size;
uint8_t temp_cmd = sfdp->eraser[i].cmd;
sfdp->eraser[i].size = sfdp->eraser[j].size;
sfdp->eraser[i].cmd = sfdp->eraser[j].cmd;
sfdp->eraser[j].size = temp_size;
sfdp->eraser[j].cmd = temp_cmd;
}
}
}
}
sfdp->available = true;
return true;
}
static sfud_err read_sfdp_data(const sfud_flash *flash, uint32_t addr, uint8_t *read_buf, size_t size) {
uint8_t cmd[] = {
SFUD_CMD_READ_SFDP_REGISTER,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
(addr >> 0) & 0xFF,
SFUD_DUMMY_DATA,
};
SFUD_ASSERT(flash);
SFUD_ASSERT(addr < 1L << 24);
SFUD_ASSERT(read_buf);
SFUD_ASSERT(flash->spi.wr);
return flash->spi.wr(&flash->spi, cmd, sizeof(cmd), read_buf, size);
}
/**
* get the most suitable eraser for erase process from SFDP parameter
*
* @param flash flash device
* @param addr start address
* @param erase_size will be erased size
*
* @return the eraser index of SFDP eraser table @see sfud_sfdp.eraser[]
*/
size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr, size_t erase_size) {
size_t index = SMALLEST_ERASER_INDEX, i;
/* only used when flash supported SFDP */
SFUD_ASSERT(flash->sfdp.available);
/* the address isn't align by smallest eraser's size, then use the smallest eraser */
if (addr % flash->sfdp.eraser[SMALLEST_ERASER_INDEX].size) {
return SMALLEST_ERASER_INDEX;
}
/* Find the suitable eraser.
* The largest size eraser is at the end of eraser table.
* In order to decrease erase command counts, so the find process is from the end of eraser table. */
for (i = SFUD_SFDP_ERASE_TYPE_MAX_NUM - 1;; i--) {
if ((flash->sfdp.eraser[i].size != 0) && (erase_size >= flash->sfdp.eraser[i].size)
&& (addr % flash->sfdp.eraser[i].size == 0)) {
index = i;
break;
}
if (i == SMALLEST_ERASER_INDEX) {
break;
}
}
return index;
}
#endif /* SFUD_USING_SFDP */

View file

@ -0,0 +1,525 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-10-11 kyle first version
*/
#include <spi-bit-ops.h>
#include <rtdevice.h>
#define DBG_TAG "SPI"
#ifdef RT_SPI_BITOPS_DEBUG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_ERROR
#endif
#include <rtdbg.h>
#define TOG_SCLK(ops) ops->tog_sclk(ops->data)
#define SET_SCLK(ops, val) ops->set_sclk(ops->data, val)
#define SET_MOSI(ops, val) ops->set_mosi(ops->data, val)
#define SET_MISO(ops, val) ops->set_miso(ops->data, val)
#define GET_SCLK(ops) ops->get_sclk(ops->data)
#define GET_MOSI(ops) ops->get_mosi(ops->data)
#define GET_MISO(ops) ops->get_miso(ops->data)
#define DIR_MOSI(ops, val) ops->dir_mosi(ops->data, val)
#define DIR_MISO(ops, val) ops->dir_miso(ops->data, val)
rt_inline void spi_delay(struct rt_spi_bit_ops *ops)
{
ops->udelay((ops->delay_us + 1) >> 1);
}
rt_inline void spi_delay2(struct rt_spi_bit_ops *ops)
{
ops->udelay(ops->delay_us);
}
#define SCLK_H(ops) SET_SCLK(ops, 1)
#define SCLK_L(ops) SET_SCLK(ops, 0)
#define MOSI_H(ops) SET_MOSI(ops, 1)
#define MOSI_L(ops) SET_MOSI(ops, 0)
#define MOSI_IN(ops) DIR_MOSI(ops, 1)
#define MOSI_OUT(ops) DIR_MOSI(ops, 0)
#define MISO_IN(ops) DIR_MISO(ops, 1)
#define MISO_OUT(ops) DIR_MISO(ops, 0)
rt_inline rt_ssize_t spi_xfer_4line_data8(struct rt_spi_bit_ops *ops,
struct rt_spi_configuration *config,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
int i = 0;
RT_ASSERT(ops != RT_NULL);
RT_ASSERT(length != 0);
{
const rt_uint8_t *send_ptr = send_buf;
rt_uint8_t *recv_ptr = recv_buf;
rt_uint32_t size = length;
while (size--)
{
rt_uint8_t tx_data = 0xFF;
rt_uint8_t rx_data = 0xFF;
rt_uint8_t bit = 0;
if (send_buf != RT_NULL)
{
tx_data = *send_ptr++;
}
for (i = 0; i < 8; i++)
{
if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (7 - i)); }
else { bit = tx_data & (0x1 << i); }
if (bit) MOSI_H(ops);
else MOSI_L(ops);
spi_delay2(ops);
TOG_SCLK(ops);
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
else { rx_data >>= 1; bit = 0x80; }
if (GET_MISO(ops)) { rx_data |= bit; }
else { rx_data &= ~bit; }
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
{
TOG_SCLK(ops);
}
}
if (recv_buf != RT_NULL)
{
*recv_ptr++ = rx_data;
}
}
}
return length;
}
rt_inline rt_ssize_t spi_xfer_4line_data16(struct rt_spi_bit_ops *ops,
struct rt_spi_configuration *config,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
int i = 0;
RT_ASSERT(ops != RT_NULL);
RT_ASSERT(length != 0);
{
const rt_uint16_t *send_ptr = send_buf;
rt_uint16_t *recv_ptr = recv_buf;
rt_uint32_t size = length;
while (size--)
{
rt_uint16_t tx_data = 0xFFFF;
rt_uint16_t rx_data = 0xFFFF;
rt_uint16_t bit = 0;
if (send_buf != RT_NULL)
{
tx_data = *send_ptr++;
}
for (i = 0; i < 16; i++)
{
if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (15 - i)); }
else { bit = tx_data & (0x1 << i); }
if (bit) MOSI_H(ops);
else MOSI_L(ops);
spi_delay2(ops);
TOG_SCLK(ops);
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
else { rx_data >>= 1; bit = 0x8000; }
if (GET_MISO(ops)) { rx_data |= bit; }
else { rx_data &= ~bit; }
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
{
TOG_SCLK(ops);
}
}
if (recv_buf != RT_NULL)
{
*recv_ptr++ = rx_data;
}
}
}
return length;
}
rt_inline rt_ssize_t spi_xfer_3line_data8(struct rt_spi_bit_ops *ops,
struct rt_spi_configuration *config,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
int i = 0;
RT_ASSERT(ops != RT_NULL);
RT_ASSERT(length != 0);
{
const rt_uint8_t *send_ptr = send_buf;
rt_uint8_t *recv_ptr = recv_buf;
rt_uint32_t size = length;
rt_uint8_t send_flg = 0;
if ((send_buf != RT_NULL) || (recv_buf == RT_NULL))
{
MOSI_OUT(ops);
send_flg = 1;
}
else
{
MOSI_IN(ops);
}
while (size--)
{
rt_uint8_t tx_data = 0xFF;
rt_uint8_t rx_data = 0xFF;
rt_uint8_t bit = 0;
if (send_buf != RT_NULL)
{
tx_data = *send_ptr++;
}
if (send_flg)
{
for (i = 0; i < 8; i++)
{
if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (7 - i)); }
else { bit = tx_data & (0x1 << i); }
if (bit) MOSI_H(ops);
else MOSI_L(ops);
spi_delay2(ops);
TOG_SCLK(ops);
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
{
TOG_SCLK(ops);
}
}
rx_data = tx_data;
}
else
{
for (i = 0; i < 8; i++)
{
spi_delay2(ops);
TOG_SCLK(ops);
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x01; }
else { rx_data >>= 1; bit = 0x80; }
if (GET_MOSI(ops)) { rx_data |= bit; }
else { rx_data &= ~bit; }
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 7))
{
TOG_SCLK(ops);
}
}
}
if (recv_buf != RT_NULL)
{
*recv_ptr++ = rx_data;
}
}
if (!send_flg)
{
MOSI_OUT(ops);
}
}
return length;
}
rt_inline rt_ssize_t spi_xfer_3line_data16(struct rt_spi_bit_ops *ops,
struct rt_spi_configuration *config,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
int i = 0;
RT_ASSERT(ops != RT_NULL);
RT_ASSERT(length != 0);
{
const rt_uint16_t *send_ptr = send_buf;
rt_uint16_t *recv_ptr = recv_buf;
rt_uint32_t size = length;
rt_uint8_t send_flg = 0;
if ((send_buf != RT_NULL) || (recv_buf == RT_NULL))
{
MOSI_OUT(ops);
send_flg = 1;
}
else
{
MOSI_IN(ops);
}
while (size--)
{
rt_uint16_t tx_data = 0xFFFF;
rt_uint16_t rx_data = 0xFFFF;
rt_uint16_t bit = 0;
if (send_buf != RT_NULL)
{
tx_data = *send_ptr++;
}
if (send_flg)
{
for (i = 0; i < 16; i++)
{
if (config->mode & RT_SPI_MSB) { bit = tx_data & (0x1 << (15 - i)); }
else { bit = tx_data & (0x1 << i); }
if (bit) MOSI_H(ops);
else MOSI_L(ops);
spi_delay2(ops);
TOG_SCLK(ops);
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
{
TOG_SCLK(ops);
}
}
rx_data = tx_data;
}
else
{
for (i = 0; i < 16; i++)
{
spi_delay2(ops);
TOG_SCLK(ops);
if (config->mode & RT_SPI_MSB) { rx_data <<= 1; bit = 0x0001; }
else { rx_data >>= 1; bit = 0x8000; }
if (GET_MOSI(ops)) { rx_data |= bit; }
else { rx_data &= ~bit; }
spi_delay2(ops);
if (!(config->mode & RT_SPI_CPHA) || (size != 0) || (i < 15))
{
TOG_SCLK(ops);
}
}
}
if (recv_buf != RT_NULL)
{
*recv_ptr++ = rx_data;
}
}
if (!send_flg)
{
MOSI_OUT(ops);
}
}
return length;
}
rt_err_t spi_bit_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
{
struct rt_spi_bit_obj *obj = rt_container_of(device->bus, struct rt_spi_bit_obj, bus);
struct rt_spi_bit_ops *ops = obj->ops;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(configuration != RT_NULL);
if (configuration->mode & RT_SPI_SLAVE)
{
return -RT_EIO;
}
if (configuration->mode & RT_SPI_CPOL)
{
SCLK_H(ops);
}
else
{
SCLK_L(ops);
}
if (configuration->max_hz < 200000)
{
ops->delay_us = 1;
}
else
{
ops->delay_us = 0;
}
rt_memcpy(&obj->config, configuration, sizeof(struct rt_spi_configuration));
return RT_EOK;
}
rt_ssize_t spi_bit_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
struct rt_spi_bit_obj *obj = rt_container_of(device->bus, struct rt_spi_bit_obj, bus);
struct rt_spi_bit_ops *ops = obj->ops;
struct rt_spi_configuration *config = &obj->config;
rt_base_t cs_pin = device->cs_pin;
RT_ASSERT(device != NULL);
RT_ASSERT(message != NULL);
#ifdef RT_SPI_BITOPS_DEBUG
if (!ops->tog_sclk || !ops->set_sclk || !ops->get_sclk)
{
LOG_E("SPI bus error, SCLK line not defined");
}
if (!ops->set_mosi || !ops->get_mosi)
{
LOG_E("SPI bus error, MOSI line not defined");
}
if (!ops->set_miso || !ops->get_miso)
{
LOG_E("SPI bus error, MISO line not defined");
}
#endif
/* take CS */
if (message->cs_take && (cs_pin != PIN_NONE))
{
LOG_I("spi take cs\n");
rt_pin_write(cs_pin, PIN_LOW);
spi_delay(ops);
/* spi phase */
if (config->mode & RT_SPI_CPHA)
{
spi_delay(ops);
TOG_SCLK(ops);
}
}
if (config->mode & RT_SPI_3WIRE)
{
if (config->data_width <= 8)
{
spi_xfer_3line_data8(ops,
config,
message->send_buf,
message->recv_buf,
message->length);
}
else if (config->data_width <= 16)
{
spi_xfer_3line_data16(ops,
config,
message->send_buf,
message->recv_buf,
message->length);
}
}
else
{
if (config->data_width <= 8)
{
spi_xfer_4line_data8(ops,
config,
message->send_buf,
message->recv_buf,
message->length);
}
else if (config->data_width <= 16)
{
spi_xfer_4line_data16(ops,
config,
message->send_buf,
message->recv_buf,
message->length);
}
}
/* release CS */
if (message->cs_release && (cs_pin != PIN_NONE))
{
spi_delay(ops);
rt_pin_write(cs_pin, PIN_HIGH);
LOG_I("spi release cs\n");
}
return message->length;
}
static const struct rt_spi_ops spi_bit_bus_ops =
{
.configure = spi_bit_configure,
.xfer = spi_bit_xfer,
};
rt_err_t rt_spi_bit_add_bus(struct rt_spi_bit_obj *obj,
const char *bus_name,
struct rt_spi_bit_ops *ops)
{
obj->ops = ops;
obj->config.data_width = 8;
obj->config.max_hz = 1 * 1000 * 1000;
obj->config.mode = RT_SPI_MASTER | RT_SPI_MSB | RT_SPI_MODE_0;
/* idle status */
if (obj->config.mode & RT_SPI_CPOL) SCLK_H(ops);
else SCLK_L(ops);
return rt_spi_bus_register(&obj->bus, bus_name, &spi_bit_bus_ops);
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2021-10-11 kyle first version
* 2022-6-14 solar Remove the const attribute of private data in ops
*/
#ifndef __SPI_BIT_OPS_H__
#define __SPI_BIT_OPS_H__
#include <rtdevice.h>
#ifdef __cplusplus
extern "C" {
#endif
struct rt_spi_bit_ops
{
void *data; /* private data for lowlevel routines */
void (*const tog_sclk)(void *data);
void (*const set_sclk)(void *data, rt_int32_t state);
void (*const set_mosi)(void *data, rt_int32_t state);
void (*const set_miso)(void *data, rt_int32_t state);
rt_int32_t (*const get_sclk)(void *data);
rt_int32_t (*const get_mosi)(void *data);
rt_int32_t (*const get_miso)(void *data);
void (*const dir_mosi)(void *data, rt_int32_t state);
void (*const dir_miso)(void *data, rt_int32_t state);
void (*const udelay)(rt_uint32_t us);
rt_uint32_t delay_us; /* sclk, mosi and miso line delay */
};
struct rt_spi_bit_obj
{
struct rt_spi_bus bus;
struct rt_spi_bit_ops *ops;
struct rt_spi_configuration config;
};
rt_err_t rt_spi_bit_add_bus(struct rt_spi_bit_obj *obj,
const char *bus_name,
struct rt_spi_bit_ops *ops);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,513 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-01-08 bernard first version.
* 2012-02-03 bernard add const attribute to the ops.
* 2012-05-15 dzzxzz fixed the return value in attach_device.
* 2012-05-18 bernard Changed SPI message to message list.
* Added take/release SPI device/bus interface.
* 2012-09-28 aozima fixed rt_spi_release_bus assert error.
*/
#include <drivers/spi.h>
#define DBG_TAG "spi.core"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
extern rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name);
extern rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name);
rt_err_t rt_spi_bus_register(struct rt_spi_bus *bus,
const char *name,
const struct rt_spi_ops *ops)
{
rt_err_t result;
result = rt_spi_bus_device_init(bus, name);
if (result != RT_EOK)
return result;
/* initialize mutex lock */
rt_mutex_init(&(bus->lock), name, RT_IPC_FLAG_PRIO);
/* set ops */
bus->ops = ops;
/* initialize owner */
bus->owner = RT_NULL;
/* set bus mode */
bus->mode = RT_SPI_BUS_MODE_SPI;
return RT_EOK;
}
rt_err_t rt_spi_bus_attach_device_cspin(struct rt_spi_device *device,
const char *name,
const char *bus_name,
rt_base_t cs_pin,
void *user_data)
{
rt_err_t result;
rt_device_t bus;
/* get physical spi bus */
bus = rt_device_find(bus_name);
if (bus != RT_NULL && bus->type == RT_Device_Class_SPIBUS)
{
device->bus = (struct rt_spi_bus *)bus;
/* initialize spidev device */
result = rt_spidev_device_init(device, name);
if (result != RT_EOK)
return result;
if(cs_pin != PIN_NONE)
{
rt_pin_mode(cs_pin, PIN_MODE_OUTPUT);
}
rt_memset(&device->config, 0, sizeof(device->config));
device->parent.user_data = user_data;
device->cs_pin = cs_pin;
return RT_EOK;
}
/* not found the host bus */
return -RT_ERROR;
}
rt_err_t rt_spi_bus_attach_device(struct rt_spi_device *device,
const char *name,
const char *bus_name,
void *user_data)
{
return rt_spi_bus_attach_device_cspin(device, name, bus_name, PIN_NONE, user_data);
}
rt_err_t rt_spi_configure(struct rt_spi_device *device,
struct rt_spi_configuration *cfg)
{
rt_err_t result = -RT_ERROR;
RT_ASSERT(device != RT_NULL);
/* set configuration */
device->config.data_width = cfg->data_width;
device->config.mode = cfg->mode & RT_SPI_MODE_MASK ;
device->config.max_hz = cfg->max_hz ;
if (device->cs_pin != PIN_NONE)
{
if (device->config.mode & RT_SPI_CS_HIGH)
rt_pin_write(device->cs_pin, PIN_LOW);
else
rt_pin_write(device->cs_pin, PIN_HIGH);
}
if (device->bus != RT_NULL)
{
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner == device)
{
/* current device is using, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result != RT_EOK)
{
/* configure SPI bus failed */
LOG_E("SPI device %s configuration failed", device->parent.parent.name);
}
}
/* release lock */
rt_mutex_release(&(device->bus->lock));
}
}
else
{
result = RT_EOK;
}
return result;
}
rt_err_t rt_spi_send_then_send(struct rt_spi_device *device,
const void *send_buf1,
rt_size_t send_length1,
const void *send_buf2,
rt_size_t send_length2)
{
rt_err_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
LOG_E("SPI device %s configuration failed", device->parent.parent.name);
goto __exit;
}
}
/* send data1 */
message.send_buf = send_buf1;
message.recv_buf = RT_NULL;
message.length = send_length1;
message.cs_take = 1;
message.cs_release = 0;
message.next = RT_NULL;
result = device->bus->ops->xfer(device, &message);
if (result < 0)
{
LOG_E("SPI device %s transfer failed", device->parent.parent.name);
goto __exit;
}
/* send data2 */
message.send_buf = send_buf2;
message.recv_buf = RT_NULL;
message.length = send_length2;
message.cs_take = 0;
message.cs_release = 1;
message.next = RT_NULL;
result = device->bus->ops->xfer(device, &message);
if (result < 0)
{
LOG_E("SPI device %s transfer failed", device->parent.parent.name);
goto __exit;
}
result = RT_EOK;
}
else
{
return -RT_EIO;
}
__exit:
rt_mutex_release(&(device->bus->lock));
return result;
}
rt_err_t rt_spi_send_then_recv(struct rt_spi_device *device,
const void *send_buf,
rt_size_t send_length,
void *recv_buf,
rt_size_t recv_length)
{
rt_err_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
LOG_E("SPI device %s configuration failed", device->parent.parent.name);
goto __exit;
}
}
/* send data */
message.send_buf = send_buf;
message.recv_buf = RT_NULL;
message.length = send_length;
message.cs_take = 1;
message.cs_release = 0;
message.next = RT_NULL;
result = device->bus->ops->xfer(device, &message);
if (result < 0)
{
LOG_E("SPI device %s transfer failed", device->parent.parent.name);
goto __exit;
}
/* recv data */
message.send_buf = RT_NULL;
message.recv_buf = recv_buf;
message.length = recv_length;
message.cs_take = 0;
message.cs_release = 1;
message.next = RT_NULL;
result = device->bus->ops->xfer(device, &message);
if (result < 0)
{
LOG_E("SPI device %s transfer failed", device->parent.parent.name);
goto __exit;
}
result = RT_EOK;
}
else
{
return -RT_EIO;
}
__exit:
rt_mutex_release(&(device->bus->lock));
return result;
}
rt_ssize_t rt_spi_transfer(struct rt_spi_device *device,
const void *send_buf,
void *recv_buf,
rt_size_t length)
{
rt_ssize_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result == RT_EOK)
{
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
LOG_E("SPI device %s configuration failed", device->parent.parent.name);
goto __exit;
}
}
/* 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 = RT_NULL;
/* transfer message */
result = device->bus->ops->xfer(device, &message);
if (result < 0)
{
LOG_E("SPI device %s transfer failed", device->parent.parent.name);
goto __exit;
}
}
else
{
return -RT_EIO;
}
__exit:
rt_mutex_release(&(device->bus->lock));
return result;
}
rt_err_t rt_spi_sendrecv16(struct rt_spi_device *device,
rt_uint16_t senddata,
rt_uint16_t *recvdata)
{
rt_err_t result;
rt_uint16_t tmp;
if (device->config.mode & RT_SPI_MSB)
{
tmp = ((senddata & 0xff00) >> 8) | ((senddata & 0x00ff) << 8);
senddata = tmp;
}
result = rt_spi_send_then_recv(device, &senddata, 2, recvdata, 2);
if(result != RT_EOK)
{
return result;
}
if (device->config.mode & RT_SPI_MSB)
{
tmp = ((*recvdata & 0xff00) >> 8) | ((*recvdata & 0x00ff) << 8);
*recvdata = tmp;
}
return result;
}
struct rt_spi_message *rt_spi_transfer_message(struct rt_spi_device *device,
struct rt_spi_message *message)
{
rt_err_t result;
struct rt_spi_message *index;
RT_ASSERT(device != RT_NULL);
/* get first message */
index = message;
if (index == RT_NULL)
return index;
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
return index;
}
/* configure SPI bus */
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
goto __exit;
}
}
/* transmit each SPI message */
while (index != RT_NULL)
{
/* transmit SPI message */
result = device->bus->ops->xfer(device, index);
if (result < 0)
{
break;
}
index = index->next;
}
__exit:
/* release bus lock */
rt_mutex_release(&(device->bus->lock));
return index;
}
rt_err_t rt_spi_take_bus(struct rt_spi_device *device)
{
rt_err_t result = RT_EOK;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
return -RT_EBUSY;
}
/* configure SPI bus */
if (device->bus->owner != device)
{
/* not the same owner as current, re-configure SPI bus */
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
/* set SPI bus owner */
device->bus->owner = device;
}
else
{
/* configure SPI bus failed */
rt_mutex_release(&(device->bus->lock));
return result;
}
}
return result;
}
rt_err_t rt_spi_release_bus(struct rt_spi_device *device)
{
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
RT_ASSERT(device->bus->owner == device);
/* release lock */
return rt_mutex_release(&(device->bus->lock));
}
rt_err_t rt_spi_take(struct rt_spi_device *device)
{
rt_ssize_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
rt_memset(&message, 0, sizeof(message));
message.cs_take = 1;
result = device->bus->ops->xfer(device, &message);
if(result < 0)
{
return (rt_err_t)result;
}
return RT_EOK;
}
rt_err_t rt_spi_release(struct rt_spi_device *device)
{
rt_ssize_t result;
struct rt_spi_message message;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
rt_memset(&message, 0, sizeof(message));
message.cs_release = 1;
result = device->bus->ops->xfer(device, &message);
if(result < 0)
{
return (rt_err_t)result;
}
return RT_EOK;
}

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
*/
#include <rtthread.h>
#include <drivers/spi.h>
/* SPI bus device interface, compatible with RT-Thread 0.3.x/1.0.x */
static rt_ssize_t _spi_bus_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_spi_bus *bus;
bus = (struct rt_spi_bus *)dev;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(bus->owner != RT_NULL);
return rt_spi_transfer(bus->owner, RT_NULL, buffer, size);
}
static rt_ssize_t _spi_bus_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_spi_bus *bus;
bus = (struct rt_spi_bus *)dev;
RT_ASSERT(bus != RT_NULL);
RT_ASSERT(bus->owner != RT_NULL);
return rt_spi_transfer(bus->owner, buffer, RT_NULL, size);
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops spi_bus_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
_spi_bus_device_read,
_spi_bus_device_write,
RT_NULL
};
#endif
rt_err_t rt_spi_bus_device_init(struct rt_spi_bus *bus, const char *name)
{
struct rt_device *device;
RT_ASSERT(bus != RT_NULL);
device = &bus->parent;
/* set device type */
device->type = RT_Device_Class_SPIBUS;
/* initialize device interface */
#ifdef RT_USING_DEVICE_OPS
device->ops = &spi_bus_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = _spi_bus_device_read;
device->write = _spi_bus_device_write;
device->control = RT_NULL;
#endif
/* register to device manager */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}
/* SPI Dev device interface, compatible with RT-Thread 0.3.x/1.0.x */
static rt_ssize_t _spidev_device_read(rt_device_t dev,
rt_off_t pos,
void *buffer,
rt_size_t size)
{
struct rt_spi_device *device;
device = (struct rt_spi_device *)dev;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
return rt_spi_transfer(device, RT_NULL, buffer, size);
}
static rt_ssize_t _spidev_device_write(rt_device_t dev,
rt_off_t pos,
const void *buffer,
rt_size_t size)
{
struct rt_spi_device *device;
device = (struct rt_spi_device *)dev;
RT_ASSERT(device != RT_NULL);
RT_ASSERT(device->bus != RT_NULL);
return rt_spi_transfer(device, buffer, RT_NULL, size);
}
static rt_err_t _spidev_device_control(rt_device_t dev,
int cmd,
void *args)
{
switch (cmd)
{
case 0: /* set device */
break;
case 1:
break;
}
return RT_EOK;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops spi_device_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
_spidev_device_read,
_spidev_device_write,
_spidev_device_control
};
#endif
rt_err_t rt_spidev_device_init(struct rt_spi_device *dev, const char *name)
{
struct rt_device *device;
RT_ASSERT(dev != RT_NULL);
device = &(dev->parent);
/* set device type */
device->type = RT_Device_Class_SPIDevice;
#ifdef RT_USING_DEVICE_OPS
device->ops = &spi_device_ops;
#else
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = _spidev_device_read;
device->write = _spidev_device_write;
device->control = _spidev_device_control;
#endif
/* register to device manager */
return rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2016/5/20 bernard the first version
* 2020/1/7 redoc add include
*/
#ifndef SPI_FLASH_H__
#define SPI_FLASH_H__
#include <rtdevice.h>
struct spi_flash_device
{
struct rt_device flash_device;
struct rt_device_blk_geometry geometry;
struct rt_spi_device * rt_spi_device;
struct rt_mutex lock;
void * user_data;
};
typedef struct spi_flash_device *rt_spi_flash_device_t;
#ifdef RT_USING_MTD_NOR
struct spi_flash_mtd
{
struct rt_mtd_nor_device mtd_device;
struct rt_spi_device * rt_spi_device;
struct rt_mutex lock;
void * user_data;
};
#endif
#endif

View file

@ -0,0 +1,779 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2016-09-28 armink first version.
*/
#include <stdint.h>
#include <string.h>
#include <rtdevice.h>
#include "spi_flash.h"
#include "spi_flash_sfud.h"
#ifdef RT_USING_SFUD
#ifndef RT_SFUD_DEFAULT_SPI_CFG
#ifndef RT_SFUD_SPI_MAX_HZ
#define RT_SFUD_SPI_MAX_HZ 50000000
#endif
/* read the JEDEC SFDP command must run at 50 MHz or less */
#define RT_SFUD_DEFAULT_SPI_CFG \
{ \
.mode = RT_SPI_MODE_0 | RT_SPI_MSB, \
.data_width = 8, \
.max_hz = RT_SFUD_SPI_MAX_HZ, \
}
#endif /* RT_SFUD_DEFAULT_SPI_CFG */
#ifdef SFUD_USING_QSPI
#define RT_SFUD_DEFAULT_QSPI_CFG \
{ \
RT_SFUD_DEFAULT_SPI_CFG, \
.medium_size = 0x800000, \
.ddr_mode = 0, \
.qspi_dl_width = 4, \
}
#endif /* SFUD_USING_QSPI */
static rt_err_t rt_sfud_control(rt_device_t dev, int cmd, void *args) {
RT_ASSERT(dev);
switch (cmd) {
case RT_DEVICE_CTRL_BLK_GETGEOME: {
struct rt_device_blk_geometry *geometry = (struct rt_device_blk_geometry *) args;
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data);
if (rtt_dev == RT_NULL || geometry == RT_NULL) {
return -RT_ERROR;
}
geometry->bytes_per_sector = rtt_dev->geometry.bytes_per_sector;
geometry->sector_count = rtt_dev->geometry.sector_count;
geometry->block_size = rtt_dev->geometry.block_size;
break;
}
case RT_DEVICE_CTRL_BLK_ERASE: {
rt_uint32_t *addrs = (rt_uint32_t *) args, start_addr = addrs[0], end_addr = addrs[1], phy_start_addr;
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data);
sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data);
rt_size_t phy_size;
if (addrs == RT_NULL || start_addr > end_addr || rtt_dev == RT_NULL || sfud_dev == RT_NULL) {
return -RT_ERROR;
}
if (end_addr == start_addr) {
end_addr ++;
}
phy_start_addr = start_addr * rtt_dev->geometry.bytes_per_sector;
phy_size = (end_addr - start_addr) * rtt_dev->geometry.bytes_per_sector;
if (sfud_erase(sfud_dev, phy_start_addr, phy_size) != SFUD_SUCCESS) {
return -RT_ERROR;
}
break;
}
}
return RT_EOK;
}
static rt_ssize_t rt_sfud_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size) {
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data);
sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data);
RT_ASSERT(dev);
RT_ASSERT(rtt_dev);
RT_ASSERT(sfud_dev);
/* change the block device's logic address to physical address */
rt_off_t phy_pos = pos * rtt_dev->geometry.bytes_per_sector;
rt_size_t phy_size = size * rtt_dev->geometry.bytes_per_sector;
if (sfud_read(sfud_dev, phy_pos, phy_size, buffer) != SFUD_SUCCESS) {
return 0;
} else {
return size;
}
}
static rt_ssize_t rt_sfud_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size) {
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (dev->user_data);
sfud_flash *sfud_dev = (sfud_flash *) (rtt_dev->user_data);
RT_ASSERT(dev);
RT_ASSERT(rtt_dev);
RT_ASSERT(sfud_dev);
/* change the block device's logic address to physical address */
rt_off_t phy_pos = pos * rtt_dev->geometry.bytes_per_sector;
rt_size_t phy_size = size * rtt_dev->geometry.bytes_per_sector;
if (sfud_erase_write(sfud_dev, phy_pos, phy_size, buffer) != SFUD_SUCCESS) {
return 0;
} else {
return size;
}
}
/**
* SPI write data then read data
*/
static sfud_err spi_write_read(const sfud_spi *spi, const uint8_t *write_buf, size_t write_size, uint8_t *read_buf,
size_t read_size) {
sfud_err result = SFUD_SUCCESS;
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);
RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);
#ifdef SFUD_USING_QSPI
struct rt_qspi_device *qspi_dev = RT_NULL;
#endif
if (write_size) {
RT_ASSERT(write_buf);
}
if (read_size) {
RT_ASSERT(read_buf);
}
#ifdef SFUD_USING_QSPI
if(rtt_dev->rt_spi_device->bus->mode & RT_SPI_BUS_MODE_QSPI) {
qspi_dev = (struct rt_qspi_device *) (rtt_dev->rt_spi_device);
if (write_size && read_size) {
if (rt_qspi_send_then_recv(qspi_dev, write_buf, write_size, read_buf, read_size) <= 0) {
result = SFUD_ERR_TIMEOUT;
}
} else if (write_size) {
if (rt_qspi_send(qspi_dev, write_buf, write_size) <= 0) {
result = SFUD_ERR_TIMEOUT;
}
}
}
else
#endif
{
if (write_size && read_size) {
if (rt_spi_send_then_recv(rtt_dev->rt_spi_device, write_buf, write_size, read_buf, read_size) != RT_EOK) {
result = SFUD_ERR_TIMEOUT;
}
} else if (write_size) {
if (rt_spi_send(rtt_dev->rt_spi_device, write_buf, write_size) <= 0) {
result = SFUD_ERR_TIMEOUT;
}
} else {
if (rt_spi_recv(rtt_dev->rt_spi_device, read_buf, read_size) <= 0) {
result = SFUD_ERR_TIMEOUT;
}
}
}
return result;
}
#ifdef SFUD_USING_QSPI
/**
* QSPI fast read data
*/
static sfud_err qspi_read(const struct __sfud_spi *spi, uint32_t addr, sfud_qspi_read_cmd_format *qspi_read_cmd_format, uint8_t *read_buf, size_t read_size) {
struct rt_qspi_message message;
sfud_err result = SFUD_SUCCESS;
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);
struct rt_qspi_device *qspi_dev = (struct rt_qspi_device *) (rtt_dev->rt_spi_device);
RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);
RT_ASSERT(qspi_dev);
/* set message struct */
message.instruction.content = qspi_read_cmd_format->instruction;
message.instruction.qspi_lines = qspi_read_cmd_format->instruction_lines;
message.address.content = addr;
message.address.size = qspi_read_cmd_format->address_size;
message.address.qspi_lines = qspi_read_cmd_format->address_lines;
message.alternate_bytes.content = 0;
message.alternate_bytes.size = 0;
message.alternate_bytes.qspi_lines = 0;
message.dummy_cycles = qspi_read_cmd_format->dummy_cycles;
message.parent.send_buf = RT_NULL;
message.parent.recv_buf = read_buf;
message.parent.length = read_size;
message.parent.cs_release = 1;
message.parent.cs_take = 1;
message.qspi_data_lines = qspi_read_cmd_format->data_lines;
if (rt_qspi_transfer_message(qspi_dev, &message) != read_size) {
result = SFUD_ERR_TIMEOUT;
}
return result;
}
#endif
static void spi_lock(const sfud_spi *spi) {
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);
RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);
rt_mutex_take(&(rtt_dev->lock), RT_WAITING_FOREVER);
}
static void spi_unlock(const sfud_spi *spi) {
sfud_flash *sfud_dev = (sfud_flash *) (spi->user_data);
struct spi_flash_device *rtt_dev = (struct spi_flash_device *) (sfud_dev->user_data);
RT_ASSERT(spi);
RT_ASSERT(sfud_dev);
RT_ASSERT(rtt_dev);
rt_mutex_release(&(rtt_dev->lock));
}
static void retry_delay_100us(void) {
/* 100 microsecond delay */
rt_thread_delay((RT_TICK_PER_SECOND * 1 + 9999) / 10000);
}
sfud_err sfud_spi_port_init(sfud_flash *flash) {
sfud_err result = SFUD_SUCCESS;
RT_ASSERT(flash);
/* port SPI device interface */
flash->spi.wr = spi_write_read;
#ifdef SFUD_USING_QSPI
flash->spi.qspi_read = qspi_read;
#endif
flash->spi.lock = spi_lock;
flash->spi.unlock = spi_unlock;
flash->spi.user_data = flash;
if (RT_TICK_PER_SECOND < 1000) {
LOG_W("[SFUD] Warning: The OS tick(%d) is less than 1000. So the flash write will take more time.", RT_TICK_PER_SECOND);
}
/* 100 microsecond delay */
flash->retry.delay = retry_delay_100us;
/* 60 seconds timeout */
flash->retry.times = 60 * 10000;
return result;
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops flash_device_ops =
{
RT_NULL,
RT_NULL,
RT_NULL,
rt_sfud_read,
rt_sfud_write,
rt_sfud_control
};
#endif
/**
* Probe SPI flash by SFUD (Serial Flash Universal Driver) driver library and though SPI device by specified configuration.
*
* @param spi_flash_dev_name the name which will create SPI flash device
* @param spi_dev_name using SPI device name
* @param spi_cfg SPI device configuration
* @param qspi_cfg QSPI device configuration
*
* @return probed SPI flash device, probe failed will return RT_NULL
*/
rt_spi_flash_device_t rt_sfud_flash_probe_ex(const char *spi_flash_dev_name, const char *spi_dev_name,
struct rt_spi_configuration *spi_cfg, struct rt_qspi_configuration *qspi_cfg)
{
rt_spi_flash_device_t rtt_dev = RT_NULL;
sfud_flash *sfud_dev = RT_NULL;
char *spi_flash_dev_name_bak = RT_NULL, *spi_dev_name_bak = RT_NULL;
extern sfud_err sfud_device_init(sfud_flash *flash);
#ifdef SFUD_USING_QSPI
struct rt_qspi_device *qspi_dev = RT_NULL;
#endif
RT_ASSERT(spi_flash_dev_name);
RT_ASSERT(spi_dev_name);
rtt_dev = (rt_spi_flash_device_t) rt_malloc(sizeof(struct spi_flash_device));
sfud_dev = (sfud_flash_t) rt_malloc(sizeof(sfud_flash));
spi_flash_dev_name_bak = (char *) rt_malloc(rt_strlen(spi_flash_dev_name) + 1);
spi_dev_name_bak = (char *) rt_malloc(rt_strlen(spi_dev_name) + 1);
if (rtt_dev) {
rt_memset(rtt_dev, 0, sizeof(struct spi_flash_device));
/* initialize lock */
rt_mutex_init(&(rtt_dev->lock), spi_flash_dev_name, RT_IPC_FLAG_PRIO);
}
if (rtt_dev && sfud_dev && spi_flash_dev_name_bak && spi_dev_name_bak) {
rt_memset(sfud_dev, 0, sizeof(sfud_flash));
rt_strncpy(spi_flash_dev_name_bak, spi_flash_dev_name, rt_strlen(spi_flash_dev_name));
rt_strncpy(spi_dev_name_bak, spi_dev_name, rt_strlen(spi_dev_name));
/* make string end sign */
spi_flash_dev_name_bak[rt_strlen(spi_flash_dev_name)] = '\0';
spi_dev_name_bak[rt_strlen(spi_dev_name)] = '\0';
/* SPI configure */
{
/* RT-Thread SPI device initialize */
rtt_dev->rt_spi_device = (struct rt_spi_device *) rt_device_find(spi_dev_name);
if (rtt_dev->rt_spi_device == RT_NULL || rtt_dev->rt_spi_device->parent.type != RT_Device_Class_SPIDevice) {
LOG_E("ERROR: SPI device %s not found!", spi_dev_name);
goto error;
}
sfud_dev->spi.name = spi_dev_name_bak;
#ifdef SFUD_USING_QSPI
/* set the qspi line number and configure the QSPI bus */
if(rtt_dev->rt_spi_device->bus->mode &RT_SPI_BUS_MODE_QSPI) {
qspi_dev = (struct rt_qspi_device *)rtt_dev->rt_spi_device;
qspi_cfg->qspi_dl_width = qspi_dev->config.qspi_dl_width;
rt_qspi_configure(qspi_dev, qspi_cfg);
}
else
#endif
rt_spi_configure(rtt_dev->rt_spi_device, spi_cfg);
}
/* SFUD flash device initialize */
{
sfud_dev->name = spi_flash_dev_name_bak;
/* accessed each other */
rtt_dev->user_data = sfud_dev;
rtt_dev->rt_spi_device->user_data = rtt_dev;
rtt_dev->flash_device.user_data = rtt_dev;
sfud_dev->user_data = rtt_dev;
/* initialize SFUD device */
if (sfud_device_init(sfud_dev) != SFUD_SUCCESS) {
LOG_E("ERROR: SPI flash probe failed by SPI device %s.", spi_dev_name);
goto error;
}
/* when initialize success, then copy SFUD flash device's geometry to RT-Thread SPI flash device */
rtt_dev->geometry.sector_count = sfud_dev->chip.capacity / sfud_dev->chip.erase_gran;
rtt_dev->geometry.bytes_per_sector = sfud_dev->chip.erase_gran;
rtt_dev->geometry.block_size = sfud_dev->chip.erase_gran;
#ifdef SFUD_USING_QSPI
/* reconfigure the QSPI bus for medium size */
if(rtt_dev->rt_spi_device->bus->mode &RT_SPI_BUS_MODE_QSPI) {
qspi_cfg->medium_size = sfud_dev->chip.capacity;
rt_qspi_configure(qspi_dev, qspi_cfg);
if(qspi_dev->enter_qspi_mode != RT_NULL)
qspi_dev->enter_qspi_mode(qspi_dev);
/* set data lines width */
sfud_qspi_fast_read_enable(sfud_dev, qspi_dev->config.qspi_dl_width);
}
#endif /* SFUD_USING_QSPI */
}
/* register device */
rtt_dev->flash_device.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
rtt_dev->flash_device.ops = &flash_device_ops;
#else
rtt_dev->flash_device.init = RT_NULL;
rtt_dev->flash_device.open = RT_NULL;
rtt_dev->flash_device.close = RT_NULL;
rtt_dev->flash_device.read = rt_sfud_read;
rtt_dev->flash_device.write = rt_sfud_write;
rtt_dev->flash_device.control = rt_sfud_control;
#endif
rt_device_register(&(rtt_dev->flash_device), spi_flash_dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
LOG_I("Probe SPI flash %s by SPI device %s success.",spi_flash_dev_name, spi_dev_name);
return rtt_dev;
} else {
LOG_E("ERROR: Low memory.");
goto error;
}
error:
if (rtt_dev) {
rt_mutex_detach(&(rtt_dev->lock));
}
/* may be one of objects memory was malloc success, so need free all */
rt_free(rtt_dev);
rt_free(sfud_dev);
rt_free(spi_flash_dev_name_bak);
rt_free(spi_dev_name_bak);
return RT_NULL;
}
/**
* Probe SPI flash by SFUD(Serial Flash Universal Driver) driver library and though SPI device.
*
* @param spi_flash_dev_name the name which will create SPI flash device
* @param spi_dev_name using SPI device name
*
* @return probed SPI flash device, probe failed will return RT_NULL
*/
rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name, const char *spi_dev_name)
{
struct rt_spi_configuration cfg = RT_SFUD_DEFAULT_SPI_CFG;
#ifndef SFUD_USING_QSPI
return rt_sfud_flash_probe_ex(spi_flash_dev_name, spi_dev_name, &cfg, RT_NULL);
#else
struct rt_qspi_configuration qspi_cfg = RT_SFUD_DEFAULT_QSPI_CFG;
return rt_sfud_flash_probe_ex(spi_flash_dev_name, spi_dev_name, &cfg, &qspi_cfg);
#endif
}
/**
* Delete SPI flash device
*
* @param spi_flash_dev SPI flash device
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_sfud_flash_delete(rt_spi_flash_device_t spi_flash_dev) {
sfud_flash *sfud_flash_dev = (sfud_flash *) (spi_flash_dev->user_data);
RT_ASSERT(spi_flash_dev);
RT_ASSERT(sfud_flash_dev);
rt_device_unregister(&(spi_flash_dev->flash_device));
rt_mutex_detach(&(spi_flash_dev->lock));
rt_free(sfud_flash_dev->spi.name);
rt_free(sfud_flash_dev->name);
rt_free(sfud_flash_dev);
rt_free(spi_flash_dev);
return RT_EOK;
}
sfud_flash_t rt_sfud_flash_find(const char *spi_dev_name)
{
rt_spi_flash_device_t rtt_dev = RT_NULL;
struct rt_spi_device *rt_spi_device = RT_NULL;
sfud_flash_t sfud_dev = RT_NULL;
rt_spi_device = (struct rt_spi_device *) rt_device_find(spi_dev_name);
if (rt_spi_device == RT_NULL || rt_spi_device->parent.type != RT_Device_Class_SPIDevice) {
LOG_E("ERROR: SPI device %s not found!", spi_dev_name);
goto __error;
}
rtt_dev = (rt_spi_flash_device_t) (rt_spi_device->user_data);
if (rtt_dev && rtt_dev->user_data) {
sfud_dev = (sfud_flash_t) (rtt_dev->user_data);
return sfud_dev;
} else {
LOG_E("ERROR: SFUD flash device not found!");
goto __error;
}
__error:
return RT_NULL;
}
sfud_flash_t rt_sfud_flash_find_by_dev_name(const char *flash_dev_name)
{
rt_spi_flash_device_t rtt_dev = RT_NULL;
sfud_flash_t sfud_dev = RT_NULL;
rtt_dev = (rt_spi_flash_device_t) rt_device_find(flash_dev_name);
if (rtt_dev == RT_NULL || rtt_dev->flash_device.type != RT_Device_Class_Block) {
LOG_E("ERROR: Flash device %s not found!", flash_dev_name);
goto __error;
}
if (rtt_dev->user_data) {
sfud_dev = (sfud_flash_t) (rtt_dev->user_data);
return sfud_dev;
} else {
LOG_E("ERROR: SFUD flash device not found!");
goto __error;
}
__error:
return RT_NULL;
}
#if defined(RT_USING_FINSH)
#include <finsh.h>
static void sf(uint8_t argc, char **argv) {
#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
#define HEXDUMP_WIDTH 16
#define CMD_PROBE_INDEX 0
#define CMD_READ_INDEX 1
#define CMD_WRITE_INDEX 2
#define CMD_ERASE_INDEX 3
#define CMD_RW_STATUS_INDEX 4
#define CMD_BENCH_INDEX 5
sfud_err result = SFUD_SUCCESS;
static const sfud_flash *sfud_dev = NULL;
static rt_spi_flash_device_t rtt_dev = NULL, rtt_dev_bak = NULL;
size_t i = 0, j = 0;
const char* sf_help_info[] = {
[CMD_PROBE_INDEX] = "sf probe [spi_device] - probe and init SPI flash by given 'spi_device'",
[CMD_READ_INDEX] = "sf read addr size - read 'size' bytes starting at 'addr'",
[CMD_WRITE_INDEX] = "sf write addr data1 ... dataN - write some bytes 'data' to flash starting at 'addr'",
[CMD_ERASE_INDEX] = "sf erase addr size - erase 'size' bytes starting at 'addr'",
[CMD_RW_STATUS_INDEX] = "sf status [<volatile> <status>] - read or write '1:volatile|0:non-volatile' 'status'",
[CMD_BENCH_INDEX] = "sf bench - full chip benchmark. DANGER: It will erase full chip!",
};
if (argc < 2) {
rt_kprintf("Usage:\n");
for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) {
rt_kprintf("%s\n", sf_help_info[i]);
}
rt_kprintf("\n");
} else {
const char *operator = argv[1];
uint32_t addr, size;
if (!strcmp(operator, "probe")) {
if (argc < 3) {
rt_kprintf("Usage: %s.\n", sf_help_info[CMD_PROBE_INDEX]);
} else {
char *spi_dev_name = argv[2];
rtt_dev_bak = rtt_dev;
/* delete the old SPI flash device */
if(rtt_dev_bak) {
rt_sfud_flash_delete(rtt_dev_bak);
}
rtt_dev = rt_sfud_flash_probe("sf_cmd", spi_dev_name);
if (!rtt_dev) {
return;
}
sfud_dev = (sfud_flash_t)rtt_dev->user_data;
if (sfud_dev->chip.capacity < 1024 * 1024) {
rt_kprintf("%d KB %s is current selected device.\n", sfud_dev->chip.capacity / 1024, sfud_dev->name);
} else {
rt_kprintf("%d MB %s is current selected device.\n", sfud_dev->chip.capacity / 1024 / 1024,
sfud_dev->name);
}
}
} else {
if (!sfud_dev) {
rt_kprintf("No flash device selected. Please run 'sf probe'.\n");
return;
}
if (!rt_strcmp(operator, "read")) {
if (argc < 4) {
rt_kprintf("Usage: %s.\n", sf_help_info[CMD_READ_INDEX]);
return;
} else {
addr = strtol(argv[2], NULL, 0);
size = strtol(argv[3], NULL, 0);
uint8_t *data = rt_malloc(size);
if (data) {
result = sfud_read(sfud_dev, addr, size, data);
if (result == SFUD_SUCCESS) {
rt_kprintf("Read the %s flash data success. Start from 0x%08X, size is %ld. The data is:\n",
sfud_dev->name, addr, size);
rt_kprintf("Offset (h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n");
for (i = 0; i < size; i += HEXDUMP_WIDTH)
{
rt_kprintf("[%08X] ", addr + i);
/* dump hex */
for (j = 0; j < HEXDUMP_WIDTH; j++) {
if (i + j < size) {
rt_kprintf("%02X ", data[i + j]);
} else {
rt_kprintf(" ");
}
}
/* dump char for hex */
for (j = 0; j < HEXDUMP_WIDTH; j++) {
if (i + j < size) {
rt_kprintf("%c", __is_print(data[i + j]) ? data[i + j] : '.');
}
}
rt_kprintf("\n");
}
rt_kprintf("\n");
}
rt_free(data);
} else {
rt_kprintf("Low memory!\n");
}
}
} else if (!rt_strcmp(operator, "write")) {
if (argc < 4) {
rt_kprintf("Usage: %s.\n", sf_help_info[CMD_WRITE_INDEX]);
return;
} else {
addr = strtol(argv[2], NULL, 0);
size = argc - 3;
uint8_t *data = rt_malloc(size);
if (data) {
for (i = 0; i < size; i++) {
data[i] = strtol(argv[3 + i], NULL, 0);
}
result = sfud_write(sfud_dev, addr, size, data);
if (result == SFUD_SUCCESS) {
rt_kprintf("Write the %s flash data success. Start from 0x%08X, size is %ld.\n",
sfud_dev->name, addr, size);
rt_kprintf("Write data: ");
for (i = 0; i < size; i++) {
rt_kprintf("%d ", data[i]);
}
rt_kprintf(".\n");
}
rt_free(data);
} else {
rt_kprintf("Low memory!\n");
}
}
} else if (!rt_strcmp(operator, "erase")) {
if (argc < 4) {
rt_kprintf("Usage: %s.\n", sf_help_info[CMD_ERASE_INDEX]);
return;
} else {
addr = strtol(argv[2], NULL, 0);
size = strtol(argv[3], NULL, 0);
result = sfud_erase(sfud_dev, addr, size);
if (result == SFUD_SUCCESS) {
rt_kprintf("Erase the %s flash data success. Start from 0x%08X, size is %ld.\n", sfud_dev->name,
addr, size);
}
}
} else if (!rt_strcmp(operator, "status")) {
if (argc < 3) {
uint8_t status;
result = sfud_read_status(sfud_dev, &status);
if (result == SFUD_SUCCESS) {
rt_kprintf("The %s flash status register current value is 0x%02X.\n", sfud_dev->name, status);
}
} else if (argc == 4) {
bool is_volatile = strtol(argv[2], NULL, 0);
uint8_t status = strtol(argv[3], NULL, 0);
result = sfud_write_status(sfud_dev, is_volatile, status);
if (result == SFUD_SUCCESS) {
rt_kprintf("Write the %s flash status register to 0x%02X success.\n", sfud_dev->name, status);
}
} else {
rt_kprintf("Usage: %s.\n", sf_help_info[CMD_RW_STATUS_INDEX]);
return;
}
} else if (!rt_strcmp(operator, "bench")) {
if ((argc > 2 && rt_strcmp(argv[2], "yes")) || argc < 3) {
rt_kprintf("DANGER: It will erase full chip! Please run 'sf bench yes'.\n");
return;
}
/* full chip benchmark test */
addr = 0;
size = sfud_dev->chip.capacity;
uint32_t start_time, time_cast;
size_t write_size = SFUD_WRITE_MAX_PAGE_SIZE, read_size = SFUD_WRITE_MAX_PAGE_SIZE, cur_op_size;
uint8_t *write_data = rt_malloc(write_size), *read_data = rt_malloc(read_size);
if (write_data && read_data) {
for (i = 0; i < write_size; i ++) {
write_data[i] = i & 0xFF;
}
/* benchmark testing */
rt_kprintf("Erasing the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
start_time = rt_tick_get();
result = sfud_erase(sfud_dev, addr, size);
if (result == SFUD_SUCCESS) {
time_cast = rt_tick_get() - start_time;
rt_kprintf("Erase benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
} else {
rt_kprintf("Erase benchmark has an error. Error code: %d.\n", result);
}
/* write test */
rt_kprintf("Writing the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
start_time = rt_tick_get();
for (i = 0; i < size; i += write_size) {
if (i + write_size <= size) {
cur_op_size = write_size;
} else {
cur_op_size = size - i;
}
result = sfud_write(sfud_dev, addr + i, cur_op_size, write_data);
if (result != SFUD_SUCCESS) {
rt_kprintf("Writing %s failed, already wr for %lu bytes, write %d each time\n", sfud_dev->name, i, write_size);
break;
}
}
if (result == SFUD_SUCCESS) {
time_cast = rt_tick_get() - start_time;
rt_kprintf("Write benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
} else {
rt_kprintf("Write benchmark has an error. Error code: %d.\n", result);
}
/* read test */
rt_kprintf("Reading the %s %ld bytes data, waiting...\n", sfud_dev->name, size);
start_time = rt_tick_get();
for (i = 0; i < size; i += read_size) {
if (i + read_size <= size) {
cur_op_size = read_size;
} else {
cur_op_size = size - i;
}
result = sfud_read(sfud_dev, addr + i, cur_op_size, read_data);
/* data check */
if (memcmp(write_data, read_data, cur_op_size))
{
rt_kprintf("Data check ERROR! Please check you flash by other command.\n");
result = SFUD_ERR_READ;
}
if (result != SFUD_SUCCESS) {
rt_kprintf("Read %s failed, already rd for %lu bytes, read %d each time\n", sfud_dev->name, i, read_size);
break;
}
}
if (result == SFUD_SUCCESS) {
time_cast = rt_tick_get() - start_time;
rt_kprintf("Read benchmark success, total time: %d.%03dS.\n", time_cast / RT_TICK_PER_SECOND,
time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));
} else {
rt_kprintf("Read benchmark has an error. Error code: %d.\n", result);
}
} else {
rt_kprintf("Low memory!\n");
}
rt_free(write_data);
rt_free(read_data);
} else {
rt_kprintf("Usage:\n");
for (i = 0; i < sizeof(sf_help_info) / sizeof(char*); i++) {
rt_kprintf("%s\n", sf_help_info[i]);
}
rt_kprintf("\n");
return;
}
if (result != SFUD_SUCCESS) {
rt_kprintf("This flash operate has an error. Error code: %d.\n", result);
}
}
}
}
MSH_CMD_EXPORT(sf, SPI Flash operate.);
#endif /* defined(RT_USING_FINSH) */
#endif /* RT_USING_SFUD */

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2016-09-28 armink first version.
*/
#ifndef _SPI_FLASH_SFUD_H_
#define _SPI_FLASH_SFUD_H_
#include <rtthread.h>
#include <rtdevice.h>
#include "./sfud/inc/sfud.h"
#include "spi_flash.h"
/**
* Probe SPI flash by SFUD(Serial Flash Universal Driver) driver library and though SPI device.
*
* @param spi_flash_dev_name the name which will create SPI flash device
* @param spi_dev_name using SPI device name
*
* @return probed SPI flash device, probe failed will return RT_NULL
*/
rt_spi_flash_device_t rt_sfud_flash_probe(const char *spi_flash_dev_name, const char *spi_dev_name);
/**
* Probe SPI flash by SFUD (Serial Flash Universal Driver) driver library and though SPI device by specified configuration.
*
* @param spi_flash_dev_name the name which will create SPI flash device
* @param spi_dev_name using SPI device name
* @param spi_cfg SPI device configuration
* @param qspi_cfg QSPI device configuration
*
* @return probed SPI flash device, probe failed will return RT_NULL
*/
rt_spi_flash_device_t rt_sfud_flash_probe_ex(const char *spi_flash_dev_name, const char *spi_dev_name,
struct rt_spi_configuration *spi_cfg, struct rt_qspi_configuration *qspi_cfg);
/**
* Delete SPI flash device
*
* @param spi_flash_dev SPI flash device
*
* @return the operation status, RT_EOK on successful
*/
rt_err_t rt_sfud_flash_delete(rt_spi_flash_device_t spi_flash_dev);
/**
* Find sfud flash device by SPI device name
*
* @param spi_dev_name using SPI device name
*
* @return sfud flash device if success, otherwise return RT_NULL
*/
sfud_flash_t rt_sfud_flash_find(const char *spi_dev_name);
/**
* Find sfud flash device by flash device name
*
* @param flash_dev_name using flash device name
*
* @return sfud flash device if success, otherwise return RT_NULL
*/
sfud_flash_t rt_sfud_flash_find_by_dev_name(const char *flash_dev_name);
#endif /* _SPI_FLASH_SFUD_H_ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2009-04-17 Bernard first version.
*/
#ifndef SPI_MSD_H_INCLUDED
#define SPI_MSD_H_INCLUDED
#include <stdint.h>
#include <drivers/spi.h>
/* SD command (SPI mode) */
#define GO_IDLE_STATE 0 /* CMD0 R1 */
#define SEND_OP_COND 1 /* CMD1 R1 */
#define SWITCH_FUNC 6 /* CMD6 R1 */
#define SEND_IF_COND 8 /* CMD8 R7 */
#define SEND_CSD 9 /* CMD9 R1 */
#define SEND_CID 10 /* CMD10 R1 */
#define STOP_TRANSMISSION 12 /* CMD12 R1B */
#define SEND_STATUS 13 /* CMD13 R2 */
#define SET_BLOCKLEN 16 /* CMD16 R1 */
#define READ_SINGLE_BLOCK 17 /* CMD17 R1 */
#define READ_MULTIPLE_BLOCK 18 /* CMD18 R1 */
#define WRITE_BLOCK 24 /* CMD24 R1 */
#define WRITE_MULTIPLE_BLOCK 25 /* CMD25 R1 */
#define PROGRAM_CSD 27 /* CMD27 R1 */
#define SET_WRITE_PROT 28 /* CMD28 R1B */
#define CLR_WRITE_PROT 29 /* CMD29 R1B */
#define SEND_WRITE_PROT 30 /* CMD30 R1 */
#define ERASE_WR_BLK_START_ADDR 32 /* CMD32 R1 */
#define ERASE_WR_BLK_END_ADDR 33 /* CMD33 R1 */
#define ERASE 38 /* CMD38 R1B */
#define LOCK_UNLOCK 42 /* CMD42 R1 */
#define APP_CMD 55 /* CMD55 R1 */
#define GEN_CMD 56 /* CMD56 R1 */
#define READ_OCR 58 /* CMD58 R3 */
#define CRC_ON_OFF 59 /* CMD59 R1 */
/* Application-Specific Command */
#define SD_STATUS 13 /* ACMD13 R2 */
#define SEND_NUM_WR_BLOCKS 22 /* ACMD22 R1 */
#define SET_WR_BLK_ERASE_COUNT 23 /* ACMD23 R1 */
#define SD_SEND_OP_COND 41 /* ACMD41 R1 */
#define SET_CLR_CARD_DETECT 42 /* ACMD42 R1 */
#define SEND_SCR 51 /* ACMD51 R1 */
/* Start Data tokens */
/* Tokens (necessary because at nop/idle (and CS active) only 0xff is on the data/command line) */
#define MSD_TOKEN_READ_START 0xFE /* Data token start byte, Start Single Block Read */
#define MSD_TOKEN_WRITE_SINGLE_START 0xFE /* Data token start byte, Start Single Block Write */
#define MSD_TOKEN_WRITE_MULTIPLE_START 0xFC /* Data token start byte, Start Multiple Block Write */
#define MSD_TOKEN_WRITE_MULTIPLE_STOP 0xFD /* Data toke stop byte, Stop Multiple Block Write */
/* MSD reponses and error flags */
#define MSD_RESPONSE_NO_ERROR 0x00
#define MSD_IN_IDLE_STATE 0x01
#define MSD_ERASE_RESET 0x02
#define MSD_ILLEGAL_COMMAND 0x04
#define MSD_COM_CRC_ERROR 0x08
#define MSD_ERASE_SEQUENCE_ERROR 0x10
#define MSD_ADDRESS_ERROR 0x20
#define MSD_PARAMETER_ERROR 0x40
#define MSD_RESPONSE_FAILURE 0xFF
/* Data response error */
#define MSD_DATA_OK 0x05
#define MSD_DATA_CRC_ERROR 0x0B
#define MSD_DATA_WRITE_ERROR 0x0D
#define MSD_DATA_OTHER_ERROR 0xFF
#define MSD_DATA_RESPONSE_MASK 0x1F
#define MSD_GET_DATA_RESPONSE(res) (res & MSD_DATA_RESPONSE_MASK)
#define MSD_CMD_LEN 6 /**< command, arg and crc. */
#define MSD_RESPONSE_MAX_LEN 5 /**< response max len */
#define MSD_CSD_LEN 16 /**< SD crad CSD register len */
#define SECTOR_SIZE 512 /**< sector size, default 512byte */
/* card try timeout, unit: ms */
#define CARD_TRY_TIMES 3000
#define CARD_TRY_TIMES_ACMD41 800
#define CARD_WAIT_TOKEN_TIMES 800
#define MSD_USE_PRE_ERASED /**< id define MSD_USE_PRE_ERASED, before CMD25, send ACMD23 */
/**
* SD/MMC card type
*/
typedef enum
{
MSD_CARD_TYPE_UNKNOWN = 0, /**< unknown */
MSD_CARD_TYPE_MMC, /**< MultiMedia Card */
MSD_CARD_TYPE_SD_V1_X, /**< Ver 1.X Standard Capacity SD Memory Card */
MSD_CARD_TYPE_SD_V2_X, /**< Ver 2.00 or later Standard Capacity SD Memory Card */
MSD_CARD_TYPE_SD_SDHC, /**< High Capacity SD Memory Card */
MSD_CARD_TYPE_SD_SDXC, /**< later Extended Capacity SD Memory Card */
}msd_card_type;
typedef enum
{
response_type_unknown = 0,
response_r1,
response_r1b,
response_r2,
response_r3,
response_r4,
response_r5,
response_r7,
}response_type;
struct msd_device
{
struct rt_device parent; /**< RT-Thread device struct */
struct rt_device_blk_geometry geometry; /**< sector size, sector count */
struct rt_spi_device * spi_device; /**< SPI interface */
msd_card_type card_type; /**< card type: MMC SD1.x SD2.0 SDHC SDXC */
uint32_t max_clock; /**< MAX SPI clock */
};
extern rt_err_t msd_init(const char * sd_device_name, const char * spi_device_name);
#endif // SPI_MSD_H_INCLUDED

View file

@ -0,0 +1,852 @@
/*
* COPYRIGHT (C) 2011-2023, Real-Thread Information Technology Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2014-07-31 aozima the first version
* 2014-09-18 aozima update command & response.
* 2017-07-28 armink fix auto reconnect feature
*/
#include <rtthread.h>
#include <drivers/spi.h>
#include <netif/ethernetif.h>
#include <netif/etharp.h>
#include <lwip/icmp.h>
#include "lwipopts.h"
#define WIFI_DEBUG_ON
// #define ETH_RX_DUMP
// #define ETH_TX_DUMP
#ifdef WIFI_DEBUG_ON
#define WIFI_DEBUG rt_kprintf("[RW009] ");rt_kprintf
//#define SPI_DEBUG rt_kprintf("[SPI] ");rt_kprintf
#define SPI_DEBUG(...)
#else
#define WIFI_DEBUG(...)
#define SPI_DEBUG(...)
#endif /* #ifdef WIFI_DEBUG_ON */
/********************************* RW009 **************************************/
#include "spi_wifi_rw009.h"
/* tools */
#define node_entry(node, type, member) \
((type *)((char *)(node) - (unsigned long)(&((type *)0)->member)))
#define member_offset(type, member) \
((unsigned long)(&((type *)0)->member))
#define MAX_SPI_PACKET_SIZE (member_offset(struct spi_data_packet, buffer) + SPI_MAX_DATA_LEN)
#define MAX_SPI_BUFFER_SIZE (sizeof(struct spi_response) + MAX_SPI_PACKET_SIZE)
#define MAX_ADDR_LEN 6
struct rw009_wifi
{
/* inherit from ethernet device */
struct eth_device parent;
struct rt_spi_device *rt_spi_device;
/* interface address info. */
rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */
rt_uint8_t active;
struct rt_mempool spi_tx_mp;
struct rt_mempool spi_rx_mp;
struct rt_mailbox spi_tx_mb;
struct rt_mailbox eth_rx_mb;
int spi_tx_mb_pool[SPI_TX_POOL_SIZE + 1];
int eth_rx_mb_pool[SPI_RX_POOL_SIZE + 1];
int rw009_cmd_mb_pool[3];
struct rt_mailbox rw009_cmd_mb;
uint32_t last_cmd;
rt_align(4)
rt_uint8_t spi_tx_mempool[(sizeof(struct spi_data_packet) + 4) * SPI_TX_POOL_SIZE];
rt_align(4)
rt_uint8_t spi_rx_mempool[(sizeof(struct spi_data_packet) + 4) * SPI_RX_POOL_SIZE];
rt_align(4)
uint8_t spi_hw_rx_buffer[MAX_SPI_BUFFER_SIZE];
/* status for RW009 */
rw009_ap_info ap_info; /* AP info for conn. */
rw009_ap_info *ap_scan; /* AP list for SCAN. */
uint32_t ap_scan_count;
};
static struct rw009_wifi rw009_wifi_device;
static struct rt_event spi_wifi_data_event;
static void resp_handler(struct rw009_wifi *wifi_device, struct rw009_resp *resp)
{
struct rw009_resp *resp_return = RT_NULL;
switch (resp->cmd)
{
case RW009_CMD_INIT:
WIFI_DEBUG("resp_handler RW009_CMD_INIT\n");
resp_return = (struct rw009_resp *)rt_malloc(member_offset(struct rw009_resp, resp) + sizeof(rw009_resp_init)); //TODO:
if(resp_return == RT_NULL) break;
rt_memcpy(resp_return, resp, member_offset(struct rw009_resp, resp) + sizeof(rw009_resp_init));
WIFI_DEBUG("sn:%-*.*s\n", sizeof(resp->resp.init.sn), sizeof(resp->resp.init.sn), resp->resp.init.sn);
WIFI_DEBUG("version:%-*.*s\n", sizeof(resp->resp.init.version), sizeof(resp->resp.init.version), resp->resp.init.version);
rt_memcpy(wifi_device->dev_addr, resp->resp.init.mac, 6);
break;
case RW009_CMD_SCAN:
if( resp->len == sizeof(rw009_ap_info) )
{
rw009_ap_info *ap_scan = rt_realloc(wifi_device->ap_scan, sizeof(rw009_ap_info) * (wifi_device->ap_scan_count + 1) );
if(ap_scan != RT_NULL)
{
rt_memcpy( &ap_scan[wifi_device->ap_scan_count], &resp->resp.ap_info, sizeof(rw009_ap_info) );
//dump
if(1)
{
#ifdef WIFI_DEBUG_ON
rw009_ap_info *ap_info = &resp->resp.ap_info;
WIFI_DEBUG("SCAN SSID:%-32.32s\n", ap_info->ssid);
WIFI_DEBUG("SCAN BSSID:%02X-%02X-%02X-%02X-%02X-%02X\n",
ap_info->bssid[0],
ap_info->bssid[1],
ap_info->bssid[2],
ap_info->bssid[3],
ap_info->bssid[4],
ap_info->bssid[5]);
WIFI_DEBUG("SCAN rssi:%ddBm\n", ap_info->rssi);
WIFI_DEBUG("SCAN rate:%dMbps\n", ap_info->max_data_rate/1000);
WIFI_DEBUG("SCAN channel:%d\n", ap_info->channel);
WIFI_DEBUG("SCAN security:%08X\n\n", ap_info->security);
#endif /* WIFI_DEBUG_ON */
}
wifi_device->ap_scan_count++;
wifi_device->ap_scan = ap_scan;
}
return; /* wait for next ap */
}
break;
case RW009_CMD_JOIN:
case RW009_CMD_EASY_JOIN:
WIFI_DEBUG("resp_handler RW009_CMD_EASY_JOIN\n");
resp_return = (struct rw009_resp *)rt_malloc(member_offset(struct rw009_resp, resp) + sizeof(rw009_resp_join)); //TODO:
if(resp_return == RT_NULL) break;
rt_memcpy(resp_return, resp, member_offset(struct rw009_resp, resp) + sizeof(rw009_resp_join));
if( resp->result == 0 )
{
rt_memcpy(&wifi_device->ap_info, &resp_return->resp.ap_info, sizeof(rw009_resp_join));
wifi_device->active = 1;
eth_device_linkchange(&wifi_device->parent, RT_TRUE);
}
else
{
wifi_device->active = 1;
eth_device_linkchange(&wifi_device->parent, RT_FALSE);
WIFI_DEBUG("RW009_CMD_EASY_JOIN result: %d\n", resp->result );
}
//dupm
if(1)
{
#ifdef WIFI_DEBUG_ON
rw009_ap_info *ap_info = &resp->resp.ap_info;
WIFI_DEBUG("JOIN SSID:%-32.32s\n", ap_info->ssid);
WIFI_DEBUG("JOIN BSSID:%02X-%02X-%02X-%02X-%02X-%02X\n",
ap_info->bssid[0],
ap_info->bssid[1],
ap_info->bssid[2],
ap_info->bssid[3],
ap_info->bssid[4],
ap_info->bssid[5]);
WIFI_DEBUG("JOIN rssi:%ddBm\n", ap_info->rssi);
WIFI_DEBUG("JOIN rate:%dMbps\n", ap_info->max_data_rate/1000);
WIFI_DEBUG("JOIN channel:%d\n", ap_info->channel);
WIFI_DEBUG("JOIN security:%08X\n\n", ap_info->security);
#endif /* WIFI_DEBUG_ON */
}
break;
case RW009_CMD_RSSI:
// TODO: client RSSI.
{
rw009_ap_info *ap_info = &resp->resp.ap_info;
wifi_device->ap_info.rssi = ap_info->rssi;
WIFI_DEBUG("current RSSI: %d\n", wifi_device->ap_info.rssi);
}
break;
case RW009_CMD_SOFTAP:
{
if( resp->result == 0 )
{
;
wifi_device->active = 1;
eth_device_linkchange(&wifi_device->parent, RT_TRUE);
}
else
{
WIFI_DEBUG("RW009_CMD_EASY_JOIN result: %d\n", resp->result );
}
}
break;
default:
WIFI_DEBUG("resp_handler %d\n", resp->cmd);
break;
}
if(resp->cmd == wifi_device->last_cmd)
{
rt_mb_send(&wifi_device->rw009_cmd_mb, (rt_uint32_t)resp_return);
return;
}
else
{
rt_free(resp_return);
}
}
static rt_err_t rw009_cmd(struct rw009_wifi *wifi_device, uint32_t cmd, void *args)
{
rt_err_t result = RT_EOK;
rt_int32_t timeout = RW009_CMD_TIMEOUT;
struct spi_data_packet *data_packet;
struct rw009_cmd *wifi_cmd = RT_NULL;
struct rw009_resp *resp = RT_NULL;
wifi_device->last_cmd = cmd;
data_packet = (struct spi_data_packet *)rt_mp_alloc(&wifi_device->spi_tx_mp, RT_WAITING_FOREVER);
wifi_cmd = (struct rw009_cmd *)data_packet->buffer;
wifi_cmd->cmd = cmd;
wifi_cmd->len = 0;
if( cmd == RW009_CMD_INIT )
{
wifi_cmd->len = sizeof(rw009_cmd_init);
}
else if( cmd == RW009_CMD_SCAN )
{
wifi_cmd->len = 0;
timeout += RT_TICK_PER_SECOND*10;
if(wifi_device->ap_scan)
{
rt_free(wifi_device->ap_scan);
wifi_device->ap_scan = RT_NULL;
wifi_device->ap_scan_count = 0;
}
}
else if( cmd == RW009_CMD_JOIN )
{
wifi_cmd->len = sizeof(rw009_cmd_join);
}
else if( cmd == RW009_CMD_EASY_JOIN )
{
wifi_cmd->len = sizeof(rw009_cmd_easy_join);
timeout += RT_TICK_PER_SECOND*5;
}
else if( cmd == RW009_CMD_RSSI )
{
wifi_cmd->len = sizeof(rw009_cmd_rssi);
}
else if( cmd == RW009_CMD_SOFTAP )
{
wifi_cmd->len = sizeof(rw009_cmd_softap);
}
else
{
WIFI_DEBUG("unkown RW009 CMD %d\n", cmd);
result = -RT_ENOSYS;
rt_mp_free(data_packet);
data_packet = RT_NULL;
}
if(data_packet == RT_NULL)
{
goto _exit;
}
if(wifi_cmd->len)
rt_memcpy(&wifi_cmd->params, args, wifi_cmd->len);
data_packet->data_type = data_type_cmd;
data_packet->data_len = member_offset(struct rw009_cmd, params) + wifi_cmd->len;
rt_mb_send(&wifi_device->spi_tx_mb, (rt_uint32_t)data_packet);
rt_event_send(&spi_wifi_data_event, 1);
result = rt_mb_recv(&wifi_device->rw009_cmd_mb,
(rt_uint32_t *)&resp,
timeout);
if ( result != RT_EOK )
{
WIFI_DEBUG("CMD %d error, resultL %d\n", cmd, result );
}
if(resp != RT_NULL)
result = resp->result;
_exit:
wifi_device->last_cmd = 0;
if(resp) rt_free(resp);
return result;
}
static rt_err_t spi_wifi_transfer(struct rw009_wifi *dev)
{
struct pbuf *p = RT_NULL;
struct spi_cmd_request cmd;
struct spi_response resp;
rt_err_t result;
const struct spi_data_packet *data_packet = RT_NULL;
struct rw009_wifi *wifi_device = (struct rw009_wifi *)dev;
struct rt_spi_device *rt_spi_device = wifi_device->rt_spi_device;
spi_wifi_int_cmd(0);
while (spi_wifi_is_busy());
SPI_DEBUG("sequence start!\n");
rt_memset(&cmd, 0, sizeof(struct spi_cmd_request));
cmd.magic1 = CMD_MAGIC1;
cmd.magic2 = CMD_MAGIC2;
cmd.flag |= CMD_FLAG_MRDY;
result = rt_mb_recv(&wifi_device->spi_tx_mb,
(rt_uint32_t *)&data_packet,
0);
if ((result == RT_EOK) && (data_packet != RT_NULL) && (data_packet->data_len > 0))
{
cmd.M2S_len = data_packet->data_len + member_offset(struct spi_data_packet, buffer);
//SPI_DEBUG("cmd.M2S_len = %d\n", cmd.M2S_len);
}
rt_spi_send(rt_spi_device, &cmd, sizeof(cmd));
while (spi_wifi_is_busy());
{
struct rt_spi_message message;
uint32_t max_data_len = 0;
/* setup message */
message.send_buf = RT_NULL;
message.recv_buf = &resp;
message.length = sizeof(resp);
message.cs_take = 1;
message.cs_release = 0;
rt_spi_take_bus(rt_spi_device);
/* transfer message */
rt_spi_device->bus->ops->xfer(rt_spi_device, &message);
if ((resp.magic1 != RESP_MAGIC1) || (resp.magic2 != RESP_MAGIC2))
{
SPI_DEBUG("bad resp magic, abort!\n");
goto _bad_resp_magic;
}
if (resp.flag & RESP_FLAG_SRDY)
{
SPI_DEBUG("RESP_FLAG_SRDY\n");
max_data_len = cmd.M2S_len;
}
if (resp.S2M_len)
{
SPI_DEBUG("resp.S2M_len: %d\n", resp.S2M_len);
if (resp.S2M_len > MAX_SPI_PACKET_SIZE)
{
SPI_DEBUG("resp.S2M_len %d > %d(MAX_SPI_PACKET_SIZE), drop!\n", resp.S2M_len, MAX_SPI_PACKET_SIZE);
resp.S2M_len = 0;//drop
}
if (resp.S2M_len > max_data_len)
max_data_len = resp.S2M_len;
}
if (max_data_len == 0)
{
SPI_DEBUG("no rx or tx data!\n");
}
//SPI_DEBUG("max_data_len = %d\n", max_data_len);
_bad_resp_magic:
/* setup message */
message.send_buf = data_packet;//&tx_buffer;
message.recv_buf = wifi_device->spi_hw_rx_buffer;//&rx_buffer;
message.length = max_data_len;
message.cs_take = 0;
message.cs_release = 1;
/* transfer message */
rt_spi_device->bus->ops->xfer(rt_spi_device, &message);
rt_spi_release_bus(rt_spi_device);
if (cmd.M2S_len && (resp.flag & RESP_FLAG_SRDY))
{
rt_mp_free((void *)data_packet);
}
if ((resp.S2M_len) && (resp.S2M_len <= MAX_SPI_PACKET_SIZE))
{
data_packet = (struct spi_data_packet *)wifi_device->spi_hw_rx_buffer;
if (data_packet->data_type == data_type_eth_data)
{
if (wifi_device->active)
{
p = pbuf_alloc(PBUF_LINK, data_packet->data_len, PBUF_RAM);
pbuf_take(p, (rt_uint8_t *)data_packet->buffer, data_packet->data_len);
rt_mb_send(&wifi_device->eth_rx_mb, (rt_uint32_t)p);
eth_device_ready((struct eth_device *)dev);
}
else
{
SPI_DEBUG("!active, RX drop.\n");
}
}
else if (data_packet->data_type == data_type_resp)
{
SPI_DEBUG("data_type_resp\n");
resp_handler(dev, (struct rw009_resp *)data_packet->buffer);
}
else
{
SPI_DEBUG("data_type: %d, %dbyte\n",
data_packet->data_type,
data_packet->data_len);
}
}
}
spi_wifi_int_cmd(1);
SPI_DEBUG("sequence finish!\n\n");
if ((cmd.M2S_len == 0) && (resp.S2M_len == 0))
{
return -RT_ERROR;
}
return RT_EOK;
}
#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
static void packet_dump(const char *msg, const struct pbuf *p)
{
const struct pbuf* q;
rt_uint32_t i,j;
rt_uint8_t *ptr = p->payload;
rt_kprintf("%s %d byte\n", msg, p->tot_len);
i=0;
for(q=p; q != RT_NULL; q= q->next)
{
ptr = q->payload;
for(j=0; j<q->len; j++)
{
if( (i%8) == 0 )
{
rt_kprintf(" ");
}
if( (i%16) == 0 )
{
rt_kprintf("\r\n");
}
rt_kprintf("%02x ",*ptr);
i++;
ptr++;
}
}
rt_kprintf("\n\n");
}
#endif /* dump */
/********************************* RT-Thread Ethernet interface begin **************************************/
static rt_err_t rw009_wifi_init(rt_device_t dev)
{
return RT_EOK;
}
static rt_err_t rw009_wifi_open(rt_device_t dev, rt_uint16_t oflag)
{
return RT_EOK;
}
static rt_err_t rw009_wifi_close(rt_device_t dev)
{
return RT_EOK;
}
static rt_ssize_t rw009_wifi_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_ssize_t rw009_wifi_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
rt_set_errno(-RT_ENOSYS);
return 0;
}
static rt_err_t rw009_wifi_control(rt_device_t dev, int cmd, void *args)
{
struct rw009_wifi *wifi_device = (struct rw009_wifi *)dev;
rt_err_t result = RT_EOK;
if (cmd == NIOCTL_GADDR)
{
rt_memcpy(args, wifi_device->dev_addr, 6);
}
else
{
result = rw009_cmd(wifi_device, cmd, args);
}
return result;
}
/* transmit packet. */
rt_err_t rw009_wifi_tx(rt_device_t dev, struct pbuf *p)
{
rt_err_t result = RT_EOK;
struct spi_data_packet *data_packet;
struct rw009_wifi *wifi_device = (struct rw009_wifi *)dev;
if (!wifi_device->active)
{
WIFI_DEBUG("!active, TX drop!\n");
return RT_EOK;
}
/* get free tx buffer */
data_packet = (struct spi_data_packet *)rt_mp_alloc(&wifi_device->spi_tx_mp, RT_WAITING_FOREVER);
if (data_packet != RT_NULL)
{
data_packet->data_type = data_type_eth_data;
data_packet->data_len = p->tot_len;
pbuf_copy_partial(p, data_packet->buffer, data_packet->data_len, 0);
rt_mb_send(&wifi_device->spi_tx_mb, (rt_uint32_t)data_packet);
rt_event_send(&spi_wifi_data_event, 1);
}
else
return -RT_ERROR;
#ifdef ETH_TX_DUMP
packet_dump("TX dump", p);
#endif /* ETH_TX_DUMP */
/* Return SUCCESS */
return result;
}
/* reception packet. */
struct pbuf *rw009_wifi_rx(rt_device_t dev)
{
struct pbuf *p = RT_NULL;
struct rw009_wifi *wifi_device = (struct rw009_wifi *)dev;
if (rt_mb_recv(&wifi_device->eth_rx_mb, (rt_uint32_t *)&p, 0) != RT_EOK)
{
return RT_NULL;
}
#ifdef ETH_RX_DUMP
if(p)
packet_dump("RX dump", p);
#endif /* ETH_RX_DUMP */
return p;
}
/********************************* RT-Thread Ethernet interface end **************************************/
static void spi_wifi_data_thread_entry(void *parameter)
{
rt_uint32_t e;
rt_err_t result;
while (1)
{
/* receive first event */
if (rt_event_recv(&spi_wifi_data_event,
1,
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
RT_WAITING_FOREVER,
&e) != RT_EOK)
{
continue;
}
result = spi_wifi_transfer(&rw009_wifi_device);
if (result == RT_EOK)
{
rt_event_send(&spi_wifi_data_event, 1);
}
}
}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops rw009_ops =
{
rw009_wifi_init,
rw009_wifi_open,
rw009_wifi_close,
rw009_wifi_read,
rw009_wifi_write,
rw009_wifi_control
};
#endif
rt_err_t rt_hw_wifi_init(const char *spi_device_name, wifi_mode_t mode)
{
/* align and struct size check. */
RT_ASSERT( (SPI_MAX_DATA_LEN & 0x03) == 0);
RT_ASSERT( sizeof(struct rw009_resp) <= SPI_MAX_DATA_LEN);
rt_memset(&rw009_wifi_device, 0, sizeof(struct rw009_wifi));
rw009_wifi_device.rt_spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name);
if (rw009_wifi_device.rt_spi_device == RT_NULL)
{
SPI_DEBUG("spi device %s not found!\r\n", spi_device_name);
return -RT_ENOSYS;
}
/* config spi */
{
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0. */
cfg.max_hz = 15 * 1000000; /* 10M */
rt_spi_configure(rw009_wifi_device.rt_spi_device, &cfg);
}
#ifdef RT_USING_DEVICE_OPS
rw009_wifi_device.parent.parent.ops = &rw009_ops;
#else
rw009_wifi_device.parent.parent.init = rw009_wifi_init;
rw009_wifi_device.parent.parent.open = rw009_wifi_open;
rw009_wifi_device.parent.parent.close = rw009_wifi_close;
rw009_wifi_device.parent.parent.read = rw009_wifi_read;
rw009_wifi_device.parent.parent.write = rw009_wifi_write;
rw009_wifi_device.parent.parent.control = rw009_wifi_control;
#endif
rw009_wifi_device.parent.parent.user_data = RT_NULL;
rw009_wifi_device.parent.eth_rx = rw009_wifi_rx;
rw009_wifi_device.parent.eth_tx = rw009_wifi_tx;
rt_mp_init(&rw009_wifi_device.spi_tx_mp,
"spi_tx",
&rw009_wifi_device.spi_tx_mempool[0],
sizeof(rw009_wifi_device.spi_tx_mempool),
sizeof(struct spi_data_packet));
rt_mp_init(&rw009_wifi_device.spi_rx_mp,
"spi_rx",
&rw009_wifi_device.spi_rx_mempool[0],
sizeof(rw009_wifi_device.spi_rx_mempool),
sizeof(struct spi_data_packet));
rt_mb_init(&rw009_wifi_device.spi_tx_mb,
"spi_tx",
&rw009_wifi_device.spi_tx_mb_pool[0],
SPI_TX_POOL_SIZE,
RT_IPC_FLAG_PRIO);
rt_mb_init(&rw009_wifi_device.eth_rx_mb,
"eth_rx",
&rw009_wifi_device.eth_rx_mb_pool[0],
SPI_TX_POOL_SIZE,
RT_IPC_FLAG_PRIO);
rt_mb_init(&rw009_wifi_device.rw009_cmd_mb,
"wifi_cmd",
&rw009_wifi_device.rw009_cmd_mb_pool[0],
sizeof(rw009_wifi_device.rw009_cmd_mb_pool) / 4,
RT_IPC_FLAG_PRIO);
rt_event_init(&spi_wifi_data_event, "wifi", RT_IPC_FLAG_FIFO);
spi_wifi_hw_init();
{
rt_thread_t tid;
tid = rt_thread_create("wifi",
spi_wifi_data_thread_entry,
RT_NULL,
2048,
RT_THREAD_PRIORITY_MAX - 2,
20);
if (tid != RT_NULL)
rt_thread_startup(tid);
}
/* init: get mac address */
{
rw009_cmd_init init;
init.mode = mode;
WIFI_DEBUG("wifi_control RW009_CMD_INIT\n");
rw009_wifi_control((rt_device_t)&rw009_wifi_device,
RW009_CMD_INIT,
(void *)&init); // 0: firmware, 1: STA, 2:AP
}
/* register eth device */
eth_device_init(&(rw009_wifi_device.parent), "w0");
eth_device_linkchange(&rw009_wifi_device.parent, RT_FALSE);
return RT_EOK;
}
void spi_wifi_isr(int vector)
{
/* enter interrupt */
rt_interrupt_enter();
SPI_DEBUG("spi_wifi_isr\n");
rt_event_send(&spi_wifi_data_event, 1);
/* leave interrupt */
rt_interrupt_leave();
}
/********************************* RW009 tools **************************************/
rt_err_t rw009_join(const char * SSID, const char * passwd)
{
rt_err_t result;
rt_device_t wifi_device;
rw009_cmd_easy_join easy_join;
wifi_device = rt_device_find("w0");
if(wifi_device == RT_NULL)
return -RT_ENOSYS;
strncpy( easy_join.ssid, SSID, sizeof(easy_join.ssid) );
strncpy( easy_join.passwd, passwd, sizeof(easy_join.passwd) );
result = rt_device_control(wifi_device,
RW009_CMD_EASY_JOIN,
(void *)&easy_join);
return result;
}
rt_err_t rw009_softap(const char * SSID, const char * passwd,uint32_t security,uint32_t channel)
{
rt_err_t result;
rt_device_t wifi_device;
rw009_cmd_softap softap;
wifi_device = rt_device_find("w0");
if(wifi_device == RT_NULL)
return -RT_ENOSYS;
strncpy( softap.ssid, SSID, sizeof(softap.ssid) );
strncpy( softap.passwd, passwd, sizeof(softap.passwd) );
softap.security = security;
softap.channel = channel;
result = rt_device_control(wifi_device,
RW009_CMD_SOFTAP,
(void *)&softap);
return result;
}
int32_t rw009_rssi(void)
{
rt_err_t result;
struct rw009_wifi * wifi_device;
wifi_device = (struct rw009_wifi *)rt_device_find("w0");
if(wifi_device == RT_NULL)
return 0;
if(wifi_device->active == 0)
return 0;
// SCAN
result = rt_device_control((rt_device_t)wifi_device,
RW009_CMD_RSSI,
RT_NULL);
if(result == RT_EOK)
{
return wifi_device->ap_info.rssi;
}
return 0;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
static rt_err_t rw009_scan(void)
{
rt_err_t result;
struct rw009_wifi * wifi_device;
wifi_device = (struct rw009_wifi *)rt_device_find("w0");
rt_kprintf("\nCMD RW009_CMD_SCAN \n");
result = rt_device_control((rt_device_t)wifi_device,
RW009_CMD_SCAN,
RT_NULL);
rt_kprintf("CMD RW009_CMD_SCAN result:%d\n", result);
if(result == RT_EOK)
{
uint32_t i;
rw009_ap_info *ap_info;
for(i=0; i<wifi_device->ap_scan_count; i++)
{
ap_info = &wifi_device->ap_scan[i];
rt_kprintf("AP #%02d SSID: %-32.32s\n", i, ap_info->ssid );
}
}
return result;
}
FINSH_FUNCTION_EXPORT(rw009_scan, SACN and list AP.);
FINSH_FUNCTION_EXPORT(rw009_join, RW009 join to AP.);
FINSH_FUNCTION_EXPORT(rw009_rssi, get RW009 current AP rssi.);
#endif // RT_USING_FINSH

View file

@ -0,0 +1,212 @@
/*
* COPYRIGHT (C) 2011-2023, Real-Thread Information Technology Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2014-07-31 aozima the first version
* 2014-09-18 aozima update command & response.
*/
#ifndef SPI_WIFI_H_INCLUDED
#define SPI_WIFI_H_INCLUDED
#include <stdint.h>
// little-endian
struct spi_cmd_request
{
uint32_t flag;
uint32_t M2S_len; // master to slave data len.
uint32_t magic1;
uint32_t magic2;
};
#define CMD_MAGIC1 (0x67452301)
#define CMD_MAGIC2 (0xEFCDAB89)
#define CMD_FLAG_MRDY (0x01)
// little-endian
struct spi_response
{
uint32_t flag;
uint32_t S2M_len; // slave to master data len.
uint32_t magic1;
uint32_t magic2;
};
#define RESP_FLAG_SRDY (0x01)
#define RESP_MAGIC1 (0x98BADCFE)
#define RESP_MAGIC2 (0x10325476)
/* spi slave configure. */
#define SPI_MAX_DATA_LEN 1520
#define SPI_TX_POOL_SIZE 2
#define SPI_RX_POOL_SIZE 2
typedef enum
{
data_type_eth_data = 0,
data_type_cmd,
data_type_resp,
data_type_status,
}
app_data_type_typedef;
struct spi_data_packet
{
uint32_t data_len;
uint32_t data_type;
char buffer[SPI_MAX_DATA_LEN];
};
/********************************* RW009 **************************************/
/* option */
#define RW009_CMD_TIMEOUT (RT_TICK_PER_SECOND*3)
#define SSID_NAME_LENGTH_MAX (32)
#define PASSWORD_LENGTH_MAX (64)
typedef enum
{
MODE_STATION=0,
MODE_SOFTAP=1,
} wifi_mode_t;
typedef struct _rw009_ap_info
{
char ssid[SSID_NAME_LENGTH_MAX];
uint8_t bssid[8]; // 6byte + 2byte PAD.
int rssi; /* Receive Signal Strength Indication in dBm. */
uint32_t max_data_rate; /* Maximum data rate in kilobits/s */
uint32_t security; /* Security type */
uint32_t channel; /* Radio channel that the AP beacon was received on */
} rw009_ap_info;
typedef struct _rw009_cmd_init
{
uint32_t mode;
} rw009_cmd_init;
typedef struct _rw009_resp_init
{
uint8_t mac[8]; // 6byte + 2byte PAD.
uint8_t sn[24]; // serial.
char version[16]; // firmware version.
} rw009_resp_init;
typedef struct _rw009_cmd_easy_join
{
char ssid[SSID_NAME_LENGTH_MAX];
char passwd[PASSWORD_LENGTH_MAX];
} rw009_cmd_easy_join;
typedef struct _rw009_cmd_join
{
uint8_t bssid[8]; // 6byte + 2byte PAD.
char passwd[PASSWORD_LENGTH_MAX];
} rw009_cmd_join;
typedef struct _rw009_cmd_rssi
{
uint8_t bssid[8]; // 6byte + 2byte PAD.
} rw009_cmd_rssi;
typedef struct _rw009_cmd_softap
{
char ssid[SSID_NAME_LENGTH_MAX];
char passwd[PASSWORD_LENGTH_MAX];
uint32_t security; /* Security type. */
uint32_t channel; /* Radio channel that the AP beacon was received on */
} rw009_cmd_softap;
typedef struct _rw009_resp_join
{
rw009_ap_info ap_info;
} rw009_resp_join;
struct rw009_cmd
{
uint32_t cmd;
uint32_t len;
/** command body */
union
{
rw009_cmd_init init;
rw009_cmd_easy_join easy_join;
rw009_cmd_join join;
rw009_cmd_rssi rssi;
rw009_cmd_softap softap;
} params;
};
struct rw009_resp
{
uint32_t cmd;
uint32_t len;
int32_t result; // result for CMD.
/** resp Body */
union
{
rw009_resp_init init;
rw009_ap_info ap_info;
} resp;
};
#define RW009_CMD_INIT 128
#define RW009_CMD_SCAN 129
#define RW009_CMD_JOIN 130
#define RW009_CMD_EASY_JOIN 131
#define RW009_CMD_RSSI 132
#define RW009_CMD_SOFTAP 133
/** cond !ADDTHIS*/
#define SHARED_ENABLED 0x00008000
#define WPA_SECURITY 0x00200000
#define WPA2_SECURITY 0x00400000
#define WPS_ENABLED 0x10000000
#define WEP_ENABLED 0x0001
#define TKIP_ENABLED 0x0002
#define AES_ENABLED 0x0004
#define WSEC_SWFLAG 0x0008
/** endcond */
/**
* Enumeration of Wi-Fi security modes
*/
typedef enum
{
SECURITY_OPEN = 0, /**< Open security */
SECURITY_WEP_PSK = WEP_ENABLED, /**< WEP Security with open authentication */
SECURITY_WEP_SHARED = ( WEP_ENABLED | SHARED_ENABLED ), /**< WEP Security with shared authentication */
SECURITY_WPA_TKIP_PSK = ( WPA_SECURITY | TKIP_ENABLED ), /**< WPA Security with TKIP */
SECURITY_WPA_AES_PSK = ( WPA_SECURITY | AES_ENABLED ), /**< WPA Security with AES */
SECURITY_WPA2_AES_PSK = ( WPA2_SECURITY | AES_ENABLED ), /**< WPA2 Security with AES */
SECURITY_WPA2_TKIP_PSK = ( WPA2_SECURITY | TKIP_ENABLED ), /**< WPA2 Security with TKIP */
SECURITY_WPA2_MIXED_PSK = ( WPA2_SECURITY | AES_ENABLED | TKIP_ENABLED ), /**< WPA2 Security with AES & TKIP */
SECURITY_WPS_OPEN = WPS_ENABLED, /**< WPS with open security */
SECURITY_WPS_SECURE = (WPS_ENABLED | AES_ENABLED), /**< WPS with AES security */
SECURITY_UNKNOWN = -1, /**< May be returned by scan function if security is unknown. Do not pass this to the join function! */
SECURITY_FORCE_32_BIT = 0x7fffffff /**< Exists only to force wiced_security_t type to 32 bits */
} security_t;
/* porting */
extern void spi_wifi_hw_init(void);
extern void spi_wifi_int_cmd(rt_bool_t cmd);
extern rt_bool_t spi_wifi_is_busy(void);
/* export API. */
extern rt_err_t rt_hw_wifi_init(const char *spi_device_name,wifi_mode_t mode);
extern int32_t rw009_rssi(void);
extern rt_err_t rw009_join(const char * SSID, const char * passwd);
extern rt_err_t rw009_softap(const char * SSID, const char * passwd,uint32_t security,uint32_t channel);
#endif // SPI_WIFI_H_INCLUDED