patch #9816: Implement new programmer type: linuxspi

* linuxspi.c: (New file.)
* linuxspi.h: (New file.)
* Makefile.am: Add new files
* configure.ac: Add "linuxspi" --enable option
* avrdude.conf.in: Add "linuxspi" programmer template
* pgm_type.c: Include linuxspi programmer
* doc/avrdude.texi: Document new programmer
* avrdude.1: (Dito.)

Submitted by Ralf Ramsauer



git-svn-id: svn://svn.savannah.nongnu.org/avrdude/trunk/avrdude@1447 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
Joerg Wunsch 2020-09-19 21:32:38 +00:00
parent 9b3762a812
commit 40b0b104d6
10 changed files with 456 additions and 2 deletions

View File

@ -1,3 +1,16 @@
2020-09-19 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
Submitted by Ralf Ramsauer
patch #9816: Implement new programmer type: linuxspi
* linuxspi.c: (New file.)
* linuxspi.h: (New file.)
* Makefile.am: Add new files
* configure.ac: Add "linuxspi" --enable option
* avrdude.conf.in: Add "linuxspi" programmer template
* pgm_type.c: Include linuxspi programmer
* doc/avrdude.texi: Document new programmer
* avrdude.1: (Dito.)
2020-09-18 Joerg Wunsch <j.gnu@uriah.heep.sax.de>
Submitted by Marcin Miskiewic

View File

@ -149,6 +149,8 @@ libavrdude_a_SOURCES = \
libavrdude.h \
linuxgpio.c \
linuxgpio.h \
linuxspi.c \
linuxspi.h \
linux_ppdev.h \
lists.c \
my_ddk_hidsdi.h \

2
NEWS
View File

@ -38,6 +38,7 @@ Current:
- XplainedMini in UPDI mode
- JTAGICE3 in UPDI mode
- Atmel Powerdebugger in all modes (JTAG, PDI, UPDI, debugWIRE, ISP)
- linuxspi (direct SPI bus e.g. on Raspberry Pi devices)
* Bugfixes:
bug #47550: Linux GPIO broken
@ -76,6 +77,7 @@ Current:
patch #9732: usbtiny_paged_load overflows buffer e.g. when reading EEPROM
patch #9966: Add JTAGICE3 in UPDI mode
patch #9963: UsbAsp 3 MHz patch for UsbAsp-flash firmware
patch #9816: Implement new programmer type: linuxspi
* Internals:
- New avrdude.conf keyword "family_id", used to verify SIB attributes

View File

@ -1,6 +1,6 @@
.\"
.\" avrdude - A Downloader/Uploader for AVR device programmers
.\" Copyright (C) 2001, 2002, 2003, 2005 - 2016 Joerg Wunsch
.\" Copyright (C) 2001, 2002, 2003, 2005 - 2020 Joerg Wunsch
.\"
.\" 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
@ -18,7 +18,7 @@
.\"
.\" $Id$
.\"
.Dd DATE February 15, 2016
.Dd DATE September 19, 2020
.Os
.Dt AVRDUDE 1
.Sh NAME
@ -109,6 +109,34 @@ some resistors in series or better yet use a 3-state buffer driver like
the 74HC244. Have a look at http://kolev.info/avrdude-linuxgpio for a more
detailed tutorial about using this programmer type.
.Pp
Under a Linux installation with direct access to the SPI bus and GPIO
pins, such as would be found on a Raspberry Pi, the ``linuxspi''
programmer type can be used to directly connect to and program a chip
using the built in interfaces on the computer. The requirements to use
this type are that an SPI interface is exposed along with one GPIO
pin. The GPIO serves as the reset output since the Linux SPI drivers
do not hold slave select down when a transfer is not occuring and thus
it cannot be used as the reset pin. A readily available level
translator should be used between the SPI bus/reset GPIO and the chip
to avoid potentially damaging the computer's SPI controller in the
event that the chip is running at 5V and the SPI runs at 3.3V. The
GPIO chosen for reset can be configured in the avrdude configuration
file using the
.Li reset
entry under the linuxspi programmer, or
directly in the port specification. An external pull-up resistor
should be connected between the AVR's reset pin and Vcc. If Vcc is not
the same as the SPI voltage, this should be done on the AVR side of
the level translator to protect the hardware from damage.
.Pp
A commented-out template for this programmer is provided in the
avrdude configuration file. To use it, clone that entry into the
per-user configuration file, and configure the
.Li reset
GPIO
number accordingly. Linuxspi can be used as follows:
.Dl avrdude -c linuxspi -P /dev/spidev:/dev/gpiochip[:resetpin]
.Pp
Atmel's STK500 programmer is also supported and connects to a serial
port.
Both, firmware versions 1.x and 2.x can be handled, but require a

View File

@ -1487,6 +1487,23 @@ programmer
# miso = ?;
#;
# This programmer uses the built in linux SPI bus devices to program an
# attached AVR. A GPIO accessed through the sysfs GPIO interface needs to
# be specified for a reset pin since the linux SPI userspace functions do
# not allow for control over the slave select/chip select signal.
#
# To use it, copy this snippet into your ~/.avrduderc, make sure the
# 'reset' entry is configured correctly.
#
# programmer
# id = "linuxspi";
# desc = "Use Linux SPI device in /dev/spidev*";
# type = "linuxspi";
# reset = 25;
# baudrate=400000;
# ;
# some ultra cheap programmers use bitbanging on the
# serialport.
#

View File

@ -336,6 +336,18 @@ AC_ARG_ENABLE(
esac],
[enabled_linuxgpio=no])
AC_ARG_ENABLE(
[linuxspi],
AC_HELP_STRING(
[--enable-linuxspi],
[Enable the Linux SPIDEV interface programmer type]),
[case "${enableval}" in
yes) enabled_linuxspi=yes ;;
no) enabled_linuxspi=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for enable-linuxspi option) ;;
esac],
[enabled_linuxspi=no])
DIST_SUBDIRS_AC='doc windows'
if test "$enabled_doc" = "yes"; then
@ -413,6 +425,14 @@ else
fi
if test "$enabled_linuxspi" = "yes"; then
AC_DEFINE(HAVE_LINUXSPI, 1, [Linux SPI support enabled])
confsubst="$confsubst -e /^@HAVE_LINUXSPI_/d"
else
confsubst="$confsubst -e /^@HAVE_LINUXSPI_BEGIN@/,/^@HAVE_LINUXSPI_END@/d"
fi
# If we are compiling with gcc, enable all warning and make warnings errors.
if test "$GCC" = yes; then
ENABLE_WARNINGS="-Wall"
@ -590,3 +610,9 @@ else
echo "DISABLED linuxgpio"
fi
if test x$enabled_linuxspi = xyes; then
echo "ENABLED linuxspi"
else
echo "DISABLED linuxspi"
fi

View File

@ -180,6 +180,33 @@ some resistors in series or better yet use a 3-state buffer driver like
the 74HC244. Have a look at http://kolev.info/avrdude-linuxgpio for a more
detailed tutorial about using this programmer type.
Under a Linux installation with direct access to the SPI bus and GPIO
pins, such as would be found on a Raspberry Pi, the ``linuxspi''
programmer type can be used to directly connect to and program a chip
using the built in interfaces on the computer. The requirements to use
this type are that an SPI interface is exposed along with one GPIO
pin. The GPIO serves as the reset output since the Linux SPI drivers
do not hold slave select down when a transfer is not occuring and thus
it cannot be used as the reset pin. A readily available level
translator should be used between the SPI bus/reset GPIO and the chip
to avoid potentially damaging the computer's SPI controller in the
event that the chip is running at 5V and the SPI runs at 3.3V. The
GPIO chosen for reset can be configured in the avrdude configuration
file using the @code{reset} entry under the linuxspi programmer, or
directly in the port specification. An external pull-up resistor
should be connected between the AVR's reset pin and Vcc. If Vcc is not
the same as the SPI voltage, this should be done on the AVR side of
the level translator to protect the hardware from damage.
A commented-out template for this programmer is provided in the
avrdude configuration file. To use it, clone that entry into the
per-user configuration file, and configure the @code{reset} GPIO
number accordingly. Linuxspi can be used as follows:
@smallexample
avrdude -c linuxspi -P /dev/spidev:/dev/gpiochip[:resetpin]
@end smallexample
The STK500, JTAG ICE, avr910, and avr109/butterfly use the serial port to communicate with the PC.
The STK600, JTAG ICE mkII/3, AVRISP mkII, USBasp, avrftdi (and derivatives), and USBtinyISP
programmers communicate through the USB, using @code{libusb} as a

302
linuxspi.c Normal file
View File

@ -0,0 +1,302 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Support for using spidev userspace drivers to communicate directly over SPI
*
* Copyright (C) 2013 Kevin Cuzner <kevin@kevincuzner.com>
* Copyright (C) 2018 Ralf Ramsauer <ralf@vmexit.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Support for inversion of reset pin, Tim Chilton 02/05/2014
* Review code, rebase to latest trunk, add linux/gpio.h support, Ralf Ramsauer 2018-09-07
*/
#include "ac_cfg.h"
#include "avrdude.h"
#include "libavrdude.h"
#include "linuxspi.h"
#if HAVE_LINUXSPI
/**
* Linux Kernel SPI Drivers
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>
* Copyright (C) 2007 David Brownell (simplification, cleanup)
*
* 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.
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <linux/gpio.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define LINUXSPI "linuxspi"
static int fd_spidev, fd_gpiochip, fd_linehandle;
/**
* @brief Sends/receives a message in full duplex mode
* @return -1 on failure, otherwise number of bytes sent/recieved
*/
static int linuxspi_spi_duplex(PROGRAMMER *pgm, const unsigned char *tx, unsigned char *rx, int len)
{
struct spi_ioc_transfer tr;
int ret;
tr = (struct spi_ioc_transfer) {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = 1,
//should settle around 400Khz, a standard SPI speed. Adjust using baud parameter (-b)
.speed_hz = pgm->baudrate == 0 ? 400000 : pgm->baudrate,
.bits_per_word = 8,
};
ret = ioctl(fd_spidev, SPI_IOC_MESSAGE(1), &tr);
if (ret != len)
avrdude_message(MSG_INFO, "\n%s: error: Unable to send SPI message\n", progname);
return (ret == -1) ? -1 : 0;
}
static void linuxspi_setup(PROGRAMMER *pgm)
{
}
static void linuxspi_teardown(PROGRAMMER* pgm)
{
}
static int linuxspi_open(PROGRAMMER *pgm, char *port)
{
const char *port_error = "%s: error: Unknown port specification. Please use the format /dev/spidev:/dev/gpiochip[:resetno]\n";
char *spidev, *gpiochip, *reset_pin;
struct gpiohandle_request req;
struct gpiohandle_data data;
int ret;
if (!port || !strcmp(port, "unknown")) {
avrdude_message(MSG_INFO, "%s: error: No port specified. Port should point to an spidev device.\n", progname);
return -1;
}
spidev = strtok(port, ":");
if (!spidev) {
avrdude_message(MSG_INFO, port_error, progname);
return -1;
}
gpiochip = strtok(NULL, ":");
if (!gpiochip) {
avrdude_message(MSG_INFO, port_error, progname);
return -1;
}
/* optional: override reset pin in configuration */
reset_pin = strtok(NULL, ":");
if (reset_pin)
pgm->pinno[PIN_AVR_RESET] = strtoul(reset_pin, NULL, 0);
strcpy(pgm->port, port);
fd_spidev = open(pgm->port, O_RDWR);
if (fd_spidev < 0) {
avrdude_message(MSG_INFO, "\n%s: error: Unable to open the spidev device %s", progname, pgm->port);
return -1;
}
fd_gpiochip = open(gpiochip, 0);
if (fd_gpiochip < 0) {
close(fd_spidev);
avrdude_message(MSG_INFO, "\n%s error: Unable to open the gpiochip %s", progname, gpiochip);
ret = -1;
goto close_spidev;
}
strcpy(req.consumer_label, progname);
req.lines = 1;
req.lineoffsets[0] = pgm->pinno[PIN_AVR_RESET];
req.flags = GPIOHANDLE_REQUEST_OUTPUT;
ret = ioctl(fd_gpiochip, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (ret == -1) {
ret = -errno;
goto close_gpiochip;
}
fd_linehandle = req.fd;
/*
* Set the reset state and keep it. The pin will be released and set back to
* its initial value, once the fd_gpiochip is closed.
*/
data.values[0] = !!(pgm->pinno[PIN_AVR_RESET] & PIN_INVERSE);
ret = ioctl(fd_linehandle, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (ret == -1) {
ret = -errno;
goto close_out;
}
return 0;
close_out:
close(fd_linehandle);
close_gpiochip:
close(fd_gpiochip);
close_spidev:
close(fd_spidev);
return ret;
}
static void linuxspi_close(PROGRAMMER *pgm)
{
close(fd_spidev);
close(fd_gpiochip);
}
static void linuxspi_disable(PROGRAMMER* pgm)
{
}
static void linuxspi_enable(PROGRAMMER* pgm)
{
}
static void linuxspi_display(PROGRAMMER* pgm, const char* p)
{
}
static int linuxspi_initialize(PROGRAMMER *pgm, AVRPART *p)
{
int tries, ret;
if (p->flags & AVRPART_HAS_TPI) {
/* We do not support tpi. This is a dedicated SPI thing */
avrdude_message(MSG_INFO, "%s: error: Programmer " LINUXSPI " does not support TPI\n", progname);
return -1;
}
//enable programming on the part
tries = 0;
do
{
ret = pgm->program_enable(pgm, p);
if (ret == 0 || ret == -1)
break;
} while(tries++ < 65);
if (ret)
avrdude_message(MSG_INFO, "%s: error: AVR device not responding\n", progname);
return ret;
}
static int linuxspi_cmd(PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res)
{
return linuxspi_spi_duplex(pgm, cmd, res, 4);
}
static int linuxspi_program_enable(PROGRAMMER *pgm, AVRPART *p)
{
unsigned char cmd[4], res[4];
if (!p->op[AVR_OP_PGM_ENABLE]) {
avrdude_message(MSG_INFO, "%s: error: program enable instruction not defined for part \"%s\"\n", progname, p->desc);
return -1;
}
memset(cmd, 0, sizeof(cmd));
avr_set_bits(p->op[AVR_OP_PGM_ENABLE], cmd); //set the cmd
pgm->cmd(pgm, cmd, res);
if (res[2] != cmd[1])
return -2;
return 0;
}
static int linuxspi_chip_erase(PROGRAMMER *pgm, AVRPART *p)
{
unsigned char cmd[4], res[4];
if (!p->op[AVR_OP_CHIP_ERASE]) {
avrdude_message(MSG_INFO, "%s: error: chip erase instruction not defined for part \"%s\"\n", progname, p->desc);
return -1;
}
memset(cmd, 0, sizeof(cmd));
avr_set_bits(p->op[AVR_OP_CHIP_ERASE], cmd);
pgm->cmd(pgm, cmd, res);
usleep(p->chip_erase_delay);
pgm->initialize(pgm, p);
return 0;
}
void linuxspi_initpgm(PROGRAMMER *pgm)
{
strcpy(pgm->type, LINUXSPI);
pgm_fill_old_pins(pgm); // TODO to be removed if old pin data no longer needed
/* mandatory functions */
pgm->initialize = linuxspi_initialize;
pgm->display = linuxspi_display;
pgm->enable = linuxspi_enable;
pgm->disable = linuxspi_disable;
pgm->program_enable = linuxspi_program_enable;
pgm->chip_erase = linuxspi_chip_erase;
pgm->cmd = linuxspi_cmd;
pgm->open = linuxspi_open;
pgm->close = linuxspi_close;
pgm->read_byte = avr_read_byte_default;
pgm->write_byte = avr_write_byte_default;
/* optional functions */
pgm->setup = linuxspi_setup;
pgm->teardown = linuxspi_teardown;
}
const char linuxspi_desc[] = "SPI using Linux spidev driver";
#else /* !HAVE_LINUXSPI */
void linuxspi_initpgm(PROGRAMMER * pgm)
{
avrdude_message(MSG_INFO, "%s: Linux SPI driver not available in this configuration\n",
progname);
}
const char linuxspi_desc[] = "SPI using Linux spidev driver (not available)";
#endif /* HAVE_LINUXSPI */

35
linuxspi.h Normal file
View File

@ -0,0 +1,35 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2013 Kevin Cuzner <kevin@kevincuner.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef linuxspi_h
#define linuxspi_h
#ifdef __cplusplus
extern "C" {
#endif
extern const char linuxspi_desc[];
void linuxspi_initpgm (PROGRAMMER * pgm);
#ifdef __cplusplus
}
#endif
#endif //linuxspi_h

View File

@ -40,6 +40,7 @@
#include "jtagmkII.h"
#include "jtag3.h"
#include "linuxgpio.h"
#include "linuxspi.h"
#include "par.h"
#include "pickit2.h"
#include "ppi.h"
@ -81,6 +82,7 @@ const PROGRAMMER_TYPE programmers_types[] = {
{"jtagice3_dw", jtag3_dw_initpgm, jtag3_dw_desc},
{"jtagice3_isp", stk500v2_jtag3_initpgm, stk500v2_jtag3_desc},
{"linuxgpio", linuxgpio_initpgm, linuxgpio_desc},
{"linuxspi", linuxspi_initpgm, linuxspi_desc},
{"par", par_initpgm, par_desc},
{"pickit2", pickit2_initpgm, pickit2_desc},
{"serbb", serbb_initpgm, serbb_desc},