2023-07-13 06:48:52 +03:00
// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause
/*
* Copyright 2023 Schweitzer Engineering Laboratories , Inc .
* 2350 NE Hopkins Court , Pullman , WA 99163 USA
*
* Platform support for the b2093 mainboard used in SEL - 3350 computers .
* Consumes GPIO from the SoC to provide standard LED and power supply
* devices .
*/
# include <linux/acpi.h>
# include <linux/gpio/consumer.h>
# include <linux/gpio/machine.h>
# include <linux/leds.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
/* Broxton communities */
# define BXT_NW "INT3452:01"
# define BXT_W "INT3452:02"
# define BXT_SW "INT3452:03"
# define B2093_GPIO_ACPI_ID "SEL0003"
# define SEL_PS_A "sel_ps_a"
# define SEL_PS_A_DETECT "sel_ps_a_detect"
# define SEL_PS_A_GOOD "sel_ps_a_good"
# define SEL_PS_B "sel_ps_b"
# define SEL_PS_B_DETECT "sel_ps_b_detect"
# define SEL_PS_B_GOOD "sel_ps_b_good"
/* LEDs */
static const struct gpio_led sel3350_leds [ ] = {
{ . name = " sel:green:aux1 " } ,
{ . name = " sel:green:aux2 " } ,
{ . name = " sel:green:aux3 " } ,
{ . name = " sel:green:aux4 " } ,
{ . name = " sel:red:alarm " } ,
{ . name = " sel:green:enabled " ,
. default_state = LEDS_GPIO_DEFSTATE_ON } ,
{ . name = " sel:red:aux1 " } ,
{ . name = " sel:red:aux2 " } ,
{ . name = " sel:red:aux3 " } ,
{ . name = " sel:red:aux4 " } ,
} ;
static const struct gpio_led_platform_data sel3350_leds_pdata = {
. num_leds = ARRAY_SIZE ( sel3350_leds ) ,
. leds = sel3350_leds ,
} ;
/* Map GPIOs to LEDs */
static struct gpiod_lookup_table sel3350_leds_table = {
. dev_id = " leds-gpio " ,
. table = {
GPIO_LOOKUP_IDX ( BXT_NW , 49 , NULL , 0 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_NW , 50 , NULL , 1 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_NW , 51 , NULL , 2 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_NW , 52 , NULL , 3 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_W , 20 , NULL , 4 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_W , 21 , NULL , 5 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_SW , 37 , NULL , 6 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_SW , 38 , NULL , 7 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_SW , 39 , NULL , 8 , GPIO_ACTIVE_HIGH ) ,
GPIO_LOOKUP_IDX ( BXT_SW , 40 , NULL , 9 , GPIO_ACTIVE_HIGH ) ,
{ } ,
}
} ;
/* Map GPIOs to power supplies */
static struct gpiod_lookup_table sel3350_gpios_table = {
. dev_id = B2093_GPIO_ACPI_ID " :00 " ,
. table = {
GPIO_LOOKUP ( BXT_NW , 44 , SEL_PS_A_DETECT , GPIO_ACTIVE_LOW ) ,
GPIO_LOOKUP ( BXT_NW , 45 , SEL_PS_A_GOOD , GPIO_ACTIVE_LOW ) ,
GPIO_LOOKUP ( BXT_NW , 46 , SEL_PS_B_DETECT , GPIO_ACTIVE_LOW ) ,
GPIO_LOOKUP ( BXT_NW , 47 , SEL_PS_B_GOOD , GPIO_ACTIVE_LOW ) ,
{ } ,
}
} ;
/* Power Supplies */
struct sel3350_power_cfg_data {
struct gpio_desc * ps_detect ;
struct gpio_desc * ps_good ;
} ;
static int sel3350_power_get_property ( struct power_supply * psy ,
enum power_supply_property psp ,
union power_supply_propval * val )
{
struct sel3350_power_cfg_data * data = power_supply_get_drvdata ( psy ) ;
switch ( psp ) {
case POWER_SUPPLY_PROP_HEALTH :
if ( gpiod_get_value ( data - > ps_detect ) ) {
if ( gpiod_get_value ( data - > ps_good ) )
val - > intval = POWER_SUPPLY_HEALTH_GOOD ;
else
val - > intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE ;
} else {
val - > intval = POWER_SUPPLY_HEALTH_UNKNOWN ;
}
break ;
case POWER_SUPPLY_PROP_PRESENT :
val - > intval = gpiod_get_value ( data - > ps_detect ) ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = gpiod_get_value ( data - > ps_good ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static const enum power_supply_property sel3350_power_properties [ ] = {
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_ONLINE ,
} ;
static const struct power_supply_desc sel3350_ps_a_desc = {
. name = SEL_PS_A ,
. type = POWER_SUPPLY_TYPE_MAINS ,
. properties = sel3350_power_properties ,
. num_properties = ARRAY_SIZE ( sel3350_power_properties ) ,
. get_property = sel3350_power_get_property ,
} ;
static const struct power_supply_desc sel3350_ps_b_desc = {
. name = SEL_PS_B ,
. type = POWER_SUPPLY_TYPE_MAINS ,
. properties = sel3350_power_properties ,
. num_properties = ARRAY_SIZE ( sel3350_power_properties ) ,
. get_property = sel3350_power_get_property ,
} ;
struct sel3350_data {
struct platform_device * leds_pdev ;
struct power_supply * ps_a ;
struct power_supply * ps_b ;
struct sel3350_power_cfg_data ps_a_cfg_data ;
struct sel3350_power_cfg_data ps_b_cfg_data ;
} ;
static int sel3350_probe ( struct platform_device * pdev )
{
int rs ;
struct sel3350_data * sel3350 ;
struct power_supply_config ps_cfg = { } ;
sel3350 = devm_kzalloc ( & pdev - > dev , sizeof ( struct sel3350_data ) , GFP_KERNEL ) ;
if ( ! sel3350 )
return - ENOMEM ;
platform_set_drvdata ( pdev , sel3350 ) ;
gpiod_add_lookup_table ( & sel3350_leds_table ) ;
gpiod_add_lookup_table ( & sel3350_gpios_table ) ;
sel3350 - > leds_pdev = platform_device_register_data (
NULL ,
" leds-gpio " ,
PLATFORM_DEVID_NONE ,
& sel3350_leds_pdata ,
sizeof ( sel3350_leds_pdata ) ) ;
if ( IS_ERR ( sel3350 - > leds_pdev ) ) {
rs = PTR_ERR ( sel3350 - > leds_pdev ) ;
dev_err ( & pdev - > dev , " Failed registering platform device: %d \n " , rs ) ;
goto err_platform ;
}
/* Power Supply A */
sel3350 - > ps_a_cfg_data . ps_detect = devm_gpiod_get ( & pdev - > dev ,
SEL_PS_A_DETECT ,
GPIOD_IN ) ;
sel3350 - > ps_a_cfg_data . ps_good = devm_gpiod_get ( & pdev - > dev ,
SEL_PS_A_GOOD ,
GPIOD_IN ) ;
ps_cfg . drv_data = & sel3350 - > ps_a_cfg_data ;
sel3350 - > ps_a = devm_power_supply_register ( & pdev - > dev ,
& sel3350_ps_a_desc ,
& ps_cfg ) ;
if ( IS_ERR ( sel3350 - > ps_a ) ) {
rs = PTR_ERR ( sel3350 - > ps_a ) ;
dev_err ( & pdev - > dev , " Failed registering power supply A: %d \n " , rs ) ;
goto err_ps ;
}
/* Power Supply B */
sel3350 - > ps_b_cfg_data . ps_detect = devm_gpiod_get ( & pdev - > dev ,
SEL_PS_B_DETECT ,
GPIOD_IN ) ;
sel3350 - > ps_b_cfg_data . ps_good = devm_gpiod_get ( & pdev - > dev ,
SEL_PS_B_GOOD ,
GPIOD_IN ) ;
ps_cfg . drv_data = & sel3350 - > ps_b_cfg_data ;
sel3350 - > ps_b = devm_power_supply_register ( & pdev - > dev ,
& sel3350_ps_b_desc ,
& ps_cfg ) ;
if ( IS_ERR ( sel3350 - > ps_b ) ) {
rs = PTR_ERR ( sel3350 - > ps_b ) ;
dev_err ( & pdev - > dev , " Failed registering power supply B: %d \n " , rs ) ;
goto err_ps ;
}
return 0 ;
err_ps :
platform_device_unregister ( sel3350 - > leds_pdev ) ;
err_platform :
gpiod_remove_lookup_table ( & sel3350_gpios_table ) ;
gpiod_remove_lookup_table ( & sel3350_leds_table ) ;
return rs ;
}
2023-09-27 11:10:35 +03:00
static void sel3350_remove ( struct platform_device * pdev )
2023-07-13 06:48:52 +03:00
{
struct sel3350_data * sel3350 = platform_get_drvdata ( pdev ) ;
platform_device_unregister ( sel3350 - > leds_pdev ) ;
gpiod_remove_lookup_table ( & sel3350_gpios_table ) ;
gpiod_remove_lookup_table ( & sel3350_leds_table ) ;
}
static const struct acpi_device_id sel3350_device_ids [ ] = {
{ B2093_GPIO_ACPI_ID , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , sel3350_device_ids ) ;
static struct platform_driver sel3350_platform_driver = {
. probe = sel3350_probe ,
2023-09-27 11:10:35 +03:00
. remove_new = sel3350_remove ,
2023-07-13 06:48:52 +03:00
. driver = {
. name = " sel3350-platform " ,
. acpi_match_table = sel3350_device_ids ,
} ,
} ;
module_platform_driver ( sel3350_platform_driver ) ;
MODULE_AUTHOR ( " Schweitzer Engineering Laboratories " ) ;
MODULE_DESCRIPTION ( " SEL-3350 platform driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_SOFTDEP ( " pre: pinctrl_broxton leds-gpio " ) ;