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