1be4fdf99SVadim Pasternak /* 2be4fdf99SVadim Pasternak * drivers/leds/leds-mlxcpld.c 3be4fdf99SVadim Pasternak * Copyright (c) 2016 Mellanox Technologies. All rights reserved. 4be4fdf99SVadim Pasternak * Copyright (c) 2016 Vadim Pasternak <vadimp@mellanox.com> 5be4fdf99SVadim Pasternak * 6be4fdf99SVadim Pasternak * Redistribution and use in source and binary forms, with or without 7be4fdf99SVadim Pasternak * modification, are permitted provided that the following conditions are met: 8be4fdf99SVadim Pasternak * 9be4fdf99SVadim Pasternak * 1. Redistributions of source code must retain the above copyright 10be4fdf99SVadim Pasternak * notice, this list of conditions and the following disclaimer. 11be4fdf99SVadim Pasternak * 2. Redistributions in binary form must reproduce the above copyright 12be4fdf99SVadim Pasternak * notice, this list of conditions and the following disclaimer in the 13be4fdf99SVadim Pasternak * documentation and/or other materials provided with the distribution. 14be4fdf99SVadim Pasternak * 3. Neither the names of the copyright holders nor the names of its 15be4fdf99SVadim Pasternak * contributors may be used to endorse or promote products derived from 16be4fdf99SVadim Pasternak * this software without specific prior written permission. 17be4fdf99SVadim Pasternak * 18be4fdf99SVadim Pasternak * Alternatively, this software may be distributed under the terms of the 19be4fdf99SVadim Pasternak * GNU General Public License ("GPL") version 2 as published by the Free 20be4fdf99SVadim Pasternak * Software Foundation. 21be4fdf99SVadim Pasternak * 22be4fdf99SVadim Pasternak * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23be4fdf99SVadim Pasternak * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24be4fdf99SVadim Pasternak * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25be4fdf99SVadim Pasternak * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 26be4fdf99SVadim Pasternak * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27be4fdf99SVadim Pasternak * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28be4fdf99SVadim Pasternak * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29be4fdf99SVadim Pasternak * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30be4fdf99SVadim Pasternak * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31be4fdf99SVadim Pasternak * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32be4fdf99SVadim Pasternak * POSSIBILITY OF SUCH DAMAGE. 33be4fdf99SVadim Pasternak */ 34be4fdf99SVadim Pasternak 35be4fdf99SVadim Pasternak #include <linux/acpi.h> 36be4fdf99SVadim Pasternak #include <linux/device.h> 37be4fdf99SVadim Pasternak #include <linux/dmi.h> 38be4fdf99SVadim Pasternak #include <linux/hwmon.h> 39be4fdf99SVadim Pasternak #include <linux/hwmon-sysfs.h> 40be4fdf99SVadim Pasternak #include <linux/io.h> 41be4fdf99SVadim Pasternak #include <linux/leds.h> 42be4fdf99SVadim Pasternak #include <linux/module.h> 43be4fdf99SVadim Pasternak #include <linux/mod_devicetable.h> 44be4fdf99SVadim Pasternak #include <linux/platform_device.h> 45be4fdf99SVadim Pasternak #include <linux/slab.h> 46be4fdf99SVadim Pasternak 47be4fdf99SVadim Pasternak #define MLXPLAT_CPLD_LPC_REG_BASE_ADRR 0x2500 /* LPC bus access */ 48be4fdf99SVadim Pasternak 49be4fdf99SVadim Pasternak /* Color codes for LEDs */ 50be4fdf99SVadim Pasternak #define MLXCPLD_LED_OFFSET_HALF 0x01 /* Offset from solid: 3Hz blink */ 51be4fdf99SVadim Pasternak #define MLXCPLD_LED_OFFSET_FULL 0x02 /* Offset from solid: 6Hz blink */ 52be4fdf99SVadim Pasternak #define MLXCPLD_LED_IS_OFF 0x00 /* Off */ 53be4fdf99SVadim Pasternak #define MLXCPLD_LED_RED_STATIC_ON 0x05 /* Solid red */ 54be4fdf99SVadim Pasternak #define MLXCPLD_LED_RED_BLINK_HALF (MLXCPLD_LED_RED_STATIC_ON + \ 55be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_HALF) 56be4fdf99SVadim Pasternak #define MLXCPLD_LED_RED_BLINK_FULL (MLXCPLD_LED_RED_STATIC_ON + \ 57be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_FULL) 58be4fdf99SVadim Pasternak #define MLXCPLD_LED_GREEN_STATIC_ON 0x0D /* Solid green */ 59be4fdf99SVadim Pasternak #define MLXCPLD_LED_GREEN_BLINK_HALF (MLXCPLD_LED_GREEN_STATIC_ON + \ 60be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_HALF) 61be4fdf99SVadim Pasternak #define MLXCPLD_LED_GREEN_BLINK_FULL (MLXCPLD_LED_GREEN_STATIC_ON + \ 62be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_FULL) 63be4fdf99SVadim Pasternak #define MLXCPLD_LED_BLINK_3HZ 167 /* ~167 msec off/on */ 64be4fdf99SVadim Pasternak #define MLXCPLD_LED_BLINK_6HZ 83 /* ~83 msec off/on */ 65be4fdf99SVadim Pasternak 66be4fdf99SVadim Pasternak /** 67be4fdf99SVadim Pasternak * mlxcpld_param - LED access parameters: 68be4fdf99SVadim Pasternak * @offset - offset for LED access in CPLD device 69be4fdf99SVadim Pasternak * @mask - mask for LED access in CPLD device 70be4fdf99SVadim Pasternak * @base_color - base color code for LED 71be4fdf99SVadim Pasternak **/ 72be4fdf99SVadim Pasternak struct mlxcpld_param { 73be4fdf99SVadim Pasternak u8 offset; 74be4fdf99SVadim Pasternak u8 mask; 75be4fdf99SVadim Pasternak u8 base_color; 76be4fdf99SVadim Pasternak }; 77be4fdf99SVadim Pasternak 78be4fdf99SVadim Pasternak /** 79be4fdf99SVadim Pasternak * mlxcpld_led_priv - LED private data: 80be4fdf99SVadim Pasternak * @cled - LED class device instance 81be4fdf99SVadim Pasternak * @param - LED CPLD access parameters 82be4fdf99SVadim Pasternak **/ 83be4fdf99SVadim Pasternak struct mlxcpld_led_priv { 84be4fdf99SVadim Pasternak struct led_classdev cdev; 85be4fdf99SVadim Pasternak struct mlxcpld_param param; 86be4fdf99SVadim Pasternak }; 87be4fdf99SVadim Pasternak 88be4fdf99SVadim Pasternak #define cdev_to_priv(c) container_of(c, struct mlxcpld_led_priv, cdev) 89be4fdf99SVadim Pasternak 90be4fdf99SVadim Pasternak /** 91be4fdf99SVadim Pasternak * mlxcpld_led_profile - system LED profile (defined per system class): 92be4fdf99SVadim Pasternak * @offset - offset for LED access in CPLD device 93be4fdf99SVadim Pasternak * @mask - mask for LED access in CPLD device 94be4fdf99SVadim Pasternak * @base_color - base color code 95be4fdf99SVadim Pasternak * @brightness - default brightness setting (on/off) 96be4fdf99SVadim Pasternak * @name - LED name 97be4fdf99SVadim Pasternak **/ 98be4fdf99SVadim Pasternak struct mlxcpld_led_profile { 99be4fdf99SVadim Pasternak u8 offset; 100be4fdf99SVadim Pasternak u8 mask; 101be4fdf99SVadim Pasternak u8 base_color; 102be4fdf99SVadim Pasternak enum led_brightness brightness; 103be4fdf99SVadim Pasternak const char *name; 104be4fdf99SVadim Pasternak }; 105be4fdf99SVadim Pasternak 106be4fdf99SVadim Pasternak /** 107be4fdf99SVadim Pasternak * mlxcpld_led_pdata - system LED private data 108be4fdf99SVadim Pasternak * @pdev - platform device pointer 109be4fdf99SVadim Pasternak * @pled - LED class device instance 110be4fdf99SVadim Pasternak * @profile - system configuration profile 111be4fdf99SVadim Pasternak * @num_led_instances - number of LED instances 112be4fdf99SVadim Pasternak * @lock - device access lock 113be4fdf99SVadim Pasternak **/ 114be4fdf99SVadim Pasternak struct mlxcpld_led_pdata { 115be4fdf99SVadim Pasternak struct platform_device *pdev; 116be4fdf99SVadim Pasternak struct mlxcpld_led_priv *pled; 117be4fdf99SVadim Pasternak struct mlxcpld_led_profile *profile; 118be4fdf99SVadim Pasternak int num_led_instances; 119be4fdf99SVadim Pasternak spinlock_t lock; 120be4fdf99SVadim Pasternak }; 121be4fdf99SVadim Pasternak 122be4fdf99SVadim Pasternak static struct mlxcpld_led_pdata *mlxcpld_led; 123be4fdf99SVadim Pasternak 124be4fdf99SVadim Pasternak /* Default profile fit the next Mellanox systems: 125be4fdf99SVadim Pasternak * "msx6710", "msx6720", "msb7700", "msn2700", "msx1410", 126be4fdf99SVadim Pasternak * "msn2410", "msb7800", "msn2740" 127be4fdf99SVadim Pasternak */ 128be4fdf99SVadim Pasternak static struct mlxcpld_led_profile mlxcpld_led_default_profile[] = { 129be4fdf99SVadim Pasternak { 130be4fdf99SVadim Pasternak 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 131be4fdf99SVadim Pasternak "mlxcpld:fan1:green", 132be4fdf99SVadim Pasternak }, 133be4fdf99SVadim Pasternak { 134be4fdf99SVadim Pasternak 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 135be4fdf99SVadim Pasternak "mlxcpld:fan1:red", 136be4fdf99SVadim Pasternak }, 137be4fdf99SVadim Pasternak { 138be4fdf99SVadim Pasternak 0x21, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, 139be4fdf99SVadim Pasternak "mlxcpld:fan2:green", 140be4fdf99SVadim Pasternak }, 141be4fdf99SVadim Pasternak { 142be4fdf99SVadim Pasternak 0x21, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 143be4fdf99SVadim Pasternak "mlxcpld:fan2:red", 144be4fdf99SVadim Pasternak }, 145be4fdf99SVadim Pasternak { 146be4fdf99SVadim Pasternak 0x22, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 147be4fdf99SVadim Pasternak "mlxcpld:fan3:green", 148be4fdf99SVadim Pasternak }, 149be4fdf99SVadim Pasternak { 150be4fdf99SVadim Pasternak 0x22, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 151be4fdf99SVadim Pasternak "mlxcpld:fan3:red", 152be4fdf99SVadim Pasternak }, 153be4fdf99SVadim Pasternak { 154be4fdf99SVadim Pasternak 0x22, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, 155be4fdf99SVadim Pasternak "mlxcpld:fan4:green", 156be4fdf99SVadim Pasternak }, 157be4fdf99SVadim Pasternak { 158be4fdf99SVadim Pasternak 0x22, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 159be4fdf99SVadim Pasternak "mlxcpld:fan4:red", 160be4fdf99SVadim Pasternak }, 161be4fdf99SVadim Pasternak { 162be4fdf99SVadim Pasternak 0x20, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, 163be4fdf99SVadim Pasternak "mlxcpld:psu:green", 164be4fdf99SVadim Pasternak }, 165be4fdf99SVadim Pasternak { 166be4fdf99SVadim Pasternak 0x20, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 167be4fdf99SVadim Pasternak "mlxcpld:psu:red", 168be4fdf99SVadim Pasternak }, 169be4fdf99SVadim Pasternak { 170be4fdf99SVadim Pasternak 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 171be4fdf99SVadim Pasternak "mlxcpld:status:green", 172be4fdf99SVadim Pasternak }, 173be4fdf99SVadim Pasternak { 174be4fdf99SVadim Pasternak 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 175be4fdf99SVadim Pasternak "mlxcpld:status:red", 176be4fdf99SVadim Pasternak }, 177be4fdf99SVadim Pasternak }; 178be4fdf99SVadim Pasternak 179be4fdf99SVadim Pasternak /* Profile fit the Mellanox systems based on "msn2100" */ 180be4fdf99SVadim Pasternak static struct mlxcpld_led_profile mlxcpld_led_msn2100_profile[] = { 181be4fdf99SVadim Pasternak { 182be4fdf99SVadim Pasternak 0x21, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 183be4fdf99SVadim Pasternak "mlxcpld:fan:green", 184be4fdf99SVadim Pasternak }, 185be4fdf99SVadim Pasternak { 186be4fdf99SVadim Pasternak 0x21, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 187be4fdf99SVadim Pasternak "mlxcpld:fan:red", 188be4fdf99SVadim Pasternak }, 189be4fdf99SVadim Pasternak { 190be4fdf99SVadim Pasternak 0x23, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 191be4fdf99SVadim Pasternak "mlxcpld:psu1:green", 192be4fdf99SVadim Pasternak }, 193be4fdf99SVadim Pasternak { 194be4fdf99SVadim Pasternak 0x23, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 195be4fdf99SVadim Pasternak "mlxcpld:psu1:red", 196be4fdf99SVadim Pasternak }, 197be4fdf99SVadim Pasternak { 198be4fdf99SVadim Pasternak 0x23, 0x0f, MLXCPLD_LED_GREEN_STATIC_ON, 1, 199be4fdf99SVadim Pasternak "mlxcpld:psu2:green", 200be4fdf99SVadim Pasternak }, 201be4fdf99SVadim Pasternak { 202be4fdf99SVadim Pasternak 0x23, 0x0f, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 203be4fdf99SVadim Pasternak "mlxcpld:psu2:red", 204be4fdf99SVadim Pasternak }, 205be4fdf99SVadim Pasternak { 206be4fdf99SVadim Pasternak 0x20, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, 1, 207be4fdf99SVadim Pasternak "mlxcpld:status:green", 208be4fdf99SVadim Pasternak }, 209be4fdf99SVadim Pasternak { 210be4fdf99SVadim Pasternak 0x20, 0xf0, MLXCPLD_LED_RED_STATIC_ON, LED_OFF, 211be4fdf99SVadim Pasternak "mlxcpld:status:red", 212be4fdf99SVadim Pasternak }, 213be4fdf99SVadim Pasternak { 214be4fdf99SVadim Pasternak 0x24, 0xf0, MLXCPLD_LED_GREEN_STATIC_ON, LED_OFF, 215be4fdf99SVadim Pasternak "mlxcpld:uid:blue", 216be4fdf99SVadim Pasternak }, 217be4fdf99SVadim Pasternak }; 218be4fdf99SVadim Pasternak 219be4fdf99SVadim Pasternak enum mlxcpld_led_platform_types { 220be4fdf99SVadim Pasternak MLXCPLD_LED_PLATFORM_DEFAULT, 221be4fdf99SVadim Pasternak MLXCPLD_LED_PLATFORM_MSN2100, 222be4fdf99SVadim Pasternak }; 223be4fdf99SVadim Pasternak 224be4fdf99SVadim Pasternak static const char *mlx_product_names[] = { 225be4fdf99SVadim Pasternak "DEFAULT", 226be4fdf99SVadim Pasternak "MSN2100", 227be4fdf99SVadim Pasternak }; 228be4fdf99SVadim Pasternak 229be4fdf99SVadim Pasternak static enum 230be4fdf99SVadim Pasternak mlxcpld_led_platform_types mlxcpld_led_platform_check_sys_type(void) 231be4fdf99SVadim Pasternak { 232be4fdf99SVadim Pasternak const char *mlx_product_name; 233be4fdf99SVadim Pasternak int i; 234be4fdf99SVadim Pasternak 235be4fdf99SVadim Pasternak mlx_product_name = dmi_get_system_info(DMI_PRODUCT_NAME); 236be4fdf99SVadim Pasternak if (!mlx_product_name) 237be4fdf99SVadim Pasternak return MLXCPLD_LED_PLATFORM_DEFAULT; 238be4fdf99SVadim Pasternak 239be4fdf99SVadim Pasternak for (i = 1; i < ARRAY_SIZE(mlx_product_names); i++) { 240be4fdf99SVadim Pasternak if (strstr(mlx_product_name, mlx_product_names[i])) 241be4fdf99SVadim Pasternak return i; 242be4fdf99SVadim Pasternak } 243be4fdf99SVadim Pasternak 244be4fdf99SVadim Pasternak return MLXCPLD_LED_PLATFORM_DEFAULT; 245be4fdf99SVadim Pasternak } 246be4fdf99SVadim Pasternak 247be4fdf99SVadim Pasternak static void mlxcpld_led_bus_access_func(u16 base, u8 offset, u8 rw_flag, 248be4fdf99SVadim Pasternak u8 *data) 249be4fdf99SVadim Pasternak { 250be4fdf99SVadim Pasternak u32 addr = base + offset; 251be4fdf99SVadim Pasternak 252be4fdf99SVadim Pasternak if (rw_flag == 0) 253be4fdf99SVadim Pasternak outb(*data, addr); 254be4fdf99SVadim Pasternak else 255be4fdf99SVadim Pasternak *data = inb(addr); 256be4fdf99SVadim Pasternak } 257be4fdf99SVadim Pasternak 258be4fdf99SVadim Pasternak static void mlxcpld_led_store_hw(u8 mask, u8 off, u8 vset) 259be4fdf99SVadim Pasternak { 260be4fdf99SVadim Pasternak u8 nib, val; 261be4fdf99SVadim Pasternak 262be4fdf99SVadim Pasternak /* 263be4fdf99SVadim Pasternak * Each LED is controlled through low or high nibble of the relevant 264be4fdf99SVadim Pasternak * CPLD register. Register offset is specified by off parameter. 265be4fdf99SVadim Pasternak * Parameter vset provides color code: 0x0 for off, 0x5 for solid red, 266be4fdf99SVadim Pasternak * 0x6 for 3Hz blink red, 0xd for solid green, 0xe for 3Hz blink 267be4fdf99SVadim Pasternak * green. 268be4fdf99SVadim Pasternak * Parameter mask specifies which nibble is used for specific LED: mask 269be4fdf99SVadim Pasternak * 0xf0 - lower nibble is to be used (bits from 0 to 3), mask 0x0f - 270be4fdf99SVadim Pasternak * higher nibble (bits from 4 to 7). 271be4fdf99SVadim Pasternak */ 272be4fdf99SVadim Pasternak spin_lock(&mlxcpld_led->lock); 273be4fdf99SVadim Pasternak mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 1, 274be4fdf99SVadim Pasternak &val); 275be4fdf99SVadim Pasternak nib = (mask == 0xf0) ? vset : (vset << 4); 276be4fdf99SVadim Pasternak val = (val & mask) | nib; 277be4fdf99SVadim Pasternak mlxcpld_led_bus_access_func(MLXPLAT_CPLD_LPC_REG_BASE_ADRR, off, 0, 278be4fdf99SVadim Pasternak &val); 279be4fdf99SVadim Pasternak spin_unlock(&mlxcpld_led->lock); 280be4fdf99SVadim Pasternak } 281be4fdf99SVadim Pasternak 282be4fdf99SVadim Pasternak static void mlxcpld_led_brightness_set(struct led_classdev *led, 283be4fdf99SVadim Pasternak enum led_brightness value) 284be4fdf99SVadim Pasternak { 285be4fdf99SVadim Pasternak struct mlxcpld_led_priv *pled = cdev_to_priv(led); 286be4fdf99SVadim Pasternak 287be4fdf99SVadim Pasternak if (value) { 288be4fdf99SVadim Pasternak mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, 289be4fdf99SVadim Pasternak pled->param.base_color); 290be4fdf99SVadim Pasternak return; 291be4fdf99SVadim Pasternak } 292be4fdf99SVadim Pasternak 293be4fdf99SVadim Pasternak mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, 294be4fdf99SVadim Pasternak MLXCPLD_LED_IS_OFF); 295be4fdf99SVadim Pasternak } 296be4fdf99SVadim Pasternak 297be4fdf99SVadim Pasternak static int mlxcpld_led_blink_set(struct led_classdev *led, 298be4fdf99SVadim Pasternak unsigned long *delay_on, 299be4fdf99SVadim Pasternak unsigned long *delay_off) 300be4fdf99SVadim Pasternak { 301be4fdf99SVadim Pasternak struct mlxcpld_led_priv *pled = cdev_to_priv(led); 302be4fdf99SVadim Pasternak 303be4fdf99SVadim Pasternak /* 304be4fdf99SVadim Pasternak * HW supports two types of blinking: full (6Hz) and half (3Hz). 305be4fdf99SVadim Pasternak * For delay on/off zero default setting 3Hz is used. 306be4fdf99SVadim Pasternak */ 307be4fdf99SVadim Pasternak if (!(*delay_on == 0 && *delay_off == 0) && 308be4fdf99SVadim Pasternak !(*delay_on == MLXCPLD_LED_BLINK_3HZ && 309be4fdf99SVadim Pasternak *delay_off == MLXCPLD_LED_BLINK_3HZ) && 310be4fdf99SVadim Pasternak !(*delay_on == MLXCPLD_LED_BLINK_6HZ && 311be4fdf99SVadim Pasternak *delay_off == MLXCPLD_LED_BLINK_6HZ)) 312be4fdf99SVadim Pasternak return -EINVAL; 313be4fdf99SVadim Pasternak 314be4fdf99SVadim Pasternak if (*delay_on == MLXCPLD_LED_BLINK_6HZ) 315be4fdf99SVadim Pasternak mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, 316be4fdf99SVadim Pasternak pled->param.base_color + 317be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_FULL); 318be4fdf99SVadim Pasternak else 319be4fdf99SVadim Pasternak mlxcpld_led_store_hw(pled->param.mask, pled->param.offset, 320be4fdf99SVadim Pasternak pled->param.base_color + 321be4fdf99SVadim Pasternak MLXCPLD_LED_OFFSET_HALF); 322be4fdf99SVadim Pasternak 323be4fdf99SVadim Pasternak return 0; 324be4fdf99SVadim Pasternak } 325be4fdf99SVadim Pasternak 326be4fdf99SVadim Pasternak static int mlxcpld_led_config(struct device *dev, 327be4fdf99SVadim Pasternak struct mlxcpld_led_pdata *cpld) 328be4fdf99SVadim Pasternak { 329be4fdf99SVadim Pasternak int i; 330be4fdf99SVadim Pasternak int err; 331be4fdf99SVadim Pasternak 332*a86854d0SKees Cook cpld->pled = devm_kcalloc(dev, 333*a86854d0SKees Cook cpld->num_led_instances, 334*a86854d0SKees Cook sizeof(struct mlxcpld_led_priv), 335*a86854d0SKees Cook GFP_KERNEL); 336be4fdf99SVadim Pasternak if (!cpld->pled) 337be4fdf99SVadim Pasternak return -ENOMEM; 338be4fdf99SVadim Pasternak 339be4fdf99SVadim Pasternak for (i = 0; i < cpld->num_led_instances; i++) { 340be4fdf99SVadim Pasternak cpld->pled[i].cdev.name = cpld->profile[i].name; 341be4fdf99SVadim Pasternak cpld->pled[i].cdev.brightness = cpld->profile[i].brightness; 342be4fdf99SVadim Pasternak cpld->pled[i].cdev.max_brightness = 1; 343be4fdf99SVadim Pasternak cpld->pled[i].cdev.brightness_set = mlxcpld_led_brightness_set; 344be4fdf99SVadim Pasternak cpld->pled[i].cdev.blink_set = mlxcpld_led_blink_set; 345be4fdf99SVadim Pasternak cpld->pled[i].cdev.flags = LED_CORE_SUSPENDRESUME; 346be4fdf99SVadim Pasternak err = devm_led_classdev_register(dev, &cpld->pled[i].cdev); 347be4fdf99SVadim Pasternak if (err) 348be4fdf99SVadim Pasternak return err; 349be4fdf99SVadim Pasternak 350be4fdf99SVadim Pasternak cpld->pled[i].param.offset = mlxcpld_led->profile[i].offset; 351be4fdf99SVadim Pasternak cpld->pled[i].param.mask = mlxcpld_led->profile[i].mask; 352be4fdf99SVadim Pasternak cpld->pled[i].param.base_color = 353be4fdf99SVadim Pasternak mlxcpld_led->profile[i].base_color; 354be4fdf99SVadim Pasternak 355be4fdf99SVadim Pasternak if (mlxcpld_led->profile[i].brightness) 356be4fdf99SVadim Pasternak mlxcpld_led_brightness_set(&cpld->pled[i].cdev, 357be4fdf99SVadim Pasternak mlxcpld_led->profile[i].brightness); 358be4fdf99SVadim Pasternak } 359be4fdf99SVadim Pasternak 360be4fdf99SVadim Pasternak return 0; 361be4fdf99SVadim Pasternak } 362be4fdf99SVadim Pasternak 363be4fdf99SVadim Pasternak static int __init mlxcpld_led_probe(struct platform_device *pdev) 364be4fdf99SVadim Pasternak { 365be4fdf99SVadim Pasternak enum mlxcpld_led_platform_types mlxcpld_led_plat = 366be4fdf99SVadim Pasternak mlxcpld_led_platform_check_sys_type(); 367be4fdf99SVadim Pasternak 368be4fdf99SVadim Pasternak mlxcpld_led = devm_kzalloc(&pdev->dev, sizeof(*mlxcpld_led), 369be4fdf99SVadim Pasternak GFP_KERNEL); 370be4fdf99SVadim Pasternak if (!mlxcpld_led) 371be4fdf99SVadim Pasternak return -ENOMEM; 372be4fdf99SVadim Pasternak 373be4fdf99SVadim Pasternak mlxcpld_led->pdev = pdev; 374be4fdf99SVadim Pasternak 375be4fdf99SVadim Pasternak switch (mlxcpld_led_plat) { 376be4fdf99SVadim Pasternak case MLXCPLD_LED_PLATFORM_MSN2100: 377be4fdf99SVadim Pasternak mlxcpld_led->profile = mlxcpld_led_msn2100_profile; 378be4fdf99SVadim Pasternak mlxcpld_led->num_led_instances = 379be4fdf99SVadim Pasternak ARRAY_SIZE(mlxcpld_led_msn2100_profile); 380be4fdf99SVadim Pasternak break; 381be4fdf99SVadim Pasternak 382be4fdf99SVadim Pasternak default: 383be4fdf99SVadim Pasternak mlxcpld_led->profile = mlxcpld_led_default_profile; 384be4fdf99SVadim Pasternak mlxcpld_led->num_led_instances = 385be4fdf99SVadim Pasternak ARRAY_SIZE(mlxcpld_led_default_profile); 386be4fdf99SVadim Pasternak break; 387be4fdf99SVadim Pasternak } 388be4fdf99SVadim Pasternak 389be4fdf99SVadim Pasternak spin_lock_init(&mlxcpld_led->lock); 390be4fdf99SVadim Pasternak 391be4fdf99SVadim Pasternak return mlxcpld_led_config(&pdev->dev, mlxcpld_led); 392be4fdf99SVadim Pasternak } 393be4fdf99SVadim Pasternak 394be4fdf99SVadim Pasternak static struct platform_driver mlxcpld_led_driver = { 395be4fdf99SVadim Pasternak .driver = { 396be4fdf99SVadim Pasternak .name = KBUILD_MODNAME, 397be4fdf99SVadim Pasternak }, 398be4fdf99SVadim Pasternak }; 399be4fdf99SVadim Pasternak 400be4fdf99SVadim Pasternak static int __init mlxcpld_led_init(void) 401be4fdf99SVadim Pasternak { 402be4fdf99SVadim Pasternak struct platform_device *pdev; 403be4fdf99SVadim Pasternak int err; 404be4fdf99SVadim Pasternak 405dca897e2SVadim Pasternak if (!dmi_match(DMI_CHASSIS_VENDOR, "Mellanox Technologies Ltd.")) 406dca897e2SVadim Pasternak return -ENODEV; 407dca897e2SVadim Pasternak 408be4fdf99SVadim Pasternak pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); 409be4fdf99SVadim Pasternak if (IS_ERR(pdev)) { 410be4fdf99SVadim Pasternak pr_err("Device allocation failed\n"); 411be4fdf99SVadim Pasternak return PTR_ERR(pdev); 412be4fdf99SVadim Pasternak } 413be4fdf99SVadim Pasternak 414be4fdf99SVadim Pasternak err = platform_driver_probe(&mlxcpld_led_driver, mlxcpld_led_probe); 415be4fdf99SVadim Pasternak if (err) { 416be4fdf99SVadim Pasternak pr_err("Probe platform driver failed\n"); 417be4fdf99SVadim Pasternak platform_device_unregister(pdev); 418be4fdf99SVadim Pasternak } 419be4fdf99SVadim Pasternak 420be4fdf99SVadim Pasternak return err; 421be4fdf99SVadim Pasternak } 422be4fdf99SVadim Pasternak 423be4fdf99SVadim Pasternak static void __exit mlxcpld_led_exit(void) 424be4fdf99SVadim Pasternak { 425be4fdf99SVadim Pasternak platform_device_unregister(mlxcpld_led->pdev); 426be4fdf99SVadim Pasternak platform_driver_unregister(&mlxcpld_led_driver); 427be4fdf99SVadim Pasternak } 428be4fdf99SVadim Pasternak 429be4fdf99SVadim Pasternak module_init(mlxcpld_led_init); 430be4fdf99SVadim Pasternak module_exit(mlxcpld_led_exit); 431be4fdf99SVadim Pasternak 432be4fdf99SVadim Pasternak MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); 433be4fdf99SVadim Pasternak MODULE_DESCRIPTION("Mellanox board LED driver"); 434dca897e2SVadim Pasternak MODULE_LICENSE("Dual BSD/GPL"); 435be4fdf99SVadim Pasternak MODULE_ALIAS("platform:leds_mlxcpld"); 436