Actually I've made some progress with both Joshua's code and the
existing 355 spi codebase.
Joshua - I use your code to make quick changes to test out settings that
I need for my final driver, but I will most likely rewrite the eeprom
driver to do what I want. There are some places where you put while
loops that never terminate, and I've been caught with a hung driver a
few times. An example is if you supply an even write count - the driver
spins waiting for an incoming value, but a command was never
initiated.... But it does work, and thats what I need at the moment!
As for the existing distribution - the
linux/drivers/mtd/devices/at25xxA_eeprom.c is a good place to start to
write your own device driver.
Steve
Joshua Hintze wrote:
> Hi BJ,
>
> Did you see the code that I wrote at while ago from the DM6446. It was a
> complete module with instructions on how to insert it and connect up the
> correct device nodes, etc. Now I know your using the DM3555 but I have been
> talking to Steve and he said that it should work and I think he got it all
> going except for the read function. Anyways I copied and pasted my old
> message I sent out originally.
>
> I was in your exact same place 3 months ago. Also this was my very FIRST
> linux module I had ever written so it probably shows.
>
> Josh
>
> ----------------------------------------
> Hey guys,
>
> I promised I would post this for people who are faced with the same problem
> as I have, no spi driver support for the davinci.
>
> After some help from people on the email list, I wrote my own character spi
> driver. Please bear with me since this is the FIRST linux driver I have ever
> written. Its also hard coded to certain SPI clock polarity, phase,
> setup/hold times, etc.
>
> There are many possible advances that could be made so that you can
> configure it through IOCTL commands, but my work isn't paying me to do that.
> At this point I am diverging from this SPI driver and will start adding GPIO
> interrupts to this driver so I know when to pull the device connected to the
> SPI port.
>
> To use this driver you will have to:
>
> Copy all the code in this email into a text file called dv_spi.c
> Copy the simple Makefile into a text file
> Define the LINUXKERNEL_INSTALL_DIR path in your environment
> Run make with with the Makefile and dv_spi.c
> Now that you have the dv_spi.ko module copy it to the davinci
> Create a spi device node in the /dev folder using this command "mknod -m 666
> /dev/spi c 60 0"
> Note if don't want to type step 6 on every boot up of the davinci you need
> to either a.) make a udev rule or easier b.) create a startup script in
> /etc/rc.d/init.d/ and ln -s link it into the rc3.d folder
> Now you need to load the dv_spi.ko kernel module. Use "insmod dv_spi.ko"
> To use this you just open up the /dev/spi as a file in C code and read/write
> or fread/fwrite to it. Remember that spi is full duplex so when you write
> the driver will read data as well and store it internal to the driver and
> buffer it until the next read.
>
> You can also echo "blah" > /dev/spi and it works as well.
>
> Ok here is the Make file
>
> ----------------- Makefile start ----------------------------------
>
> obj-m += dv_spi.o
>
> MVTOOL_PREFIX=arm_v5t_le-
>
> MAKE_ENV = ARCH=arm CROSS_COMPILE=$(MVTOOL_PREFIX)
>
> .PHONY: clean release default
>
> default: release
>
> release:
> make -C $(LINUXKERNEL_INSTALL_DIR) M=`pwd` $(MAKE_ENV) modules
>
> clean:
> -rm -rf *.o *mod* *.ko .cmem* .tmp*
>
> ---------Makefile end-----------------------------
>
>
>
>
> -------- dv_spi.c start --------------------------
> //
> --------------------------------------------------------------------------
> /*
> File: dv_spi.c
>
> Author: Joshua Hintze
> E-Mail: [EMAIL PROTECTED]
>
> Description: A very simple implementation for using the SPI
> port on the Davinci 6446 platform. This is my first linux driver ever
> so comments are appreciated.
>
> Thanks goes to Sean on Davinci Mailing List
>
> Limitations: Currently this is written to only use a single Chip Select
> /SPI_ENO
> the reason being that the
> /SPI_EN1 is multiplexed with the ATA HDDIR lines.
>
> Platform Dependencies: Davinci
>
> Change History:
> Date Author Description
> */
> //
> --------------------------------------------------------------------------
>
> ///////////////////////////
> // INCLUDES
> //////////////////////////
> #include <linux/init.h>
> #include <linux/module.h>
> #include <linux/kernel.h>
> #include <linux/delay.h> // udelay()
> #include <linux/fs.h> // everything...
> #include <asm/uaccess.h> // copy_from/to_user
>
> // Definition for SPI Base Address and the local Power or Sleep Controller
> LPSoC
> #include <asm/arch/hardware.h>
> #include <asm/hardware/clock.h>
>
> ///////////////////////////
> // PROTOTYPES
> //////////////////////////
> /* Declaration of dv_spi.c functions */
> int dv_spi_open(struct inode *inode, struct file *filp);
> int dv_spi_release(struct inode *inode, struct file *filp);
> ssize_t dv_spi_read(struct file *filp, char *buf, size_t count, loff_t
> *f_pos);
> ssize_t dv_spi_write(struct file *filp, const char *buf, size_t count,
> loff_t *f_pos);
> static void dv_spi_exit(void);
> static int dv_spi_init(void);
>
> ///////////////////////////
> // DEFINITIONS & GLOBALS
> //////////////////////////
> // Register definitions to control the SPI
> #define SPIGCR0 0x01c66800
> #define SPIGCR1 0x01c66804
> #define SPIINT 0x01c66808
> #define SPILVL 0x01c6680c
> #define SPIFLG 0x01C66810
> // SPI Flag Status Register
> #define SPIPC0 0x01C66814
> // SPI Pin Control Register 0
> #define SPIPC2 0x01C6681C
> // SPI Pin Control Register 2
> #define SPIDAT1 0x01C6683C
> // SPI Shift Register 1
> #define SPIBUF 0x01C66840
> // SPI Buffer Register
> #define SPIEMU 0x01C66844
> // SPI Emulation Register
> #define SPIDELAY 0x01C66848
> // SPI Delay Register
> #define SPIDEF 0x01C6684C
> // SPI Default Chip Select Register
> #define SPIFMT0 0x01C66850
> // SPI Data Format Register 0
> #define SPIFMT1 0x01C66854
> // SPI Data Format Register 1
> #define SPIFMT2 0x01C66858
> // SPI Data Format Register 2
> #define SPIFMT3 0x01C6685C
> // SPI Data Format Register 3
> #define INTVEC0 0x01C66860
> // SPI Interrupt Vector Register 0
> #define INTVEC1 0x01C66864
> // SPI Interrupt Vector Register 1
>
> // Definition for GPIO Pin Multiplexing
> #define PINMUX1 0x01c40004
>
> // SPI format - Polarity and Phase
> // ***NOTE*** It doesn't seem like the SPI Phase for the davinci follows the
> standard
> // phase as described by the motorola architecture. I.E. phase 0 = sample on
> rising edge of clock
> // In the davinci it seems this is opposite.
> #define SPI_PHASE 1
> // Set these values to whatever you need
> #define SPI_POLARITY 0
>
> // Macro for accessing a memory location such as a register
> #define SPI_REG(reg) (*(int *__iomem) IO_ADDRESS(reg))
>
> // Version numbers
> #define MAJOR_VERSION 60
> #define MINOR_VERSION 01
>
> // Global pointer to the clock struct. We use this to start up the LPSoC
> (local power system on chip)
> // so our SPI peripheral has power going to it.
> static struct clk *g_clkptr = 0;
>
> // Structure that declares the usual file access functions
> static struct file_operations dv_spi_fops = {
> read: dv_spi_read,
> write: dv_spi_write,
> open: dv_spi_open,
> release: dv_spi_release
> };
>
> // We will use a 1K read buffer to store data
> static unsigned char *g_readbuf = 0;
> static unsigned int g_readbufcount = 0;
>
>
> static int dv_spi_init(void)
> {
> int result;
>
> /* Registering device */
> result = register_chrdev(MAJOR_VERSION, "spi",
> &dv_spi_fops);
> if (result < 0)
> {
> printk("<1>dv_spi: cannot obtain major
> number %d\n", MAJOR_VERSION);
> return result;
> }
>
> // Allocate space for the read buffer
> g_readbuf = kmalloc(1024, GFP_KERNEL);
> if (!g_readbuf)
> {
> result = -ENOMEM;
> dv_spi_exit();
> return result;
> }
>
> printk("<1>Inserting SPI module\n");
> return 0;
> }
>
> static void dv_spi_exit(void)
> {
> /* Freeing the major number */
> unregister_chrdev(MAJOR_VERSION, "spi");
>
> /* Freeing buffer memory */
> if(g_readbuf)
> kfree(g_readbuf);
>
> if (g_clkptr)
> dv_spi_release(0,0);
>
> printk("<1>Removing SPI module\n");
> }
>
>
> // Called when a userspace program opens the file
> int dv_spi_open(struct inode *inode, struct file *filp)
> {
> unsigned int control;
>
> // Power up the SPI hardware by requesting and enabling the
> clock
> g_clkptr = clk_get(NULL, "SPICLK");
> if(g_clkptr <= 0) printk("<l>Error could not get the
> clock\n");
> else clk_enable(g_clkptr);
>
> // --------------------------------
> // Configure GPIO Pins for SPI
> // --------------------------------
> // Enable the SPI pins on the GPIO
> SPI_REG(PINMUX1) |= 0x100;
>
> // --------------------------------
> // Reset SPI
> // --------------------------------
> control=0x00000000;
> SPI_REG(SPIGCR0)=control;
> // Place SPI peripheral in reset
> mdelay(1);
> // Delay for a bit
> control=0x00000001;
> SPI_REG(SPIGCR0)=control;
> // Remove from reset
>
> // --------------------------------
> // Enable SPI CLK & Master
> // --------------------------------
> control=0x00000003;
> SPI_REG(SPIGCR1)=control;
>
> // --------------------------------
> // Enable pins : DI,DO,CLK,EN0
> // --------------------------------
> control=0x00000E01;
> SPI_REG(SPIPC0)=control;
>
> /* --------------------------------
> // Set data format in SPIFMT0 - THIS CAN BE CHANGED BY IOCTL
> commands
> // SHIFTDIR in bit 20 set to 1 : MSB first
> // POLARITY and PHASE in bit 17, 16 set to 0, 0
> // PRESCALE in bit 15-8 set to whatever you need, SPI_CLK =
> SYSCLK5 / (Prescale + 1)
> // CHARLEN in bit 4-0 set to 08 : 8 bit characters
> -------------------------------- */
> control=0x00000000 | (SPI_POLARITY << 17) | (SPI_PHASE <<
> 16) | (0x7 << 8) | 0x8;
> SPI_REG(SPIFMT0)=control;
>
> // --------------------------------
> // Set data format for used -> SPIFMT0
> // --------------------------------
> control=0x00000000 | (0x00 << 24);
> SPI_REG(SPIDAT1)=control;
>
>
> // --------------------------------
> // Set hold time and setup time
> // --------------------------------
> control=0x00000000 | (0x03 << 16) | (0x06 << 24);
> SPI_REG(SPIDELAY)=control;
>
>
> // --------------------------------
> // Set Chip Select Default
> // CSHOLD -> 0 -> release SPI_EN0 state after transmission
> -> bit 28
> // CSNR -> 2 -> Only use SPI_EN0
> // --------------------------------
> control=SPI_REG(SPIDAT1);
> control|= (0x2 << 16);
> SPI_REG(SPIDAT1)=control;
>
> // --------------------------------
> // Enable for transmitting
> // --------------------------------
> control=SPI_REG(SPIGCR1);
> control=control | 1 << 24; // enable SPIENA
> SPI_REG(SPIGCR1)=control;
>
> // Zero out our read buffer
> memset(g_readbuf, 0, 1024);
> g_readbufcount = 0;
>
> return 0;
> }
>
>
> // Called when a userspace program closes the file
> int dv_spi_release(struct inode *inode, struct file *filp)
> {
> // Place SPI peripheral into reset
> SPI_REG(SPIGCR0)=0;
>
> // Remove the SPI output on the GPIO
> SPI_REG(PINMUX1) &= ~0x100;
>
> // Disable the clock thus removing power from the peripheral
> if(g_clkptr)
> clk_disable(g_clkptr);
> g_clkptr = 0;
>
> return 0;
> }
>
>
> // Reading from the SPI device
> ssize_t dv_spi_read(struct file *filp, char *buf, size_t count, loff_t
> *f_pos)
> {
> // printk("<1>Attempting to read %d bytes and we only have %d
> bytes\n",count,g_readbufcount);
>
> if(g_readbufcount == 0) return 0; // No data
>
>
> // See if there is enough available
> if(count > g_readbufcount) count = g_readbufcount;
>
> // Transferring data to user space
> copy_to_user(buf,g_readbuf,count);
>
> // Now shift the memory down
>
> memmove(&g_readbuf[0],&g_readbuf[count],g_readbufcount-count);
> g_readbufcount -= count;
>
> // Advance the file pointer
> *f_pos += count;
> return count;
> }
>
> // Writing to the SPI device
> ssize_t dv_spi_write(struct file *filp, const char *buf, size_t count,
> loff_t *f_pos)
> {
> unsigned char spiData;
> size_t i;
> unsigned int control;
> unsigned int ReadByte;
>
> // Wait until the TX buffer is clear
> control = SPI_REG(SPIBUF);
> while(control & (1 << 29)) //
> Wait until the TX data has been transmitted
> control = SPI_REG(SPIBUF);
>
> // Write out data one byte at a time
> for(i=0; i < count; i++)
> {
> ReadByte = 0;
> // Send the data
> copy_from_user(&spiData,buf+i,1);
>
> control = 0x00000000 | (0x1 << 28) | (0x2 <<
> 16) | spiData; // Hold the chip select line between multibytes
> SPI_REG(SPIDAT1) = control;
> if(i == (count -1))
> SPI_REG(SPIDAT1) =
> 0x00000000 | (0x2 << 16) | spiData; // Remove the chip select hold
>
>
> control = SPI_REG(SPIBUF);
> while(control & (1 << 29))
> // Wait until the TX data has been transmitted
> {
> // Check for data received
> if(!(control & (1 << 31)))
> {
> // We have
> just read a byte of data, store it.
>
> if(g_readbufcount < 1024)
> {
> //
> printk("<1>Read in byte value = %d!\n",count);
>
> g_readbuf[g_readbufcount] = control & 0xFF;
>
> g_readbufcount++;
>
> ReadByte = 1;
> }
> }
> control = SPI_REG(SPIBUF);
> }
>
> // Make sure we have always read in a byte
> while(!ReadByte)
> {
> // Check for data received
> if(!(control & (1 << 31)))
> {
> //
> printk("<1>we received a byte with value %d\n",control & 0xFF);
> // We have
> just read a byte of data, store it.
>
> if(g_readbufcount < 1024)
> {
> //
> printk("<1>Read in byte value = %d\n",control);
>
> g_readbuf[g_readbufcount] = control & 0xFF;
>
> g_readbufcount++;
>
> ReadByte = 1;
> }
> }
> control = SPI_REG(SPIBUF);
> }
> }
>
> return count;
> }
>
>
> MODULE_LICENSE("Dual BSD/GPL");
> module_init(dv_spi_init);
> module_exit(dv_spi_exit);
>
> -------- dv_spi.c end --------------------------
>
>
>
>
>
> -----Original Message-----
> From: [EMAIL PROTECTED]
> [mailto:[EMAIL PROTECTED]
> om] On Behalf Of BJ Opp
> Sent: Thursday, March 06, 2008 12:07 PM
> Cc: [email protected]
> Subject: RE: DM355 SPI
>
> To take Stephen's question a bit further. I'm trying to write a library
> that uses the spi driver on the dm355. I was under the assumption that I'd
> be able to access it much like the i2c is used, namely that there was a
> /dev/spi node that I could open. I'm pretty sure that I have to write my
> own driver using the functions available in dm355_spi_master but I'm sort of
> at a loss how I can begin to do so. I have a small kernel module written
> that I can insert and remove into the kernel at runtime but the details of
> how to actually get it to use the spi bus are escaping me.
>
> Has anybody written their own driver/library that uses the dm355 spi bus?
> If so, could you point me in the right direction toward gettign mine up and
> running. I looked at the code for the at25xxfl_eeprom, but that's more of
> an mtd and I would think that my device (a controller for an optrex lcd)
> would fit more as a character device since the read/writes don't operate in
> block sizes. As you can see I'm a bit confused here and would really
> appreciate some help.
>
> Thanks
> BJ
>
> Viet Hoang wrote:
>
>> Hi Steve,
>> Boot the board, login, then use this command: cat /dev/mtdblock5.
>> Put the scope at SCK(pin 6) , SI (pin 5) or SO (pin 2) (That EEPROM is
>> U65), you can see the signal.
>> Does this help?
>> Regards,
>> Viet Hoang.
>>
>> */Stephen Berry <[EMAIL PROTECTED]>/* wrote:
>>
>>
>> Does anyone have any experience in getting the SPI interface
>> running on the 355?
>>
>> It looks to me that the SPI framework as shipped with the 355
>> doesn't work. To prove this I put a scope on the SPI eeprom on the
>> EVM board - and the part isn't used during boot. I have made an
>> assumption that the onboard eeprom was there for things like MAC
>> addresses and other important numbers...
>>
>> My driver does the same thing on my hardware - no hardware
>> response when I issue spi transactions (the software returns zeros).
>>
>> I do have Joshua Hintze's SPI code - but I would rather have my
>> driver fit into an existing, working framework (if it exists).
>>
>> Does anyone have any patches for the original MVL 2.6.10 SPI code
>> (1.3beta) - or anything else that may help?
>>
>> Thanks,
>>
>> Steve Berry
>> ID2
>>
>> _______________________________________________
>> Davinci-linux-open-source mailing list
>> [email protected]
>> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
>>
>>
>> Send instant messages to your online friends
>> http://uk.messenger.yahoo.com
>>
>>
>
>
>
_______________________________________________
Davinci-linux-open-source mailing list
[email protected]
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source