📄 stargate_ssp.c
字号:
/*
* sg_ssp.c -- Stargate SSP Driver for mote programming
*
* Portions Copyright (c) 2002-2003 Intel Corporation
* All rights reserved.
*
* This file is distributed under the terms in the attached INTEL-LICENSE
* file. If you do not find these files, copies can be found by writing to
* Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
* 94704. Attention: Intel License Inquiry.
* Portions Copyright (C) 2001 Alessandro Rubini and Jonathan Corbet
* Copyright (C) 2001 O'Reilly & Associates
*
* The source code in this file can be freely used, adapted,
* and redistributed in source or binary form, so long as an
* acknowledgment appears in derived source files. The citation
* should list that the code comes from the book "Linux Device
* Drivers" by Alessandro Rubini and Jonathan Corbet, published
* by O'Reilly & Associates. No warranty is attached;
* we cannot take responsibility for errors or fitness for use.
*
* $Id: stargate_ssp.c,v 1.4 2006/12/12 18:23:01 vlahan Exp $
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/config.h>
#ifdef CONFIG_MODVERSIONS
#define MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/kernel.h> /* printk() */
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/delay.h> /* udelay */
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/tqueue.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#define SG_SSP_PHYSBASE (__PREG(SSCR0))
#define SG_SSP_SIZE (5*4) /* 5 Registers, 4 Bytes each */
#define SG_GPIOCNTL_PHYSBASE (__PREG(GPLR0))
#define SG_GPIOCNTL_SIZE (27*4) /* 27 Registers, 4 Bytes each */
#define SSPS_TNF (1 << 2)
#define SSPS_RNE (1 << 3)
#define SSPS_BSY (1 << 4)
#define SSPS_TFS (1 << 5)
#define SSPS_RFS (1 << 6)
#define SSPS_ROR (1 << 7)
#define SSPC_SSE (1 << 7)
/*
* all of the parameters have no "sg_ssp_" prefix, to save typing when
* specifying them at load time
*/
static int major = 0; /* dynamic by default */
MODULE_PARM(major, "i");
/* Since sg_ssp_base is vremapped in case use_mem==1, remember the phys addr. */
unsigned long sg_ssp_vbase;
MODULE_AUTHOR ("Phil Buonadonna");
MODULE_DESCRIPTION ("Stargate SSP Driver v 0.91");
/*
* The devices with low minor numbers write/read burst of data to/from
* specific I/O ports (by default the parallel ones).
*
* The device with 128 as minor number returns ascii strings telling
* when interrupts have been received. Writing to the device toggles
* 00/FF on the parallel data lines. If there is a loopback wire, this
* generates interrupts.
*/
int sg_ssp_open (struct inode *inode, struct file *filp)
{
MOD_INC_USE_COUNT;
GPCR(22) = GPIO_bit(22); // MUX -> Programming mode
SSCR0 = ((10 << 8) | SSPC_SSE | 7); /* SRC=3, SSE, DSS = 8-bit data */
/* Put attached mote into reset */
GPCR(77) = GPIO_bit(77); // RSTN -> 0
return 0;
}
int sg_ssp_release (struct inode *inode, struct file *filp)
{
SSCR0 = 0;
GPSR(22) = GPIO_bit(22); // MUX -> Serial Comm mode
/* Clear the reset */
GPSR(77) = GPIO_bit(77); // RSTN -> 1
MOD_DEC_USE_COUNT;
return 0;
}
ssize_t sg_ssp_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
int retval = count, lcnt = count;
unsigned char *kbuf=kmalloc(count, GFP_KERNEL), *ptr;
unsigned int value;
if (!kbuf) {
return -ENOMEM;
}
ptr = kbuf;
while (lcnt > 0) {
while (!(SSSR & SSPS_RNE)) {
cpu_relax();
}
value = SSDR;
*(ptr++) = value;
//printk(KERN_INFO "sg_ssp: read 0x%lx from ssp\n",value);
lcnt--;
}
if ( (retval > 0) && copy_to_user(buf, kbuf, retval))
retval = -EFAULT;
kfree(kbuf);
return retval;
}
ssize_t sg_ssp_write(struct file *filp, const char *buf, size_t count,
loff_t *f_pos)
{
int retval = count, lcnt = count;
unsigned char *kbuf=kmalloc((count+1), GFP_KERNEL), *ptr;
unsigned int value;
if (!kbuf) {
return -ENOMEM;
}
if (copy_from_user(kbuf, buf, count)) {
return -EFAULT;
}
ptr = kbuf;
while (lcnt > 0) {
while (!(SSSR & SSPS_TNF)) {
cpu_relax();
}
value = *(ptr++);
SSDR = value;
//printk(KERN_INFO "sg_ssp: wrote 0x%lx to ssp\n",value);
lcnt--;
}
kfree(kbuf);
return retval;
}
unsigned int sg_ssp_poll(struct file *filp, poll_table *wait)
{
return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
}
struct file_operations sg_ssp_fops = {
read: sg_ssp_read,
write: sg_ssp_write,
poll: sg_ssp_poll,
open: sg_ssp_open,
release: sg_ssp_release,
};
void sg_ssp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned int status = SSSR;
if (status & SSPS_ROR) {
printk(KERN_WARNING "sg_ssp: receiver overrun 0x%lx",status);
SSSR = SSPS_ROR;
}
}
/* Two wrappers, to use non-page-aligned ioremap() on 2.0 */
/* Remap a not (necessarily) aligned port region */
void *sg_ssp_remap(unsigned long phys_addr, unsigned long phys_size)
{
/* The code comes mainly from arch/any/mm/ioremap.c */
unsigned long offset, last_addr, size;
last_addr = phys_addr + phys_size - 1;
offset = phys_addr & ~PAGE_MASK;
/* Adjust the begin and end to remap a full page */
phys_addr &= PAGE_MASK;
size = PAGE_ALIGN(last_addr) - phys_addr;
return ioremap(phys_addr, size) + offset;
}
/* Unmap a region obtained with sg_ssp_remap */
void sg_ssp_unmap(void *virt_add)
{
iounmap((void *)((unsigned long)virt_add & PAGE_MASK));
}
/* Finally, init and cleanup */
int sg_ssp_init(void)
{
int result;
/* Set up owner pointers.*/
SET_MODULE_OWNER(&sg_ssp_fops);
/* Get our needed resources. */
result = check_mem_region(SG_SSP_PHYSBASE, SG_SSP_SIZE);
if (result) {
printk(KERN_INFO "sg_ssp: can't get I/O mem address 0x%lx\n",
SG_SSP_PHYSBASE);
return result;
}
request_mem_region(SG_SSP_PHYSBASE, SG_SSP_SIZE, "SSP");
result = check_mem_region(SG_GPIOCNTL_PHYSBASE, SG_GPIOCNTL_SIZE);
if (result) {
printk(KERN_INFO "sg_ssp: can't get GPIO I/O mem address 0x%lx\n",
SG_GPIOCNTL_PHYSBASE);
return result;
}
request_mem_region(SG_GPIOCNTL_PHYSBASE, SG_GPIOCNTL_SIZE, "SSPGPIO");
/* also, ioremap it */
sg_ssp_vbase = (unsigned long)sg_ssp_remap(SG_SSP_PHYSBASE,SG_SSP_SIZE);
/* Hmm... we should check the return value */
result = register_chrdev(major, "ssp", &sg_ssp_fops);
if (result < 0) {
printk(KERN_INFO "sg_ssp: can't get major number\n");
sg_ssp_unmap((void *)sg_ssp_vbase);
release_mem_region(SG_SSP_PHYSBASE,SG_SSP_SIZE);
return result;
}
if (major == 0) major = result; /* dynamic */
result = request_irq(IRQ_SSP, sg_ssp_interrupt,
SA_INTERRUPT, "ssp", NULL);
if (result) {
printk(KERN_INFO "sg_ssp: can't get assigned irq %i\n",
IRQ_SSP);
sg_ssp_unmap((void *)sg_ssp_vbase);
release_mem_region(SG_SSP_PHYSBASE,SG_SSP_SIZE);
return result;
}
SSSR = SSPS_ROR;
SSCR1 = 0;
// Enable the SSP alternate functions
set_GPIO_mode(GPIO23_SCLK_md);
set_GPIO_mode(GPIO25_STXD_MD); // Enable the SSP TX/RX lines
set_GPIO_mode(GPIO26_SRXD_MD);
set_GPIO_mode((GPIO27_SEXTCLK | GPIO_IN)); // Avoid driving the RED LED
set_GPIO_mode((22 | GPIO_OUT)); // MUX Selector
set_GPIO_mode((77 | GPIO_OUT)); // RSTN
/* Set the state of the reset pin */
GPSR(77) = GPIO_bit(77); // RSTN -> 1
return 0;
}
void sg_ssp_cleanup(void)
{
free_irq(IRQ_SSP,NULL);
unregister_chrdev(major, "ssp");
sg_ssp_unmap((void *)sg_ssp_vbase);
release_mem_region(SG_SSP_PHYSBASE,SG_SSP_SIZE);
release_mem_region(SG_GPIOCNTL_PHYSBASE,SG_GPIOCNTL_SIZE);
}
module_init(sg_ssp_init);
module_exit(sg_ssp_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -