2011-02-14 05:20:39 +03:00
/*
* OpenCores tiny SPI master driver
*
* http : //opencores.org/project,tiny_spi
*
* Copyright ( C ) 2011 Thomas Chou < thomas @ wytron . com . tw >
*
* Based on spi_s3c24xx . c , which is :
* Copyright ( c ) 2006 Ben Dooks
* Copyright ( c ) 2006 Simtec Electronics
* Ben Dooks < ben @ simtec . co . uk >
*
* 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 .
*/
# include <linux/interrupt.h>
# include <linux/errno.h>
2011-07-03 23:44:29 +04:00
# include <linux/module.h>
2011-02-14 05:20:39 +03:00
# include <linux/platform_device.h>
# include <linux/spi/spi.h>
# include <linux/spi/spi_bitbang.h>
# include <linux/spi/spi_oc_tiny.h>
# include <linux/io.h>
# include <linux/gpio.h>
# include <linux/of.h>
# define DRV_NAME "spi_oc_tiny"
# define TINY_SPI_RXDATA 0
# define TINY_SPI_TXDATA 4
# define TINY_SPI_STATUS 8
# define TINY_SPI_CONTROL 12
# define TINY_SPI_BAUD 16
# define TINY_SPI_STATUS_TXE 0x1
# define TINY_SPI_STATUS_TXR 0x2
struct tiny_spi {
/* bitbang has to be first */
struct spi_bitbang bitbang ;
struct completion done ;
void __iomem * base ;
int irq ;
unsigned int freq ;
unsigned int baudwidth ;
unsigned int baud ;
unsigned int speed_hz ;
unsigned int mode ;
unsigned int len ;
unsigned int txc , rxc ;
const u8 * txp ;
u8 * rxp ;
2013-02-12 21:48:37 +04:00
int gpio_cs_count ;
2011-02-14 05:20:39 +03:00
int * gpio_cs ;
} ;
static inline struct tiny_spi * tiny_spi_to_hw ( struct spi_device * sdev )
{
return spi_master_get_devdata ( sdev - > master ) ;
}
static unsigned int tiny_spi_baud ( struct spi_device * spi , unsigned int hz )
{
struct tiny_spi * hw = tiny_spi_to_hw ( spi ) ;
return min ( DIV_ROUND_UP ( hw - > freq , hz * 2 ) , ( 1U < < hw - > baudwidth ) ) - 1 ;
}
static void tiny_spi_chipselect ( struct spi_device * spi , int is_active )
{
struct tiny_spi * hw = tiny_spi_to_hw ( spi ) ;
2013-02-12 21:48:37 +04:00
if ( hw - > gpio_cs_count > 0 ) {
2011-02-14 05:20:39 +03:00
gpio_set_value ( hw - > gpio_cs [ spi - > chip_select ] ,
( spi - > mode & SPI_CS_HIGH ) ? is_active : ! is_active ) ;
}
}
static int tiny_spi_setup_transfer ( struct spi_device * spi ,
struct spi_transfer * t )
{
struct tiny_spi * hw = tiny_spi_to_hw ( spi ) ;
unsigned int baud = hw - > baud ;
if ( t ) {
if ( t - > speed_hz & & t - > speed_hz ! = hw - > speed_hz )
baud = tiny_spi_baud ( spi , t - > speed_hz ) ;
}
writel ( baud , hw - > base + TINY_SPI_BAUD ) ;
writel ( hw - > mode , hw - > base + TINY_SPI_CONTROL ) ;
return 0 ;
}
static int tiny_spi_setup ( struct spi_device * spi )
{
struct tiny_spi * hw = tiny_spi_to_hw ( spi ) ;
if ( spi - > max_speed_hz ! = hw - > speed_hz ) {
hw - > speed_hz = spi - > max_speed_hz ;
hw - > baud = tiny_spi_baud ( spi , hw - > speed_hz ) ;
}
hw - > mode = spi - > mode & ( SPI_CPOL | SPI_CPHA ) ;
return 0 ;
}
static inline void tiny_spi_wait_txr ( struct tiny_spi * hw )
{
while ( ! ( readb ( hw - > base + TINY_SPI_STATUS ) &
TINY_SPI_STATUS_TXR ) )
cpu_relax ( ) ;
}
static inline void tiny_spi_wait_txe ( struct tiny_spi * hw )
{
while ( ! ( readb ( hw - > base + TINY_SPI_STATUS ) &
TINY_SPI_STATUS_TXE ) )
cpu_relax ( ) ;
}
static int tiny_spi_txrx_bufs ( struct spi_device * spi , struct spi_transfer * t )
{
struct tiny_spi * hw = tiny_spi_to_hw ( spi ) ;
const u8 * txp = t - > tx_buf ;
u8 * rxp = t - > rx_buf ;
unsigned int i ;
if ( hw - > irq > = 0 ) {
2012-07-30 18:16:14 +04:00
/* use interrupt driven data transfer */
2011-02-14 05:20:39 +03:00
hw - > len = t - > len ;
hw - > txp = t - > tx_buf ;
hw - > rxp = t - > rx_buf ;
hw - > txc = 0 ;
hw - > rxc = 0 ;
/* send the first byte */
if ( t - > len > 1 ) {
writeb ( hw - > txp ? * hw - > txp + + : 0 ,
hw - > base + TINY_SPI_TXDATA ) ;
hw - > txc + + ;
writeb ( hw - > txp ? * hw - > txp + + : 0 ,
hw - > base + TINY_SPI_TXDATA ) ;
hw - > txc + + ;
writeb ( TINY_SPI_STATUS_TXR , hw - > base + TINY_SPI_STATUS ) ;
} else {
writeb ( hw - > txp ? * hw - > txp + + : 0 ,
hw - > base + TINY_SPI_TXDATA ) ;
hw - > txc + + ;
writeb ( TINY_SPI_STATUS_TXE , hw - > base + TINY_SPI_STATUS ) ;
}
wait_for_completion ( & hw - > done ) ;
} else {
2014-01-08 12:00:04 +04:00
/* we need to tighten the transfer loop */
writeb ( txp ? * txp + + : 0 , hw - > base + TINY_SPI_TXDATA ) ;
for ( i = 1 ; i < t - > len ; i + + ) {
writeb ( txp ? * txp + + : 0 , hw - > base + TINY_SPI_TXDATA ) ;
if ( rxp | | ( i ! = t - > len - 1 ) )
2011-02-14 05:20:39 +03:00
tiny_spi_wait_txr ( hw ) ;
2014-01-08 12:00:04 +04:00
if ( rxp )
* rxp + + = readb ( hw - > base + TINY_SPI_TXDATA ) ;
2011-02-14 05:20:39 +03:00
}
tiny_spi_wait_txe ( hw ) ;
2014-01-08 12:00:04 +04:00
if ( rxp )
* rxp + + = readb ( hw - > base + TINY_SPI_RXDATA ) ;
2011-02-14 05:20:39 +03:00
}
2014-01-08 12:00:04 +04:00
2011-02-14 05:20:39 +03:00
return t - > len ;
}
static irqreturn_t tiny_spi_irq ( int irq , void * dev )
{
struct tiny_spi * hw = dev ;
writeb ( 0 , hw - > base + TINY_SPI_STATUS ) ;
if ( hw - > rxc + 1 = = hw - > len ) {
if ( hw - > rxp )
* hw - > rxp + + = readb ( hw - > base + TINY_SPI_RXDATA ) ;
hw - > rxc + + ;
complete ( & hw - > done ) ;
} else {
if ( hw - > rxp )
* hw - > rxp + + = readb ( hw - > base + TINY_SPI_TXDATA ) ;
hw - > rxc + + ;
if ( hw - > txc < hw - > len ) {
writeb ( hw - > txp ? * hw - > txp + + : 0 ,
hw - > base + TINY_SPI_TXDATA ) ;
hw - > txc + + ;
writeb ( TINY_SPI_STATUS_TXR ,
hw - > base + TINY_SPI_STATUS ) ;
} else {
writeb ( TINY_SPI_STATUS_TXE ,
hw - > base + TINY_SPI_STATUS ) ;
}
}
return IRQ_HANDLED ;
}
# ifdef CONFIG_OF
# include <linux/of_gpio.h>
2012-12-07 20:57:14 +04:00
static int tiny_spi_of_probe ( struct platform_device * pdev )
2011-02-14 05:20:39 +03:00
{
struct tiny_spi * hw = platform_get_drvdata ( pdev ) ;
struct device_node * np = pdev - > dev . of_node ;
unsigned int i ;
2015-09-09 14:55:53 +03:00
u32 val ;
2011-02-14 05:20:39 +03:00
if ( ! np )
return 0 ;
hw - > gpio_cs_count = of_gpio_count ( np ) ;
2013-02-12 21:48:37 +04:00
if ( hw - > gpio_cs_count > 0 ) {
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 00:07:58 +03:00
hw - > gpio_cs = devm_kcalloc ( & pdev - > dev ,
hw - > gpio_cs_count , sizeof ( unsigned int ) ,
2011-02-14 05:20:39 +03:00
GFP_KERNEL ) ;
if ( ! hw - > gpio_cs )
return - ENOMEM ;
}
for ( i = 0 ; i < hw - > gpio_cs_count ; i + + ) {
hw - > gpio_cs [ i ] = of_get_gpio_flags ( np , i , NULL ) ;
if ( hw - > gpio_cs [ i ] < 0 )
return - ENODEV ;
}
hw - > bitbang . master - > dev . of_node = pdev - > dev . of_node ;
2015-09-09 14:55:53 +03:00
if ( ! of_property_read_u32 ( np , " clock-frequency " , & val ) )
hw - > freq = val ;
if ( ! of_property_read_u32 ( np , " baud-width " , & val ) )
hw - > baudwidth = val ;
2011-02-14 05:20:39 +03:00
return 0 ;
}
# else /* !CONFIG_OF */
2012-12-07 20:57:14 +04:00
static int tiny_spi_of_probe ( struct platform_device * pdev )
2011-02-14 05:20:39 +03:00
{
return 0 ;
}
# endif /* CONFIG_OF */
2012-12-07 20:57:14 +04:00
static int tiny_spi_probe ( struct platform_device * pdev )
2011-02-14 05:20:39 +03:00
{
2013-07-30 11:58:59 +04:00
struct tiny_spi_platform_data * platp = dev_get_platdata ( & pdev - > dev ) ;
2011-02-14 05:20:39 +03:00
struct tiny_spi * hw ;
struct spi_master * master ;
struct resource * res ;
unsigned int i ;
int err = - ENODEV ;
master = spi_alloc_master ( & pdev - > dev , sizeof ( struct tiny_spi ) ) ;
if ( ! master )
return err ;
/* setup the master state. */
master - > bus_num = pdev - > id ;
master - > num_chipselect = 255 ;
master - > mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH ;
master - > setup = tiny_spi_setup ;
hw = spi_master_get_devdata ( master ) ;
platform_set_drvdata ( pdev , hw ) ;
/* setup the state for the bitbang driver */
2013-09-10 11:43:41 +04:00
hw - > bitbang . master = master ;
2011-02-14 05:20:39 +03:00
hw - > bitbang . setup_transfer = tiny_spi_setup_transfer ;
hw - > bitbang . chipselect = tiny_spi_chipselect ;
hw - > bitbang . txrx_bufs = tiny_spi_txrx_bufs ;
/* find and map our resources */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-08-24 21:13:15 +04:00
hw - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( hw - > base ) ) {
err = PTR_ERR ( hw - > base ) ;
goto exit ;
}
2011-02-14 05:20:39 +03:00
/* irq is optional */
hw - > irq = platform_get_irq ( pdev , 0 ) ;
if ( hw - > irq > = 0 ) {
init_completion ( & hw - > done ) ;
err = devm_request_irq ( & pdev - > dev , hw - > irq , tiny_spi_irq , 0 ,
pdev - > name , hw ) ;
if ( err )
goto exit ;
}
/* find platform data */
if ( platp ) {
hw - > gpio_cs_count = platp - > gpio_cs_count ;
hw - > gpio_cs = platp - > gpio_cs ;
2013-08-24 21:13:15 +04:00
if ( platp - > gpio_cs_count & & ! platp - > gpio_cs ) {
err = - EBUSY ;
goto exit ;
}
2011-02-14 05:20:39 +03:00
hw - > freq = platp - > freq ;
hw - > baudwidth = platp - > baudwidth ;
} else {
err = tiny_spi_of_probe ( pdev ) ;
if ( err )
goto exit ;
}
for ( i = 0 ; i < hw - > gpio_cs_count ; i + + ) {
err = gpio_request ( hw - > gpio_cs [ i ] , dev_name ( & pdev - > dev ) ) ;
if ( err )
goto exit_gpio ;
gpio_direction_output ( hw - > gpio_cs [ i ] , 1 ) ;
}
2013-02-12 21:48:37 +04:00
hw - > bitbang . master - > num_chipselect = max ( 1 , hw - > gpio_cs_count ) ;
2011-02-14 05:20:39 +03:00
/* register our spi controller */
err = spi_bitbang_start ( & hw - > bitbang ) ;
if ( err )
goto exit ;
dev_info ( & pdev - > dev , " base %p, irq %d \n " , hw - > base , hw - > irq ) ;
return 0 ;
exit_gpio :
while ( i - - > 0 )
gpio_free ( hw - > gpio_cs [ i ] ) ;
exit :
spi_master_put ( master ) ;
return err ;
}
2012-12-07 20:57:14 +04:00
static int tiny_spi_remove ( struct platform_device * pdev )
2011-02-14 05:20:39 +03:00
{
struct tiny_spi * hw = platform_get_drvdata ( pdev ) ;
struct spi_master * master = hw - > bitbang . master ;
unsigned int i ;
spi_bitbang_stop ( & hw - > bitbang ) ;
for ( i = 0 ; i < hw - > gpio_cs_count ; i + + )
gpio_free ( hw - > gpio_cs [ i ] ) ;
spi_master_put ( master ) ;
return 0 ;
}
# ifdef CONFIG_OF
static const struct of_device_id tiny_spi_match [ ] = {
{ . compatible = " opencores,tiny-spi-rtlsvn2 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tiny_spi_match ) ;
# endif /* CONFIG_OF */
static struct platform_driver tiny_spi_driver = {
. probe = tiny_spi_probe ,
2012-12-07 20:57:14 +04:00
. remove = tiny_spi_remove ,
2011-02-14 05:20:39 +03:00
. driver = {
. name = DRV_NAME ,
. pm = NULL ,
2013-03-14 14:01:50 +04:00
. of_match_table = of_match_ptr ( tiny_spi_match ) ,
2011-02-14 05:20:39 +03:00
} ,
} ;
2011-10-05 21:29:49 +04:00
module_platform_driver ( tiny_spi_driver ) ;
2011-02-14 05:20:39 +03:00
MODULE_DESCRIPTION ( " OpenCores tiny SPI driver " ) ;
MODULE_AUTHOR ( " Thomas Chou <thomas@wytron.com.tw> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;