2011-03-05 02:00:17 +03:00
/*
* Programmable Real - Time Unit Sub System ( PRUSS ) UIO driver ( uio_pruss )
*
* This driver exports PRUSS host event out interrupts and PRUSS , L3 RAM ,
* and DDR RAM to user space for applications interacting with PRUSS firmware
*
* Copyright ( C ) 2010 - 11 Texas Instruments Incorporated - http : //www.ti.com/
*
* 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 version 2.
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/device.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/platform_device.h>
# include <linux/uio_driver.h>
# include <linux/platform_data/uio_pruss.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/dma-mapping.h>
2015-06-10 20:00:19 +03:00
# include <linux/sizes.h>
2011-03-05 02:00:17 +03:00
# include <linux/slab.h>
2012-10-05 21:04:40 +04:00
# include <linux/genalloc.h>
2011-03-05 02:00:17 +03:00
# define DRV_NAME "pruss_uio"
# define DRV_VERSION "1.0"
static int sram_pool_sz = SZ_16K ;
module_param ( sram_pool_sz , int , 0 ) ;
MODULE_PARM_DESC ( sram_pool_sz , " sram pool size to allocate " ) ;
static int extram_pool_sz = SZ_256K ;
module_param ( extram_pool_sz , int , 0 ) ;
MODULE_PARM_DESC ( extram_pool_sz , " external ram pool size to allocate " ) ;
/*
2011-03-31 05:57:33 +04:00
* Host event IRQ numbers from PRUSS - PRUSS can generate up to 8 interrupt
2011-03-05 02:00:17 +03:00
* events to AINTC of ARM host processor - which can be used for IPC b / w PRUSS
* firmware and user space application , async notification from PRU firmware
* to user space application
* 3 PRU_EVTOUT0
* 4 PRU_EVTOUT1
* 5 PRU_EVTOUT2
* 6 PRU_EVTOUT3
* 7 PRU_EVTOUT4
* 8 PRU_EVTOUT5
* 9 PRU_EVTOUT6
* 10 PRU_EVTOUT7
*/
# define MAX_PRUSS_EVT 8
# define PINTC_HIDISR 0x0038
# define PINTC_HIPIR 0x0900
# define HIPIR_NOPEND 0x80000000
# define PINTC_HIER 0x1500
struct uio_pruss_dev {
struct uio_info * info ;
struct clk * pruss_clk ;
dma_addr_t sram_paddr ;
dma_addr_t ddr_paddr ;
void __iomem * prussio_vaddr ;
2012-10-05 21:04:40 +04:00
unsigned long sram_vaddr ;
2011-03-05 02:00:17 +03:00
void * ddr_vaddr ;
unsigned int hostirq_start ;
unsigned int pintc_base ;
2012-10-05 21:04:40 +04:00
struct gen_pool * sram_pool ;
2011-03-05 02:00:17 +03:00
} ;
static irqreturn_t pruss_handler ( int irq , struct uio_info * info )
{
struct uio_pruss_dev * gdev = info - > priv ;
int intr_bit = ( irq - gdev - > hostirq_start + 2 ) ;
int val , intr_mask = ( 1 < < intr_bit ) ;
void __iomem * base = gdev - > prussio_vaddr + gdev - > pintc_base ;
void __iomem * intren_reg = base + PINTC_HIER ;
void __iomem * intrdis_reg = base + PINTC_HIDISR ;
void __iomem * intrstat_reg = base + PINTC_HIPIR + ( intr_bit < < 2 ) ;
val = ioread32 ( intren_reg ) ;
/* Is interrupt enabled and active ? */
if ( ! ( val & intr_mask ) & & ( ioread32 ( intrstat_reg ) & HIPIR_NOPEND ) )
return IRQ_NONE ;
/* Disable interrupt */
iowrite32 ( intr_bit , intrdis_reg ) ;
return IRQ_HANDLED ;
}
2014-06-29 20:21:35 +04:00
static void pruss_cleanup ( struct device * dev , struct uio_pruss_dev * gdev )
2011-03-05 02:00:17 +03:00
{
int cnt ;
struct uio_info * p = gdev - > info ;
for ( cnt = 0 ; cnt < MAX_PRUSS_EVT ; cnt + + , p + + ) {
uio_unregister_device ( p ) ;
kfree ( p - > name ) ;
}
iounmap ( gdev - > prussio_vaddr ) ;
if ( gdev - > ddr_vaddr ) {
2014-06-29 20:21:35 +04:00
dma_free_coherent ( dev , extram_pool_sz , gdev - > ddr_vaddr ,
2011-03-05 02:00:17 +03:00
gdev - > ddr_paddr ) ;
}
if ( gdev - > sram_vaddr )
2012-10-05 21:04:40 +04:00
gen_pool_free ( gdev - > sram_pool ,
gdev - > sram_vaddr ,
sram_pool_sz ) ;
2011-03-05 02:00:17 +03:00
kfree ( gdev - > info ) ;
2016-11-26 00:55:30 +03:00
clk_disable ( gdev - > pruss_clk ) ;
2011-03-05 02:00:17 +03:00
clk_put ( gdev - > pruss_clk ) ;
kfree ( gdev ) ;
}
2014-06-29 20:21:35 +04:00
static int pruss_probe ( struct platform_device * pdev )
2011-03-05 02:00:17 +03:00
{
struct uio_info * p ;
struct uio_pruss_dev * gdev ;
struct resource * regs_prussio ;
2014-06-29 20:21:35 +04:00
struct device * dev = & pdev - > dev ;
2011-03-05 02:00:17 +03:00
int ret = - ENODEV , cnt = 0 , len ;
2014-06-29 20:21:35 +04:00
struct uio_pruss_pdata * pdata = dev_get_platdata ( dev ) ;
2011-03-05 02:00:17 +03:00
gdev = kzalloc ( sizeof ( struct uio_pruss_dev ) , GFP_KERNEL ) ;
if ( ! gdev )
return - ENOMEM ;
gdev - > info = kzalloc ( sizeof ( * p ) * MAX_PRUSS_EVT , GFP_KERNEL ) ;
if ( ! gdev - > info ) {
kfree ( gdev ) ;
return - ENOMEM ;
}
2014-06-29 20:21:35 +04:00
2011-03-05 02:00:17 +03:00
/* Power on PRU in case its not done as part of boot-loader */
2014-06-29 20:21:35 +04:00
gdev - > pruss_clk = clk_get ( dev , " pruss " ) ;
2011-03-05 02:00:17 +03:00
if ( IS_ERR ( gdev - > pruss_clk ) ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " Failed to get clock \n " ) ;
2013-06-06 17:55:28 +04:00
ret = PTR_ERR ( gdev - > pruss_clk ) ;
2011-03-05 02:00:17 +03:00
kfree ( gdev - > info ) ;
kfree ( gdev ) ;
return ret ;
} else {
2016-11-26 00:55:30 +03:00
ret = clk_enable ( gdev - > pruss_clk ) ;
if ( ret ) {
dev_err ( dev , " Failed to enable clock \n " ) ;
clk_put ( gdev - > pruss_clk ) ;
kfree ( gdev - > info ) ;
kfree ( gdev ) ;
return ret ;
}
2011-03-05 02:00:17 +03:00
}
2014-06-29 20:21:35 +04:00
regs_prussio = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2011-03-05 02:00:17 +03:00
if ( ! regs_prussio ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " No PRUSS I/O resource specified \n " ) ;
2011-03-05 02:00:17 +03:00
goto out_free ;
}
if ( ! regs_prussio - > start ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " Invalid memory resource \n " ) ;
2011-03-05 02:00:17 +03:00
goto out_free ;
}
2012-10-05 21:04:40 +04:00
if ( pdata - > sram_pool ) {
gdev - > sram_pool = pdata - > sram_pool ;
gdev - > sram_vaddr =
2013-11-13 03:09:57 +04:00
( unsigned long ) gen_pool_dma_alloc ( gdev - > sram_pool ,
sram_pool_sz , & gdev - > sram_paddr ) ;
2012-10-05 21:04:40 +04:00
if ( ! gdev - > sram_vaddr ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " Could not allocate SRAM pool \n " ) ;
2012-10-05 21:04:40 +04:00
goto out_free ;
}
2011-03-05 02:00:17 +03:00
}
2014-06-29 20:21:35 +04:00
gdev - > ddr_vaddr = dma_alloc_coherent ( dev , extram_pool_sz ,
2011-03-05 02:00:17 +03:00
& ( gdev - > ddr_paddr ) , GFP_KERNEL | GFP_DMA ) ;
if ( ! gdev - > ddr_vaddr ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " Could not allocate external memory \n " ) ;
2011-03-05 02:00:17 +03:00
goto out_free ;
}
len = resource_size ( regs_prussio ) ;
gdev - > prussio_vaddr = ioremap ( regs_prussio - > start , len ) ;
if ( ! gdev - > prussio_vaddr ) {
2014-06-29 20:21:35 +04:00
dev_err ( dev , " Can't remap PRUSS I/O address range \n " ) ;
2011-03-05 02:00:17 +03:00
goto out_free ;
}
gdev - > pintc_base = pdata - > pintc_base ;
2014-06-29 20:21:35 +04:00
gdev - > hostirq_start = platform_get_irq ( pdev , 0 ) ;
2011-03-05 02:00:17 +03:00
for ( cnt = 0 , p = gdev - > info ; cnt < MAX_PRUSS_EVT ; cnt + + , p + + ) {
p - > mem [ 0 ] . addr = regs_prussio - > start ;
p - > mem [ 0 ] . size = resource_size ( regs_prussio ) ;
p - > mem [ 0 ] . memtype = UIO_MEM_PHYS ;
p - > mem [ 1 ] . addr = gdev - > sram_paddr ;
p - > mem [ 1 ] . size = sram_pool_sz ;
p - > mem [ 1 ] . memtype = UIO_MEM_PHYS ;
p - > mem [ 2 ] . addr = gdev - > ddr_paddr ;
p - > mem [ 2 ] . size = extram_pool_sz ;
p - > mem [ 2 ] . memtype = UIO_MEM_PHYS ;
p - > name = kasprintf ( GFP_KERNEL , " pruss_evt%d " , cnt ) ;
p - > version = DRV_VERSION ;
/* Register PRUSS IRQ lines */
p - > irq = gdev - > hostirq_start + cnt ;
p - > handler = pruss_handler ;
p - > priv = gdev ;
2014-06-29 20:21:35 +04:00
ret = uio_register_device ( dev , p ) ;
2011-03-05 02:00:17 +03:00
if ( ret < 0 )
goto out_free ;
}
2014-06-29 20:21:35 +04:00
platform_set_drvdata ( pdev , gdev ) ;
2011-03-05 02:00:17 +03:00
return 0 ;
out_free :
pruss_cleanup ( dev , gdev ) ;
return ret ;
}
2012-11-19 22:26:19 +04:00
static int pruss_remove ( struct platform_device * dev )
2011-03-05 02:00:17 +03:00
{
struct uio_pruss_dev * gdev = platform_get_drvdata ( dev ) ;
2014-06-29 20:21:35 +04:00
pruss_cleanup ( & dev - > dev , gdev ) ;
2011-03-05 02:00:17 +03:00
return 0 ;
}
static struct platform_driver pruss_driver = {
. probe = pruss_probe ,
2012-11-19 22:21:07 +04:00
. remove = pruss_remove ,
2011-03-05 02:00:17 +03:00
. driver = {
. name = DRV_NAME ,
} ,
} ;
2011-11-26 11:18:55 +04:00
module_platform_driver ( pruss_driver ) ;
2011-03-05 02:00:17 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
MODULE_AUTHOR ( " Amit Chatterjee <amit.chatterjee@ti.com> " ) ;
MODULE_AUTHOR ( " Pratheesh Gangadhar <pratheesh@ti.com> " ) ;