URL
https://opencores.org/ocsvn/sata_controller_core/sata_controller_core/trunk
Subversion Repositories sata_controller_core
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 13 to Rev 14
- ↔ Reverse comparison
Rev 13 → Rev 14
/sata_controller_core/trunk/sata2_driver_v1_00_a/sata_drv.c
0,0 → 1,458
/* |
* sata-drv.c |
* |
* Linux block device driver for Ashwin Mendon's SATA controller. |
* |
* Author: Bin Huang <bin.arthur@gmail.com> |
* |
* 2012 (c) Reconfigurable Computing System Lab at University of North |
* Carolina at Charlotte. This file is licensed under |
* the terms of the GNU General Public License version 2. This program |
* is licensed "as is" without any warranty of any kind, whether express |
* or implied. The code originally comes from the book "Linux Device |
* Drivers" by Alessandro Rubini and Jonathan Corbet, published |
* by O'Reilly & Associates. |
*/ |
|
#include <linux/module.h> |
#include <linux/moduleparam.h> |
#include <linux/init.h> |
#include <linux/sched.h> |
#include <linux/kernel.h> /* printk() */ |
#include <linux/slab.h> /* kmalloc() */ |
#include <linux/fs.h> /* everything... */ |
#include <linux/errno.h> /* error codes */ |
#include <linux/types.h> /* size_t */ |
#include <linux/fcntl.h> /* O_ACCMODE */ |
#include <linux/hdreg.h> /* HDIO_GETGEO */ |
#include <linux/kdev_t.h> |
#include <linux/vmalloc.h> |
#include <linux/genhd.h> |
#include <linux/blkdev.h> |
#include <linux/buffer_head.h> /* invalidate_bdev */ |
#include <linux/bio.h> |
#include <linux/delay.h> /* udelay() */ |
#include "sata_drv.h" |
|
MODULE_LICENSE("Dual BSD/GPL"); |
|
static int sata_major = 0; |
module_param(sata_major, int, 0); |
static int hardsect_size = HARDSECT_SIZE; |
module_param(hardsect_size, int, 0); |
static int nsectors = N_SECTORS; /* How big the drive is */ |
module_param(nsectors, int, 0); |
static int ndevices = 1; |
module_param(ndevices, int, 0); |
|
/* |
* The different "request modes" we can use. |
*/ |
enum { |
RM_SIMPLE = 0, /* The extra-simple request function */ |
RM_FULL = 1, /* The full-blown version */ |
RM_NOQUEUE = 2, /* Use make_request */ |
}; |
static int request_mode = RM_NOQUEUE; |
module_param(request_mode, int, 0); |
|
static struct sata_dev *Devices = NULL; |
|
|
void read_sectors(struct sata_dev *dev, int sector_addr, int sector_count, unsigned int dma_phy_addr); |
void write_sectors(struct sata_dev *dev, int sector_addr, int sector_count, unsigned int dma_phy_addr); |
|
void read_sectors(struct sata_dev *dev, int sector_addr, int sector_count, unsigned int dma_phy_addr) |
{ |
|
// DDR Read Space Start Address |
dev->scp->npi_wr_addr_reg = dma_phy_addr; |
// Clear SATA Control Register |
dev->scp->ctrl_reg = REG_CLEAR; |
|
// Input Sector Address, Count and Command to Sata Core |
dev->scp->sector_addr_reg = sector_addr; |
dev->scp->sector_count_reg = sector_count; |
dev->scp->cmd_reg = (READ_CMD); |
// Trigger SATA Core |
dev->scp->ctrl_reg = (NEW_CMD); |
|
// Wait for Command Completion |
while ((dev->scp->status_reg & SATA_CORE_DONE) != SATA_CORE_DONE); |
|
//Time to Read Sectors from Disk |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk("Number of clock cycles to complete this read: %d\n",dev->scp->sector_timer_reg); |
#endif |
|
while ((dev->scp->status_reg & NPI_DONE) != NPI_DONE); |
} |
|
|
void write_sectors(struct sata_dev *dev, int sector_addr, int sector_count, unsigned int dma_phy_addr) |
{ |
// DDR Write Space Start Address |
dev->scp->npi_rd_addr_reg = dma_phy_addr; |
// Clear SATA Control Register |
dev->scp->ctrl_reg = REG_CLEAR; |
|
// Input Sector Address, Count, DATA and Command to Sata Core |
dev->scp->sector_addr_reg = sector_addr; |
dev->scp->sector_count_reg = sector_count; |
//scp->wr_data_reg = write_data; |
dev->scp->cmd_reg = (WRITE_CMD); |
// Trigger SATA Core |
dev->scp->ctrl_reg = (NEW_CMD); |
// Wait for Command Completion |
|
while ((dev->scp->status_reg & SATA_CORE_DONE) != SATA_CORE_DONE); |
|
//Time to Write Sectors from Disk |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk("Number of clock cycles to complete this write: %d\n",dev->scp->sector_timer_reg); |
#endif |
} |
|
|
/* |
* Handle an I/O request. |
*/ |
static void sata_transfer(struct sata_dev *dev, unsigned long sector, |
unsigned long nsect, char *buffer, int write) |
{ |
unsigned long offset = sector*KERNEL_SECTOR_SIZE; |
unsigned long nbytes = nsect*KERNEL_SECTOR_SIZE; |
|
if ((offset + nbytes) > dev->size) { |
printk (KERN_INFO "Beyond-end write (%ld %ld)\n", offset, nbytes); |
return; |
} |
if (write){ |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk (KERN_INFO "Write request to: \n"); |
printk (KERN_INFO "sector = %lu, nsect = %lu, buf_phy_addr = 0x%08lx ... \n ", sector, nsect, virt_to_phys(buffer)); |
#endif |
|
write_sectors(dev, sector, nsect, virt_to_phys(buffer)); |
|
#ifdef CONFIG_RCS_SATA_DEBUG |
printk (KERN_INFO "Write done.\n"); |
#endif |
} |
else{ |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk (KERN_INFO "Read request to: \n"); |
printk (KERN_INFO "sector = %lu, nsect = %lu, buf_phy_addr = 0x%08lx ... \n", sector, nsect, virt_to_phys(buffer)); |
#endif |
|
read_sectors(dev, sector, nsect, virt_to_phys(buffer)); |
|
#ifdef CONFIG_RCS_SATA_DEBUG |
printk (KERN_INFO "Read done\n"); |
#endif |
} |
} |
|
|
static void sata_request(struct request_queue *q) |
{ |
struct request *req; |
|
req = blk_fetch_request(q); |
while (req != NULL) { |
struct sata_dev *dev = req->rq_disk->private_data; |
if (req == NULL || (req->cmd_type != REQ_TYPE_FS)) { |
printk (KERN_NOTICE "Skip non-CMD request\n"); |
__blk_end_request_all(req, -EIO); |
continue; |
} |
sata_transfer(dev, blk_rq_pos(req), blk_rq_cur_sectors(req), |
req->buffer, rq_data_dir(req)); |
if ( ! __blk_end_request_cur(req, 0) ) { |
req = blk_fetch_request(q); |
} |
} |
} |
|
|
/* |
* Transfer a single BIO. |
*/ |
static int sata_xfer_bio(struct sata_dev *dev, struct bio *bio) |
{ |
int i; |
struct bio_vec *bvec; |
sector_t sector = bio->bi_sector; |
|
void *mem; |
|
// Do each segment independently. |
bio_for_each_segment(bvec, bio, i) { |
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0); |
|
mem = kmap(bvec->bv_page); |
kunmap(bvec->bv_page); |
|
sata_transfer(dev, sector, bio_cur_bytes(bio) >> SECTOR_SHIFT, |
buffer, bio_data_dir(bio) == WRITE); |
sector += bio_cur_bytes(bio) >> SECTOR_SHIFT; |
__bio_kunmap_atomic(bio, KM_USER0); |
} |
return 0; // Always "succeed" |
} |
|
|
/* |
* The direct make request version. |
*/ |
static int sata_make_request(struct request_queue *q, struct bio *bio) |
{ |
struct sata_dev *dev = q->queuedata; |
int status; |
|
status = sata_xfer_bio(dev, bio); |
bio_endio(bio, status); |
return 0; |
} |
|
|
/* |
* Open and close. |
*/ |
|
static int sata_open(struct block_device *bdev, fmode_t mode) |
{ |
struct sata_dev *dev = bdev->bd_disk->private_data; |
unsigned long flags; |
|
spin_lock_irqsave(&dev->lock, flags); |
dev->users++; |
spin_unlock_irqrestore(&dev->lock, flags); |
|
check_disk_change(bdev); |
return 0; |
} |
|
|
static int sata_release(struct gendisk *disk, fmode_t mode) |
{ |
struct sata_dev *dev = disk->private_data; |
|
spin_lock(&dev->lock); |
dev->users--; |
|
spin_unlock(&dev->lock); |
|
return 0; |
} |
|
|
/* |
* The HDIO_GETGEO ioctl is handled in blkdev_ioctl. |
*/ |
int sata_getgeo (struct block_device *bdev, struct hd_geometry *geo) |
{ |
long size; |
struct sata_dev *dev = bdev->bd_disk->private_data; |
|
printk(KERN_INFO "SATA block device driver command : HDIO_GETGEO .\n"); |
size = dev->size*(hardsect_size/KERNEL_SECTOR_SIZE); |
geo->cylinders = (size & ~0x3f) >> 6; |
geo->heads = 4; |
geo->sectors = 16; |
geo->start = 0; |
return 0; |
|
} |
|
|
/* |
* The device operations structure. |
*/ |
static struct block_device_operations sata_ops = |
{ |
.owner = THIS_MODULE, |
.open = sata_open, |
.release = sata_release, |
.getgeo = sata_getgeo |
}; |
|
|
/* |
* Set up our internal device. |
*/ |
static int setup_device(struct sata_dev *dev, int which) |
{ |
int retval = 0; |
int try_setup_link = 0; |
|
/* |
* Initialize data structure. |
*/ |
memset (dev, 0, sizeof (struct sata_dev)); |
dev->size = nsectors*hardsect_size; |
|
/* |
* Remap I/O for SATA controller. |
*/ |
|
if( !request_mem_region(SATA_CFG_BASE, SATA_CFG_REMAP_SIZE, DRIVER_NAME)){ |
printk(KERN_ERR "Request for SATA configuration memory region failed!\n"); |
dev->scp = 0; |
retval = -1; |
goto fail; |
} |
else { |
dev->scp = (SATA_core_t *) ioremap_nocache(SATA_CFG_BASE, SATA_CFG_REMAP_SIZE); |
if (dev->scp == 0) { |
printk(KERN_ERR |
"%s: Couldn't ioremap memory at 0x%08X\n", |
DRIVER_NAME, SATA_CFG_BASE); |
iounmap(dev->scp); |
retval = -EFAULT; |
goto fail; |
} |
else { |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk(KERN_INFO "Remap Returned Virtual Address: %x\n",(unsigned int)dev->scp); |
#endif |
} |
} |
|
dev->scp->ctrl_reg = REG_CLEAR; |
dev->scp->cmd_reg = REG_CLEAR; |
|
while ((dev->scp->status_reg & SATA_LINK_READY) != SATA_LINK_READY) |
{ |
dev->scp->ctrl_reg = SW_RESET; |
dev->scp->ctrl_reg = REG_CLEAR; |
|
try_setup_link++; |
|
udelay(100000); |
|
//It is very disk specific. For the OCZ SSD, it takes a few resets for the link |
//to come up but on the Micron SSD or on the Western Digital HD, it comes up |
//after the first reset. |
if (try_setup_link == 100) { |
#ifdef CONFIG_RCS_SATA_DEBUG |
printk(KERN_INFO "SATA Link is not ready yet.\n"); |
#endif |
retval = -ENODEV; |
goto fail; |
} |
} |
|
spin_lock_init(&dev->lock); |
|
/* |
* The I/O queue, depending on whether we are using our own |
* make_request function or not. |
*/ |
switch (request_mode) { |
case RM_NOQUEUE: |
dev->queue = blk_alloc_queue(GFP_KERNEL); |
if (dev->queue == NULL) |
goto fail; |
blk_queue_make_request(dev->queue, sata_make_request); |
break; |
|
case RM_SIMPLE: |
dev->queue = blk_init_queue(sata_request, &dev->lock); |
if (dev->queue == NULL) |
{ |
retval = -ENODEV; |
goto fail; |
} |
break; |
|
default: |
printk(KERN_NOTICE "Bad request mode %d, using simple\n", request_mode); |
} |
blk_queue_logical_block_size(dev->queue, hardsect_size); |
dev->queue->queuedata = dev; |
|
/* |
* And the gendisk structure. |
*/ |
dev->gd = alloc_disk(SATA_MINORS); |
if (! dev->gd) { |
printk (KERN_INFO "alloc_disk failure\n"); |
retval = -ENODEV; |
goto fail; |
} |
printk (KERN_INFO "alloc_disk success\n"); |
dev->gd->major = sata_major; |
dev->gd->first_minor = which*SATA_MINORS; |
dev->gd->fops = &sata_ops; |
dev->gd->queue = dev->queue; |
dev->gd->private_data = dev; |
snprintf (dev->gd->disk_name, 32, "sata%c", which + 'a'); |
set_capacity(dev->gd, nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); |
add_disk(dev->gd); |
|
return 0; /* success */ |
|
fail: |
printk(KERN_INFO "Fail to setup SATA block device.\n"); |
printk("SATA controller status: 0x%08x\n", dev->scp->status_reg); |
return retval; |
} |
|
|
static int __init sata_init(void) |
{ |
int i; |
int retval = 0; |
|
/* |
* Get registered. |
*/ |
printk(KERN_INFO "Block device driver for sata controller\n"); |
sata_major = register_blkdev(sata_major, "sata"); |
if (sata_major <= 0) { |
printk(KERN_WARNING "sata: unable to get major number\n"); |
return -EBUSY; |
} |
|
/* |
* Allocate the device array, and initialize each one. |
*/ |
Devices = kmalloc(ndevices*sizeof (struct sata_dev), GFP_KERNEL); |
if (Devices == NULL) |
goto out_unregister; |
for (i = 0; i < ndevices; i++){ |
retval = setup_device(Devices + i, i); |
} |
return retval; |
|
out_unregister: |
unregister_blkdev(sata_major, "sata"); |
return -ENOMEM; |
} |
|
|
static void sata_exit(void) |
{ |
int i; |
|
for (i = 0; i < ndevices; i++) { |
struct sata_dev *dev = Devices + i; |
|
if (dev->gd) { |
del_gendisk(dev->gd); |
put_disk(dev->gd); |
} |
if (dev->queue) { |
if (request_mode == RM_NOQUEUE) |
blk_put_queue(dev->queue); |
else |
blk_cleanup_queue(dev->queue); |
} |
} |
unregister_blkdev(sata_major, "sata"); |
kfree(Devices); |
} |
|
module_init(sata_init); |
module_exit(sata_exit); |
/sata_controller_core/trunk/sata2_driver_v1_00_a/sata_drv.h
0,0 → 1,58
/* |
* sata_drv.h |
* |
* Definitions for the SATA device driver |
* |
* Author: Bin Huang <bin.arthur@gmail.com> |
* |
* 2012 (c) Reconfigurable Computing System Lab at University of North |
* Carolina at Charlotte. This file is licensed under |
* the terms of the GNU General Public License version 2. This program |
* is licensed "as is" without any warranty of any kind, whether express |
* or implied. The code originally comes from the book "Linux Device |
* Drivers" by Alessandro Rubini and Jonathan Corbet, published |
* by O'Reilly & Associates. |
*/ |
|
#include <linux/ioctl.h> |
#include "sata_cfg.h" |
|
#define DRIVER_NAME "sata" |
|
/* |
* Minor number and partition management. |
*/ |
#define SATA_MINORS 2 |
#define MINOR_SHIFT 4 |
#define DEVNUM(kdevnum) (MINOR(kdev_t_to_nr(kdevnum)) >> MINOR_SHIFT |
|
/* |
* The internal representation of our device. |
*/ |
struct sata_dev { |
int size; /* Device size in sectors */ |
short users; /* How many users */ |
short media_change; /* Flag a media change? */ |
spinlock_t lock; /* For mutual exclusion */ |
struct request_queue *queue; /* The device request queue */ |
struct gendisk *gd; /* The gendisk structure */ |
SATA_core_t *scp; /* SATA Core Slave Registers */ |
}; |
|
|
#define HARDSECT_SIZE 512 |
|
/* |
* N_SECTORS: |
* 409600 -> 200MB |
* 819200 -> 400MB |
*/ |
#define N_SECTORS 2097152 //1024MB |
|
/* |
* We can tweak our hardware sector size, but the kernel talks to us |
* in terms of small sectors, always. |
*/ |
#define KERNEL_SECTOR_SIZE 512 |
|
#define SECTOR_SHIFT 9 |
/sata_controller_core/trunk/sata2_driver_v1_00_a/Makefile
0,0 → 1,22
LINUX = ../linux-2.6-xlnx |
DRIVER_NAME=sata |
|
# If KERNELRELEASE is defined, we've been invoked from the |
# kernel build system and can use its language. |
ifneq ($(KERNELRELEASE),) |
obj-m := $(DRIVER_NAME).o |
$(DRIVER_NAME)-objs := $(DRIVER_NAME)_drv.o |
|
# Otherwise we were called directly from the command |
# line; invoke the kernel build system. |
else |
KERNELDIR ?= $(LINUX) |
PWD := $(shell pwd) |
|
default: |
$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=microblaze CROSS_COMPILE=microblaze-unknown-linux-gnu- modules |
endif |
|
clean: |
rm -rf *.ko *.o *~ modules.order Module.symvers *.mod.c .tmp_versions .$(DRIVER_NAME)* |
|
/sata_controller_core/trunk/sata2_driver_v1_00_a/sata_cfg.h
0,0 → 1,52
/* |
* sata_cfg.h |
* |
* Definitions for the SATA core configuration registers |
* |
* Author: Bin Huang <bin.arthur@gmail.com> |
* |
* 2012 (c) Reconfigurable Computing System Lab at University of North |
* Carolina at Charlotte. This file is licensed under |
* the terms of the GNU General Public License version 2. This program |
* is licensed "as is" without any warranty of any kind, whether express |
* or implied. The code originally comes from the book "Linux Device |
* Drivers" by Alessandro Rubini and Jonathan Corbet, published |
* by O'Reilly & Associates. |
*/ |
|
#include "xparameters.h" |
|
#define SATA_CFG_BASE XPAR_SATA_CORE_0_BASEADDR |
#define SATA_CFG_HIGH XPAR_SATA_CORE_0_HIGHADDR |
#define SATA_CFG_REMAP_SIZE (SATA_CFG_HIGH - SATA_CFG_BASE + 1) |
|
// Structure maps to SATA Core Slave Registers |
typedef struct { |
volatile unsigned int ctrl_reg; |
volatile unsigned int cmd_reg; |
volatile unsigned int status_reg; |
volatile unsigned int sector_addr_reg; |
volatile unsigned int sector_count_reg; |
volatile unsigned int sector_timer_reg; |
volatile unsigned int npi_rd_addr_reg; |
volatile unsigned int npi_wr_addr_reg; |
} SATA_core_t, |
*pSATA_core_t; |
|
// Special Commands to SATA controller |
#define REG_CLEAR 0x00000000 |
#define SATA_LINK_READY 0x00000002 |
#define SW_RESET 0x00000001 |
#define NPI_DONE 0x00000008 |
#define SATA_CORE_DONE 0x00000004 |
#define NEW_CMD 0x00000002 |
#define READ_CMD 0x00000000 |
#define WRITE_CMD 0x00000001 |
#define FPDMA_READ_CMD 0x00000002 |
#define FPDMA_WRITE_CMD 0x00000003 |
#define IDEN_DEV_CMD 0x00000004 |
#define SET_FEATURES_CMD 0x00000005 |
#define QUEUE_DEPTH 0x00000000 |
#define QUEUE_DEPTH_INT 1 |
|
#define WORDS_PER_SECTOR 128 |