diff --git a/driver/Makefile b/driver/Makefile new file mode 100644 index 0000000..6450a81 --- /dev/null +++ b/driver/Makefile @@ -0,0 +1,11 @@ +ifneq ($(KERNELRELEASE),) +obj-m := at91.o +else +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +default: + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules +endif + +modules: at91.o \ No newline at end of file diff --git a/driver/at91.c b/driver/at91.c new file mode 100644 index 0000000..b864665 --- /dev/null +++ b/driver/at91.c @@ -0,0 +1,363 @@ +/* + * at91 boot program driver + * + * Copyright (C) 2005 Erik Gilling + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * This driver is based on drivers/usb/usb-skeleton.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Define these values to match your devices */ +#define USB_VENDOR_ID_ATMEL 0x03eb +#define USB_DEVICE_ID_SAM7BOOT 0x6124 + +/* table of devices that work with this driver */ +static struct usb_device_id at91_table [] = { + { USB_DEVICE( USB_VENDOR_ID_ATMEL, USB_DEVICE_ID_SAM7BOOT ) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, at91_table); + + +/* Get a minor range for your devices from the usb maintainer */ +#define USB_AT91_MINOR_BASE 192 + +/* Structure to hold all of our device specific stuff */ +struct usb_at91 { + struct usb_device * udev; /* the usb device for this device */ + struct usb_interface * interface; /* the interface for this device */ + unsigned char * bulk_in_buffer; /* the buffer to receive data */ + size_t bulk_in_size; /* the size of the receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + struct kref kref; +}; +#define to_at91_dev(d) container_of(d, struct usb_at91, kref) + +static struct usb_driver at91_driver; + +static void at91_delete(struct kref *kref) +{ + struct usb_at91 *dev = to_at91_dev(kref); + + usb_put_dev(dev->udev); + kfree (dev->bulk_in_buffer); + kfree (dev); +} + +static int at91_open(struct inode *inode, struct file *file) +{ + struct usb_at91 *dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + + subminor = iminor(inode); + + interface = usb_find_interface(&at91_driver, subminor); + if (!interface) { + err ("%s - error, can't find device for minor %d", + __FUNCTION__, subminor); + retval = -ENODEV; + goto exit; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto exit; + } + + /* increment our usage count for the device */ + kref_get(&dev->kref); + + /* save our object in the file's private structure */ + file->private_data = dev; + +exit: + return retval; +} + +static int at91_release(struct inode *inode, struct file *file) +{ + struct usb_at91 *dev; + + dev = (struct usb_at91 *)file->private_data; + if (dev == NULL) + return -ENODEV; + + /* decrement the count on our device */ + kref_put(&dev->kref, at91_delete); + return 0; +} + +static ssize_t at91_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usb_at91 *dev; + int retval = 0; + int bytes_read; + + dev = (struct usb_at91 *)file->private_data; + + /* do a blocking bulk read to get data from the device */ + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + dev->bulk_in_buffer, + min(dev->bulk_in_size, count), + &bytes_read, 10000); + + /* if the read was successful, copy the data to userspace */ + if (!retval) { + if (copy_to_user(buffer, dev->bulk_in_buffer, bytes_read)) + retval = -EFAULT; + else + retval = bytes_read; + } + + return retval; +} + +static void at91_write_bulk_callback(struct urb *urb, struct pt_regs *regs) +{ + struct usb_at91 *dev; + + dev = (struct usb_at91 *)urb->context; + + /* sync/async unlink faults aren't errors */ + if (urb->status && + !(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -ESHUTDOWN)) { + dbg("%s - nonzero write bulk status received: %d", + __FUNCTION__, urb->status); + } + + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); +} + +static ssize_t at91_write(struct file *file, const char *user_buffer, size_t count, loff_t *ppos) +{ + struct usb_at91 *dev; + int retval = 0; + struct urb *urb = NULL; + char *buf = NULL; + + dev = (struct usb_at91 *)file->private_data; + + /* verify that we actually have some data to write */ + if (count == 0) + goto exit; + + /* create a urb, and a buffer for it, and copy the data to the urb */ + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + retval = -ENOMEM; + goto error; + } + + buf = usb_buffer_alloc(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + goto error; + } + + if (copy_from_user(buf, user_buffer, count)) { + retval = -EFAULT; + goto error; + } + + /* initialize the urb properly */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + buf, count, at91_write_bulk_callback, dev); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + /* send the data out the bulk port */ + retval = usb_submit_urb(urb, GFP_KERNEL); + if (retval) { + err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); + goto error; + } + + /* release our reference to this urb, the USB core will eventually free it entirely */ + usb_free_urb(urb); + +exit: + return count; + +error: + usb_buffer_free(dev->udev, count, buf, urb->transfer_dma); + usb_free_urb(urb); + return retval; +} + +static struct file_operations at91_fops = { + .owner = THIS_MODULE, + .read = at91_read, + .write = at91_write, + .open = at91_open, + .release = at91_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver at91_class = { + .name = "usb/at91_%d", + .fops = &at91_fops, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15) + .mode = S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, +#endif + .minor_base = USB_AT91_MINOR_BASE, +}; + +static int at91_probe(struct usb_interface *interface, const struct usb_device_id *id) +{ + struct usb_at91 *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + err("Out of memory"); + goto error; + } + memset(dev, 0x00, sizeof(*dev)); + kref_init(&dev->kref); + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!dev->bulk_in_buffer) { + err("Could not allocate bulk_in_buffer"); + goto error; + } + } + + if (!dev->bulk_out_endpointAddr && + ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_OUT) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk out endpoint */ + dev->bulk_out_endpointAddr = endpoint->bEndpointAddress; + } + } + if (!(dev->bulk_in_endpointAddr && dev->bulk_out_endpointAddr)) { + err("Could not find both bulk-in and bulk-out endpoints"); + goto error; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* we can register the device now, as it is ready */ + retval = usb_register_dev(interface, &at91_class); + if (retval) { + /* something prevented us from registering this driver */ + err("Not able to get a minor for this device."); + usb_set_intfdata(interface, NULL); + goto error; + } + + /* let the user know what node this device is now attached to */ + info("USB At91 device now attached to AT91-%d", interface->minor); + return 0; + +error: + if (dev) + kref_put(&dev->kref, at91_delete); + return retval; +} + +static void at91_disconnect(struct usb_interface *interface) +{ + struct usb_at91 *dev; + int minor = interface->minor; + + /* prevent at91_open() from racing at91_disconnect() */ + lock_kernel(); + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + /* give back our minor */ + usb_deregister_dev(interface, &at91_class); + + unlock_kernel(); + + /* decrement our usage count */ + kref_put(&dev->kref, at91_delete); + + info("USB At91eton #%d now disconnected", minor); +} + +static struct usb_driver at91_driver = { + .owner = THIS_MODULE, + .name = "at91", + .probe = at91_probe, + .disconnect = at91_disconnect, + .id_table = at91_table, +}; + +static int __init usb_at91_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&at91_driver); + if (result) + err("usb_register failed. Error number %d", result); + + return result; +} + +static void __exit usb_at91_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&at91_driver); +} + +module_init (usb_at91_init); +module_exit (usb_at91_exit); + +MODULE_LICENSE("GPL"); diff --git a/loader/at91.h b/loader/at91.h new file mode 100644 index 0000000..5f38a53 --- /dev/null +++ b/loader/at91.h @@ -0,0 +1,61 @@ +/* + * loader/at91.h + * + * Copyright (C) 2006 Erik Gilling, all rights reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#define AT91_FLASH_ADDR ((uint32_t *) 0x100000) + +typedef unsigned int uint32_t; +typedef signed int int32_t; + +typedef unsigned short uint16_t; +typedef signed short int16_t; + +typedef unsigned char uint8_t; +typedef signed char int8_t; + +typedef volatile uint32_t arm_reg_t; + +typedef struct { + arm_reg_t rcr; /* 00 - Remap Control Register */ + arm_reg_t asr; /* 04 - Abort Status Rebister */ + arm_reg_t aasr; /* 08 - Abort Address Status Register */ + arm_reg_t res[21]; /* 0C..5C - Reserved */ + arm_reg_t fmr; /* 60 - Flash Mode Register */ + arm_reg_t fcr; /* 64 - Flash Command Register */ + arm_reg_t fsr; /* 68 - Flash Status Register */ +} at91_mc_t; + +extern volatile at91_mc_t mc; + +#define AT91_MC_FMR_FRDY (1<<0) +#define AT91_MC_FMR_LOCKE (1<<2) +#define AT91_MC_FMR_PROGE (1<<3) +#define AT91_MC_FMR_NEBP (1<<7) +#define AT91_MC_FMR_WAIT_STATE_1_2 (0<<8) +#define AT91_MC_FMR_WAIT_STATE_2_3 (1<<8) +#define AT91_MC_FMR_WAIT_STATE_3_4 (2<<8) +#define AT91_MC_FMR_WAIT_STATE_4_4 (3<<8) +#define AT91_MC_FMR_FMCN( x ) (((x)&0xff) << 16) + + +#define AT91_MC_FCR_FCMD_NOP 0x0 +#define AT91_MC_FCR_FCMD_WP 0x1 +#define AT91_MC_FCR_FCMD_SLB 0x2 +#define AT91_MC_FCR_FCMD_WPL 0x3 +#define AT91_MC_FCR_FCMD_CLB 0x4 +#define AT91_MC_FCR_FCMD_EA 0x8 +#define AT91_MC_FCR_FCMD_SGPB 0xb +#define AT91_MC_FCR_FCMD_CGPB 0xd +#define AT91_MC_FCR_FCMD_SSB 0xf + +#define AT91_MC_FCR_PAGEN( x ) (((x)&0x3ff)<<8) + +#define AT91_MC_FCR_KEY (0x5A << 24) + diff --git a/loader/bin2c.c b/loader/bin2c.c new file mode 100644 index 0000000..9e16991 --- /dev/null +++ b/loader/bin2c.c @@ -0,0 +1,30 @@ +#include + +int main( int argc, char *argv[] ) +{ + char *data_name = "data"; + int c; + int i=0; + + if( argc == 2 ) { + data_name = argv[1]; + } + + printf( "uint8_t %s[] = {\n", data_name ); + + while( (c = getchar()) >= 0 ) { + if( i== 0 ) { + printf( "\t" ); + } else if( i%8 == 0 ) { + printf( ",\n\t" ); + } else { + printf( ", " ); + } + i++; + printf( "0x%02x", c ); + } + + printf( "\n};\n" ); + + return 0; +} diff --git a/loader/crt0.S b/loader/crt0.S new file mode 100644 index 0000000..bb0a94f --- /dev/null +++ b/loader/crt0.S @@ -0,0 +1,28 @@ +// +// loader/crt0.S +// +// Copyright (C) 2006 Erik Gilling, all rights reserved +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation, version 2. +// +// + .EQU STACK, 0x201C00 + + .code 32 + .align 2 + + .global _entry + .func _entry +_entry: + // init stack + ldr sp, =STACK + + // save lr + stmfd sp!, {lr} + + bl main + + ldmia sp!, {r0} + bx r0 diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 0000000..2dc67ff --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,48 @@ +/* + * loader/loader.c + * + * Copyright (C) 2006 Erik Gilling, all rights reserved + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include "at91.h" + +// SAM-BA apears to write the following data to 0x201400 +// +// 0x00: data0 +// 0x01: data1 +// ... +// PAGE_SIZE-1: last data byte +// PAGE_SIZE : page number to write + + +#define SAMBA_PAGE_BUFF ((uint32_t *) 0x201400) + +int main( void ) +{ + int i; + uint32_t *page_buff = SAMBA_PAGE_BUFF; + // keep this in units of 32 bit words + int page_num = *(page_buff+PAGE_SIZE/4); + int page_offset = page_num * PAGE_SIZE/4; + + // wait for mc to be ready + while( !(mc.fsr & AT91_MC_FMR_FRDY) ) { } + + // write samba page buffer to flash buffer + for( i=0 ; i < (PAGE_SIZE/4) ; i++ ) { + AT91_FLASH_ADDR[ page_offset + i ] = page_buff[i]; + } + + // write page + mc.fcr = AT91_MC_FCR_FCMD_WP | AT91_MC_FCR_PAGEN(page_num) | + AT91_MC_FCR_KEY; + + // dont wait for mc to be ready here so that flash ops can stack + + return 0; +} diff --git a/loader/loader.lds b/loader/loader.lds new file mode 100644 index 0000000..32fcf50 --- /dev/null +++ b/loader/loader.lds @@ -0,0 +1,128 @@ +/***********************************************************************/ +/* */ +/* ROM.ld: Linker Script File */ +/* */ +/***********************************************************************/ +ENTRY(_entry) + +MEMORY +{ + LOADER(rwx) : ORIGIN = 0x00201600, LENGTH = 0xEA00 +} + +/* Section Definitions */ +SECTIONS +{ + + /* first section is .text which is used for code */ + .text : + { + _text = .; + PROVIDE (text = _text); + *crt0.o (.text) /* Startup code */ + *(.text .text.*) /* remaining code */ + *(.gnu.linkonce.t.*) + *(.glue_7) + *(.glue_7t) + *(.gcc_except_table) + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.gnu.linkonce.r.*) + } > LOADER + + . = ALIGN(4); + + _etext = . ; + PROVIDE (etext = .); + + /* .data section which is used for initialized data */ + .data : AT (_etext) + { + _data = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) /* mt 4/2005 */ + } > LOADER + + . = ALIGN(4); + _edata = . ; + PROVIDE (edata = .); + + /* .bss section which is used for uninitialized data */ + .bss (NOLOAD) : + { + __bss_start = . ; + __bss_start__ = . ; + *(.bss) + *(.gnu.linkonce.b*) + *(COMMON) + . = ALIGN(4); + } > LOADER + + . = ALIGN(4); + __bss_end__ = . ; + PROVIDE (__bss_end = .); + + + + _end = . ; + PROVIDE (end = .); + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} + + +PROVIDE(tc = 0xFFFA0000); +PROVIDE(udp = 0xFFFB0000); +PROVIDE(twi = 0xFFFB8000); +PROVIDE(usart0 = 0xFFFC0000); +PROVIDE(usart1 = 0xFFFC4000); +PROVIDE(pwmc = 0xFFFCC000); +PROVIDE(ssc = 0xFFFD4000); +PROVIDE(adc = 0xFFFD8000); +PROVIDE(spi = 0xFFFE0000); +PROVIDE(aic = 0xFFFFF000); +PROVIDE(dbgu = 0xFFFFF200); +PROVIDE(pioa = 0xFFFFF400); +PROVIDE(pmc = 0xFFFFFC00); +PROVIDE(rstc = 0xFFFFFD00); +PROVIDE(rtt = 0xFFFFFD20); +PROVIDE(pit = 0xFFFFFD30); +PROVIDE(wdt = 0xFFFFFD40); +PROVIDE(vreg = 0xFFFFFD60); +PROVIDE(mc = 0xFFFFFF00); + +