2018-11-29 01:28:46 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-05-03 00:11:34 +03:00
/*
* GPIO driver for NXP LPC18xx / 43 xx .
*
* Copyright ( C ) 2015 Joachim Eastwood < manabian @ gmail . com >
*
*/
# include <linux/clk.h>
# include <linux/gpio/driver.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_gpio.h>
# include <linux/pinctrl/consumer.h>
# include <linux/platform_device.h>
/* LPC18xx GPIO register offsets */
# define LPC18XX_REG_DIR(n) (0x2000 + n * sizeof(u32))
# define LPC18XX_MAX_PORTS 8
# define LPC18XX_PINS_PER_PORT 32
struct lpc18xx_gpio_chip {
struct gpio_chip gpio ;
void __iomem * base ;
struct clk * clk ;
spinlock_t lock ;
} ;
static void lpc18xx_gpio_set ( struct gpio_chip * chip , unsigned offset , int value )
{
2015-12-07 11:13:57 +03:00
struct lpc18xx_gpio_chip * gc = gpiochip_get_data ( chip ) ;
2015-05-03 00:11:34 +03:00
writeb ( value ? 1 : 0 , gc - > base + offset ) ;
}
static int lpc18xx_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
2015-12-07 11:13:57 +03:00
struct lpc18xx_gpio_chip * gc = gpiochip_get_data ( chip ) ;
2015-05-03 00:11:34 +03:00
return ! ! readb ( gc - > base + offset ) ;
}
static int lpc18xx_gpio_direction ( struct gpio_chip * chip , unsigned offset ,
bool out )
{
2015-12-07 11:13:57 +03:00
struct lpc18xx_gpio_chip * gc = gpiochip_get_data ( chip ) ;
2015-05-03 00:11:34 +03:00
unsigned long flags ;
u32 port , pin , dir ;
port = offset / LPC18XX_PINS_PER_PORT ;
pin = offset % LPC18XX_PINS_PER_PORT ;
spin_lock_irqsave ( & gc - > lock , flags ) ;
dir = readl ( gc - > base + LPC18XX_REG_DIR ( port ) ) ;
if ( out )
dir | = BIT ( pin ) ;
else
dir & = ~ BIT ( pin ) ;
writel ( dir , gc - > base + LPC18XX_REG_DIR ( port ) ) ;
spin_unlock_irqrestore ( & gc - > lock , flags ) ;
return 0 ;
}
static int lpc18xx_gpio_direction_input ( struct gpio_chip * chip ,
unsigned offset )
{
return lpc18xx_gpio_direction ( chip , offset , false ) ;
}
static int lpc18xx_gpio_direction_output ( struct gpio_chip * chip ,
unsigned offset , int value )
{
lpc18xx_gpio_set ( chip , offset , value ) ;
return lpc18xx_gpio_direction ( chip , offset , true ) ;
}
2016-09-11 15:14:37 +03:00
static const struct gpio_chip lpc18xx_chip = {
2015-05-03 00:11:34 +03:00
. label = " lpc18xx/43xx-gpio " ,
2015-10-11 18:34:16 +03:00
. request = gpiochip_generic_request ,
. free = gpiochip_generic_free ,
2015-05-03 00:11:34 +03:00
. direction_input = lpc18xx_gpio_direction_input ,
. direction_output = lpc18xx_gpio_direction_output ,
. set = lpc18xx_gpio_set ,
. get = lpc18xx_gpio_get ,
. ngpio = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT ,
. owner = THIS_MODULE ,
} ;
static int lpc18xx_gpio_probe ( struct platform_device * pdev )
{
2018-11-29 01:28:47 +03:00
struct device * dev = & pdev - > dev ;
2015-05-03 00:11:34 +03:00
struct lpc18xx_gpio_chip * gc ;
struct resource * res ;
int ret ;
2018-11-29 01:28:47 +03:00
gc = devm_kzalloc ( dev , sizeof ( * gc ) , GFP_KERNEL ) ;
2015-05-03 00:11:34 +03:00
if ( ! gc )
return - ENOMEM ;
gc - > gpio = lpc18xx_chip ;
platform_set_drvdata ( pdev , gc ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2018-11-29 01:28:47 +03:00
gc - > base = devm_ioremap_resource ( dev , res ) ;
2015-05-03 00:11:34 +03:00
if ( IS_ERR ( gc - > base ) )
return PTR_ERR ( gc - > base ) ;
2018-11-29 01:28:47 +03:00
gc - > clk = devm_clk_get ( dev , NULL ) ;
2015-05-03 00:11:34 +03:00
if ( IS_ERR ( gc - > clk ) ) {
2018-11-29 01:28:47 +03:00
dev_err ( dev , " input clock not found \n " ) ;
2015-05-03 00:11:34 +03:00
return PTR_ERR ( gc - > clk ) ;
}
ret = clk_prepare_enable ( gc - > clk ) ;
if ( ret ) {
2018-11-29 01:28:47 +03:00
dev_err ( dev , " unable to enable clock \n " ) ;
2015-05-03 00:11:34 +03:00
return ret ;
}
spin_lock_init ( & gc - > lock ) ;
2018-11-29 01:28:47 +03:00
gc - > gpio . parent = dev ;
2015-05-03 00:11:34 +03:00
2018-11-29 01:28:48 +03:00
ret = devm_gpiochip_add_data ( dev , & gc - > gpio , gc ) ;
2015-05-03 00:11:34 +03:00
if ( ret ) {
2018-11-29 01:28:47 +03:00
dev_err ( dev , " failed to add gpio chip \n " ) ;
2015-05-03 00:11:34 +03:00
clk_disable_unprepare ( gc - > clk ) ;
return ret ;
}
return 0 ;
}
static int lpc18xx_gpio_remove ( struct platform_device * pdev )
{
struct lpc18xx_gpio_chip * gc = platform_get_drvdata ( pdev ) ;
clk_disable_unprepare ( gc - > clk ) ;
return 0 ;
}
static const struct of_device_id lpc18xx_gpio_match [ ] = {
{ . compatible = " nxp,lpc1850-gpio " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , lpc18xx_gpio_match ) ;
static struct platform_driver lpc18xx_gpio_driver = {
. probe = lpc18xx_gpio_probe ,
. remove = lpc18xx_gpio_remove ,
. driver = {
. name = " lpc18xx-gpio " ,
. of_match_table = lpc18xx_gpio_match ,
} ,
} ;
module_platform_driver ( lpc18xx_gpio_driver ) ;
MODULE_AUTHOR ( " Joachim Eastwood <manabian@gmail.com> " ) ;
MODULE_DESCRIPTION ( " GPIO driver for LPC18xx/43xx " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;