From 9b4319f735bf1c209f51ce2c5e7fd642ceb60909 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Fri, 21 Aug 2009 11:10:09 +0000 Subject: git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1099 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/io/platforms/AT91SAM7X/mii.h | 156 ++++++++++++ os/io/platforms/AT91SAM7X/pal_lld.c | 116 +++++++++ os/io/platforms/AT91SAM7X/pal_lld.h | 245 +++++++++++++++++++ os/io/platforms/AT91SAM7X/platform.dox | 73 ++++++ os/io/platforms/AT91SAM7X/sam7x_emac.c | 418 +++++++++++++++++++++++++++++++++ os/io/platforms/AT91SAM7X/sam7x_emac.h | 93 ++++++++ 6 files changed, 1101 insertions(+) create mode 100644 os/io/platforms/AT91SAM7X/mii.h create mode 100644 os/io/platforms/AT91SAM7X/pal_lld.c create mode 100644 os/io/platforms/AT91SAM7X/pal_lld.h create mode 100644 os/io/platforms/AT91SAM7X/platform.dox create mode 100644 os/io/platforms/AT91SAM7X/sam7x_emac.c create mode 100644 os/io/platforms/AT91SAM7X/sam7x_emac.h (limited to 'os/io') diff --git a/os/io/platforms/AT91SAM7X/mii.h b/os/io/platforms/AT91SAM7X/mii.h new file mode 100644 index 000000000..4ec25667a --- /dev/null +++ b/os/io/platforms/AT91SAM7X/mii.h @@ -0,0 +1,156 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/* + * Parts of this file are borrowed by the Linux include file linux/mii.h: + * Copyright (C) 1996, 1999, 2001 David S. Miller (davem@redhat.com) + */ + +#ifndef _MII_H_ +#define _MII_H_ + +/* Generic MII registers. */ +#define MII_BMCR 0x00 /* Basic mode control register */ +#define MII_BMSR 0x01 /* Basic mode status register */ +#define MII_PHYSID1 0x02 /* PHYS ID 1 */ +#define MII_PHYSID2 0x03 /* PHYS ID 2 */ +#define MII_ADVERTISE 0x04 /* Advertisement control reg */ +#define MII_LPA 0x05 /* Link partner ability reg */ +#define MII_EXPANSION 0x06 /* Expansion register */ +#define MII_CTRL1000 0x09 /* 1000BASE-T control */ +#define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_ESTATUS 0x0f /* Extended Status */ +#define MII_DCOUNTER 0x12 /* Disconnect counter */ +#define MII_FCSCOUNTER 0x13 /* False carrier counter */ +#define MII_NWAYTEST 0x14 /* N-way auto-neg test reg */ +#define MII_RERRCOUNTER 0x15 /* Receive error counter */ +#define MII_SREVISION 0x16 /* Silicon revision */ +#define MII_RESV1 0x17 /* Reserved... */ +#define MII_LBRERROR 0x18 /* Lpback, rx, bypass error */ +#define MII_PHYADDR 0x19 /* PHY address */ +#define MII_RESV2 0x1a /* Reserved... */ +#define MII_TPISTATUS 0x1b /* TPI status for 10mbps */ +#define MII_NCONFIG 0x1c /* Network interface config */ + +/* Basic mode control register. */ +#define BMCR_RESV 0x003f /* Unused... */ +#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */ +#define BMCR_CTST 0x0080 /* Collision test */ +#define BMCR_FULLDPLX 0x0100 /* Full duplex */ +#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ +#define BMCR_ISOLATE 0x0400 /* Disconnect DP83840 from MII */ +#define BMCR_PDOWN 0x0800 /* Powerdown the DP83840 */ +#define BMCR_ANENABLE 0x1000 /* Enable auto negotiation */ +#define BMCR_SPEED100 0x2000 /* Select 100Mbps */ +#define BMCR_LOOPBACK 0x4000 /* TXD loopback bits */ +#define BMCR_RESET 0x8000 /* Reset the DP83840 */ + +/* Basic mode status register. */ +#define BMSR_ERCAP 0x0001 /* Ext-reg capability */ +#define BMSR_JCD 0x0002 /* Jabber detected */ +#define BMSR_LSTATUS 0x0004 /* Link status */ +#define BMSR_ANEGCAPABLE 0x0008 /* Able to do auto-negotiation */ +#define BMSR_RFAULT 0x0010 /* Remote fault detected */ +#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */ +#define BMSR_RESV 0x00c0 /* Unused... */ +#define BMSR_ESTATEN 0x0100 /* Extended Status in R15 */ +#define BMSR_100HALF2 0x0200 /* Can do 100BASE-T2 HDX */ +#define BMSR_100FULL2 0x0400 /* Can do 100BASE-T2 FDX */ +#define BMSR_10HALF 0x0800 /* Can do 10mbps, half-duplex */ +#define BMSR_10FULL 0x1000 /* Can do 10mbps, full-duplex */ +#define BMSR_100HALF 0x2000 /* Can do 100mbps, half-duplex */ +#define BMSR_100FULL 0x4000 /* Can do 100mbps, full-duplex */ +#define BMSR_100BASE4 0x8000 /* Can do 100mbps, 4k packets */ + +/* Advertisement control register. */ +#define ADVERTISE_SLCT 0x001f /* Selector bits */ +#define ADVERTISE_CSMA 0x0001 /* Only selector supported */ +#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */ +#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */ +#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */ +#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */ +#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */ +#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */ +#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */ +#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */ +#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */ +#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */ +#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymetric pause */ +#define ADVERTISE_RESV 0x1000 /* Unused... */ +#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */ +#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */ +#define ADVERTISE_NPAGE 0x8000 /* Next page bit */ + +#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ + ADVERTISE_CSMA) +#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \ + ADVERTISE_100HALF | ADVERTISE_100FULL) + +/* Link partner ability register. */ +#define LPA_SLCT 0x001f /* Same as advertise selector */ +#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */ +#define LPA_1000XFULL 0x0020 /* Can do 1000BASE-X full-duplex */ +#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */ +#define LPA_1000XHALF 0x0040 /* Can do 1000BASE-X half-duplex */ +#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */ +#define LPA_1000XPAUSE 0x0080 /* Can do 1000BASE-X pause */ +#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */ +#define LPA_1000XPAUSE_ASYM 0x0100 /* Can do 1000BASE-X pause asym*/ +#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */ +#define LPA_PAUSE_CAP 0x0400 /* Can pause */ +#define LPA_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +#define LPA_RESV 0x1000 /* Unused... */ +#define LPA_RFAULT 0x2000 /* Link partner faulted */ +#define LPA_LPACK 0x4000 /* Link partner acked us */ +#define LPA_NPAGE 0x8000 /* Next page bit */ + +#define LPA_DUPLEX (LPA_10FULL | LPA_100FULL) +#define LPA_100 (LPA_100FULL | LPA_100HALF | LPA_100BASE4) + +/* Expansion register for auto-negotiation. */ +#define EXPANSION_NWAY 0x0001 /* Can do N-way auto-nego */ +#define EXPANSION_LCWP 0x0002 /* Got new RX page code word */ +#define EXPANSION_ENABLENPAGE 0x0004 /* This enables npage words */ +#define EXPANSION_NPCAPABLE 0x0008 /* Link partner supports npage */ +#define EXPANSION_MFAULTS 0x0010 /* Multiple faults detected */ +#define EXPANSION_RESV 0xffe0 /* Unused... */ + +#define ESTATUS_1000_TFULL 0x2000 /* Can do 1000BT Full */ +#define ESTATUS_1000_THALF 0x1000 /* Can do 1000BT Half */ + +/* N-way test register. */ +#define NWAYTEST_RESV1 0x00ff /* Unused... */ +#define NWAYTEST_LOOPBACK 0x0100 /* Enable loopback for N-way */ +#define NWAYTEST_RESV2 0xfe00 /* Unused... */ + +/* 1000BASE-T Control register */ +#define ADVERTISE_1000FULL 0x0200 /* Advertise 1000BASE-T full duplex */ +#define ADVERTISE_1000HALF 0x0100 /* Advertise 1000BASE-T half duplex */ + +/* 1000BASE-T Status register */ +#define LPA_1000LOCALRXOK 0x2000 /* Link partner local receiver status */ +#define LPA_1000REMRXOK 0x1000 /* Link partner remote receiver status */ +#define LPA_1000FULL 0x0800 /* Link partner 1000BASE-T full duplex */ +#define LPA_1000HALF 0x0400 /* Link partner 1000BASE-T half duplex */ + +#define MII_DM9161_ID 0x0181b8a0 +#define MII_AM79C875_ID 0x00225540 +#define MII_MICREL_ID 0x00221610 + +#endif /* _MII_H_ */ diff --git a/os/io/platforms/AT91SAM7X/pal_lld.c b/os/io/platforms/AT91SAM7X/pal_lld.c new file mode 100644 index 000000000..6c8b26cda --- /dev/null +++ b/os/io/platforms/AT91SAM7X/pal_lld.c @@ -0,0 +1,116 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file os/ports/GCC/ARM7/AT91SAM7X/pal_lld.c + * @brief AT91SAM7X PIO low level driver code + * @addtogroup AT91SAM7X_PAL + * @{ + */ + +#include +#include + +/** + * @brief AT91SAM7X I/O ports configuration. + * @details PIO registers initialization. + * + * @param[in] config the AT91SAM7X ports configuration + */ +void _pal_lld_init(const AT91SAM7XPIOConfig *config) { + + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PIOA) | (1 << AT91C_ID_PIOB); + + /* + * PIOA setup. + */ + AT91C_BASE_PIOA->PIO_OER = config->P0Data.pusr; /* Pull-up as spec.*/ + AT91C_BASE_PIOA->PIO_ODR = ~config->P0Data.pusr; + AT91C_BASE_PIOA->PIO_PER = 0xFFFFFFFF; /* PIO enabled.*/ + AT91C_BASE_PIOA->PIO_ODSR = config->P0Data.odsr; /* Data as specified.*/ + AT91C_BASE_PIOA->PIO_OER = config->P0Data.osr; /* Dir. as specified.*/ + AT91C_BASE_PIOA->PIO_ODR = ~config->P0Data.osr; + AT91C_BASE_PIOA->PIO_IFDR = 0xFFFFFFFF; /* Filter disabled.*/ + AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF; /* Int. disabled.*/ + AT91C_BASE_PIOA->PIO_MDDR = 0xFFFFFFFF; /* Push Pull drive.*/ + AT91C_BASE_PIOA->PIO_ASR = 0xFFFFFFFF; /* Peripheral A.*/ + AT91C_BASE_PIOA->PIO_OWER = 0xFFFFFFFF; /* Write enabled.*/ + + /* + * PIOB setup. + */ + AT91C_BASE_PIOB->PIO_OER = config->P0Data.pusr; /* Pull-up as spec.*/ + AT91C_BASE_PIOB->PIO_ODR = ~config->P0Data.pusr; + AT91C_BASE_PIOB->PIO_PER = 0xFFFFFFFF; /* PIO enabled.*/ + AT91C_BASE_PIOB->PIO_ODSR = config->P1Data.odsr; /* Data as specified.*/ + AT91C_BASE_PIOB->PIO_OER = config->P1Data.osr; /* Dir. as specified.*/ + AT91C_BASE_PIOB->PIO_ODR = ~config->P1Data.osr; + AT91C_BASE_PIOB->PIO_IFDR = 0xFFFFFFFF; /* Filter disabled.*/ + AT91C_BASE_PIOB->PIO_IDR = 0xFFFFFFFF; /* Int. disabled.*/ + AT91C_BASE_PIOB->PIO_MDDR = 0xFFFFFFFF; /* Push Pull drive.*/ + AT91C_BASE_PIOB->PIO_ASR = 0xFFFFFFFF; /* Peripheral A.*/ + AT91C_BASE_PIOB->PIO_OWER = 0xFFFFFFFF; /* Write enabled.*/ +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @note This function is not meant to be invoked directly by the application + * code. + * @note @p PAL_MODE_RESET is implemented as input with pull-up. + * @note @p PAL_MODE_UNCONNECTED is implemented as push pull output with high + * state. + * @note @p PAL_MODE_OUTPUT_OPENDRAIN also enables the pull-up resistor. + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + uint_fast8_t mode) { + + switch (mode & PAL_MODE_MASK) { + case PAL_MODE_RESET: + case PAL_MODE_INPUT_PULLUP: + port->PIO_PPUER = mask; + port->PIO_ODR = mask; + break; + case PAL_MODE_INPUT: + port->PIO_PPUDR = mask; + port->PIO_ODR = mask; + break; + case PAL_MODE_UNCONNECTED: + port->PIO_SODR = mask; + /* Falls in */ + case PAL_MODE_OUTPUT_PUSHPULL: + port->PIO_PPUDR = mask; + port->PIO_OER = mask; + port->PIO_MDDR = mask; + break; + case PAL_MODE_OUTPUT_OPENDRAIN: + port->PIO_PPUER = mask; + port->PIO_OER = mask; + port->PIO_MDER = mask; + } +} + +/** @} */ diff --git a/os/io/platforms/AT91SAM7X/pal_lld.h b/os/io/platforms/AT91SAM7X/pal_lld.h new file mode 100644 index 000000000..1f50b5734 --- /dev/null +++ b/os/io/platforms/AT91SAM7X/pal_lld.h @@ -0,0 +1,245 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file os/ports/GCC/ARM7/AT91SAM7X/pal_lld.h + * @brief AT91SAM7X PIO low level driver header + * @addtogroup AT91SAM7X_PAL + * @{ + */ + +#ifndef _PAL_LLD_H_ +#define _PAL_LLD_H_ + +#include "at91lib/AT91SAM7X256.h" + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +#undef PAL_MODE_INPUT_PULLDOWN + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +/** + * @brief PIO port setup info. + */ +typedef struct { + /** Initial value for ODSR register (data).*/ + uint32_t odsr; + /** Initial value for OSR register (direction).*/ + uint32_t osr; + /** Initial value for PUSR register (Pull-ups).*/ + uint32_t pusr; +} at91sam7x_pio_setup_t; + +/** + * @brief AT91SAM7X PIO static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialized the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + */ +typedef struct { + /** @brief Port 0 setup data.*/ + at91sam7x_pio_setup_t P0Data; + /** @brief Port 1 setup data.*/ + at91sam7x_pio_setup_t P1Data; +} AT91SAM7XPIOConfig; + +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 32 + +/** + * @brief Digital I/O port sized unsigned type. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef AT91PS_PIO ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/*===========================================================================*/ + +/** + * @brief PIO port A identifier. + */ +#define IOPORT_A AT91C_BASE_PIOA + +/** + * @brief PIO port B identifier. + */ +#define IOPORT_B AT91C_BASE_PIOB + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in a file named pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief Low level PAL subsystem initialization. + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads the physical I/O port states. + * @details This function is implemented by reading the PIO_PDSR register, the + * implementation has no side effects. + * + * @param[in] port the port identifier + * @return The port bits. + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_readport(port) ((port)->PIO_PDSR) + +/** + * @brief Reads the output latch. + * @details This function is implemented by reading the PIO_ODSR register, the + * implementation has no side effects. + * + * @param[in] port the port identifier + * @return The latched logical states. + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_readlatch(port) ((port)->PIO_ODSR) + +/** + * @brief Writes a bits mask on a I/O port. + * @details This function is implemented by writing the PIO_ODSR register, the + * implementation has no side effects. + * + * @param[in] port the port identifier + * @param[in] bits the bits to be written on the specified port + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_writeport(port, bits) { \ + (port)->PIO_ODSR = (bits); \ +} + +/** + * @brief Sets a bits mask on a I/O port. + * @details This function is implemented by writing the PIO_SODR register, the + * implementation has no side effects. + * + * @param[in] port the port identifier + * @param[in] bits the bits to be ORed on the specified port + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_setport(port, bits) { \ + (port)->PIO_SODR = (bits); \ +} + + +/** + * @brief Clears a bits mask on a I/O port. + * @details This function is implemented by writing the PIO_CODR register, the + * implementation has no side effects. + * + * @param[in] port the port identifier + * @param[in] bits the bits to be cleared on the specified port + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_clearport(port, bits) { \ + (port)->PIO_CODR = (bits); \ +} + +/** + * @brief Writes a group of bits. + * @details This function is implemented by writing the PIO_OWER, PIO_ODSR and + * PIO_OWDR registers, the implementation is not atomic because the + * multiple accesses. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] offset the group bit offset within the port + * @param[in] bits the bits to be written. Values exceeding the group width + * are masked. + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_writegroup(port, mask, offset, bits) { \ + (port)->PIO_OWER = (mask) << (offset); \ + (port)->PIO_ODSR = (bits) << (offset); \ + (port)->PIO_OWDR = (mask) << (offset); \ +} + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @note This function is not meant to be invoked directly by the application + * code. + * @note @p PAL_MODE_UNCONNECTED is implemented as push pull output with high + * state. + */ +#define pal_lld_setgroupmode(port, mask, mode) \ + _pal_lld_setgroupmode(port, mask, mode) + +/** + * @brief Writes a logical state on an output pad. + * + * @param[in] port the port identifier + * @param[in] pad the pad number within the port + * @param[out] bit the logical value, the value must be @p 0 or @p 1 + * + * @note This function is not meant to be invoked directly by the application + * code. + */ +#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const AT91SAM7XPIOConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + uint_fast8_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/io/platforms/AT91SAM7X/platform.dox b/os/io/platforms/AT91SAM7X/platform.dox new file mode 100644 index 000000000..dac6ed181 --- /dev/null +++ b/os/io/platforms/AT91SAM7X/platform.dox @@ -0,0 +1,73 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @defgroup AT91SAM7X AT91SAM7X Support + * @brief AT91SAM7X specific support. + * @details The AT91SAM7X support includes: + * - Buffered, interrupt driven, serial driver. + * - EMAC driver with MII support. + * - A demo supporting the kernel test suite. + * - A Web server demo using the uIP TCP/IP stack. + * . + * @ingroup ARM7 + */ + +/** + * @defgroup AT91SAM7X_PAL I/O Ports Support + * @brief I/O Ports peripherals support. + * @details This module supports the AT91SAM7X PIO controller. The controller + * supports the following features (see @ref PAL): + * - 32 bits wide ports. + * - Atomic set/reset functions. + * - Output latched regardless of the pad setting. + * - Direct read of input pads regardless of the pad setting. + * . + *

Supported Setup Modes

+ * - @p PAL_MODE_RESET. + * - @p PAL_MODE_UNCONNECTED. + * - @p PAL_MODE_INPUT. + * - @p PAL_MODE_INPUT_PULLUP. + * - @p PAL_MODE_OUTPUT_PUSHPULL. + * - @p PAL_MODE_OUTPUT_OPENDRAIN. + * . + * Any attempt to setup an invalid mode is ignored. + * + *

Suboptimal Behavior

+ * Some PIO features are less than optimal: + * - Pad/port toggling operations are not atomic. + * - Pad/group mode setup is not atomic. + * . + * @ingroup AT91SAM7X + */ + +/** + * @defgroup AT91SAM7X_SERIAL USART Support + * @brief USART peripherals support. + * @details The serial driver supports the AT91SAM7X USART peripherals. + * + * @ingroup AT91SAM7X + */ + +/** + * @defgroup AT91SAM7X_EMAC EMAC Support + * @brief EMAC peripheral support. + * + * @ingroup AT91SAM7X + */ diff --git a/os/io/platforms/AT91SAM7X/sam7x_emac.c b/os/io/platforms/AT91SAM7X/sam7x_emac.c new file mode 100644 index 000000000..82d7e8381 --- /dev/null +++ b/os/io/platforms/AT91SAM7X/sam7x_emac.c @@ -0,0 +1,418 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file AT91SAM7X/sam7x_emac.c + * @brief AT91SAM7X EMAC driver code. + * @addtogroup AT91SAM7X_EMAC + * @{ + */ + +#include + +#include + +#include "board.h" +#include "sam7x_emac.h" +#include "mii.h" +#include "at91lib/aic.h" + +EventSource EMACFrameTransmitted; /* A frame was transmitted. */ +EventSource EMACFrameReceived; /* A frame was received. */ + +#ifndef __DOXYGEN__ +//static int received; /* Buffered frames counter. */ +static bool_t link_up; /* Last from EMACGetLinkStatus()*/ + +static uint8_t default_mac[] = {0xAA, 0x55, 0x13, 0x37, 0x01, 0x10}; + +static BufDescriptorEntry rent[EMAC_RECEIVE_BUFFERS] __attribute__((aligned(8))); +static uint8_t rbuffers[EMAC_RECEIVE_BUFFERS * EMAC_RECEIVE_BUFFERS_SIZE] __attribute__((aligned(8))); +static BufDescriptorEntry *rxptr; + +static BufDescriptorEntry tent[EMAC_TRANSMIT_BUFFERS] __attribute__((aligned(8))); +static uint8_t tbuffers[EMAC_TRANSMIT_BUFFERS * EMAC_TRANSMIT_BUFFERS_SIZE] __attribute__((aligned(8))); +static BufDescriptorEntry *txptr; +#endif + +#define AT91C_PB15_ERXDV AT91C_PB15_ERXDV_ECRSDV +#define EMAC_PIN_MASK (AT91C_PB0_ETXCK_EREFCK | \ + AT91C_PB1_ETXEN | AT91C_PB2_ETX0 | \ + AT91C_PB3_ETX1 | AT91C_PB4_ECRS | \ + AT91C_PB5_ERX0 | AT91C_PB6_ERX1 | \ + AT91C_PB7_ERXER | AT91C_PB8_EMDC | \ + AT91C_PB9_EMDIO | AT91C_PB10_ETX2 | \ + AT91C_PB11_ETX3 | AT91C_PB12_ETXER | \ + AT91C_PB13_ERX2 | AT91C_PB14_ERX3 | \ + AT91C_PB15_ERXDV | AT91C_PB16_ECOL | \ + AT91C_PB17_ERXCK) + +#define PHY_LATCHED_PINS (AT91C_PB4_ECRS | AT91C_PB5_ERX0 | AT91C_PB6_ERX1 | \ + AT91C_PB7_ERXER | AT91C_PB13_ERX2 | AT91C_PB14_ERX3 | \ + AT91C_PB15_ERXDV | AT91C_PB16_ECOL | PIOB_PHY_IRQ_MASK) + +/* + * PHY utilities. + */ +static uint32_t phy_get(uint8_t regno) { + + AT91C_BASE_EMAC->EMAC_MAN = (1 << 30) | // SOF = 01 + (2 << 28) | // RW = 10 + (PHY_ADDRESS << 23) | + (regno << 18) | + (2 << 16); // CODE = 10 + while (!( AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE)) + ; + return AT91C_BASE_EMAC->EMAC_MAN & 0xFFFF; +} + +/*static void phy_put(uint8_t regno, uint32_t val) { + + AT91C_BASE_EMAC->EMAC_MAN = (1 << 30) | // SOF = 01 + (1 << 28) | // RW = 01 + (PHY_ADDRESS << 23) | + (regno << 18) | + (2 << 16) | // CODE = 10 + val; + while (!( AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE)) + ; +}*/ + +#define RSR_BITS (AT91C_EMAC_BNA | AT91C_EMAC_REC | AT91C_EMAC_OVR) +#define TSR_BITS (AT91C_EMAC_UBR | AT91C_EMAC_COL | AT91C_EMAC_RLES | \ + AT91C_EMAC_BEX | AT91C_EMAC_COMP | AT91C_EMAC_UND) + +__attribute__((noinline)) +static void ServeInterrupt(void) { + uint32_t isr, rsr, tsr; + + /* Fix for the EMAC errata */ + isr = AT91C_BASE_EMAC->EMAC_ISR; + rsr = AT91C_BASE_EMAC->EMAC_RSR; + tsr = AT91C_BASE_EMAC->EMAC_TSR; + + if ((isr & AT91C_EMAC_RCOMP) || (rsr & RSR_BITS)) { + if (rsr & AT91C_EMAC_REC) { +// received++; + chSysLockFromIsr(); + chEvtBroadcastI(&EMACFrameReceived); + chSysUnlockFromIsr(); + } + AT91C_BASE_EMAC->EMAC_RSR = RSR_BITS; + } + + if ((isr & AT91C_EMAC_TCOMP) || (tsr & TSR_BITS)) { + if (tsr & AT91C_EMAC_COMP) { + chSysLockFromIsr(); + chEvtBroadcastI(&EMACFrameTransmitted); + chSysUnlockFromIsr(); + } + AT91C_BASE_EMAC->EMAC_TSR = TSR_BITS; + } + AT91C_BASE_AIC->AIC_EOICR = 0; +} + +CH_IRQ_HANDLER(EMACIrqHandler) { + + CH_IRQ_PROLOGUE(); + + ServeInterrupt(); + + CH_IRQ_EPILOGUE(); +} + +/* + * EMAC subsystem initialization. + */ +void emac_init(int prio) { + int i; + + /* + * Buffers initialization. + */ +// received = 0; + for (i = 0; i < EMAC_RECEIVE_BUFFERS; i++) { + rent[i].w1 = (uint32_t)&rbuffers[i * EMAC_RECEIVE_BUFFERS_SIZE]; + rent[i].w2 = 0; + } + rent[EMAC_RECEIVE_BUFFERS - 1].w1 |= W1_R_WRAP; + rxptr = rent; + for (i = 0; i < EMAC_TRANSMIT_BUFFERS; i++) { + tent[i].w1 = (uint32_t)&tbuffers[i * EMAC_TRANSMIT_BUFFERS_SIZE]; + tent[i].w2 = EMAC_TRANSMIT_BUFFERS_SIZE | W2_T_USED; + } + tent[EMAC_TRANSMIT_BUFFERS - 1].w2 |= W2_T_WRAP; + txptr = tent; + + /* + * Disables the pullups on all the pins that are latched on reset by the PHY. + * The status latched into the PHY is: + * PHYADDR = 00001 + * PCS_LPBK = 0 (disabled) + * ISOLATE = 0 (disabled) + * RMIISEL = 0 (MII mode) + * RMIIBTB = 0 (BTB mode disabled) + * SPEED = 1 (100mbps) + * DUPLEX = 1 (full duplex) + * ANEG_EN = 1 (auto negotiation enabled) + */ + AT91C_BASE_PIOB->PIO_PPUDR = PHY_LATCHED_PINS; + + /* + * PHY power control. + */ + AT91C_BASE_PIOB->PIO_OER = PIOB_PHY_PD_MASK; // Becomes an output. + AT91C_BASE_PIOB->PIO_PPUDR = PIOB_PHY_PD_MASK; // Default pullup disabled. + AT91C_BASE_PIOB->PIO_SODR = PIOB_PHY_PD_MASK; // Output to high level. + + /* + * PHY reset by pulsing the NRST pin. + */ + AT91C_BASE_RSTC->RSTC_RMR = 0xA5000100; + AT91C_BASE_RSTC->RSTC_RCR = 0xA5000000 | AT91C_RSTC_EXTRST; + while (!(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_NRSTL)) + ; + + /* + * EMAC pins setup and clock enable. Note, PB18 is not included because it is + * used as #PD control and not as EF100. + */ + AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_EMAC; + AT91C_BASE_PIOB->PIO_ASR = EMAC_PIN_MASK; + AT91C_BASE_PIOB->PIO_PDR = EMAC_PIN_MASK; + AT91C_BASE_PIOB->PIO_PPUDR = EMAC_PIN_MASK; + + /* + * EMAC setup. + */ + AT91C_BASE_EMAC->EMAC_NCR = 0; // Initial setting. + AT91C_BASE_EMAC->EMAC_NCFGR = 2 << 10; // MDC-CLK = MCK / 32 + AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN; // Enable EMAC in MII mode + AT91C_BASE_EMAC->EMAC_RBQP = (AT91_REG)rent; // RX buffers list + AT91C_BASE_EMAC->EMAC_TBQP = (AT91_REG)tent; // TX buffers list + AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_OVR | + AT91C_EMAC_REC | + AT91C_EMAC_BNA; // Clears RSR + AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_DRFCS; // Initial NCFGR settings + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE | + AT91C_EMAC_RE | + AT91C_EMAC_CLRSTAT; // Initial NCR settings + EMACSetAddress(default_mac); + + /* + * PHY detection and settings. + */ + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE; + if ((phy_get(MII_PHYSID1) != (MII_MICREL_ID >> 16)) || + ((phy_get(MII_PHYSID2) & 0xFFF0) != (MII_MICREL_ID & 0xFFF0))) + chSysHalt(); + + /* + * Waits for auto-negotiation to end and then detects the link status. + */ + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE; + + /* + * Interrupt setup. + */ + AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP | AT91C_EMAC_TCOMP; + AIC_ConfigureIT(AT91C_ID_EMAC, + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | prio, + EMACIrqHandler); + AIC_EnableIT(AT91C_ID_EMAC); + + /* + * Event sources setup. + */ + chEvtInit(&EMACFrameTransmitted); + chEvtInit(&EMACFrameReceived); +} + +/* + * Set the MAC address. + */ +void EMACSetAddress(const uint8_t *eaddr) { + + AT91C_BASE_EMAC->EMAC_SA1L = (AT91_REG)((eaddr[3] << 24) | (eaddr[2] << 16) | + (eaddr[1] << 8) | eaddr[0]); + AT91C_BASE_EMAC->EMAC_SA1H = (AT91_REG)((eaddr[5] << 8) | eaddr[4]); +} + +/* + * Returns TRUE if the link is active. To be invoked at regular intervals in + * order to monitor the link. + * @note It is not thread-safe. + */ +bool_t EMACGetLinkStatus(void) { + uint32_t ncfgr, bmsr, bmcr, lpa; + + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE; + (void)phy_get(MII_BMSR); + bmsr = phy_get(MII_BMSR); + if (!(bmsr & BMSR_LSTATUS)) { + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE; + return link_up = FALSE; + } + + ncfgr = AT91C_BASE_EMAC->EMAC_NCFGR & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); + bmcr = phy_get(MII_BMCR); + if (bmcr & BMCR_ANENABLE) { + lpa = phy_get(MII_LPA); + if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4)) + ncfgr |= AT91C_EMAC_SPD; + if (lpa & (LPA_10FULL | LPA_100FULL)) + ncfgr |= AT91C_EMAC_FD; + } + else { + if (bmcr & BMCR_SPEED100) + ncfgr |= AT91C_EMAC_SPD; + if (bmcr & BMCR_FULLDPLX) + ncfgr |= AT91C_EMAC_FD; + } + AT91C_BASE_EMAC->EMAC_NCFGR = ncfgr; + AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE; + return link_up = TRUE; +} + +/* + * Allocates and locks a buffer for a transmission operation. + */ +BufDescriptorEntry *EMACGetTransmitBuffer(void) { + BufDescriptorEntry *cptr; + + if (!link_up) + return NULL; + + chSysLock(); + cptr = txptr; + if (!(cptr->w2 & W2_T_USED) || + (cptr->w2 & W2_T_LOCKED)) { + chSysUnlock(); + return FALSE; + } + cptr->w2 |= W2_T_LOCKED; /* Locks the buffer while copying.*/ + if (++txptr >= &tent[EMAC_TRANSMIT_BUFFERS]) + txptr = tent; + chSysUnlock(); + return cptr; +} + +/* + * Transmits a previously allocated buffer and then releases it. + */ +void EMACTransmit(BufDescriptorEntry *cptr, size_t size) { + + chDbgAssert(size <= EMAC_TRANSMIT_BUFFERS_SIZE, + "EMACTransmit(), #1", + "unexpected size"); + + chSysLock(); + if (cptr < &tent[EMAC_TRANSMIT_BUFFERS - 1]) + cptr->w2 = size | W2_T_LAST_BUFFER; + else + cptr->w2 = size | W2_T_LAST_BUFFER | W2_T_WRAP; + AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART; + chSysUnlock(); +} + +/* + * Reads a buffered frame. + * Returns TRUE if a frame was present and read else FALSE. + * @note It is not thread-safe. + */ +bool_t EMACReceive(uint8_t *buf, size_t *sizep) { + unsigned n; + size_t size; + uint8_t *p; + bool_t overflow, found; + +// chSysLock(); +// if (received <= 0) { +// chSysUnlock(); +// return FALSE; +// } +// received--; +// chSysUnlock(); + + n = EMAC_RECEIVE_BUFFERS; + + /* + * Skips unused buffers, if any. + */ +skip: + while (n && !(rxptr->w1 & W1_R_OWNERSHIP)) { + if (++rxptr >= &rent[EMAC_RECEIVE_BUFFERS]) + rxptr = rent; + n--; + } + + /* + * Skips fragments, if any. + */ + while (n && (rxptr->w1 & W1_R_OWNERSHIP) && !(rxptr->w2 & W2_R_FRAME_START)) { + rxptr->w1 &= ~W1_R_OWNERSHIP; + if (++rxptr >= &rent[EMAC_RECEIVE_BUFFERS]) + rxptr = rent; + n--; + } + +restart: + p = buf; + size = 0; + found = overflow = FALSE; + while (n && !found) { + size_t segsize; + + if (!(rxptr->w1 & W1_R_OWNERSHIP)) + goto skip; /* Empty buffer for some reason... */ + + if (size && (rxptr->w2 & W2_R_FRAME_START)) + goto restart; /* Another start buffer for some reason... */ + + if (rxptr->w2 & W2_R_FRAME_END) { + segsize = (rxptr->w2 & W2_T_LENGTH_MASK) - size; + if (((rxptr->w2 & W2_T_LENGTH_MASK) > *sizep) || + (segsize > EMAC_RECEIVE_BUFFERS_SIZE)) + overflow = TRUE; + found = TRUE; + } + else { + segsize = EMAC_RECEIVE_BUFFERS_SIZE; + if (size + segsize > *sizep) + overflow = TRUE; + } + + if (!overflow) { + chDbgAssert(segsize <= 128, "EMACReceive(), #1", ""); + memcpy(p, (void *)(rxptr->w1 & W1_R_ADDRESS_MASK), segsize); + p += segsize; + size += segsize; + } + + rxptr->w1 &= ~W1_R_OWNERSHIP; + if (++rxptr >= &rent[EMAC_RECEIVE_BUFFERS]) + rxptr = rent; + n--; + } + + *sizep = size; + return found && !overflow; +} + +/** @} */ diff --git a/os/io/platforms/AT91SAM7X/sam7x_emac.h b/os/io/platforms/AT91SAM7X/sam7x_emac.h new file mode 100644 index 000000000..de81fe4c6 --- /dev/null +++ b/os/io/platforms/AT91SAM7X/sam7x_emac.h @@ -0,0 +1,93 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 Giovanni Di Sirio. + + This file is part of ChibiOS/RT. + + ChibiOS/RT is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/RT is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file AT91SAM7X/sam7x_emac.h + * @brief AT91SAM7X EMAC driver macros and structures. + * @addtogroup AT91SAM7X_EMAC + * @{ + */ + +#ifndef _SAM7X_EMAC_H_ +#define _SAM7X_EMAC_H_ + +#define PHY_ADDRESS 1 + +#define EMAC_RECEIVE_BUFFERS 24 +#define EMAC_RECEIVE_BUFFERS_SIZE 128 +#define EMAC_TRANSMIT_BUFFERS 2 +#define EMAC_TRANSMIT_BUFFERS_SIZE 1518 + +typedef struct { + uint32_t w1; + uint32_t w2; +} BufDescriptorEntry; + +#define W1_R_OWNERSHIP 0x00000001 +#define W1_R_WRAP 0x00000002 +#define W1_R_ADDRESS_MASK 0xFFFFFFFC + +#define W2_R_LENGTH_MASK 0x00000FFF +#define W2_R_FRAME_START 0x00004000 +#define W2_R_FRAME_END 0x00008000 +#define W2_R_CFI 0x00010000 +#define W2_R_VLAN_PRIO_MASK 0x000E0000 +#define W2_R_PRIO_TAG_DETECTED 0x00100000 +#define W2_R_VLAN_TAG_DETECTED 0x00200000 +#define W2_R_TYPE_ID_MATCH 0x00400000 +#define W2_R_ADDR4_MATCH 0x00800000 +#define W2_R_ADDR3_MATCH 0x01000000 +#define W2_R_ADDR2_MATCH 0x02000000 +#define W2_R_ADDR1_MATCH 0x04000000 +#define W2_R_RFU1 0x08000000 +#define W2_R_ADDR_EXT_MATCH 0x10000000 +#define W2_R_UNICAST_MATCH 0x20000000 +#define W2_R_MULTICAST_MATCH 0x40000000 +#define W2_R_BROADCAST_DETECTED 0x80000000 + +#define W2_T_LENGTH_MASK 0x000007FF +#define W2_T_LOCKED 0x00000800 /* Not an EMAC flag, used by the driver */ +#define W2_T_RFU1 0x00003000 +#define W2_T_LAST_BUFFER 0x00008000 +#define W2_T_NO_CRC 0x00010000 +#define W2_T_RFU2 0x07FE0000 +#define W2_T_BUFFERS_EXHAUSTED 0x08000000 +#define W2_T_TRANSMIT_UNDERRUN 0x10000000 +#define W2_T_RETRY_LIMIT_EXC 0x20000000 +#define W2_T_WRAP 0x40000000 +#define W2_T_USED 0x80000000 + +#ifdef __cplusplus +extern "C" { +#endif + void emac_init(int prio); + void EMACSetAddress(const uint8_t *eaddr); + bool_t EMACGetLinkStatus(void); + BufDescriptorEntry *EMACGetTransmitBuffer(void); + void EMACTransmit(BufDescriptorEntry *cptr, size_t size); + bool_t EMACReceive(uint8_t *buf, size_t *sizep); +#ifdef __cplusplus +} +#endif + +extern EventSource EMACFrameTransmitted, EMACFrameReceived; + +#endif /* _SAM7X_EMAC_H_ */ + +/** @} */ -- cgit v1.2.3