On 05/30/2017 02:32 AM, Bertho Stultiens wrote:
> After some digging I noticed that there might be a data-barrier problem,
> where peripheral register access can become out-of-order. The ARM has
> the __sync_synchronize() (via gcc) to insert DMB (data-memory-barrier)
> instructions when you need to guarantee ordering. Inserting DMB made
> things worse, such that sometimes the setup is 32MHz and sometimes
> 50MHz. Well, actually, it exposes a deeper problem.

I suddenly realized that the SPI peripheral is configured and accessed
in userspace. That will fail miserably if not extremely careful,
especially on SMP.

However, there already is a solution for this! The linux kernel has a
SPI driver, which is quite good (used it before). It solves all the
userspace problems and, to say the least, some very clever people have
had a crack at this problem before.

So, drop userspace access to the hardware and use the kernel interface.

1 - run raspi-config and enable the kernel SPI driver -> reboot
2 - you should now have /dev/spidev0.[01]
3 - replace the hm2_rpspi.c file in src/hal/drivers/mesa-hostmot2/ with
the one attached
4 - recompile
5 - run

Gene, with my (4GB) SD image do (you know the cmdline):
- run raspi-config to enable the kernel SPI driver and reboot
- save attached file on SD card as hm2_rpspi.c.new
$ cp hm2_rpspi.c.new linuxcnc-git/src/hal/drivers/mesa-hostmot2/hm2_rpspi.c
$ cd linuxcnc-git/src
$ make
$ sudo make setuid
$ ../scripts/linuxcnc -v ../../linuxcnc/configs/sheldon-lathe/7i90-axis.ini

I get the result as shown in the images. The advantage is that there are
no inter-word delays anymore. The time for chip select to be active
varies a bit (16.5 us in image 8 vs. 6.3 us data transfer in image 6).
This variability is inherent to the driver how it handles chip select
asynchronously. Still, it is an improvement, especially for large transfers.

I did add a rtapi module parameter - spiclk - which should set the clock
upon load. However, I've not been able to get that to work. Probably
something trivial I did wrong. Ah well, at least it runs at a good speed
now, also after reboots.

My hm2_rpspi driver hack is fixed to /dev/spidev0.0 (CE0 pin). This
should be a configurable parameter (features for the future).

-- 
Greetings Bertho

(disclaimers are disclaimed)
/*    This is a component for RaspberryPi to hostmot2 over SPI for linuxcnc.
 *    Copyright 2016 Matsche <tin...@play-pla.net>
 *    Copyright 2017 B.Stultiens <l...@vagrearg.org>
 *
 *    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
 */

/* Without Source Tree */
#undef WOST

#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/fsuid.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

#include "hal.h"
#include "rtapi.h"
#include "rtapi_app.h"

#include "rtapi_stdint.h"
#include "rtapi_bool.h"
#include "rtapi_gfp.h"

#include "rtapi_bool.h"

#if defined (WOST)
#include "include/hostmot2-lowlevel.h"
#include "include/hostmot2.h"
#else
#include "hostmot2-lowlevel.h"
#include "hostmot2.h"
#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matsche");
MODULE_DESCRIPTION("Driver for HostMot2 devices connected via SPI to RaspberryPi");
MODULE_SUPPORTED_DEVICE("Mesa-AnythingIO-7i90");

#define MAX_BOARDS	1		// XXX: cannot be anything else than 1 at the moment
#define MAX_MSG		512

typedef struct hm2_rpspi_struct {
	hm2_lowlevel_io_t llio;
	int nr;
	uint32_t txBuf[MAX_MSG];
	uint32_t rxBuf[MAX_MSG];
	int spiclk;
	int spifd;
} hm2_rpspi_t;

static char *config[MAX_BOARDS];
static int spiclk = 32000000;

RTAPI_MP_ARRAY_STRING(config, MAX_BOARDS, "config string for the AnyIO boards (see hostmot2(9) manpage)")
RTAPI_MP_INT(spiclk, "SPI clock frequency in Hz, default 32000000 Hz)");

static hm2_rpspi_t boards[MAX_BOARDS];
static int comp_id;

static int spi_open(const char *dev, int mode, int speed, int wsize)
{
	int fd;

	if(-1 == (fd = open(dev, O_RDWR|O_NOCTTY))) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: %s: open: %s", dev, strerror(errno));
		return -1;
	}

	if(-1 == ioctl(fd, SPI_IOC_WR_MODE, &mode)) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: %s: ioctl: SPI_IOC_WR_MODE: %s", dev, strerror(errno));
		close(fd);
		return -1;
	}

	if(-1 == ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &wsize)) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: %s: ioctl: SPI_IOC_WR_BITS_PER_WORD: %s", dev, strerror(errno));
		close(fd);
		return -1;
	}

	if(-1 == ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed)) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: %s: ioctl: SPI_IOC_WR_MAX_SPEED_HZ: %s", dev, strerror(errno));
		close(fd);
		return -1;
	}

	return fd;
}

static inline int spi_close(int fd)
{
	return close(fd);
}

/*************************************************/
// aib (address increment bit)
static inline uint32_t mk_read_cmd(uint32_t addr, unsigned msglen, bool aib)
{
	return 0 | (addr << 16) | 0xA000 | (aib ? 0x800 : 0) | (msglen << 4);
}

/*************************************************/
static inline uint32_t mk_write_cmd(uint32_t addr, unsigned msglen, bool aib)
{
	return 0 | (addr << 16) | 0xB000 | (aib ? 0x800 : 0) | (msglen << 4);
}

/*************************************************/
static int hm2_rpspi_write(hm2_lowlevel_io_t *llio, uint32_t addr, void *buffer, int size)
{
	hm2_rpspi_t *this = (hm2_rpspi_t *)llio;
	ssize_t len;

	if(!size)
		return 0;
	if(size % 4)
		return -EINVAL;
	if(size > (MAX_MSG - 1)*sizeof(this->txBuf[0]))
		return -EINVAL;

	this->txBuf[0] = mk_write_cmd(addr, size/4, true);
	memcpy(&this->txBuf[1], buffer, size);

	len = write(this->spifd, this->txBuf, size + 4);

	if(-1 == len) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Error writing spidev (%s)\n", strerror(errno));
		return -errno;
	}

	if(len != size + 4) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Error writing spidev, size mismatch write:%d -> wrote:%d\n", size + 4, (int)len);
		return 0;
	}

	return 1;
}

/*************************************************/
static int hm2_rpspi_read(hm2_lowlevel_io_t *llio, uint32_t addr, void *buffer, int size)
{
	hm2_rpspi_t *this = (hm2_rpspi_t *)llio;
	struct spi_ioc_transfer xfer;
	int res;

	if(!size)
		return 0;
	if(size % 4)
		return -EINVAL;
	if(size > (MAX_MSG - 1)*sizeof(this->txBuf[0]))
		return -EINVAL;

	memset(this->txBuf, 0, sizeof(this->txBuf));
	memset(this->rxBuf, 0, sizeof(this->rxBuf));
	this->txBuf[0] = mk_read_cmd(addr, size/4, true);

	memset(&xfer, 0, sizeof(xfer));
	xfer.tx_buf = (unsigned long)this->txBuf;
	xfer.rx_buf = (unsigned long)this->rxBuf;
	xfer.len = size + 4;

	res = ioctl(this->spifd, SPI_IOC_MESSAGE(1), &xfer);

	if(-1 == res) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Error reading spidev (%s)\n", strerror(errno));
		return -errno;
	}

	memcpy(buffer, &this->rxBuf[1], size);
	if(xfer.len != size + 4) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Error reading spidev, size mismatch read:%d -> read:%d\n", size + 4, (int)xfer.len);
		return 0;
	}

	return 1;
}

/*************************************************/
static int check_cookie(hm2_rpspi_t *board)
{
	uint32_t cookie[4];
	uint32_t xcookie[4] = {0x55aacafe, 0x54534f48, 0x32544f4d, 0x00000400};
	int r = hm2_rpspi_read(&board->llio, 0x100, cookie, 16);
	if(r < 0) return -errno;

	if(memcmp(cookie, xcookie, sizeof(cookie))) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Invalid cookie\n");
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Read: %08x %08x %08x %08x\n",
			cookie[0], cookie[1], cookie[2], cookie[3]);
		return -ENODEV;
	}
	return 0;
}

/*************************************************/
static int read_ident(hm2_rpspi_t *board, char *ident) {
	return hm2_rpspi_read(&board->llio, 0x40c, ident, 8);
}

/*************************************************/
static int probe_board(hm2_rpspi_t *board) {
	int ret;
	char ident[8+1];
	char *base;

	if((ret = check_cookie(board)) < 0)
		return ret;

	if((ret = read_ident(board, ident)) < 0)
		return ret;

	if(!memcmp(ident, "MESA7I90", 8)) {
		base = "hm2_7i90";
		board->llio.num_ioport_connectors = 3;
		board->llio.pins_per_connector = 24;
		board->llio.ioport_connector_name[0] = "P1";
		board->llio.ioport_connector_name[1] = "P2";
		board->llio.ioport_connector_name[2] = "P3";
		board->llio.num_leds = 2;
		board->llio.fpga_part_number = "xc6slx9tq144";
	} else {
		int i;
		for(i = 0; i < sizeof(ident) - 1; i++) {
			if(!isprint(ident[i]))
				ident[i] = '?';
		}
		ident[sizeof(ident)-1] = 0;
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Unknown board: %.8s\n", ident);
		return -1;
	}

	rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Base:%s.%d", base, board->nr);
	rtapi_snprintf(board->llio.name, sizeof(board->llio.name), "%s.%d", base, board->nr);
	board->llio.comp_id = comp_id;
	board->llio.private = &board;
	board->llio.read = hm2_rpspi_read;
	board->llio.write = hm2_rpspi_write;
	board->llio.queue_read = 0;
	board->llio.queue_write = 0;

	return 0;
}

/*************************************************/
static int hm2_rpspi_setup(void)
{
	int i;
	int spifd;

	if(spiclk < 0 || spiclk > 50000000) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: 'spiclk' out of range (%d), setting to 32000000 Hz\n", spiclk);
		spiclk = 32000000;
	} else {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: spiclk=%d Hz\n", spiclk);
	}

	if(-1 == (spifd = spi_open("/dev/spidev0.0", SPI_MODE_0, spiclk, 8))) {
		rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: Spidev open failed: %s\n", strerror(errno));
		return 0;
	}

	// FIXME: there is no separete spi device for other boards.
	for(i = 0; i < MAX_BOARDS; i++) {
		int retval = -1;
		boards[i].nr = i;
		boards[i].spifd = spifd;
		boards[i].spiclk = spiclk;
		if((retval = probe_board(&boards[i])) < 0) {
			spi_close(spifd);
			return retval;		// FIXME: this will not work if MAX_BOARDS > 1
		}
		if((retval = hm2_register(&boards[i].llio, config[i])) < 0) {
			spi_close(spifd);
			rtapi_print_msg(RTAPI_MSG_ERR, "hm2_rpspi: hm2_register failed (%d)", retval);
			return retval;		// FIXME: this will not work if MAX_BOARDS > 1
		}
	}

	return 0;
}

/*************************************************/
static void hm2_rpspi_cleanup(void)
{
	int i;
	for(i = 0; i < MAX_BOARDS; i++)
		spi_close(boards[i].spifd);
}

/*************************************************/
int rtapi_app_main()
{
	int ret;

	comp_id = ret = hal_init("hm2_rpspi");
	if(ret < 0)
		goto fail;

	ret = hm2_rpspi_setup();
	if(ret < 0)
		goto fail;

	hal_ready(comp_id);
	return 0;

fail:
	hm2_rpspi_cleanup();
	return ret;
}

/*************************************************/
void rtapi_app_exit(void) {
	// FIXME: shouldn't we cleanup/restore the spidev?
	hal_exit(comp_id);
}

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Emc-users mailing list
Emc-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/emc-users

Reply via email to