Написал я вышеупомянутый драйвер под ядро Linux 2.4.19-rm7-pxa1, поскольку готового не нашел. Сильно много пока не тестировал, но вроде работает. Поскольку код работы с DMA взял из \drivers\sound\pxa-audio.c, то не все понял. В частности закомментировал в функции ssp_read(struct file *file, char *buffer, size_t count, loff_t * ppos):
Цитата
/* if (ppos != &file->f_pos)
return -ESPIPE;*/ /*BUG Ya ne ponyal na figa?*/
Также не понял, зачем в функции ssp_sync надо было делать #if 0?
Предлагаю покритиковать. Заодно посоветуйте, на каких форумах (зарубежных и наших) обычно подобные вещи обсуждаются?
файл pxa2xx_ssp.c
Цитата
/*
* Copyright © 2006 Yevgeny Romanov
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h> /*??*/
#include <asm/uaccess.h> /*??*/
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include "pxa2xx_ssp.h"
#define NSSCR0 __REG(0x41400000) /* NSSP Control Register 0 */
#define NSSCR1 __REG(0x41400004) /* NSSP Control Register 1 */
#define NSSSR __REG(0x41400008) /* SSP Status Register */
#define NSSITR __REG(0x4140000C) /* SSP Interrupt Test Register */
#define NSSDR __REG(0x41400010) /* (Write / Read) SSP Data Write Register/SSP Data Read Register */
#define NSSTO __REG(0x41400028) /* */
#define NSSPSP __REG(0x4140002c) /* */
#define DEVICE_NAME "ssp"
#define MAJOR_NUM 100
#define SUCCESS 0
#define CKEN9_NSSP (1<<9) /*NSSP CLK enable bit*/
/*NSSCRO bits definition*/
#define SPI_FRAME 0
#define TI_FRAME 0x10
#define MICROWIRE_FRAME 0x20
#define ENA_BIT 0x80
#define N_ENA_BIT 0xffffff7f
#define EDS 0x00100000 /*data size >16 bit */
#define DATA_SIZE 0xf
#define FRAME_FORMAT TI_FRAME
#define CLOCK_RATE 0
/*NSSCR1 bits definition*/
#define SCLK_SLAVE 0x02000000
#define SCLK_MASTER 0x0
#define FRM_SLAVE 0x01000000
#define FRM_MASTER 0x0
#define RWOT 0x00800000
#define TSRE 0x00200000
#define RSRE 0x00100000
#define SCLKDIR SCLK_MASTER
#define SFRMDIR FRM_MASTER
#define RFT_VAL (4-1)
#define TFT_VAL (12-1)
/*SSSR definitions*/
#define SSP_RNE ((NSSSR&0x08)!=0)
#define SSP_TNF ((NSSSR&0x04)!=0)
/*debug definitions*/
#define DEBUG
#define KPARAM_DBG KERN_INFO
#define SSP_NBFRAGS_DEFAULT 8
#define SSP_FRAGSIZE_DEFAULT 8192
#define MAX_DMA_SIZE 4096
#define DMA_DESC_SIZE sizeof(pxa_dma_desc)
static ssp_stream_t *output_stream=NULL;
static ssp_stream_t *input_stream=NULL;
/*
* This function frees all buffers
*/
#define ssp_clear_buf pxa_ssp_clear_buf
void pxa_ssp_clear_buf(ssp_stream_t * s)
{
DECLARE_WAITQUEUE(wait, current);
int frag;
if (!s->buffers)
return;
/* Ensure DMA isn't running */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&s->stop_wq, &wait);
DCSR(s->dma_ch) = DCSR_STOPIRQEN;
schedule();
remove_wait_queue(&s->stop_wq, &wait);
/* free DMA buffers */
for (frag = 0; frag < s->nbfrags; frag++) {
ssp_buf_t *b = &s->buffers[frag];
if (!b->master)
continue;
consistent_free(b->data, b->master, b->dma_desc->dsadr);
}
/* free descriptor ring */
if (s->buffers->dma_desc)
consistent_free(s->buffers->dma_desc,
s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE,
s->dma_desc_phys);
/* free buffer structure array */
kfree(s->buffers);
s->buffers = NULL;
}
/*
* This function allocates the DMA descriptor array and buffer data space
* according to the current number of fragments and fragment size.
*/
static int ssp_setup_buf(ssp_stream_t * s)
{
pxa_dma_desc *dma_desc;
dma_addr_t dma_desc_phys;
int nb_desc, frag, i, buf_size = 0;
char *dma_buf = NULL;
dma_addr_t dma_buf_phys = 0;
if (s->buffers)
return -EBUSY;
/* Our buffer structure array */
s->buffers = kmalloc(sizeof(ssp_buf_t) * s->nbfrags, GFP_KERNEL);
if (!s->buffers)
goto err;
memzero(s->buffers, sizeof(ssp_buf_t) * s->nbfrags);
/*
* Our DMA descriptor array:
* for Each fragment we have one checkpoint descriptor plus one
* descriptor per MAX_DMA_SIZE byte data blocks.
*/
nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags;
dma_desc = consistent_alloc(GFP_KERNEL,
nb_desc * DMA_DESC_SIZE,
&dma_desc_phys);
if (!dma_desc)
goto err;
s->descs_per_frag = nb_desc / s->nbfrags;
s->buffers->dma_desc = dma_desc;
s->dma_desc_phys = dma_desc_phys;
for (i = 0; i < nb_desc - 1; i++)
dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE;
dma_desc[i].ddadr = dma_desc_phys;
/* Our actual DMA buffers */
for (frag = 0; frag < s->nbfrags; frag++) {
ssp_buf_t *b = &s->buffers[frag];
/*
* Let's allocate non-cached memory for DMA buffers.
* We try to allocate all memory at once.
* If this fails (a common reason is memory fragmentation),
* then we'll try allocating smaller buffers.
*/
if (!buf_size) {
buf_size = (s->nbfrags - frag) * s->fragsize;
do {
dma_buf = consistent_alloc(GFP_KERNEL,
buf_size,
&dma_buf_phys);
if (!dma_buf)
buf_size -= s->fragsize;
} while (!dma_buf && buf_size);
if (!dma_buf)
goto err;
b->master = buf_size;
memzero(dma_buf, buf_size);
}
/*
* Set up our checkpoint descriptor. Since the count
* is always zero, we'll abuse the dsadr and dtadr fields
* just in case this one is picked up by the hardware
* while processing SOUND_DSP_GETPTR.
*/
dma_desc->dsadr = dma_buf_phys;
dma_desc->dtadr = dma_buf_phys;
dma_desc->dcmd = DCMD_ENDIRQEN;
if (s->output && !s->mapped)
dma_desc->ddadr |= DDADR_STOP;
b->dma_desc = dma_desc++;
/* set up the actual data descriptors */
for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) {
dma_desc[i].dsadr = (s->output) ?
(dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr;
dma_desc[i].dtadr = (s->output) ?
s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE);
dma_desc[i].dcmd = s->dcmd |
((s->fragsize < MAX_DMA_SIZE) ?
s->fragsize : MAX_DMA_SIZE);
}
dma_desc += i;
/* handle buffer pointers */
b->data = dma_buf;
dma_buf += s->fragsize;
dma_buf_phys += s->fragsize;
buf_size -= s->fragsize;
}
s->usr_frag = s->dma_frag = 0;
s->bytecount = 0;
s->fragcount = 0;
sema_init(&s->sem, (s->output) ? s->nbfrags : 0);
return 0;
err:
printk("pxa-audio: unable to allocate audio memory\n ");
ssp_clear_buf(s);
return -ENOMEM;
}
/*
* Our DMA interrupt handler
*/
static void dma_handler(int ch, void *data, struct pt_regs *regs)
{
ssp_stream_t *s = data;
u_int dcsr;
dcsr = DCSR(ch);
DCSR(ch) = dcsr & ~DCSR_STOPIRQEN;
if (!s->buffers) {
printk("SSP DMA: wow... received IRQ for channel %d but no buffer exists\n", ch);
return;
}
if (dcsr & DCSR_BUSERR){
printk("SSP DMA: bus error interrupt on channel %d\n", ch);
#ifdef DEBUG
printk(KPARAM_DBG "DDADR %x , DSADR %x DTADR %x DCMD %x \n", DDADR(ch), DSADR(ch), DTADR(ch), DCMD(ch));
#endif
}
if (dcsr & DCSR_ENDINTR) {
u_long cur_dma_desc;
u_int cur_dma_frag;
/*
* Find out which DMA desc is current. Note that DDADR
* points to the next desc, not the current one.
*/
cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE;
/*
* Let the compiler nicely optimize constant divisors into
* multiplications for the common cases which is much faster.
* Common cases: x = 1 + (1 << y) for y = [0..3]
*/
switch (s->descs_per_frag) {
case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break;
case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break;
case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break;
case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break;
default: cur_dma_frag =
cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE);
}
/* Account for possible wrap back of cur_dma_desc above */
if (cur_dma_frag >= s->nbfrags)
cur_dma_frag = s->nbfrags - 1;
while (s->dma_frag != cur_dma_frag) {
if (!s->mapped) {
/*
* This fragment is done - set the checkpoint
* descriptor to STOP until it is gets
* processed by the read or write function.
*/
s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP;
up(&s->sem);
}
if (++s->dma_frag >= s->nbfrags)
s->dma_frag = 0;
/* Accounting */
s->bytecount += s->fragsize;
s->fragcount++;
}
/* ... and for polling processes */
wake_up(&s->frag_wq);
}
if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE))
wake_up(&s->stop_wq);
}
static ssize_t ssp_write( struct file *file, const char *buffer, size_t count, loff_t *offset)
{
const char *buffer0 = buffer;
ssp_stream_t *s = output_stream;
int chunksize, ret = 0;
/* if (ppos != &file->f_pos)
return -ESPIPE;*/ /*BUG Ya ne ponyal na figa?*/
if (s->mapped)
return -ENXIO;
if (!s->buffers && ssp_setup_buf(s))
return -ENOMEM;
while (count > 0) {
ssp_buf_t *b = &s->buffers[s->usr_frag];
/* Grab a fragment */
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
if (down_trylock(&s->sem))
break;
} else {
ret = -ERESTARTSYS;
if (down_interruptible(&s->sem))
break;
}
/* Feed the current buffer */
chunksize = s->fragsize - b->offset;
if (chunksize > count)
chunksize = count;
if (copy_from_user(b->data + b->offset, buffer, chunksize)) {
up(&s->sem);
return -EFAULT;
}
b->offset += chunksize;
buffer += chunksize;
count -= chunksize;
if (b->offset < s->fragsize) {
up(&s->sem);
break;
}
/*
* Activate DMA on current buffer.
* We unlock this fragment's checkpoint descriptor and
* kick DMA if it is idle. Using checkpoint descriptors
* allows for control operations without the need for
* stopping the DMA channel if it is already running.
*/
b->offset = 0;
b->dma_desc->ddadr &= ~DDADR_STOP;
if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
DDADR(s->dma_ch) = b->dma_desc->ddadr;
DCSR(s->dma_ch) = DCSR_RUN;
}
/* move the index to the next fragment */
if (++s->usr_frag >= s->nbfrags)
s->usr_frag = 0;
}
if ((buffer - buffer0))
ret = buffer - buffer0;
return ret;
}
static int ssp_read(struct file *file, char *buffer,
size_t count, loff_t * ppos)
{
char *buffer0 = buffer;
ssp_stream_t *s = input_stream;
int chunksize, ret = 0;
/* if (ppos != &file->f_pos)
return -ESPIPE;*/ /*BUG Ya ne ponyal na figa?*/
if (s->mapped)
return -ENXIO;
if (!s->buffers && ssp_setup_buf(s))
return -ENOMEM;
while (count > 0) {
ssp_buf_t *b = &s->buffers[s->usr_frag];
/*
* kick DMA if it is idle. Using checkpoint descriptors
* allows for control operations without the need for
* stopping the DMA channel if it is already running.
*/
/* prime DMA */
if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
DDADR(s->dma_ch) =
s->buffers[s->dma_frag].dma_desc->ddadr;
DCSR(s->dma_ch) = DCSR_RUN;
}
/* Wait for a buffer to become full */
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
if (down_trylock(&s->sem))
break;
} else {
ret = -ERESTARTSYS;
if (down_interruptible(&s->sem))
break;
}
/* Grab data from current buffer */
chunksize = s->fragsize - b->offset;
if (chunksize > count)
chunksize = count;
if (copy_to_user(buffer, b->data + b->offset, chunksize)) {
up(&s->sem);
return -EFAULT;
}
b->offset += chunksize;
buffer += chunksize;
count -= chunksize;
if (b->offset < s->fragsize) {
up(&s->sem);
break;
}
/*
* Make this buffer available for DMA again.
* We unlock this fragment's checkpoint descriptor
*/
b->offset = 0;
b->dma_desc->ddadr &= ~DDADR_STOP;
/* move the index to the next fragment */
if (++s->usr_frag >= s->nbfrags)
s->usr_frag = 0;
}
if ((buffer - buffer0))
ret = buffer - buffer0;
return ret;
}
/* For FMODE_WRITE only*/
static int ssp_sync(struct file *file)
{
ssp_stream_t *s = output_stream;
ssp_buf_t *b;
pxa_dma_desc *final_desc;
u_long dcmd_save = 0;
DECLARE_WAITQUEUE(wait, current);
if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped)
return 0;
/*
* Send current buffer if it contains data. Be sure to send
* a full sample count.
*/
final_desc = NULL;
b = &s->buffers[s->usr_frag];
if (b->offset &= ~3) {
final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE];
b->offset &= (MAX_DMA_SIZE-1);
dcmd_save = final_desc->dcmd;
final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN;
final_desc->ddadr |= DDADR_STOP;
b->offset = 0;
b->dma_desc->ddadr &= ~DDADR_STOP;
if (DCSR(s->dma_ch) & DCSR_STOPSTATE) {
DDADR(s->dma_ch) = b->dma_desc->ddadr;
DCSR(s->dma_ch) = DCSR_RUN;
}
}
/* Wait for DMA to complete. */
set_current_state(TASK_INTERRUPTIBLE);
#if 0
/*
* The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set
* along wotj DCSR_RUN. Silicon bug?
*/
add_wait_queue(&s->stop_wq, &wait);
DCSR(s->dma_ch) |= DCSR_STOPIRQEN;
schedule();
#else
add_wait_queue(&s->frag_wq, &wait);
while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
#endif
set_current_state(TASK_RUNNING);
remove_wait_queue(&s->frag_wq, &wait);
/* Restore the descriptor chain. */
if (final_desc) {
final_desc->dcmd = dcmd_save;
final_desc->ddadr &= ~DDADR_STOP;
b->dma_desc->ddadr |= DDADR_STOP;
}
return 0;
}
/*
* While not used
*/
static int ssp_ioctl(struct inode *inode, struct file *file, unsigned int ioctl_num, unsigned long ioctl_param)
{
#ifdef DEBUG
printk(KPARAM_DBG "device_ioctl(%p,%p)\n", inode, file);
#endif
return 0;
}
/*
* Process try to open device file
*/
static int ssp_open(struct inode *inode, struct file *file)
{
int status = 0;
#ifdef DEBUG
printk(KPARAM_DBG "device_open(%p,%p)\n", inode, file);
#endif
/*
* Only one process allowed to open device to write and one to read
*/
if ((file->f_mode & FMODE_WRITE)&(output_stream!=NULL)){
printk(KERN_ERR " Device already open for write \n");
return -EBUSY;
}
if ((file->f_mode & FMODE_READ)&(input_stream!=NULL)){
printk(KERN_ERR " Device already open for read \n");
return -EBUSY;
}
MOD_INC_USE_COUNT;
if (file->f_mode & FMODE_WRITE){
output_stream = kmalloc(sizeof(ssp_stream_t),GFP_KERNEL);
if(!output_stream) {
printk(KERN_ERR " Cannot allocate output stream structure \n");
goto out_stream_err_allocate;
}
output_stream->buffers = NULL;
output_stream->fragsize = SSP_FRAGSIZE_DEFAULT;
output_stream->nbfrags = SSP_NBFRAGS_DEFAULT;
output_stream->mapped = 0;
output_stream->output = 1;
output_stream->dma_ch = -1;
output_stream->dev_addr = __PREG(NSSDR);
output_stream->dcmd = DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST8|DCMD_WIDTH4;
init_waitqueue_head(&output_stream->frag_wq);
init_waitqueue_head(&output_stream->stop_wq);
output_stream->dma_ch = pxa_request_dma("pxa2xx_ssp_tx",
DMA_PRIO_MEDIUM,
dma_handler,
output_stream);
if (output_stream->dma_ch < 0) {
printk(KERN_ERR "problem (%d) requesting tx channel\n",
output_stream->dma_ch);
status = -ENODEV;
goto out_error_dma_alloc;
}
/* Map DMA channals*/
DRCMR16 = output_stream->dma_ch|DRCMR_MAPVLD;
#ifdef DEBUG
printk(KPARAM_DBG "TX DMA channel=%d \n", output_stream->dma_ch);
#endif
}
if (file->f_mode & FMODE_READ){
input_stream = kmalloc(sizeof(ssp_stream_t),GFP_KERNEL);
if(!input_stream) {
printk(KERN_ERR " Cannot allocate input stream structure \n");
goto in_stream_err_allocate;
}
input_stream->buffers = NULL;
input_stream->fragsize = SSP_FRAGSIZE_DEFAULT;
input_stream->nbfrags = SSP_NBFRAGS_DEFAULT;
input_stream->mapped = 0;
input_stream->output = 0;
input_stream->dma_ch = -1;
input_stream->dev_addr = __PREG(NSSDR);
input_stream->dcmd = DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST8|DCMD_WIDTH4;
init_waitqueue_head(&input_stream->frag_wq);
init_waitqueue_head(&input_stream->stop_wq);
/* Get DMA channels rx */
input_stream->dma_ch = pxa_request_dma("pxa2xx_ssp_rx",
DMA_PRIO_HIGH,
dma_handler,
input_stream);
if (input_stream->dma_ch < 0) {
printk(KERN_ERR "problem (%d) requesting rx channel\n",
input_stream->dma_ch);
status = -ENODEV;
goto out_error_rx_dma_alloc;
}
/* Map DMA channals*/
DRCMR15 = input_stream->dma_ch|DRCMR_MAPVLD;
#ifdef DEBUG
printk(KPARAM_DBG "RX DMA channel=%d \n", input_stream->dma_ch);
#endif
}
NSSCR0 |= ENA_BIT;
#ifdef DEBUG
printk(KPARAM_DBG "NSSSR=%x \n", NSSSR );
#endif
return SUCCESS;
out_error_rx_dma_alloc:
if ((file->f_mode & FMODE_READ)&(input_stream->dma_ch >= 0)){
DRCMR15 &= ~DRCMR_MAPVLD; /*unmap DMA cannal*/
pxa_free_dma(input_stream->dma_ch);
}
if((file->f_mode & FMODE_READ)&(!input_stream)){
kfree(input_stream);
input_stream=NULL;
}
in_stream_err_allocate:
out_error_dma_alloc:
if ((file->f_mode & FMODE_WRITE)&(output_stream->dma_ch >= 0)){
DRCMR16 &= ~DRCMR_MAPVLD; /*unmap DMA cannal*/
pxa_free_dma(output_stream->dma_ch);
}
if((file->f_mode & FMODE_WRITE)&(!output_stream)){
kfree(output_stream);
output_stream=NULL;
}
out_stream_err_allocate:
MOD_DEC_USE_COUNT;
return status;
}
static int ssp_release(struct inode *inode, struct file *file)
{
#ifdef DEBUG
printk(KPARAM_DBG "device_release(%p,%p)\n", inode, file);
#endif
if (file->f_mode & FMODE_WRITE){
ssp_sync(file);
ssp_clear_buf(output_stream);
if (output_stream->dma_ch >= 0){
DRCMR16 &= ~DRCMR_MAPVLD; /*unmap DMA cannal*/
pxa_free_dma(output_stream->dma_ch);
}
kfree(output_stream);
output_stream=NULL;
}
if (file->f_mode & FMODE_READ){
ssp_clear_buf(input_stream);
if (input_stream->dma_ch >= 0){
DRCMR15 &= ~DRCMR_MAPVLD; /*unmap DMA cannal*/
pxa_free_dma(input_stream->dma_ch);
}
kfree(input_stream);
input_stream=NULL;
}
MOD_DEC_USE_COUNT;
if(! MOD_IN_USE)
NSSCR0 &= ~ENA_BIT;
return SUCCESS;
}
static struct file_operations Fops = {
read: ssp_read,
write: ssp_write,
ioctl: ssp_ioctl,
open: ssp_open,
release: ssp_release
};
static int __init pxa2xx_ssp_init(void)
{
int status = 0;
/* int irq;*/
printk(KERN_INFO "Loading PXA2xx SSP driver\n");
if (request_mem_region(__PREG(NSSCR0), 0x30, "SSP")==NULL) {
printk(KERN_ERR " Cannot request resource \n");
return -EBUSY;
}
GPDR2 |= 0x000e0000; /*GP81 - GP83 outputs*/
GAFR2_U = 0x0254|(GAFR2_U&0x03); /*GP81 - GP83 alternative function 1, GP84 alternative function 2*/
CKEN |= CKEN9_NSSP; /*CLK enable*/
#ifdef DEBUG
printk(KPARAM_DBG "GPDR2=%x ,GAFR2_U=%x \n", GPDR2, GAFR2_U);
printk(KPARAM_DBG "CKEN=%x \n", CKEN);
#endif
NSSCR1 = SCLKDIR|SFRMDIR|TSRE|RSRE|(RFT_VAL<<10)|(TFT_VAL<<6); /*DMA enable*/
NSSCR0 = EDS|DATA_SIZE|FRAME_FORMAT|(CLOCK_RATE<<8);
#ifdef DEBUG
printk(KPARAM_DBG "NSSCR0=%x ,NSSCR1=%x , NSSITR=%x \n", NSSCR0, NSSCR1, NSSITR);
#endif
/*
* Try to register character device
*/
status = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);
if (status < 0) {
printk(KERN_INFO "%s failed with %d\n",
" Sorry, registering the character device ", status);
goto out_error_dev_register;
}
printk(KERN_INFO "%s The major device number is %d.\n",
"Registeration is a success.", MAJOR_NUM);
printk(KERN_INFO "If you want to talk to the device driver,\n");
printk(KERN_INFO "you'll have to create a device file. \n");
printk(KERN_INFO "We suggest you use:\n");
printk(KERN_INFO "mknod %s c %d 0\n", "/dev/ssp", MAJOR_NUM);
return 0;
out_error_dev_register:
release_mem_region(__PREG(NSSCR0), 0x30);
return status;
}
static void __exit pxa2xx_ssp_exit(void)
{
int ret;
printk(KERN_INFO "Remove PXA2xx SSP driver\n");
CKEN &= ~CKEN9_NSSP; /*CLK disable*/
ret = unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
if (ret < 0)
printk(KERN_ERR "Error in module_unregister_chrdev: %d\n", ret);
release_mem_region(__PREG(NSSCR0), 0x30);
}
module_init(pxa2xx_ssp_init);
module_exit(pxa2xx_ssp_exit);
MODULE_AUTHOR("Yevgeny Romanov");
MODULE_DESCRIPTION("PXA2xx SSP SPI Contoller");
MODULE_LICENSE("GPL");
файл pxa2xx_ssp.h
Цитата
/*
* linux/drivers/spi/pxa2xx_ssp.h
*
* Author: Yevgeny Romanov
* Created: May 16, 2006
* Copyright: Neurosoft Inc.
*
* 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.
*/
typedef struct {
int offset; /* current buffer position */
char *data; /* actual buffer */
pxa_dma_desc *dma_desc; /* pointer to the starting desc */
int master; /* owner for buffer allocation, contain size whn true */
} ssp_buf_t;
typedef struct {
char *name; /* stream identifier */
ssp_buf_t *buffers; /* pointer to ssp buffer array */
u_int usr_frag; /* user fragment index */
u_int dma_frag; /* DMA fragment index */
u_int fragsize; /* fragment size */
u_int nbfrags; /* number of fragments */
u_int dma_ch; /* DMA channel number */
dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */
u_int descs_per_frag; /* nbr descriptors per fragment */
int bytecount; /* nbr of processed bytes */
int fragcount; /* nbr of fragment transitions */
struct semaphore sem; /* account for fragment usage */
wait_queue_head_t frag_wq; /* for poll(), etc. */
wait_queue_head_t stop_wq; /* for users of DCSR_STOPIRQEN */
u_long dcmd; /* DMA descriptor dcmd field */
volatile u32 *drcmr; /* the DMA request channel to use */
u_long dev_addr; /* device physical address for DMA */
int mapped:1; /* mmap()'ed buffers */
int output:1; /* 0 for input, 1 for output */
} ssp_stream_t;