2016-07-20 16:11:36 +02:00
/*
2017-12-05 15:46:41 +01:00
* AXP20x pinctrl and GPIO driver
2016-07-20 16:11:36 +02:00
*
* Copyright ( C ) 2016 Maxime Ripard < maxime . ripard @ free - electrons . com >
2017-12-05 15:46:41 +01:00
* Copyright ( C ) 2017 Quentin Schulz < quentin . schulz @ free - electrons . com >
2016-07-20 16:11:36 +02:00
*
* 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 ; either version 2 of the License , or ( at your
* option ) any later version .
*/
# include <linux/bitops.h>
# include <linux/device.h>
# include <linux/gpio/driver.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/mfd/axp20x.h>
# include <linux/module.h>
# include <linux/of.h>
2017-12-05 15:46:46 +01:00
# include <linux/of_device.h>
2017-12-05 15:46:41 +01:00
# include <linux/pinctrl/pinconf-generic.h>
# include <linux/pinctrl/pinctrl.h>
# include <linux/pinctrl/pinmux.h>
2016-07-20 16:11:36 +02:00
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# define AXP20X_GPIO_FUNCTIONS 0x7
# define AXP20X_GPIO_FUNCTION_OUT_LOW 0
# define AXP20X_GPIO_FUNCTION_OUT_HIGH 1
# define AXP20X_GPIO_FUNCTION_INPUT 2
2017-12-05 15:46:41 +01:00
# define AXP20X_FUNC_GPIO_OUT 0
# define AXP20X_FUNC_GPIO_IN 1
# define AXP20X_FUNC_LDO 2
# define AXP20X_FUNC_ADC 3
# define AXP20X_FUNCS_NB 4
# define AXP20X_MUX_GPIO_OUT 0
# define AXP20X_MUX_GPIO_IN BIT(1)
# define AXP20X_MUX_ADC BIT(2)
2017-12-05 15:46:46 +01:00
# define AXP813_MUX_ADC (BIT(2) | BIT(0))
2017-12-05 15:46:41 +01:00
struct axp20x_pctrl_desc {
const struct pinctrl_pin_desc * pins ;
unsigned int npins ;
/* Stores the pins supporting LDO function. Bit offset is pin number. */
u8 ldo_mask ;
/* Stores the pins supporting ADC function. Bit offset is pin number. */
u8 adc_mask ;
2017-12-05 15:46:44 +01:00
u8 gpio_status_offset ;
2017-12-05 15:46:45 +01:00
u8 adc_mux ;
2017-12-05 15:46:41 +01:00
} ;
struct axp20x_pinctrl_function {
const char * name ;
unsigned int muxval ;
const char * * groups ;
unsigned int ngroups ;
} ;
2017-12-05 15:46:43 +01:00
struct axp20x_pctl {
2016-07-20 16:11:36 +02:00
struct gpio_chip chip ;
struct regmap * regmap ;
2017-12-05 15:46:41 +01:00
struct pinctrl_dev * pctl_dev ;
struct device * dev ;
const struct axp20x_pctrl_desc * desc ;
struct axp20x_pinctrl_function funcs [ AXP20X_FUNCS_NB ] ;
} ;
static const struct pinctrl_pin_desc axp209_pins [ ] = {
PINCTRL_PIN ( 0 , " GPIO0 " ) ,
PINCTRL_PIN ( 1 , " GPIO1 " ) ,
PINCTRL_PIN ( 2 , " GPIO2 " ) ,
} ;
2017-12-05 15:46:46 +01:00
static const struct pinctrl_pin_desc axp813_pins [ ] = {
PINCTRL_PIN ( 0 , " GPIO0 " ) ,
PINCTRL_PIN ( 1 , " GPIO1 " ) ,
} ;
2017-12-05 15:46:41 +01:00
static const struct axp20x_pctrl_desc axp20x_data = {
. pins = axp209_pins ,
. npins = ARRAY_SIZE ( axp209_pins ) ,
. ldo_mask = BIT ( 0 ) | BIT ( 1 ) ,
. adc_mask = BIT ( 0 ) | BIT ( 1 ) ,
2017-12-05 15:46:44 +01:00
. gpio_status_offset = 4 ,
2017-12-05 15:46:45 +01:00
. adc_mux = AXP20X_MUX_ADC ,
2016-07-20 16:11:36 +02:00
} ;
2017-12-05 15:46:46 +01:00
static const struct axp20x_pctrl_desc axp813_data = {
. pins = axp813_pins ,
. npins = ARRAY_SIZE ( axp813_pins ) ,
. ldo_mask = BIT ( 0 ) | BIT ( 1 ) ,
. adc_mask = BIT ( 0 ) ,
. gpio_status_offset = 0 ,
. adc_mux = AXP813_MUX_ADC ,
} ;
2017-12-05 15:46:39 +01:00
static int axp20x_gpio_get_reg ( unsigned int offset )
2016-07-20 16:11:36 +02:00
{
switch ( offset ) {
case 0 :
return AXP20X_GPIO0_CTRL ;
case 1 :
return AXP20X_GPIO1_CTRL ;
case 2 :
return AXP20X_GPIO2_CTRL ;
}
return - EINVAL ;
}
2017-12-05 15:46:39 +01:00
static int axp20x_gpio_input ( struct gpio_chip * chip , unsigned int offset )
2016-07-20 16:11:36 +02:00
{
2017-12-05 15:46:41 +01:00
return pinctrl_gpio_direction_input ( chip - > base + offset ) ;
2016-07-20 16:11:36 +02:00
}
2017-12-05 15:46:39 +01:00
static int axp20x_gpio_get ( struct gpio_chip * chip , unsigned int offset )
2016-07-20 16:11:36 +02:00
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = gpiochip_get_data ( chip ) ;
2016-07-20 16:11:36 +02:00
unsigned int val ;
2016-11-23 15:11:50 +01:00
int ret ;
2016-07-20 16:11:36 +02:00
2017-12-05 15:46:43 +01:00
ret = regmap_read ( pctl - > regmap , AXP20X_GPIO20_SS , & val ) ;
2016-07-20 16:11:36 +02:00
if ( ret )
return ret ;
2017-12-05 15:46:44 +01:00
return ! ! ( val & BIT ( offset + pctl - > desc - > gpio_status_offset ) ) ;
2016-07-20 16:11:36 +02:00
}
2017-12-05 15:46:39 +01:00
static int axp20x_gpio_get_direction ( struct gpio_chip * chip ,
unsigned int offset )
2016-09-21 23:51:22 +03:00
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = gpiochip_get_data ( chip ) ;
2016-09-21 23:51:22 +03:00
unsigned int val ;
int reg , ret ;
reg = axp20x_gpio_get_reg ( offset ) ;
if ( reg < 0 )
return reg ;
2017-12-05 15:46:43 +01:00
ret = regmap_read ( pctl - > regmap , reg , & val ) ;
2016-09-21 23:51:22 +03:00
if ( ret )
return ret ;
/*
* This shouldn ' t really happen if the pin is in use already ,
* or if it ' s not in use yet , it doesn ' t matter since we ' re
* going to change the value soon anyway . Default to output .
*/
if ( ( val & AXP20X_GPIO_FUNCTIONS ) > 2 )
return 0 ;
/*
* The GPIO directions are the three lowest values .
* 2 is input , 0 and 1 are output
*/
return val & 2 ;
}
2017-12-05 15:46:39 +01:00
static int axp20x_gpio_output ( struct gpio_chip * chip , unsigned int offset ,
2016-07-20 16:11:36 +02:00
int value )
2017-12-05 15:46:41 +01:00
{
chip - > set ( chip , offset , value ) ;
return 0 ;
}
static void axp20x_gpio_set ( struct gpio_chip * chip , unsigned int offset ,
int value )
2016-07-20 16:11:36 +02:00
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = gpiochip_get_data ( chip ) ;
2016-07-20 16:11:36 +02:00
int reg ;
2017-12-05 15:46:41 +01:00
reg = axp20x_gpio_get_reg ( offset ) ;
if ( reg < 0 )
return ;
2017-12-05 15:46:43 +01:00
regmap_update_bits ( pctl - > regmap , reg ,
2017-12-05 15:46:41 +01:00
AXP20X_GPIO_FUNCTIONS ,
value ? AXP20X_GPIO_FUNCTION_OUT_HIGH :
AXP20X_GPIO_FUNCTION_OUT_LOW ) ;
}
static int axp20x_pmx_set ( struct pinctrl_dev * pctldev , unsigned int offset ,
u8 config )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
int reg ;
2016-07-20 16:11:36 +02:00
reg = axp20x_gpio_get_reg ( offset ) ;
if ( reg < 0 )
return reg ;
2017-12-05 15:46:43 +01:00
return regmap_update_bits ( pctl - > regmap , reg , AXP20X_GPIO_FUNCTIONS ,
2017-12-05 15:46:41 +01:00
config ) ;
2016-07-20 16:11:36 +02:00
}
2017-12-05 15:46:41 +01:00
static int axp20x_pmx_func_cnt ( struct pinctrl_dev * pctldev )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
return ARRAY_SIZE ( pctl - > funcs ) ;
2017-12-05 15:46:41 +01:00
}
static const char * axp20x_pmx_func_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
return pctl - > funcs [ selector ] . name ;
2017-12-05 15:46:41 +01:00
}
static int axp20x_pmx_func_groups ( struct pinctrl_dev * pctldev ,
unsigned int selector ,
const char * const * * groups ,
unsigned int * num_groups )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
* groups = pctl - > funcs [ selector ] . groups ;
* num_groups = pctl - > funcs [ selector ] . ngroups ;
2017-12-05 15:46:41 +01:00
return 0 ;
}
static int axp20x_pmx_set_mux ( struct pinctrl_dev * pctldev ,
unsigned int function , unsigned int group )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
unsigned int mask ;
/* Every pin supports GPIO_OUT and GPIO_IN functions */
if ( function < = AXP20X_FUNC_GPIO_IN )
return axp20x_pmx_set ( pctldev , group ,
2017-12-05 15:46:43 +01:00
pctl - > funcs [ function ] . muxval ) ;
2017-12-05 15:46:41 +01:00
if ( function = = AXP20X_FUNC_LDO )
2017-12-05 15:46:43 +01:00
mask = pctl - > desc - > ldo_mask ;
2017-12-05 15:46:41 +01:00
else
2017-12-05 15:46:43 +01:00
mask = pctl - > desc - > adc_mask ;
2017-12-05 15:46:41 +01:00
if ( ! ( BIT ( group ) & mask ) )
return - EINVAL ;
/*
* We let the regulator framework handle the LDO muxing as muxing bits
* are basically also regulators on / off bits . It ' s better not to enforce
* any state of the regulator when selecting LDO mux so that we don ' t
* interfere with the regulator driver .
*/
if ( function = = AXP20X_FUNC_LDO )
return 0 ;
2017-12-05 15:46:43 +01:00
return axp20x_pmx_set ( pctldev , group , pctl - > funcs [ function ] . muxval ) ;
2017-12-05 15:46:41 +01:00
}
static int axp20x_pmx_gpio_set_direction ( struct pinctrl_dev * pctldev ,
struct pinctrl_gpio_range * range ,
unsigned int offset , bool input )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
if ( input )
return axp20x_pmx_set ( pctldev , offset ,
2017-12-05 15:46:43 +01:00
pctl - > funcs [ AXP20X_FUNC_GPIO_IN ] . muxval ) ;
2017-12-05 15:46:41 +01:00
return axp20x_pmx_set ( pctldev , offset ,
2017-12-05 15:46:43 +01:00
pctl - > funcs [ AXP20X_FUNC_GPIO_OUT ] . muxval ) ;
2017-12-05 15:46:41 +01:00
}
static const struct pinmux_ops axp20x_pmx_ops = {
. get_functions_count = axp20x_pmx_func_cnt ,
. get_function_name = axp20x_pmx_func_name ,
. get_function_groups = axp20x_pmx_func_groups ,
. set_mux = axp20x_pmx_set_mux ,
. gpio_set_direction = axp20x_pmx_gpio_set_direction ,
. strict = true ,
} ;
static int axp20x_groups_cnt ( struct pinctrl_dev * pctldev )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
return pctl - > desc - > npins ;
2017-12-05 15:46:41 +01:00
}
static int axp20x_group_pins ( struct pinctrl_dev * pctldev , unsigned int selector ,
const unsigned int * * pins , unsigned int * num_pins )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
* pins = ( unsigned int * ) & pctl - > desc - > pins [ selector ] ;
2017-12-05 15:46:41 +01:00
* num_pins = 1 ;
return 0 ;
}
static const char * axp20x_group_name ( struct pinctrl_dev * pctldev ,
unsigned int selector )
2016-07-20 16:11:36 +02:00
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = pinctrl_dev_get_drvdata ( pctldev ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
return pctl - > desc - > pins [ selector ] . name ;
2017-12-05 15:46:41 +01:00
}
static const struct pinctrl_ops axp20x_pctrl_ops = {
. dt_node_to_map = pinconf_generic_dt_node_to_map_group ,
. dt_free_map = pinconf_generic_dt_free_map ,
. get_groups_count = axp20x_groups_cnt ,
. get_group_name = axp20x_group_name ,
. get_group_pins = axp20x_group_pins ,
} ;
static void axp20x_funcs_groups_from_mask ( struct device * dev , unsigned int mask ,
unsigned int mask_len ,
struct axp20x_pinctrl_function * func ,
const struct pinctrl_pin_desc * pins )
{
unsigned long int mask_cpy = mask ;
const char * * group ;
unsigned int ngroups = hweight8 ( mask ) ;
int bit ;
func - > ngroups = ngroups ;
if ( func - > ngroups > 0 ) {
func - > groups = devm_kzalloc ( dev , ngroups * sizeof ( const char * ) ,
GFP_KERNEL ) ;
group = func - > groups ;
for_each_set_bit ( bit , & mask_cpy , mask_len ) {
* group = pins [ bit ] . name ;
group + + ;
}
}
}
static void axp20x_build_funcs_groups ( struct platform_device * pdev )
{
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl = platform_get_drvdata ( pdev ) ;
int i , pin , npins = pctl - > desc - > npins ;
pctl - > funcs [ AXP20X_FUNC_GPIO_OUT ] . name = " gpio_out " ;
pctl - > funcs [ AXP20X_FUNC_GPIO_OUT ] . muxval = AXP20X_MUX_GPIO_OUT ;
pctl - > funcs [ AXP20X_FUNC_GPIO_IN ] . name = " gpio_in " ;
pctl - > funcs [ AXP20X_FUNC_GPIO_IN ] . muxval = AXP20X_MUX_GPIO_IN ;
pctl - > funcs [ AXP20X_FUNC_LDO ] . name = " ldo " ;
2017-12-05 15:46:41 +01:00
/*
* Muxval for LDO is useless as we won ' t use it .
* See comment in axp20x_pmx_set_mux .
*/
2017-12-05 15:46:43 +01:00
pctl - > funcs [ AXP20X_FUNC_ADC ] . name = " adc " ;
2017-12-05 15:46:45 +01:00
pctl - > funcs [ AXP20X_FUNC_ADC ] . muxval = pctl - > desc - > adc_mux ;
2017-12-05 15:46:41 +01:00
/* Every pin supports GPIO_OUT and GPIO_IN functions */
for ( i = 0 ; i < = AXP20X_FUNC_GPIO_IN ; i + + ) {
2017-12-05 15:46:43 +01:00
pctl - > funcs [ i ] . ngroups = npins ;
pctl - > funcs [ i ] . groups = devm_kzalloc ( & pdev - > dev ,
2017-12-05 15:46:41 +01:00
npins * sizeof ( char * ) ,
GFP_KERNEL ) ;
for ( pin = 0 ; pin < npins ; pin + + )
2017-12-05 15:46:43 +01:00
pctl - > funcs [ i ] . groups [ pin ] = pctl - > desc - > pins [ pin ] . name ;
2017-12-05 15:46:41 +01:00
}
2017-12-05 15:46:43 +01:00
axp20x_funcs_groups_from_mask ( & pdev - > dev , pctl - > desc - > ldo_mask ,
npins , & pctl - > funcs [ AXP20X_FUNC_LDO ] ,
pctl - > desc - > pins ) ;
2017-12-05 15:46:41 +01:00
2017-12-05 15:46:43 +01:00
axp20x_funcs_groups_from_mask ( & pdev - > dev , pctl - > desc - > adc_mask ,
npins , & pctl - > funcs [ AXP20X_FUNC_ADC ] ,
pctl - > desc - > pins ) ;
2016-07-20 16:11:36 +02:00
}
2017-12-05 15:46:46 +01:00
static const struct of_device_id axp20x_pctl_match [ ] = {
{ . compatible = " x-powers,axp209-gpio " , . data = & axp20x_data , } ,
{ . compatible = " x-powers,axp813-gpio " , . data = & axp813_data , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , axp20x_pctl_match ) ;
2017-12-05 15:46:43 +01:00
static int axp20x_pctl_probe ( struct platform_device * pdev )
2016-07-20 16:11:36 +02:00
{
struct axp20x_dev * axp20x = dev_get_drvdata ( pdev - > dev . parent ) ;
2017-12-05 15:46:43 +01:00
struct axp20x_pctl * pctl ;
2017-12-05 15:46:46 +01:00
struct device * dev = & pdev - > dev ;
2017-12-05 15:46:41 +01:00
struct pinctrl_desc * pctrl_desc ;
2016-07-20 16:11:36 +02:00
int ret ;
if ( ! of_device_is_available ( pdev - > dev . of_node ) )
return - ENODEV ;
if ( ! axp20x ) {
dev_err ( & pdev - > dev , " Parent drvdata not set \n " ) ;
return - EINVAL ;
}
2017-12-05 15:46:43 +01:00
pctl = devm_kzalloc ( & pdev - > dev , sizeof ( * pctl ) , GFP_KERNEL ) ;
if ( ! pctl )
2016-07-20 16:11:36 +02:00
return - ENOMEM ;
2017-12-05 15:46:43 +01:00
pctl - > chip . base = - 1 ;
pctl - > chip . can_sleep = true ;
pctl - > chip . request = gpiochip_generic_request ;
pctl - > chip . free = gpiochip_generic_free ;
pctl - > chip . parent = & pdev - > dev ;
pctl - > chip . label = dev_name ( & pdev - > dev ) ;
pctl - > chip . owner = THIS_MODULE ;
pctl - > chip . get = axp20x_gpio_get ;
pctl - > chip . get_direction = axp20x_gpio_get_direction ;
pctl - > chip . set = axp20x_gpio_set ;
pctl - > chip . direction_input = axp20x_gpio_input ;
pctl - > chip . direction_output = axp20x_gpio_output ;
2018-01-02 14:28:04 +01:00
pctl - > desc = of_device_get_match_data ( dev ) ;
2017-12-13 09:55:03 +01:00
pctl - > chip . ngpio = pctl - > desc - > npins ;
2017-12-05 15:46:43 +01:00
pctl - > regmap = axp20x - > regmap ;
pctl - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , pctl ) ;
2017-12-05 15:46:41 +01:00
axp20x_build_funcs_groups ( pdev ) ;
pctrl_desc = devm_kzalloc ( & pdev - > dev , sizeof ( * pctrl_desc ) , GFP_KERNEL ) ;
if ( ! pctrl_desc )
return - ENOMEM ;
pctrl_desc - > name = dev_name ( & pdev - > dev ) ;
pctrl_desc - > owner = THIS_MODULE ;
2017-12-05 15:46:43 +01:00
pctrl_desc - > pins = pctl - > desc - > pins ;
pctrl_desc - > npins = pctl - > desc - > npins ;
2017-12-05 15:46:41 +01:00
pctrl_desc - > pctlops = & axp20x_pctrl_ops ;
pctrl_desc - > pmxops = & axp20x_pmx_ops ;
2017-12-05 15:46:43 +01:00
pctl - > pctl_dev = devm_pinctrl_register ( & pdev - > dev , pctrl_desc , pctl ) ;
if ( IS_ERR ( pctl - > pctl_dev ) ) {
2017-12-05 15:46:41 +01:00
dev_err ( & pdev - > dev , " couldn't register pinctrl driver \n " ) ;
2017-12-05 15:46:43 +01:00
return PTR_ERR ( pctl - > pctl_dev ) ;
2017-12-05 15:46:41 +01:00
}
2016-07-20 16:11:36 +02:00
2017-12-05 15:46:43 +01:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & pctl - > chip , pctl ) ;
2016-07-20 16:11:36 +02:00
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to register GPIO chip \n " ) ;
return ret ;
}
2017-12-05 15:46:43 +01:00
ret = gpiochip_add_pin_range ( & pctl - > chip , dev_name ( & pdev - > dev ) ,
pctl - > desc - > pins - > number ,
pctl - > desc - > pins - > number ,
pctl - > desc - > npins ) ;
2017-12-05 15:46:41 +01:00
if ( ret ) {
dev_err ( & pdev - > dev , " failed to add pin range \n " ) ;
return ret ;
}
dev_info ( & pdev - > dev , " AXP209 pinctrl and GPIO driver loaded \n " ) ;
2016-07-20 16:11:36 +02:00
return 0 ;
}
2017-12-05 15:46:43 +01:00
static struct platform_driver axp20x_pctl_driver = {
. probe = axp20x_pctl_probe ,
2016-07-20 16:11:36 +02:00
. driver = {
. name = " axp20x-gpio " ,
2017-12-05 15:46:43 +01:00
. of_match_table = axp20x_pctl_match ,
2016-07-20 16:11:36 +02:00
} ,
} ;
2017-12-05 15:46:43 +01:00
module_platform_driver ( axp20x_pctl_driver ) ;
2016-07-20 16:11:36 +02:00
MODULE_AUTHOR ( " Maxime Ripard <maxime.ripard@free-electrons.com> " ) ;
2017-12-05 15:46:41 +01:00
MODULE_AUTHOR ( " Quentin Schulz <quentin.schulz@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " AXP20x PMIC pinctrl and GPIO driver " ) ;
2016-07-20 16:11:36 +02:00
MODULE_LICENSE ( " GPL " ) ;