2020-12-10 17:12:12 +08:00
// SPDX-License-Identifier: GPL-2.0
/*
* Intel Lightning Mountain SoC LED Serial Shift Output Controller driver
*
* Copyright ( c ) 2020 Intel Corporation .
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
2021-06-14 01:16:47 +02:00
# include <linux/gpio/consumer.h>
# include <linux/gpio/driver.h>
2020-12-10 17:12:12 +08:00
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/leds.h>
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# include <linux/regmap.h>
# include <linux/sizes.h>
# include <linux/uaccess.h>
# define SSO_DEV_NAME "lgm-sso"
# define LED_BLINK_H8_0 0x0
# define LED_BLINK_H8_1 0x4
# define GET_FREQ_OFFSET(pin, src) (((pin) * 6) + ((src) * 2))
# define GET_SRC_OFFSET(pinc) (((pin) * 6) + 4)
# define DUTY_CYCLE(x) (0x8 + ((x) * 4))
# define SSO_CON0 0x2B0
# define SSO_CON0_RZFL BIT(26)
# define SSO_CON0_BLINK_R BIT(30)
# define SSO_CON0_SWU BIT(31)
# define SSO_CON1 0x2B4
# define SSO_CON1_FCDSC GENMASK(21, 20) /* Fixed Divider Shift Clock */
# define SSO_CON1_FPID GENMASK(24, 23)
# define SSO_CON1_GPTD GENMASK(26, 25)
# define SSO_CON1_US GENMASK(31, 30)
# define SSO_CPU 0x2B8
# define SSO_CON2 0x2C4
# define SSO_CON3 0x2C8
/* Driver MACRO */
# define MAX_PIN_NUM_PER_BANK SZ_32
# define MAX_GROUP_NUM SZ_4
# define PINS_PER_GROUP SZ_8
# define FPID_FREQ_RANK_MAX SZ_4
# define SSO_LED_MAX_NUM SZ_32
# define MAX_FREQ_RANK 10
# define DEF_GPTC_CLK_RATE 200000000
# define SSO_DEF_BRIGHTNESS LED_HALF
# define DATA_CLK_EDGE 0 /* 0-rising, 1-falling */
static const u32 freq_div_tbl [ ] = { 4000 , 2000 , 1000 , 800 } ;
static const int freq_tbl [ ] = { 2 , 4 , 8 , 10 , 50000 , 100000 , 200000 , 250000 } ;
static const int shift_clk_freq_tbl [ ] = { 25000000 , 12500000 , 6250000 , 3125000 } ;
/*
* Update Source to update the SOUTs
* SW - Software has to update the SWU bit
* GPTC - General Purpose timer is used as clock source
* FPID - Divided FSC clock ( FPID ) is used as clock source
*/
enum {
US_SW = 0 ,
US_GPTC = 1 ,
US_FPID = 2
} ;
enum {
MAX_FPID_FREQ_RANK = 5 , /* 1 to 4 */
MAX_GPTC_FREQ_RANK = 9 , /* 5 to 8 */
MAX_GPTC_HS_FREQ_RANK = 10 , /* 9 to 10 */
} ;
enum {
LED_GRP0_PIN_MAX = 24 ,
LED_GRP1_PIN_MAX = 29 ,
LED_GRP2_PIN_MAX = 32 ,
} ;
enum {
LED_GRP0_0_23 ,
LED_GRP1_24_28 ,
LED_GRP2_29_31 ,
LED_GROUP_MAX ,
} ;
enum {
CLK_SRC_FPID = 0 ,
CLK_SRC_GPTC = 1 ,
CLK_SRC_GPTC_HS = 2 ,
} ;
struct sso_led_priv ;
struct sso_led_desc {
const char * name ;
const char * default_trigger ;
unsigned int brightness ;
unsigned int blink_rate ;
unsigned int retain_state_suspended : 1 ;
unsigned int retain_state_shutdown : 1 ;
unsigned int panic_indicator : 1 ;
unsigned int hw_blink : 1 ;
unsigned int hw_trig : 1 ;
unsigned int blinking : 1 ;
int freq_idx ;
u32 pin ;
} ;
struct sso_led {
struct list_head list ;
struct led_classdev cdev ;
struct gpio_desc * gpiod ;
struct sso_led_desc desc ;
struct sso_led_priv * priv ;
} ;
struct sso_gpio {
struct gpio_chip chip ;
int shift_clk_freq ;
int edge ;
int freq ;
u32 pins ;
u32 alloc_bitmap ;
} ;
struct sso_led_priv {
struct regmap * mmap ;
struct device * dev ;
struct platform_device * pdev ;
2021-05-10 12:50:24 +03:00
struct clk_bulk_data clocks [ 2 ] ;
2020-12-10 17:12:12 +08:00
u32 fpid_clkrate ;
u32 gptc_clkrate ;
u32 freq [ MAX_FREQ_RANK ] ;
struct list_head led_list ;
struct sso_gpio gpio ;
} ;
static int sso_get_blink_rate_idx ( struct sso_led_priv * priv , u32 rate )
{
int i ;
for ( i = 0 ; i < MAX_FREQ_RANK ; i + + ) {
if ( rate < = priv - > freq [ i ] )
return i ;
}
return - 1 ;
}
static unsigned int sso_led_pin_to_group ( u32 pin )
{
if ( pin < LED_GRP0_PIN_MAX )
return LED_GRP0_0_23 ;
else if ( pin < LED_GRP1_PIN_MAX )
return LED_GRP1_24_28 ;
else
return LED_GRP2_29_31 ;
}
static u32 sso_led_get_freq_src ( int freq_idx )
{
if ( freq_idx < MAX_FPID_FREQ_RANK )
return CLK_SRC_FPID ;
else if ( freq_idx < MAX_GPTC_FREQ_RANK )
return CLK_SRC_GPTC ;
else
return CLK_SRC_GPTC_HS ;
}
static u32 sso_led_pin_blink_off ( u32 pin , unsigned int group )
{
if ( group = = LED_GRP2_29_31 )
return pin - LED_GRP1_PIN_MAX ;
else if ( group = = LED_GRP1_24_28 )
return pin - LED_GRP0_PIN_MAX ;
else /* led 0 - 23 in led 32 location */
return SSO_LED_MAX_NUM - LED_GRP1_PIN_MAX ;
}
static struct sso_led
* cdev_to_sso_led_data ( struct led_classdev * led_cdev )
{
return container_of ( led_cdev , struct sso_led , cdev ) ;
}
static void sso_led_freq_set ( struct sso_led_priv * priv , u32 pin , int freq_idx )
{
u32 reg , off , freq_src , val_freq ;
u32 low , high , val ;
unsigned int group ;
if ( ! freq_idx )
return ;
group = sso_led_pin_to_group ( pin ) ;
freq_src = sso_led_get_freq_src ( freq_idx ) ;
off = sso_led_pin_blink_off ( pin , group ) ;
if ( group = = LED_GRP0_0_23 )
return ;
else if ( group = = LED_GRP1_24_28 )
reg = LED_BLINK_H8_0 ;
else
reg = LED_BLINK_H8_1 ;
if ( freq_src = = CLK_SRC_FPID )
val_freq = freq_idx - 1 ;
else if ( freq_src = = CLK_SRC_GPTC )
val_freq = freq_idx - MAX_FPID_FREQ_RANK ;
/* set blink rate idx */
if ( freq_src ! = CLK_SRC_GPTC_HS ) {
low = GET_FREQ_OFFSET ( off , freq_src ) ;
high = low + 2 ;
val = val_freq < < high ;
regmap_update_bits ( priv - > mmap , reg , GENMASK ( high , low ) , val ) ;
}
/* select clock source */
low = GET_SRC_OFFSET ( off ) ;
high = low + 2 ;
val = freq_src < < high ;
regmap_update_bits ( priv - > mmap , reg , GENMASK ( high , low ) , val ) ;
}
static void sso_led_brightness_set ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
{
struct sso_led_priv * priv ;
struct sso_led_desc * desc ;
struct sso_led * led ;
int val ;
led = cdev_to_sso_led_data ( led_cdev ) ;
priv = led - > priv ;
desc = & led - > desc ;
desc - > brightness = brightness ;
regmap_write ( priv - > mmap , DUTY_CYCLE ( desc - > pin ) , brightness ) ;
if ( brightness = = LED_OFF )
val = 0 ;
else
val = 1 ;
/* HW blink off */
if ( desc - > hw_blink & & ! val & & desc - > blinking ) {
desc - > blinking = 0 ;
regmap_update_bits ( priv - > mmap , SSO_CON2 , BIT ( desc - > pin ) , 0 ) ;
} else if ( desc - > hw_blink & & val & & ! desc - > blinking ) {
desc - > blinking = 1 ;
regmap_update_bits ( priv - > mmap , SSO_CON2 , BIT ( desc - > pin ) ,
1 < < desc - > pin ) ;
}
2021-05-10 12:50:29 +03:00
if ( ! desc - > hw_trig )
2020-12-10 17:12:12 +08:00
gpiod_set_value ( led - > gpiod , val ) ;
}
static enum led_brightness sso_led_brightness_get ( struct led_classdev * led_cdev )
{
struct sso_led * led = cdev_to_sso_led_data ( led_cdev ) ;
return ( enum led_brightness ) led - > desc . brightness ;
}
static int
delay_to_freq_idx ( struct sso_led * led , unsigned long * delay_on ,
unsigned long * delay_off )
{
struct sso_led_priv * priv = led - > priv ;
unsigned long delay ;
int freq_idx ;
u32 freq ;
if ( ! * delay_on & & ! * delay_off ) {
* delay_on = * delay_off = ( 1000 / priv - > freq [ 0 ] ) / 2 ;
return 0 ;
}
delay = * delay_on + * delay_off ;
freq = 1000 / delay ;
freq_idx = sso_get_blink_rate_idx ( priv , freq ) ;
if ( freq_idx = = - 1 )
freq_idx = MAX_FREQ_RANK - 1 ;
delay = 1000 / priv - > freq [ freq_idx ] ;
* delay_on = * delay_off = delay / 2 ;
if ( ! * delay_on )
* delay_on = * delay_off = 1 ;
return freq_idx ;
}
static int
sso_led_blink_set ( struct led_classdev * led_cdev , unsigned long * delay_on ,
unsigned long * delay_off )
{
struct sso_led_priv * priv ;
struct sso_led * led ;
int freq_idx ;
led = cdev_to_sso_led_data ( led_cdev ) ;
priv = led - > priv ;
freq_idx = delay_to_freq_idx ( led , delay_on , delay_off ) ;
sso_led_freq_set ( priv , led - > desc . pin , freq_idx ) ;
regmap_update_bits ( priv - > mmap , SSO_CON2 , BIT ( led - > desc . pin ) ,
1 < < led - > desc . pin ) ;
led - > desc . freq_idx = freq_idx ;
led - > desc . blink_rate = priv - > freq [ freq_idx ] ;
led - > desc . blinking = 1 ;
return 1 ;
}
static void sso_led_hw_cfg ( struct sso_led_priv * priv , struct sso_led * led )
{
struct sso_led_desc * desc = & led - > desc ;
/* set freq */
if ( desc - > hw_blink ) {
sso_led_freq_set ( priv , desc - > pin , desc - > freq_idx ) ;
regmap_update_bits ( priv - > mmap , SSO_CON2 , BIT ( desc - > pin ) ,
1 < < desc - > pin ) ;
}
if ( desc - > hw_trig )
regmap_update_bits ( priv - > mmap , SSO_CON3 , BIT ( desc - > pin ) ,
1 < < desc - > pin ) ;
/* set brightness */
regmap_write ( priv - > mmap , DUTY_CYCLE ( desc - > pin ) , desc - > brightness ) ;
/* enable output */
if ( ! desc - > hw_trig & & desc - > brightness )
gpiod_set_value ( led - > gpiod , 1 ) ;
}
static int sso_create_led ( struct sso_led_priv * priv , struct sso_led * led ,
struct fwnode_handle * child )
{
struct sso_led_desc * desc = & led - > desc ;
struct led_init_data init_data ;
int err ;
init_data . fwnode = child ;
init_data . devicename = SSO_DEV_NAME ;
init_data . default_label = " : " ;
led - > cdev . default_trigger = desc - > default_trigger ;
led - > cdev . brightness_set = sso_led_brightness_set ;
led - > cdev . brightness_get = sso_led_brightness_get ;
led - > cdev . brightness = desc - > brightness ;
led - > cdev . max_brightness = LED_FULL ;
if ( desc - > retain_state_shutdown )
led - > cdev . flags | = LED_RETAIN_AT_SHUTDOWN ;
if ( desc - > retain_state_suspended )
led - > cdev . flags | = LED_CORE_SUSPENDRESUME ;
if ( desc - > panic_indicator )
led - > cdev . flags | = LED_PANIC_INDICATOR ;
if ( desc - > hw_blink )
led - > cdev . blink_set = sso_led_blink_set ;
sso_led_hw_cfg ( priv , led ) ;
err = devm_led_classdev_register_ext ( priv - > dev , & led - > cdev , & init_data ) ;
if ( err )
return err ;
list_add ( & led - > list , & priv - > led_list ) ;
return 0 ;
}
static void sso_init_freq ( struct sso_led_priv * priv )
{
int i ;
priv - > freq [ 0 ] = 0 ;
for ( i = 1 ; i < MAX_FREQ_RANK ; i + + ) {
if ( i < MAX_FPID_FREQ_RANK ) {
priv - > freq [ i ] = priv - > fpid_clkrate / freq_div_tbl [ i - 1 ] ;
} else if ( i < MAX_GPTC_FREQ_RANK ) {
priv - > freq [ i ] = priv - > gptc_clkrate /
freq_div_tbl [ i - MAX_FPID_FREQ_RANK ] ;
} else if ( i < MAX_GPTC_HS_FREQ_RANK ) {
priv - > freq [ i ] = priv - > gptc_clkrate ;
}
}
}
static int sso_gpio_request ( struct gpio_chip * chip , unsigned int offset )
{
struct sso_led_priv * priv = gpiochip_get_data ( chip ) ;
if ( priv - > gpio . alloc_bitmap & BIT ( offset ) )
return - EINVAL ;
priv - > gpio . alloc_bitmap | = BIT ( offset ) ;
regmap_write ( priv - > mmap , DUTY_CYCLE ( offset ) , 0xFF ) ;
return 0 ;
}
static void sso_gpio_free ( struct gpio_chip * chip , unsigned int offset )
{
struct sso_led_priv * priv = gpiochip_get_data ( chip ) ;
priv - > gpio . alloc_bitmap & = ~ BIT ( offset ) ;
regmap_write ( priv - > mmap , DUTY_CYCLE ( offset ) , 0x0 ) ;
}
static int sso_gpio_get_dir ( struct gpio_chip * chip , unsigned int offset )
{
2021-06-14 01:16:47 +02:00
return GPIO_LINE_DIRECTION_OUT ;
2020-12-10 17:12:12 +08:00
}
static int
sso_gpio_dir_out ( struct gpio_chip * chip , unsigned int offset , int value )
{
struct sso_led_priv * priv = gpiochip_get_data ( chip ) ;
bool bit = ! ! value ;
regmap_update_bits ( priv - > mmap , SSO_CPU , BIT ( offset ) , bit < < offset ) ;
if ( ! priv - > gpio . freq )
regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_SWU ,
SSO_CON0_SWU ) ;
return 0 ;
}
static int sso_gpio_get ( struct gpio_chip * chip , unsigned int offset )
{
struct sso_led_priv * priv = gpiochip_get_data ( chip ) ;
u32 reg_val ;
regmap_read ( priv - > mmap , SSO_CPU , & reg_val ) ;
return ! ! ( reg_val & BIT ( offset ) ) ;
}
static void sso_gpio_set ( struct gpio_chip * chip , unsigned int offset , int value )
{
struct sso_led_priv * priv = gpiochip_get_data ( chip ) ;
regmap_update_bits ( priv - > mmap , SSO_CPU , BIT ( offset ) , value < < offset ) ;
if ( ! priv - > gpio . freq )
regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_SWU ,
SSO_CON0_SWU ) ;
}
static int sso_gpio_gc_init ( struct device * dev , struct sso_led_priv * priv )
{
struct gpio_chip * gc = & priv - > gpio . chip ;
gc - > request = sso_gpio_request ;
gc - > free = sso_gpio_free ;
gc - > get_direction = sso_gpio_get_dir ;
gc - > direction_output = sso_gpio_dir_out ;
gc - > get = sso_gpio_get ;
gc - > set = sso_gpio_set ;
gc - > label = " lgm-sso " ;
gc - > base = - 1 ;
/* To exclude pins from control, use "gpio-reserved-ranges" */
gc - > ngpio = priv - > gpio . pins ;
gc - > parent = dev ;
gc - > owner = THIS_MODULE ;
return devm_gpiochip_add_data ( dev , gc , priv ) ;
}
static int sso_gpio_get_freq_idx ( int freq )
{
int idx ;
for ( idx = 0 ; idx < ARRAY_SIZE ( freq_tbl ) ; idx + + ) {
if ( freq < = freq_tbl [ idx ] )
return idx ;
}
return - 1 ;
}
static void sso_register_shift_clk ( struct sso_led_priv * priv )
{
int idx , size = ARRAY_SIZE ( shift_clk_freq_tbl ) ;
u32 val = 0 ;
for ( idx = 0 ; idx < size ; idx + + ) {
if ( shift_clk_freq_tbl [ idx ] < = priv - > gpio . shift_clk_freq ) {
val = idx ;
break ;
}
}
if ( idx = = size )
dev_warn ( priv - > dev , " %s: Invalid freq %d \n " ,
__func__ , priv - > gpio . shift_clk_freq ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_FCDSC ,
FIELD_PREP ( SSO_CON1_FCDSC , val ) ) ;
}
static int sso_gpio_freq_set ( struct sso_led_priv * priv )
{
int freq_idx ;
u32 val ;
freq_idx = sso_gpio_get_freq_idx ( priv - > gpio . freq ) ;
if ( freq_idx = = - 1 )
freq_idx = ARRAY_SIZE ( freq_tbl ) - 1 ;
val = freq_idx % FPID_FREQ_RANK_MAX ;
if ( ! priv - > gpio . freq ) {
regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_BLINK_R , 0 ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_US ,
FIELD_PREP ( SSO_CON1_US , US_SW ) ) ;
} else if ( freq_idx < FPID_FREQ_RANK_MAX ) {
regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_BLINK_R ,
SSO_CON0_BLINK_R ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_US ,
FIELD_PREP ( SSO_CON1_US , US_FPID ) ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_FPID ,
FIELD_PREP ( SSO_CON1_FPID , val ) ) ;
} else {
regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_BLINK_R ,
SSO_CON0_BLINK_R ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_US ,
FIELD_PREP ( SSO_CON1_US , US_GPTC ) ) ;
regmap_update_bits ( priv - > mmap , SSO_CON1 , SSO_CON1_GPTD ,
FIELD_PREP ( SSO_CON1_GPTD , val ) ) ;
}
return 0 ;
}
static int sso_gpio_hw_init ( struct sso_led_priv * priv )
{
u32 activate ;
int i , err ;
/* Clear all duty cycles */
for ( i = 0 ; i < priv - > gpio . pins ; i + + ) {
err = regmap_write ( priv - > mmap , DUTY_CYCLE ( i ) , 0 ) ;
if ( err )
return err ;
}
/* 4 groups for total 32 pins */
for ( i = 1 ; i < = MAX_GROUP_NUM ; i + + ) {
activate = ! ! ( i * PINS_PER_GROUP < = priv - > gpio . pins | |
priv - > gpio . pins > ( i - 1 ) * PINS_PER_GROUP ) ;
err = regmap_update_bits ( priv - > mmap , SSO_CON1 , BIT ( i - 1 ) ,
activate < < ( i - 1 ) ) ;
if ( err )
return err ;
}
/* NO HW directly controlled pin by default */
err = regmap_write ( priv - > mmap , SSO_CON3 , 0 ) ;
if ( err )
return err ;
/* NO BLINK for all pins */
err = regmap_write ( priv - > mmap , SSO_CON2 , 0 ) ;
if ( err )
return err ;
/* OUTPUT 0 by default */
err = regmap_write ( priv - > mmap , SSO_CPU , 0 ) ;
if ( err )
return err ;
/* update edge */
err = regmap_update_bits ( priv - > mmap , SSO_CON0 , SSO_CON0_RZFL ,
FIELD_PREP ( SSO_CON0_RZFL , priv - > gpio . edge ) ) ;
if ( err )
return err ;
/* Set GPIO update rate */
sso_gpio_freq_set ( priv ) ;
/* Register shift clock */
sso_register_shift_clk ( priv ) ;
return 0 ;
}
static void sso_led_shutdown ( struct sso_led * led )
{
struct sso_led_priv * priv = led - > priv ;
/* unregister led */
devm_led_classdev_unregister ( priv - > dev , & led - > cdev ) ;
/* clear HW control bit */
if ( led - > desc . hw_trig )
regmap_update_bits ( priv - > mmap , SSO_CON3 , BIT ( led - > desc . pin ) , 0 ) ;
led - > priv = NULL ;
}
static int
__sso_led_dt_parse ( struct sso_led_priv * priv , struct fwnode_handle * fw_ssoled )
{
struct fwnode_handle * fwnode_child ;
struct device * dev = priv - > dev ;
struct sso_led_desc * desc ;
struct sso_led * led ;
const char * tmp ;
u32 prop ;
int ret ;
fwnode_for_each_child_node ( fw_ssoled , fwnode_child ) {
led = devm_kzalloc ( dev , sizeof ( * led ) , GFP_KERNEL ) ;
2021-05-29 14:19:26 +03:00
if ( ! led ) {
ret = - ENOMEM ;
goto __dt_err ;
}
2020-12-10 17:12:12 +08:00
INIT_LIST_HEAD ( & led - > list ) ;
led - > priv = priv ;
desc = & led - > desc ;
2022-09-02 17:55:26 -07:00
led - > gpiod = devm_fwnode_gpiod_get ( dev , fwnode_child , NULL ,
GPIOD_ASIS , NULL ) ;
2020-12-10 17:12:12 +08:00
if ( IS_ERR ( led - > gpiod ) ) {
2021-08-05 14:26:19 +03:00
ret = dev_err_probe ( dev , PTR_ERR ( led - > gpiod ) , " led: get gpio fail! \n " ) ;
2020-12-10 17:12:12 +08:00
goto __dt_err ;
}
fwnode_property_read_string ( fwnode_child ,
" linux,default-trigger " ,
& desc - > default_trigger ) ;
if ( fwnode_property_present ( fwnode_child ,
" retain-state-suspended " ) )
desc - > retain_state_suspended = 1 ;
if ( fwnode_property_present ( fwnode_child ,
" retain-state-shutdown " ) )
desc - > retain_state_shutdown = 1 ;
if ( fwnode_property_present ( fwnode_child , " panic-indicator " ) )
desc - > panic_indicator = 1 ;
ret = fwnode_property_read_u32 ( fwnode_child , " reg " , & prop ) ;
2021-08-05 14:26:19 +03:00
if ( ret )
goto __dt_err ;
if ( prop > = SSO_LED_MAX_NUM ) {
2020-12-10 17:12:12 +08:00
dev_err ( dev , " invalid LED pin:%u \n " , prop ) ;
2021-08-05 14:26:19 +03:00
ret = - EINVAL ;
2020-12-10 17:12:12 +08:00
goto __dt_err ;
}
desc - > pin = prop ;
if ( fwnode_property_present ( fwnode_child , " intel,sso-hw-blink " ) )
desc - > hw_blink = 1 ;
desc - > hw_trig = fwnode_property_read_bool ( fwnode_child ,
" intel,sso-hw-trigger " ) ;
if ( desc - > hw_trig ) {
desc - > default_trigger = NULL ;
desc - > retain_state_shutdown = 0 ;
desc - > retain_state_suspended = 0 ;
desc - > panic_indicator = 0 ;
desc - > hw_blink = 0 ;
}
if ( fwnode_property_read_u32 ( fwnode_child ,
" intel,sso-blink-rate-hz " , & prop ) ) {
/* default first freq rate */
desc - > freq_idx = 0 ;
desc - > blink_rate = priv - > freq [ desc - > freq_idx ] ;
} else {
desc - > freq_idx = sso_get_blink_rate_idx ( priv , prop ) ;
if ( desc - > freq_idx = = - 1 )
desc - > freq_idx = MAX_FREQ_RANK - 1 ;
desc - > blink_rate = priv - > freq [ desc - > freq_idx ] ;
}
if ( ! fwnode_property_read_string ( fwnode_child , " default-state " , & tmp ) ) {
if ( ! strcmp ( tmp , " on " ) )
desc - > brightness = LED_FULL ;
}
2021-08-05 14:26:19 +03:00
ret = sso_create_led ( priv , led , fwnode_child ) ;
if ( ret )
2020-12-10 17:12:12 +08:00
goto __dt_err ;
}
return 0 ;
2021-05-29 14:19:26 +03:00
2020-12-10 17:12:12 +08:00
__dt_err :
2021-05-29 14:19:26 +03:00
fwnode_handle_put ( fwnode_child ) ;
2020-12-10 17:12:12 +08:00
/* unregister leds */
2021-05-29 14:19:29 +03:00
list_for_each_entry ( led , & priv - > led_list , list )
2020-12-10 17:12:12 +08:00
sso_led_shutdown ( led ) ;
2021-08-05 14:26:19 +03:00
return ret ;
2020-12-10 17:12:12 +08:00
}
static int sso_led_dt_parse ( struct sso_led_priv * priv )
{
struct fwnode_handle * fwnode = dev_fwnode ( priv - > dev ) ;
struct fwnode_handle * fw_ssoled ;
struct device * dev = priv - > dev ;
int count ;
int ret ;
count = device_get_child_node_count ( dev ) ;
if ( ! count )
return 0 ;
fw_ssoled = fwnode_get_named_child_node ( fwnode , " ssoled " ) ;
if ( fw_ssoled ) {
ret = __sso_led_dt_parse ( priv , fw_ssoled ) ;
2021-05-29 14:19:26 +03:00
fwnode_handle_put ( fw_ssoled ) ;
2020-12-10 17:12:12 +08:00
if ( ret )
return ret ;
}
return 0 ;
}
static int sso_probe_gpios ( struct sso_led_priv * priv )
{
struct device * dev = priv - > dev ;
int ret ;
if ( device_property_read_u32 ( dev , " ngpios " , & priv - > gpio . pins ) )
priv - > gpio . pins = MAX_PIN_NUM_PER_BANK ;
if ( priv - > gpio . pins > MAX_PIN_NUM_PER_BANK )
return - EINVAL ;
if ( device_property_read_u32 ( dev , " intel,sso-update-rate-hz " ,
& priv - > gpio . freq ) )
priv - > gpio . freq = 0 ;
priv - > gpio . edge = DATA_CLK_EDGE ;
priv - > gpio . shift_clk_freq = - 1 ;
ret = sso_gpio_hw_init ( priv ) ;
if ( ret )
return ret ;
return sso_gpio_gc_init ( dev , priv ) ;
}
2021-05-10 12:50:24 +03:00
static void sso_clock_disable_unprepare ( void * data )
2020-12-10 17:12:12 +08:00
{
struct sso_led_priv * priv = data ;
2021-05-10 12:50:24 +03:00
clk_bulk_disable_unprepare ( ARRAY_SIZE ( priv - > clocks ) , priv - > clocks ) ;
2020-12-10 17:12:12 +08:00
}
static int intel_sso_led_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct sso_led_priv * priv ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > pdev = pdev ;
priv - > dev = dev ;
/* gate clock */
2021-05-10 12:50:24 +03:00
priv - > clocks [ 0 ] . id = " sso " ;
/* fpid clock */
priv - > clocks [ 1 ] . id = " fpid " ;
2020-12-10 17:12:12 +08:00
2021-05-10 12:50:24 +03:00
ret = devm_clk_bulk_get ( dev , ARRAY_SIZE ( priv - > clocks ) , priv - > clocks ) ;
2020-12-10 17:12:12 +08:00
if ( ret ) {
2021-05-10 12:50:24 +03:00
dev_err ( dev , " Getting clocks failed! \n " ) ;
2020-12-10 17:12:12 +08:00
return ret ;
}
2021-05-10 12:50:24 +03:00
ret = clk_bulk_prepare_enable ( ARRAY_SIZE ( priv - > clocks ) , priv - > clocks ) ;
2020-12-10 17:12:12 +08:00
if ( ret ) {
2021-05-10 12:50:24 +03:00
dev_err ( dev , " Failed to prepare and enable clocks! \n " ) ;
2020-12-10 17:12:12 +08:00
return ret ;
}
2021-05-10 12:50:24 +03:00
ret = devm_add_action_or_reset ( dev , sso_clock_disable_unprepare , priv ) ;
if ( ret )
2020-12-10 17:12:12 +08:00
return ret ;
2021-05-10 12:50:24 +03:00
priv - > fpid_clkrate = clk_get_rate ( priv - > clocks [ 1 ] . clk ) ;
priv - > mmap = syscon_node_to_regmap ( dev - > of_node ) ;
2020-12-10 17:12:12 +08:00
priv - > mmap = syscon_node_to_regmap ( dev - > of_node ) ;
if ( IS_ERR ( priv - > mmap ) ) {
dev_err ( dev , " Failed to map iomem! \n " ) ;
return PTR_ERR ( priv - > mmap ) ;
}
ret = sso_probe_gpios ( priv ) ;
if ( ret ) {
regmap_exit ( priv - > mmap ) ;
return ret ;
}
INIT_LIST_HEAD ( & priv - > led_list ) ;
platform_set_drvdata ( pdev , priv ) ;
sso_init_freq ( priv ) ;
priv - > gptc_clkrate = DEF_GPTC_CLK_RATE ;
ret = sso_led_dt_parse ( priv ) ;
if ( ret ) {
regmap_exit ( priv - > mmap ) ;
return ret ;
}
dev_info ( priv - > dev , " sso LED init success! \n " ) ;
return 0 ;
}
2023-09-17 15:09:47 +02:00
static void intel_sso_led_remove ( struct platform_device * pdev )
2020-12-10 17:12:12 +08:00
{
struct sso_led_priv * priv ;
2021-05-29 14:19:29 +03:00
struct sso_led * led , * n ;
2020-12-10 17:12:12 +08:00
priv = platform_get_drvdata ( pdev ) ;
2021-05-29 14:19:29 +03:00
list_for_each_entry_safe ( led , n , & priv - > led_list , list ) {
list_del ( & led - > list ) ;
2020-12-10 17:12:12 +08:00
sso_led_shutdown ( led ) ;
}
regmap_exit ( priv - > mmap ) ;
}
static const struct of_device_id of_sso_led_match [ ] = {
{ . compatible = " intel,lgm-ssoled " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , of_sso_led_match ) ;
static struct platform_driver intel_sso_led_driver = {
. probe = intel_sso_led_probe ,
2023-09-17 15:09:47 +02:00
. remove_new = intel_sso_led_remove ,
2020-12-10 17:12:12 +08:00
. driver = {
. name = " lgm-ssoled " ,
2021-05-10 12:50:27 +03:00
. of_match_table = of_sso_led_match ,
2020-12-10 17:12:12 +08:00
} ,
} ;
module_platform_driver ( intel_sso_led_driver ) ;
MODULE_DESCRIPTION ( " Intel SSO LED/GPIO driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;