/* * <:copyright-BRCM:2018:DUAL/GPL:standard * * Copyright (c) 2018 Broadcom * All Rights Reserved * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed * to you under the terms of the GNU General Public License version 2 * (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, * with the following added to such license: * * As a special exception, the copyright holders of this software give * you permission to link this software with independent modules, and * to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent * module, the terms and conditions of the license of that module. * An independent module is a module which is not derived from this * software. The special exception does not apply to any modifications * of the software. * * Not withstanding the above, under no circumstances may you combine * this software in any way with any other Broadcom software provided * under a license other than the GPL, without Broadcom's express prior * written consent. * * :> */ // BCMFORMAT: notabs reindent:uncrustify:bcm_minimal_i4.cfg #include "boardparms.h" #include "bcm_led.h" #include "bcm_gpio.h" #include #include #include #include #if defined(CONFIG_BCM_BCA_LED) #include #endif //#define BCM_LED_DEBUG 1 /* The Linux board driver handles any necessary locking so these functions should not be called directly from elsewhere. */ /* bcm_led_driver_set(number, state) -- on/off bcm_led_driver_toggle(number) future: bcm_led_driver_brightness(number, brighness) -- 0-255 future: bcm_led_driver_map_glow(number, state, waveform[]) For now, stete 0 = off state 1 = on This will be replaced with a more flexible set of states, preserving 0 and 1 */ static short g_optled_map[BP_PINMUX_MAX]; static volatile uint32* LedData=NULL; static struct bcm_led_driver_state { unsigned short led_state; unsigned short led_bp; unsigned char is_hw_led; } bcm_led_driver_state[LED_NUM_LEDS]; static void led_do_set(unsigned short num) { unsigned short val; val = bcm_led_driver_state[num].led_state; if(LedData == NULL) return; if ( bcm_led_driver_state[num].is_hw_led ) { // HW LEDs have HW disabled when not in the ON state if (val) { *LedData |= LED_NUM_TO_MASK(num); LED->hWLedEn |= LED_NUM_TO_MASK(num); } else { *LedData &= ~LED_NUM_TO_MASK(num); LED->hWLedEn &= ~LED_NUM_TO_MASK(num); } } else { if (val) { *LedData |= LED_NUM_TO_MASK(num); } else { *LedData &= ~LED_NUM_TO_MASK(num); } } } /* bcm_led_driver_get_optled_map() permits pinmux init code to get a pointer to the optled_map. Any time that pinmux init code creates a mapping where a specific GPIO NUMBER maps to an LED register bit number. this map must be populated. This includes direct mapping when, for example, GPIO3 maps to LED3 */ short * bcm_led_driver_get_optled_map(void) { return(g_optled_map); } /* dim the led connected to gpio pin num. * dim val can be between 0 and 255. * The gpio has to be configured with BpSetupGpioOptled before */ void bcm_led_driver_set_dim(unsigned short num, unsigned short val) { unsigned short led; if (num & BP_LED_USE_GPIO) { return; } else { unsigned int config_value = ((val / 2) & 0xff) << 6; led = num & BP_GPIO_NUM_MASK; led = g_optled_map[led]; /* only change if needed to prevent flickering */ if (LED->LedCfg[led].config[0] != config_value ) { LED->LedCfg[led].config[0] = config_value; LED->ChnActive |= LED_NUM_TO_MASK(led); } } } void bcm_led_driver_set(unsigned short num, unsigned short state) { unsigned short led; unsigned short gpio_state; #ifdef BCM_LED_DEBUG printk("LED %x set state %d\n",num,state); #endif if (num & BP_LED_USE_GPIO) { if (((num & BP_ACTIVE_LOW) && (state == BCM_LED_ON)) || (!(num & BP_ACTIVE_LOW) && (state == BCM_LED_OFF))) gpio_state = 0; else gpio_state = 1; bcm_gpio_set_dir(num, 1); bcm_gpio_set_data(num, gpio_state); } else { led = num & BP_GPIO_NUM_MASK; led = g_optled_map[led]; bcm_led_driver_state[led].led_state = state; bcm_led_driver_state[led].led_bp = num; led_do_set(led); } } void bcm_led_driver_toggle(unsigned short num) { unsigned short led; led = num & BP_GPIO_NUM_MASK; if(LedData == NULL) return; if (num & BP_LED_USE_GPIO) { bcm_gpio_set_dir(num, 1); bcm_gpio_set_data(num, GPIO_NUM_TO_MASK(num)^bcm_gpio_get_data(num)); } else { led = g_optled_map[led]; if ( bcm_led_driver_state[led].is_hw_led ) { // HW LEDs just toggle *LedData = *LedData ^ LED_NUM_TO_MASK(led); LED->hWLedEn &= ~LED_NUM_TO_MASK(led); } else { bcm_led_driver_state[led].led_state = bcm_led_driver_state[led].led_state ^ 1; bcm_led_driver_state[led].led_bp = num; led_do_set(led); } } } void bcm_led_zero_flash_rate(int channel) { return; } void bcm_led_set_source(unsigned int serial_sel, unsigned int hwled_sel) { LED->serialLedShiftSel = serial_sel; LED->hWLedEn = hwled_sel; return; } void bcm_common_led_init(void) { unsigned short order; LedData = &LED->SwData; if ((BP_SUCCESS == BpGetSerialLedShiftOrder(&order)) && (order == BP_SERIAL_LED_SHIFT_MSB_FIRST)) LED->glbCtrl |= LED_SERIAL_LED_MSB_FIRST; else LED->glbCtrl &= ~LED_SERIAL_LED_MSB_FIRST; return; } void bcm_common_led_setAllSoftLedsOff(void) { unsigned short gpio; unsigned short led; unsigned int parallel = 0; int i = 0, rc; void* token = NULL; for(;;) { rc = BpGetLedGpio(i, &token, &gpio); if( rc == BP_MAX_ITEM_EXCEEDED ) break; else if( rc == BP_SUCCESS && gpio != BP_GPIO_NONE ) { if (gpio & BP_LED_USE_GPIO) { bcm_led_driver_set(gpio, BCM_LED_OFF); } else { led = gpio & BP_GPIO_NUM_MASK; led = g_optled_map[led]; switch (gpio & (BP_ACTIVE_MASK | BP_GPIO_SERIAL)) { case (BP_ACTIVE_LOW | BP_GPIO_SERIAL): printk("Error: serial LED does not support active low polarity(gpio 0x%x)!\n", gpio); break; case (BP_ACTIVE_LOW): parallel &= ~LED_NUM_TO_MASK(led); break; case (BP_ACTIVE_HIGH): parallel |= LED_NUM_TO_MASK(led); break; default: break; } if (LED->hWLedEn & LED_NUM_TO_MASK(led)) { bcm_led_driver_state[led].is_hw_led = 1; #ifdef BCM_LED_DEBUG printk("off: LED %d is HW\n",led); #endif } else { bcm_led_driver_set( gpio, 0 ); #ifdef BCM_LED_DEBUG printk("off: LED %d is OFF\n",led); #endif } } } else { token = 0; i++; } } #ifdef BCM_LED_DEBUG for (i = 0; i < 64 ; i++) { if ((i & 0x7) == 0) { printk("\noptled_map %d:",i); } printk(" %4d",g_optled_map[i]); } #endif /* setup output polarity */ LED->ParallelLedPolarity = parallel; #ifdef BCM_LED_DEBUG printk("parallel 0x%x\n", parallel); #endif return; } void bcm_common_led_setInitial(void) { unsigned short gpio; if( BpGetBootloaderPowerOnLedGpio( &gpio ) == BP_SUCCESS ) bcm_led_driver_set( gpio, BCM_LED_ON ); if( BpGetWanDataLedGpio( &gpio ) == BP_SUCCESS ) bcm_led_driver_set( gpio, BCM_LED_OFF ); } static int bcm_common_led_linux_init(void) { #if defined(CONFIG_BCM_BCA_LED) struct property *prop=NULL; struct device_node *np=NULL; if ((np = of_find_compatible_node(NULL, NULL, "brcm,bca-leds-ctrl")) || (np = of_find_compatible_node(NULL, NULL, "brcm,bca-cleds-ctrl")) || (np = of_find_compatible_node(NULL, NULL, "brcm,bca-cleds-ctrl,v2")) || (np = of_find_compatible_node(NULL, NULL, "brcm,bca-cleds-ctrl,legacy"))) { prop=of_find_property(np, "status", NULL); if (prop && !strncmp(prop->value, "okay", 4)) { printk("Skip Legacy Led init\n"); return 0; } } #endif bcm_common_led_init(); bcm_common_led_setAllSoftLedsOff(); bcm_common_led_setInitial(); return 0; } subsys_initcall(bcm_common_led_linux_init);