2022-07-20 16:45:59 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Intel 8255 Programmable Peripheral Interface
* Copyright ( C ) 2022 William Breathitt Gray
*/
2022-12-27 17:09:46 +03:00
# include <linux/bits.h>
2022-12-27 17:09:43 +03:00
# include <linux/device.h>
2022-07-20 16:45:59 +03:00
# include <linux/err.h>
# include <linux/export.h>
2022-12-27 17:09:43 +03:00
# include <linux/gpio/regmap.h>
2022-07-20 16:45:59 +03:00
# include <linux/module.h>
2022-12-27 17:09:43 +03:00
# include <linux/regmap.h>
2022-07-20 16:45:59 +03:00
# include "gpio-i8255.h"
2022-12-27 17:09:43 +03:00
# define I8255_NGPIO 24
# define I8255_NGPIO_PER_REG 8
2022-07-20 16:45:59 +03:00
# define I8255_CONTROL_PORTC_LOWER_DIRECTION BIT(0)
# define I8255_CONTROL_PORTB_DIRECTION BIT(1)
# define I8255_CONTROL_PORTC_UPPER_DIRECTION BIT(3)
# define I8255_CONTROL_PORTA_DIRECTION BIT(4)
# define I8255_CONTROL_MODE_SET BIT(7)
2022-12-27 17:09:43 +03:00
# define I8255_PORTA 0x0
# define I8255_PORTB 0x1
# define I8255_PORTC 0x2
# define I8255_CONTROL 0x3
# define I8255_REG_DAT_BASE I8255_PORTA
# define I8255_REG_DIR_IN_BASE I8255_CONTROL
2022-07-20 16:45:59 +03:00
2022-12-27 17:09:43 +03:00
static int i8255_direction_mask ( const unsigned int offset )
2022-07-20 16:45:59 +03:00
{
2022-12-27 17:09:43 +03:00
const unsigned int stride = offset / I8255_NGPIO_PER_REG ;
const unsigned int line = offset % I8255_NGPIO_PER_REG ;
2022-07-20 16:45:59 +03:00
2022-12-27 17:09:43 +03:00
switch ( stride ) {
2022-07-20 16:45:59 +03:00
case I8255_PORTA :
return I8255_CONTROL_PORTA_DIRECTION ;
case I8255_PORTB :
return I8255_CONTROL_PORTB_DIRECTION ;
case I8255_PORTC :
/* Port C can be configured by nibble */
2022-12-27 17:09:43 +03:00
if ( line > = 4 )
2022-07-20 16:45:59 +03:00
return I8255_CONTROL_PORTC_UPPER_DIRECTION ;
return I8255_CONTROL_PORTC_LOWER_DIRECTION ;
default :
/* Should never reach this path */
return 0 ;
}
}
2022-12-27 17:09:43 +03:00
static int i8255_ppi_init ( struct regmap * const map , const unsigned int base )
{
int err ;
/* Configure all ports to MODE 0 output mode */
err = regmap_write ( map , base + I8255_CONTROL , I8255_CONTROL_MODE_SET ) ;
if ( err )
return err ;
/* Initialize all GPIO to output 0 */
err = regmap_write ( map , base + I8255_PORTA , 0x00 ) ;
if ( err )
return err ;
err = regmap_write ( map , base + I8255_PORTB , 0x00 ) ;
if ( err )
return err ;
return regmap_write ( map , base + I8255_PORTC , 0x00 ) ;
}
static int i8255_reg_mask_xlate ( struct gpio_regmap * gpio , unsigned int base ,
unsigned int offset , unsigned int * reg ,
unsigned int * mask )
{
const unsigned int ppi = offset / I8255_NGPIO ;
const unsigned int ppi_offset = offset % I8255_NGPIO ;
const unsigned int stride = ppi_offset / I8255_NGPIO_PER_REG ;
const unsigned int line = ppi_offset % I8255_NGPIO_PER_REG ;
switch ( base ) {
case I8255_REG_DAT_BASE :
* reg = base + stride + ppi * 4 ;
* mask = BIT ( line ) ;
return 0 ;
case I8255_REG_DIR_IN_BASE :
* reg = base + ppi * 4 ;
* mask = i8255_direction_mask ( ppi_offset ) ;
return 0 ;
default :
/* Should never reach this path */
return - EINVAL ;
}
}
/**
* devm_i8255_regmap_register - Register an i8255 GPIO controller
* @ dev : device that is registering this i8255 GPIO device
* @ config : configuration for i8255_regmap_config
*
* Registers an Intel 8255 Programmable Peripheral Interface GPIO controller .
* Returns 0 on success and negative error number on failure .
*/
int devm_i8255_regmap_register ( struct device * const dev ,
const struct i8255_regmap_config * const config )
{
struct gpio_regmap_config gpio_config = { 0 } ;
unsigned long i ;
int err ;
if ( ! config - > parent )
return - EINVAL ;
if ( ! config - > map )
return - EINVAL ;
if ( ! config - > num_ppi )
return - EINVAL ;
for ( i = 0 ; i < config - > num_ppi ; i + + ) {
err = i8255_ppi_init ( config - > map , i * 4 ) ;
if ( err )
return err ;
}
gpio_config . parent = config - > parent ;
gpio_config . regmap = config - > map ;
gpio_config . ngpio = I8255_NGPIO * config - > num_ppi ;
gpio_config . names = config - > names ;
gpio_config . reg_dat_base = GPIO_REGMAP_ADDR ( I8255_REG_DAT_BASE ) ;
gpio_config . reg_set_base = GPIO_REGMAP_ADDR ( I8255_REG_DAT_BASE ) ;
gpio_config . reg_dir_in_base = GPIO_REGMAP_ADDR ( I8255_REG_DIR_IN_BASE ) ;
gpio_config . ngpio_per_reg = I8255_NGPIO_PER_REG ;
gpio_config . irq_domain = config - > domain ;
gpio_config . reg_mask_xlate = i8255_reg_mask_xlate ;
return PTR_ERR_OR_ZERO ( devm_gpio_regmap_register ( dev , & gpio_config ) ) ;
}
EXPORT_SYMBOL_NS_GPL ( devm_i8255_regmap_register , I8255 ) ;
2022-07-20 16:45:59 +03:00
MODULE_AUTHOR ( " William Breathitt Gray " ) ;
MODULE_DESCRIPTION ( " Intel 8255 Programmable Peripheral Interface " ) ;
MODULE_LICENSE ( " GPL " ) ;