2019-08-21 00:05:28 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2019 American Megatrends International LLC .
*
* Author : Karthikeyan Mani < karthikeyanm @ amiindia . co . in >
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/gpio/driver.h>
# include <linux/hashtable.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# include <linux/string.h>
# define MAX_NR_SGPIO 80
# define ASPEED_SGPIO_CTRL 0x54
# define ASPEED_SGPIO_PINS_MASK GENMASK(9, 6)
# define ASPEED_SGPIO_CLK_DIV_MASK GENMASK(31, 16)
# define ASPEED_SGPIO_ENABLE BIT(0)
struct aspeed_sgpio {
struct gpio_chip chip ;
struct clk * pclk ;
spinlock_t lock ;
void __iomem * base ;
uint32_t dir_in [ 3 ] ;
int irq ;
} ;
struct aspeed_sgpio_bank {
uint16_t val_regs ;
uint16_t rdata_reg ;
uint16_t irq_regs ;
const char names [ 4 ] [ 3 ] ;
} ;
/*
* Note : The " value " register returns the input value when the GPIO is
* configured as an input .
*
* The " rdata " register returns the output value when the GPIO is
* configured as an output .
*/
static const struct aspeed_sgpio_bank aspeed_sgpio_banks [ ] = {
{
. val_regs = 0x0000 ,
. rdata_reg = 0x0070 ,
. irq_regs = 0x0004 ,
. names = { " A " , " B " , " C " , " D " } ,
} ,
{
. val_regs = 0x001C ,
. rdata_reg = 0x0074 ,
. irq_regs = 0x0020 ,
. names = { " E " , " F " , " G " , " H " } ,
} ,
{
. val_regs = 0x0038 ,
. rdata_reg = 0x0078 ,
. irq_regs = 0x003C ,
. names = { " I " , " J " } ,
} ,
} ;
enum aspeed_sgpio_reg {
reg_val ,
reg_rdata ,
reg_irq_enable ,
reg_irq_type0 ,
reg_irq_type1 ,
reg_irq_type2 ,
reg_irq_status ,
} ;
# define GPIO_VAL_VALUE 0x00
# define GPIO_IRQ_ENABLE 0x00
# define GPIO_IRQ_TYPE0 0x04
# define GPIO_IRQ_TYPE1 0x08
# define GPIO_IRQ_TYPE2 0x0C
# define GPIO_IRQ_STATUS 0x10
static void __iomem * bank_reg ( struct aspeed_sgpio * gpio ,
const struct aspeed_sgpio_bank * bank ,
const enum aspeed_sgpio_reg reg )
{
switch ( reg ) {
case reg_val :
return gpio - > base + bank - > val_regs + GPIO_VAL_VALUE ;
case reg_rdata :
return gpio - > base + bank - > rdata_reg ;
case reg_irq_enable :
return gpio - > base + bank - > irq_regs + GPIO_IRQ_ENABLE ;
case reg_irq_type0 :
return gpio - > base + bank - > irq_regs + GPIO_IRQ_TYPE0 ;
case reg_irq_type1 :
return gpio - > base + bank - > irq_regs + GPIO_IRQ_TYPE1 ;
case reg_irq_type2 :
return gpio - > base + bank - > irq_regs + GPIO_IRQ_TYPE2 ;
case reg_irq_status :
return gpio - > base + bank - > irq_regs + GPIO_IRQ_STATUS ;
default :
/* acturally if code runs to here, it's an error case */
2019-12-10 23:28:31 +03:00
BUG ( ) ;
2019-08-21 00:05:28 +03:00
}
}
# define GPIO_BANK(x) ((x) >> 5)
# define GPIO_OFFSET(x) ((x) & 0x1f)
# define GPIO_BIT(x) BIT(GPIO_OFFSET(x))
static const struct aspeed_sgpio_bank * to_bank ( unsigned int offset )
{
unsigned int bank = GPIO_BANK ( offset ) ;
WARN_ON ( bank > = ARRAY_SIZE ( aspeed_sgpio_banks ) ) ;
return & aspeed_sgpio_banks [ bank ] ;
}
static int aspeed_sgpio_get ( struct gpio_chip * gc , unsigned int offset )
{
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
const struct aspeed_sgpio_bank * bank = to_bank ( offset ) ;
unsigned long flags ;
enum aspeed_sgpio_reg reg ;
bool is_input ;
int rc = 0 ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
is_input = gpio - > dir_in [ GPIO_BANK ( offset ) ] & GPIO_BIT ( offset ) ;
reg = is_input ? reg_val : reg_rdata ;
rc = ! ! ( ioread32 ( bank_reg ( gpio , bank , reg ) ) & GPIO_BIT ( offset ) ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
return rc ;
}
static void sgpio_set_value ( struct gpio_chip * gc , unsigned int offset , int val )
{
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
const struct aspeed_sgpio_bank * bank = to_bank ( offset ) ;
void __iomem * addr ;
u32 reg = 0 ;
addr = bank_reg ( gpio , bank , reg_val ) ;
reg = ioread32 ( addr ) ;
if ( val )
reg | = GPIO_BIT ( offset ) ;
else
reg & = ~ GPIO_BIT ( offset ) ;
iowrite32 ( reg , addr ) ;
}
static void aspeed_sgpio_set ( struct gpio_chip * gc , unsigned int offset , int val )
{
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
sgpio_set_value ( gc , offset , val ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
}
static int aspeed_sgpio_dir_in ( struct gpio_chip * gc , unsigned int offset )
{
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
gpio - > dir_in [ GPIO_BANK ( offset ) ] | = GPIO_BIT ( offset ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
return 0 ;
}
static int aspeed_sgpio_dir_out ( struct gpio_chip * gc , unsigned int offset , int val )
{
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
gpio - > dir_in [ GPIO_BANK ( offset ) ] & = ~ GPIO_BIT ( offset ) ;
sgpio_set_value ( gc , offset , val ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
return 0 ;
}
static int aspeed_sgpio_get_direction ( struct gpio_chip * gc , unsigned int offset )
{
int dir_status ;
struct aspeed_sgpio * gpio = gpiochip_get_data ( gc ) ;
unsigned long flags ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
dir_status = gpio - > dir_in [ GPIO_BANK ( offset ) ] & GPIO_BIT ( offset ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
return dir_status ;
}
static void irqd_to_aspeed_sgpio_data ( struct irq_data * d ,
struct aspeed_sgpio * * gpio ,
const struct aspeed_sgpio_bank * * bank ,
u32 * bit , int * offset )
{
struct aspeed_sgpio * internal ;
* offset = irqd_to_hwirq ( d ) ;
internal = irq_data_get_irq_chip_data ( d ) ;
WARN_ON ( ! internal ) ;
* gpio = internal ;
* bank = to_bank ( * offset ) ;
* bit = GPIO_BIT ( * offset ) ;
}
static void aspeed_sgpio_irq_ack ( struct irq_data * d )
{
const struct aspeed_sgpio_bank * bank ;
struct aspeed_sgpio * gpio ;
unsigned long flags ;
void __iomem * status_addr ;
int offset ;
u32 bit ;
irqd_to_aspeed_sgpio_data ( d , & gpio , & bank , & bit , & offset ) ;
status_addr = bank_reg ( gpio , bank , reg_irq_status ) ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
iowrite32 ( bit , status_addr ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
}
static void aspeed_sgpio_irq_set_mask ( struct irq_data * d , bool set )
{
const struct aspeed_sgpio_bank * bank ;
struct aspeed_sgpio * gpio ;
unsigned long flags ;
u32 reg , bit ;
void __iomem * addr ;
int offset ;
irqd_to_aspeed_sgpio_data ( d , & gpio , & bank , & bit , & offset ) ;
addr = bank_reg ( gpio , bank , reg_irq_enable ) ;
spin_lock_irqsave ( & gpio - > lock , flags ) ;
reg = ioread32 ( addr ) ;
if ( set )
reg | = bit ;
else
reg & = ~ bit ;
iowrite32 ( reg , addr ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
}
static void aspeed_sgpio_irq_mask ( struct irq_data * d )
{
aspeed_sgpio_irq_set_mask ( d , false ) ;
}
static void aspeed_sgpio_irq_unmask ( struct irq_data * d )
{
aspeed_sgpio_irq_set_mask ( d , true ) ;
}
static int aspeed_sgpio_set_type ( struct irq_data * d , unsigned int type )
{
u32 type0 = 0 ;
u32 type1 = 0 ;
u32 type2 = 0 ;
u32 bit , reg ;
const struct aspeed_sgpio_bank * bank ;
irq_flow_handler_t handler ;
struct aspeed_sgpio * gpio ;
unsigned long flags ;
void __iomem * addr ;
int offset ;
irqd_to_aspeed_sgpio_data ( d , & gpio , & bank , & bit , & offset ) ;
switch ( type & IRQ_TYPE_SENSE_MASK ) {
case IRQ_TYPE_EDGE_BOTH :
type2 | = bit ;
/* fall through */
case IRQ_TYPE_EDGE_RISING :
type0 | = bit ;
/* fall through */
case IRQ_TYPE_EDGE_FALLING :
handler = handle_edge_irq ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
type0 | = bit ;
/* fall through */
case IRQ_TYPE_LEVEL_LOW :
type1 | = bit ;
handler = handle_level_irq ;
break ;
default :
return - EINVAL ;
}
spin_lock_irqsave ( & gpio - > lock , flags ) ;
addr = bank_reg ( gpio , bank , reg_irq_type0 ) ;
reg = ioread32 ( addr ) ;
reg = ( reg & ~ bit ) | type0 ;
iowrite32 ( reg , addr ) ;
addr = bank_reg ( gpio , bank , reg_irq_type1 ) ;
reg = ioread32 ( addr ) ;
reg = ( reg & ~ bit ) | type1 ;
iowrite32 ( reg , addr ) ;
addr = bank_reg ( gpio , bank , reg_irq_type2 ) ;
reg = ioread32 ( addr ) ;
reg = ( reg & ~ bit ) | type2 ;
iowrite32 ( reg , addr ) ;
spin_unlock_irqrestore ( & gpio - > lock , flags ) ;
irq_set_handler_locked ( d , handler ) ;
return 0 ;
}
static void aspeed_sgpio_irq_handler ( struct irq_desc * desc )
{
struct gpio_chip * gc = irq_desc_get_handler_data ( desc ) ;
struct irq_chip * ic = irq_desc_get_chip ( desc ) ;
struct aspeed_sgpio * data = gpiochip_get_data ( gc ) ;
unsigned int i , p , girq ;
unsigned long reg ;
chained_irq_enter ( ic , desc ) ;
for ( i = 0 ; i < ARRAY_SIZE ( aspeed_sgpio_banks ) ; i + + ) {
const struct aspeed_sgpio_bank * bank = & aspeed_sgpio_banks [ i ] ;
reg = ioread32 ( bank_reg ( data , bank , reg_irq_status ) ) ;
for_each_set_bit ( p , & reg , 32 ) {
girq = irq_find_mapping ( gc - > irq . domain , i * 32 + p ) ;
generic_handle_irq ( girq ) ;
}
}
chained_irq_exit ( ic , desc ) ;
}
static struct irq_chip aspeed_sgpio_irqchip = {
. name = " aspeed-sgpio " ,
. irq_ack = aspeed_sgpio_irq_ack ,
. irq_mask = aspeed_sgpio_irq_mask ,
. irq_unmask = aspeed_sgpio_irq_unmask ,
. irq_set_type = aspeed_sgpio_set_type ,
} ;
static int aspeed_sgpio_setup_irqs ( struct aspeed_sgpio * gpio ,
struct platform_device * pdev )
{
int rc , i ;
const struct aspeed_sgpio_bank * bank ;
struct gpio_irq_chip * irq ;
rc = platform_get_irq ( pdev , 0 ) ;
if ( rc < 0 )
return rc ;
gpio - > irq = rc ;
2020-01-18 13:53:19 +03:00
/* Disable IRQ and clear Interrupt status registers for all SGPIO Pins. */
2019-08-21 00:05:28 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( aspeed_sgpio_banks ) ; i + + ) {
bank = & aspeed_sgpio_banks [ i ] ;
/* disable irq enable bits */
iowrite32 ( 0x00000000 , bank_reg ( gpio , bank , reg_irq_enable ) ) ;
/* clear status bits */
iowrite32 ( 0xffffffff , bank_reg ( gpio , bank , reg_irq_status ) ) ;
}
irq = & gpio - > chip . irq ;
irq - > chip = & aspeed_sgpio_irqchip ;
irq - > handler = handle_bad_irq ;
irq - > default_type = IRQ_TYPE_NONE ;
irq - > parent_handler = aspeed_sgpio_irq_handler ;
irq - > parent_handler_data = gpio ;
irq - > parents = & gpio - > irq ;
irq - > num_parents = 1 ;
/* set IRQ settings and Enable Interrupt */
for ( i = 0 ; i < ARRAY_SIZE ( aspeed_sgpio_banks ) ; i + + ) {
bank = & aspeed_sgpio_banks [ i ] ;
/* set falling or level-low irq */
iowrite32 ( 0x00000000 , bank_reg ( gpio , bank , reg_irq_type0 ) ) ;
/* trigger type is edge */
iowrite32 ( 0x00000000 , bank_reg ( gpio , bank , reg_irq_type1 ) ) ;
/* dual edge trigger mode. */
iowrite32 ( 0xffffffff , bank_reg ( gpio , bank , reg_irq_type2 ) ) ;
/* enable irq */
iowrite32 ( 0xffffffff , bank_reg ( gpio , bank , reg_irq_enable ) ) ;
}
return 0 ;
}
static const struct of_device_id aspeed_sgpio_of_table [ ] = {
{ . compatible = " aspeed,ast2400-sgpio " } ,
{ . compatible = " aspeed,ast2500-sgpio " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , aspeed_sgpio_of_table ) ;
static int __init aspeed_sgpio_probe ( struct platform_device * pdev )
{
struct aspeed_sgpio * gpio ;
u32 nr_gpios , sgpio_freq , sgpio_clk_div ;
int rc ;
unsigned long apb_freq ;
gpio = devm_kzalloc ( & pdev - > dev , sizeof ( * gpio ) , GFP_KERNEL ) ;
if ( ! gpio )
return - ENOMEM ;
gpio - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( gpio - > base ) )
return PTR_ERR ( gpio - > base ) ;
rc = of_property_read_u32 ( pdev - > dev . of_node , " ngpios " , & nr_gpios ) ;
if ( rc < 0 ) {
dev_err ( & pdev - > dev , " Could not read ngpios property \n " ) ;
return - EINVAL ;
} else if ( nr_gpios > MAX_NR_SGPIO ) {
dev_err ( & pdev - > dev , " Number of GPIOs exceeds the maximum of %d: %d \n " ,
MAX_NR_SGPIO , nr_gpios ) ;
return - EINVAL ;
}
rc = of_property_read_u32 ( pdev - > dev . of_node , " bus-frequency " , & sgpio_freq ) ;
if ( rc < 0 ) {
dev_err ( & pdev - > dev , " Could not read bus-frequency property \n " ) ;
return - EINVAL ;
}
gpio - > pclk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( gpio - > pclk ) ) {
dev_err ( & pdev - > dev , " devm_clk_get failed \n " ) ;
return PTR_ERR ( gpio - > pclk ) ;
}
apb_freq = clk_get_rate ( gpio - > pclk ) ;
/*
* From the datasheet ,
* SGPIO period = 1 / PCLK * 2 * ( GPIO254 [ 31 : 16 ] + 1 )
* period = 2 * ( GPIO254 [ 31 : 16 ] + 1 ) / PCLK
* frequency = 1 / ( 2 * ( GPIO254 [ 31 : 16 ] + 1 ) / PCLK )
* frequency = PCLK / ( 2 * ( GPIO254 [ 31 : 16 ] + 1 ) )
* frequency * 2 * ( GPIO254 [ 31 : 16 ] + 1 ) = PCLK
* GPIO254 [ 31 : 16 ] = PCLK / ( frequency * 2 ) - 1
*/
if ( sgpio_freq = = 0 )
return - EINVAL ;
sgpio_clk_div = ( apb_freq / ( sgpio_freq * 2 ) ) - 1 ;
if ( sgpio_clk_div > ( 1 < < 16 ) - 1 )
return - EINVAL ;
iowrite32 ( FIELD_PREP ( ASPEED_SGPIO_CLK_DIV_MASK , sgpio_clk_div ) |
FIELD_PREP ( ASPEED_SGPIO_PINS_MASK , ( nr_gpios / 8 ) ) |
ASPEED_SGPIO_ENABLE ,
gpio - > base + ASPEED_SGPIO_CTRL ) ;
spin_lock_init ( & gpio - > lock ) ;
gpio - > chip . parent = & pdev - > dev ;
gpio - > chip . ngpio = nr_gpios ;
gpio - > chip . direction_input = aspeed_sgpio_dir_in ;
gpio - > chip . direction_output = aspeed_sgpio_dir_out ;
gpio - > chip . get_direction = aspeed_sgpio_get_direction ;
gpio - > chip . request = NULL ;
gpio - > chip . free = NULL ;
gpio - > chip . get = aspeed_sgpio_get ;
gpio - > chip . set = aspeed_sgpio_set ;
gpio - > chip . set_config = NULL ;
gpio - > chip . label = dev_name ( & pdev - > dev ) ;
gpio - > chip . base = - 1 ;
/* set all SGPIO pins as input (1). */
memset ( gpio - > dir_in , 0xff , sizeof ( gpio - > dir_in ) ) ;
aspeed_sgpio_setup_irqs ( gpio , pdev ) ;
rc = devm_gpiochip_add_data ( & pdev - > dev , & gpio - > chip , gpio ) ;
if ( rc < 0 )
return rc ;
return 0 ;
}
static struct platform_driver aspeed_sgpio_driver = {
. driver = {
. name = KBUILD_MODNAME ,
. of_match_table = aspeed_sgpio_of_table ,
} ,
} ;
module_platform_driver_probe ( aspeed_sgpio_driver , aspeed_sgpio_probe ) ;
MODULE_DESCRIPTION ( " Aspeed Serial GPIO Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;