This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/PROCESSOR-SDK-AM335X: GPMC EDMA Test

Part Number: PROCESSOR-SDK-AM335X
Other Parts Discussed in Thread: AM3359

Tool/software: Linux

We are working on a board containing AM3359 processor.

SDK we are using is ti-processor-sdk-linux-am335x-evm-05.00.00.15.

Our customer wants to see the throughput of the data transfer between processor (AM3359) and FPGA connected through 8 GPMC lines using EDMA

As of now we've created a project in Code Composer Studio ( CCS ) with a GEL file and a C program & we are able to send data to FPGA through these 8 GPMC lines.

Right now we didn't insert any SD Card(i.e. Processor is not booted up).

Using JTAG & CCS only we did it.

Now we are not clear about how to proceed further in this regard on how to associate this gpmc program with EDMA protocol in Linux-SDK to send data to FPGA.

All we want now is to implement GPMC_EDMA data transfer program as per the below flow.

Processor booted ----> Run GPMC_EDMA Data transfer program ----> Complete data transfer ----> Calculate throughput.

Here is my code for transferring data to FPGA through 8 GPMC lines.

#include <stdio.h>
#define GPMC_BASE 0x50000000
#define GPMC_SYSCONFIG   *( volatile unsigned int * )(GPMC_BASE + 0x010)
#define GPMC_SYSSTATUS   *( volatile unsigned int * )(GPMC_BASE + 0x014)
#define GPMC_CONFIG      *( volatile unsigned int * )(GPMC_BASE + 0x050)
#define GPMC_STATUS      *( volatile unsigned int * )(GPMC_BASE + 0x054)
//Following are config registers for CS_0
#define GPMC_CONFIG1_0   *( volatile unsigned int * )(GPMC_BASE + 0x060)
#define GPMC_CONFIG2_0   *( volatile unsigned int * )(GPMC_BASE + 0x064) //CS settings
#define GPMC_CONFIG3_0   *( volatile unsigned int * )(GPMC_BASE + 0x068) //ADV settings
#define GPMC_CONFIG4_0   *( volatile unsigned int * )(GPMC_BASE + 0x06C) //OE settings
#define GPMC_CONFIG5_0   *( volatile unsigned int * )(GPMC_BASE + 0x070) //
#define GPMC_CONFIG6_0   *( volatile unsigned int * )(GPMC_BASE + 0x074) //
#define GPMC_CONFIG7_0   *( volatile unsigned int * )(GPMC_BASE + 0x078) //

#define PRCM_BASE            0x44E00000
#define CM_PER_GPMC_CLKCTRL  *( volatile unsigned int * )(PRCM_BASE + 0x030)

#define BUF_SIZE 10
int main(void)
{
    unsigned int i,j,k;
    unsigned int buf[BUF_SIZE];
    for(i=0;i<BUF_SIZE;i++){
        buf[i]=i+1;
    }
    printf("Test GPMC memory Async 8 bit\n");

    //CM_PER_GPMC_CLKCTRL = 0x2;

    i = GPMC_CONFIG;
    i = i | 2;
    //GPMC_CONFIG = i;   // We are not using any address bits.

    //GPMC_CONFIG1_0 = 0x13;
    GPMC_CONFIG2_0 = 0x61F10;//0x1F1F1F;    // Highest delay - optimize this for speed.
    GPMC_CONFIG3_0 = 0x77011F71;//0x771F1F7F;  // Highest delay - optimize this for speed.
    GPMC_CONFIG4_0 = 0x0501E77F;//0x1F0FE77F;  // Highest delay - optimize this for speed.
    GPMC_CONFIG5_0 = 0xF1F061F;//0xF1F1F1F;   // Highest delay - optimize this for speed.
    GPMC_CONFIG6_0 = 0x1F70F0F;//0x1F70F0F;   // Highest delay - optimize this for speed.
    GPMC_CONFIG7_0 = 0xF48;       // CS0 starts at 0x08000000 and size is 16MB.  First 0xFFFFF not available

#if 0
    for(i=0; i < 1024; i+=2){
        j = (0x08000000) ; //+i);
        k = *(unsigned int *)j;
        printf("0x%X\n", k );
    }
#endif
int t=0;
    while(1){
        //count++;
        // write to the same location - for oscilloscope probe of signals.

        //printf("writing 0's\n");
        //*(unsigned int *)(0x08000000) = 0x0;
        printf("Sent %d bytes\n",BUF_SIZE);
        for(i=0;i<BUF_SIZE;i++){
        *(unsigned int *)(0x08000000) = buf[i];
        }
        //*(unsigned int *)(0x08000000) = buf[1];
        /*if(t==0){
            //*(unsigned int *)(0x08000000) = 0xA5;
            *(unsigned int *)(0x08000000) = count;
            t=1;
        }
        else if(t==1){
            //*(unsigned int *)(0x08000000) = 0x5A;
            *(unsigned int *)(0x08000000) = count;
            t=0;
        }*/
    }

    return 0;
}

Someone kindly help me to proceed further as we have a severe time restriction and also we are new to this one.

Thanks & Regards

Vamsi

  • Hello Vamsi,

    You can refer to the provided materials in this post.

    Best regards,
    Kemal

  • Hi Kemal,

    We did it already.

    Our EDMA definitons:

    edma: edma@49000000 {
    compatible = "ti,edma3-tpcc";
    ti,hwmods = "tpcc";
    reg = <0x49000000 0x10000>;
    reg-names = "edma3_cc";
    interrupts = <12 13 14>;
    interrupt-names = "edma3_ccint", "edma3_mperr",
    "edma3_ccerrint";
    dma-requests = <64>;
    #dma-cells = <2>;

    ti,tptcs = <&edma_tptc0 7>, <&edma_tptc1 5>,
    <&edma_tptc2 0>;

    ti,edma-memcpy-channels = <20 21>;
    };

    edma_tptc0: tptc@49800000 {
    compatible = "ti,edma3-tptc";
    ti,hwmods = "tptc0";
    reg = <0x49800000 0x100000>;
    interrupts = <112>;
    interrupt-names = "edma3_tcerrint";
    };

    edma_tptc1: tptc@49900000 {
    compatible = "ti,edma3-tptc";
    ti,hwmods = "tptc1";
    reg = <0x49900000 0x100000>;
    interrupts = <113>;
    interrupt-names = "edma3_tcerrint";
    };

    edma_tptc2: tptc@49a00000 {
    compatible = "ti,edma3-tptc";
    ti,hwmods = "tptc2";
    reg = <0x49a00000 0x100000>;
    interrupts = <114>;
    interrupt-names = "edma3_tcerrint";
    };
    mmc1: mmc@48060000 {
    compatible = "ti,omap4-hsmmc";
    ti,hwmods = "mmc1";
    ti,dual-volt;
    ti,needs-special-reset;
    ti,needs-special-hs-handling;
    dmas = <&edma_xbar 24 0 0
    &edma_xbar 25 0 0>;
    dma-names = "tx", "rx";
    interrupts = <64>;
    reg = <0x48060000 0x1000>;
    status = "disabled";
    };

    mmc2: mmc@481d8000 {
    compatible = "ti,omap4-hsmmc";
    ti,hwmods = "mmc2";
    ti,needs-special-reset;
    dmas = <&edma 2 0
    &edma 3 0>;
    dma-names = "tx", "rx";
    interrupts = <28>;
    reg = <0x481d8000 0x1000>;
    status = "disabled";
    };

    mmc3: mmc@47810000 {
    compatible = "ti,omap4-hsmmc";
    ti,hwmods = "mmc3";
    ti,needs-special-reset;
    interrupts = <29>;
    reg = <0x47810000 0x1000>;
    status = "disabled";
    };

    root@am335x-evm:~# modprobe dmatest timeout=200 iterations=10000 run=1

    [ 585.652192] dmatest: Started 1 threads using dma1chan0

    [ 585.667611] dmatest: Started 1 threads using dma1chan1

    root@am335x-evm:~# [ 599.547727] dmatest: dma1chan1-copy0: summary 10000 tests, 0 failures 1388 iops 11161 KB/s (0)

    [ 599.559230] dmatest: dma1chan0-copy0: summary 10000 tests, 0 failures 1392 iops 11001 KB/s (0)

    Here i am not clear with 2 things.

    1. How can I say that, data is transferred between the processor and the FPGA through the 8 GPMC lines using EDMA only.

    2. How to send some custom data and see. How to provide the input. Here i didn't find any such provision to input some data.


    Kindly help.


    Regards
    Vamsi

  • Can someone help me in modifying this attached dmatest.c
    This application is using threads and channels for transferring data within the processors memory.
    Now i want to modify it to send data to FPGA. I have a buffer containing numbers from 1 to 4096.
    When i run this dmatest using modprobe dmatest.... it should send data to FPGA and print the throughput.

    I am really tensed so much in making it happen for the last 3 weeks as we have so much pressure from our client. And also that I am new to kernel programming.

    kindly help.

    /*
     * DMA Engine test module
     *
     * Copyright (C) 2007 Atmel Corporation
     * Copyright (C) 2013 Intel Corporation
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     */
    #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
    
    #include <linux/delay.h>
    #include <linux/dma-mapping.h>
    #include <linux/dmaengine.h>
    #include <linux/freezer.h>
    #include <linux/init.h>
    #include <linux/kthread.h>
    #include <linux/sched/task.h>
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/random.h>
    #include <linux/slab.h>
    #include <linux/wait.h>
    
    static unsigned int test_buf_size = 16384;
    module_param(test_buf_size, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer");
    
    static char test_channel[20];
    module_param_string(channel, test_channel, sizeof(test_channel),
    		S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)");
    
    static char test_device[32];
    module_param_string(device, test_device, sizeof(test_device),
    		S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)");
    
    static unsigned int threads_per_chan = 1;
    module_param(threads_per_chan, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(threads_per_chan,
    		"Number of threads to start per channel (default: 1)");
    
    static unsigned int max_channels;
    module_param(max_channels, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(max_channels,
    		"Maximum number of channels to use (default: all)");
    
    static unsigned int iterations;
    module_param(iterations, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(iterations,
    		"Iterations before stopping test (default: infinite)");
    
    static unsigned int dmatest;
    module_param(dmatest, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(dmatest,
    		"dmatest 0-memcpy 1-memset (default: 0)");
    
    static unsigned int xor_sources = 3;
    module_param(xor_sources, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(xor_sources,
    		"Number of xor source buffers (default: 3)");
    
    static unsigned int pq_sources = 3;
    module_param(pq_sources, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(pq_sources,
    		"Number of p+q source buffers (default: 3)");
    
    static int timeout = 3000;
    module_param(timeout, uint, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), "
    		 "Pass -1 for infinite timeout");
    
    static bool noverify;
    module_param(noverify, bool, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(noverify, "Disable random data setup and verification");
    
    static bool verbose;
    module_param(verbose, bool, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)");
    
    /**
     * struct dmatest_params - test parameters.
     * @buf_size:		size of the memcpy test buffer
     * @channel:		bus ID of the channel to test
     * @device:		bus ID of the DMA Engine to test
     * @threads_per_chan:	number of threads to start per channel
     * @max_channels:	maximum number of channels to use
     * @iterations:		iterations before stopping test
     * @xor_sources:	number of xor source buffers
     * @pq_sources:		number of p+q source buffers
     * @timeout:		transfer timeout in msec, -1 for infinite timeout
     */
    struct dmatest_params {
    	unsigned int	buf_size;
    	char		channel[20];
    	char		device[32];
    	unsigned int	threads_per_chan;
    	unsigned int	max_channels;
    	unsigned int	iterations;
    	unsigned int	xor_sources;
    	unsigned int	pq_sources;
    	int		timeout;
    	bool		noverify;
    };
    
    /**
     * struct dmatest_info - test information.
     * @params:		test parameters
     * @lock:		access protection to the fields of this structure
     */
    static struct dmatest_info {
    	/* Test parameters */
    	struct dmatest_params	params;
    
    	/* Internal state */
    	struct list_head	channels;
    	unsigned int		nr_channels;
    	struct mutex		lock;
    	bool			did_init;
    } test_info = {
    	.channels = LIST_HEAD_INIT(test_info.channels),
    	.lock = __MUTEX_INITIALIZER(test_info.lock),
    };
    
    static int dmatest_run_set(const char *val, const struct kernel_param *kp);
    static int dmatest_run_get(char *val, const struct kernel_param *kp);
    static const struct kernel_param_ops run_ops = {
    	.set = dmatest_run_set,
    	.get = dmatest_run_get,
    };
    static bool dmatest_run;
    module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR);
    MODULE_PARM_DESC(run, "Run the test (default: false)");
    
    /* Maximum amount of mismatched bytes in buffer to print */
    #define MAX_ERROR_COUNT		32
    
    /*
     * Initialization patterns. All bytes in the source buffer has bit 7
     * set, all bytes in the destination buffer has bit 7 cleared.
     *
     * Bit 6 is set for all bytes which are to be copied by the DMA
     * engine. Bit 5 is set for all bytes which are to be overwritten by
     * the DMA engine.
     *
     * The remaining bits are the inverse of a counter which increments by
     * one for each byte address.
     */
    #define PATTERN_SRC		0x80
    #define PATTERN_DST		0x00
    #define PATTERN_COPY		0x40
    #define PATTERN_OVERWRITE	0x20
    #define PATTERN_COUNT_MASK	0x1f
    #define PATTERN_MEMSET_IDX	0x01
    
    /* poor man's completion - we want to use wait_event_freezable() on it */
    struct dmatest_done {
    	bool			done;
    	wait_queue_head_t	*wait;
    };
    
    struct dmatest_thread {
    	struct list_head	node;
    	struct dmatest_info	*info;
    	struct task_struct	*task;
    	struct dma_chan		*chan;
    	u8			**srcs;
    	u8			**usrcs;
    	u8			**dsts;
    	u8			**udsts;
    	enum dma_transaction_type type;
    	wait_queue_head_t done_wait;
    	struct dmatest_done test_done;
    	bool			done;
    };
    
    struct dmatest_chan {
    	struct list_head	node;
    	struct dma_chan		*chan;
    	struct list_head	threads;
    };
    
    static DECLARE_WAIT_QUEUE_HEAD(thread_wait);
    static bool wait;
    
    static bool is_threaded_test_run(struct dmatest_info *info)
    {
    	struct dmatest_chan *dtc;
    
    	list_for_each_entry(dtc, &info->channels, node) {
    		struct dmatest_thread *thread;
    
    		list_for_each_entry(thread, &dtc->threads, node) {
    			if (!thread->done)
    				return true;
    		}
    	}
    
    	return false;
    }
    
    static int dmatest_wait_get(char *val, const struct kernel_param *kp)
    {
    	struct dmatest_info *info = &test_info;
    	struct dmatest_params *params = &info->params;
    
    	if (params->iterations)
    		wait_event(thread_wait, !is_threaded_test_run(info));
    	wait = true;
    	return param_get_bool(val, kp);
    }
    
    static const struct kernel_param_ops wait_ops = {
    	.get = dmatest_wait_get,
    	.set = param_set_bool,
    };
    module_param_cb(wait, &wait_ops, &wait, S_IRUGO);
    MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)");
    
    static bool dmatest_match_channel(struct dmatest_params *params,
    		struct dma_chan *chan)
    {
    	if (params->channel[0] == '\0')
    		return true;
    	return strcmp(dma_chan_name(chan), params->channel) == 0;
    }
    
    static bool dmatest_match_device(struct dmatest_params *params,
    		struct dma_device *device)
    {
    	if (params->device[0] == '\0')
    		return true;
    	return strcmp(dev_name(device->dev), params->device) == 0;
    }
    
    static unsigned long dmatest_random(void)
    {
    	unsigned long buf;
    
    	prandom_bytes(&buf, sizeof(buf));
    	return buf;
    }
    
    static inline u8 gen_inv_idx(u8 index, bool is_memset)
    {
    	u8 val = is_memset ? PATTERN_MEMSET_IDX : index;
    
    	return ~val & PATTERN_COUNT_MASK;
    }
    
    static inline u8 gen_src_value(u8 index, bool is_memset)
    {
    	return PATTERN_SRC | gen_inv_idx(index, is_memset);
    }
    
    static inline u8 gen_dst_value(u8 index, bool is_memset)
    {
    	return PATTERN_DST | gen_inv_idx(index, is_memset);
    }
    
    static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len,
    		unsigned int buf_size, bool is_memset)
    {
    	unsigned int i;
    	u8 *buf;
    
    	for (; (buf = *bufs); bufs++) {
    		for (i = 0; i < start; i++)
    			buf[i] = gen_src_value(i, is_memset);
    		for ( ; i < start + len; i++)
    			buf[i] = gen_src_value(i, is_memset) | PATTERN_COPY;
    		for ( ; i < buf_size; i++)
    			buf[i] = gen_src_value(i, is_memset);
    		buf++;
    	}
    }
    
    static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len,
    		unsigned int buf_size, bool is_memset)
    {
    	unsigned int i;
    	u8 *buf;
    
    	for (; (buf = *bufs); bufs++) {
    		for (i = 0; i < start; i++)
    			buf[i] = gen_dst_value(i, is_memset);
    		for ( ; i < start + len; i++)
    			buf[i] = gen_dst_value(i, is_memset) |
    						PATTERN_OVERWRITE;
    		for ( ; i < buf_size; i++)
    			buf[i] = gen_dst_value(i, is_memset);
    	}
    }
    
    static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index,
    		unsigned int counter, bool is_srcbuf, bool is_memset)
    {
    	u8		diff = actual ^ pattern;
    	u8		expected = pattern | gen_inv_idx(counter, is_memset);
    	const char	*thread_name = current->comm;
    
    	if (is_srcbuf)
    		pr_warn("%s: srcbuf[0x%x] overwritten! Expected %02x, got %02x\n",
    			thread_name, index, expected, actual);
    	else if ((pattern & PATTERN_COPY)
    			&& (diff & (PATTERN_COPY | PATTERN_OVERWRITE)))
    		pr_warn("%s: dstbuf[0x%x] not copied! Expected %02x, got %02x\n",
    			thread_name, index, expected, actual);
    	else if (diff & PATTERN_SRC)
    		pr_warn("%s: dstbuf[0x%x] was copied! Expected %02x, got %02x\n",
    			thread_name, index, expected, actual);
    	else
    		pr_warn("%s: dstbuf[0x%x] mismatch! Expected %02x, got %02x\n",
    			thread_name, index, expected, actual);
    }
    
    static unsigned int dmatest_verify(u8 **bufs, unsigned int start,
    		unsigned int end, unsigned int counter, u8 pattern,
    		bool is_srcbuf, bool is_memset)
    {
    	unsigned int i;
    	unsigned int error_count = 0;
    	u8 actual;
    	u8 expected;
    	u8 *buf;
    	unsigned int counter_orig = counter;
    
    	for (; (buf = *bufs); bufs++) {
    		counter = counter_orig;
    		for (i = start; i < end; i++) {
    			actual = buf[i];
    			expected = pattern | gen_inv_idx(counter, is_memset);
    			if (actual != expected) {
    				if (error_count < MAX_ERROR_COUNT)
    					dmatest_mismatch(actual, pattern, i,
    							 counter, is_srcbuf,
    							 is_memset);
    				error_count++;
    			}
    			counter++;
    		}
    	}
    
    	if (error_count > MAX_ERROR_COUNT)
    		pr_warn("%s: %u errors suppressed\n",
    			current->comm, error_count - MAX_ERROR_COUNT);
    
    	return error_count;
    }
    
    
    static void dmatest_callback(void *arg)
    {
    	struct dmatest_done *done = arg;
    	struct dmatest_thread *thread =
    		container_of(done, struct dmatest_thread, test_done);
    	if (!thread->done) {
    		done->done = true;
    		wake_up_all(done->wait);
    	} else {
    		/*
    		 * If thread->done, it means that this callback occurred
    		 * after the parent thread has cleaned up. This can
    		 * happen in the case that driver doesn't implement
    		 * the terminate_all() functionality and a dma operation
    		 * did not occur within the timeout period
    		 */
    		WARN(1, "dmatest: Kernel memory may be corrupted!!\n");
    	}
    }
    
    static unsigned int min_odd(unsigned int x, unsigned int y)
    {
    	unsigned int val = min(x, y);
    
    	return val % 2 ? val : val - 1;
    }
    
    static void result(const char *err, unsigned int n, unsigned int src_off,
    		   unsigned int dst_off, unsigned int len, unsigned long data)
    {
    	pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n",
    		current->comm, n, err, src_off, dst_off, len, data);
    }
    
    static void dbg_result(const char *err, unsigned int n, unsigned int src_off,
    		       unsigned int dst_off, unsigned int len,
    		       unsigned long data)
    {
    	pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n",
    		 current->comm, n, err, src_off, dst_off, len, data);
    }
    
    #define verbose_result(err, n, src_off, dst_off, len, data) ({	\
    	if (verbose)						\
    		result(err, n, src_off, dst_off, len, data);	\
    	else							\
    		dbg_result(err, n, src_off, dst_off, len, data);\
    })
    
    static unsigned long long dmatest_persec(s64 runtime, unsigned int val)
    {
    	unsigned long long per_sec = 1000000;
    
    	if (runtime <= 0)
    		return 0;
    
    	/* drop precision until runtime is 32-bits */
    	while (runtime > UINT_MAX) {
    		runtime >>= 1;
    		per_sec <<= 1;
    	}
    
    	per_sec *= val;
    	do_div(per_sec, runtime);
    	return per_sec;
    }
    
    static unsigned long long dmatest_KBs(s64 runtime, unsigned long long len)
    {
    	return dmatest_persec(runtime, len >> 10);
    }
    
    /*
     * This function repeatedly tests DMA transfers of various lengths and
     * offsets for a given operation type until it is told to exit by
     * kthread_stop(). There may be multiple threads running this function
     * in parallel for a single channel, and there may be multiple channels
     * being tested in parallel.
     *
     * Before each test, the source and destination buffer is initialized
     * with a known pattern. This pattern is different depending on
     * whether it's in an area which is supposed to be copied or
     * overwritten, and different in the source and destination buffers.
     * So if the DMA engine doesn't copy exactly what we tell it to copy,
     * we'll notice.
     */
    static int dmatest_func(void *data)
    {
    	struct dmatest_thread	*thread = data;
    	struct dmatest_done	*done = &thread->test_done;
    	struct dmatest_info	*info;
    	struct dmatest_params	*params;
    	struct dma_chan		*chan;
    	struct dma_device	*dev;
    	unsigned int		error_count;
    	unsigned int		failed_tests = 0;
    	unsigned int		total_tests = 0;
    	dma_cookie_t		cookie;
    	enum dma_status		status;
    	enum dma_ctrl_flags 	flags;
    	u8			*pq_coefs = NULL;
    	int			ret;
    	int			src_cnt;
    	int			dst_cnt;
    	int			i;
    	ktime_t			ktime, start, diff;
    	ktime_t			filltime = 0;
    	ktime_t			comparetime = 0;
    	s64			runtime = 0;
    	unsigned long long	total_len = 0;
    	u8			align = 0;
    	bool			is_memset = false;
    
    	set_freezable();
    
    	ret = -ENOMEM;
    
    	smp_rmb();
    	info = thread->info;
    	params = &info->params;
    	chan = thread->chan;
    	dev = chan->device;
    	if (thread->type == DMA_MEMCPY) {
    		align = dev->copy_align;
    		src_cnt = dst_cnt = 1;
    	} else if (thread->type == DMA_MEMSET) {
    		align = dev->fill_align;
    		src_cnt = dst_cnt = 1;
    		is_memset = true;
    	} else if (thread->type == DMA_XOR) {
    		/* force odd to ensure dst = src */
    		src_cnt = min_odd(params->xor_sources | 1, dev->max_xor);
    		dst_cnt = 1;
    		align = dev->xor_align;
    	} else if (thread->type == DMA_PQ) {
    		/* force odd to ensure dst = src */
    		src_cnt = min_odd(params->pq_sources | 1, dma_maxpq(dev, 0));
    		dst_cnt = 2;
    		align = dev->pq_align;
    
    		pq_coefs = kmalloc(params->pq_sources + 1, GFP_KERNEL);
    		if (!pq_coefs)
    			goto err_thread_type;
    
    		for (i = 0; i < src_cnt; i++)
    			pq_coefs[i] = 1;
    	} else
    		goto err_thread_type;
    
    	thread->srcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
    	if (!thread->srcs)
    		goto err_srcs;
    
    	thread->usrcs = kcalloc(src_cnt + 1, sizeof(u8 *), GFP_KERNEL);
    	if (!thread->usrcs)
    		goto err_usrcs;
    
    	for (i = 0; i < src_cnt; i++) {
    		thread->usrcs[i] = kmalloc(params->buf_size + align,
    					   GFP_KERNEL);
    		if (!thread->usrcs[i])
    			goto err_srcbuf;
    
    		/* align srcs to alignment restriction */
    		if (align)
    			thread->srcs[i] = PTR_ALIGN(thread->usrcs[i], align);
    		else
    			thread->srcs[i] = thread->usrcs[i];
    	}
    	thread->srcs[i] = NULL;
    
    	thread->dsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
    	if (!thread->dsts)
    		goto err_dsts;
    
    	thread->udsts = kcalloc(dst_cnt + 1, sizeof(u8 *), GFP_KERNEL);
    	if (!thread->udsts)
    		goto err_udsts;
    
    	for (i = 0; i < dst_cnt; i++) {
    		thread->udsts[i] = kmalloc(params->buf_size + align,
    					   GFP_KERNEL);
    		if (!thread->udsts[i])
    			goto err_dstbuf;
    
    		/* align dsts to alignment restriction */
    		if (align)
    			thread->dsts[i] = PTR_ALIGN(thread->udsts[i], align);
    		else
    			thread->dsts[i] = thread->udsts[i];
    	}
    	thread->dsts[i] = NULL;
    
    	set_user_nice(current, 10);
    
    	/*
    	 * src and dst buffers are freed by ourselves below
    	 */
    	flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
    
    	ktime = ktime_get();
    	while (!kthread_should_stop()
    	       && !(params->iterations && total_tests >= params->iterations)) {
    		struct dma_async_tx_descriptor *tx = NULL;
    		struct dmaengine_unmap_data *um;
    		dma_addr_t srcs[src_cnt];
    		dma_addr_t *dsts;
    		unsigned int src_off, dst_off, len;
    
    		total_tests++;
    
    		/* Check if buffer count fits into map count variable (u8) */
    		if ((src_cnt + dst_cnt) >= 255) {
    			pr_err("too many buffers (%d of 255 supported)\n",
    			       src_cnt + dst_cnt);
    			break;
    		}
    
    		if (1 << align > params->buf_size) {
    			pr_err("%u-byte buffer too small for %d-byte alignment\n",
    			       params->buf_size, 1 << align);
    			break;
    		}
    
    		if (params->noverify)
    			len = params->buf_size;
    		else
    			len = dmatest_random() % params->buf_size + 1;
    
    		len = (len >> align) << align;
    		if (!len)
    			len = 1 << align;
    
    		total_len += len;
    
    		if (params->noverify) {
    			src_off = 0;
    			dst_off = 0;
    		} else {
    			start = ktime_get();
    			src_off = dmatest_random() % (params->buf_size - len + 1);
    			dst_off = dmatest_random() % (params->buf_size - len + 1);
    
    			src_off = (src_off >> align) << align;
    			dst_off = (dst_off >> align) << align;
    
    			dmatest_init_srcs(thread->srcs, src_off, len,
    					  params->buf_size, is_memset);
    			dmatest_init_dsts(thread->dsts, dst_off, len,
    					  params->buf_size, is_memset);
    
    			diff = ktime_sub(ktime_get(), start);
    			filltime = ktime_add(filltime, diff);
    		}
    
    		um = dmaengine_get_unmap_data(dev->dev, src_cnt + dst_cnt,
    					      GFP_KERNEL);
    		if (!um) {
    			failed_tests++;
    			result("unmap data NULL", total_tests,
    			       src_off, dst_off, len, ret);
    			continue;
    		}
    
    		um->len = params->buf_size;
    		for (i = 0; i < src_cnt; i++) {
    			void *buf = thread->srcs[i];
    			struct page *pg = virt_to_page(buf);
    			unsigned long pg_off = offset_in_page(buf);
    
    			um->addr[i] = dma_map_page(dev->dev, pg, pg_off,
    						   um->len, DMA_TO_DEVICE);
    			srcs[i] = um->addr[i] + src_off;
    			ret = dma_mapping_error(dev->dev, um->addr[i]);
    			if (ret) {
    				dmaengine_unmap_put(um);
    				result("src mapping error", total_tests,
    				       src_off, dst_off, len, ret);
    				failed_tests++;
    				continue;
    			}
    			um->to_cnt++;
    		}
    		/* map with DMA_BIDIRECTIONAL to force writeback/invalidate */
    		dsts = &um->addr[src_cnt];
    		for (i = 0; i < dst_cnt; i++) {
    			void *buf = thread->dsts[i];
    			struct page *pg = virt_to_page(buf);
    			unsigned long pg_off = offset_in_page(buf);
    
    			dsts[i] = dma_map_page(dev->dev, pg, pg_off, um->len,
    					       DMA_BIDIRECTIONAL);
    			ret = dma_mapping_error(dev->dev, dsts[i]);
    			if (ret) {
    				dmaengine_unmap_put(um);
    				result("dst mapping error", total_tests,
    				       src_off, dst_off, len, ret);
    				failed_tests++;
    				continue;
    			}
    			um->bidi_cnt++;
    		}
    
    		if (thread->type == DMA_MEMCPY)
    			tx = dev->device_prep_dma_memcpy(chan,
    							 dsts[0] + dst_off,
    							 srcs[0], len, flags);
    		else if (thread->type == DMA_MEMSET)
    			tx = dev->device_prep_dma_memset(chan,
    						dsts[0] + dst_off,
    						*(thread->srcs[0] + src_off),
    						len, flags);
    		else if (thread->type == DMA_XOR)
    			tx = dev->device_prep_dma_xor(chan,
    						      dsts[0] + dst_off,
    						      srcs, src_cnt,
    						      len, flags);
    		else if (thread->type == DMA_PQ) {
    			dma_addr_t dma_pq[dst_cnt];
    
    			for (i = 0; i < dst_cnt; i++)
    				dma_pq[i] = dsts[i] + dst_off;
    			tx = dev->device_prep_dma_pq(chan, dma_pq, srcs,
    						     src_cnt, pq_coefs,
    						     len, flags);
    		}
    
    		if (!tx) {
    			dmaengine_unmap_put(um);
    			result("prep error", total_tests, src_off,
    			       dst_off, len, ret);
    			msleep(100);
    			failed_tests++;
    			continue;
    		}
    
    		done->done = false;
    		tx->callback = dmatest_callback;
    		tx->callback_param = done;
    		cookie = tx->tx_submit(tx);
    
    		if (dma_submit_error(cookie)) {
    			dmaengine_unmap_put(um);
    			result("submit error", total_tests, src_off,
    			       dst_off, len, ret);
    			msleep(100);
    			failed_tests++;
    			continue;
    		}
    		dma_async_issue_pending(chan);
    
    		wait_event_freezable_timeout(thread->done_wait, done->done,
    					     msecs_to_jiffies(params->timeout));
    
    		status = dma_async_is_tx_complete(chan, cookie, NULL, NULL);
    
    		if (!done->done) {
    			dmaengine_unmap_put(um);
    			result("test timed out", total_tests, src_off, dst_off,
    			       len, 0);
    			failed_tests++;
    			continue;
    		} else if (status != DMA_COMPLETE) {
    			dmaengine_unmap_put(um);
    			result(status == DMA_ERROR ?
    			       "completion error status" :
    			       "completion busy status", total_tests, src_off,
    			       dst_off, len, ret);
    			failed_tests++;
    			continue;
    		}
    
    		dmaengine_unmap_put(um);
    
    		if (params->noverify) {
    			verbose_result("test passed", total_tests, src_off,
    				       dst_off, len, 0);
    			continue;
    		}
    
    		start = ktime_get();
    		pr_debug("%s: verifying source buffer...\n", current->comm);
    		error_count = dmatest_verify(thread->srcs, 0, src_off,
    				0, PATTERN_SRC, true, is_memset);
    		error_count += dmatest_verify(thread->srcs, src_off,
    				src_off + len, src_off,
    				PATTERN_SRC | PATTERN_COPY, true, is_memset);
    		error_count += dmatest_verify(thread->srcs, src_off + len,
    				params->buf_size, src_off + len,
    				PATTERN_SRC, true, is_memset);
    
    		pr_debug("%s: verifying dest buffer...\n", current->comm);
    		error_count += dmatest_verify(thread->dsts, 0, dst_off,
    				0, PATTERN_DST, false, is_memset);
    
    		error_count += dmatest_verify(thread->dsts, dst_off,
    				dst_off + len, src_off,
    				PATTERN_SRC | PATTERN_COPY, false, is_memset);
    
    		error_count += dmatest_verify(thread->dsts, dst_off + len,
    				params->buf_size, dst_off + len,
    				PATTERN_DST, false, is_memset);
    
    		diff = ktime_sub(ktime_get(), start);
    		comparetime = ktime_add(comparetime, diff);
    
    		if (error_count) {
    			result("data error", total_tests, src_off, dst_off,
    			       len, error_count);
    			failed_tests++;
    		} else {
    			verbose_result("test passed", total_tests, src_off,
    				       dst_off, len, 0);
    		}
    	}
    	ktime = ktime_sub(ktime_get(), ktime);
    	ktime = ktime_sub(ktime, comparetime);
    	ktime = ktime_sub(ktime, filltime);
    	runtime = ktime_to_us(ktime);
    
    	ret = 0;
    err_dstbuf:
    	for (i = 0; thread->udsts[i]; i++)
    		kfree(thread->udsts[i]);
    	kfree(thread->udsts);
    err_udsts:
    	kfree(thread->dsts);
    err_dsts:
    err_srcbuf:
    	for (i = 0; thread->usrcs[i]; i++)
    		kfree(thread->usrcs[i]);
    	kfree(thread->usrcs);
    err_usrcs:
    	kfree(thread->srcs);
    err_srcs:
    	kfree(pq_coefs);
    err_thread_type:
    	pr_info("%s: summary %u tests, %u failures %llu iops %llu KB/s (%d)\n",
    		current->comm, total_tests, failed_tests,
    		dmatest_persec(runtime, total_tests),
    		dmatest_KBs(runtime, total_len), ret);
    
    	/* terminate all transfers on specified channels */
    	if (ret || failed_tests)
    		dmaengine_terminate_all(chan);
    
    	thread->done = true;
    	wake_up(&thread_wait);
    
    	return ret;
    }
    
    static void dmatest_cleanup_channel(struct dmatest_chan *dtc)
    {
    	struct dmatest_thread	*thread;
    	struct dmatest_thread	*_thread;
    	int			ret;
    
    	list_for_each_entry_safe(thread, _thread, &dtc->threads, node) {
    		ret = kthread_stop(thread->task);
    		pr_debug("thread %s exited with status %d\n",
    			 thread->task->comm, ret);
    		list_del(&thread->node);
    		put_task_struct(thread->task);
    		kfree(thread);
    	}
    
    	/* terminate all transfers on specified channels */
    	dmaengine_terminate_all(dtc->chan);
    
    	kfree(dtc);
    }
    
    static int dmatest_add_threads(struct dmatest_info *info,
    		struct dmatest_chan *dtc, enum dma_transaction_type type)
    {
    	struct dmatest_params *params = &info->params;
    	struct dmatest_thread *thread;
    	struct dma_chan *chan = dtc->chan;
    	char *op;
    	unsigned int i;
    
    	if (type == DMA_MEMCPY)
    		op = "copy";
    	else if (type == DMA_MEMSET)
    		op = "set";
    	else if (type == DMA_XOR)
    		op = "xor";
    	else if (type == DMA_PQ)
    		op = "pq";
    	else
    		return -EINVAL;
    
    	for (i = 0; i < params->threads_per_chan; i++) {
    		thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL);
    		if (!thread) {
    			pr_warn("No memory for %s-%s%u\n",
    				dma_chan_name(chan), op, i);
    			break;
    		}
    		thread->info = info;
    		thread->chan = dtc->chan;
    		thread->type = type;
    		thread->test_done.wait = &thread->done_wait;
    		init_waitqueue_head(&thread->done_wait);
    		smp_wmb();
    		thread->task = kthread_create(dmatest_func, thread, "%s-%s%u",
    				dma_chan_name(chan), op, i);
    		if (IS_ERR(thread->task)) {
    			pr_warn("Failed to create thread %s-%s%u\n",
    				dma_chan_name(chan), op, i);
    			kfree(thread);
    			break;
    		}
    
    		/* srcbuf and dstbuf are allocated by the thread itself */
    		get_task_struct(thread->task);
    		list_add_tail(&thread->node, &dtc->threads);
    		wake_up_process(thread->task);
    	}
    
    	return i;
    }
    
    static int dmatest_add_channel(struct dmatest_info *info,
    		struct dma_chan *chan)
    {
    	struct dmatest_chan	*dtc;
    	struct dma_device	*dma_dev = chan->device;
    	unsigned int		thread_count = 0;
    	int cnt;
    
    	dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL);
    	if (!dtc) {
    		pr_warn("No memory for %s\n", dma_chan_name(chan));
    		return -ENOMEM;
    	}
    
    	dtc->chan = chan;
    	INIT_LIST_HEAD(&dtc->threads);
    
    	if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
    		if (dmatest == 0) {
    			cnt = dmatest_add_threads(info, dtc, DMA_MEMCPY);
    			thread_count += cnt > 0 ? cnt : 0;
    		}
    	}
    
    	if (dma_has_cap(DMA_MEMSET, dma_dev->cap_mask)) {
    		if (dmatest == 1) {
    			cnt = dmatest_add_threads(info, dtc, DMA_MEMSET);
    			thread_count += cnt > 0 ? cnt : 0;
    		}
    	}
    
    	if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
    		cnt = dmatest_add_threads(info, dtc, DMA_XOR);
    		thread_count += cnt > 0 ? cnt : 0;
    	}
    	if (dma_has_cap(DMA_PQ, dma_dev->cap_mask)) {
    		cnt = dmatest_add_threads(info, dtc, DMA_PQ);
    		thread_count += cnt > 0 ? cnt : 0;
    	}
    
    	pr_info("Started %u threads using %s\n",
    		thread_count, dma_chan_name(chan));
    
    	list_add_tail(&dtc->node, &info->channels);
    	info->nr_channels++;
    
    	return 0;
    }
    
    static bool filter(struct dma_chan *chan, void *param)
    {
    	struct dmatest_params *params = param;
    
    	if (!dmatest_match_channel(params, chan) ||
    	    !dmatest_match_device(params, chan->device))
    		return false;
    	else
    		return true;
    }
    
    static void request_channels(struct dmatest_info *info,
    			     enum dma_transaction_type type)
    {
    	dma_cap_mask_t mask;
    
    	dma_cap_zero(mask);
    	dma_cap_set(type, mask);
    	for (;;) {
    		struct dmatest_params *params = &info->params;
    		struct dma_chan *chan;
    
    		chan = dma_request_channel(mask, filter, params);
    		if (chan) {
    			if (dmatest_add_channel(info, chan)) {
    				dma_release_channel(chan);
    				break; /* add_channel failed, punt */
    			}
    		} else
    			break; /* no more channels available */
    		if (params->max_channels &&
    		    info->nr_channels >= params->max_channels)
    			break; /* we have all we need */
    	}
    }
    
    static void run_threaded_test(struct dmatest_info *info)
    {
    	struct dmatest_params *params = &info->params;
    
    	/* Copy test parameters */
    	params->buf_size = test_buf_size;
    	strlcpy(params->channel, strim(test_channel), sizeof(params->channel));
    	strlcpy(params->device, strim(test_device), sizeof(params->device));
    	params->threads_per_chan = threads_per_chan;
    	params->max_channels = max_channels;
    	params->iterations = iterations;
    	params->xor_sources = xor_sources;
    	params->pq_sources = pq_sources;
    	params->timeout = timeout;
    	params->noverify = noverify;
    
    	request_channels(info, DMA_MEMCPY);
    	request_channels(info, DMA_MEMSET);
    	request_channels(info, DMA_XOR);
    	request_channels(info, DMA_PQ);
    }
    
    static void stop_threaded_test(struct dmatest_info *info)
    {
    	struct dmatest_chan *dtc, *_dtc;
    	struct dma_chan *chan;
    
    	list_for_each_entry_safe(dtc, _dtc, &info->channels, node) {
    		list_del(&dtc->node);
    		chan = dtc->chan;
    		dmatest_cleanup_channel(dtc);
    		pr_debug("dropped channel %s\n", dma_chan_name(chan));
    		dma_release_channel(chan);
    	}
    
    	info->nr_channels = 0;
    }
    
    static void restart_threaded_test(struct dmatest_info *info, bool run)
    {
    	/* we might be called early to set run=, defer running until all
    	 * parameters have been evaluated
    	 */
    	if (!info->did_init)
    		return;
    
    	/* Stop any running test first */
    	stop_threaded_test(info);
    
    	/* Run test with new parameters */
    	run_threaded_test(info);
    }
    
    static int dmatest_run_get(char *val, const struct kernel_param *kp)
    {
    	struct dmatest_info *info = &test_info;
    
    	mutex_lock(&info->lock);
    	if (is_threaded_test_run(info)) {
    		dmatest_run = true;
    	} else {
    		stop_threaded_test(info);
    		dmatest_run = false;
    	}
    	mutex_unlock(&info->lock);
    
    	return param_get_bool(val, kp);
    }
    
    static int dmatest_run_set(const char *val, const struct kernel_param *kp)
    {
    	struct dmatest_info *info = &test_info;
    	int ret;
    
    	mutex_lock(&info->lock);
    	ret = param_set_bool(val, kp);
    	if (ret) {
    		mutex_unlock(&info->lock);
    		return ret;
    	}
    
    	if (is_threaded_test_run(info))
    		ret = -EBUSY;
    	else if (dmatest_run)
    		restart_threaded_test(info, dmatest_run);
    
    	mutex_unlock(&info->lock);
    
    	return ret;
    }
    
    static int __init dmatest_init(void)
    {
    	struct dmatest_info *info = &test_info;
    	struct dmatest_params *params = &info->params;
    
    	if (dmatest_run) {
    		mutex_lock(&info->lock);
    		run_threaded_test(info);
    		mutex_unlock(&info->lock);
    	}
    
    	if (params->iterations && wait)
    		wait_event(thread_wait, !is_threaded_test_run(info));
    
    	/* module parameters are stable, inittime tests are started,
    	 * let userspace take over 'run' control
    	 */
    	info->did_init = true;
    
    	return 0;
    }
    /* when compiled-in wait for drivers to load first */
    late_initcall(dmatest_init);
    
    static void __exit dmatest_exit(void)
    {
    	struct dmatest_info *info = &test_info;
    
    	mutex_lock(&info->lock);
    	stop_threaded_test(info);
    	mutex_unlock(&info->lock);
    }
    module_exit(dmatest_exit);
    
    MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
    MODULE_LICENSE("GPL v2");
    

  • Hello Vamsi,

    You can also refer to the this, this and this material.

    Best regards,
    Kemal

  • Can you please check gpmc node declaration from my dts file below. I made changes for nor gpmc interface.

    Let me know if any changes has to be done.

    &gpmc
    {
        pinctrl-names = "default";
        pinctrl-0 = <&gpmc_pins>;
        ti,pindir-d0-out-d1-in = <1>;
        status = "okay";
        nor@0,0 {
            compatible = "cfi-flash";
            //linux,mtd-name= "intel,pf48f6000m0y1be";
            #address-cells = <1>;
            #size-cells = <1>;
            reg = <0 0 0x08000000>;
            bank-width = <8>;

            gpmc,mux-add-data;
            gpmc,cs-on-ns = <0>;
            gpmc,cs-rd-off-ns = <10>;
            gpmc,cs-wr-off-ns = <6>;
            gpmc,adv-on-ns = <1>;
            gpmc,adv-rd-off-ns = <1>;
            gpmc,adv-wr-off-ns = <1>;
            gpmc,oe-on-ns = <3>;
            gpmc,oe-off-ns = <10>;
            gpmc,we-on-ns = <1>;
            gpmc,we-off-ns = <5>;
            gpmc,rd-cycle-ns = <9>;
            gpmc,wr-cycle-ns = <6>;
            gpmc,access-ns = <9>;
            gpmc,page-burst-access-ns = <1>;
            gpmc,bus-turnaround-ns = <12>;
            gpmc,cycle2cycle-delay-ns = <18>;
            gpmc,wr-data-mux-bus-ns = <90>;
            gpmc,wr-access-ns = <9>;
            gpmc,cycle2cycle-samecsen;
            gpmc,cycle2cycle-diffcsen;

            //partition@0 {
            //    label = "bootloader-nor";
            //    reg = <0 0x40000>;
            //};
            //partition@0x40000 {
            //    label = "params-nor";
            //    reg = <0x40000 0x40000>;
            //};
            //partition@0x80000 {
            //    label = "kernel-nor";
            //    reg = <0x80000 0x200000>;
            //};
            //partition@0x280000 {
            //    label = "filesystem-nor";
            //    reg = <0x240000 0x7d80000>;
            //};
        };
    };

    gpmc_pins: pinmux_gpmc_pins {
            pinctrl-single,pins = <
                AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad0.gpmc_ad0 */
                AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad1.gpmc_ad1 */
                AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad2.gpmc_ad2 */
                AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad3.gpmc_ad3 */
                AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad4.gpmc_ad4 */
                AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad5.gpmc_ad5 */
                AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad6.gpmc_ad6 */
                AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE0)    /* gpmc_ad7.gpmc_ad7 */
                AM33XX_IOPAD(0x894, PIN_OUTPUT_PULLUP | MUX_MODE0)     /* gpmc_OEn_REn.gpmc_OEn_REn */
                AM33XX_IOPAD(0x890, PIN_OUTPUT_PULLUP | MUX_MODE0)     /* gpmc_ADVn_ALE.gpmc_ADVn_ALE */
                AM33XX_IOPAD(0x89c, PIN_OUTPUT_PULLUP | MUX_MODE0)    /* gpmc_BEn0_CLE.gpmc_BEn0_CLE */
            >;
        };   

  • does this linuxutils_2_24_03.tar.gz
    in
    software-dl.ti.com/.../ work with AM335x SDK: ti-processor-sdk-linux-am335x-evm-05.00.00.15  ????????

  • 1. I've added gpmc node and pinmux in dts.

    GPMC node:

    &gpmc
    {
    pinctrl-names = "default";
    pinctrl-0 = <&gpmc_pins>;
    ti,pindir-d0-out-d1-in = <1>;
    status = "okay";
    nor@0,0 {
    compatible = "cfi-flash";
    //linux,mtd-name= "intel,pf48f6000m0y1be";
    #address-cells = <1>;
    #size-cells = <1>;
    reg = <0 0 0x08000000>;
    bank-width = <8>;

    gpmc,mux-add-data;
    gpmc,cs-on-ns = <0>;
    gpmc,cs-rd-off-ns = <10>;
    gpmc,cs-wr-off-ns = <6>;
    gpmc,adv-on-ns = <1>;
    gpmc,adv-rd-off-ns = <1>;
    gpmc,adv-wr-off-ns = <1>;
    gpmc,oe-on-ns = <3>;
    gpmc,oe-off-ns = <10>;
    gpmc,we-on-ns = <1>;
    gpmc,we-off-ns = <5>;
    gpmc,rd-cycle-ns = <9>;
    gpmc,wr-cycle-ns = <6>;
    gpmc,access-ns = <9>;
    gpmc,page-burst-access-ns = <1>;
    gpmc,bus-turnaround-ns = <12>;
    gpmc,cycle2cycle-delay-ns = <18>;
    gpmc,wr-data-mux-bus-ns = <90>;
    gpmc,wr-access-ns = <9>;
    gpmc,cycle2cycle-samecsen;
    gpmc,cycle2cycle-diffcsen;

    //partition@0 {
    // label = "bootloader-nor";
    // reg = <0 0x40000>;
    //};
    //partition@0x40000 {
    // label = "params-nor";
    // reg = <0x40000 0x40000>;
    //};
    //partition@0x80000 {
    // label = "kernel-nor";
    // reg = <0x80000 0x200000>;
    //};
    //partition@0x280000 {
    // label = "filesystem-nor";
    // reg = <0x240000 0x7d80000>;
    //};
    };
    };

    Pinmux:

    gpmc_pins: pinmux_gpmc_pins {
    pinctrl-single,pins = <
    AM33XX_IOPAD(0x800, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad0.gpmc_ad0 */
    AM33XX_IOPAD(0x804, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad1.gpmc_ad1 */
    AM33XX_IOPAD(0x808, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad2.gpmc_ad2 */
    AM33XX_IOPAD(0x80c, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad3.gpmc_ad3 */
    AM33XX_IOPAD(0x810, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad4.gpmc_ad4 */
    AM33XX_IOPAD(0x814, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad5.gpmc_ad5 */
    AM33XX_IOPAD(0x818, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad6.gpmc_ad6 */
    AM33XX_IOPAD(0x81c, PIN_INPUT_PULLUP | MUX_MODE0) /* gpmc_ad7.gpmc_ad7 */
    AM33XX_IOPAD(0x894, PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_OEn_REn.gpmc_OEn_REn */
    AM33XX_IOPAD(0x890, PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_ADVn_ALE.gpmc_ADVn_ALE */
    AM33XX_IOPAD(0x89c, PIN_OUTPUT_PULLUP | MUX_MODE0) /* gpmc_BEn0_CLE.gpmc_BEn0_CLE */
    >;
    };

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    2. I checked for GPMC under /proc/iomem (highlighted in red below)

    root@am335x-evm:~/boot# cat /proc/iomem
    40300000-4030ffff : 40300000.ocmcram
    44d00000-44d03fff : umem
    44d80000-44d81fff : dmem
    44e07000-44e07fff : /ocp/gpio@44e07000
    44e09000-44e0901f : serial
    44e0b000-44e0bfff : /ocp/i2c@44e0b000
    44e0d000-44e0dfff : /ocp/tscadc@44e0d000
    44e10650-44e10653 : gmii-sel
    44e10800-44e10a37 : pinctrl-single
    44e10f90-44e10fcf : /ocp/l4_wkup@44c00000/scm@210000/dma-router@f90
    44e11324-44e11347 : /ocp/l4_wkup@44c00000/scm@210000/wkup_m3_ipc@1324
    44e35000-44e35fff : /ocp/wdt@44e35000
    44e3e000-44e3efff : /ocp/rtc@44e3e000
    48022000-4802201f : serial
    48030000-480303ff : /ocp/spi@48030000
    48042000-480423ff : /ocp/timer@48042000
    48044000-480443ff : /ocp/timer@48044000
    48046000-480463ff : /ocp/timer@48046000
    48048000-480483ff : /ocp/timer@48048000
    4804a000-4804a3ff : /ocp/timer@4804a000
    4804c000-4804cfff : /ocp/gpio@4804c000
    48060000-48060fff : /ocp/mmc@48060000
    480c8000-480c81ff : /ocp/mailbox@480C8000
    481a0000-481a03ff : /ocp/spi@481a0000
    481ac000-481acfff : /ocp/gpio@481ac000
    481ae000-481aefff : /ocp/gpio@481ae000
    481d8000-481d8fff : /ocp/mmc@481d8000
    48304100-4830417f : /ocp/epwmss@48304000/ecap@48304100
    48310000-48311fff : /ocp/rng@48310000
    49000000-4900ffff : edma3_cc
    4a100000-4a1007ff : /ocp/ethernet@4a100000
    4a101000-4a1010ff : /ocp/ethernet@4a100000/mdio@4a101000
    4a101200-4a1012ff : /ocp/ethernet@4a100000
    4a320000-4a321fff : intc
    4a322000-4a3223ff : control
    4a322400-4a3224ff : debug
    4a324000-4a3243ff : control
    4a324400-4a3244ff : debug
    4a334000-4a335fff : iram
    4a338000-4a339fff : iram
    4c000000-4c000fff : /ocp/emif@4c000000
    50000000-50001fff : /ocp/gpmc@50000000
    53100000-531001ff : /ocp/sham@53100000
    53500000-5350009f : /ocp/aes@53500000
    80000000-8fffffff : System RAM
    80008000-80bfffff : Kernel code
    80d00000-80d94923 : Kernel data

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    3. Following is my code to send data to FPGA using GPMC lines.

    #include<stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <termios.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <time.h>
    #include <sys/mman.h>

    #define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
    __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)

    int verbose = 0;

    int main(){
        int fd;char ch;
        if((fd=open("/dev/mem", O_RDWR | O_SYNC)) == -1) {printf("Error in opening gpmc\n");return 0;}
        printf("Successfully opened\n");
        volatile uint8_t *map_base;
        map_base =(uint8_t *) mmap(0, 0x01000000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x02000000);
        if(map_base == (void *) -1) FATAL;if (verbose) {
               printf("Memory mapped at address %p.\n", map_base);
               fflush(stdout);
         }
         volatile uint32_t *p32 = (uint32_t *) &map_base[0x100020];

         printf("Hello 2\n");
         *p32 = 0x1; //It is causing bus error.

         printf("Closing FD\n");
         close(fd);

    }

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    Output:

    root@am335x-evm:~/boot# ./a.out
    Successfully opened[ 3348.926190] Unhandled fault: external abort on non-linefetch (0x1818) at 0xb5eab800
    [ 3348.934354] pgd = c60f0000
    [ 3348.937074] [b5eab800] *pgd=86028831, *pte=02000343, *ppte=02000833

    Hello 2
    Bus error (core dumped)

    -----------------------------------------------------------------------------------------------------------------------------------------------------------

    Please verify the above things and help me where I am wrong.

    Regards

    Vamsi

  • I am happy to tell that, gpmc fpga data transferring is working fine. Following changes I made to make it work.

    1. gpmc-fpga pinmux added from
    e2e.ti.com/.../555762



    2. added to /linux-4.14.40+gitAUTOINC+4796173fc5-g4796173fc5/drivers/memoryomap-gpmc.c

    replaced the function:

    static void gpmc_cs_set_reserved(int cs, int reserved)
    {
    struct gpmc_cs_data *gpmc = &gpmc_cs[cs];

    gpmc->flags |= GPMC_CS_RESERVED;
    }

    with:

    static void gpmc_cs_set_reserved(int cs, int reserved)
    {
    struct gpmc_cs_data *gpmc = &gpmc_cs[cs];

    if (reserved)
    gpmc->flags |= GPMC_CS_RESERVED;
    else
    gpmc->flags &= ~GPMC_CS_RESERVED;
    }

    My user level program for transferring data to FPGA using GPMC.

    #include<stdio.h>                                                            
    #include <stdint.h>                                                          
    #include <stdlib.h>                                                          
    #include <termios.h>                                                         
    #include <string.h>                                                          
    #include <unistd.h>                                                          
    #include <fcntl.h>                                                           
    #include <errno.h>                                                           
    #include <time.h>                                                            
    #include <sys/mman.h>                                                        
                                                                                 
    #define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
      __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
                                                               
    int verbose = 0;                                                                                                      
                                                                                                                          
    #define BUF_SIZE 4096                                                                                                 
                                                                                                                          
    int main(){                                                                                                           
            int i,fd;char ch;                                                                                             
            int buf[BUF_SIZE];                                                                                            
            volatile uint8_t *map_base;                                                                                   
            int a=1;   

            for(i=0;i<BUF_SIZE;i++){                                             
                    if(i%100==0){                                                
                            a++;                                                 
                            printf("i: %d,   a: %d\n",i,a);                      
                    }                                                 
                    buf[i]=a;                                                    
            }                                                                    
            //buf[BUF_SIZE]=43;                                                  
                                                                                 
            if((fd=open("/dev/mem", O_RDWR | O_SYNC)) == -1) {                   
                    printf("Error in opening gpmc\n");return 0;                  
            }                                                                    
            printf("Successfully opened\n");                          
                                                               
            map_base =(uint8_t *) mmap((void*)0x01000000, 0x01000000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x01000000);
            if(map_base == (void *) -1) FATAL;                                                                            
            if (verbose) {                                                                                                
              printf("Memory mapped at address %p.\n", map_base);                                                         
              fflush(stdout);                                                                                             
            }                                                                                                             
            volatile uint32_t *p32 = (uint32_t *) &map_base[0x0800];                                                      
            //while(1/*(ch=getchar())!='\n'*/){                                                                           
                    //*p32 = 0x1;                                                                                         
                    for(i=0;i<BUF_SIZE;i++){                                                                              
                            *p32 = buf[i];                                                                                
                    }                                                                                                     
                    printf("Sent %d bytes\n",BUF_SIZE);                                                                   
            //}                                                                                                           
            printf("Closing FD\n");                                                                                       
            close(fd);                                                                                                    

    Now having seen this GPMC working, we want to add EDMA for speeding up of data transfer.

    Thank you.

  • Please someone tell me how to approach EDMA for the GPMC in my above post.
  • Can we write EDMA application at user level also? or only we can do it at kernel level?
  • Hello Vamsi,

    Please refer to the following threads: thread1 and thread2

    Regards,

    Krunal

  • Thank you Krunal.

    I've already referred to them.

    I'm facing difficulty is in editing that dmatest.c. It is a kernel application which will move data between the 2 buffers present inside the processors itself. But my requirement is to send data to FPGA. Where exactly I've to change the code i'm facing the difficulty.

    In function, dmatest_func(), I'm thinking of changing the dmatest_init_srcs() & dmatest_init_dsts(). And also device_prep_dma_memcpy(), has to be changed.

    Kindly help me to proceed further.

    Regards
    Vamsi
  • Hello Vamsi,

    Dmatest is a reference example integrated in kernel and TI does not support it.

    Regards,
    Krunal
  • Yes I agree that it is a reference example. But If it is getting compiled & running means, TI SDK is supporting that dma test. isn't it?
    Please correct me if I am wrong.

    Regards
    Vamsi
  • Hello Vamsi,

    Processor SDK Linux on Sitara device integrates many free and open source software (FOSS) to demonstrate the hardware capabilities of Sitara SoC. While TI integrates these FOSS and put together example applications in Processor SDK Linux for ease of software development, TI does not own, maintain and support these FOSS.

    Regards,
    Krunal