Files
openocd/src/flash/nor/at91sam4.c
T
Tim Newsome 2a69f1bd2f From upstream (#286)
* flash/nor: Add support for TI CC26xx/CC13xx flash

Added cc26xx flash driver to support the TI CC26xx and CC13xx
microcontrollers. Driver is capable of determining which MCU
is connected and configures itself accordingly. Added config
files for four specific variants: CC26x0, CC13x0, CC26x2, and
CC13x2.

Note that the flash loader code is based on the sources used
to support flash in Code Composer Studio and Uniflash from TI.

Removed cc26xx.cfg file made obsolete by this patch.

Change-Id: Ie2b0f74f8af7517a9184704b839677d1c9787862
Signed-off-by: Edward Fewell <efewell@ti.com>
Reviewed-on: http://openocd.zylin.com/4358
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Fredrik Hederstierna <fredrik@hederstierna.com>

* flash/nor/nrf5: remove is_erased setting and autoerase before write

Cached flash erase state in sectors[].is_erased is not reliable as running
target can change the flash.

Autoerase was issued before flash write on condition is_erased != 1
Remove autoerase completely as it is a quite non-standard feature.

Change-Id: I19bef459e6afdc4c5fcaa2ccd194cf05be8a42b6
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4400
Tested-by: jenkins

* src/flash/tms470: remove testing of sectors[].is_erased state

The erase check routine checked sectors only if is_erased != 1

Check sector unconditionally.

While on it fix clang static analyzer warnings.

Change-Id: I9988615fd8530c55a9b0c54b1900f89b550345e9
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4401
Tested-by: jenkins

* tcl/target/stm32f7x: configure faster system clock in reset-init

STM32F7xx devices need faster clock for flash programming
over JTAG transport. Using reset default 16 MHz clock
resulted in lot of DAP WAITs and substantial decrease
of flashing performance.

Adapted to the restructured dap support
(see 2231da8ec4).

Change-Id: Ida6915331dd924c9c0d08822fd94c04ad408cdc5
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4464
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>

* flash/nor/psoc5lp: fix compile issue on GCC 8.1.0

Issue already identified by Alex https://sourceforge.net/u/alexbour/
in ticket #191 https://sourceforge.net/p/openocd/tickets/191/

	src/flash/nor/psoc5lp.c:237:2: error: ‘strncpy’ output
	truncated before terminating nul copying 2 bytes from a
	string of the same length [-Werror=stringop-truncation]

Fix it by assigning the value to the array elements.

Change-Id: I22468e5700efa64ea48ae8cdec930c48b4a7d8fb
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4563
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm: Add PLD command to ARM disassembler.

Updates the ARM disassembler to handle PLD (PreLoad Data) commands.
Previously handled by printing a TODO message. There are three forms of
the command: literal, register, and immediate. Simply decode based off
of the A1 encoding for the instructions in the ARM ARM. Also fixes mask
to handle PLDW commands.

Change-Id: I63bf97f16af254e838462c7cfac80f6c4681c556
Signed-off-by: James Marshall <jcmarsh@gwmail.gwu.edu>
Reviewed-on: http://openocd.zylin.com/4348
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* mips_m4k.c: Fix build with --disable-target64

Replace PRIx64 with TARGET_PRIxADDR to avoid build problems
when --disable-target64 is used during configure.

Change-Id: I054a27a491e86c42c9386a0488194320b808ba96
Signed-off-by: Liviu Ionescu <ilg@livius.net>
Reviewed-on: http://openocd.zylin.com/4566
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Tim Newsome <tim@sifive.com>

* target/arm_adi_v5: sync CSW and TAR cache on apreg write

When using apreg to change AP registers CSW or TAR we get internal
cached value not valid anymore.

Reuse the setup functions for CSW and TAR to write them.
Invalidate the cached value before the call to force the write, thus
keeping original apreg behaviour.

Change-Id: Ib14fafd5e584345de94f2e983de55406c588ac1c
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4565
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm_adi_v5: keep CSW and TAR cache updated

The call to dap_queue_ap_write() can fail and the value in CSW and
TAR becomes unknown.

Invalidate the OpenOCD cache if dap_queue_ap_write() fails.

Change-Id: Id6ec370b4c5ad07e454464780c1a1c8ae34ac870
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4564
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/target: Add Renesas R-Car R8A7794 E2 target

Add configuration for the Renesas R-Car R8A7794 E2 target.
This is an SoC with two Cortex A7 ARMv7a cores, both A7
cores are supported.

Change-Id: Ic1c81840e3bfcef8ee1de5acedffae5c83612a5e
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4531
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7790 H2 Stout board

Add configuration for the Renesas R-Car R8A7790 H2
based Stout ADAS board.

Change-Id: Ib880b5d2e1fab5c8c0bc0dbcedcdce8055463fe2
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4497
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7791 M2W Porter board

Add configuration for the Renesas R-Car R8A7791 M2W
based Porter evaluation board.

Change-Id: Iaadb18f29748f890ebb68519ea9ddbd18e7649af
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4498
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Add Renesas R-Car R8A7794 E2 Silk board

Add configuration for the Renesas R-Car R8A7794 E2
based Silk evaluation board.

Change-Id: I504b5630b1a2791ed6967c6c2af8851ceef9723f
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
---
NOTE: This requires SW7[1] in position 1 (default is 0)
Reviewed-on: http://openocd.zylin.com/4532
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/board: Factor out common R-Car Gen2 code

Factor out the code shared by all R-Car Gen2 boards into a single
file to get rid of the duplication.

Change-Id: I70b302c2e71f4e6fdccb2817dd65a5493bb393d8
Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Reviewed-on: http://openocd.zylin.com/4533
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* jtag/drivers/cmsis-dap: fix connect in cmsis_dap_swd_switch_seq()

The proc cmsis_dap_swd_switch_seq() is part of the SWD API for
this interface driver. It is valid only when the interface is
used in SWD mode.
In this proc there is the need to call, in sequence, first
cmsis_dap_cmd_DAP_Disconnect() then cmsis_dap_cmd_DAP_Connect().
The latter call requires the connection mode as parameter, that
inside cmsis_dap_swd_switch_seq() can only be CONNECT_SWD.

The current implementation is not correct and in some cases can
pass mode CONNECT_JTAG. Moreover, JTAG is optional in CMSIS-DAP
and passing mode CONNECT_JTAG triggers an error with SWD-only
interfaces.

Use mode CONNECT_SWD in SWD specific cmsis_dap_swd_switch_seq().

Change-Id: Ib455bf5b69cb2a2d146a6c8875387b00c27a5690
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4571
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: return error if breakpoint address is out of range

If the "Flash Patch and Breakpoint" unit is rev.1 then it can only
accept breakpoint addresses below 0x1FFFFFFF.
Detailed info in "ARM v7-M Architecture Reference Manual", DDI0403E
at chapter "C1.11 Flash Patch and Breakpoint unit".

Print a message and return error if the address of hardware
breakpoint cannot be handled by the breakpoint unit.

Change-Id: I95c92b1f058f0dfc568bf03015f99e439b27c59b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4535
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* flash/nor/stm32: Report errors in wait_status_busy

Flash operation errors that occur during algorithm programming are
reported via the algorithm return value. However, Flash operation
errors that occur during non-algorithm work (erasing, programming
without a work area, programming the last non-multiple-of-32-bytes on
an H7, etc.) generally end with a call to stm32x_wait_status_busy,
which reads the status register and clears the error flags but fails
to actually report that something went wrong should an error flag
(other than WRPERR) be set. Return an error status from
stm32x_wait_status_busy in those cases. Correct a log message
accordingly.

Change-Id: I09369ea5f924fe58833aec1f45e52320ab4aaf43
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4519
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/stm32: Eliminate working area leak

On a specific early-return path, an allocated working area was not
freed. Free it.

Change-Id: I7c8fe51ff475f191624086996be1c77251780b77
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4520
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/stm32h7: Fix incorrect comment

The name of the bit according to the reference manual is inconsistency
error, not increment error.

Change-Id: Ie3b73c0312db586e35519e03fd1a5cb225673d97
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4521
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* target: fix 'bp' command help message

"asid" and "length" are separate arguments of the command.
Put space between them.

Change-Id: I36cfc1e3a01caafef4fc3b26972a0cc192b0b963
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4511
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* Add ARM v8 AArch64 semihosting support

This patch implements semihosting support for AArch64. This picks
code from previously submitted AArch64 semihosting support patch
and rebases on top of reworked semihosting code. Tested in AArch64
mode on a Lemaker Hikey Board with NewLib and GDB.

Change-Id: I228a38f1de24f79e49ba99d8514d822a28c2950b
Signed-off-by: Omair Javaid <omair.javaid@linaro.org>
Reviewed-on: http://openocd.zylin.com/4537
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* GDB fileIO stdout support

This patch fixes gdb fileio support to allow gdb console to be used as stdout.

Now we can do something like
gdb <inferior file>

(gdb) tar ext :3333
(gdb) load
(gdb) monitor arm semihosting enable
(gdb) monitor arm semihosting_fileio enable
(gdb) continue

Here: Output from inferior using puts, printf etc will be routed to gdb console.

Change-Id: I9cb0dddda1de58038c84f5b035c38229828cd744
Signed-off-by: Omair Javaid <omair.javaid@linaro.org>
Reviewed-on: http://openocd.zylin.com/4538
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target: armv8: Avoid semihosting segfault on halt

Avoid a NULL pointer dereference when halting an aarch64 core.

Change-Id: I333d40475ab26e2f0dca5c27302a5fa4d817a12f
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4593
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl: target: Add NXP LS1012A config

As seen on the FRDM-LS1012A board.

Change-Id: Ifc9074b3f7535167b9ded5f544501ec2879f5db7
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4594
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl: board: Add NXP Freedom FRDM-LS1012A config

An update for the K20 CMSIS-DAP firmware can be found here:
https://community.nxp.com/thread/387080?commentID=840141#comment-840141

Change-Id: I149d7f8610aa56daf1aeb95f14ee1bf88f7cb647
Signed-off-by: Andreas Färber <afaerber@suse.de>
Reviewed-on: http://openocd.zylin.com/4595
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* gdb_server: only trigger once the event gdb-detach at gdb quit

When GDB quits (e.g. with "quit" command) we first execute
gdb_detach() to reply "OK" then, at GDB disconnect (either TCP
or pipe connection type), we execute gdb_connection_closed().
In case GDB is killed or it crashes, OpenOCD only executes the
latter when detects the disconnection.
Both gdb_detach() and gdb_connection_closed() trigger the event
TARGET_EVENT_GDB_DETACH thus getting it triggered twice on clean
GDB quit.

Do not trigger the event TARGET_EVENT_GDB_DETACH in gdb_detach()
and let only gdb_connection_closed() to handle it.

Change-Id: Iacf035c855b8b3e2239c1c0e259c279688b418ee
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4585
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* gdb_server: set current_target from connection's one

In a multi-target environment we are supposed to have a single
gdb server for each target (or for each group of targets within
a SMP node).
By default, the gdb attached to a server sends its command to
the target (or to the SMP node targets) linked to that server.

This is working fine for the normal gdb commands, but it is
broken for the native OpenOCD commands executed through gdb
"monitor" command. In the latter case, gdb "monitor" commands
will be executed on the current target of OpenOCD configuration
script (that is either the last target created or the target
specified in a "targets" command).

Fixed in gdb_new_connection() by replacing the current target
in the connection's copy of command context.

Change-Id: If7c8f2dce4a3138f0907d3000dd0b15e670cfa80
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4586
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/image: make i/j unsigned to avoid ubsan runtime error

	src/target/image.c:1055:15: runtime error: left shift of 128 by 24 places cannot be represented in type 'int'

Change-Id: I322fd391cf3f242beffc8a274824763c8c5e69a4
Signed-off-by: Cody Schafer <openocd@codyps.com>
Reviewed-on: http://openocd.zylin.com/4584
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/stm32f7x: Clear stuck HSE clock with CSS

Change-Id: Ica0025ea465910dd664ab546b66f4f25b271f1f5
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4570
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* psoc5lp: fix erase check, add free_driver_priv

psoc5lp_erase_check() was not properly adapted to the new
armv7m_blank_check_memory() in the hot fix 53376dbbed
This change fixes handling of num_sectors in dependecy of ecc_enabled.
Also add comments how ecc_enabled influences num_sectors.

Add pointer to default_flash_free_driver_priv() to all psoc5lp flash
drivers to keep valgrind happy.

Change-Id: Ie1806538becd364fe0efb7a414f0fe6a84b2055b
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/4569
Tested-by: jenkins

* target: atmel samd10 xplained mini

cortex m0+ on a tiny board, with an mEDBG (CMSIS-DAP) debug interface.

Change-Id: Iaedfab578b4eb4aa2d923bd80f220f59b34e6ef9
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3402
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/board: add SAMD11 Xplained Pro evaluation board

Change-Id: Id996c4de6dc9f25f71424017bf07689fea7bd3af
Signed-off-by: Peter Lawrence <majbthrd@gmail.com>
Reviewed-on: http://openocd.zylin.com/4507
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* Adds SAMD11D14AU flash support.

Corrects names of SAMD11D14AM and SAMD11D14ASS per datasheet.

Change-Id: I8beb15d5376966a4f8d7de76bfb2cbda2db440dc
Signed-off-by: Christopher Hoover <ch@murgatroid.com>
Reviewed-on: http://openocd.zylin.com/4597
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nds32: Avoid detected JTAG clock

AICE2 doesn't support scan for the maximum clock frequency of
JTAG chain. It will cause USB command timeout.

Change-Id: I41d1e3be387b6ed5a4dd0be663385a5f053fbcf9
Signed-off-by: Hellosun Wu <wujiheng.tw@gmail.com>
Reviewed-on: http://openocd.zylin.com/4292
Tested-by: jenkins
Reviewed-by: Hsiangkai Wang <hsiangkai@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/tcl: Distinguish between sectors and blocks in status messages

Use the right word in flash protect command status messages based on
whether the target bank defines num_prot_blocks. Minor message style
tidy-up.

Change-Id: I5f40fb5627422536ce737f242fbf80feafe7a1fc
Signed-off-by: Dominik Peklo <dom.peklo@gmail.com>
Reviewed-on: http://openocd.zylin.com/4573
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Christopher Head <chead@zaber.com>

* drivers: cmsis-dap: pull up common connect code

Just a minor deduplication

Change-Id: Idd256883e5f6d4bd4dcc18462dd5468991f507b3
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3403
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* drivers: cmsis-dap: Print version info when available

No need to wait until after connecting, might help diagnose part information by
printing earlier.

Change-Id: I51eb0d584be306baa811fbeb1ad6a604773e602c
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3404
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor: add support for TI MSP432 devices

Added msp432 flash driver to support the TI MSP432P4x and
MSP432E4x microcontrollers. Implemented the flash algo
helper as used in the TI debug and flash tools. This
implemention supports the MSP432E4, Falcon, and Falcon 2M
variants. The flash driver automatically detects the
connected variant and configures itself appropriately.
Added command to mass erase device for consistency with
TI tools and added command to unlock the protected BSL
region.

Tested using MSP432E401Y, MSP432P401R, and MSP432P4111
LaunchPads.
Tested with embedded XDS110 debug probe in CMSIS-DAP
mode and with external SEGGER J-Link probe.

Removed ti_msp432p4xx.cfg file made obsolete by this
patch.
Change-Id: I3b29d39ccc492524ef2c4a1733f7f9942c2684c0
Signed-off-by: Edward Fewell <efewell@ti.com>
Reviewed-on: http://openocd.zylin.com/4153
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/at91sam4: fix sam4sa16c flash banks and its gpnvms count

There was already a github fork that had this fixed, but as we try
to use the latest, non-modified version of all software we use,
I would like to have this fix in the next releases of OpenOCD so
that if people uses $packagemanager, they will not have issues flashing
the last part of the flash of sam4sa16c chips.

Additionally, I've added some more logging related to the flash
bank that was used, and the chip ID that was detected.

Change-Id: I7ea5970105906e4560b727e46222ae9a91e41559
Signed-off-by: Erwin Oegema <blablaechthema@hotmail.com>
Reviewed-on: http://openocd.zylin.com/4599
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Tested-by: jenkins

* flash/nor/stm32lx: Add revision 'V' for STM32L1xx Cat.3 devices

Change-Id: Ic92b0fb5b738af3bec79ae335876aa9e26f5f4cd
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4600
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* Avoid null target->semihosting references.

The new common semihosting code introduced a bug,
in certain conditions target->semihosting was
used without semihosting being initialised.

The solution was to explicitly test for
target->semihosting before dereferencing it.

Change-Id: I4c83e596140c68fe4ab32e586e51f7e981a40798
Signed-off-by: Liviu Ionescu <ilg@livius.net>
Reviewed-on: http://openocd.zylin.com/4603
Tested-by: jenkins
Reviewed-by: Jonathan Larmour <jifl@eCosCentric.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nrf5: Add HWID 0x139 (52832 rev E0)

Change-Id: I71b7471ccfcb8fcc6de30da57ce4165c7fb1f73f
Signed-off-by: James Jacobsson <slowcoder@gmail.com>
Reviewed-on: http://openocd.zylin.com/4604
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target: Fix segfault for 'mem2array'

Call 'mem2array' without arguments to reproduce the segmentation
fault.

Change-Id: I02bf46cc8bd317abbb721a8c75d7cbfac99eb34e
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4534
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Christopher Head <chead@zaber.com>

* target/armv7m_trace: Fix typo in enum

Change-Id: I6364ee5011ef2d55c59674e3b97504a285de0cb2
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/3904
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/armv7m_trace: Use prefix for enums

Change-Id: I3f199e6053146a1094d96b98ea174b41bb021599
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/3905
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/aarch64: Call aarch64_init_debug_access() earlier in aarch64_deassert_reset()

On Renesas R-Car, calling 'reset halt' and 'reset init' always made DAP inaccessible. Calling 'reset' and 'halt' seperatly worked fine.
The only differences seems to be the point in time when aarch64_init_debug_access() is called. This patch aligns the behaviour.

Change-Id: I2296c65e48414a7d9846f12a395e5eca315b49ca
Signed-off-by: Dennis Ostermann <dennis.ostermann@renesas.com>
Reviewed-on: http://openocd.zylin.com/4607
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* server: Improve signal handling under Linux

Commit 5087a955 added custom signal handlers for the openocd
server process.

Before this commit, when openocd is run as a background process
having the same controlling terminal as gdb, Control-C would be
handled by gdb to stop target execution and return to the gdb prompt.

However, after commit 5087a955, the SIGINT caused by pressing
Control-C also terminates openocd, effectively crashing the
debugging session.  The only way to avoid this is run openocd in
a different controling terminal or to detach openocd from its
controlling terminal,
thus losing all job control for the openocd process.

This patch improves the server's handling of POSIX signals:
1) Keyboard generated signals (INT and QUIT) are ignored
   when server process has is no controlling terminal.
2) SIGHUP and SIGPIPE are handled to ensure that .quit functions
   for each interface are called if user's logs out of X
   session or there is a network failure.

SIG_INT & SIG_QUIT still stop openocd
when it is running in the foreground.

Change-Id: I03ad645e62408fdaf4edc49a3550b89b287eda10
Signed-off-by: Brent Roman <genosensor@gmail.com>
Signed-off-by: Tomas Vanek <vanekt@fbl.cz>
Reviewed-on: http://openocd.zylin.com/3963
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>

* armv7a: read ttbcr and ttb0/1 at every entry in debug state

Commit bfc5c764df avoids reading
ttbcr and ttb0/1 at every virt2phys translation by caching them,
and it updates the cached values in armv7a_arch_state().
But the purpose of any (*arch_state)() method, thus including
armv7a_arch_state(), is to only print out and inform the user
about some architecture specific status.
Moreover, to reduce the verbosity during a GDB session, the
method (*arch_state)() is not executed anymore at debug state
entry (check use of target->verbose_halt_msg in src/openocd.c),
thus the state of translation table gets out-of-sync triggering
	Error: Address translation failure
or even using a wrong address in the memory R/W operation.

In addition, the commit above breaks the case of armv7r by
calling armv7a_read_ttbcr() unconditionally.

Fixed by moving in cortex_a_post_debug_entry() the call to
armv7a_read_ttbcr() on armv7a case only.
Remove the call to armv7a_read_ttbcr() in armv7a_identify_cache()
since it is (conditionally) called only in the same procedure
cortex_a_post_debug_entry().

Fixes: bfc5c764df ("armv7a: cache ttbcr and ttb0/1 on debug
state entry")
Change-Id: Ifc20eca190111832e339a01b7f85d28c1547c8ba
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4601
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Avoid dereferencing NULL pointer.

If a NULL pointer is passed, don't attempt to increment it.  This avoids
passing the now not-NULL pointer on and eventually segfaulting.  Also
remove some unnecessary temporary variables.

Change-Id: I268e225121aa283d59179bfae407ebf6959d3a4e
Signed-off-by: Darius Rad <darius@bluespec.com>
Reviewed-on: http://openocd.zylin.com/4550
Tested-by: jenkins
Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>

* Remove FSF mailing address.

Checkpatch complains about this (FSF_MAILING_ADDRESS).

Change-Id: Ib46a7704f9aed4ed16ce7733d43c58254a094149
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4559
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* drivers: cmsis_dap_usb: implement cmd JTAG_TMS

Simply add a wrapper around cmsis_dap_cmd_DAP_SWJ_Sequence()

Change-Id: Icf86f84b24e9fec56e2f9e155396aac34b0e06d2
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4517
Tested-by: jenkins
Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>

* arm_adi_v5: put SWJ-DP back to JTAG mode at exit

When SWD mode is used, current OpenOCD code left the SWJ-DP in
SWD mode at exit. Also, current code is unable to switch back the
SWJ-DP in JTAG at next run, thus a power cycle of both target and
interface is required in order to run OpenOCD in JTAG mode again.

Put the SWJ-DP back to JTAG mode before exit from OpenOCD.

Use switch_seq(SWD_TO_JTAG) instead of dap_to_jtag(), because the
latter is not implemented on some interfaces. This is aligned
with the use of switch_seq(JTAG_TO_SWD) in swd_connect().

Change-Id: I55d3faebe60d6402037ec39dd9700dc5f17c53b0
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4493
Tested-by: jenkins
Reviewed-by: Bohdan Tymkiv <bhdt@cypress.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Add RISC-V support.

This supports both 0.11 and 0.13 versions of the debug spec.

Support for `-rtos riscv` will come in a separate commit since it was
easy to separate out, and is likely to be more controversial.

Flash support for the SiFive boards will also come in a later commit.

Change-Id: I1d38fe669c2041b4e21a5c54a091594aac3e2190
Signed-off-by: Tim Newsome <tim@sifive.com>
Reviewed-on: http://openocd.zylin.com/4578
Tested-by: jenkins
Reviewed-by: Liviu Ionescu <ilg@livius.net>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* usb_blaster: Don't unnecessarily go through DR-/IR-Pause

There is no need to pass through DR-/IR-Pause after a scan if we want to
go to DR-/IR-Update. We just have to skip the first step of the path to
the end state because we already did that step when shifting the last
bit.

v2:
 - Fix comments as remarked in review of v1

Change-Id: I3c10f02794b2233f63d2150934e2768430873caa
Signed-off-by: Daniel Glöckner <daniel-gl@gmx.net>
Reviewed-on: http://openocd.zylin.com/4245
Tested-by: jenkins
Reviewed-by: Christopher Head <chead@zaber.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* cortex_a: fix virt2phys when mmu is disabled

When the MMU is not enabled on debug state entry, virt2phys cannot
perform a translation since it is unknown whether a valid MMU
configuration existed before. In this case, return the virtual
address as physical address.

Change-Id: I6f85a7a5dbc200be1a4b5badf10a1a717f1c79c0
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/4480
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* drivers: cmsis-dap: print serial if available

Helpful for sanity checking connections

Change-Id: Ife0d8b4e12d4c03685aac8115c9739a4c1e994fe
Signed-off-by: Karl Palsson <karlp@tweak.net.au>
Reviewed-on: http://openocd.zylin.com/3405
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: make a variable local

The vec_ids variable is not referenced anywhere other than the vector
catch command handler. Make it local to that function.

Change-Id: Ie5865e8f78698c19a09f0b9d58269ced1c9db440
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4606
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_a: fix compile error for uninitialized variable

Commit ad6c71e151 introduced the
variable "mmu_enabled" whose pointer is passed to cortex_a_mmu()
that initialises it.
This initialization is not visible to the compiler that issue
a compile error.
The same situation is common across the same file and the usual
workaround is to initialize it to zero; thus the same fix i
applied here.

Ticket: https://sourceforge.net/p/openocd/tickets/197/
Fixes: commit ad6c71e151 ("cortex_a: fix virt2phys when mmu is disabled")
Change-Id: I77dec41acdf4c715b45ae37b72e36719d96d9283
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4619
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* mips_m4k: add optional reset handler

In some cases by using SRST we can't halt CPU early enough. And
option PrRst is not available too. In this case the only way is
to set BOOT flag over EJTAG and reset CPU or SoC from CPU itself.
For example by writing to some reset register.

This patch is providing possibility to use user defined reset-assert
handler which will be enabled only in case SRST is disabled. It is
needed to be able switch between two different reset variants on run
time.

Change-Id: I6ef98f1871ea657115877190f7cc7a5e8f3233e4
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4404
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target: add config for Qualcomm QCA4531

The QCA4531 is a two stream (2x2) 802.11b/g/n single-band programmable
Wi-Fi System-on-Chip (SoC) for the Internet of Things (IoT).
https://www.qualcomm.com/products/qca4531

Change-Id: I58398c00943b005cfaf0ac1eaad92d1fa4e2cba7
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4405
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/board: add config for 8devices LIMA board

More information about this board can be found here:
https://www.8devices.com/products/lima

Change-Id: Id35a35d3e986630d58d37b47828870afd107cc6a
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4406
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target|board: move common AR9331 code to atheros_ar9331.cfg

The ar9331_25mhz_pll_init and ar9331_ddr1_init routines
can be used not only for TP-Link MR3020 board,
so move them to the common atheros_ar9331.cfg file.

Change-Id: I04090856b08151d6bb0f5ef9cc654efae1c81835
Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com>
Reviewed-on: http://openocd.zylin.com/2999
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target/atheros_ar9331: add DDR2 helper

this helper works on many different boards, so it is
good to have it in target config

Change-Id: I068deac36fdd73dbbcedffc87865cc5b9d992c1d
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4422
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/target/atheros_ar9331: add documentation and extra helpers

Sync it with experience gathered on Qualcomm QCA4531 SoC. This
chips are in many ways similar.

Change-Id: I06b9c85e5985a09a9be3cb6cc0ce3b37695d2e54
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4423
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* tcl/board: add DPTechnics DPT-Board-v1

it is Atheros AR9331 based IoT dev board.

Change-Id: I6fc3cdea1bef49c53045018ff5acfec4d5610ba6
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4424
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* fpga/altera-10m50: add all device id

add all currently know Intel (Alter) MAX 10 device ids

Change-Id: I6a88fef222c8e206812499d41be863c3d89fa944
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4598
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target|board: Add Intel (Altera) Arria 10 target and related board

Target information about this SoC can be found here:
https://www.altera.com/products/fpga/arria-series/arria-10/overview.html

Achilles Instant-Development Kit Arria 10 SoC SoM:
https://www.reflexces.com/products-solutions/development-kits/arria-10/achilles-instant-development-kit-arria-10-soc-som

Change-Id: Id78c741be6a8b7d3a70f37d41088e47ee61b437a
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-on: http://openocd.zylin.com/4583
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/riscv: fix compile error with gcc 8.1.1

Fix compile error:
src/target/riscv/riscv-011.c: In function ‘slot_offset’:
src/target/riscv/riscv-011.c:238:4: error: this statement may fall through
 [-Werror=implicit-fallthrough=]
    switch (slot) {
    ^~~~~~
src/target/riscv/riscv-011.c:243:3: note: here
   case 64:
   ^~~~

Fixes: a51ab8ddf6 ("Add RISC-V support.")
Change-Id: I7fa86b305bd90cc590fd4359c3698632d44712e5
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4618
Tested-by: jenkins
Reviewed-by: Jiri Kastner <cz172638@gmail.com>
Reviewed-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-by: Tim Newsome <tim@sifive.com>
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* server: explicitly call "shutdown" when catch CTRL-C or a signal

Every TCL command can be renamed (or deleted) and then replaced by
a TCL proc that has the same name of the original TCL command.
This can be used either to completely replace an existing command
or to wrap the original command to extend its functionality.
This applies also to the OpenOCD command "shutdown" and can be
useful, for example, to set back some default value to the target
before quitting OpenOCD.
E.g. (TCL code):
	rename shutdown original_shutdown
	proc shutdown {} {
		puts "This is my implementation of shutdown"
		# my own stuff before exit OpenOCD
		original_shutdown
	}

Unfortunately, sending a signal (or pressing CTRL-C) to terminate
OpenOCD doesn't trigger calling the original "shutdown" command
nor its (eventual) replacement.

Detect if the main loop is terminated by an external signal and
in such case execute explicitly the command "shutdown".
Replace with enum the magic numbers assumed by "shutdown_openocd".

Please notice that it's possible to write a custom "shutdown" TCL
proc that does not call the original "shutdown" command. This is
useful, for example, to prevent the user to quit OpenOCD by typing
"shutdown" in the telnet session.
Such case will not prevent OpenOCD to terminate when receiving a
signal; OpenOCD will quit after executing the custom "shutdown"
command.

Change-Id: I86b8f9eab8dbd7a28dad58b8cafd97caa7a82f43
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4551
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* zy1000: fix compile error with gcc 8.1.1

The fall-through comment is not taken in consideration by gcc 8.1.1
because it is inside the braces of a C-code block.

Move the comment outside the C block.

Change-Id: I22d87b2dee109fb8bcf2071ac55fdf7171ffcf4b
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4614
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* flash/nor/tcl.c: fix flash bank bounds check in 'flash fill' command handler

Steps to reproduce ( STM32F103 'Blue Pill', 128KiB of flash ):
> flash fillh 0x0801FFFE 00 1
wrote 2 bytes to 0x0801fffe in 0.019088s (0.102 KiB/s)
> flash fillw 0x0801FFFE 00 1
Error: stm32f1x.cpu -- clearing lockup after double fault
Error: error waiting for target flash write algorithm
Error: error writing to flash at address 0x08000000 at offset 0x0001fffe

Change-Id: I145092ec5e45bc586b3df48bf37c38c9226915c1
Signed-off-by: Bohdan Tymkiv <bhdt@cypress.com>
Reviewed-on: http://openocd.zylin.com/4516
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/arm_adi_v5: add command "dpreg"

For very low level debug or development around DAP, it is useful
to have direct access to DP registers.

Add command "dpreg" by mimic the syntax of the existing "apreg"
command:
	$dap_name dpreg reg [value]

Change-Id: Ic4ab451eb5e74453133adee61050b4c6f656ffa3
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4612
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* nrf5: add free_driver_priv

Change-Id: I429a9868deb0c4b51f47a4bbad844bdc348e8d21
Signed-off-by: Jim Paris <jim@jtan.com>
Reviewed-on: http://openocd.zylin.com/4608
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* rtos: add support for NuttX

This patch introduces RTOS support for NuttX. Currently,
only ARM Cortex-M (both FPU and FPU-less) targets are supported.

To use, add the following lines to ~/.gdbinit.

define hookpost-file
  eval "monitor nuttx.pid_offset %d", &((struct tcb_s *)(0))->pid
  eval "monitor nuttx.xcpreg_offset %d", &((struct tcb_s *)(0))->xcp.regs
  eval "monitor nuttx.state_offset %d", &((struct tcb_s *)(0))->task_state
  eval "monitor nuttx.name_offset %d", &((struct tcb_s *)(0))->name
  eval "monitor nuttx.name_size %d", sizeof(((struct tcb_s *)(0))->name)
end

And please make sure the above values are the same as in
src/rtos/nuttx_header.h

Change-Id: I2aaf8644d24dfb84b500516a9685382d5d8fe48f
Signed-off-by: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com>
Signed-off-by: Masatoshi Tateishi <Masatoshi.Tateishi@jp.sony.com>
Signed-off-by: Nobuto Kobayashi <Nobuto.Kobayashi@sony.com>
Reviewed-on: http://openocd.zylin.com/4103
Tested-by: jenkins
Reviewed-by: Alan Carvalho de Assis <acassis@gmail.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* server/server: Add ability to remove services

Add the ability to remove services while OpenOCD is running.

Change-Id: I4067916fda6d03485463fa40901b40484d94e24e
Signed-off-by: Marc Schink <openocd-dev@marcschink.de>
Reviewed-on: http://openocd.zylin.com/4054
Tested-by: jenkins
Reviewed-by: Fredrik Hederstierna <fredrik@hederstierna.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: fix incorrect comment

The code sets C_MASKINTS if that bit is not already set (correctly). Fix
the comment to agree.

Change-Id: If4543e2660a9fa2cdabb2d2698427a6c8d9a274c
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4620
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/target/stm32f0x: Allow overriding the Flash bank size

Copy & paste from another stm32 target.

Change-Id: I0f6cbcec974ce70c23c1850526354106caee1172
Signed-off-by: Dominik Peklo <dom.peklo@gmail.com>
Reviewed-on: http://openocd.zylin.com/4575
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* tcl/target: add Allwinner V3s SoC support

Change-Id: I2459d2b137050985b7301047f9651951d72d9e9e
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Reviewed-on: http://openocd.zylin.com/4427
Tested-by: jenkins
Reviewed-by: Paul Fertser <fercerpav@gmail.com>

* target/arm_adi_v5: allow commands apsel and apcsw during init phase

The current implementation of apsel cannot be executed during the
initialization phase because it queries the DAP AP to retrieve and
print the content of IDR register, and the query is only possible
later on during the exec phase.
But IDR information is already printed by the dedicated command
apid, making redundant printing it by apsel too.
Being unable to run apsel during initialization, makes also apcsw
command (that depends on apsel) not usable in such phase.

Modify the command apsel to only set the current AP, without making
any transfer to the (possibly not initialized yet) DAP. When run
without parameters, just print the current AP number.
Change mode to COMMAND_ANY to apsel and to apcsw.

Change-Id: Ibea6d531e435d1d49d782de1ed8ee6846e91bfdf
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4624
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_a: allow command dacrfixup during init phase

There is no reason to restrict the command "cortex_a dacrfixup"
to the EXEC phase only.
Change the command mode to ANY so the command can be used in
the initialization phase too.

Change-Id: I498cc6b2dbdc48b3b2dd5f0445519a51857b295f
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4623
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* target/armv7a_cache: add gdb keep-alive and fix a missing dpm finish

Depending on range size, the loop on cache operations can take quite
some time, causing gdb to timeout.

Add keep-alive to prevent gdb to timeout.
Add also a missing dpm->finish() to balance dpm->prepare().

Change-Id: Ia87934b1ec19a0332bb50e3010b582381e5f3685
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-on: http://openocd.zylin.com/4627
Tested-by: jenkins
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>

* Add detail to `wrong register size` error.

Signed-off-by: Tim Newsome <tim@sifive.com>
Change-Id: Id31499c94b539969970251145e42c89c943fd87c
Reviewed-on: http://openocd.zylin.com/4577
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* doc: fix typo in cortex_m maskisr command

Change-Id: I37795c320ff7cbf6f2c7434e03b26dbaf6fc6db4
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4621
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/cortex_m: restore C_MASKINTS after reset

The cortex_m maskisr user-facing setting is not changed across a target
reset. However, the in-core C_MASKINTS bit was always cleared as part of
reset processing, meaning that a cortex_m maskisr on setting would not
be respected after a reset. Set C_MASKINTS based on the user-facing
setting value rather than always clearing it after reset.

Change-Id: I5aa5b9dfde04a0fb9c6816fa55b5ef1faf39f8de
Signed-off-by: Christopher Head <chead@zaber.com>
Reviewed-on: http://openocd.zylin.com/4605
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* tcl/board: update all uses of interface/stlink-v2-1 to interface/stlink

Change-Id: I5e27e84d022f73101376e8b4a1bdc65f58fd348a
Signed-off-by: Cody P Schafer <openocd@codyps.com>
Reviewed-on: http://openocd.zylin.com/4456
Tested-by: jenkins
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>

* target/riscv/riscv-011: fix compile warning about uninitialized variable

In MSYS2 MinGW 64-bit
git clone git://git.code.sf.net/p/openocd/code openocd
$ gcc --version
gcc.exe (Rev1, Built by MSYS2 project) 8.2.0
./bootstrap
./configure --prefix=
$ cat config.status | grep CFLAGS
CFLAGS='-g -O2'
make bindir = "bin-x64"

depbase=`echo src/target/riscv/riscv-011.lo | sed 's|[^/]*$|.deps/&|;s|\.lo$||'`;\
/bin/sh ./libtool  --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I.  -D__USE_MINGW_ANSI_STDIO -I./src -I./src -I./src/helper -DPKGDATADIR=\"/mingw64/share/openocd\" -DBINDIR=\"bin-x64\" -I./jimtcl -I./jimtcl  -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Werror -g -O2 -MT src/target/riscv/riscv-011.lo -MD -MP -MF $depbase.Tpo -c -o src/target/riscv/riscv-011.lo src/target/riscv/riscv-011.c &&\
mv -f $depbase.Tpo $depbase.Plo
libtool: compile:  gcc -DHAVE_CONFIG_H -I. -D__USE_MINGW_ANSI_STDIO -I./src -I./src -I./src/helper -DPKGDATADIR=\"/mingw64/share/openocd\" -DBINDIR=\"bin-x64\" -I./jimtcl -I./jimtcl -Wall -Wstrict-prototypes -Wformat-security -Wshadow -Wextra -Wno-unused-parameter -Wbad-function-cast -Wcast-align -Wredundant-decls -Werror -g -O2 -MT src/target/riscv/riscv-011.lo -MD -MP -MF src/target/riscv/.deps/riscv-011.Tpo -c src/target/riscv/riscv-011.c -o src/target/riscv/riscv-011.o
src/target/riscv/riscv-011.c: In function 'poll_target':
src/target/riscv/riscv-011.c:1799:6: error: 'reg' may be used uninitialized in this function [-Werror=maybe-uninitialized]
      reg_cache_set(target, reg, ((data & 0xffffffff) << 32) | value);
      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/target/riscv/riscv-011.c:1686:17: note: 'reg' was declared here
    unsigned int reg;
                 ^~~
cc1.exe: all warnings being treated as errors
make[2]: *** [Makefile:3250: src/target/riscv/riscv-011.lo] Error 1

Change-Id: I6996dcb866fbace26817636f4bedba09510a087f
Signed-off-by: Svetoslav Enchev <svetoslav.enchev@gmail.com>
Reviewed-on: http://openocd.zylin.com/4635
Tested-by: jenkins
Reviewed-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Tim Newsome <tim@sifive.com>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
2018-08-20 14:55:30 -07:00

3208 lines
78 KiB
C

/***************************************************************************
* Copyright (C) 2009 by Duane Ellis *
* openocd@duaneellis.com *
* *
* Copyright (C) 2010 by Olaf Lüke (at91sam3s* support) *
* olaf@uni-paderborn.de *
* *
* Copyright (C) 2011 by Olivier Schonken, Jim Norris *
* (at91sam3x* & at91sam4 support)* *
* *
* 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, see <http://www.gnu.org/licenses/>. *
****************************************************************************/
/* Some of the the lower level code was based on code supplied by
* ATMEL under this copyright. */
/* BEGIN ATMEL COPYRIGHT */
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2009, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
/* END ATMEL COPYRIGHT */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "imp.h"
#include <helper/time_support.h>
#define REG_NAME_WIDTH (12)
/* at91sam4s/at91sam4e/at91sam4c series (has always one flash bank)*/
#define FLASH_BANK_BASE_S 0x00400000
#define FLASH_BANK_BASE_C 0x01000000
/* at91sam4sd series (two one flash banks), first bank address */
#define FLASH_BANK0_BASE_SD FLASH_BANK_BASE_S
/* at91sam4sd16x, second bank address */
#define FLASH_BANK1_BASE_1024K_SD (FLASH_BANK0_BASE_SD+(1024*1024/2))
/* at91sam4sd32x, second bank address */
#define FLASH_BANK1_BASE_2048K_SD (FLASH_BANK0_BASE_SD+(2048*1024/2))
/* at91sam4c32x, first and second bank address */
#define FLASH_BANK0_BASE_C32 FLASH_BANK_BASE_C
#define FLASH_BANK1_BASE_C32 (FLASH_BANK_BASE_C+(2048*1024/2))
#define AT91C_EFC_FCMD_GETD (0x0) /* (EFC) Get Flash Descriptor */
#define AT91C_EFC_FCMD_WP (0x1) /* (EFC) Write Page */
#define AT91C_EFC_FCMD_WPL (0x2) /* (EFC) Write Page and Lock */
#define AT91C_EFC_FCMD_EWP (0x3) /* (EFC) Erase Page and Write Page */
#define AT91C_EFC_FCMD_EWPL (0x4) /* (EFC) Erase Page and Write Page then Lock */
#define AT91C_EFC_FCMD_EA (0x5) /* (EFC) Erase All */
/* cmd6 is not present in the at91sam4u4/2/1 data sheet table 19-2 */
/* #define AT91C_EFC_FCMD_EPL (0x6) // (EFC) Erase plane? */
#define AT91C_EFC_FCMD_EPA (0x7) /* (EFC) Erase pages */
#define AT91C_EFC_FCMD_SLB (0x8) /* (EFC) Set Lock Bit */
#define AT91C_EFC_FCMD_CLB (0x9) /* (EFC) Clear Lock Bit */
#define AT91C_EFC_FCMD_GLB (0xA) /* (EFC) Get Lock Bit */
#define AT91C_EFC_FCMD_SFB (0xB) /* (EFC) Set Fuse Bit */
#define AT91C_EFC_FCMD_CFB (0xC) /* (EFC) Clear Fuse Bit */
#define AT91C_EFC_FCMD_GFB (0xD) /* (EFC) Get Fuse Bit */
#define AT91C_EFC_FCMD_STUI (0xE) /* (EFC) Start Read Unique ID */
#define AT91C_EFC_FCMD_SPUI (0xF) /* (EFC) Stop Read Unique ID */
#define offset_EFC_FMR 0
#define offset_EFC_FCR 4
#define offset_EFC_FSR 8
#define offset_EFC_FRR 12
extern struct flash_driver at91sam4_flash;
static float _tomhz(uint32_t freq_hz)
{
float f;
f = ((float)(freq_hz)) / 1000000.0;
return f;
}
/* How the chip is configured. */
struct sam4_cfg {
uint32_t unique_id[4];
uint32_t slow_freq;
uint32_t rc_freq;
uint32_t mainosc_freq;
uint32_t plla_freq;
uint32_t mclk_freq;
uint32_t cpu_freq;
uint32_t fclk_freq;
uint32_t pclk0_freq;
uint32_t pclk1_freq;
uint32_t pclk2_freq;
#define SAM4_CHIPID_CIDR (0x400E0740)
uint32_t CHIPID_CIDR;
#define SAM4_CHIPID_EXID (0x400E0744)
uint32_t CHIPID_EXID;
#define SAM4_PMC_BASE (0x400E0400)
#define SAM4_PMC_SCSR (SAM4_PMC_BASE + 0x0008)
uint32_t PMC_SCSR;
#define SAM4_PMC_PCSR (SAM4_PMC_BASE + 0x0018)
uint32_t PMC_PCSR;
#define SAM4_CKGR_UCKR (SAM4_PMC_BASE + 0x001c)
uint32_t CKGR_UCKR;
#define SAM4_CKGR_MOR (SAM4_PMC_BASE + 0x0020)
uint32_t CKGR_MOR;
#define SAM4_CKGR_MCFR (SAM4_PMC_BASE + 0x0024)
uint32_t CKGR_MCFR;
#define SAM4_CKGR_PLLAR (SAM4_PMC_BASE + 0x0028)
uint32_t CKGR_PLLAR;
#define SAM4_PMC_MCKR (SAM4_PMC_BASE + 0x0030)
uint32_t PMC_MCKR;
#define SAM4_PMC_PCK0 (SAM4_PMC_BASE + 0x0040)
uint32_t PMC_PCK0;
#define SAM4_PMC_PCK1 (SAM4_PMC_BASE + 0x0044)
uint32_t PMC_PCK1;
#define SAM4_PMC_PCK2 (SAM4_PMC_BASE + 0x0048)
uint32_t PMC_PCK2;
#define SAM4_PMC_SR (SAM4_PMC_BASE + 0x0068)
uint32_t PMC_SR;
#define SAM4_PMC_IMR (SAM4_PMC_BASE + 0x006c)
uint32_t PMC_IMR;
#define SAM4_PMC_FSMR (SAM4_PMC_BASE + 0x0070)
uint32_t PMC_FSMR;
#define SAM4_PMC_FSPR (SAM4_PMC_BASE + 0x0074)
uint32_t PMC_FSPR;
};
struct sam4_bank_private {
int probed;
/* DANGER: THERE ARE DRAGONS HERE.. */
/* NOTE: If you add more 'ghost' pointers */
/* be aware that you must *manually* update */
/* these pointers in the function sam4_GetDetails() */
/* See the comment "Here there be dragons" */
/* so we can find the chip we belong to */
struct sam4_chip *pChip;
/* so we can find the original bank pointer */
struct flash_bank *pBank;
unsigned bank_number;
uint32_t controller_address;
uint32_t base_address;
uint32_t flash_wait_states;
bool present;
unsigned size_bytes;
unsigned nsectors;
unsigned sector_size;
unsigned page_size;
};
struct sam4_chip_details {
/* THERE ARE DRAGONS HERE.. */
/* note: If you add pointers here */
/* be careful about them as they */
/* may need to be updated inside */
/* the function: "sam4_GetDetails() */
/* which copy/overwrites the */
/* 'runtime' copy of this structure */
uint32_t chipid_cidr;
const char *name;
unsigned n_gpnvms;
#define SAM4_N_NVM_BITS 3
unsigned gpnvm[SAM4_N_NVM_BITS];
unsigned total_flash_size;
unsigned total_sram_size;
unsigned n_banks;
#define SAM4_MAX_FLASH_BANKS 2
/* these are "initialized" from the global const data */
struct sam4_bank_private bank[SAM4_MAX_FLASH_BANKS];
};
struct sam4_chip {
struct sam4_chip *next;
int probed;
/* this is "initialized" from the global const structure */
struct sam4_chip_details details;
struct target *target;
struct sam4_cfg cfg;
};
struct sam4_reg_list {
uint32_t address; size_t struct_offset; const char *name;
void (*explain_func)(struct sam4_chip *pInfo);
};
static struct sam4_chip *all_sam4_chips;
static struct sam4_chip *get_current_sam4(struct command_context *cmd_ctx)
{
struct target *t;
static struct sam4_chip *p;
t = get_current_target(cmd_ctx);
if (!t) {
command_print(cmd_ctx, "No current target?");
return NULL;
}
p = all_sam4_chips;
if (!p) {
/* this should not happen */
/* the command is not registered until the chip is created? */
command_print(cmd_ctx, "No SAM4 chips exist?");
return NULL;
}
while (p) {
if (p->target == t)
return p;
p = p->next;
}
command_print(cmd_ctx, "Cannot find SAM4 chip?");
return NULL;
}
/*The actual sector size of the SAM4S flash memory is 65536 bytes. 16 sectors for a 1024KB device*/
/*The lockregions are 8KB per lock region, with a 1024KB device having 128 lock regions. */
/*For the best results, nsectors are thus set to the amount of lock regions, and the sector_size*/
/*set to the lock region size. Page erases are used to erase 8KB sections when programming*/
/* these are used to *initialize* the "pChip->details" structure. */
static const struct sam4_chip_details all_sam4_details[] = {
/* Start at91sam4c* series */
/* at91sam4c32e - LQFP144 */
{
.chipid_cidr = 0xA66D0EE0,
.name = "at91sam4c32e",
.total_flash_size = 2024 * 1024,
.total_sram_size = 256 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_C32,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_C32,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
},
},
/* at91sam4c32c - LQFP100 */
{
.chipid_cidr = 0xA64D0EE0,
.name = "at91sam4c32c",
.total_flash_size = 2024 * 1024,
.total_sram_size = 256 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_C32,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_C32,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
},
},
/* at91sam4c16c - LQFP100 */
{
.chipid_cidr = 0xA64C0CE0,
.name = "at91sam4c16c",
.total_flash_size = 1024 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_C,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/* at91sam4c8c - LQFP100 */
{
.chipid_cidr = 0xA64C0AE0,
.name = "at91sam4c8c",
.total_flash_size = 512 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_C,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/* at91sam4c4c (rev B) - LQFP100 */
{
.chipid_cidr = 0xA64C0CE5,
.name = "at91sam4c4c",
.total_flash_size = 256 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_C,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 256 * 1024,
.nsectors = 32,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/* Start at91sam4e* series */
/*atsam4e16e - LQFP144/LFBGA144*/
{
.chipid_cidr = 0xA3CC0CE0,
.name = "at91sam4e16e",
.total_flash_size = 1024 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/* Start at91sam4n* series */
/*atsam4n8a - LQFP48/QFN48*/
{
.chipid_cidr = 0x293B0AE0,
.name = "at91sam4n8a",
.total_flash_size = 512 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4n8b - LQFP64/QFN64*/
{
.chipid_cidr = 0x294B0AE0,
.name = "at91sam4n8b",
.total_flash_size = 512 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4n8c - LQFP100/TFBGA100/VFBGA100*/
{
.chipid_cidr = 0x295B0AE0,
.name = "at91sam4n8c",
.total_flash_size = 512 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4n16b - LQFP64/QFN64*/
{
.chipid_cidr = 0x29460CE0,
.name = "at91sam4n16b",
.total_flash_size = 1024 * 1024,
.total_sram_size = 80 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4n16c - LQFP100/TFBGA100/VFBGA100*/
{
.chipid_cidr = 0x29560CE0,
.name = "at91sam4n16c",
.total_flash_size = 1024 * 1024,
.total_sram_size = 80 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/* Start at91sam4s* series */
/*atsam4s16c - LQFP100/BGA100*/
{
.chipid_cidr = 0x28AC0CE0,
.name = "at91sam4s16c",
.total_flash_size = 1024 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*at91sam4sa16c - TFBGA100/VFBGA100/LQFP100*/
{
.chipid_cidr = 0x28a70ce0,
.name = "at91sam4sa16c",
.total_flash_size = 1024 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s16b - LQFP64/QFN64/WLCSP64*/
{
.chipid_cidr = 0x289C0CE0,
.name = "at91sam4s16b",
.total_flash_size = 1024 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4sa16b - LQFP64/QFN64*/
{
.chipid_cidr = 0x28970CE0,
.name = "at91sam4sa16b",
.total_flash_size = 1024 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s16a - LQFP48/QFN48*/
{
.chipid_cidr = 0x288C0CE0,
.name = "at91sam4s16a",
.total_flash_size = 1024 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s8c - LQFP100/BGA100*/
{
.chipid_cidr = 0x28AC0AE0,
.name = "at91sam4s8c",
.total_flash_size = 512 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s8b - LQFP64/QFN64/WLCSP64*/
{
.chipid_cidr = 0x289C0AE0,
.name = "at91sam4s8b",
.total_flash_size = 512 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s8a - LQFP48/BGA48*/
{
.chipid_cidr = 0x288C0AE0,
.name = "at91sam4s8a",
.total_flash_size = 512 * 1024,
.total_sram_size = 128 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s4c - LQFP100/BGA100*/
{
.chipid_cidr = 0x28ab09e0,
.name = "at91sam4s4c",
.total_flash_size = 256 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 256 * 1024,
.nsectors = 32,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s4b - LQFP64/QFN64/WLCSP64*/
{
.chipid_cidr = 0x289b09e0,
.name = "at91sam4s4b",
.total_flash_size = 256 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 256 * 1024,
.nsectors = 32,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s4a - LQFP48/QFN48*/
{
.chipid_cidr = 0x288b09e0,
.name = "at91sam4s4a",
.total_flash_size = 256 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 256 * 1024,
.nsectors = 32,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s2c - LQFP100/BGA100*/
{
.chipid_cidr = 0x28ab07e0,
.name = "at91sam4s2c",
.total_flash_size = 128 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 128 * 1024,
.nsectors = 16,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s2b - LQPF64/QFN64/WLCSP64*/
{
.chipid_cidr = 0x289b07e0,
.name = "at91sam4s2b",
.total_flash_size = 128 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 128 * 1024,
.nsectors = 16,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*atsam4s2a - LQFP48/QFN48*/
{
.chipid_cidr = 0x288b07e0,
.name = "at91sam4s2a",
.total_flash_size = 128 * 1024,
.total_sram_size = 64 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = {*/
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 128 * 1024,
.nsectors = 16,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
},
},
/*at91sam4sd32c - LQFP100/BGA100*/
{
.chipid_cidr = 0x29a70ee0,
.name = "at91sam4sd32c",
.total_flash_size = 2048 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_SD,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_2048K_SD,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
},
},
/*at91sam4sd32b - LQFP64/BGA64*/
{
.chipid_cidr = 0x29970ee0,
.name = "at91sam4sd32b",
.total_flash_size = 2048 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_SD,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_2048K_SD,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 1024 * 1024,
.nsectors = 128,
.sector_size = 8192,
.page_size = 512,
},
},
},
/*at91sam4sd16c - LQFP100/BGA100*/
{
.chipid_cidr = 0x29a70ce0,
.name = "at91sam4sd16c",
.total_flash_size = 1024 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_SD,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_1024K_SD,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
},
},
/*at91sam4sd16b - LQFP64/BGA64*/
{
.chipid_cidr = 0x29970ce0,
.name = "at91sam4sd16b",
.total_flash_size = 1024 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 3,
.n_banks = 2,
/* .bank[0] = { */
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK0_BASE_SD,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = { */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 1,
.base_address = FLASH_BANK1_BASE_1024K_SD,
.controller_address = 0x400e0c00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
},
},
/* atsamg53n19 */
{
.chipid_cidr = 0x247e0ae0,
.name = "atsamg53n19",
.total_flash_size = 512 * 1024,
.total_sram_size = 96 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
/* .bank[0] = {*/
{
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = {*/
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
}
},
/* atsamg55g19 */
{
.chipid_cidr = 0x24470ae0,
.name = "atsamg55g19",
.total_flash_size = 512 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = */
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
}
},
/* atsamg55j19 */
{
.chipid_cidr = 0x24570ae0,
.name = "atsamg55j19",
.total_flash_size = 512 * 1024,
.total_sram_size = 160 * 1024,
.n_gpnvms = 2,
.n_banks = 1,
{
/* .bank[0] = */
{
.probed = 0,
.pChip = NULL,
.pBank = NULL,
.bank_number = 0,
.base_address = FLASH_BANK_BASE_S,
.controller_address = 0x400e0a00,
.flash_wait_states = 5,
.present = 1,
.size_bytes = 512 * 1024,
.nsectors = 64,
.sector_size = 8192,
.page_size = 512,
},
/* .bank[1] = */
{
.present = 0,
.probed = 0,
.bank_number = 1,
},
}
},
/* terminate */
{
.chipid_cidr = 0,
.name = NULL,
}
};
/* Globals above */
/***********************************************************************
**********************************************************************
**********************************************************************
**********************************************************************
**********************************************************************
**********************************************************************/
/* *ATMEL* style code - from the SAM4 driver code */
/**
* Get the current status of the EEFC and
* the value of some status bits (LOCKE, PROGE).
* @param pPrivate - info about the bank
* @param v - result goes here
*/
static int EFC_GetStatus(struct sam4_bank_private *pPrivate, uint32_t *v)
{
int r;
r = target_read_u32(pPrivate->pChip->target,
pPrivate->controller_address + offset_EFC_FSR,
v);
LOG_DEBUG("Status: 0x%08x (lockerror: %d, cmderror: %d, ready: %d)",
(unsigned int)(*v),
((unsigned int)((*v >> 2) & 1)),
((unsigned int)((*v >> 1) & 1)),
((unsigned int)((*v >> 0) & 1)));
return r;
}
/**
* Get the result of the last executed command.
* @param pPrivate - info about the bank
* @param v - result goes here
*/
static int EFC_GetResult(struct sam4_bank_private *pPrivate, uint32_t *v)
{
int r;
uint32_t rv;
r = target_read_u32(pPrivate->pChip->target,
pPrivate->controller_address + offset_EFC_FRR,
&rv);
if (v)
*v = rv;
LOG_DEBUG("Result: 0x%08x", ((unsigned int)(rv)));
return r;
}
static int EFC_StartCommand(struct sam4_bank_private *pPrivate,
unsigned command, unsigned argument)
{
uint32_t n, v;
int r;
int retry;
retry = 0;
do_retry:
/* Check command & argument */
switch (command) {
case AT91C_EFC_FCMD_WP:
case AT91C_EFC_FCMD_WPL:
case AT91C_EFC_FCMD_EWP:
case AT91C_EFC_FCMD_EWPL:
/* case AT91C_EFC_FCMD_EPL: */
case AT91C_EFC_FCMD_EPA:
case AT91C_EFC_FCMD_SLB:
case AT91C_EFC_FCMD_CLB:
n = (pPrivate->size_bytes / pPrivate->page_size);
if (argument >= n)
LOG_ERROR("*BUG*: Embedded flash has only %u pages", (unsigned)(n));
break;
case AT91C_EFC_FCMD_SFB:
case AT91C_EFC_FCMD_CFB:
if (argument >= pPrivate->pChip->details.n_gpnvms) {
LOG_ERROR("*BUG*: Embedded flash has only %d GPNVMs",
pPrivate->pChip->details.n_gpnvms);
}
break;
case AT91C_EFC_FCMD_GETD:
case AT91C_EFC_FCMD_EA:
case AT91C_EFC_FCMD_GLB:
case AT91C_EFC_FCMD_GFB:
case AT91C_EFC_FCMD_STUI:
case AT91C_EFC_FCMD_SPUI:
if (argument != 0)
LOG_ERROR("Argument is meaningless for cmd: %d", command);
break;
default:
LOG_ERROR("Unknown command %d", command);
break;
}
if (command == AT91C_EFC_FCMD_SPUI) {
/* this is a very special situation. */
/* Situation (1) - error/retry - see below */
/* And we are being called recursively */
/* Situation (2) - normal, finished reading unique id */
} else {
/* it should be "ready" */
EFC_GetStatus(pPrivate, &v);
if (v & 1) {
/* then it is ready */
/* we go on */
} else {
if (retry) {
/* we have done this before */
/* the controller is not responding. */
LOG_ERROR("flash controller(%d) is not ready! Error",
pPrivate->bank_number);
return ERROR_FAIL;
} else {
retry++;
LOG_ERROR("Flash controller(%d) is not ready, attempting reset",
pPrivate->bank_number);
/* we do that by issuing the *STOP* command */
EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0);
/* above is recursive, and further recursion is blocked by */
/* if (command == AT91C_EFC_FCMD_SPUI) above */
goto do_retry;
}
}
}
v = (0x5A << 24) | (argument << 8) | command;
LOG_DEBUG("Command: 0x%08x", ((unsigned int)(v)));
r = target_write_u32(pPrivate->pBank->target,
pPrivate->controller_address + offset_EFC_FCR, v);
if (r != ERROR_OK)
LOG_DEBUG("Error Write failed");
return r;
}
/**
* Performs the given command and wait until its completion (or an error).
* @param pPrivate - info about the bank
* @param command - Command to perform.
* @param argument - Optional command argument.
* @param status - put command status bits here
*/
static int EFC_PerformCommand(struct sam4_bank_private *pPrivate,
unsigned command,
unsigned argument,
uint32_t *status)
{
int r;
uint32_t v;
int64_t ms_now, ms_end;
/* default */
if (status)
*status = 0;
r = EFC_StartCommand(pPrivate, command, argument);
if (r != ERROR_OK)
return r;
ms_end = 10000 + timeval_ms();
do {
r = EFC_GetStatus(pPrivate, &v);
if (r != ERROR_OK)
return r;
ms_now = timeval_ms();
if (ms_now > ms_end) {
/* error */
LOG_ERROR("Command timeout");
return ERROR_FAIL;
}
} while ((v & 1) == 0);
/* error bits.. */
if (status)
*status = (v & 0x6);
return ERROR_OK;
}
/**
* Read the unique ID.
* @param pPrivate - info about the bank
* The unique ID is stored in the 'pPrivate' structure.
*/
static int FLASHD_ReadUniqueID(struct sam4_bank_private *pPrivate)
{
int r;
uint32_t v;
int x;
/* assume 0 */
pPrivate->pChip->cfg.unique_id[0] = 0;
pPrivate->pChip->cfg.unique_id[1] = 0;
pPrivate->pChip->cfg.unique_id[2] = 0;
pPrivate->pChip->cfg.unique_id[3] = 0;
LOG_DEBUG("Begin");
r = EFC_StartCommand(pPrivate, AT91C_EFC_FCMD_STUI, 0);
if (r < 0)
return r;
for (x = 0; x < 4; x++) {
r = target_read_u32(pPrivate->pChip->target,
pPrivate->pBank->base + (x * 4),
&v);
if (r < 0)
return r;
pPrivate->pChip->cfg.unique_id[x] = v;
}
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SPUI, 0, NULL);
LOG_DEBUG("End: R=%d, id = 0x%08x, 0x%08x, 0x%08x, 0x%08x",
r,
(unsigned int)(pPrivate->pChip->cfg.unique_id[0]),
(unsigned int)(pPrivate->pChip->cfg.unique_id[1]),
(unsigned int)(pPrivate->pChip->cfg.unique_id[2]),
(unsigned int)(pPrivate->pChip->cfg.unique_id[3]));
return r;
}
/**
* Erases the entire flash.
* @param pPrivate - the info about the bank.
*/
static int FLASHD_EraseEntireBank(struct sam4_bank_private *pPrivate)
{
LOG_DEBUG("Here");
return EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_EA, 0, NULL);
}
/**
* Erases the entire flash.
* @param pPrivate - the info about the bank.
*/
static int FLASHD_ErasePages(struct sam4_bank_private *pPrivate,
int firstPage,
int numPages,
uint32_t *status)
{
LOG_DEBUG("Here");
uint8_t erasePages;
switch (numPages) {
case 4:
erasePages = 0x00;
break;
case 8:
erasePages = 0x01;
break;
case 16:
erasePages = 0x02;
break;
case 32:
erasePages = 0x03;
break;
default:
erasePages = 0x00;
break;
}
/* AT91C_EFC_FCMD_EPA
* According to the datasheet FARG[15:2] defines the page from which
* the erase will start.This page must be modulo 4, 8, 16 or 32
* according to the number of pages to erase. FARG[1:0] defines the
* number of pages to be erased. Previously (firstpage << 2) was used
* to conform to this, seems it should not be shifted...
*/
return EFC_PerformCommand(pPrivate,
/* send Erase Page */
AT91C_EFC_FCMD_EPA,
(firstPage) | erasePages,
status);
}
/**
* Gets current GPNVM state.
* @param pPrivate - info about the bank.
* @param gpnvm - GPNVM bit index.
* @param puthere - result stored here.
*/
/* ------------------------------------------------------------------------------ */
static int FLASHD_GetGPNVM(struct sam4_bank_private *pPrivate, unsigned gpnvm, unsigned *puthere)
{
uint32_t v;
int r;
LOG_DEBUG("Here");
if (pPrivate->bank_number != 0) {
LOG_ERROR("GPNVM only works with Bank0");
return ERROR_FAIL;
}
if (gpnvm >= pPrivate->pChip->details.n_gpnvms) {
LOG_ERROR("Invalid GPNVM %d, max: %d, ignored",
gpnvm, pPrivate->pChip->details.n_gpnvms);
return ERROR_FAIL;
}
/* Get GPNVMs status */
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GFB, 0, NULL);
if (r != ERROR_OK) {
LOG_ERROR("Failed");
return r;
}
r = EFC_GetResult(pPrivate, &v);
if (puthere) {
/* Check if GPNVM is set */
/* get the bit and make it a 0/1 */
*puthere = (v >> gpnvm) & 1;
}
return r;
}
/**
* Clears the selected GPNVM bit.
* @param pPrivate info about the bank
* @param gpnvm GPNVM index.
* @returns 0 if successful; otherwise returns an error code.
*/
static int FLASHD_ClrGPNVM(struct sam4_bank_private *pPrivate, unsigned gpnvm)
{
int r;
unsigned v;
LOG_DEBUG("Here");
if (pPrivate->bank_number != 0) {
LOG_ERROR("GPNVM only works with Bank0");
return ERROR_FAIL;
}
if (gpnvm >= pPrivate->pChip->details.n_gpnvms) {
LOG_ERROR("Invalid GPNVM %d, max: %d, ignored",
gpnvm, pPrivate->pChip->details.n_gpnvms);
return ERROR_FAIL;
}
r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v);
if (r != ERROR_OK) {
LOG_DEBUG("Failed: %d", r);
return r;
}
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CFB, gpnvm, NULL);
LOG_DEBUG("End: %d", r);
return r;
}
/**
* Sets the selected GPNVM bit.
* @param pPrivate info about the bank
* @param gpnvm GPNVM index.
*/
static int FLASHD_SetGPNVM(struct sam4_bank_private *pPrivate, unsigned gpnvm)
{
int r;
unsigned v;
if (pPrivate->bank_number != 0) {
LOG_ERROR("GPNVM only works with Bank0");
return ERROR_FAIL;
}
if (gpnvm >= pPrivate->pChip->details.n_gpnvms) {
LOG_ERROR("Invalid GPNVM %d, max: %d, ignored",
gpnvm, pPrivate->pChip->details.n_gpnvms);
return ERROR_FAIL;
}
r = FLASHD_GetGPNVM(pPrivate, gpnvm, &v);
if (r != ERROR_OK)
return r;
if (v) {
/* already set */
r = ERROR_OK;
} else {
/* set it */
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SFB, gpnvm, NULL);
}
return r;
}
/**
* Returns a bit field (at most 64) of locked regions within a page.
* @param pPrivate info about the bank
* @param v where to store locked bits
*/
static int FLASHD_GetLockBits(struct sam4_bank_private *pPrivate, uint32_t *v)
{
int r;
LOG_DEBUG("Here");
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_GLB, 0, NULL);
if (r == ERROR_OK) {
EFC_GetResult(pPrivate, v);
EFC_GetResult(pPrivate, v);
EFC_GetResult(pPrivate, v);
r = EFC_GetResult(pPrivate, v);
}
LOG_DEBUG("End: %d", r);
return r;
}
/**
* Unlocks all the regions in the given address range.
* @param pPrivate info about the bank
* @param start_sector first sector to unlock
* @param end_sector last (inclusive) to unlock
*/
static int FLASHD_Unlock(struct sam4_bank_private *pPrivate,
unsigned start_sector,
unsigned end_sector)
{
int r;
uint32_t status;
uint32_t pg;
uint32_t pages_per_sector;
pages_per_sector = pPrivate->sector_size / pPrivate->page_size;
/* Unlock all pages */
while (start_sector <= end_sector) {
pg = start_sector * pages_per_sector;
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_CLB, pg, &status);
if (r != ERROR_OK)
return r;
start_sector++;
}
return ERROR_OK;
}
/**
* Locks regions
* @param pPrivate - info about the bank
* @param start_sector - first sector to lock
* @param end_sector - last sector (inclusive) to lock
*/
static int FLASHD_Lock(struct sam4_bank_private *pPrivate,
unsigned start_sector,
unsigned end_sector)
{
uint32_t status;
uint32_t pg;
uint32_t pages_per_sector;
int r;
pages_per_sector = pPrivate->sector_size / pPrivate->page_size;
/* Lock all pages */
while (start_sector <= end_sector) {
pg = start_sector * pages_per_sector;
r = EFC_PerformCommand(pPrivate, AT91C_EFC_FCMD_SLB, pg, &status);
if (r != ERROR_OK)
return r;
start_sector++;
}
return ERROR_OK;
}
/****** END SAM4 CODE ********/
/* begin helpful debug code */
/* print the fieldname, the field value, in dec & hex, and return field value */
static uint32_t sam4_reg_fieldname(struct sam4_chip *pChip,
const char *regname,
uint32_t value,
unsigned shift,
unsigned width)
{
uint32_t v;
int hwidth, dwidth;
/* extract the field */
v = value >> shift;
v = v & ((1 << width)-1);
if (width <= 16) {
hwidth = 4;
dwidth = 5;
} else {
hwidth = 8;
dwidth = 12;
}
/* show the basics */
LOG_USER_N("\t%*s: %*" PRId32 " [0x%0*" PRIx32 "] ",
REG_NAME_WIDTH, regname,
dwidth, v,
hwidth, v);
return v;
}
static const char _unknown[] = "unknown";
static const char *const eproc_names[] = {
"Cortex-M7", /* 0 */
"arm946es", /* 1 */
"arm7tdmi", /* 2 */
"Cortex-M3", /* 3 */
"arm920t", /* 4 */
"arm926ejs", /* 5 */
"Cortex-A5", /* 6 */
"Cortex-M4", /* 7 */
_unknown, /* 8 */
_unknown, /* 9 */
_unknown, /* 10 */
_unknown, /* 11 */
_unknown, /* 12 */
_unknown, /* 13 */
_unknown, /* 14 */
_unknown, /* 15 */
};
#define nvpsize2 nvpsize /* these two tables are identical */
static const char *const nvpsize[] = {
"none", /* 0 */
"8K bytes", /* 1 */
"16K bytes", /* 2 */
"32K bytes", /* 3 */
_unknown, /* 4 */
"64K bytes", /* 5 */
_unknown, /* 6 */
"128K bytes", /* 7 */
"160K bytes", /* 8 */
"256K bytes", /* 9 */
"512K bytes", /* 10 */
_unknown, /* 11 */
"1024K bytes", /* 12 */
_unknown, /* 13 */
"2048K bytes", /* 14 */
_unknown, /* 15 */
};
static const char *const sramsize[] = {
"48K Bytes", /* 0 */
"1K Bytes", /* 1 */
"2K Bytes", /* 2 */
"6K Bytes", /* 3 */
"112K Bytes", /* 4 */
"4K Bytes", /* 5 */
"80K Bytes", /* 6 */
"160K Bytes", /* 7 */
"8K Bytes", /* 8 */
"16K Bytes", /* 9 */
"32K Bytes", /* 10 */
"64K Bytes", /* 11 */
"128K Bytes", /* 12 */
"256K Bytes", /* 13 */
"96K Bytes", /* 14 */
"512K Bytes", /* 15 */
};
static const struct archnames { unsigned value; const char *name; } archnames[] = {
{ 0x19, "AT91SAM9xx Series" },
{ 0x29, "AT91SAM9XExx Series" },
{ 0x34, "AT91x34 Series" },
{ 0x37, "CAP7 Series" },
{ 0x39, "CAP9 Series" },
{ 0x3B, "CAP11 Series" },
{ 0x3C, "ATSAM4E" },
{ 0x40, "AT91x40 Series" },
{ 0x42, "AT91x42 Series" },
{ 0x43, "SAMG51 Series"
},
{ 0x44, "SAMG55 Series (49-pin WLCSP)" },
{ 0x45, "SAMG55 Series (64-pin)" },
{ 0x47, "SAMG53 Series"
},
{ 0x55, "AT91x55 Series" },
{ 0x60, "AT91SAM7Axx Series" },
{ 0x61, "AT91SAM7AQxx Series" },
{ 0x63, "AT91x63 Series" },
{ 0x64, "SAM4CxxC (100-pin version)" },
{ 0x66, "SAM4CxxE (144-pin version)" },
{ 0x70, "AT91SAM7Sxx Series" },
{ 0x71, "AT91SAM7XCxx Series" },
{ 0x72, "AT91SAM7SExx Series" },
{ 0x73, "AT91SAM7Lxx Series" },
{ 0x75, "AT91SAM7Xxx Series" },
{ 0x76, "AT91SAM7SLxx Series" },
{ 0x80, "ATSAM3UxC Series (100-pin version)" },
{ 0x81, "ATSAM3UxE Series (144-pin version)" },
{ 0x83, "ATSAM3A/SAM4A xC Series (100-pin version)"},
{ 0x84, "ATSAM3X/SAM4X xC Series (100-pin version)"},
{ 0x85, "ATSAM3X/SAM4X xE Series (144-pin version)"},
{ 0x86, "ATSAM3X/SAM4X xG Series (208/217-pin version)" },
{ 0x88, "ATSAM3S/SAM4S xA Series (48-pin version)" },
{ 0x89, "ATSAM3S/SAM4S xB Series (64-pin version)" },
{ 0x8A, "ATSAM3S/SAM4S xC Series (100-pin version)"},
{ 0x92, "AT91x92 Series" },
{ 0x93, "ATSAM3NxA Series (48-pin version)" },
{ 0x94, "ATSAM3NxB Series (64-pin version)" },
{ 0x95, "ATSAM3NxC Series (100-pin version)" },
{ 0x98, "ATSAM3SDxA Series (48-pin version)" },
{ 0x99, "ATSAM3SDxB Series (64-pin version)" },
{ 0x9A, "ATSAM3SDxC Series (100-pin version)" },
{ 0xA5, "ATSAM5A" },
{ 0xF0, "AT75Cxx Series" },
{ -1, NULL },
};
static const char *const nvptype[] = {
"rom", /* 0 */
"romless or onchip flash", /* 1 */
"embedded flash memory",/* 2 */
"rom(nvpsiz) + embedded flash (nvpsiz2)", /* 3 */
"sram emulating flash", /* 4 */
_unknown, /* 5 */
_unknown, /* 6 */
_unknown, /* 7 */
};
static const char *_yes_or_no(uint32_t v)
{
if (v)
return "YES";
else
return "NO";
}
static const char *const _rc_freq[] = {
"4 MHz", "8 MHz", "12 MHz", "reserved"
};
static void sam4_explain_ckgr_mor(struct sam4_chip *pChip)
{
uint32_t v;
uint32_t rcen;
v = sam4_reg_fieldname(pChip, "MOSCXTEN", pChip->cfg.CKGR_MOR, 0, 1);
LOG_USER("(main xtal enabled: %s)", _yes_or_no(v));
v = sam4_reg_fieldname(pChip, "MOSCXTBY", pChip->cfg.CKGR_MOR, 1, 1);
LOG_USER("(main osc bypass: %s)", _yes_or_no(v));
rcen = sam4_reg_fieldname(pChip, "MOSCRCEN", pChip->cfg.CKGR_MOR, 3, 1);
LOG_USER("(onchip RC-OSC enabled: %s)", _yes_or_no(rcen));
v = sam4_reg_fieldname(pChip, "MOSCRCF", pChip->cfg.CKGR_MOR, 4, 3);
LOG_USER("(onchip RC-OSC freq: %s)", _rc_freq[v]);
pChip->cfg.rc_freq = 0;
if (rcen) {
switch (v) {
default:
pChip->cfg.rc_freq = 0;
break;
case 0:
pChip->cfg.rc_freq = 4 * 1000 * 1000;
break;
case 1:
pChip->cfg.rc_freq = 8 * 1000 * 1000;
break;
case 2:
pChip->cfg.rc_freq = 12 * 1000 * 1000;
break;
}
}
v = sam4_reg_fieldname(pChip, "MOSCXTST", pChip->cfg.CKGR_MOR, 8, 8);
LOG_USER("(startup clks, time= %f uSecs)",
((float)(v * 1000000)) / ((float)(pChip->cfg.slow_freq)));
v = sam4_reg_fieldname(pChip, "MOSCSEL", pChip->cfg.CKGR_MOR, 24, 1);
LOG_USER("(mainosc source: %s)",
v ? "external xtal" : "internal RC");
v = sam4_reg_fieldname(pChip, "CFDEN", pChip->cfg.CKGR_MOR, 25, 1);
LOG_USER("(clock failure enabled: %s)",
_yes_or_no(v));
}
static void sam4_explain_chipid_cidr(struct sam4_chip *pChip)
{
int x;
uint32_t v;
const char *cp;
sam4_reg_fieldname(pChip, "Version", pChip->cfg.CHIPID_CIDR, 0, 5);
LOG_USER_N("\n");
v = sam4_reg_fieldname(pChip, "EPROC", pChip->cfg.CHIPID_CIDR, 5, 3);
LOG_USER("%s", eproc_names[v]);
v = sam4_reg_fieldname(pChip, "NVPSIZE", pChip->cfg.CHIPID_CIDR, 8, 4);
LOG_USER("%s", nvpsize[v]);
v = sam4_reg_fieldname(pChip, "NVPSIZE2", pChip->cfg.CHIPID_CIDR, 12, 4);
LOG_USER("%s", nvpsize2[v]);
v = sam4_reg_fieldname(pChip, "SRAMSIZE", pChip->cfg.CHIPID_CIDR, 16, 4);
LOG_USER("%s", sramsize[v]);
v = sam4_reg_fieldname(pChip, "ARCH", pChip->cfg.CHIPID_CIDR, 20, 8);
cp = _unknown;
for (x = 0; archnames[x].name; x++) {
if (v == archnames[x].value) {
cp = archnames[x].name;
break;
}
}
LOG_USER("%s", cp);
v = sam4_reg_fieldname(pChip, "NVPTYP", pChip->cfg.CHIPID_CIDR, 28, 3);
LOG_USER("%s", nvptype[v]);
v = sam4_reg_fieldname(pChip, "EXTID", pChip->cfg.CHIPID_CIDR, 31, 1);
LOG_USER("(exists: %s)", _yes_or_no(v));
}
static void sam4_explain_ckgr_mcfr(struct sam4_chip *pChip)
{
uint32_t v;
v = sam4_reg_fieldname(pChip, "MAINFRDY", pChip->cfg.CKGR_MCFR, 16, 1);
LOG_USER("(main ready: %s)", _yes_or_no(v));
v = sam4_reg_fieldname(pChip, "MAINF", pChip->cfg.CKGR_MCFR, 0, 16);
v = (v * pChip->cfg.slow_freq) / 16;
pChip->cfg.mainosc_freq = v;
LOG_USER("(%3.03f Mhz (%" PRIu32 ".%03" PRIu32 "khz slowclk)",
_tomhz(v),
(uint32_t)(pChip->cfg.slow_freq / 1000),
(uint32_t)(pChip->cfg.slow_freq % 1000));
}
static void sam4_explain_ckgr_plla(struct sam4_chip *pChip)
{
uint32_t mula, diva;
diva = sam4_reg_fieldname(pChip, "DIVA", pChip->cfg.CKGR_PLLAR, 0, 8);
LOG_USER_N("\n");
mula = sam4_reg_fieldname(pChip, "MULA", pChip->cfg.CKGR_PLLAR, 16, 11);
LOG_USER_N("\n");
pChip->cfg.plla_freq = 0;
if (mula == 0)
LOG_USER("\tPLLA Freq: (Disabled,mula = 0)");
else if (diva == 0)
LOG_USER("\tPLLA Freq: (Disabled,diva = 0)");
else if (diva >= 1) {
pChip->cfg.plla_freq = (pChip->cfg.mainosc_freq * (mula + 1) / diva);
LOG_USER("\tPLLA Freq: %3.03f MHz",
_tomhz(pChip->cfg.plla_freq));
}
}
static void sam4_explain_mckr(struct sam4_chip *pChip)
{
uint32_t css, pres, fin = 0;
int pdiv = 0;
const char *cp = NULL;
css = sam4_reg_fieldname(pChip, "CSS", pChip->cfg.PMC_MCKR, 0, 2);
switch (css & 3) {
case 0:
fin = pChip->cfg.slow_freq;
cp = "slowclk";
break;
case 1:
fin = pChip->cfg.mainosc_freq;
cp = "mainosc";
break;
case 2:
fin = pChip->cfg.plla_freq;
cp = "plla";
break;
case 3:
if (pChip->cfg.CKGR_UCKR & (1 << 16)) {
fin = 480 * 1000 * 1000;
cp = "upll";
} else {
fin = 0;
cp = "upll (*ERROR* UPLL is disabled)";
}
break;
default:
assert(0);
break;
}
LOG_USER("%s (%3.03f Mhz)",
cp,
_tomhz(fin));
pres = sam4_reg_fieldname(pChip, "PRES", pChip->cfg.PMC_MCKR, 4, 3);
switch (pres & 0x07) {
case 0:
pdiv = 1;
cp = "selected clock";
break;
case 1:
pdiv = 2;
cp = "clock/2";
break;
case 2:
pdiv = 4;
cp = "clock/4";
break;
case 3:
pdiv = 8;
cp = "clock/8";
break;
case 4:
pdiv = 16;
cp = "clock/16";
break;
case 5:
pdiv = 32;
cp = "clock/32";
break;
case 6:
pdiv = 64;
cp = "clock/64";
break;
case 7:
pdiv = 6;
cp = "clock/6";
break;
default:
assert(0);
break;
}
LOG_USER("(%s)", cp);
fin = fin / pdiv;
/* sam4 has a *SINGLE* clock - */
/* other at91 series parts have divisors for these. */
pChip->cfg.cpu_freq = fin;
pChip->cfg.mclk_freq = fin;
pChip->cfg.fclk_freq = fin;
LOG_USER("\t\tResult CPU Freq: %3.03f",
_tomhz(fin));
}
#if 0
static struct sam4_chip *target2sam4(struct target *pTarget)
{
struct sam4_chip *pChip;
if (pTarget == NULL)
return NULL;
pChip = all_sam4_chips;
while (pChip) {
if (pChip->target == pTarget)
break; /* return below */
else
pChip = pChip->next;
}
return pChip;
}
#endif
static uint32_t *sam4_get_reg_ptr(struct sam4_cfg *pCfg, const struct sam4_reg_list *pList)
{
/* this function exists to help */
/* keep funky offsetof() errors */
/* and casting from causing bugs */
/* By using prototypes - we can detect what would */
/* be casting errors. */
return (uint32_t *)(void *)(((char *)(pCfg)) + pList->struct_offset);
}
#define SAM4_ENTRY(NAME, FUNC) { .address = SAM4_ ## NAME, .struct_offset = offsetof( \
struct sam4_cfg, \
NAME), # NAME, FUNC }
static const struct sam4_reg_list sam4_all_regs[] = {
SAM4_ENTRY(CKGR_MOR, sam4_explain_ckgr_mor),
SAM4_ENTRY(CKGR_MCFR, sam4_explain_ckgr_mcfr),
SAM4_ENTRY(CKGR_PLLAR, sam4_explain_ckgr_plla),
SAM4_ENTRY(CKGR_UCKR, NULL),
SAM4_ENTRY(PMC_FSMR, NULL),
SAM4_ENTRY(PMC_FSPR, NULL),
SAM4_ENTRY(PMC_IMR, NULL),
SAM4_ENTRY(PMC_MCKR, sam4_explain_mckr),
SAM4_ENTRY(PMC_PCK0, NULL),
SAM4_ENTRY(PMC_PCK1, NULL),
SAM4_ENTRY(PMC_PCK2, NULL),
SAM4_ENTRY(PMC_PCSR, NULL),
SAM4_ENTRY(PMC_SCSR, NULL),
SAM4_ENTRY(PMC_SR, NULL),
SAM4_ENTRY(CHIPID_CIDR, sam4_explain_chipid_cidr),
SAM4_ENTRY(CHIPID_EXID, NULL),
/* TERMINATE THE LIST */
{ .name = NULL }
};
#undef SAM4_ENTRY
static struct sam4_bank_private *get_sam4_bank_private(struct flash_bank *bank)
{
return bank->driver_priv;
}
/**
* Given a pointer to where it goes in the structure,
* determine the register name, address from the all registers table.
*/
static const struct sam4_reg_list *sam4_GetReg(struct sam4_chip *pChip, uint32_t *goes_here)
{
const struct sam4_reg_list *pReg;
pReg = &(sam4_all_regs[0]);
while (pReg->name) {
uint32_t *pPossible;
/* calculate where this one go.. */
/* it is "possibly" this register. */
pPossible = ((uint32_t *)(void *)(((char *)(&(pChip->cfg))) + pReg->struct_offset));
/* well? Is it this register */
if (pPossible == goes_here) {
/* Jump for joy! */
return pReg;
}
/* next... */
pReg++;
}
/* This is *TOTAL*PANIC* - we are totally screwed. */
LOG_ERROR("INVALID SAM4 REGISTER");
return NULL;
}
static int sam4_ReadThisReg(struct sam4_chip *pChip, uint32_t *goes_here)
{
const struct sam4_reg_list *pReg;
int r;
pReg = sam4_GetReg(pChip, goes_here);
if (!pReg)
return ERROR_FAIL;
r = target_read_u32(pChip->target, pReg->address, goes_here);
if (r != ERROR_OK) {
LOG_ERROR("Cannot read SAM4 register: %s @ 0x%08x, Err: %d",
pReg->name, (unsigned)(pReg->address), r);
}
return r;
}
static int sam4_ReadAllRegs(struct sam4_chip *pChip)
{
int r;
const struct sam4_reg_list *pReg;
pReg = &(sam4_all_regs[0]);
while (pReg->name) {
r = sam4_ReadThisReg(pChip,
sam4_get_reg_ptr(&(pChip->cfg), pReg));
if (r != ERROR_OK) {
LOG_ERROR("Cannot read SAM4 register: %s @ 0x%08x, Error: %d",
pReg->name, ((unsigned)(pReg->address)), r);
return r;
}
pReg++;
}
return ERROR_OK;
}
static int sam4_GetInfo(struct sam4_chip *pChip)
{
const struct sam4_reg_list *pReg;
uint32_t regval;
pReg = &(sam4_all_regs[0]);
while (pReg->name) {
/* display all regs */
LOG_DEBUG("Start: %s", pReg->name);
regval = *sam4_get_reg_ptr(&(pChip->cfg), pReg);
LOG_USER("%*s: [0x%08" PRIx32 "] -> 0x%08" PRIx32,
REG_NAME_WIDTH,
pReg->name,
pReg->address,
regval);
if (pReg->explain_func)
(*(pReg->explain_func))(pChip);
LOG_DEBUG("End: %s", pReg->name);
pReg++;
}
LOG_USER(" rc-osc: %3.03f MHz", _tomhz(pChip->cfg.rc_freq));
LOG_USER(" mainosc: %3.03f MHz", _tomhz(pChip->cfg.mainosc_freq));
LOG_USER(" plla: %3.03f MHz", _tomhz(pChip->cfg.plla_freq));
LOG_USER(" cpu-freq: %3.03f MHz", _tomhz(pChip->cfg.cpu_freq));
LOG_USER("mclk-freq: %3.03f MHz", _tomhz(pChip->cfg.mclk_freq));
LOG_USER(" UniqueId: 0x%08" PRIx32 " 0x%08" PRIx32 " 0x%08" PRIx32 " 0x%08"PRIx32,
pChip->cfg.unique_id[0],
pChip->cfg.unique_id[1],
pChip->cfg.unique_id[2],
pChip->cfg.unique_id[3]);
return ERROR_OK;
}
static int sam4_protect_check(struct flash_bank *bank)
{
int r;
uint32_t v[4] = {0};
unsigned x;
struct sam4_bank_private *pPrivate;
LOG_DEBUG("Begin");
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
pPrivate = get_sam4_bank_private(bank);
if (!pPrivate) {
LOG_ERROR("no private for this bank?");
return ERROR_FAIL;
}
if (!(pPrivate->probed))
return ERROR_FLASH_BANK_NOT_PROBED;
r = FLASHD_GetLockBits(pPrivate, v);
if (r != ERROR_OK) {
LOG_DEBUG("Failed: %d", r);
return r;
}
for (x = 0; x < pPrivate->nsectors; x++)
bank->sectors[x].is_protected = (!!(v[x >> 5] & (1 << (x % 32))));
LOG_DEBUG("Done");
return ERROR_OK;
}
FLASH_BANK_COMMAND_HANDLER(sam4_flash_bank_command)
{
struct sam4_chip *pChip;
pChip = all_sam4_chips;
/* is this an existing chip? */
while (pChip) {
if (pChip->target == bank->target)
break;
pChip = pChip->next;
}
if (!pChip) {
/* this is a *NEW* chip */
pChip = calloc(1, sizeof(struct sam4_chip));
if (!pChip) {
LOG_ERROR("NO RAM!");
return ERROR_FAIL;
}
pChip->target = bank->target;
/* insert at head */
pChip->next = all_sam4_chips;
all_sam4_chips = pChip;
pChip->target = bank->target;
/* assumption is this runs at 32khz */
pChip->cfg.slow_freq = 32768;
pChip->probed = 0;
}
switch (bank->base) {
default:
LOG_ERROR("Address 0x%08x invalid bank address (try 0x%08x"
"[at91sam4s series] )",
((unsigned int)(bank->base)),
((unsigned int)(FLASH_BANK_BASE_S)));
return ERROR_FAIL;
break;
/* at91sam4s series only has bank 0*/
/* at91sam4sd series has the same address for bank 0 (FLASH_BANK0_BASE_SD)*/
case FLASH_BANK_BASE_S:
case FLASH_BANK_BASE_C:
bank->driver_priv = &(pChip->details.bank[0]);
bank->bank_number = 0;
pChip->details.bank[0].pChip = pChip;
pChip->details.bank[0].pBank = bank;
break;
/* Bank 1 of at91sam4sd/at91sam4c32 series */
case FLASH_BANK1_BASE_1024K_SD:
case FLASH_BANK1_BASE_2048K_SD:
case FLASH_BANK1_BASE_C32:
bank->driver_priv = &(pChip->details.bank[1]);
bank->bank_number = 1;
pChip->details.bank[1].pChip = pChip;
pChip->details.bank[1].pBank = bank;
break;
}
/* we initialize after probing. */
return ERROR_OK;
}
/**
* Remove all chips from the internal list without distingushing which one
* is owned by this bank. This simplification works only for one shot
* deallocation like current flash_free_all_banks()
*/
static void sam4_free_driver_priv(struct flash_bank *bank)
{
struct sam4_chip *chip = all_sam4_chips;
while (chip) {
struct sam4_chip *next = chip->next;
free(chip);
chip = next;
}
all_sam4_chips = NULL;
}
static int sam4_GetDetails(struct sam4_bank_private *pPrivate)
{
const struct sam4_chip_details *pDetails;
struct sam4_chip *pChip;
struct flash_bank *saved_banks[SAM4_MAX_FLASH_BANKS];
unsigned x;
LOG_DEBUG("Begin");
pDetails = all_sam4_details;
while (pDetails->name) {
/* Compare cidr without version bits */
if (pDetails->chipid_cidr == (pPrivate->pChip->cfg.CHIPID_CIDR & 0xFFFFFFE0))
break;
else
pDetails++;
}
if (pDetails->name == NULL) {
LOG_ERROR("SAM4 ChipID 0x%08x not found in table (perhaps you can ID this chip?)",
(unsigned int)(pPrivate->pChip->cfg.CHIPID_CIDR));
/* Help the victim, print details about the chip */
LOG_INFO("SAM4 CHIPID_CIDR: 0x%08" PRIx32 " decodes as follows",
pPrivate->pChip->cfg.CHIPID_CIDR);
sam4_explain_chipid_cidr(pPrivate->pChip);
return ERROR_FAIL;
} else {
LOG_INFO("SAM4 Found chip %s, CIDR 0x%08x", pDetails->name, pDetails->chipid_cidr);
}
/* DANGER: THERE ARE DRAGONS HERE */
/* get our pChip - it is going */
/* to be over-written shortly */
pChip = pPrivate->pChip;
/* Note that, in reality: */
/* */
/* pPrivate = &(pChip->details.bank[0]) */
/* or pPrivate = &(pChip->details.bank[1]) */
/* */
/* save the "bank" pointers */
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++)
saved_banks[x] = pChip->details.bank[x].pBank;
/* Overwrite the "details" structure. */
memcpy(&(pPrivate->pChip->details),
pDetails,
sizeof(pPrivate->pChip->details));
/* now fix the ghosted pointers */
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) {
pChip->details.bank[x].pChip = pChip;
pChip->details.bank[x].pBank = saved_banks[x];
}
/* update the *BANK*SIZE* */
LOG_DEBUG("End");
return ERROR_OK;
}
static int _sam4_probe(struct flash_bank *bank, int noise)
{
unsigned x;
int r;
struct sam4_bank_private *pPrivate;
LOG_DEBUG("Begin: Bank: %d, Noise: %d", bank->bank_number, noise);
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
pPrivate = get_sam4_bank_private(bank);
if (!pPrivate) {
LOG_ERROR("Invalid/unknown bank number");
return ERROR_FAIL;
}
r = sam4_ReadAllRegs(pPrivate->pChip);
if (r != ERROR_OK)
return r;
LOG_DEBUG("Here");
if (pPrivate->pChip->probed)
r = sam4_GetInfo(pPrivate->pChip);
else
r = sam4_GetDetails(pPrivate);
if (r != ERROR_OK)
return r;
/* update the flash bank size */
for (x = 0; x < SAM4_MAX_FLASH_BANKS; x++) {
if (bank->base == pPrivate->pChip->details.bank[x].base_address) {
bank->size = pPrivate->pChip->details.bank[x].size_bytes;
LOG_INFO("SAM4 Set flash bank to %08X - %08X, idx %d", bank->base, bank->base + bank->size, x);
break;
}
}
if (bank->sectors == NULL) {
bank->sectors = calloc(pPrivate->nsectors, (sizeof((bank->sectors)[0])));
if (bank->sectors == NULL) {
LOG_ERROR("No memory!");
return ERROR_FAIL;
}
bank->num_sectors = pPrivate->nsectors;
for (x = 0; ((int)(x)) < bank->num_sectors; x++) {
bank->sectors[x].size = pPrivate->sector_size;
bank->sectors[x].offset = x * (pPrivate->sector_size);
/* mark as unknown */
bank->sectors[x].is_erased = -1;
bank->sectors[x].is_protected = -1;
}
}
pPrivate->probed = 1;
r = sam4_protect_check(bank);
if (r != ERROR_OK)
return r;
LOG_DEBUG("Bank = %d, nbanks = %d",
pPrivate->bank_number, pPrivate->pChip->details.n_banks);
if ((pPrivate->bank_number + 1) == pPrivate->pChip->details.n_banks) {
/* read unique id, */
/* it appears to be associated with the *last* flash bank. */
FLASHD_ReadUniqueID(pPrivate);
}
return r;
}
static int sam4_probe(struct flash_bank *bank)
{
return _sam4_probe(bank, 1);
}
static int sam4_auto_probe(struct flash_bank *bank)
{
return _sam4_probe(bank, 0);
}
static int sam4_erase(struct flash_bank *bank, int first, int last)
{
struct sam4_bank_private *pPrivate;
int r;
int i;
int pageCount;
/*16 pages equals 8KB - Same size as a lock region*/
pageCount = 16;
uint32_t status;
LOG_DEBUG("Here");
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
r = sam4_auto_probe(bank);
if (r != ERROR_OK) {
LOG_DEBUG("Here,r=%d", r);
return r;
}
pPrivate = get_sam4_bank_private(bank);
if (!(pPrivate->probed))
return ERROR_FLASH_BANK_NOT_PROBED;
if ((first == 0) && ((last + 1) == ((int)(pPrivate->nsectors)))) {
/* whole chip */
LOG_DEBUG("Here");
return FLASHD_EraseEntireBank(pPrivate);
}
LOG_INFO("sam4 does not auto-erase while programming (Erasing relevant sectors)");
LOG_INFO("sam4 First: 0x%08x Last: 0x%08x", (unsigned int)(first), (unsigned int)(last));
for (i = first; i <= last; i++) {
/*16 pages equals 8KB - Same size as a lock region*/
r = FLASHD_ErasePages(pPrivate, (i * pageCount), pageCount, &status);
LOG_INFO("Erasing sector: 0x%08x", (unsigned int)(i));
if (r != ERROR_OK)
LOG_ERROR("SAM4: Error performing Erase page @ lock region number %d",
(unsigned int)(i));
if (status & (1 << 2)) {
LOG_ERROR("SAM4: Lock Region %d is locked", (unsigned int)(i));
return ERROR_FAIL;
}
if (status & (1 << 1)) {
LOG_ERROR("SAM4: Flash Command error @lock region %d", (unsigned int)(i));
return ERROR_FAIL;
}
}
return ERROR_OK;
}
static int sam4_protect(struct flash_bank *bank, int set, int first, int last)
{
struct sam4_bank_private *pPrivate;
int r;
LOG_DEBUG("Here");
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
return ERROR_TARGET_NOT_HALTED;
}
pPrivate = get_sam4_bank_private(bank);
if (!(pPrivate->probed))
return ERROR_FLASH_BANK_NOT_PROBED;
if (set)
r = FLASHD_Lock(pPrivate, (unsigned)(first), (unsigned)(last));
else
r = FLASHD_Unlock(pPrivate, (unsigned)(first), (unsigned)(last));
LOG_DEBUG("End: r=%d", r);
return r;
}
static int sam4_page_read(struct sam4_bank_private *pPrivate, unsigned pagenum, uint8_t *buf)
{
uint32_t adr;
int r;
adr = pagenum * pPrivate->page_size;
adr = adr + pPrivate->base_address;
r = target_read_memory(pPrivate->pChip->target,
adr,
4, /* THIS*MUST*BE* in 32bit values */
pPrivate->page_size / 4,
buf);
if (r != ERROR_OK)
LOG_ERROR("SAM4: Flash program failed to read page phys address: 0x%08x",
(unsigned int)(adr));
return r;
}
static int sam4_page_write(struct sam4_bank_private *pPrivate, unsigned pagenum, const uint8_t *buf)
{
uint32_t adr;
uint32_t status;
uint32_t fmr; /* EEFC Flash Mode Register */
int r;
adr = pagenum * pPrivate->page_size;
adr = (adr + pPrivate->base_address);
/* Get flash mode register value */
r = target_read_u32(pPrivate->pChip->target, pPrivate->controller_address, &fmr);
if (r != ERROR_OK)
LOG_DEBUG("Error Read failed: read flash mode register");
/* Clear flash wait state field */
fmr &= 0xfffff0ff;
/* set FWS (flash wait states) field in the FMR (flash mode register) */
fmr |= (pPrivate->flash_wait_states << 8);
LOG_DEBUG("Flash Mode: 0x%08x", ((unsigned int)(fmr)));
r = target_write_u32(pPrivate->pBank->target, pPrivate->controller_address, fmr);
if (r != ERROR_OK)
LOG_DEBUG("Error Write failed: set flash mode register");
/* 1st sector 8kBytes - page 0 - 15*/
/* 2nd sector 8kBytes - page 16 - 30*/
/* 3rd sector 48kBytes - page 31 - 127*/
LOG_DEBUG("Wr Page %u @ phys address: 0x%08x", pagenum, (unsigned int)(adr));
r = target_write_memory(pPrivate->pChip->target,
adr,
4, /* THIS*MUST*BE* in 32bit values */
pPrivate->page_size / 4,
buf);
if (r != ERROR_OK) {
LOG_ERROR("SAM4: Failed to write (buffer) page at phys address 0x%08x",
(unsigned int)(adr));
return r;
}
r = EFC_PerformCommand(pPrivate,
/* send Erase & Write Page */
AT91C_EFC_FCMD_WP, /*AT91C_EFC_FCMD_EWP only works on first two 8kb sectors*/
pagenum,
&status);
if (r != ERROR_OK)
LOG_ERROR("SAM4: Error performing Write page @ phys address 0x%08x",
(unsigned int)(adr));
if (status & (1 << 2)) {
LOG_ERROR("SAM4: Page @ Phys address 0x%08x is locked", (unsigned int)(adr));
return ERROR_FAIL;
}
if (status & (1 << 1)) {
LOG_ERROR("SAM4: Flash Command error @phys address 0x%08x", (unsigned int)(adr));
return ERROR_FAIL;
}
return ERROR_OK;
}
static int sam4_write(struct flash_bank *bank,
const uint8_t *buffer,
uint32_t offset,
uint32_t count)
{
int n;
unsigned page_cur;
unsigned page_end;
int r;
unsigned page_offset;
struct sam4_bank_private *pPrivate;
uint8_t *pagebuffer;
/* incase we bail further below, set this to null */
pagebuffer = NULL;
/* ignore dumb requests */
if (count == 0) {
r = ERROR_OK;
goto done;
}
if (bank->target->state != TARGET_HALTED) {
LOG_ERROR("Target not halted");
r = ERROR_TARGET_NOT_HALTED;
goto done;
}
pPrivate = get_sam4_bank_private(bank);
if (!(pPrivate->probed)) {
r = ERROR_FLASH_BANK_NOT_PROBED;
goto done;
}
if ((offset + count) > pPrivate->size_bytes) {
LOG_ERROR("Flash write error - past end of bank");
LOG_ERROR(" offset: 0x%08x, count 0x%08x, BankEnd: 0x%08x",
(unsigned int)(offset),
(unsigned int)(count),
(unsigned int)(pPrivate->size_bytes));
r = ERROR_FAIL;
goto done;
}
pagebuffer = malloc(pPrivate->page_size);
if (!pagebuffer) {
LOG_ERROR("No memory for %d Byte page buffer", (int)(pPrivate->page_size));
r = ERROR_FAIL;
goto done;
}
/* what page do we start & end in? */
page_cur = offset / pPrivate->page_size;
page_end = (offset + count - 1) / pPrivate->page_size;
LOG_DEBUG("Offset: 0x%08x, Count: 0x%08x", (unsigned int)(offset), (unsigned int)(count));
LOG_DEBUG("Page start: %d, Page End: %d", (int)(page_cur), (int)(page_end));
/* Special case: all one page */
/* */
/* Otherwise: */
/* (1) non-aligned start */
/* (2) body pages */
/* (3) non-aligned end. */
/* Handle special case - all one page. */
if (page_cur == page_end) {
LOG_DEBUG("Special case, all in one page");
r = sam4_page_read(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
page_offset = (offset & (pPrivate->page_size-1));
memcpy(pagebuffer + page_offset,
buffer,
count);
r = sam4_page_write(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
r = ERROR_OK;
goto done;
}
/* non-aligned start */
page_offset = offset & (pPrivate->page_size - 1);
if (page_offset) {
LOG_DEBUG("Not-Aligned start");
/* read the partial */
r = sam4_page_read(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
/* over-write with new data */
n = (pPrivate->page_size - page_offset);
memcpy(pagebuffer + page_offset,
buffer,
n);
r = sam4_page_write(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
count -= n;
offset += n;
buffer += n;
page_cur++;
}
/* By checking that offset is correct here, we also
fix a clang warning */
assert(offset % pPrivate->page_size == 0);
/* intermediate large pages */
/* also - the final *terminal* */
/* if that terminal page is a full page */
LOG_DEBUG("Full Page Loop: cur=%d, end=%d, count = 0x%08x",
(int)page_cur, (int)page_end, (unsigned int)(count));
while ((page_cur < page_end) &&
(count >= pPrivate->page_size)) {
r = sam4_page_write(pPrivate, page_cur, buffer);
if (r != ERROR_OK)
goto done;
count -= pPrivate->page_size;
buffer += pPrivate->page_size;
page_cur += 1;
}
/* terminal partial page? */
if (count) {
LOG_DEBUG("Terminal partial page, count = 0x%08x", (unsigned int)(count));
/* we have a partial page */
r = sam4_page_read(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
/* data goes at start */
memcpy(pagebuffer, buffer, count);
r = sam4_page_write(pPrivate, page_cur, pagebuffer);
if (r != ERROR_OK)
goto done;
}
LOG_DEBUG("Done!");
r = ERROR_OK;
done:
if (pagebuffer)
free(pagebuffer);
return r;
}
COMMAND_HANDLER(sam4_handle_info_command)
{
struct sam4_chip *pChip;
pChip = get_current_sam4(CMD_CTX);
if (!pChip)
return ERROR_OK;
unsigned x;
int r;
/* bank0 must exist before we can do anything */
if (pChip->details.bank[0].pBank == NULL) {
x = 0;
need_define:
command_print(CMD_CTX,
"Please define bank %d via command: flash bank %s ... ",
x,
at91sam4_flash.name);
return ERROR_FAIL;
}
/* if bank 0 is not probed, then probe it */
if (!(pChip->details.bank[0].probed)) {
r = sam4_auto_probe(pChip->details.bank[0].pBank);
if (r != ERROR_OK)
return ERROR_FAIL;
}
/* above guarantees the "chip details" structure is valid */
/* and thus, bank private areas are valid */
/* and we have a SAM4 chip, what a concept! */
/* auto-probe other banks, 0 done above */
for (x = 1; x < SAM4_MAX_FLASH_BANKS; x++) {
/* skip banks not present */
if (!(pChip->details.bank[x].present))
continue;
if (pChip->details.bank[x].pBank == NULL)
goto need_define;
if (pChip->details.bank[x].probed)
continue;
r = sam4_auto_probe(pChip->details.bank[x].pBank);
if (r != ERROR_OK)
return r;
}
r = sam4_GetInfo(pChip);
if (r != ERROR_OK) {
LOG_DEBUG("Sam4Info, Failed %d", r);
return r;
}
return ERROR_OK;
}
COMMAND_HANDLER(sam4_handle_gpnvm_command)
{
unsigned x, v;
int r, who;
struct sam4_chip *pChip;
pChip = get_current_sam4(CMD_CTX);
if (!pChip)
return ERROR_OK;
if (pChip->target->state != TARGET_HALTED) {
LOG_ERROR("sam4 - target not halted");
return ERROR_TARGET_NOT_HALTED;
}
if (pChip->details.bank[0].pBank == NULL) {
command_print(CMD_CTX, "Bank0 must be defined first via: flash bank %s ...",
at91sam4_flash.name);
return ERROR_FAIL;
}
if (!pChip->details.bank[0].probed) {
r = sam4_auto_probe(pChip->details.bank[0].pBank);
if (r != ERROR_OK)
return r;
}
switch (CMD_ARGC) {
default:
return ERROR_COMMAND_SYNTAX_ERROR;
break;
case 0:
goto showall;
break;
case 1:
who = -1;
break;
case 2:
if ((0 == strcmp(CMD_ARGV[0], "show")) && (0 == strcmp(CMD_ARGV[1], "all")))
who = -1;
else {
uint32_t v32;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], v32);
who = v32;
}
break;
}
if (0 == strcmp("show", CMD_ARGV[0])) {
if (who == -1) {
showall:
r = ERROR_OK;
for (x = 0; x < pChip->details.n_gpnvms; x++) {
r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), x, &v);
if (r != ERROR_OK)
break;
command_print(CMD_CTX, "sam4-gpnvm%u: %u", x, v);
}
return r;
}
if ((who >= 0) && (((unsigned)(who)) < pChip->details.n_gpnvms)) {
r = FLASHD_GetGPNVM(&(pChip->details.bank[0]), who, &v);
command_print(CMD_CTX, "sam4-gpnvm%u: %u", who, v);
return r;
} else {
command_print(CMD_CTX, "sam4-gpnvm invalid GPNVM: %u", who);
return ERROR_COMMAND_SYNTAX_ERROR;
}
}
if (who == -1) {
command_print(CMD_CTX, "Missing GPNVM number");
return ERROR_COMMAND_SYNTAX_ERROR;
}
if (0 == strcmp("set", CMD_ARGV[0]))
r = FLASHD_SetGPNVM(&(pChip->details.bank[0]), who);
else if ((0 == strcmp("clr", CMD_ARGV[0])) ||
(0 == strcmp("clear", CMD_ARGV[0]))) /* quietly accept both */
r = FLASHD_ClrGPNVM(&(pChip->details.bank[0]), who);
else {
command_print(CMD_CTX, "Unknown command: %s", CMD_ARGV[0]);
r = ERROR_COMMAND_SYNTAX_ERROR;
}
return r;
}
COMMAND_HANDLER(sam4_handle_slowclk_command)
{
struct sam4_chip *pChip;
pChip = get_current_sam4(CMD_CTX);
if (!pChip)
return ERROR_OK;
switch (CMD_ARGC) {
case 0:
/* show */
break;
case 1:
{
/* set */
uint32_t v;
COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], v);
if (v > 200000) {
/* absurd slow clock of 200Khz? */
command_print(CMD_CTX, "Absurd/illegal slow clock freq: %d\n", (int)(v));
return ERROR_COMMAND_SYNTAX_ERROR;
}
pChip->cfg.slow_freq = v;
break;
}
default:
/* error */
command_print(CMD_CTX, "Too many parameters");
return ERROR_COMMAND_SYNTAX_ERROR;
break;
}
command_print(CMD_CTX, "Slowclk freq: %d.%03dkhz",
(int)(pChip->cfg.slow_freq / 1000),
(int)(pChip->cfg.slow_freq % 1000));
return ERROR_OK;
}
static const struct command_registration at91sam4_exec_command_handlers[] = {
{
.name = "gpnvm",
.handler = sam4_handle_gpnvm_command,
.mode = COMMAND_EXEC,
.usage = "[('clr'|'set'|'show') bitnum]",
.help = "Without arguments, shows all bits in the gpnvm "
"register. Otherwise, clears, sets, or shows one "
"General Purpose Non-Volatile Memory (gpnvm) bit.",
},
{
.name = "info",
.handler = sam4_handle_info_command,
.mode = COMMAND_EXEC,
.help = "Print information about the current at91sam4 chip"
"and its flash configuration.",
},
{
.name = "slowclk",
.handler = sam4_handle_slowclk_command,
.mode = COMMAND_EXEC,
.usage = "[clock_hz]",
.help = "Display or set the slowclock frequency "
"(default 32768 Hz).",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration at91sam4_command_handlers[] = {
{
.name = "at91sam4",
.mode = COMMAND_ANY,
.help = "at91sam4 flash command group",
.usage = "",
.chain = at91sam4_exec_command_handlers,
},
COMMAND_REGISTRATION_DONE
};
struct flash_driver at91sam4_flash = {
.name = "at91sam4",
.commands = at91sam4_command_handlers,
.flash_bank_command = sam4_flash_bank_command,
.erase = sam4_erase,
.protect = sam4_protect,
.write = sam4_write,
.read = default_flash_read,
.probe = sam4_probe,
.auto_probe = sam4_auto_probe,
.erase_check = default_flash_blank_check,
.protect_check = sam4_protect_check,
.free_driver_priv = sam4_free_driver_priv,
};