[U-Boot] Xilinx Zynq platform

Hi,
I am sending basic support for new Xilinx Zynq platform. It is new ARM cortex a9 cpu with programmable logic. For more information look at www.xilinx.com/zynq.
In future we are going to extend this platform for i2c, spi, mmc, fpga, nand, nor support, and maybe more. These drivers will be send in the next round when this series is applied to mainline.
Also this platform can also use all drivers targetted to Microblaze (axi) for example emaclite, axi ethernet, uartlite, ns16550a, etc.
As all others Xilinx platforms this is candidate for OF driven configuration.
It is tested on ZC702 platform.
Thanks for your review.
Thanks, Michal

The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu --- drivers/serial/Makefile | 1 + drivers/serial/serial_xpssuart.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_xpssuart.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_xpssuart.c b/drivers/serial/serial_xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_xpssuart.c @@ -0,0 +1,218 @@ +/* + * U-Boot driver for Xilinx Dragonfire UART. + * + * Copyright (C) 2012 Michal Simek monstr@monstr.eu + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h> + +#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */ + +#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */ + +#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */ + +/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */ + +struct xdfuart { + u32 control; /* Control Register [8:0] */ + u32 mode; /* Mode Register [10:0] */ + u32 reserved1[4]; + u32 baud_rate_gen; /* Baud Rate Generator [15:0] */ + u32 reserved2[4]; + u32 channel_sts; /* Channel Status [11:0] */ + u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */ + u32 baud_rate_divider; /* Baud Rate Divider [7:0] */ +}; + +static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0 + [0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR0, +#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1 + [1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR1, +#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR2 + [2] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR2, +#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR3 + [3] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR3, +#endif +}; + +struct xdfuart_params { + u32 baudrate; + u32 clock; +}; + +static struct xdfuart_params xdf_ports_param[4] = { +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE0) && defined(CONFIG_XPSS_SERIAL_CLOCK0) + [0].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE0, + [0].clock = CONFIG_XPSS_SERIAL_CLOCK0, +#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE1) && defined(CONFIG_XPSS_SERIAL_CLOCK1) + [1].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE1, + [1].clock = CONFIG_XPSS_SERIAL_CLOCK1, +#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE2) && defined(CONFIG_XPSS_SERIAL_CLOCK2) + [2].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE2, + [2].clock = CONFIG_XPSS_SERIAL_CLOCK2, +#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE3) && defined(CONFIG_XPSS_SERIAL_CLOCK3) + [3].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE3, + [3].clock = CONFIG_XPSS_SERIAL_CLOCK3, +#endif +}; + +/* Set up the baud rate in gd struct */ +static void xdfuart_serial_setbrg(const int port) +{ + /* Calculation results. */ + unsigned int calc_bauderror, bdiv, bgen; + unsigned long calc_baud = 0; + unsigned long baud = xdf_ports_param[port].baudrate; + unsigned long clock = xdf_ports_param[port].clock; + struct xdfuart *regs = xdf_ports[port]; + + /* master clock + * Baud rate = ------------------ + * bgen * (bdiv + 1) + * + * Find acceptable values for baud generation. + */ + for (bdiv = 4; bdiv < 255; bdiv++) { + bgen = clock / (baud * (bdiv + 1)); + if (bgen < 2 || bgen > 65535) + continue; + + calc_baud = clock / (bgen * (bdiv + 1)); + + /* + * Use first calculated baudrate with + * an acceptable (<3%) error + */ + if (baud > calc_baud) + calc_bauderror = baud - calc_baud; + else + calc_bauderror = calc_baud - baud; + if (((calc_bauderror * 100) / baud) < 3) + break; + } + + writel(bdiv, ®s->baud_rate_divider); + writel(bgen, ®s->baud_rate_gen); +} + +/* Initialize the UART, with...some settings. */ +static int xdfuart_serial_init(const int port) +{ + struct xdfuart *regs = xdf_ports[port]; + + if (!regs) + return -1; + + /* RX/TX enabled & reset */ + writel(XDFUART_CR_TX_EN | XDFUART_CR_RX_EN | XDFUART_CR_TXRST | \ + XDFUART_CR_RXRST, ®s->control); + writel(XDFUART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */ + xdfuart_serial_setbrg(port); + + return 0; +} + +static void xdfuart_serial_putc(const char c, const int port) +{ + struct xdfuart *regs = xdf_ports[port]; + + while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0) + WATCHDOG_RESET(); + + if (c == '\n') { + writel('\r', ®s->tx_rx_fifo); + while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0) + WATCHDOG_RESET(); + } + writel(c, ®s->tx_rx_fifo); +} + +static void xdfuart_serial_puts(const char *s, const int port) +{ + while (*s) + xdfuart_serial_putc(*s++, port); +} + +static int xdfuart_serial_tstc(const int port) +{ + struct xdfuart *regs = xdf_ports[port]; + + return (readl(®s->channel_sts) & XDFUART_SR_RXEMPTY) == 0; +} + +static int xdfuart_serial_getc(const int port) +{ + struct xdfuart *regs = xdf_ports[port]; + + while (!xdfuart_serial_tstc(port)) + WATCHDOG_RESET(); + return readl(®s->tx_rx_fifo); +} + +#if !defined(CONFIG_SERIAL_MULTI) +int serial_init(void) +{ + return xdfuart_serial_init(0); +} + +void serial_setbrg(void) +{ + xdfuart_serial_setbrg(0); +} + +void serial_putc(const char c) +{ + xdfuart_serial_putc(c, 0); +} + +void serial_puts(const char *s) +{ + xdfuart_serial_puts(s, 0); +} + +int serial_getc(void) +{ + return xdfuart_serial_getc(0); +} + +int serial_tstc(void) +{ + return xdfuart_serial_tstc(0); +} +#endif

Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu
drivers/serial/Makefile | 1 + drivers/serial/serial_xpssuart.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_xpssuart.c
I think this would be clearer if it was named serial_zynq.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
Replace every reference to "XPSS" with "ZYNQ".
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_xpssuart.c b/drivers/serial/serial_xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_xpssuart.c @@ -0,0 +1,218 @@ +/*
- U-Boot driver for Xilinx Dragonfire UART.
Use the released name "Zynq" not the old codename "Dragonfire".
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h>
+#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
Replace all references to "XDFUART" with "ZYNQ_UART".
+#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */
+#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */
+struct xdfuart {
u32 control; /* Control Register [8:0] */
u32 mode; /* Mode Register [10:0] */
u32 reserved1[4];
u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
u32 reserved2[4];
u32 channel_sts; /* Channel Status [11:0] */
u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
+};
+static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0
[0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR0,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1
[1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR1,
+#endif
There are 2 UARTS in hard silicon. They should be supported with their known base addresses (0xE0000000 and 0xE0001000) here without pushing that into the config header.
+#ifdef CONFIG_XPSS_SERIAL_BASEADDR2
[2] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR2,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR3
[3] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR3,
+#endif +};
+struct xdfuart_params {
u32 baudrate;
u32 clock;
+};
+static struct xdfuart_params xdf_ports_param[4] = { +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE0) && defined(CONFIG_XPSS_SERIAL_CLOCK0)
[0].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE0,
[0].clock = CONFIG_XPSS_SERIAL_CLOCK0,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE1) && defined(CONFIG_XPSS_SERIAL_CLOCK1)
[1].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE1,
[1].clock = CONFIG_XPSS_SERIAL_CLOCK1,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE2) && defined(CONFIG_XPSS_SERIAL_CLOCK2)
[2].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE2,
[2].clock = CONFIG_XPSS_SERIAL_CLOCK2,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE3) && defined(CONFIG_XPSS_SERIAL_CLOCK3)
[3].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE3,
[3].clock = CONFIG_XPSS_SERIAL_CLOCK3,
+#endif +};
+/* Set up the baud rate in gd struct */ +static void xdfuart_serial_setbrg(const int port) +{
/* Calculation results. */
unsigned int calc_bauderror, bdiv, bgen;
unsigned long calc_baud = 0;
unsigned long baud = xdf_ports_param[port].baudrate;
unsigned long clock = xdf_ports_param[port].clock;
struct xdfuart *regs = xdf_ports[port];
/* master clock
* Baud rate = ------------------
* bgen * (bdiv + 1)
*
* Find acceptable values for baud generation.
*/
for (bdiv = 4; bdiv < 255; bdiv++) {
bgen = clock / (baud * (bdiv + 1));
if (bgen < 2 || bgen > 65535)
continue;
calc_baud = clock / (bgen * (bdiv + 1));
/*
* Use first calculated baudrate with
* an acceptable (<3%) error
*/
if (baud > calc_baud)
calc_bauderror = baud - calc_baud;
else
calc_bauderror = calc_baud - baud;
if (((calc_bauderror * 100) / baud) < 3)
break;
}
writel(bdiv, ®s->baud_rate_divider);
writel(bgen, ®s->baud_rate_gen);
+}
+/* Initialize the UART, with...some settings. */ +static int xdfuart_serial_init(const int port) +{
struct xdfuart *regs = xdf_ports[port];
if (!regs)
return -1;
/* RX/TX enabled & reset */
writel(XDFUART_CR_TX_EN | XDFUART_CR_RX_EN | XDFUART_CR_TXRST | \
XDFUART_CR_RXRST, ®s->control);
writel(XDFUART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */
xdfuart_serial_setbrg(port);
return 0;
+}
+static void xdfuart_serial_putc(const char c, const int port) +{
struct xdfuart *regs = xdf_ports[port];
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0)
WATCHDOG_RESET();
if (c == '\n') {
writel('\r', ®s->tx_rx_fifo);
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0)
WATCHDOG_RESET();
}
writel(c, ®s->tx_rx_fifo);
+}
+static void xdfuart_serial_puts(const char *s, const int port) +{
while (*s)
xdfuart_serial_putc(*s++, port);
+}
+static int xdfuart_serial_tstc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
return (readl(®s->channel_sts) & XDFUART_SR_RXEMPTY) == 0;
+}
+static int xdfuart_serial_getc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
while (!xdfuart_serial_tstc(port))
WATCHDOG_RESET();
return readl(®s->tx_rx_fifo);
+}
+#if !defined(CONFIG_SERIAL_MULTI) +int serial_init(void) +{
return xdfuart_serial_init(0);
If there is no SERIAL_MULTI support, there should be a config to select which one to use.
+}
+void serial_setbrg(void) +{
xdfuart_serial_setbrg(0);
+}
+void serial_putc(const char c) +{
xdfuart_serial_putc(c, 0);
+}
+void serial_puts(const char *s) +{
xdfuart_serial_puts(s, 0);
+}
+int serial_getc(void) +{
return xdfuart_serial_getc(0);
+}
+int serial_tstc(void) +{
return xdfuart_serial_tstc(0);
+}
+#endif
1.7.0.4
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On 08/14/2012 04:09 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu
drivers/serial/Makefile | 1 + drivers/serial/serial_xpssuart.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_xpssuart.c
I think this would be clearer if it was named serial_zynq.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
Replace every reference to "XPSS" with "ZYNQ".
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_xpssuart.c b/drivers/serial/serial_xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_xpssuart.c @@ -0,0 +1,218 @@ +/*
- U-Boot driver for Xilinx Dragonfire UART.
Use the released name "Zynq" not the old codename "Dragonfire".
ok.
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h>
+#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
Replace all references to "XDFUART" with "ZYNQ_UART".
agree.
+#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */
+#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */
+struct xdfuart {
u32 control; /* Control Register [8:0] */
u32 mode; /* Mode Register [10:0] */
u32 reserved1[4];
u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
u32 reserved2[4];
u32 channel_sts; /* Channel Status [11:0] */
u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
+};
+static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0
[0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR0,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1
[1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR1,
+#endif
There are 2 UARTS in hard silicon.
My fault.
They should be supported with
their known base addresses (0xE0000000 and 0xE0001000) here without pushing that into the config header.
I am not sure that hardcoding addresses here is the right thing to do. The main reason is that none has tested option that hard IPs can be also used from programmable logic. It means that this driver could be possible to use from Microblaze with address translation.
No problem to setup these addresses in config file.
+#ifdef CONFIG_XPSS_SERIAL_BASEADDR2
[2] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR2,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR3
[3] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR3,
+#endif +};
+struct xdfuart_params {
u32 baudrate;
u32 clock;
+};
+static struct xdfuart_params xdf_ports_param[4] = { +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE0) && defined(CONFIG_XPSS_SERIAL_CLOCK0)
[0].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE0,
[0].clock = CONFIG_XPSS_SERIAL_CLOCK0,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE1) && defined(CONFIG_XPSS_SERIAL_CLOCK1)
[1].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE1,
[1].clock = CONFIG_XPSS_SERIAL_CLOCK1,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE2) && defined(CONFIG_XPSS_SERIAL_CLOCK2)
[2].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE2,
[2].clock = CONFIG_XPSS_SERIAL_CLOCK2,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE3) && defined(CONFIG_XPSS_SERIAL_CLOCK3)
[3].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE3,
[3].clock = CONFIG_XPSS_SERIAL_CLOCK3,
+#endif +};
+/* Set up the baud rate in gd struct */ +static void xdfuart_serial_setbrg(const int port) +{
/* Calculation results. */
unsigned int calc_bauderror, bdiv, bgen;
unsigned long calc_baud = 0;
unsigned long baud = xdf_ports_param[port].baudrate;
unsigned long clock = xdf_ports_param[port].clock;
struct xdfuart *regs = xdf_ports[port];
/* master clock
* Baud rate = ------------------
* bgen * (bdiv + 1)
*
* Find acceptable values for baud generation.
*/
for (bdiv = 4; bdiv < 255; bdiv++) {
bgen = clock / (baud * (bdiv + 1));
if (bgen < 2 || bgen > 65535)
continue;
calc_baud = clock / (bgen * (bdiv + 1));
/*
* Use first calculated baudrate with
* an acceptable (<3%) error
*/
if (baud > calc_baud)
calc_bauderror = baud - calc_baud;
else
calc_bauderror = calc_baud - baud;
if (((calc_bauderror * 100) / baud) < 3)
break;
}
writel(bdiv, ®s->baud_rate_divider);
writel(bgen, ®s->baud_rate_gen);
+}
+/* Initialize the UART, with...some settings. */ +static int xdfuart_serial_init(const int port) +{
struct xdfuart *regs = xdf_ports[port];
if (!regs)
return -1;
/* RX/TX enabled & reset */
writel(XDFUART_CR_TX_EN | XDFUART_CR_RX_EN | XDFUART_CR_TXRST | \
XDFUART_CR_RXRST, ®s->control);
writel(XDFUART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity */
xdfuart_serial_setbrg(port);
return 0;
+}
+static void xdfuart_serial_putc(const char c, const int port) +{
struct xdfuart *regs = xdf_ports[port];
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0)
WATCHDOG_RESET();
if (c == '\n') {
writel('\r', ®s->tx_rx_fifo);
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0)
WATCHDOG_RESET();
}
writel(c, ®s->tx_rx_fifo);
+}
+static void xdfuart_serial_puts(const char *s, const int port) +{
while (*s)
xdfuart_serial_putc(*s++, port);
+}
+static int xdfuart_serial_tstc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
return (readl(®s->channel_sts) & XDFUART_SR_RXEMPTY) == 0;
+}
+static int xdfuart_serial_getc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
while (!xdfuart_serial_tstc(port))
WATCHDOG_RESET();
return readl(®s->tx_rx_fifo);
+}
+#if !defined(CONFIG_SERIAL_MULTI) +int serial_init(void) +{
return xdfuart_serial_init(0);
If there is no SERIAL_MULTI support, there should be a config to select which one to use.
Not right now but I will add it in the next patch. But yes, this ifdef shouldn't be here.
Thanks, Michal

Hi Michal,
On Tue, Aug 14, 2012 at 11:38 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 04:09 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu
drivers/serial/Makefile | 1 + drivers/serial/serial_xpssuart.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_xpssuart.c
I think this would be clearer if it was named serial_zynq.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
Replace every reference to "XPSS" with "ZYNQ".
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_xpssuart.c b/drivers/serial/serial_xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_xpssuart.c @@ -0,0 +1,218 @@ +/*
- U-Boot driver for Xilinx Dragonfire UART.
Use the released name "Zynq" not the old codename "Dragonfire".
ok.
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h>
+#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
Replace all references to "XDFUART" with "ZYNQ_UART".
agree.
+#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */
+#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */
+struct xdfuart {
u32 control; /* Control Register [8:0] */
u32 mode; /* Mode Register [10:0] */
u32 reserved1[4];
u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
u32 reserved2[4];
u32 channel_sts; /* Channel Status [11:0] */
u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
+};
+static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0
[0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR0,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1
[1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR1,
+#endif
There are 2 UARTS in hard silicon.
My fault.
They should be supported with
their known base addresses (0xE0000000 and 0xE0001000) here without pushing that into the config header.
I am not sure that hardcoding addresses here is the right thing to do. The main reason is that none has tested option that hard IPs can be also used from programmable logic. It means that this driver could be possible to use from Microblaze with address translation.
No problem to setup these addresses in config file.
I accept that you can access these from microblaze, but I think that will be the 1% use-case. You can make the base address overridable, but use the ARM core address space by default and have them here instead of copied to every config.
+#ifdef CONFIG_XPSS_SERIAL_BASEADDR2
[2] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR2,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR3
[3] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR3,
+#endif +};
+struct xdfuart_params {
u32 baudrate;
u32 clock;
+};
+static struct xdfuart_params xdf_ports_param[4] = { +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE0) && defined(CONFIG_XPSS_SERIAL_CLOCK0)
[0].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE0,
[0].clock = CONFIG_XPSS_SERIAL_CLOCK0,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE1) && defined(CONFIG_XPSS_SERIAL_CLOCK1)
[1].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE1,
[1].clock = CONFIG_XPSS_SERIAL_CLOCK1,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE2) && defined(CONFIG_XPSS_SERIAL_CLOCK2)
[2].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE2,
[2].clock = CONFIG_XPSS_SERIAL_CLOCK2,
+#endif +#if defined(CONFIG_XPSS_SERIAL_BAUDRATE3) && defined(CONFIG_XPSS_SERIAL_CLOCK3)
[3].baudrate = CONFIG_XPSS_SERIAL_BAUDRATE3,
[3].clock = CONFIG_XPSS_SERIAL_CLOCK3,
+#endif +};
+/* Set up the baud rate in gd struct */ +static void xdfuart_serial_setbrg(const int port) +{
/* Calculation results. */
unsigned int calc_bauderror, bdiv, bgen;
unsigned long calc_baud = 0;
unsigned long baud = xdf_ports_param[port].baudrate;
unsigned long clock = xdf_ports_param[port].clock;
struct xdfuart *regs = xdf_ports[port];
/* master clock
* Baud rate = ------------------
* bgen * (bdiv + 1)
*
* Find acceptable values for baud generation.
*/
for (bdiv = 4; bdiv < 255; bdiv++) {
bgen = clock / (baud * (bdiv + 1));
if (bgen < 2 || bgen > 65535)
continue;
calc_baud = clock / (bgen * (bdiv + 1));
/*
* Use first calculated baudrate with
* an acceptable (<3%) error
*/
if (baud > calc_baud)
calc_bauderror = baud - calc_baud;
else
calc_bauderror = calc_baud - baud;
if (((calc_bauderror * 100) / baud) < 3)
break;
}
writel(bdiv, ®s->baud_rate_divider);
writel(bgen, ®s->baud_rate_gen);
+}
+/* Initialize the UART, with...some settings. */ +static int xdfuart_serial_init(const int port) +{
struct xdfuart *regs = xdf_ports[port];
if (!regs)
return -1;
/* RX/TX enabled & reset */
writel(XDFUART_CR_TX_EN | XDFUART_CR_RX_EN | XDFUART_CR_TXRST | \
XDFUART_CR_RXRST,
®s->control);
writel(XDFUART_MR_PARITY_NONE, ®s->mode); /* 8 bit, no parity
*/
xdfuart_serial_setbrg(port);
return 0;
+}
+static void xdfuart_serial_putc(const char c, const int port) +{
struct xdfuart *regs = xdf_ports[port];
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) != 0)
WATCHDOG_RESET();
if (c == '\n') {
writel('\r', ®s->tx_rx_fifo);
while ((readl(®s->channel_sts) & XDFUART_SR_TXFULL) !=
WATCHDOG_RESET();
}
writel(c, ®s->tx_rx_fifo);
+}
+static void xdfuart_serial_puts(const char *s, const int port) +{
while (*s)
xdfuart_serial_putc(*s++, port);
+}
+static int xdfuart_serial_tstc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
return (readl(®s->channel_sts) & XDFUART_SR_RXEMPTY) == 0;
+}
+static int xdfuart_serial_getc(const int port) +{
struct xdfuart *regs = xdf_ports[port];
while (!xdfuart_serial_tstc(port))
WATCHDOG_RESET();
return readl(®s->tx_rx_fifo);
+}
+#if !defined(CONFIG_SERIAL_MULTI) +int serial_init(void) +{
return xdfuart_serial_init(0);
If there is no SERIAL_MULTI support, there should be a config to select which one to use.
Not right now but I will add it in the next patch. But yes, this ifdef shouldn't be here.
Thanks, Michal
-- Michal Simek, Ing. (M.Eng) w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/ Microblaze U-BOOT custodian

On 08/14/2012 06:45 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:38 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 04:09 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu
drivers/serial/Makefile | 1 + drivers/serial/serial_xpssuart.c | 218 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_xpssuart.c
I think this would be clearer if it was named serial_zynq.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
Replace every reference to "XPSS" with "ZYNQ".
ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_xpssuart.c b/drivers/serial/serial_xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_xpssuart.c @@ -0,0 +1,218 @@ +/*
- U-Boot driver for Xilinx Dragonfire UART.
Use the released name "Zynq" not the old codename "Dragonfire".
ok.
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h>
+#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
Replace all references to "XDFUART" with "ZYNQ_UART".
agree.
+#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */
+#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */
+struct xdfuart {
u32 control; /* Control Register [8:0] */
u32 mode; /* Mode Register [10:0] */
u32 reserved1[4];
u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
u32 reserved2[4];
u32 channel_sts; /* Channel Status [11:0] */
u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
+};
+static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0
[0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR0,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1
[1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_BASEADDR1,
+#endif
There are 2 UARTS in hard silicon.
My fault.
They should be supported with
their known base addresses (0xE0000000 and 0xE0001000) here without pushing that into the config header.
I am not sure that hardcoding addresses here is the right thing to do. The main reason is that none has tested option that hard IPs can be also used from programmable logic. It means that this driver could be possible to use from Microblaze with address translation.
No problem to setup these addresses in config file.
I accept that you can access these from microblaze, but I think that will be the 1% use-case. You can make the base address overridable, but use the ARM core address space by default and have them here instead of copied to every config.
None has shown any table with use cases that why you can't say it will be 1% use-cases.
+ I don't think that hardcode IP address in generic driver is right way to go. For example we are using SERIAL0 as console and in EDK you can setup which IP it is. It means that sometimes SERIAL0 is at 0xe0001000 and sometimes at 0xe0000000. This setting is also done for Linux that on serial0 is also Linux console which simplify configuration.
Thanks, Michal

On Wed, Aug 15, 2012 at 3:17 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 06:45 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:38 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 04:09 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
The driver is used on Xilinx Zynq platform.
Signed-off-by: Michal Simek monstr@monstr.eu
drivers/serial/Makefile | 1 + drivers/serial/serial_**xpssuart.c | 218 ++++++++++++++++++++++++++++++**++++++++ 2 files changed, 219 insertions(+), 0 deletions(-) create mode 100644 drivers/serial/serial_**xpssuart.c
I think this would be clearer if it was named serial_zynq.c
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 65d0f23..81350d0 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -56,6 +56,7 @@ COBJS-$(CONFIG_S3C44B0_SERIAL) += serial_s3c44b0.o COBJS-$(CONFIG_XILINX_**UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o +COBJS-$(CONFIG_XPSS_SERIAL) += serial_xpssuart.o
Replace every reference to "XPSS" with "ZYNQ".
ifndef CONFIG_SPL_BUILD
COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial_**xpssuart.c b/drivers/serial/serial_**xpssuart.c new file mode 100644 index 0000000..3c6d838 --- /dev/null +++ b/drivers/serial/serial_**xpssuart.c @@ -0,0 +1,218 @@ +/*
- U-Boot driver for Xilinx Dragonfire UART.
Use the released name "Zynq" not the old codename "Dragonfire".
ok.
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <watchdog.h> +#include <asm/io.h> +#include <serial.h>
+#define XDFUART_SR_TXFULL 0x00000010 /* TX FIFO full */ +#define XDFUART_SR_RXEMPTY 0x00000002 /* RX FIFO empty */
Replace all references to "XDFUART" with "ZYNQ_UART".
agree.
+#define XDFUART_CR_TX_EN 0x00000010 /* TX enabled */ +#define XDFUART_CR_RX_EN 0x00000004 /* RX enabled */ +#define XDFUART_CR_TXRST 0x00000002 /* TX logic reset */ +#define XDFUART_CR_RXRST 0x00000001 /* RX logic reset */
+#define XDFUART_MR_PARITY_NONE 0x00000020 /* No parity mode */
+/* Some clock/baud constants */ +#define XDFUART_BDIV 15 /* Default/reset BDIV value */ +#define XDFUART_BASECLK 3125000L /* master / (bdiv + 1) */
+struct xdfuart {
u32 control; /* Control Register [8:0] */
u32 mode; /* Mode Register [10:0] */
u32 reserved1[4];
u32 baud_rate_gen; /* Baud Rate Generator [15:0] */
u32 reserved2[4];
u32 channel_sts; /* Channel Status [11:0] */
u32 tx_rx_fifo; /* FIFO [15:0] or [7:0] */
u32 baud_rate_divider; /* Baud Rate Divider [7:0] */
+};
+static struct xdfuart *xdf_ports[4] = { +#ifdef CONFIG_XPSS_SERIAL_BASEADDR0
[0] = (struct xdfuart *)CONFIG_XPSS_SERIAL_**BASEADDR0,
+#endif +#ifdef CONFIG_XPSS_SERIAL_BASEADDR1
[1] = (struct xdfuart *)CONFIG_XPSS_SERIAL_**BASEADDR1,
+#endif
There are 2 UARTS in hard silicon.
My fault.
They should be supported with
their known base addresses (0xE0000000 and 0xE0001000) here without pushing that into the config header.
I am not sure that hardcoding addresses here is the right thing to do. The main reason is that none has tested option that hard IPs can be also used from programmable logic. It means that this driver could be possible to use from Microblaze with address translation.
No problem to setup these addresses in config file.
I accept that you can access these from microblaze, but I think that will be the 1% use-case. You can make the base address overridable, but use the ARM core address space by default and have them here instead of copied to every config.
None has shown any table with use cases that why you can't say it will be 1% use-cases.
- I don't think that hardcode IP address in generic driver is right way to
go. For example we are using SERIAL0 as console and in EDK you can setup which IP it is. It means that sometimes SERIAL0 is at 0xe0001000 and sometimes at 0xe0000000. This setting is also done for Linux that on serial0 is also Linux console which simplify configuration.
I agree - coding base addresses in drivers, rather than machines/platforms, sounds like a bad idea.
There is a somewhat open question in both Linux and u-boot about what to do if the MIOs enable PS7_UART1 but not PS7_UART0. Should UART1 enumerate as u-boot's first serial device? In Linux as /dev/ttyS0?
Let's keep addresses and decisions like this in platform files, not device drivers.
John

Device driver for Zynq Gem IP.
Signed-off-by: Michal Simek monstr@monstr.eu CC: Joe Hershberger joe.hershberger@gmail.com --- drivers/net/Makefile | 1 + drivers/net/xilinx_gem.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++ include/netdev.h | 1 + 3 files changed, 516 insertions(+), 0 deletions(-) create mode 100644 drivers/net/xilinx_gem.c
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 430f90c..e510ffa 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,6 +77,7 @@ COBJS-$(CONFIG_ULI526X) += uli526x.o COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o COBJS-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +COBJS-$(CONFIG_XILINX_GEM) += xilinx_gem.o COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
diff --git a/drivers/net/xilinx_gem.c b/drivers/net/xilinx_gem.c new file mode 100644 index 0000000..7c8c14d --- /dev/null +++ b/drivers/net/xilinx_gem.c @@ -0,0 +1,514 @@ +/* + * (C) Copyright 2011 Michal Simek + * + * Michal SIMEK monstr@monstr.eu + * + * Based on Xilinx gmac driver: + * (C) Copyright 2011 Xilinx + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <net.h> +#include <config.h> +#include <malloc.h> +#include <asm/io.h> +#include <phy.h> +#include <miiphy.h> + +#if !defined(CONFIG_PHYLIB) +# error XILINX_GEM_ETHERNET requires PHYLIB +#endif + +/* Bit/mask specification */ +#define XEMACPSS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ +#define XEMACPSS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ +#define XEMACPSS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ +#define XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ +#define XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */ + +#define XEMACPSS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ +#define XEMACPSS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ +#define XEMACPSS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */ + +#define XEMACPSS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ +#define XEMACPSS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ +#define XEMACPSS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */ + +/* Wrap bit, last descriptor */ +#define XEMACPSS_TXBUF_WRAP_MASK 0x40000000 +#define XEMACPSS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */ + +#define XEMACPSS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */ +#define XEMACPSS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */ +/* Transmit buffs exhausted mid frame */ +#define XEMACPSS_TXSR_BUFEXH_MASK 0x00000010 + +#define XEMACPSS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ +#define XEMACPSS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ +#define XEMACPSS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ +#define XEMACPSS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */ + +#define XEMACPSS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */ + +/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1 + +/* Mask used to verify certain PHY features (or register contents) + * in the register above: + * 0x1000: 10Mbps full duplex support + * 0x0800: 10Mbps half duplex support + * 0x0008: Auto-negotiation support + */ +#define PHY_DETECT_MASK 0x1808 + +/* Device registers */ +struct gem_regs { + u32 nwctrl; /* Network Control reg */ + u32 nwcfg; /* Network Config reg */ + u32 nwsr; /* Network Status reg */ + u32 reserved1; + u32 dmacr; /* DMA Control reg */ + u32 txsr; /* TX Status reg */ + u32 rxqbase; /* RX Q Base address reg */ + u32 txqbase; /* TX Q Base address reg */ + u32 rxsr; /* RX Status reg */ + u32 reserved2[2]; + u32 idr; /* Interrupt Disable reg */ + u32 reserved3; + u32 phymntnc; /* Phy Maintaince reg */ + u32 reserved4[18]; + u32 hashl; /* Hash Low address reg */ + u32 hashh; /* Hash High address reg */ +#define LADDR_LOW 0 +#define LADDR_HIGH 1 + u32 laddr[4][LADDR_HIGH + 1]; /* Specific1 addr low/high reg */ + u32 match[4]; /* Type ID1 Match reg */ + u32 reserved6[18]; + u32 stat[44]; /* Octects transmitted Low reg - stat start */ +}; + +/* BD descriptors */ +struct emac_bd { + u32 addr; /* Next descriptor pointer */ + u32 status; +}; + +#define RX_BUF 3 + +/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct gemac_priv { + struct emac_bd tx_bd; + struct emac_bd rx_bd[RX_BUF]; + u32 initialized; + char rxbuffers[RX_BUF * PKTSIZE_ALIGN]; + u32 rxbd_current; + u32 rx_first_buf; + int phyaddr; + struct phy_device *phydev; + struct mii_dev *bus; +}; + +static inline int mdio_wait(struct eth_device *dev) +{ + struct gem_regs *regs = (struct gem_regs *)dev->iobase; + u32 timeout = 200; + + /* Wait till MDIO interface is ready to accept a new transaction. */ + while (timeout && !(readl(®s->nwsr) & XEMACPSS_NWSR_MDIOIDLE_MASK)) { + timeout--; + udelay(1); + } + + if (!timeout) { + printf("%s: Timeout\n", __func__); + return 1; + } + + return 0; +} + +static u32 phy_setup_op(struct eth_device *dev, u32 phy_addr, u32 regnum, + u32 op, u16 *data) +{ + u32 mgtcr; + struct gem_regs *regs = (struct gem_regs *)dev->iobase; + + if (mdio_wait(dev)) + return 1; + + /* Construct mgtcr mask for the operation */ + mgtcr = XEMACPSS_PHYMNTNC_OP_MASK | op | + (phy_addr << XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK) | + (regnum << XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK) | *data; + + /* Write mgtcr and wait for completion */ + writel(mgtcr, ®s->phymntnc); + + if (mdio_wait(dev)) + return 1; + + if (op == XEMACPSS_PHYMNTNC_OP_R_MASK) + *data = readl(®s->phymntnc); + + return 0; +} + +static u32 phyread(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 *val) +{ + return phy_setup_op(dev, phy_addr, regnum, + XEMACPSS_PHYMNTNC_OP_R_MASK, val); +} + +static u32 phywrite(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 data) +{ + return phy_setup_op(dev, phy_addr, regnum, + XEMACPSS_PHYMNTNC_OP_W_MASK, &data); +} + +static void phy_detection(struct eth_device *dev) +{ + int i, ret; + u16 phyreg; + struct gemac_priv *priv = dev->priv; + + if (priv->phyaddr != -1) { + ret = phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg); + if (!ret && (phyreg != 0xFFFF) && + ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + debug("Default phy address %d is valid\n", + priv->phyaddr); + return; + } else { + debug("PHY address is not setup correctly %d\n", + priv->phyaddr); + priv->phyaddr = -1; + } + } + + debug("detecting phy address\n"); + if (priv->phyaddr == -1) { + /* detect the PHY address */ + for (i = 31; i >= 0; i--) { + ret = phyread(dev, i, PHY_DETECT_REG, &phyreg); + if (!ret && (phyreg != 0xFFFF) && + ((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) { + /* Found a valid PHY address */ + priv->phyaddr = i; + debug("Found valid phy address, %d\n", i); + return; + } + } + } + printf("PHY is not detected\n"); +} + +/* Address bits[47:32] bit[31:0] are in BOTTOM */ +#define XEMACPSS_LADDR_MACH_MASK 0x0000FFFF + +static void setmacaddress(struct eth_device *dev, void *addressptr, u8 index) +{ + u32 macaddr; + struct gem_regs *regs = (struct gem_regs *)dev->iobase; + u8 *aptr = (u8 *) addressptr; + + /* Set the MAC bits [31:0] in BOT */ + macaddr = aptr[0]; + macaddr |= aptr[1] << 8; + macaddr |= aptr[2] << 16; + macaddr |= aptr[3] << 24; + writel(macaddr, ®s->laddr[index][LADDR_LOW]); + + /* There are reserved bits in TOP so don't affect them */ + macaddr = readl(®s->laddr[index][LADDR_HIGH]); + + macaddr &= ~XEMACPSS_LADDR_MACH_MASK; + + /* Set MAC bits [47:32] in TOP */ + macaddr |= aptr[4]; + macaddr |= aptr[5] << 8; + + writel(macaddr, ®s->laddr[index][LADDR_HIGH]); + + /* Set the ID bits in MATCHx register - settypeidcheck */ + writel(0, ®s->match[index]); +} + +static int gem_init(struct eth_device *dev, bd_t * bis) +{ + int tmp; + int i; + struct gem_regs *regs = (struct gem_regs *)dev->iobase; + char emacpss_zero_mac[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }; + struct gemac_priv *priv = dev->priv; + struct phy_device *phydev; + u32 supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full; + + if (priv->initialized) + return 0; + + /* Disable all interrupts */ + writel(0xFFFFFFFF, ®s->idr); + + /* Disable the receiver & transmitter */ + writel(0, ®s->nwctrl); + writel(0, ®s->txsr); + writel(0, ®s->rxsr); + writel(0, ®s->phymntnc); + + /* Clear the Hash registers for the mac address pointed by AddressPtr */ + writel(0x0, ®s->hashl); + /* Write bits [63:32] in TOP */ + writel(0x0, ®s->hashh); + + for (i = 0; i < 4; i++) + setmacaddress(dev, emacpss_zero_mac, i); + + /* Clear all counters */ + for (i = 0; i <= (sizeof(struct gem_regs) - + offsetof(struct gem_regs, stat)) / 4; i++) + readl(®s->stat[i]); + + /* Setup RxBD space */ + memset(&(priv->rx_bd), 0, sizeof(priv->rx_bd)); + /* Create the RxBD ring */ + memset(&(priv->rxbuffers), 0, sizeof(priv->rxbuffers)); + + for (i = 0; i < RX_BUF; i++) { + priv->rx_bd[i].status = 0xF0000000; + priv->rx_bd[i].addr = (u32)((char *) &(priv->rxbuffers) + + (i * PKTSIZE_ALIGN)); + } + /* WRAP bit to last BD */ + priv->rx_bd[--i].addr |= XEMACPSS_RXBUF_WRAP_MASK; + /* Write RxBDs to IP */ + writel((u32) &(priv->rx_bd), ®s->rxqbase); + + + /* MAC Setup */ + /* + * Following is the setup for Network Configuration register. + * Bit 0: Set for 100 Mbps operation. + * Bit 1: Set for Full Duplex mode. + * Bit 4: Set to allow Copy all frames. + * Bit 17: Set for FCS removal. + * Bits 20-18: Set with value binary 010 to divide pclk by 32 + * (pclk up to 80 MHz) + */ + writel(0x000A0013, ®s->nwcfg); + + /* + * Following is the setup for DMA Configuration register. + * Bits 4-0: To set AHB fixed burst length for DMA data operations -> + * Set with binary 00100 to use INCR4 AHB bursts. + * Bits 9-8: Receiver packet buffer memory size -> + * Set with binary 11 to Use full configured addressable space (8 Kb) + * Bit 10 : Transmitter packet buffer memory size -> + * Set with binary 1 to Use full configured addressable space (4 Kb) + * Bits 23-16 : DMA receive buffer size in AHB system memory -> + * Set with binary 00011000 to use 1536 byte(1*max length frame/buffer) + */ + writel(0x00180704, ®s->dmacr); + + /* + * Following is the setup for Network Control register. + * Bit 2: Set to enable Receive operation. + * Bit 3: Set to enable Transmitt operation. + * Bit 4: Set to enable MDIO operation. + */ + tmp = readl(®s->nwctrl); + /* MDIO, Rx and Tx enable */ + tmp |= XEMACPSS_NWCTRL_MDEN_MASK | XEMACPSS_NWCTRL_RXEN_MASK | + XEMACPSS_NWCTRL_TXEN_MASK; + writel(tmp, ®s->nwctrl); + + phy_detection(dev); + + /* interface - look at tsec */ + phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0); + + phydev->supported &= supported; + phydev->advertising = phydev->supported; + priv->phydev = phydev; + phy_config(phydev); + phy_startup(phydev); + + priv->initialized = 1; + return 0; +} + +static int gem_send(struct eth_device *dev, void *ptr, int len) +{ + int status; + u32 val; + struct gemac_priv *priv = dev->priv; + struct gem_regs *regs = (struct gem_regs *)dev->iobase; + + if (!priv->initialized) { + puts("Error GMAC not initialized"); + return -1; + } + + /* setup BD */ + writel((u32)&(priv->tx_bd), ®s->txqbase); + + /* Setup Tx BD */ + memset((void *) &(priv->tx_bd), 0, sizeof(struct emac_bd)); + + priv->tx_bd.addr = (u32)ptr; + priv->tx_bd.status = len | XEMACPSS_TXBUF_LAST_MASK | + XEMACPSS_TXBUF_WRAP_MASK; + + /* Start transmit */ + val = readl(®s->nwctrl) | XEMACPSS_NWCTRL_STARTTX_MASK; + writel(val, ®s->nwctrl); + + /* Read the stat register to know if the packet has been transmitted */ + status = readl(®s->txsr); + if (status & (XEMACPSS_TXSR_HRESPNOK_MASK | XEMACPSS_TXSR_URUN_MASK | + XEMACPSS_TXSR_BUFEXH_MASK)) { + printf("Something has gone wrong here!? Status is 0x%x.\n", + status); + } + + /* Clear Tx status register before leaving . */ + writel(status, ®s->txsr); + return 0; +} + +/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ +static int gem_recv(struct eth_device *dev) +{ + int frame_len; + struct gemac_priv *priv = dev->priv; + + if (!(priv->rx_bd[priv->rxbd_current].addr & XEMACPSS_RXBUF_NEW_MASK)) + return 0; + + if (!(priv->rx_bd[priv->rxbd_current].status & + (XEMACPSS_RXBUF_SOF_MASK | XEMACPSS_RXBUF_EOF_MASK))) { + printf("GEM: SOF or EOF not set for last buffer received!\n"); + return 0; + } + + frame_len = priv->rx_bd[priv->rxbd_current].status & + XEMACPSS_RXBUF_LEN_MASK; + if (frame_len) { + NetReceive((u8 *) (priv->rx_bd[priv->rxbd_current].addr & + XEMACPSS_RXBUF_ADD_MASK), frame_len); + + if (priv->rx_bd[priv->rxbd_current].status & + XEMACPSS_RXBUF_SOF_MASK) + priv->rx_first_buf = priv->rxbd_current; + else { + priv->rx_bd[priv->rxbd_current].addr &= + ~XEMACPSS_RXBUF_NEW_MASK; + priv->rx_bd[priv->rxbd_current].status = 0xF0000000; + } + + if (priv->rx_bd[priv->rxbd_current].status & + XEMACPSS_RXBUF_EOF_MASK) { + priv->rx_bd[priv->rx_first_buf].addr &= + ~XEMACPSS_RXBUF_NEW_MASK; + priv->rx_bd[priv->rx_first_buf].status = 0xF0000000; + } + + if ((++priv->rxbd_current) >= RX_BUF) + priv->rxbd_current = 0; + + return frame_len; + } + + return 0; +} + +static void gem_halt(struct eth_device *dev) +{ + return; +} + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) +static int gem_miiphyread(const char *devname, uchar addr, + uchar reg, ushort *val) +{ + struct eth_device *dev = eth_get_dev(); + int ret; + + ret = phyread(dev, addr, reg, val); + debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, *val); + return ret; +} + +static int gem_miiphy_write(const char *devname, uchar addr, + uchar reg, ushort val) +{ + struct eth_device *dev = eth_get_dev(); + + debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, val); + return phywrite(dev, addr, reg, val); +} +#endif + +int xilinx_gem_initialize(bd_t *bis, int base_addr) +{ + struct eth_device *dev; + struct gemac_priv *priv; + + dev = calloc(1, sizeof(*dev)); + if (dev == NULL) + return -1; + + dev->priv = calloc(1, sizeof(struct gemac_priv)); + if (dev->priv == NULL) { + free(dev); + return -1; + } + priv = dev->priv; + +#ifdef CONFIG_PHY_ADDR + priv->phyaddr = CONFIG_PHY_ADDR; +#else + priv->phyaddr = -1; +#endif + + sprintf(dev->name, "XGem.%x", base_addr); + + dev->iobase = base_addr; + + dev->init = gem_init; + dev->halt = gem_halt; + dev->send = gem_send; + dev->recv = gem_recv; + + eth_register(dev); + +#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB) + miiphy_register(dev->name, gem_miiphyread, gem_miiphy_write); + priv->bus = miiphy_get_dev_by_name(dev->name); +#endif + + return 1; +} diff --git a/include/netdev.h b/include/netdev.h index d1aaf0c..cd60ebb 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -102,6 +102,7 @@ int xilinx_axiemac_initialize(bd_t *bis, unsigned long base_addr, unsigned long dma_addr); int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, int txpp, int rxpp); +int xilinx_gem_initialize(bd_t *bis, int base_addr); int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, unsigned long ctrl_addr);

Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Device driver for Zynq Gem IP.
Signed-off-by: Michal Simek monstr@monstr.eu CC: Joe Hershberger joe.hershberger@gmail.com
drivers/net/Makefile | 1 + drivers/net/xilinx_gem.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++ include/netdev.h | 1 + 3 files changed, 516 insertions(+), 0 deletions(-) create mode 100644 drivers/net/xilinx_gem.c
Call this zynq_gem.c My understanding is this is only available in the hard silicon for Zynq.
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 430f90c..e510ffa 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,6 +77,7 @@ COBJS-$(CONFIG_ULI526X) += uli526x.o COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o COBJS-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +COBJS-$(CONFIG_XILINX_GEM) += xilinx_gem.o COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
diff --git a/drivers/net/xilinx_gem.c b/drivers/net/xilinx_gem.c new file mode 100644 index 0000000..7c8c14d --- /dev/null +++ b/drivers/net/xilinx_gem.c @@ -0,0 +1,514 @@ +/*
- (C) Copyright 2011 Michal Simek
- Michal SIMEK monstr@monstr.eu
- Based on Xilinx gmac driver:
- (C) Copyright 2011 Xilinx
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <net.h> +#include <config.h> +#include <malloc.h> +#include <asm/io.h> +#include <phy.h> +#include <miiphy.h>
+#if !defined(CONFIG_PHYLIB) +# error XILINX_GEM_ETHERNET requires PHYLIB +#endif
+/* Bit/mask specification */ +#define XEMACPSS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ +#define XEMACPSS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ +#define XEMACPSS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ +#define XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ +#define XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */
Replace all "XEMACPSS" with "ZYNQ_GEM"
+#define XEMACPSS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ +#define XEMACPSS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ +#define XEMACPSS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */
+#define XEMACPSS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ +#define XEMACPSS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ +#define XEMACPSS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */
+/* Wrap bit, last descriptor */ +#define XEMACPSS_TXBUF_WRAP_MASK 0x40000000 +#define XEMACPSS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */
+#define XEMACPSS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */ +#define XEMACPSS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */ +/* Transmit buffs exhausted mid frame */ +#define XEMACPSS_TXSR_BUFEXH_MASK 0x00000010
+#define XEMACPSS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ +#define XEMACPSS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ +#define XEMACPSS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ +#define XEMACPSS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */
+#define XEMACPSS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */
+/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1
+/* Mask used to verify certain PHY features (or register contents)
- in the register above:
- 0x1000: 10Mbps full duplex support
- 0x0800: 10Mbps half duplex support
- 0x0008: Auto-negotiation support
- */
+#define PHY_DETECT_MASK 0x1808
+/* Device registers */ +struct gem_regs {
u32 nwctrl; /* Network Control reg */
u32 nwcfg; /* Network Config reg */
u32 nwsr; /* Network Status reg */
u32 reserved1;
u32 dmacr; /* DMA Control reg */
u32 txsr; /* TX Status reg */
u32 rxqbase; /* RX Q Base address reg */
u32 txqbase; /* TX Q Base address reg */
u32 rxsr; /* RX Status reg */
u32 reserved2[2];
u32 idr; /* Interrupt Disable reg */
u32 reserved3;
u32 phymntnc; /* Phy Maintaince reg */
u32 reserved4[18];
u32 hashl; /* Hash Low address reg */
u32 hashh; /* Hash High address reg */
+#define LADDR_LOW 0 +#define LADDR_HIGH 1
u32 laddr[4][LADDR_HIGH + 1]; /* Specific1 addr low/high reg */
u32 match[4]; /* Type ID1 Match reg */
u32 reserved6[18];
u32 stat[44]; /* Octects transmitted Low reg - stat start */
+};
+/* BD descriptors */ +struct emac_bd {
u32 addr; /* Next descriptor pointer */
u32 status;
+};
+#define RX_BUF 3
+/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct gemac_priv {
struct emac_bd tx_bd;
struct emac_bd rx_bd[RX_BUF];
u32 initialized;
char rxbuffers[RX_BUF * PKTSIZE_ALIGN];
u32 rxbd_current;
u32 rx_first_buf;
int phyaddr;
struct phy_device *phydev;
struct mii_dev *bus;
+};
+static inline int mdio_wait(struct eth_device *dev) +{
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
u32 timeout = 200;
/* Wait till MDIO interface is ready to accept a new transaction. */
while (timeout && !(readl(®s->nwsr) & XEMACPSS_NWSR_MDIOIDLE_MASK)) {
timeout--;
udelay(1);
}
if (!timeout) {
printf("%s: Timeout\n", __func__);
return 1;
}
return 0;
+}
+static u32 phy_setup_op(struct eth_device *dev, u32 phy_addr, u32 regnum,
u32 op, u16 *data)
+{
u32 mgtcr;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
if (mdio_wait(dev))
return 1;
/* Construct mgtcr mask for the operation */
mgtcr = XEMACPSS_PHYMNTNC_OP_MASK | op |
(phy_addr << XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK) |
(regnum << XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK) | *data;
/* Write mgtcr and wait for completion */
writel(mgtcr, ®s->phymntnc);
if (mdio_wait(dev))
return 1;
if (op == XEMACPSS_PHYMNTNC_OP_R_MASK)
*data = readl(®s->phymntnc);
return 0;
+}
+static u32 phyread(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 *val) +{
return phy_setup_op(dev, phy_addr, regnum,
XEMACPSS_PHYMNTNC_OP_R_MASK, val);
+}
+static u32 phywrite(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 data) +{
return phy_setup_op(dev, phy_addr, regnum,
XEMACPSS_PHYMNTNC_OP_W_MASK, &data);
+}
+static void phy_detection(struct eth_device *dev) +{
int i, ret;
u16 phyreg;
struct gemac_priv *priv = dev->priv;
if (priv->phyaddr != -1) {
ret = phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg);
if (!ret && (phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
debug("Default phy address %d is valid\n",
priv->phyaddr);
return;
} else {
debug("PHY address is not setup correctly %d\n",
priv->phyaddr);
priv->phyaddr = -1;
}
}
debug("detecting phy address\n");
if (priv->phyaddr == -1) {
/* detect the PHY address */
for (i = 31; i >= 0; i--) {
ret = phyread(dev, i, PHY_DETECT_REG, &phyreg);
if (!ret && (phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
priv->phyaddr = i;
debug("Found valid phy address, %d\n", i);
return;
}
}
}
printf("PHY is not detected\n");
+}
Phy detection should be optional (behind a config token). It also sounds like something that has nothing to do with this driver. If you want to add it, it should probably be in phylib and work with any driver.
+/* Address bits[47:32] bit[31:0] are in BOTTOM */ +#define XEMACPSS_LADDR_MACH_MASK 0x0000FFFF
+static void setmacaddress(struct eth_device *dev, void *addressptr, u8 index) +{
u32 macaddr;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
u8 *aptr = (u8 *) addressptr;
/* Set the MAC bits [31:0] in BOT */
macaddr = aptr[0];
macaddr |= aptr[1] << 8;
macaddr |= aptr[2] << 16;
macaddr |= aptr[3] << 24;
writel(macaddr, ®s->laddr[index][LADDR_LOW]);
/* There are reserved bits in TOP so don't affect them */
macaddr = readl(®s->laddr[index][LADDR_HIGH]);
macaddr &= ~XEMACPSS_LADDR_MACH_MASK;
/* Set MAC bits [47:32] in TOP */
macaddr |= aptr[4];
macaddr |= aptr[5] << 8;
writel(macaddr, ®s->laddr[index][LADDR_HIGH]);
/* Set the ID bits in MATCHx register - settypeidcheck */
writel(0, ®s->match[index]);
+}
+static int gem_init(struct eth_device *dev, bd_t * bis) +{
int tmp;
int i;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
char emacpss_zero_mac[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
struct gemac_priv *priv = dev->priv;
struct phy_device *phydev;
u32 supported = SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full;
if (priv->initialized)
return 0;
/* Disable all interrupts */
writel(0xFFFFFFFF, ®s->idr);
/* Disable the receiver & transmitter */
writel(0, ®s->nwctrl);
writel(0, ®s->txsr);
writel(0, ®s->rxsr);
writel(0, ®s->phymntnc);
/* Clear the Hash registers for the mac address pointed by AddressPtr */
writel(0x0, ®s->hashl);
/* Write bits [63:32] in TOP */
writel(0x0, ®s->hashh);
for (i = 0; i < 4; i++)
setmacaddress(dev, emacpss_zero_mac, i);
/* Clear all counters */
for (i = 0; i <= (sizeof(struct gem_regs) -
offsetof(struct gem_regs, stat)) / 4; i++)
readl(®s->stat[i]);
/* Setup RxBD space */
memset(&(priv->rx_bd), 0, sizeof(priv->rx_bd));
/* Create the RxBD ring */
memset(&(priv->rxbuffers), 0, sizeof(priv->rxbuffers));
for (i = 0; i < RX_BUF; i++) {
priv->rx_bd[i].status = 0xF0000000;
priv->rx_bd[i].addr = (u32)((char *) &(priv->rxbuffers) +
(i * PKTSIZE_ALIGN));
}
/* WRAP bit to last BD */
priv->rx_bd[--i].addr |= XEMACPSS_RXBUF_WRAP_MASK;
/* Write RxBDs to IP */
writel((u32) &(priv->rx_bd), ®s->rxqbase);
/* MAC Setup */
/*
* Following is the setup for Network Configuration register.
* Bit 0: Set for 100 Mbps operation.
* Bit 1: Set for Full Duplex mode.
* Bit 4: Set to allow Copy all frames.
* Bit 17: Set for FCS removal.
* Bits 20-18: Set with value binary 010 to divide pclk by 32
* (pclk up to 80 MHz)
*/
writel(0x000A0013, ®s->nwcfg);
/*
* Following is the setup for DMA Configuration register.
* Bits 4-0: To set AHB fixed burst length for DMA data operations ->
* Set with binary 00100 to use INCR4 AHB bursts.
* Bits 9-8: Receiver packet buffer memory size ->
* Set with binary 11 to Use full configured addressable space (8 Kb)
* Bit 10 : Transmitter packet buffer memory size ->
* Set with binary 1 to Use full configured addressable space (4 Kb)
* Bits 23-16 : DMA receive buffer size in AHB system memory ->
* Set with binary 00011000 to use 1536 byte(1*max length frame/buffer)
*/
writel(0x00180704, ®s->dmacr);
/*
* Following is the setup for Network Control register.
* Bit 2: Set to enable Receive operation.
* Bit 3: Set to enable Transmitt operation.
* Bit 4: Set to enable MDIO operation.
*/
tmp = readl(®s->nwctrl);
/* MDIO, Rx and Tx enable */
tmp |= XEMACPSS_NWCTRL_MDEN_MASK | XEMACPSS_NWCTRL_RXEN_MASK |
XEMACPSS_NWCTRL_TXEN_MASK;
writel(tmp, ®s->nwctrl);
phy_detection(dev);
/* interface - look at tsec */
phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0);
phydev->supported &= supported;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
phy_startup(phydev);
priv->initialized = 1;
return 0;
How do you set the clock rate for the GEM based on the phy's link speed? You specify that you support 10 Mb/s, 100 Mb/s, and 1000Mb/s. They need the GEM running at 2.5 MHz, 25 MHz, and 125 MHz respectively.
+}
+static int gem_send(struct eth_device *dev, void *ptr, int len) +{
int status;
u32 val;
struct gemac_priv *priv = dev->priv;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
if (!priv->initialized) {
puts("Error GMAC not initialized");
return -1;
}
/* setup BD */
writel((u32)&(priv->tx_bd), ®s->txqbase);
/* Setup Tx BD */
memset((void *) &(priv->tx_bd), 0, sizeof(struct emac_bd));
priv->tx_bd.addr = (u32)ptr;
priv->tx_bd.status = len | XEMACPSS_TXBUF_LAST_MASK |
XEMACPSS_TXBUF_WRAP_MASK;
/* Start transmit */
val = readl(®s->nwctrl) | XEMACPSS_NWCTRL_STARTTX_MASK;
writel(val, ®s->nwctrl);
/* Read the stat register to know if the packet has been transmitted */
status = readl(®s->txsr);
if (status & (XEMACPSS_TXSR_HRESPNOK_MASK | XEMACPSS_TXSR_URUN_MASK |
XEMACPSS_TXSR_BUFEXH_MASK)) {
printf("Something has gone wrong here!? Status is 0x%x.\n",
status);
}
/* Clear Tx status register before leaving . */
writel(status, ®s->txsr);
return 0;
+}
+/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ +static int gem_recv(struct eth_device *dev) +{
int frame_len;
struct gemac_priv *priv = dev->priv;
if (!(priv->rx_bd[priv->rxbd_current].addr & XEMACPSS_RXBUF_NEW_MASK))
return 0;
if (!(priv->rx_bd[priv->rxbd_current].status &
(XEMACPSS_RXBUF_SOF_MASK | XEMACPSS_RXBUF_EOF_MASK))) {
printf("GEM: SOF or EOF not set for last buffer received!\n");
return 0;
}
frame_len = priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_LEN_MASK;
if (frame_len) {
NetReceive((u8 *) (priv->rx_bd[priv->rxbd_current].addr &
XEMACPSS_RXBUF_ADD_MASK), frame_len);
if (priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_SOF_MASK)
priv->rx_first_buf = priv->rxbd_current;
else {
priv->rx_bd[priv->rxbd_current].addr &=
~XEMACPSS_RXBUF_NEW_MASK;
priv->rx_bd[priv->rxbd_current].status = 0xF0000000;
}
if (priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_EOF_MASK) {
priv->rx_bd[priv->rx_first_buf].addr &=
~XEMACPSS_RXBUF_NEW_MASK;
priv->rx_bd[priv->rx_first_buf].status = 0xF0000000;
}
if ((++priv->rxbd_current) >= RX_BUF)
priv->rxbd_current = 0;
return frame_len;
}
return 0;
+}
+static void gem_halt(struct eth_device *dev) +{
return;
+}
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
At the top of this file you error if CONFIG_PHYLIB is not defined. That means that in no useful case will this not be defined, so you can remove this #if entirely.
+static int gem_miiphyread(const char *devname, uchar addr,
uchar reg, ushort *val)
+{
struct eth_device *dev = eth_get_dev();
int ret;
ret = phyread(dev, addr, reg, val);
debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, *val);
return ret;
+}
+static int gem_miiphy_write(const char *devname, uchar addr,
uchar reg, ushort val)
+{
struct eth_device *dev = eth_get_dev();
debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, val);
return phywrite(dev, addr, reg, val);
+} +#endif
+int xilinx_gem_initialize(bd_t *bis, int base_addr) +{
struct eth_device *dev;
struct gemac_priv *priv;
dev = calloc(1, sizeof(*dev));
if (dev == NULL)
return -1;
dev->priv = calloc(1, sizeof(struct gemac_priv));
if (dev->priv == NULL) {
free(dev);
return -1;
}
priv = dev->priv;
+#ifdef CONFIG_PHY_ADDR
priv->phyaddr = CONFIG_PHY_ADDR;
+#else
priv->phyaddr = -1;
+#endif
sprintf(dev->name, "XGem.%x", base_addr);
Replace "XGem" with "Zynq GEM". Also, the base address is not nice to look at here. You should know about the 2 base addresses in hardware (0xE000B000 and 0xE000C000) and should pass in the instance instead of the base address. Then you can use the instance here and not force each board to specify the base address.
dev->iobase = base_addr;
dev->init = gem_init;
dev->halt = gem_halt;
dev->send = gem_send;
dev->recv = gem_recv;
You should have dev->write_hwaddr = setmacaddress; here. Naturally you should make the signature match and rename the function.
eth_register(dev);
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
At the top of this file you error if CONFIG_PHYLIB is not defined. That means that in no useful case will this not be defined, so you can remove this #if entirely.
miiphy_register(dev->name, gem_miiphyread, gem_miiphy_write);
priv->bus = miiphy_get_dev_by_name(dev->name);
+#endif
return 1;
+} diff --git a/include/netdev.h b/include/netdev.h index d1aaf0c..cd60ebb 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -102,6 +102,7 @@ int xilinx_axiemac_initialize(bd_t *bis, unsigned long base_addr, unsigned long dma_addr); int xilinx_emaclite_initialize(bd_t *bis, unsigned long base_addr, int txpp, int rxpp); +int xilinx_gem_initialize(bd_t *bis, int base_addr); int xilinx_ll_temac_eth_init(bd_t *bis, unsigned long base_addr, int flags, unsigned long ctrl_addr);
-- 1.7.0.4

Hi Joe,
On 08/14/2012 04:59 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Device driver for Zynq Gem IP.
Signed-off-by: Michal Simek monstr@monstr.eu CC: Joe Hershberger joe.hershberger@gmail.com
drivers/net/Makefile | 1 + drivers/net/xilinx_gem.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++ include/netdev.h | 1 + 3 files changed, 516 insertions(+), 0 deletions(-) create mode 100644 drivers/net/xilinx_gem.c
Call this zynq_gem.c My understanding is this is only available in the hard silicon for Zynq.
That's correct understanding.
diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 430f90c..e510ffa 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -77,6 +77,7 @@ COBJS-$(CONFIG_ULI526X) += uli526x.o COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o COBJS-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o +COBJS-$(CONFIG_XILINX_GEM) += xilinx_gem.o COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \ xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
diff --git a/drivers/net/xilinx_gem.c b/drivers/net/xilinx_gem.c new file mode 100644 index 0000000..7c8c14d --- /dev/null +++ b/drivers/net/xilinx_gem.c @@ -0,0 +1,514 @@ +/*
- (C) Copyright 2011 Michal Simek
- Michal SIMEK monstr@monstr.eu
- Based on Xilinx gmac driver:
- (C) Copyright 2011 Xilinx
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <net.h> +#include <config.h> +#include <malloc.h> +#include <asm/io.h> +#include <phy.h> +#include <miiphy.h>
+#if !defined(CONFIG_PHYLIB) +# error XILINX_GEM_ETHERNET requires PHYLIB +#endif
+/* Bit/mask specification */ +#define XEMACPSS_PHYMNTNC_OP_MASK 0x40020000 /* operation mask bits */ +#define XEMACPSS_PHYMNTNC_OP_R_MASK 0x20000000 /* read operation */ +#define XEMACPSS_PHYMNTNC_OP_W_MASK 0x10000000 /* write operation */ +#define XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK 23 /* Shift bits for PHYAD */ +#define XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK 18 /* Shift bits for PHREG */
Replace all "XEMACPSS" with "ZYNQ_GEM"
ok
+#define XEMACPSS_RXBUF_EOF_MASK 0x00008000 /* End of frame. */ +#define XEMACPSS_RXBUF_SOF_MASK 0x00004000 /* Start of frame. */ +#define XEMACPSS_RXBUF_LEN_MASK 0x00003FFF /* Mask for length field */
+#define XEMACPSS_RXBUF_WRAP_MASK 0x00000002 /* Wrap bit, last BD */ +#define XEMACPSS_RXBUF_NEW_MASK 0x00000001 /* Used bit.. */ +#define XEMACPSS_RXBUF_ADD_MASK 0xFFFFFFFC /* Mask for address */
+/* Wrap bit, last descriptor */ +#define XEMACPSS_TXBUF_WRAP_MASK 0x40000000 +#define XEMACPSS_TXBUF_LAST_MASK 0x00008000 /* Last buffer */
+#define XEMACPSS_TXSR_HRESPNOK_MASK 0x00000100 /* Transmit hresp not OK */ +#define XEMACPSS_TXSR_URUN_MASK 0x00000040 /* Transmit underrun */ +/* Transmit buffs exhausted mid frame */ +#define XEMACPSS_TXSR_BUFEXH_MASK 0x00000010
+#define XEMACPSS_NWCTRL_TXEN_MASK 0x00000008 /* Enable transmit */ +#define XEMACPSS_NWCTRL_RXEN_MASK 0x00000004 /* Enable receive */ +#define XEMACPSS_NWCTRL_MDEN_MASK 0x00000010 /* Enable MDIO port */ +#define XEMACPSS_NWCTRL_STARTTX_MASK 0x00000200 /* Start tx (tx_go) */
+#define XEMACPSS_NWSR_MDIOIDLE_MASK 0x00000004 /* PHY management idle */
+/* Use MII register 1 (MII status register) to detect PHY */ +#define PHY_DETECT_REG 1
+/* Mask used to verify certain PHY features (or register contents)
- in the register above:
- 0x1000: 10Mbps full duplex support
- 0x0800: 10Mbps half duplex support
- 0x0008: Auto-negotiation support
- */
+#define PHY_DETECT_MASK 0x1808
+/* Device registers */ +struct gem_regs {
u32 nwctrl; /* Network Control reg */
u32 nwcfg; /* Network Config reg */
u32 nwsr; /* Network Status reg */
u32 reserved1;
u32 dmacr; /* DMA Control reg */
u32 txsr; /* TX Status reg */
u32 rxqbase; /* RX Q Base address reg */
u32 txqbase; /* TX Q Base address reg */
u32 rxsr; /* RX Status reg */
u32 reserved2[2];
u32 idr; /* Interrupt Disable reg */
u32 reserved3;
u32 phymntnc; /* Phy Maintaince reg */
u32 reserved4[18];
u32 hashl; /* Hash Low address reg */
u32 hashh; /* Hash High address reg */
+#define LADDR_LOW 0 +#define LADDR_HIGH 1
u32 laddr[4][LADDR_HIGH + 1]; /* Specific1 addr low/high reg */
u32 match[4]; /* Type ID1 Match reg */
u32 reserved6[18];
u32 stat[44]; /* Octects transmitted Low reg - stat start */
+};
+/* BD descriptors */ +struct emac_bd {
u32 addr; /* Next descriptor pointer */
u32 status;
+};
+#define RX_BUF 3
+/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct gemac_priv {
struct emac_bd tx_bd;
struct emac_bd rx_bd[RX_BUF];
u32 initialized;
char rxbuffers[RX_BUF * PKTSIZE_ALIGN];
u32 rxbd_current;
u32 rx_first_buf;
int phyaddr;
struct phy_device *phydev;
struct mii_dev *bus;
+};
+static inline int mdio_wait(struct eth_device *dev) +{
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
u32 timeout = 200;
/* Wait till MDIO interface is ready to accept a new transaction. */
while (timeout && !(readl(®s->nwsr) & XEMACPSS_NWSR_MDIOIDLE_MASK)) {
timeout--;
udelay(1);
}
if (!timeout) {
printf("%s: Timeout\n", __func__);
return 1;
}
return 0;
+}
+static u32 phy_setup_op(struct eth_device *dev, u32 phy_addr, u32 regnum,
u32 op, u16 *data)
+{
u32 mgtcr;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
if (mdio_wait(dev))
return 1;
/* Construct mgtcr mask for the operation */
mgtcr = XEMACPSS_PHYMNTNC_OP_MASK | op |
(phy_addr << XEMACPSS_PHYMNTNC_PHYAD_SHIFT_MASK) |
(regnum << XEMACPSS_PHYMNTNC_PHREG_SHIFT_MASK) | *data;
/* Write mgtcr and wait for completion */
writel(mgtcr, ®s->phymntnc);
if (mdio_wait(dev))
return 1;
if (op == XEMACPSS_PHYMNTNC_OP_R_MASK)
*data = readl(®s->phymntnc);
return 0;
+}
+static u32 phyread(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 *val) +{
return phy_setup_op(dev, phy_addr, regnum,
XEMACPSS_PHYMNTNC_OP_R_MASK, val);
+}
+static u32 phywrite(struct eth_device *dev, u32 phy_addr, u32 regnum, u16 data) +{
return phy_setup_op(dev, phy_addr, regnum,
XEMACPSS_PHYMNTNC_OP_W_MASK, &data);
+}
+static void phy_detection(struct eth_device *dev) +{
int i, ret;
u16 phyreg;
struct gemac_priv *priv = dev->priv;
if (priv->phyaddr != -1) {
ret = phyread(dev, priv->phyaddr, PHY_DETECT_REG, &phyreg);
if (!ret && (phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
debug("Default phy address %d is valid\n",
priv->phyaddr);
return;
} else {
debug("PHY address is not setup correctly %d\n",
priv->phyaddr);
priv->phyaddr = -1;
}
}
debug("detecting phy address\n");
if (priv->phyaddr == -1) {
/* detect the PHY address */
for (i = 31; i >= 0; i--) {
ret = phyread(dev, i, PHY_DETECT_REG, &phyreg);
if (!ret && (phyreg != 0xFFFF) &&
((phyreg & PHY_DETECT_MASK) == PHY_DETECT_MASK)) {
/* Found a valid PHY address */
priv->phyaddr = i;
debug("Found valid phy address, %d\n", i);
return;
}
}
}
printf("PHY is not detected\n");
+}
Phy detection should be optional (behind a config token). It also sounds like something that has nothing to do with this driver. If you want to add it, it should probably be in phylib and work with any driver.
No problem to remove it. Will look at phylib if provides feature like this. It means detect phy or check that the actual phy address is correct.
+/* Address bits[47:32] bit[31:0] are in BOTTOM */ +#define XEMACPSS_LADDR_MACH_MASK 0x0000FFFF
+static void setmacaddress(struct eth_device *dev, void *addressptr, u8 index) +{
u32 macaddr;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
u8 *aptr = (u8 *) addressptr;
/* Set the MAC bits [31:0] in BOT */
macaddr = aptr[0];
macaddr |= aptr[1] << 8;
macaddr |= aptr[2] << 16;
macaddr |= aptr[3] << 24;
writel(macaddr, ®s->laddr[index][LADDR_LOW]);
/* There are reserved bits in TOP so don't affect them */
macaddr = readl(®s->laddr[index][LADDR_HIGH]);
macaddr &= ~XEMACPSS_LADDR_MACH_MASK;
/* Set MAC bits [47:32] in TOP */
macaddr |= aptr[4];
macaddr |= aptr[5] << 8;
writel(macaddr, ®s->laddr[index][LADDR_HIGH]);
/* Set the ID bits in MATCHx register - settypeidcheck */
writel(0, ®s->match[index]);
+}
+static int gem_init(struct eth_device *dev, bd_t * bis) +{
int tmp;
int i;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
char emacpss_zero_mac[6] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
struct gemac_priv *priv = dev->priv;
struct phy_device *phydev;
u32 supported = SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full;
if (priv->initialized)
return 0;
/* Disable all interrupts */
writel(0xFFFFFFFF, ®s->idr);
/* Disable the receiver & transmitter */
writel(0, ®s->nwctrl);
writel(0, ®s->txsr);
writel(0, ®s->rxsr);
writel(0, ®s->phymntnc);
/* Clear the Hash registers for the mac address pointed by AddressPtr */
writel(0x0, ®s->hashl);
/* Write bits [63:32] in TOP */
writel(0x0, ®s->hashh);
for (i = 0; i < 4; i++)
setmacaddress(dev, emacpss_zero_mac, i);
/* Clear all counters */
for (i = 0; i <= (sizeof(struct gem_regs) -
offsetof(struct gem_regs, stat)) / 4; i++)
readl(®s->stat[i]);
/* Setup RxBD space */
memset(&(priv->rx_bd), 0, sizeof(priv->rx_bd));
/* Create the RxBD ring */
memset(&(priv->rxbuffers), 0, sizeof(priv->rxbuffers));
for (i = 0; i < RX_BUF; i++) {
priv->rx_bd[i].status = 0xF0000000;
priv->rx_bd[i].addr = (u32)((char *) &(priv->rxbuffers) +
(i * PKTSIZE_ALIGN));
}
/* WRAP bit to last BD */
priv->rx_bd[--i].addr |= XEMACPSS_RXBUF_WRAP_MASK;
/* Write RxBDs to IP */
writel((u32) &(priv->rx_bd), ®s->rxqbase);
/* MAC Setup */
/*
* Following is the setup for Network Configuration register.
* Bit 0: Set for 100 Mbps operation.
* Bit 1: Set for Full Duplex mode.
* Bit 4: Set to allow Copy all frames.
* Bit 17: Set for FCS removal.
* Bits 20-18: Set with value binary 010 to divide pclk by 32
* (pclk up to 80 MHz)
*/
writel(0x000A0013, ®s->nwcfg);
/*
* Following is the setup for DMA Configuration register.
* Bits 4-0: To set AHB fixed burst length for DMA data operations ->
* Set with binary 00100 to use INCR4 AHB bursts.
* Bits 9-8: Receiver packet buffer memory size ->
* Set with binary 11 to Use full configured addressable space (8 Kb)
* Bit 10 : Transmitter packet buffer memory size ->
* Set with binary 1 to Use full configured addressable space (4 Kb)
* Bits 23-16 : DMA receive buffer size in AHB system memory ->
* Set with binary 00011000 to use 1536 byte(1*max length frame/buffer)
*/
writel(0x00180704, ®s->dmacr);
/*
* Following is the setup for Network Control register.
* Bit 2: Set to enable Receive operation.
* Bit 3: Set to enable Transmitt operation.
* Bit 4: Set to enable MDIO operation.
*/
tmp = readl(®s->nwctrl);
/* MDIO, Rx and Tx enable */
tmp |= XEMACPSS_NWCTRL_MDEN_MASK | XEMACPSS_NWCTRL_RXEN_MASK |
XEMACPSS_NWCTRL_TXEN_MASK;
writel(tmp, ®s->nwctrl);
phy_detection(dev);
/* interface - look at tsec */
phydev = phy_connect(priv->bus, priv->phyaddr, dev, 0);
phydev->supported &= supported;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
phy_startup(phydev);
priv->initialized = 1;
return 0;
How do you set the clock rate for the GEM based on the phy's link speed? You specify that you support 10 Mb/s, 100 Mb/s, and 1000Mb/s. They need the GEM running at 2.5 MHz, 25 MHz, and 125 MHz respectively.
I think you are talking about MDCCLKDIV value. Current setting is 010 which is pclk up to 80MHz. My zc702 works only at 100MHz that's why I can't test 1000Mb/s.
If doesn't work at 1000Mb/s, we can setup different value.
On two boards which we have tested there were no problem with this driver.
+}
+static int gem_send(struct eth_device *dev, void *ptr, int len) +{
int status;
u32 val;
struct gemac_priv *priv = dev->priv;
struct gem_regs *regs = (struct gem_regs *)dev->iobase;
if (!priv->initialized) {
puts("Error GMAC not initialized");
return -1;
}
/* setup BD */
writel((u32)&(priv->tx_bd), ®s->txqbase);
/* Setup Tx BD */
memset((void *) &(priv->tx_bd), 0, sizeof(struct emac_bd));
priv->tx_bd.addr = (u32)ptr;
priv->tx_bd.status = len | XEMACPSS_TXBUF_LAST_MASK |
XEMACPSS_TXBUF_WRAP_MASK;
/* Start transmit */
val = readl(®s->nwctrl) | XEMACPSS_NWCTRL_STARTTX_MASK;
writel(val, ®s->nwctrl);
/* Read the stat register to know if the packet has been transmitted */
status = readl(®s->txsr);
if (status & (XEMACPSS_TXSR_HRESPNOK_MASK | XEMACPSS_TXSR_URUN_MASK |
XEMACPSS_TXSR_BUFEXH_MASK)) {
printf("Something has gone wrong here!? Status is 0x%x.\n",
status);
}
/* Clear Tx status register before leaving . */
writel(status, ®s->txsr);
return 0;
+}
+/* Do not check frame_recd flag in rx_status register 0x20 - just poll BD */ +static int gem_recv(struct eth_device *dev) +{
int frame_len;
struct gemac_priv *priv = dev->priv;
if (!(priv->rx_bd[priv->rxbd_current].addr & XEMACPSS_RXBUF_NEW_MASK))
return 0;
if (!(priv->rx_bd[priv->rxbd_current].status &
(XEMACPSS_RXBUF_SOF_MASK | XEMACPSS_RXBUF_EOF_MASK))) {
printf("GEM: SOF or EOF not set for last buffer received!\n");
return 0;
}
frame_len = priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_LEN_MASK;
if (frame_len) {
NetReceive((u8 *) (priv->rx_bd[priv->rxbd_current].addr &
XEMACPSS_RXBUF_ADD_MASK), frame_len);
if (priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_SOF_MASK)
priv->rx_first_buf = priv->rxbd_current;
else {
priv->rx_bd[priv->rxbd_current].addr &=
~XEMACPSS_RXBUF_NEW_MASK;
priv->rx_bd[priv->rxbd_current].status = 0xF0000000;
}
if (priv->rx_bd[priv->rxbd_current].status &
XEMACPSS_RXBUF_EOF_MASK) {
priv->rx_bd[priv->rx_first_buf].addr &=
~XEMACPSS_RXBUF_NEW_MASK;
priv->rx_bd[priv->rx_first_buf].status = 0xF0000000;
}
if ((++priv->rxbd_current) >= RX_BUF)
priv->rxbd_current = 0;
return frame_len;
}
return 0;
+}
+static void gem_halt(struct eth_device *dev) +{
return;
+}
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
At the top of this file you error if CONFIG_PHYLIB is not defined. That means that in no useful case will this not be defined, so you can remove this #if entirely.
ok. I have one more patch which covers cases where PHYLIB is not enabled but yes it can be removed from this driver.
+static int gem_miiphyread(const char *devname, uchar addr,
uchar reg, ushort *val)
+{
struct eth_device *dev = eth_get_dev();
int ret;
ret = phyread(dev, addr, reg, val);
debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, *val);
return ret;
+}
+static int gem_miiphy_write(const char *devname, uchar addr,
uchar reg, ushort val)
+{
struct eth_device *dev = eth_get_dev();
debug("%s 0x%x, 0x%x, 0x%x\n", __func__, addr, reg, val);
return phywrite(dev, addr, reg, val);
+} +#endif
+int xilinx_gem_initialize(bd_t *bis, int base_addr) +{
struct eth_device *dev;
struct gemac_priv *priv;
dev = calloc(1, sizeof(*dev));
if (dev == NULL)
return -1;
dev->priv = calloc(1, sizeof(struct gemac_priv));
if (dev->priv == NULL) {
free(dev);
return -1;
}
priv = dev->priv;
+#ifdef CONFIG_PHY_ADDR
priv->phyaddr = CONFIG_PHY_ADDR;
+#else
priv->phyaddr = -1;
+#endif
sprintf(dev->name, "XGem.%x", base_addr);
Replace "XGem" with "Zynq GEM". Also, the base address is not nice to look at here. You should know about the 2 base addresses in hardware (0xE000B000 and 0xE000C000) and should pass in the instance instead of the base address. Then you can use the instance here and not force each board to specify the base address.
This is the same case as was for serial driver. If we connect Microblaze to it then Microblaze could use it and addresses can changed. That's why using name.<addr> is 100% description which IP u-boot uses.
About the name - there is 16 chars for it where 9 chars is used for baseaddr + dot and the rest can be used for driver identification.
I don't care if is XGem or Zynq, etc.
dev->iobase = base_addr;
dev->init = gem_init;
dev->halt = gem_halt;
dev->send = gem_send;
dev->recv = gem_recv;
You should have dev->write_hwaddr = setmacaddress; here. Naturally you should make the signature match and rename the function.
will look.
eth_register(dev);
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) || defined(CONFIG_PHYLIB)
At the top of this file you error if CONFIG_PHYLIB is not defined. That means that in no useful case will this not be defined, so you can remove this #if entirely.
The same reason as above.
Thanks, Michal

Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu --- arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)lib$(SOC).o + +COBJS = timer.o + +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) + +all: $(obj).depend $(LIB) + +$(LIB): $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Michal Simek monstr@monstr.eu + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. + * + * (C) Copyright 2008 + * Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de + * + * (C) Copyright 2004 + * Philippe Robin, ARM Ltd. philippe.robin@arm.com + * + * (C) Copyright 2002-2004 + * Gary Jennejohn, DENX Software Engineering, gj@denx.de + * + * (C) Copyright 2003 + * Texas Instruments <www.ti.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Alex Zuepke azu@sysgo.de + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <div64.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct scu_timer { + u32 load; /* Timer Load Register */ + u32 counter; /* Timer Counter Register */ + u32 control; /* Timer Control Register */ +}; + +static struct scu_timer *timer_base = CONFIG_XPSS_SCUTIMER_BASEADDR; + +#define XSCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ +#define XSCUTIMER_CONTROL_PRESCALER_SHIFT 8 +#define XSCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ +#define XSCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */ + +#define TIMER_LOAD_VAL 0xFFFFFFFF +#define TIMER_PRESCALE 255 +#define TIMER_TICK_HZ (CONFIG_CPU_FREQ_HZ / 2 / TIMER_PRESCALE) + +int timer_init(void) +{ + u32 val; + + /* Load the timer counter register */ + writel(0xFFFFFFFF, &timer_base->counter); + + /* Start the A9Timer device */ + val = readl(&timer_base->control); + /* Enable Auto reload mode */ + val |= XSCUTIMER_CONTROL_AUTO_RELOAD_MASK; + /* Clear prescaler control bits */ + val &= ~XSCUTIMER_CONTROL_PRESCALER_MASK; + /* Set prescaler value */ + val |= (TIMER_PRESCALE << XSCUTIMER_CONTROL_PRESCALER_SHIFT); + /* Enable the decrementer */ + val |= XSCUTIMER_CONTROL_ENABLE_MASK; + writel(val, &timer_base->control); + + /* Reset time */ + gd->lastinc = readl(&timer_base->counter) / + (TIMER_TICK_HZ / CONFIG_SYS_HZ); + gd->tbl = 0; + + return 0; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +ulong get_timer_masked(void) +{ + ulong now; + + now = readl(&timer_base->counter) / (TIMER_TICK_HZ / CONFIG_SYS_HZ); + + if (gd->lastinc >= now) { + /* Normal mode */ + gd->tbl += gd->lastinc - now; + } else { + /* We have an overflow ... */ + gd->tbl += gd->lastinc + TIMER_LOAD_VAL - now; + } + gd->lastinc = now; + + return gd->tbl; +} + +void __udelay(unsigned long usec) +{ + unsigned long long tmp; + ulong tmo; + + tmo = usec / (1000000 / CONFIG_SYS_HZ); + tmp = get_ticks() + tmo; /* Get current timestamp */ + + while (get_ticks() < tmp) { /* Loop till event */ + /* NOP */; + } +} + +/* Timer without interrupts */ +ulong get_timer(ulong base) +{ + return get_timer_masked() - base; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + return CONFIG_SYS_HZ; +}

Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
+static struct scu_timer *timer_base = CONFIG_XPSS_SCUTIMER_BASEADDR;
+#define XSCUTIMER_CONTROL_PRESCALER_MASK 0x0000FF00 /* Prescaler */ +#define XSCUTIMER_CONTROL_PRESCALER_SHIFT 8 +#define XSCUTIMER_CONTROL_AUTO_RELOAD_MASK 0x00000002 /* Auto-reload */ +#define XSCUTIMER_CONTROL_ENABLE_MASK 0x00000001 /* Timer enable */
+#define TIMER_LOAD_VAL 0xFFFFFFFF +#define TIMER_PRESCALE 255 +#define TIMER_TICK_HZ (CONFIG_CPU_FREQ_HZ / 2 / TIMER_PRESCALE)
+int timer_init(void) +{
u32 val;
/* Load the timer counter register */
writel(0xFFFFFFFF, &timer_base->counter);
/* Start the A9Timer device */
val = readl(&timer_base->control);
/* Enable Auto reload mode */
val |= XSCUTIMER_CONTROL_AUTO_RELOAD_MASK;
/* Clear prescaler control bits */
val &= ~XSCUTIMER_CONTROL_PRESCALER_MASK;
/* Set prescaler value */
val |= (TIMER_PRESCALE << XSCUTIMER_CONTROL_PRESCALER_SHIFT);
/* Enable the decrementer */
val |= XSCUTIMER_CONTROL_ENABLE_MASK;
writel(val, &timer_base->control);
/* Reset time */
gd->lastinc = readl(&timer_base->counter) /
(TIMER_TICK_HZ / CONFIG_SYS_HZ);
gd->tbl = 0;
return 0;
+}
+/*
- This function is derived from PowerPC code (read timebase as long long).
- On ARM it just returns the timer value.
- */
+ulong get_timer_masked(void) +{
ulong now;
now = readl(&timer_base->counter) / (TIMER_TICK_HZ / CONFIG_SYS_HZ);
if (gd->lastinc >= now) {
/* Normal mode */
gd->tbl += gd->lastinc - now;
} else {
/* We have an overflow ... */
gd->tbl += gd->lastinc + TIMER_LOAD_VAL - now;
}
gd->lastinc = now;
return gd->tbl;
+}
+void __udelay(unsigned long usec) +{
unsigned long long tmp;
ulong tmo;
tmo = usec / (1000000 / CONFIG_SYS_HZ);
tmp = get_ticks() + tmo; /* Get current timestamp */
while (get_ticks() < tmp) { /* Loop till event */
/* NOP */;
}
+}
+/* Timer without interrupts */ +ulong get_timer(ulong base) +{
return get_timer_masked() - base;
+}
+/*
- This function is derived from PowerPC code (read timebase as long long).
- On ARM it just returns the timer value.
- */
+unsigned long long get_ticks(void) +{
return get_timer(0);
+}
+/*
- This function is derived from PowerPC code (timebase clock frequency).
- On ARM it returns the number of timer ticks per second.
- */
+ulong get_tbclk(void) +{
return CONFIG_SYS_HZ;
+}
1.7.0.4
U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
Thanks, Michal

Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
-Joe

On 08/14/2012 06:41 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
For me was just safer to use it in zynq folder because I am not familiar with others ARMs. + I see that other armv7 cpus uses own timer drivers.
Any input from arm custodian will be helpful.
Thanks, Michal

Hi Michal,
On Tue, Aug 14, 2012 at 12:11 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 06:41 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
For me was just safer to use it in zynq folder because I am not familiar with others ARMs.
- I see that other armv7 cpus uses own timer drivers.
That is true, but they seem to be using other timer hardware instead of the ARM core timers. For instance, if you were using the Zynq's Triple Timer Counter module instead, then it would make sense to have this in the zynq directory.
Any input from arm custodian will be helpful.
Albert?
Thanks, Michal -- Michal Simek, Ing. (M.Eng) w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/ Microblaze U-BOOT custodian

On 08/14/2012 07:15 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 12:11 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 06:41 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote:
Add timer driver.
Signed-off-by: Michal Simek monstr@monstr.eu
arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ arch/arm/cpu/armv7/zynq/timer.c | 151
++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/armv7/zynq/Makefile create mode 100644 arch/arm/cpu/armv7/zynq/timer.c
diff --git a/arch/arm/cpu/armv7/zynq/Makefile b/arch/arm/cpu/armv7/zynq/Makefile new file mode 100644 index 0000000..814c1d4 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2000-2003 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# (C) Copyright 2008 +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +#
+include $(TOPDIR)/config.mk
+LIB = $(obj)lib$(SOC).o
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
+COBJS = timer.o
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
+SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
+all: $(obj).depend $(LIB)
+$(LIB): $(OBJS)
$(call cmd_link_o_target, $(OBJS))
+#########################################################################
+# defines $(obj).depend target +include $(SRCTREE)/rules.mk
+sinclude $(obj).depend
+######################################################################### diff --git a/arch/arm/cpu/armv7/zynq/timer.c b/arch/arm/cpu/armv7/zynq/timer.c new file mode 100644 index 0000000..d79da97 --- /dev/null +++ b/arch/arm/cpu/armv7/zynq/timer.c @@ -0,0 +1,151 @@ +/*
- Copyright (C) 2012 Michal Simek monstr@monstr.eu
- Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved.
- (C) Copyright 2008
- Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de
- (C) Copyright 2004
- Philippe Robin, ARM Ltd. philippe.robin@arm.com
- (C) Copyright 2002-2004
- Gary Jennejohn, DENX Software Engineering, gj@denx.de
- (C) Copyright 2003
- Texas Instruments <www.ti.com>
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Alex Zuepke azu@sysgo.de
- See file CREDITS for list of people who contributed to this
- project.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- MA 02111-1307 USA
- */
+#include <common.h> +#include <div64.h> +#include <asm/io.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct scu_timer {
u32 load; /* Timer Load Register */
u32 counter; /* Timer Counter Register */
u32 control; /* Timer Control Register */
+};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
For me was just safer to use it in zynq folder because I am not familiar with others ARMs.
- I see that other armv7 cpus uses own timer drivers.
That is true, but they seem to be using other timer hardware instead of the ARM core timers. For instance, if you were using the Zynq's Triple Timer Counter module instead, then it would make sense to have this in the zynq directory.
Yes, that's partially clear case if Microblaze doesn't want to use it.
Zynq can also use axi_timer. We have this driver in Microblaze folder. This is driver sharing across architectures. IRC there was any discussion about moving drivers to generic location in past but this has never happen. (BTW: this driver can be used by xilinx ppc405 and ppc440)
If Albert tends to move this driver to any general location I have no problem to do in spite of there is probably not any other armv7 close which will use it.
From my point of view if there is not other armv7 clone which will use it, make sense to keep this in zynq folder. And move it to generic location when any other armv7 wants to use.
Thanks, Michal

Hi Michal,
On Tue, Aug 14, 2012 at 12:39 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 07:15 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 12:11 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 06:41 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu wrote: > > > > Add timer driver. > > Signed-off-by: Michal Simek monstr@monstr.eu > --- > arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ > arch/arm/cpu/armv7/zynq/timer.c | 151 > ++++++++++++++++++++++++++++++++++++++ > 2 files changed, 199 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/cpu/armv7/zynq/Makefile > create mode 100644 arch/arm/cpu/armv7/zynq/timer.c > > diff --git a/arch/arm/cpu/armv7/zynq/Makefile > b/arch/arm/cpu/armv7/zynq/Makefile > new file mode 100644 > index 0000000..814c1d4 > --- /dev/null > +++ b/arch/arm/cpu/armv7/zynq/Makefile > @@ -0,0 +1,48 @@ > +# > +# (C) Copyright 2000-2003 > +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. > +# > +# (C) Copyright 2008 > +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de > +# > +# See file CREDITS for list of people who contributed to this > +# project. > +# > +# This program is free software; you can redistribute it and/or > +# modify it under the terms of the GNU General Public License as > +# published by the Free Software Foundation; either version 2 of > +# the License, or (at your option) any later version. > +# > +# This program is distributed in the hope that it will be useful, > +# but WITHOUT ANY WARRANTY; without even the implied warranty of > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +# GNU General Public License for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with this program; if not, write to the Free Software > +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, > +# MA 02111-1307 USA > +# > + > +include $(TOPDIR)/config.mk > + > +LIB = $(obj)lib$(SOC).o > +
You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
> +COBJS = timer.o > +
Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By that I mean use COBJS-y instead of COBJS directly. This is more forward looking to allow for features to be disabled in the future.
no problem with that.
> +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) > +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) > + > +all: $(obj).depend $(LIB) > + > +$(LIB): $(OBJS) > + $(call cmd_link_o_target, $(OBJS)) > + > > > > +######################################################################### > + > +# defines $(obj).depend target > +include $(SRCTREE)/rules.mk > + > +sinclude $(obj).depend > + > > > > +######################################################################### > diff --git a/arch/arm/cpu/armv7/zynq/timer.c > b/arch/arm/cpu/armv7/zynq/timer.c > new file mode 100644 > index 0000000..d79da97 > --- /dev/null > +++ b/arch/arm/cpu/armv7/zynq/timer.c > @@ -0,0 +1,151 @@ > +/* > + * Copyright (C) 2012 Michal Simek monstr@monstr.eu > + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. > + * > + * (C) Copyright 2008 > + * Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de > + * > + * (C) Copyright 2004 > + * Philippe Robin, ARM Ltd. philippe.robin@arm.com > + * > + * (C) Copyright 2002-2004 > + * Gary Jennejohn, DENX Software Engineering, gj@denx.de > + * > + * (C) Copyright 2003 > + * Texas Instruments <www.ti.com> > + * > + * (C) Copyright 2002 > + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > + * Marius Groeger mgroeger@sysgo.de > + * > + * (C) Copyright 2002 > + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> > + * Alex Zuepke azu@sysgo.de > + * > + * See file CREDITS for list of people who contributed to this > + * project. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation; either version 2 of > + * the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, > + * MA 02111-1307 USA > + */ > + > +#include <common.h> > +#include <div64.h> > +#include <asm/io.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +struct scu_timer { > + u32 load; /* Timer Load Register */ > + u32 counter; /* Timer Counter Register */ > + u32 control; /* Timer Control Register */ > +};
You are using the timer in the ARM Cortex A9 core. This is not Zynq-specific in any way. It should be made available in a different place. Probably in arch/arm/lib. It should be stripped on any "XSCU" references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
For me was just safer to use it in zynq folder because I am not familiar with others ARMs.
- I see that other armv7 cpus uses own timer drivers.
That is true, but they seem to be using other timer hardware instead of the ARM core timers. For instance, if you were using the Zynq's Triple Timer Counter module instead, then it would make sense to have this in the zynq directory.
Yes, that's partially clear case if Microblaze doesn't want to use it.
Zynq can also use axi_timer. We have this driver in Microblaze folder.
This would be bad because it would assume some timer in the fabric.
This is driver sharing across architectures. IRC there was any discussion about moving drivers to generic location in past but this has never happen. (BTW: this driver can be used by xilinx ppc405 and ppc440)
I guess by "this driver" you mean axi_timer and not scu_timer.
If Albert tends to move this driver to any general location I have no problem to do in spite of there is probably not any other armv7 close which will use it.
From my point of view if there is not other armv7 clone which will use it, make sense to keep this in zynq folder. And move it to generic location when any other armv7 wants to use.
This is exactly the opposite of what we should be doing. It is true that this refactoring has not been very wide-spread yet, but certainly for new things, the idea is to only put things into a less generic place if there is no reasonable chance that it will be reused outside of the more specific scope. If it is available in other places in hardware, make it generic. It's not as good to expect that every new board should have to search every other board dir in the hope that he finds a driver that he can reuse. He should be able to look in the arch (or even drivers/ if applicable) and complete the search sooner. Why force extra work to move it later. To the best of your ability, put it in the best place. If this driver is available for all cortex a9 based SoCs, then chances are every new one added from now on will use this instead of writing a new one, even if there are other timers available.
Thanks, -Joe

On 08/14/2012 08:19 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 12:39 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 07:15 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 12:11 PM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 06:41 PM, Joe Hershberger wrote:
Hi Michal,
On Tue, Aug 14, 2012 at 11:28 AM, Michal Simek monstr@monstr.eu wrote:
On 08/14/2012 05:36 PM, Joe Hershberger wrote: > > > > Hi Michal, > > On Tue, Aug 14, 2012 at 6:42 AM, Michal Simek monstr@monstr.eu > wrote: >> >> >> >> Add timer driver. >> >> Signed-off-by: Michal Simek monstr@monstr.eu >> --- >> arch/arm/cpu/armv7/zynq/Makefile | 48 ++++++++++++ >> arch/arm/cpu/armv7/zynq/timer.c | 151 >> ++++++++++++++++++++++++++++++++++++++ >> 2 files changed, 199 insertions(+), 0 deletions(-) >> create mode 100644 arch/arm/cpu/armv7/zynq/Makefile >> create mode 100644 arch/arm/cpu/armv7/zynq/timer.c >> >> diff --git a/arch/arm/cpu/armv7/zynq/Makefile >> b/arch/arm/cpu/armv7/zynq/Makefile >> new file mode 100644 >> index 0000000..814c1d4 >> --- /dev/null >> +++ b/arch/arm/cpu/armv7/zynq/Makefile >> @@ -0,0 +1,48 @@ >> +# >> +# (C) Copyright 2000-2003 >> +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. >> +# >> +# (C) Copyright 2008 >> +# Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de >> +# >> +# See file CREDITS for list of people who contributed to this >> +# project. >> +# >> +# This program is free software; you can redistribute it and/or >> +# modify it under the terms of the GNU General Public License as >> +# published by the Free Software Foundation; either version 2 of >> +# the License, or (at your option) any later version. >> +# >> +# This program is distributed in the hope that it will be useful, >> +# but WITHOUT ANY WARRANTY; without even the implied warranty of >> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> +# GNU General Public License for more details. >> +# >> +# You should have received a copy of the GNU General Public License >> +# along with this program; if not, write to the Free Software >> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, >> +# MA 02111-1307 USA >> +# >> + >> +include $(TOPDIR)/config.mk >> + >> +LIB = $(obj)lib$(SOC).o >> + > > > > > You should include the lowlevel_init.o here instead of in the board.
Probably make sense.
> >> +COBJS = timer.o >> + > > > > > Preferably emulate the Makefile in arch/arm/cpu/armv7/tegra2/. By > that I mean use COBJS-y instead of COBJS directly. This is more > forward looking to allow for features to be disabled in the future.
no problem with that.
> >> +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) >> +OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS)) >> + >> +all: $(obj).depend $(LIB) >> + >> +$(LIB): $(OBJS) >> + $(call cmd_link_o_target, $(OBJS)) >> + >> >> >> >> +######################################################################### >> + >> +# defines $(obj).depend target >> +include $(SRCTREE)/rules.mk >> + >> +sinclude $(obj).depend >> + >> >> >> >> +######################################################################### >> diff --git a/arch/arm/cpu/armv7/zynq/timer.c >> b/arch/arm/cpu/armv7/zynq/timer.c >> new file mode 100644 >> index 0000000..d79da97 >> --- /dev/null >> +++ b/arch/arm/cpu/armv7/zynq/timer.c >> @@ -0,0 +1,151 @@ >> +/* >> + * Copyright (C) 2012 Michal Simek monstr@monstr.eu >> + * Copyright (C) 2011-2012 Xilinx, Inc. All rights reserved. >> + * >> + * (C) Copyright 2008 >> + * Guennadi Liakhovetki, DENX Software Engineering, lg@denx.de >> + * >> + * (C) Copyright 2004 >> + * Philippe Robin, ARM Ltd. philippe.robin@arm.com >> + * >> + * (C) Copyright 2002-2004 >> + * Gary Jennejohn, DENX Software Engineering, gj@denx.de >> + * >> + * (C) Copyright 2003 >> + * Texas Instruments <www.ti.com> >> + * >> + * (C) Copyright 2002 >> + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> >> + * Marius Groeger mgroeger@sysgo.de >> + * >> + * (C) Copyright 2002 >> + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> >> + * Alex Zuepke azu@sysgo.de >> + * >> + * See file CREDITS for list of people who contributed to this >> + * project. >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License as >> + * published by the Free Software Foundation; either version 2 of >> + * the License, or (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See >> the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, >> + * MA 02111-1307 USA >> + */ >> + >> +#include <common.h> >> +#include <div64.h> >> +#include <asm/io.h> >> + >> +DECLARE_GLOBAL_DATA_PTR; >> + >> +struct scu_timer { >> + u32 load; /* Timer Load Register */ >> + u32 counter; /* Timer Counter Register */ >> + u32 control; /* Timer Control Register */ >> +}; > > > > > You are using the timer in the ARM Cortex A9 core. This is not > Zynq-specific in any way. It should be made available in a different > place. Probably in arch/arm/lib. It should be stripped on any "XSCU" > references. Use ARM names instead.
Based on this I can't see the reason why XSCU should be stripped.
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0407f/BABDEAG...
It is SCU private timer. Agree that we can remove X prefix.
I don't think that arch/arm/lib is the proper location for that. I am not arm specialist to say that this timer is available in all arm families to be in lib folder.
I'm not sure that it is available on more than armv7 (like arch/arm/lib/cache-pl310.c). If it is only available in armv7, then it can go in arch/arm/cpu/armv7/.
For me was just safer to use it in zynq folder because I am not familiar with others ARMs.
- I see that other armv7 cpus uses own timer drivers.
That is true, but they seem to be using other timer hardware instead of the ARM core timers. For instance, if you were using the Zynq's Triple Timer Counter module instead, then it would make sense to have this in the zynq directory.
Yes, that's partially clear case if Microblaze doesn't want to use it.
Zynq can also use axi_timer. We have this driver in Microblaze folder.
This would be bad because it would assume some timer in the fabric.
This is driver sharing across architectures. IRC there was any discussion about moving drivers to generic location in past but this has never happen. (BTW: this driver can be used by xilinx ppc405 and ppc440)
I guess by "this driver" you mean axi_timer and not scu_timer.
yep
If Albert tends to move this driver to any general location I have no problem to do in spite of there is probably not any other armv7 close which will use it.
From my point of view if there is not other armv7 clone which will use it, make sense to keep this in zynq folder. And move it to generic location when any other armv7 wants to use.
This is exactly the opposite of what we should be doing. It is true that this refactoring has not been very wide-spread yet, but certainly for new things, the idea is to only put things into a less generic place if there is no reasonable chance that it will be reused outside of the more specific scope. If it is available in other places in hardware, make it generic. It's not as good to expect that every new board should have to search every other board dir in the hope that he finds a driver that he can reuse. He should be able to look in the arch (or even drivers/ if applicable) and complete the search sooner. Why force extra work to move it later. To the best of your ability, put it in the best place. If this driver is available for all cortex a9 based SoCs, then chances are every new one added from now on will use this instead of writing a new one, even if there are other timers available.
I agree with that. Have no problem to move this driver to arch/arm/cpu/armv7/ folder or even to drivers/timer if is what we should do. I am just not sure if this should be done before new DM model.
Marek: I believe you are going to change all timer drivers. Are you going to move all of them to generic location?
Thanks, Michal

Dear Michal Simek,
On 08/14/2012 08:19 PM, Joe Hershberger wrote:
Hi Michal,
[...] CUT!
Marek: I believe you are going to change all timer drivers. Are you going to move all of them to generic location?
I'm not moving timer drivers in the first wave, so hacking on them should be safe sail so far.
Thanks, Michal
Best regards, Marek Vasut

Add support for Xilinx Zynq board.
Signed-off-by: Michal Simek monstr@monstr.eu --- board/xilinx/zynq/Makefile | 57 +++++++++++++++++++++++++++++++++ board/xilinx/zynq/board.c | 64 +++++++++++++++++++++++++++++++++++++ board/xilinx/zynq/lowlevel_init.S | 27 +++++++++++++++ boards.cfg | 1 + 4 files changed, 149 insertions(+), 0 deletions(-) create mode 100644 board/xilinx/zynq/Makefile create mode 100644 board/xilinx/zynq/board.c create mode 100644 board/xilinx/zynq/lowlevel_init.S
diff --git a/board/xilinx/zynq/Makefile b/board/xilinx/zynq/Makefile new file mode 100644 index 0000000..373a8a7 --- /dev/null +++ b/board/xilinx/zynq/Makefile @@ -0,0 +1,57 @@ +# +# (C) Copyright 2000-2006 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk +ifneq ($(OBJTREE),$(SRCTREE)) +$(shell mkdir -p $(obj)../common) +endif + +LIB = $(obj)lib$(BOARD).o + +COBJS-y := board.o + +COBJS := $(sort $(COBJS-y)) + +SOBJS := lowlevel_init.o + +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) +SOBJS := $(addprefix $(obj),$(SOBJS)) + +$(LIB): $(obj).depend $(OBJS) $(SOBJS) + $(call cmd_link_o_target, $(OBJS) $(SOBJS)) + +clean: + rm -f $(SOBJS) $(OBJS) + +distclean: clean + rm -f $(LIB) core *.bak $(obj).depend + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/board/xilinx/zynq/board.c b/board/xilinx/zynq/board.c new file mode 100644 index 0000000..716fcfe --- /dev/null +++ b/board/xilinx/zynq/board.c @@ -0,0 +1,64 @@ +/* + * (C) Copyright 2012 Michal Simek monstr@monstr.eu + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <netdev.h> + +DECLARE_GLOBAL_DATA_PTR; + +int board_init(void) +{ + icache_enable(); + + return 0; +} + +int board_late_init(void) +{ + return 0; +} + +#ifdef CONFIG_CMD_NET +int board_eth_init(bd_t *bis) +{ + int ret = 0; + +#if defined(CONFIG_XILINX_GEM) && defined(CONFIG_XPSS_GEM_BASEADDR0) + ret |= xilinx_gem_initialize(bis, CONFIG_XPSS_GEM_BASEADDR0); +#endif + + return ret; +} +#endif + +int dram_init(void) +{ + gd->ram_size = CONFIG_SYS_SDRAM_SIZE; + + return 0; +} + +void reset_cpu(ulong addr) +{ + while (1) + ; +} diff --git a/board/xilinx/zynq/lowlevel_init.S b/board/xilinx/zynq/lowlevel_init.S new file mode 100644 index 0000000..642eb18 --- /dev/null +++ b/board/xilinx/zynq/lowlevel_init.S @@ -0,0 +1,27 @@ +/* + * (C) Copyright 2012 Michal Simek monstr@monstr.eu + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <config.h> +#include <linux/linkage.h> + +ENTRY(lowlevel_init) + mov pc, lr +ENDPROC(lowlevel_init) diff --git a/boards.cfg b/boards.cfg index fdb84ad..aeca912 100644 --- a/boards.cfg +++ b/boards.cfg @@ -261,6 +261,7 @@ seaboard arm armv7 seaboard nvidia ventana arm armv7 ventana nvidia tegra2 whistler arm armv7 whistler nvidia tegra2 u8500_href arm armv7 u8500 st-ericsson u8500 +zynq arm armv7 zynq xilinx zynq actux1_4_16 arm ixp actux1 - - actux1:FLASH2X2 actux1_4_32 arm ixp actux1 - - actux1:FLASH2X2,RAM_32MB actux1_8_16 arm ixp actux1 - - actux1:FLASH1X8

On 08/14/2012 01:42 PM, Michal Simek wrote:
Add support for Xilinx Zynq board.
Signed-off-by: Michal Simek monstr@monstr.eu
board/xilinx/zynq/Makefile | 57 +++++++++++++++++++++++++++++++++ board/xilinx/zynq/board.c | 64 +++++++++++++++++++++++++++++++++++++ board/xilinx/zynq/lowlevel_init.S | 27 +++++++++++++++ boards.cfg | 1 + 4 files changed, 149 insertions(+), 0 deletions(-) create mode 100644 board/xilinx/zynq/Makefile create mode 100644 board/xilinx/zynq/board.c create mode 100644 board/xilinx/zynq/lowlevel_init.S
oops. I forget to add also config file. Will send v2 patch.
Thanks, Michal
participants (4)
-
Joe Hershberger
-
John Williams
-
Marek Vasut
-
Michal Simek