mirror of
https://github.com/richardghirst/PiBits.git
synced 2024-11-28 12:24:11 +01:00
Add support for B+, Pi2, and 4.1 kernels
This commit is contained in:
parent
bf455ee13b
commit
96014c804d
@ -44,15 +44,15 @@
|
||||
#define DMA_CONBLK_AD (0x04/4)
|
||||
#define DMA_DEBUG (0x20/4)
|
||||
|
||||
#define GPIO_BASE 0x20200000
|
||||
#define GPIO_BASE 0x3f200000
|
||||
#define GPIO_LEN 0xB4
|
||||
#define DMA_BASE 0x20007000
|
||||
#define DMA_BASE 0x3f007000
|
||||
#define DMA_LEN DMA_CHAN_SIZE * (DMA_CHAN_MAX+1)
|
||||
#define PWM_BASE 0x2020C000
|
||||
#define PWM_BASE 0x3f20C000
|
||||
#define PWM_LEN 0x28
|
||||
#define CLK_BASE 0x20101000
|
||||
#define CLK_BASE 0x3f101000
|
||||
#define CLK_LEN 0xA8
|
||||
#define TICK_BASE 0x20003000
|
||||
#define TICK_BASE 0x3f003000
|
||||
#define TICK_LEN 0x08
|
||||
|
||||
#define PWM_CTL (0x00/4)
|
||||
|
@ -2,8 +2,8 @@
|
||||
.PHONY: all install uninstall
|
||||
all: servod
|
||||
|
||||
servod: servod.c
|
||||
gcc -Wall -g -O2 -o servod servod.c -lm
|
||||
servod: servod.c mailbox.c
|
||||
gcc -Wall -g -O2 -o servod servod.c mailbox.c -lm
|
||||
|
||||
install: servod
|
||||
[ "`id -u`" = "0" ] || { echo "Must be run as root"; exit 1; }
|
||||
|
@ -15,7 +15,12 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
|
||||
OPTS="--idle-timeout=2000"
|
||||
|
||||
res=0
|
||||
STATUSFILE="/tmp/servoblaster-status"
|
||||
|
||||
if [ $( id -u ) != 0 ]; then
|
||||
echo "ERROR: Must be run as root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
@ -29,13 +34,31 @@ case "$1" in
|
||||
killall servod
|
||||
;;
|
||||
status)
|
||||
[ -e /dev/servoblaster ] || res=4
|
||||
if [ ! -e /dev/servoblaster ]; then
|
||||
echo "ERROR: /dev/servoblaster does not exist"
|
||||
exit 2
|
||||
fi
|
||||
rm -f $STATUSFILE
|
||||
echo "status $STATUSFILE" > /dev/servoblaster
|
||||
sleep 0.2
|
||||
if [ ! -e $STATUSFILE ]; then
|
||||
echo "ERROR: servod not responding"
|
||||
exit 3
|
||||
elif grep -q "^OK" $STATUSFILE; then
|
||||
echo "OK"
|
||||
exit 0
|
||||
elif grep "^ERROR:" $STATUSFILE; then
|
||||
exit 4
|
||||
else
|
||||
echo "ERROR: No status from servod"
|
||||
exit 5
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: servoblaster [start|stop|status]" >&2
|
||||
res=3
|
||||
exit 6
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $res
|
||||
exit 0
|
||||
|
||||
|
298
ServoBlaster/user/mailbox.c
Normal file
298
ServoBlaster/user/mailbox.c
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright (c) 2012, Broadcom Europe Ltd.
|
||||
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 following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define PAGE_SIZE (4*1024)
|
||||
|
||||
void *mapmem(unsigned base, unsigned size)
|
||||
{
|
||||
int mem_fd;
|
||||
unsigned offset = base % PAGE_SIZE;
|
||||
base = base - offset;
|
||||
/* open /dev/mem */
|
||||
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
|
||||
printf("can't open /dev/mem\nThis program should be run as root. Try prefixing command with: sudo\n");
|
||||
exit (-1);
|
||||
}
|
||||
void *mem = mmap(
|
||||
0,
|
||||
size,
|
||||
PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED/*|MAP_FIXED*/,
|
||||
mem_fd,
|
||||
base);
|
||||
#ifdef DEBUG
|
||||
printf("base=0x%x, mem=%p\n", base, mem);
|
||||
#endif
|
||||
if (mem == MAP_FAILED) {
|
||||
printf("mmap error %d\n", (int)mem);
|
||||
exit (-1);
|
||||
}
|
||||
close(mem_fd);
|
||||
return (char *)mem + offset;
|
||||
}
|
||||
|
||||
void *unmapmem(void *addr, unsigned size)
|
||||
{
|
||||
int s = munmap(addr, size);
|
||||
if (s != 0) {
|
||||
printf("munmap error %d\n", s);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* use ioctl to send mbox property message
|
||||
*/
|
||||
|
||||
static int mbox_property(int file_desc, void *buf)
|
||||
{
|
||||
int fd = file_desc;
|
||||
int ret_val = -1;
|
||||
|
||||
if (fd < 0) {
|
||||
fd = mbox_open();
|
||||
}
|
||||
if (fd >= 0) {
|
||||
ret_val = ioctl(fd, IOCTL_MBOX_PROPERTY, buf);
|
||||
|
||||
if (ret_val < 0) {
|
||||
printf("ioctl_set_msg failed, errno %d: %m\n", errno);
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
unsigned *p = buf; int i; unsigned size = *(unsigned *)buf;
|
||||
for (i=0; i<size/4; i++)
|
||||
printf("%04x: 0x%08x\n", i*sizeof *p, p[i]);
|
||||
#endif
|
||||
if (file_desc < 0)
|
||||
mbox_close(fd);
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Requesting %d bytes\n", size);
|
||||
#endif
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x3000c; // (the tag id)
|
||||
p[i++] = 12; // (size of the buffer)
|
||||
p[i++] = 12; // (size of the data)
|
||||
p[i++] = size; // (num bytes? or pages?)
|
||||
p[i++] = align; // (alignment)
|
||||
p[i++] = flags; // (MEM_FLAG_L1_NONALLOCATING)
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
if (mbox_property(file_desc, p) < 0)
|
||||
return -1;
|
||||
else
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned mem_free(int file_desc, unsigned handle)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x3000f; // (the tag id)
|
||||
p[i++] = 4; // (size of the buffer)
|
||||
p[i++] = 4; // (size of the data)
|
||||
p[i++] = handle;
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
mbox_property(file_desc, p);
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned mem_lock(int file_desc, unsigned handle)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x3000d; // (the tag id)
|
||||
p[i++] = 4; // (size of the buffer)
|
||||
p[i++] = 4; // (size of the data)
|
||||
p[i++] = handle;
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
if (mbox_property(file_desc, p) < 0)
|
||||
return ~0;
|
||||
else
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned mem_unlock(int file_desc, unsigned handle)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x3000e; // (the tag id)
|
||||
p[i++] = 4; // (size of the buffer)
|
||||
p[i++] = 4; // (size of the data)
|
||||
p[i++] = handle;
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
mbox_property(file_desc, p);
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x30010; // (the tag id)
|
||||
p[i++] = 28; // (size of the buffer)
|
||||
p[i++] = 28; // (size of the data)
|
||||
p[i++] = code;
|
||||
p[i++] = r0;
|
||||
p[i++] = r1;
|
||||
p[i++] = r2;
|
||||
p[i++] = r3;
|
||||
p[i++] = r4;
|
||||
p[i++] = r5;
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
mbox_property(file_desc, p);
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned qpu_enable(int file_desc, unsigned enable)
|
||||
{
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
|
||||
p[i++] = 0x30012; // (the tag id)
|
||||
p[i++] = 4; // (size of the buffer)
|
||||
p[i++] = 4; // (size of the data)
|
||||
p[i++] = enable;
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
mbox_property(file_desc, p);
|
||||
return p[5];
|
||||
}
|
||||
|
||||
unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout) {
|
||||
int i=0;
|
||||
unsigned p[32];
|
||||
|
||||
p[i++] = 0; // size
|
||||
p[i++] = 0x00000000; // process request
|
||||
p[i++] = 0x30011; // (the tag id)
|
||||
p[i++] = 16; // (size of the buffer)
|
||||
p[i++] = 16; // (size of the data)
|
||||
p[i++] = num_qpus;
|
||||
p[i++] = control;
|
||||
p[i++] = noflush;
|
||||
p[i++] = timeout; // ms
|
||||
|
||||
p[i++] = 0x00000000; // end tag
|
||||
p[0] = i*sizeof *p; // actual size
|
||||
|
||||
mbox_property(file_desc, p);
|
||||
return p[5];
|
||||
}
|
||||
|
||||
int mbox_open(void) {
|
||||
int file_desc;
|
||||
char filename[64];
|
||||
|
||||
// open a char device file used for communicating with kernel mbox driver
|
||||
if ((file_desc = open("/dev/vcio", 0)) >= 0) {
|
||||
/* New kernel, we use /dev/vcio, major 249, since kernel 4.1 */
|
||||
return file_desc;
|
||||
}
|
||||
|
||||
/* Most likely an old kernel, so drop back to the old major=100 device */
|
||||
sprintf(filename, "/tmp/mailbox-%d", getpid());
|
||||
unlink(filename);
|
||||
if (mknod(filename, S_IFCHR|0600, makedev(100, 0)) < 0) {
|
||||
printf("Failed to create mailbox device %s: %m\n", filename);
|
||||
return -1;
|
||||
}
|
||||
file_desc = open(filename, 0);
|
||||
if (file_desc < 0) {
|
||||
printf("Can't open device file %s: %m\n", filename);
|
||||
unlink(filename);
|
||||
return -1;
|
||||
}
|
||||
unlink(filename);
|
||||
|
||||
return file_desc;
|
||||
}
|
||||
|
||||
void mbox_close(int file_desc) {
|
||||
close(file_desc);
|
||||
}
|
46
ServoBlaster/user/mailbox.h
Normal file
46
ServoBlaster/user/mailbox.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (c) 2012, Broadcom Europe Ltd.
|
||||
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 following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||
*/
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define MAJOR_NUM 100
|
||||
#define IOCTL_MBOX_PROPERTY _IOWR(MAJOR_NUM, 0, char *)
|
||||
|
||||
int mbox_open(void);
|
||||
void mbox_close(int file_desc);
|
||||
|
||||
unsigned get_version(int file_desc);
|
||||
unsigned mem_alloc(int file_desc, unsigned size, unsigned align, unsigned flags);
|
||||
unsigned mem_free(int file_desc, unsigned handle);
|
||||
unsigned mem_lock(int file_desc, unsigned handle);
|
||||
unsigned mem_unlock(int file_desc, unsigned handle);
|
||||
void *mapmem(unsigned base, unsigned size);
|
||||
void *unmapmem(void *addr, unsigned size);
|
||||
|
||||
unsigned execute_code(int file_desc, unsigned code, unsigned r0, unsigned r1, unsigned r2, unsigned r3, unsigned r4, unsigned r5);
|
||||
unsigned execute_qpu(int file_desc, unsigned num_qpus, unsigned control, unsigned noflush, unsigned timeout);
|
||||
unsigned qpu_enable(int file_desc, unsigned enable);
|
@ -42,9 +42,11 @@
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "mailbox.h"
|
||||
|
||||
#define DMY 255 // Used to represent an invalid P1 pin, or unmapped servo
|
||||
|
||||
#define NUM_P1PINS 26
|
||||
#define NUM_P1PINS 40
|
||||
#define NUM_P5PINS 8
|
||||
|
||||
#define MAX_SERVOS 32 /* Only 21 really, but this lets you map servo IDs
|
||||
@ -68,17 +70,27 @@
|
||||
#define DMA_CHAN_MAX 14
|
||||
#define DMA_CHAN_DEFAULT 14
|
||||
|
||||
#define DMA_BASE 0x20007000
|
||||
#define DMA_BASE_OFFSET 0x00007000
|
||||
#define DMA_LEN DMA_CHAN_SIZE * (DMA_CHAN_MAX+1)
|
||||
#define PWM_BASE 0x2020C000
|
||||
#define PWM_BASE_OFFSET 0x0020C000
|
||||
#define PWM_LEN 0x28
|
||||
#define CLK_BASE 0x20101000
|
||||
#define CLK_BASE_OFFSET 0x00101000
|
||||
#define CLK_LEN 0xA8
|
||||
#define GPIO_BASE 0x20200000
|
||||
#define GPIO_BASE_OFFSET 0x00200000
|
||||
#define GPIO_LEN 0x100
|
||||
#define PCM_BASE 0x20203000
|
||||
#define PCM_BASE_OFFSET 0x00203000
|
||||
#define PCM_LEN 0x24
|
||||
|
||||
#define DMA_VIRT_BASE (periph_virt_base + DMA_BASE_OFFSET)
|
||||
#define PWM_VIRT_BASE (periph_virt_base + PWM_BASE_OFFSET)
|
||||
#define CLK_VIRT_BASE (periph_virt_base + CLK_BASE_OFFSET)
|
||||
#define GPIO_VIRT_BASE (periph_virt_base + GPIO_BASE_OFFSET)
|
||||
#define PCM_VIRT_BASE (periph_virt_base + PCM_BASE_OFFSET)
|
||||
|
||||
#define PWM_PHYS_BASE (periph_phys_base + PWM_BASE_OFFSET)
|
||||
#define PCM_PHYS_BASE (periph_phys_base + PCM_BASE_OFFSET)
|
||||
#define GPIO_PHYS_BASE (periph_phys_base + GPIO_BASE_OFFSET)
|
||||
|
||||
#define DMA_NO_WIDE_BURSTS (1<<26)
|
||||
#define DMA_WAIT_RESP (1<<3)
|
||||
#define DMA_D_DREQ (1<<6)
|
||||
@ -89,6 +101,7 @@
|
||||
|
||||
#define DMA_CS (0x00/4)
|
||||
#define DMA_CONBLK_AD (0x04/4)
|
||||
#define DMA_SOURCE_AD (0x0c/4)
|
||||
#define DMA_DEBUG (0x20/4)
|
||||
|
||||
#define GPIO_FSEL0 (0x00/4)
|
||||
@ -140,9 +153,7 @@ typedef struct {
|
||||
stride, next, pad[2];
|
||||
} dma_cb_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t physaddr;
|
||||
} page_map_t;
|
||||
#define BUS_TO_PHYS(x) ((x)&~0xC0000000)
|
||||
|
||||
/* Define which P1 header pins to use by default. These are the eight standard
|
||||
* GPIO pins (those coloured green in the diagram on this page:
|
||||
@ -235,6 +246,49 @@ static uint8_t rev2_p5pin2gpio_map[] = {
|
||||
DMY, // P5-8 Ground
|
||||
};
|
||||
|
||||
static uint8_t bplus_p1pin2gpio_map[] = {
|
||||
DMY, // P1-1 3v3
|
||||
DMY, // P1-2 5v
|
||||
2, // P1-3 GPIO 2 (SDA)
|
||||
DMY, // P1-4 5v
|
||||
3, // P1-5 GPIO 3 (SCL)
|
||||
DMY, // P1-6 Ground
|
||||
4, // P1-7 GPIO 4 (GPCLK0)
|
||||
14, // P1-8 GPIO 14 (TXD)
|
||||
DMY, // P1-9 Ground
|
||||
15, // P1-10 GPIO 15 (RXD)
|
||||
17, // P1-11 GPIO 17
|
||||
18, // P1-12 GPIO 18 (PCM_CLK)
|
||||
27, // P1-13 GPIO 27
|
||||
DMY, // P1-14 Ground
|
||||
22, // P1-15 GPIO 22
|
||||
23, // P1-16 GPIO 23
|
||||
DMY, // P1-17 3v3
|
||||
24, // P1-18 GPIO 24
|
||||
10, // P1-19 GPIO 10 (MOSI)
|
||||
DMY, // P1-20 Ground
|
||||
9, // P1-21 GPIO 9 (MISO)
|
||||
25, // P1-22 GPIO 25
|
||||
11, // P1-23 GPIO 11 (SCLK)
|
||||
8, // P1-24 GPIO 8 (CE0)
|
||||
DMY, // P1-25 Ground
|
||||
7, // P1-26 GPIO 7 (CE1)
|
||||
DMY, // P1-27 ID_SD
|
||||
DMY, // P1-28 ID_SC
|
||||
5, // P1-29 GPIO 5
|
||||
DMY, // P1-30 Ground
|
||||
6, // P1-31 GPIO 5
|
||||
12, // P1-32 GPIO 12
|
||||
13, // P1-33 GPIO 13
|
||||
DMY, // P1-34 Ground
|
||||
19, // P1-35 GPIO 19
|
||||
16, // P1-36 GPIO 16
|
||||
26, // P1-37 GPIO 26
|
||||
20, // P1-38 GPIO 20
|
||||
DMY, // P1-39 Ground
|
||||
21, // P1-40 GPIO 21
|
||||
};
|
||||
|
||||
// cycle_time_us is the pulse cycle time per servo, in microseconds.
|
||||
// Typically it should be 20ms, or 20000us.
|
||||
|
||||
@ -255,11 +309,6 @@ static int num_servos;
|
||||
static uint32_t gpiomode[MAX_SERVOS];
|
||||
static int restore_gpio_modes;
|
||||
|
||||
page_map_t *page_map;
|
||||
|
||||
static uint8_t *virtbase;
|
||||
static uint8_t *virtcached;
|
||||
|
||||
static volatile uint32_t *pwm_reg;
|
||||
static volatile uint32_t *pcm_reg;
|
||||
static volatile uint32_t *clk_reg;
|
||||
@ -282,6 +331,29 @@ static uint32_t *turnoff_mask;
|
||||
static uint32_t *turnon_mask;
|
||||
static dma_cb_t *cb_base;
|
||||
|
||||
static int board_model;
|
||||
static int gpio_cfg;
|
||||
|
||||
static uint32_t periph_phys_base;
|
||||
static uint32_t periph_virt_base;
|
||||
static uint32_t dram_phys_base;
|
||||
static uint32_t mem_flag;
|
||||
|
||||
static char *gpio_desc[] = {
|
||||
"Unknown",
|
||||
"P1 (26 pins)",
|
||||
"P1 (26 pins), P5 (8 pins)",
|
||||
"P1 (40 pins)"
|
||||
};
|
||||
|
||||
static struct {
|
||||
int handle; /* From mbox_open() */
|
||||
uint32_t size; /* Required size */
|
||||
unsigned mem_ref; /* From mem_alloc() */
|
||||
unsigned bus_addr; /* From mem_lock() */
|
||||
uint8_t *virt_addr; /* From mapmem() */
|
||||
} mbox;
|
||||
|
||||
static void set_servo(int servo, int width);
|
||||
static void set_servo_idle(int servo);
|
||||
static void gpio_set_mode(uint32_t gpio, uint32_t mode);
|
||||
@ -300,7 +372,7 @@ terminate(int dummy)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dma_reg && virtbase) {
|
||||
if (dma_reg && mbox.virt_addr) {
|
||||
for (i = 0; i < MAX_SERVOS; i++) {
|
||||
if (servo2gpio[i] != DMY)
|
||||
set_servo(i, 0);
|
||||
@ -315,6 +387,14 @@ terminate(int dummy)
|
||||
gpio_set_mode(servo2gpio[i], gpiomode[i]);
|
||||
}
|
||||
}
|
||||
if (mbox.virt_addr != NULL) {
|
||||
unmapmem(mbox.virt_addr, mbox.size);
|
||||
mem_unlock(mbox.handle, mbox.mem_ref);
|
||||
mem_free(mbox.handle, mbox.mem_ref);
|
||||
if (mbox.handle >= 0)
|
||||
mbox_close(mbox.handle);
|
||||
}
|
||||
|
||||
unlink(DEVFILE);
|
||||
unlink(CFGFILE);
|
||||
exit(1);
|
||||
@ -413,15 +493,15 @@ gpio_set(int gpio, int level)
|
||||
static uint32_t
|
||||
mem_virt_to_phys(void *virt)
|
||||
{
|
||||
uint32_t offset = (uint8_t *)virt - virtbase;
|
||||
uint32_t offset = (uint8_t *)virt - mbox.virt_addr;
|
||||
|
||||
return page_map[offset >> PAGE_SHIFT].physaddr + (offset % PAGE_SIZE);
|
||||
return mbox.bus_addr + offset;
|
||||
}
|
||||
|
||||
static void *
|
||||
map_peripheral(uint32_t base, uint32_t len)
|
||||
{
|
||||
int fd = open("/dev/mem", O_RDWR);
|
||||
int fd = open("/dev/mem", O_RDWR|O_SYNC);
|
||||
void * vaddr;
|
||||
|
||||
if (fd < 0)
|
||||
@ -500,47 +580,6 @@ set_servo(int servo, int width)
|
||||
update_idle_time(servo);
|
||||
}
|
||||
|
||||
static void
|
||||
make_pagemap(void)
|
||||
{
|
||||
int i, fd, memfd, pid;
|
||||
char pagemap_fn[64];
|
||||
|
||||
page_map = malloc(num_pages * sizeof(*page_map));
|
||||
if (page_map == 0)
|
||||
fatal("servod: Failed to malloc page_map: %m\n");
|
||||
memfd = open("/dev/mem", O_RDWR);
|
||||
if (memfd < 0)
|
||||
fatal("servod: Failed to open /dev/mem: %m\n");
|
||||
pid = getpid();
|
||||
sprintf(pagemap_fn, "/proc/%d/pagemap", pid);
|
||||
fd = open(pagemap_fn, O_RDONLY);
|
||||
if (fd < 0)
|
||||
fatal("servod: Failed to open %s: %m\n", pagemap_fn);
|
||||
if (lseek(fd, (uint32_t)(size_t)virtcached >> 9, SEEK_SET) !=
|
||||
(uint32_t)(size_t)virtcached >> 9) {
|
||||
fatal("servod: Failed to seek on %s: %m\n", pagemap_fn);
|
||||
}
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
uint64_t pfn;
|
||||
if (read(fd, &pfn, sizeof(pfn)) != sizeof(pfn))
|
||||
fatal("servod: Failed to read %s: %m\n", pagemap_fn);
|
||||
if (((pfn >> 55) & 0x1bf) != 0x10c)
|
||||
fatal("servod: Page %d not present (pfn 0x%016llx)\n", i, pfn);
|
||||
page_map[i].physaddr = (uint32_t)pfn << PAGE_SHIFT | 0x40000000;
|
||||
if (mmap(virtbase + i * PAGE_SIZE, PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED|MAP_FIXED|MAP_LOCKED|MAP_NORESERVE,
|
||||
memfd, (uint32_t)pfn << PAGE_SHIFT | 0x40000000) !=
|
||||
virtbase + i * PAGE_SIZE) {
|
||||
fatal("Failed to create uncached map of page %d at %p\n",
|
||||
i, virtbase + i * PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
close(memfd);
|
||||
memset(virtbase, 0, num_pages * PAGE_SIZE);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_sighandlers(void)
|
||||
{
|
||||
@ -568,18 +607,18 @@ init_ctrl_data(void)
|
||||
uint32_t maskall = 0;
|
||||
|
||||
if (invert) {
|
||||
phys_gpclr0 = 0x7e200000 + 0x1c;
|
||||
phys_gpset0 = 0x7e200000 + 0x28;
|
||||
phys_gpclr0 = GPIO_PHYS_BASE + 0x1c;
|
||||
phys_gpset0 = GPIO_PHYS_BASE + 0x28;
|
||||
} else {
|
||||
phys_gpclr0 = 0x7e200000 + 0x28;
|
||||
phys_gpset0 = 0x7e200000 + 0x1c;
|
||||
phys_gpclr0 = GPIO_PHYS_BASE + 0x28;
|
||||
phys_gpset0 = GPIO_PHYS_BASE + 0x1c;
|
||||
}
|
||||
|
||||
if (delay_hw == DELAY_VIA_PWM) {
|
||||
phys_fifo_addr = (PWM_BASE | 0x7e000000) + 0x18;
|
||||
phys_fifo_addr = PWM_PHYS_BASE + 0x18;
|
||||
cbinfo = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP | DMA_D_DREQ | DMA_PER_MAP(5);
|
||||
} else {
|
||||
phys_fifo_addr = (PCM_BASE | 0x7e000000) + 0x04;
|
||||
phys_fifo_addr = PCM_PHYS_BASE + 0x04;
|
||||
cbinfo = DMA_NO_WIDE_BURSTS | DMA_WAIT_RESP | DMA_D_DREQ | DMA_PER_MAP(2);
|
||||
}
|
||||
|
||||
@ -696,12 +735,46 @@ init_hardware(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_status(char *filename)
|
||||
{
|
||||
uint32_t last;
|
||||
int status = -1;
|
||||
char *p;
|
||||
int fd;
|
||||
const char *dma_dead = "ERROR: DMA not running\n";
|
||||
|
||||
while (*filename == ' ')
|
||||
filename++;
|
||||
p = filename + strlen(filename) - 1;
|
||||
while (p > filename && (*p == '\n' || *p == '\r' || *p == ' '))
|
||||
*p-- = '\0';
|
||||
|
||||
last = dma_reg[DMA_CONBLK_AD];
|
||||
udelay(step_time_us*2);
|
||||
if (dma_reg[DMA_CONBLK_AD] != last)
|
||||
status = 0;
|
||||
if ((fd = open(filename, O_WRONLY|O_CREAT, 0666)) >= 0) {
|
||||
if (status == 0)
|
||||
write(fd, "OK\n", 3);
|
||||
else
|
||||
write(fd, dma_dead, strlen(dma_dead));
|
||||
close(fd);
|
||||
} else {
|
||||
printf("Failed to open %s for writing: %m\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
do_debug(void)
|
||||
{
|
||||
int i;
|
||||
uint32_t mask = 0;
|
||||
uint32_t last = 0xffffffff;
|
||||
uint32_t last;
|
||||
|
||||
last = dma_reg[DMA_CONBLK_AD];
|
||||
udelay(step_time_us*2);
|
||||
printf("%08x %08x\n", last, dma_reg[DMA_CONBLK_AD]);
|
||||
|
||||
printf("---------------------------\n");
|
||||
printf("Servo Start Width TurnOn\n");
|
||||
@ -713,6 +786,7 @@ do_debug(void)
|
||||
}
|
||||
}
|
||||
printf("\nData:\n");
|
||||
last = 0xffffffff;
|
||||
for (i = 0; i < num_samples; i++) {
|
||||
uint32_t curr = turnoff_mask[i] & mask;
|
||||
if (curr != last)
|
||||
@ -823,6 +897,8 @@ go_go_go(void)
|
||||
n = sscanf(line, "%d=%s", &servo, width_arg);
|
||||
if (!strcmp(line, "debug\n")) {
|
||||
do_debug();
|
||||
} else if (!strncmp(line, "status ", 7)) {
|
||||
do_status(line + 7);
|
||||
} else if (n != 2) {
|
||||
fprintf(stderr, "Bad input: %s", line);
|
||||
} else if (servo < 0 || servo >= MAX_SERVOS) {
|
||||
@ -848,18 +924,18 @@ go_go_go(void)
|
||||
/* Determining the board revision is a lot more complicated than it should be
|
||||
* (see comments in wiringPi for details). We will just look at the last two
|
||||
* digits of the Revision string and treat '00' and '01' as errors, '02' and
|
||||
* '03' as rev 1, and any other hex value as rev 2.
|
||||
* '03' as rev 1, and any other hex value as rev 2. 'Pi1 and Pi2 are
|
||||
* differentiated by the Hardware being BCM2708 or BCM2709.
|
||||
*/
|
||||
static int
|
||||
board_rev(void)
|
||||
static void
|
||||
get_model_and_revision(void)
|
||||
{
|
||||
char buf[128];
|
||||
char buf[128], revstr[128], modelstr[128];
|
||||
char *ptr, *end, *res;
|
||||
static int rev = 0;
|
||||
int board_revision;
|
||||
FILE *fp;
|
||||
|
||||
if (rev)
|
||||
return rev;
|
||||
revstr[0] = modelstr[0] = '\0';
|
||||
|
||||
fp = fopen("/proc/cpuinfo", "r");
|
||||
|
||||
@ -867,26 +943,49 @@ board_rev(void)
|
||||
fatal("Unable to open /proc/cpuinfo: %m\n");
|
||||
|
||||
while ((res = fgets(buf, 128, fp))) {
|
||||
if (!strncmp(buf, "Revision", 8))
|
||||
break;
|
||||
if (!strncasecmp("hardware", buf, 8))
|
||||
memcpy(modelstr, buf, 128);
|
||||
else if (!strncasecmp(buf, "revision", 8))
|
||||
memcpy(revstr, buf, 128);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (!res)
|
||||
if (modelstr[0] == '\0')
|
||||
fatal("servod: No 'Hardware' record in /proc/cpuinfo\n");
|
||||
if (revstr[0] == '\0')
|
||||
fatal("servod: No 'Revision' record in /proc/cpuinfo\n");
|
||||
|
||||
ptr = buf + strlen(buf) - 3;
|
||||
rev = strtol(ptr, &end, 16);
|
||||
if (strstr(modelstr, "BCM2708"))
|
||||
board_model = 1;
|
||||
else if (strstr(modelstr, "BCM2709"))
|
||||
board_model = 2;
|
||||
else
|
||||
fatal("servod: Cannot parse the hardware name string\n");
|
||||
|
||||
ptr = revstr + strlen(revstr) - 3;
|
||||
board_revision = strtol(ptr, &end, 16);
|
||||
if (end != ptr + 2)
|
||||
fatal("servod: Failed to parse Revision string\n");
|
||||
if (rev < 1)
|
||||
if (board_revision < 1)
|
||||
fatal("servod: Invalid board Revision\n");
|
||||
else if (rev < 4)
|
||||
rev = 1;
|
||||
else if (board_revision < 4)
|
||||
gpio_cfg = 1;
|
||||
else if (board_revision < 16)
|
||||
gpio_cfg = 2;
|
||||
else
|
||||
rev = 2;
|
||||
gpio_cfg = 3;
|
||||
|
||||
return rev;
|
||||
if (board_model == 1) {
|
||||
periph_virt_base = 0x20000000;
|
||||
periph_phys_base = 0x7e000000;
|
||||
dram_phys_base = 0x40000000;
|
||||
mem_flag = 0x0c;
|
||||
} else {
|
||||
periph_virt_base = 0x3f000000;
|
||||
periph_phys_base = 0x7e000000;
|
||||
dram_phys_base = 0xc0000000;
|
||||
mem_flag = 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -905,23 +1004,29 @@ parse_pin_lists(int p1first, char *p1pins, char*p5pins)
|
||||
if (lst == 0 && p1first) {
|
||||
name = "P1";
|
||||
pins = p1pins;
|
||||
if (board_rev() == 1) {
|
||||
if (board_model == 1 && gpio_cfg == 1) {
|
||||
map = rev1_p1pin2gpio_map;
|
||||
mapcnt = sizeof(rev1_p1pin2gpio_map);
|
||||
} else {
|
||||
} else if (board_model == 1 && gpio_cfg == 2) {
|
||||
map = rev2_p1pin2gpio_map;
|
||||
mapcnt = sizeof(rev2_p1pin2gpio_map);
|
||||
} else {
|
||||
map = bplus_p1pin2gpio_map;
|
||||
mapcnt = sizeof(bplus_p1pin2gpio_map);
|
||||
}
|
||||
pNpin2servo = p1pin2servo;
|
||||
} else {
|
||||
name = "P5";
|
||||
pins = p5pins;
|
||||
if (board_rev() == 1) {
|
||||
if (board_model == 1 && gpio_cfg == 1) {
|
||||
map = rev1_p5pin2gpio_map;
|
||||
mapcnt = sizeof(rev1_p5pin2gpio_map);
|
||||
} else {
|
||||
} else if (board_model == 1 && gpio_cfg == 2) {
|
||||
map = rev2_p5pin2gpio_map;
|
||||
mapcnt = sizeof(rev2_p5pin2gpio_map);
|
||||
} else {
|
||||
map = NULL;
|
||||
mapcnt = 0;
|
||||
}
|
||||
pNpin2servo = p5pin2servo;
|
||||
}
|
||||
@ -982,20 +1087,25 @@ gpio2pinname(uint8_t gpio)
|
||||
static char res[16];
|
||||
uint8_t pin;
|
||||
|
||||
if (board_rev() == 1) {
|
||||
if (board_model == 1 && gpio_cfg == 1) {
|
||||
if ((pin = gpiosearch(gpio, rev1_p1pin2gpio_map, sizeof(rev1_p1pin2gpio_map))))
|
||||
sprintf(res, "P1-%d", pin);
|
||||
else if ((pin = gpiosearch(gpio, rev1_p5pin2gpio_map, sizeof(rev1_p5pin2gpio_map))))
|
||||
sprintf(res, "P5-%d", pin);
|
||||
else
|
||||
fatal("Cannot map GPIO %d to a header pin\n", gpio);
|
||||
} else {
|
||||
} else if (board_model == 1 && gpio_cfg == 2) {
|
||||
if ((pin = gpiosearch(gpio, rev2_p1pin2gpio_map, sizeof(rev2_p1pin2gpio_map))))
|
||||
sprintf(res, "P1-%d", pin);
|
||||
else if ((pin = gpiosearch(gpio, rev2_p5pin2gpio_map, sizeof(rev2_p5pin2gpio_map))))
|
||||
sprintf(res, "P5-%d", pin);
|
||||
else
|
||||
fatal("Cannot map GPIO %d to a header pin\n", gpio);
|
||||
} else {
|
||||
if ((pin = gpiosearch(gpio, bplus_p1pin2gpio_map, sizeof(bplus_p1pin2gpio_map))))
|
||||
sprintf(res, "P1-%d", pin);
|
||||
else
|
||||
fatal("Cannot map GPIO %d to a header pin\n", gpio);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -1151,8 +1261,9 @@ main(int argc, char **argv)
|
||||
fatal("Invalid parameter\n");
|
||||
}
|
||||
}
|
||||
if (board_rev() == 1 && p5pins[0])
|
||||
fatal("Board rev 1 does not have a P5 header\n");
|
||||
get_model_and_revision();
|
||||
if (board_model == 1 && gpio_cfg == 1 && p5pins[0])
|
||||
fatal("Board model 1 revision 1 does not have a P5 header\n");
|
||||
|
||||
parse_pin_lists(p1first, p1pins, p5pins);
|
||||
|
||||
@ -1232,7 +1343,8 @@ main(int argc, char **argv)
|
||||
fatal("min value is >= max value\n");
|
||||
}
|
||||
|
||||
printf("\nBoard revision: %7d\n", board_rev());
|
||||
printf("\nBoard model: %7d\n", board_model);
|
||||
printf("GPIO configuration: %s\n", gpio_desc[gpio_cfg]);
|
||||
printf("Using hardware: %s\n", delay_hw == DELAY_VIA_PWM ? "PWM" : "PCM");
|
||||
printf("Using DMA channel: %7d\n", dma_chan);
|
||||
if (idle_timeout)
|
||||
@ -1248,7 +1360,7 @@ main(int argc, char **argv)
|
||||
servo_max_ticks * step_time_us);
|
||||
printf("Output levels: %s\n", invert ? "Inverted" : " Normal");
|
||||
printf("\nUsing P1 pins: %s\n", p1pins);
|
||||
if (board_rev() > 1)
|
||||
if (board_model == 1 && gpio_cfg == 2)
|
||||
printf("Using P5 pins: %s\n", p5pins);
|
||||
printf("\nServo mapping:\n");
|
||||
for (i = 0; i < MAX_SERVOS; i++) {
|
||||
@ -1261,55 +1373,33 @@ main(int argc, char **argv)
|
||||
init_idle_timers();
|
||||
setup_sighandlers();
|
||||
|
||||
dma_reg = map_peripheral(DMA_BASE, DMA_LEN);
|
||||
dma_reg = map_peripheral(DMA_VIRT_BASE, DMA_LEN);
|
||||
dma_reg += dma_chan * DMA_CHAN_SIZE / sizeof(uint32_t);
|
||||
pwm_reg = map_peripheral(PWM_BASE, PWM_LEN);
|
||||
pcm_reg = map_peripheral(PCM_BASE, PCM_LEN);
|
||||
clk_reg = map_peripheral(CLK_BASE, CLK_LEN);
|
||||
gpio_reg = map_peripheral(GPIO_BASE, GPIO_LEN);
|
||||
pwm_reg = map_peripheral(PWM_VIRT_BASE, PWM_LEN);
|
||||
pcm_reg = map_peripheral(PCM_VIRT_BASE, PCM_LEN);
|
||||
clk_reg = map_peripheral(CLK_VIRT_BASE, CLK_LEN);
|
||||
gpio_reg = map_peripheral(GPIO_VIRT_BASE, GPIO_LEN);
|
||||
|
||||
/*
|
||||
* Map the pages to our virtual address space; this reserves them and
|
||||
* locks them in memory. However, these are L1 & L2 non-coherent
|
||||
* cached pages and we want coherent access to them so the DMA
|
||||
* controller sees our changes immediately. To get that, we create a
|
||||
* second mapping of the same size and immediately free it. This gives
|
||||
* us an address in our virtual address space where we can map in a
|
||||
* coherent view of the physical pages that were allocated by the first
|
||||
* mmap(). This coherent mapping happens in make_pagemap(). All
|
||||
* accesses to our memory that is shared with the DMA controller are
|
||||
* via this second coherent mapping. The memset() below forces the
|
||||
* pages to be allocated.
|
||||
*/
|
||||
virtcached = mmap(NULL, num_pages * PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
|
||||
-1, 0);
|
||||
if (virtcached == MAP_FAILED)
|
||||
fatal("servod: Failed to mmap for cached pages: %m\n");
|
||||
if ((unsigned long)virtcached & (PAGE_SIZE-1))
|
||||
fatal("servod: Virtual address is not page aligned\n");
|
||||
memset(virtcached, 0, num_pages * PAGE_SIZE);
|
||||
/* Use the mailbox interface to the VC to ask for physical memory */
|
||||
// Use the mailbox interface to request memory from the VideoCore
|
||||
// We specifiy (-1) for the handle rather than calling mbox_open()
|
||||
// so multiple users can share the resource.
|
||||
mbox.handle = -1; // mbox_open();
|
||||
mbox.size = num_pages * 4096;
|
||||
mbox.mem_ref = mem_alloc(mbox.handle, mbox.size, 4096, mem_flag);
|
||||
if (mbox.mem_ref < 0) {
|
||||
fatal("Failed to alloc memory from VideoCore\n");
|
||||
}
|
||||
mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref);
|
||||
if (mbox.bus_addr == ~0) {
|
||||
mem_free(mbox.handle, mbox.size);
|
||||
fatal("Failed to lock memory\n");
|
||||
}
|
||||
mbox.virt_addr = mapmem(BUS_TO_PHYS(mbox.bus_addr), mbox.size);
|
||||
|
||||
virtbase = mmap(NULL, num_pages * PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
|
||||
-1, 0);
|
||||
if (virtbase == MAP_FAILED)
|
||||
fatal("servod: Failed to mmap uncached pages: %m\n");
|
||||
if ((unsigned long)virtbase & (PAGE_SIZE-1))
|
||||
fatal("servod: Virtual address is not page aligned\n");
|
||||
munmap(virtbase, num_pages * PAGE_SIZE);
|
||||
|
||||
make_pagemap();
|
||||
|
||||
/*
|
||||
* Now the memory is all mapped, we can set up the pointers to the
|
||||
* bit masks used to turn outputs on and off, and to the DMA control
|
||||
* blocks. The control blocks must be 32 byte aligned (so round up
|
||||
* to multiple of 8, as we're then multiplying by 4).
|
||||
*/
|
||||
turnoff_mask = (uint32_t *)virtbase;
|
||||
turnon_mask = (uint32_t *)(virtbase + num_samples * sizeof(uint32_t));
|
||||
cb_base = (dma_cb_t *)(virtbase +
|
||||
turnoff_mask = (uint32_t *)mbox.virt_addr;
|
||||
turnon_mask = (uint32_t *)(mbox.virt_addr + num_samples * sizeof(uint32_t));
|
||||
cb_base = (dma_cb_t *)(mbox.virt_addr +
|
||||
ROUNDUP(num_samples + MAX_SERVOS, 8) * sizeof(uint32_t));
|
||||
|
||||
for (i = 0; i < MAX_SERVOS; i++) {
|
||||
|
Loading…
Reference in New Issue
Block a user