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.

Mcbsp5 Linux driver not working

Other Parts Discussed in Thread: OMAP3530

Hi,

I am trying to send string "test" via mcbsp5 under Linux on OMAP3530. The oscilloscope shows nothing on DX, CLCX and FSX.  The purpose of this test is just to figure out if mcbsp5 linux driver is working properly.

Here is code:

/*
 * mcbsp_drv.c: McBSP driver.
 */

 
#include "types.h"
#include "omap3530evm.h"
#include "mcbsp_utils.h"
#include "mcbsp_drv.h"
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>


extern U8 read_data_mcspi(U32 spi_base_addr, U8 channel_number, U32 * read_buffer);
extern U8 write_data_mcspi(U32 spi_base_addr, U8 channel_number, U32 * write_buffer);

struct mcbsp_drv_struct mcbsp_drv_t;


/* This function is used to initialize the McBSP. */
S32 mcbsp_init(mcbsp_drv_struct_t * mcbsp_drv)
{
	S32 ret_status = SUCCESS;

        mcbsp_drv->physical_addr = ARM_MCBSP5_BASE_ADDR;
                /* Update the Virtual address. */
        mcbsp_drv->virtual_addr = mcbsp_drv->physical_addr;


	mcbsp_drv->mcbsp_initstr.clksr_val = 1; 
	mcbsp_drv->mcbsp_initstr.master = 1; 


	ret_status = config_mcbsp(mcbsp_drv->virtual_addr,
				  mcbsp_drv->mcbsp_initstr.master,
				  mcbsp_drv->mcbsp_initstr.clksr_sel,
				  mcbsp_drv->mcbsp_initstr.clksr_val,
				  mcbsp_drv->mcbsp_initstr.num_of_phases, mcbsp_drv->mcbsp_initstr.num_words_phase1, mcbsp_drv->mcbsp_initstr.num_words_phase2, mcbsp_drv->mcbsp_initstr.word_size_phase1, mcbsp_drv->mcbsp_initstr.word_size_phase1, mcbsp_drv->mcbsp_initstr.mode);


	/* Reset the device. */
	reset_mcbsp(mcbsp_drv->virtual_addr, 1);
	return ret_status;
}


/* This function is used to read from McBSP channel. */
S32 mcbsp_write(mcbsp_drv_struct_t * mcbsp_drv, mcbsp_tag_t tag, U32 * size, U8 * buffer)
{
	
	U32 count, i;
	U16 *pBuffer;
	 /*
	 ** Buffer contains data to be written. 
	 ** Size gives the number of locations to be written.
	 */
	count = *size;
	/*
	 ** Clear size, after writing it will hold number of location 
	 ** successfully writtten.
	 */
	pBuffer = (U16 *) buffer;

	/* Reset the McBSP. */
	reset_mcbsp(mcbsp_drv->virtual_addr, 1);

	for (i = 0; i < count ; i++) {
		if (SUCCESS == write_data_mcbsp(mcbsp_drv->virtual_addr, (pBuffer + i))) {
		}

	}

	/* Reset the McBSP */
	reset_mcbsp(mcbsp_drv->virtual_addr, 0);

	return SUCCESS;
}

/* This function is used to initialize the McBSP. */
S32 mcbsp_deinit(mcbsp_drv_struct_t * mcbsp_drv)
{
	/* De-initialize the device by a soft reset. */
	reset_mcbsp(mcbsp_drv->virtual_addr, 0);

	return SUCCESS;
}

static ssize_t mcbsp_audio_start_tx(struct device *dev,
                                     struct device_attribute *attr,
                                     char *buf, size_t size)
{
        int val;

        if (strict_strtoul(buf, 16, &val))
                return -EINVAL;
	U32 string_size = 5;

	if (val == 1){
			mcbsp_write(&mcbsp_drv_t, 0, &string_size, "test");
			}
        return size;
}


static struct device_attribute mcbsp_audio_attributes[1] = {

__ATTR(mcbsp_audio_start_TX, 0222, NULL, mcbsp_audio_start_tx),  
 };

static int create_sysfs_interface(struct device *dev)
{
        int i;
        for (i = 0; i < ARRAY_SIZE(mcbsp_audio_attributes); i++)
                if (device_create_file(dev, mcbsp_audio_attributes + i))
                        goto error;
        return 0;

error:
        for ( ; i >= 0; i--)
                device_remove_file(dev, mcbsp_audio_attributes + i);
        dev_err(dev, "%s:Unable to create interface\n", __func__);
        return -1;
}

static int remove_sysfs_interface(struct device *dev)
{
        int i;
        for (i = 0; i < ARRAY_SIZE(mcbsp_audio_attributes); i++)
                device_remove_file(dev, mcbsp_audio_attributes + i);
        return 0;
}

static int mcbsp_audio_remove(struct platform_device *pdev)
{
	remove_sysfs_interface(&pdev->dev);
  	return 0;
}

static int __devinit mcbsp_audio_probe(struct platform_device *pdev)
{
        create_sysfs_interface(&pdev->dev);
	S32 status;
        status = mcbsp_init(&mcbsp_drv_t);
	return 0;
}

static struct platform_driver mcbsp_audio_driver = {
        .probe          = mcbsp_audio_probe,
        .remove         = mcbsp_audio_remove,
        .driver         = {
                .name   = "mcbsp-audio",
                .owner  = THIS_MODULE,
        },
};



static int __init mcbsp_audio_init(void)
{
	printk(KERN_INFO "mcbsp-audio driver initializing\n");
        return platform_driver_register(&mcbsp_audio_driver);
}

static void __exit mcbsp_audio_exit(void)
{
	 platform_driver_unregister(&mcbsp_audio_driver);
 
}

module_init(mcbsp_audio_init);
module_exit(mcbsp_audio_exit);

MODULE_ALIAS("mcbsp-audio");


/*
 * mcbsp_utils.c: McBSP device related routines
 */

#include <asm/io.h>
#include "types.h"
#include "omap3530evm.h"
#include "mcbsp_utils.h"
#include <linux/delay.h>

/* Enables McBSP clocks. */
void enable_mcbsp_clks(void)
{
	volatile U32 *preg1;

	/* 96 Mhz selected. */
	preg1 = (volatile U32 *)CM_CLKSEL1_PLL;
	*preg1 &= 0xFFFFFFF7;

        /* Enable the functional clock of McBSP5. */
        preg1 = (volatile U32 *)CM_FCLKEN1_CORE;
        *preg1 |= 0x00000400;

        /* Enable the Interface clock of McBSP5. */
	preg1 = (volatile U32 *)CM_ICLKEN1_CORE;
        *preg1 |= 0x00000400;

	 /* 
         ** Configure the CLKS input to 96M functional clk from PRCM 
         ** and not MCBSP.CLKS pin.
         ** CONTROL_DEVCONF0 ,set bit 5 to zero.
         */
    	 *(CONTROL_DEVCONF1) &= 0xFFFFFFEF;
		
}

/* Configure McBSP. */
U32 config_mcbsp(U32 mcbsp_base_addr, U8 master, U8 clksr_sel, U32 clksr_val, U8 num_of_phases, U8 num_words_phase1, U8 num_words_phase2, U8 word_size_phase1, U8 word_size_phase2, U8 mode)
{
	enable_mcbsp_clks();

	/* Make all registers to zero to start with. */
	out_regl((mcbsp_base_addr + MCBSP_SPCR1_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_SPCR2_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_PCR0_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_XCR1_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_RCR1_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_XCR2_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_RCR2_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_SRGR1_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_SRGR2_OFFSET), 0);

	/* Disable TX and RX DMA. */
	out_regl((mcbsp_base_addr + MCBSP_XCCR_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_RCCR_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_THRSH2_OFFSET), 0);
	out_regl((mcbsp_base_addr + MCBSP_THRSH1_OFFSET), 0);

	//Global bevavior//
	/*
	** Codec is slave McBSP is master
	** Enable the following bits FSXM, CLKXM, CLKXP.
	*/
	out_regl(mcbsp_base_addr + MCBSP_PCR0_OFFSET, 0x0A02);   
	
	//Data behavior//
	/* Tx word len1= 16bits, 1 word per frame. */
	out_regl(mcbsp_base_addr + MCBSP_XCR1_OFFSET, in_regl(mcbsp_base_addr +  MCBSP_XCR1_OFFSET) | 0x40);
	/*
	** Dual phase frame
	** Tx word len2 = 16 bits, 1 word per frame.
	** No companding data starts with MSB first
	** Transmit frame-synchronization pulses after the first are ignored.
	** Tx Data delay - 1bit.
	*/
	*((volatile U32 *)(mcbsp_base_addr + MCBSP_XCR2_OFFSET)) |= 0x8041;

	//Frame-sync behavior//
	/* Next frame-sync signal becomes active after 32 clock periods. */
	*((volatile U32 *)(mcbsp_base_addr + MCBSP_SRGR2_OFFSET)) |= ((0x2000 | 0x1000 | (BITSPERSAMPLE * 2)) - 1);

	//Clock Behaviour//
	*((volatile U32 *)(mcbsp_base_addr + MCBSP_SRGR1_OFFSET)) = ((BITSPERSAMPLE - 1) << 8) | 69; // sampling rate 44.1 kHz

	/* Enable the sample rate generator. */
	*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) |= 0x00C0;

	/* Enable the Tx */
	*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) |= 1;

	udelay(ONE_MILLISEC);

	return 0;
}

/* Reset the McBSP. */
void reset_mcbsp(U32 mcbsp_base_addr, U32 enable)
{
	/*
	 ** Checking whether McBSP is providing the clock (Word and bit CLK)
	 ** or McBSP is master.
	 */
	if (((*(volatile U32 *)(mcbsp_base_addr + MCBSP_PCR0_OFFSET)) & 0x0F00)
	    != 0x0F00) {
		/*
		 ** If McBSP is not master, then TX/RX/SCG are not enabled 
		 ** and disabled between each call to the macbsp_write/read functions. 
		 */
		return;
	} else {
		*((volatile U32 *)(mcbsp_base_addr + MCBSP_DXR_OFFSET)) = 0x0;
		if (enable) {
			/* The serial port transmitter is enabled. */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) |= 0x0001;

			/*
			 ** Sample rate generator is pulled out of reset.
			 ** Frame counters are loaded with their programmed values.
			 */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) |= 0x00C0; 
		} else {
			/*
			 ** The serial port transmitter is disabled and in reset state.
			 ** Frame-synchronization logic is reset.
			 */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET))
			    &= ~(0x00C1);

			/* The serial port receiver is disabled and in reset state. */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR1_OFFSET))
			    &= ~(0x0001);
		}
	}
}

/* Writes TX data register by polling method. */
S8 write_data_mcbsp(U32 mcbsp_base_addr, U16 * write_buffer)
{
	

	U32 ret_status = SUCCESS;
	U32 attempts;


	*((volatile U32 *)(mcbsp_base_addr + MCBSP_DXR_OFFSET)) = *write_buffer;

	/* If frame sync error - clear the error. */
	if (*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) & 0x08) {
		/* Clear error. */
		*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) &= ~(0x08);
	}

	/* Wait for transmit confirmation. */
	attempts = 0;
	while ((*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) & 0x2)
	       != 0x2) {
		if (attempts++ > 1000) {
			/* The attempt failed, so reset the Tx. */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) &= ~(0x1);

			/* Enable Tx. */
			*((volatile U32 *)(mcbsp_base_addr + MCBSP_SPCR2_OFFSET)) |= 0x1;
			printk("%s:%d - McBSP write failed.\r\n", __FILE__, __LINE__);
			return FAILURE;
		}
	}

	return ret_status;
}




/*
 * mcbsp_utils.h: McBSP interface related definitions and function prototypes.
 */

#ifndef __MS_DIAG_MCBSP_UTILS_H__
#define __MS_DIAG_MCBSP_UTILS_H__

#ifdef __cplusplus
extern "C" {
#endif

#define MCBSP_5     5
#define MCBSP_1     1
#define MCBSP_2     2


#define MCBSP_MASTER    1
#define MCBSP_SLAVE     0

#define MCBSP_SAMPLINGRATE_SEL      0
#define MCBSP_BITCLK_SEL            1

#define MCBSP_I2S           0
#define MCBSP_TDM           1
#define MCBSP_CLOCK_STOP    2

/*
** Pin Mux registers.
*/

/* ball N21. */
#define CTRL_PADCONF_MCBSP2_CLKX        (0x4800213E)
/* ball P21. */
#define CTRL_PADCONF_MCBSP2_FSX         (0x4800213C)
/* ball R21. */
#define CTRL_PADCONF_MCBSP2_DR          (0x48002140)
/* ball M21. */
#define CTRL_PADCONF_MCBSP2_DX          (0x48002142)

#define ARM_MCBSP1_BASE_ADDR            (0x48074000)
#define ARM_MCBSP2_BASE_ADDR            (0x49022000)
#define ARM_MCBSP5_BASE_ADDR            (0xfa096000)

/* McBSP Register Offsets. */
#define MCBSP_DRR_OFFSET            0x00
#define MCBSP_DXR_OFFSET            0x08
#define MCBSP_SPCR2_OFFSET          0x10
#define MCBSP_SPCR1_OFFSET          0x14
#define MCBSP_RCR2_OFFSET           0x18
#define MCBSP_RCR1_OFFSET           0x1C
#define MCBSP_XCR2_OFFSET           0x20
#define MCBSP_XCR1_OFFSET           0x24
#define MCBSP_SRGR2_OFFSET          0x28
#define MCBSP_SRGR1_OFFSET          0x2C
#define MCBSP_MCR2_OFFSET           0x30
#define MCBSP_MCR1_OFFSET           0x34
#define MCBSP_RCERA_OFFSET          0x38
#define MCBSP_RCERB_OFFSET          0x3C
#define MCBSP_XCERA_OFFSET          0x40
#define MCBSP_XCERB_OFFSET          0x44
#define MCBSP_PCR0_OFFSET           0x48

#define MCBSP_REV_OFFSET            0x7C
#define MCBSP_RINTCLR_OFFSET        0x80
#define MCBSP_XINTCLR_OFFSET        0x84
#define MCBSP_ROVFLCLR_OFFSET       0x88
#define MCBSP_SYSCONFIG_OFFSET      0x8C
#define MCBSP_THRSH2_OFFSET         0x90
#define MCBSP_THRSH1_OFFSET         0x94
#define MCBSP_IRQSTATUS_OFFSET      0xA0

#define MCBSP_IRQENABLE_OFFSET      0xA4
#define MCBSP_WAKEUPEN_OFFSET       0xA8
#define MCBSP_XCCR_OFFSET           0xAC
#define MCBSP_RCCR_OFFSET           0xB0
#define MCBSP_XBUFFSTAT_OFFSET      0xB4
#define MCBSP_RBUFFSTAT_OFFSET      0xB8

#define BITSPERSAMPLE   16


/* Function Prototypes. */
void enable_mcbsp_clks (void);
U32 config_mcbsp (U32 mcbsp_base_addr, U8 master, U8 clksr_sel, U32 clksr_val,
                 U8 num_of_phases, U8 num_words_phase1, U8 num_words_phase2,
                 U8 word_size_phase1, U8 word_size_phase2, U8 mode);
void reset_mcbsp (U32 mcbsp_base_addr, U32 enable);
S32 read_data_mcbsp (U32 mcbsp_base_addr, U16* read_buffer);
S8 write_data_mcbsp (U32 mcbsp_base_addr, U16* write_buffer);


#ifdef __cplusplus
}
#endif

#endif  /* __MS_DIAG_MCBSP_UTILS_H__ */



// mcbsp5 related code in arch/arm/mach-omap2/board-overo.c

static struct platform_device mcbsp_audio_device = {
        .name           = "mcbsp-audio",
        .id             = 1,
};

static struct platform_device *overo_devices[] __initdata = {
	&overo_dss_device,
	&mcbsp_audio_device,
};


//#ifdef CONFIG_OMAP_MUX
static struct omap_board_mux board_mux[] __initdata = {
//	OMAP3_MUX(UART2_RTS, OMAP_MUX_MODE2 | OMAP_PIN_OUTPUT),
	         /* MCBSP5 config */
        OMAP3_MUX(ETK_CLK, OMAP_MUX_MODE1 | OMAP_PIN_OUTPUT),
        OMAP3_MUX(ETK_D5, OMAP_MUX_MODE1 | OMAP_PIN_OUTPUT),
        OMAP3_MUX(ETK_D4, OMAP_MUX_MODE1 | OMAP_PIN_INPUT_PULLDOWN),
        OMAP3_MUX(ETK_D6, OMAP_MUX_MODE1 | OMAP_PIN_OUTPUT),

	{ .reg_offset = OMAP_MUX_TERMINATOR },
};
//#else
//#define board_mux	NULL
//#endif

static void __init overo_init(void)
{
	omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
	overo_i2c_init();
	platform_add_devices(overo_devices, ARRAY_SIZE(overo_devices));
	omap_serial_init();
	overo_flash_init();
	usb_musb_init();
	usb_ehci_init(&ehci_pdata);
	overo_spi_init();
	overo_init_smsc911x();
	overo_display_init();

	/* Ensure SDRC pins are mux'd for self-refresh */
	omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);
	omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT);

	omap_mux_init_signal("gpt10_pwm_evt", OMAP_PIN_OUTPUT); 

	if ((gpio_request(OVERO_GPIO_W2W_NRESET,
			  "OVERO_GPIO_W2W_NRESET") == 0) &&
	    (gpio_direction_output(OVERO_GPIO_W2W_NRESET, 1) == 0)) {
		gpio_export(OVERO_GPIO_W2W_NRESET, 0);
		gpio_set_value(OVERO_GPIO_W2W_NRESET, 0);
		udelay(10);
		gpio_set_value(OVERO_GPIO_W2W_NRESET, 1);
	} else {
		printk(KERN_ERR "could not obtain gpio for "
					"OVERO_GPIO_W2W_NRESET\n");
	}

	if ((gpio_request(OVERO_GPIO_BT_XGATE, "OVERO_GPIO_BT_XGATE") == 0) &&
	    (gpio_direction_output(OVERO_GPIO_BT_XGATE, 0) == 0))
		gpio_export(OVERO_GPIO_BT_XGATE, 0);
	else
		printk(KERN_ERR "could not obtain gpio for OVERO_GPIO_BT_XGATE\n");

	if ((gpio_request(OVERO_GPIO_BT_NRESET, "OVERO_GPIO_BT_NRESET") == 0) &&
	    (gpio_direction_output(OVERO_GPIO_BT_NRESET, 1) == 0)) {
		gpio_export(OVERO_GPIO_BT_NRESET, 0);
		gpio_set_value(OVERO_GPIO_BT_NRESET, 0);
		mdelay(6);
		gpio_set_value(OVERO_GPIO_BT_NRESET, 1);
	} else {
		printk(KERN_ERR "could not obtain gpio for "
					"OVERO_GPIO_BT_NRESET\n");
	}

	if ((gpio_request(OVERO_GPIO_USBH_CPEN, "OVERO_GPIO_USBH_CPEN") == 0) &&
	    (gpio_direction_output(OVERO_GPIO_USBH_CPEN, 1) == 0))
		gpio_export(OVERO_GPIO_USBH_CPEN, 0);
	else
		printk(KERN_ERR "could not obtain gpio for "
					"OVERO_GPIO_USBH_CPEN\n");
}