Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86: Add SEL-3350 platform driver

Add a driver for Schweitzer Engineering Laboratories SEL-3350 computers
front LEDs and power supplies. LED and power supply status is provided
by the Intel SoC GPIO.

Signed-off-by: Robert Joslyn <robert.joslyn@redrectangle.org>
Link: https://lore.kernel.org/r/20230713035714.807819-1-robert.joslyn@redrectangle.org
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Robert Joslyn and committed by
Hans de Goede
81ad5695 40e6c395

+269
+15
drivers/platform/x86/Kconfig
··· 1141 1141 buttons below the display. This module adds an input device 1142 1142 that delivers key events when these buttons are pressed. 1143 1143 1144 + config SEL3350_PLATFORM 1145 + tristate "SEL-3350 LEDs and power supplies" 1146 + depends on ACPI 1147 + depends on GPIOLIB 1148 + depends on PINCTRL_BROXTON 1149 + select POWER_SUPPLY 1150 + select NEW_LEDS 1151 + select LEDS_CLASS 1152 + select LEDS_GPIO 1153 + help 1154 + Support for LEDs and power supplies on SEL-3350 computers. 1155 + 1156 + To compile this driver as a module, choose M here: the module 1157 + will be called sel3350-platform. 1158 + 1144 1159 endif # X86_PLATFORM_DEVICES 1145 1160 1146 1161 config P2SB
+3
drivers/platform/x86/Makefile
··· 139 139 140 140 # Winmate 141 141 obj-$(CONFIG_WINMATE_FM07_KEYS) += winmate-fm07-keys.o 142 + 143 + # SEL 144 + obj-$(CONFIG_SEL3350_PLATFORM) += sel3350-platform.o
+251
drivers/platform/x86/sel3350-platform.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause 2 + /* 3 + * Copyright 2023 Schweitzer Engineering Laboratories, Inc. 4 + * 2350 NE Hopkins Court, Pullman, WA 99163 USA 5 + * 6 + * Platform support for the b2093 mainboard used in SEL-3350 computers. 7 + * Consumes GPIO from the SoC to provide standard LED and power supply 8 + * devices. 9 + */ 10 + 11 + #include <linux/acpi.h> 12 + #include <linux/gpio/consumer.h> 13 + #include <linux/gpio/machine.h> 14 + #include <linux/leds.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/power_supply.h> 18 + 19 + /* Broxton communities */ 20 + #define BXT_NW "INT3452:01" 21 + #define BXT_W "INT3452:02" 22 + #define BXT_SW "INT3452:03" 23 + 24 + #define B2093_GPIO_ACPI_ID "SEL0003" 25 + 26 + #define SEL_PS_A "sel_ps_a" 27 + #define SEL_PS_A_DETECT "sel_ps_a_detect" 28 + #define SEL_PS_A_GOOD "sel_ps_a_good" 29 + #define SEL_PS_B "sel_ps_b" 30 + #define SEL_PS_B_DETECT "sel_ps_b_detect" 31 + #define SEL_PS_B_GOOD "sel_ps_b_good" 32 + 33 + /* LEDs */ 34 + static const struct gpio_led sel3350_leds[] = { 35 + { .name = "sel:green:aux1" }, 36 + { .name = "sel:green:aux2" }, 37 + { .name = "sel:green:aux3" }, 38 + { .name = "sel:green:aux4" }, 39 + { .name = "sel:red:alarm" }, 40 + { .name = "sel:green:enabled", 41 + .default_state = LEDS_GPIO_DEFSTATE_ON }, 42 + { .name = "sel:red:aux1" }, 43 + { .name = "sel:red:aux2" }, 44 + { .name = "sel:red:aux3" }, 45 + { .name = "sel:red:aux4" }, 46 + }; 47 + 48 + static const struct gpio_led_platform_data sel3350_leds_pdata = { 49 + .num_leds = ARRAY_SIZE(sel3350_leds), 50 + .leds = sel3350_leds, 51 + }; 52 + 53 + /* Map GPIOs to LEDs */ 54 + static struct gpiod_lookup_table sel3350_leds_table = { 55 + .dev_id = "leds-gpio", 56 + .table = { 57 + GPIO_LOOKUP_IDX(BXT_NW, 49, NULL, 0, GPIO_ACTIVE_HIGH), 58 + GPIO_LOOKUP_IDX(BXT_NW, 50, NULL, 1, GPIO_ACTIVE_HIGH), 59 + GPIO_LOOKUP_IDX(BXT_NW, 51, NULL, 2, GPIO_ACTIVE_HIGH), 60 + GPIO_LOOKUP_IDX(BXT_NW, 52, NULL, 3, GPIO_ACTIVE_HIGH), 61 + GPIO_LOOKUP_IDX(BXT_W, 20, NULL, 4, GPIO_ACTIVE_HIGH), 62 + GPIO_LOOKUP_IDX(BXT_W, 21, NULL, 5, GPIO_ACTIVE_HIGH), 63 + GPIO_LOOKUP_IDX(BXT_SW, 37, NULL, 6, GPIO_ACTIVE_HIGH), 64 + GPIO_LOOKUP_IDX(BXT_SW, 38, NULL, 7, GPIO_ACTIVE_HIGH), 65 + GPIO_LOOKUP_IDX(BXT_SW, 39, NULL, 8, GPIO_ACTIVE_HIGH), 66 + GPIO_LOOKUP_IDX(BXT_SW, 40, NULL, 9, GPIO_ACTIVE_HIGH), 67 + {}, 68 + } 69 + }; 70 + 71 + /* Map GPIOs to power supplies */ 72 + static struct gpiod_lookup_table sel3350_gpios_table = { 73 + .dev_id = B2093_GPIO_ACPI_ID ":00", 74 + .table = { 75 + GPIO_LOOKUP(BXT_NW, 44, SEL_PS_A_DETECT, GPIO_ACTIVE_LOW), 76 + GPIO_LOOKUP(BXT_NW, 45, SEL_PS_A_GOOD, GPIO_ACTIVE_LOW), 77 + GPIO_LOOKUP(BXT_NW, 46, SEL_PS_B_DETECT, GPIO_ACTIVE_LOW), 78 + GPIO_LOOKUP(BXT_NW, 47, SEL_PS_B_GOOD, GPIO_ACTIVE_LOW), 79 + {}, 80 + } 81 + }; 82 + 83 + /* Power Supplies */ 84 + 85 + struct sel3350_power_cfg_data { 86 + struct gpio_desc *ps_detect; 87 + struct gpio_desc *ps_good; 88 + }; 89 + 90 + static int sel3350_power_get_property(struct power_supply *psy, 91 + enum power_supply_property psp, 92 + union power_supply_propval *val) 93 + { 94 + struct sel3350_power_cfg_data *data = power_supply_get_drvdata(psy); 95 + 96 + switch (psp) { 97 + case POWER_SUPPLY_PROP_HEALTH: 98 + if (gpiod_get_value(data->ps_detect)) { 99 + if (gpiod_get_value(data->ps_good)) 100 + val->intval = POWER_SUPPLY_HEALTH_GOOD; 101 + else 102 + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 103 + } else { 104 + val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 105 + } 106 + break; 107 + case POWER_SUPPLY_PROP_PRESENT: 108 + val->intval = gpiod_get_value(data->ps_detect); 109 + break; 110 + case POWER_SUPPLY_PROP_ONLINE: 111 + val->intval = gpiod_get_value(data->ps_good); 112 + break; 113 + default: 114 + return -EINVAL; 115 + } 116 + return 0; 117 + } 118 + 119 + static const enum power_supply_property sel3350_power_properties[] = { 120 + POWER_SUPPLY_PROP_HEALTH, 121 + POWER_SUPPLY_PROP_PRESENT, 122 + POWER_SUPPLY_PROP_ONLINE, 123 + }; 124 + 125 + static const struct power_supply_desc sel3350_ps_a_desc = { 126 + .name = SEL_PS_A, 127 + .type = POWER_SUPPLY_TYPE_MAINS, 128 + .properties = sel3350_power_properties, 129 + .num_properties = ARRAY_SIZE(sel3350_power_properties), 130 + .get_property = sel3350_power_get_property, 131 + }; 132 + 133 + static const struct power_supply_desc sel3350_ps_b_desc = { 134 + .name = SEL_PS_B, 135 + .type = POWER_SUPPLY_TYPE_MAINS, 136 + .properties = sel3350_power_properties, 137 + .num_properties = ARRAY_SIZE(sel3350_power_properties), 138 + .get_property = sel3350_power_get_property, 139 + }; 140 + 141 + struct sel3350_data { 142 + struct platform_device *leds_pdev; 143 + struct power_supply *ps_a; 144 + struct power_supply *ps_b; 145 + struct sel3350_power_cfg_data ps_a_cfg_data; 146 + struct sel3350_power_cfg_data ps_b_cfg_data; 147 + }; 148 + 149 + static int sel3350_probe(struct platform_device *pdev) 150 + { 151 + int rs; 152 + struct sel3350_data *sel3350; 153 + struct power_supply_config ps_cfg = {}; 154 + 155 + sel3350 = devm_kzalloc(&pdev->dev, sizeof(struct sel3350_data), GFP_KERNEL); 156 + if (!sel3350) 157 + return -ENOMEM; 158 + 159 + platform_set_drvdata(pdev, sel3350); 160 + 161 + gpiod_add_lookup_table(&sel3350_leds_table); 162 + gpiod_add_lookup_table(&sel3350_gpios_table); 163 + 164 + sel3350->leds_pdev = platform_device_register_data( 165 + NULL, 166 + "leds-gpio", 167 + PLATFORM_DEVID_NONE, 168 + &sel3350_leds_pdata, 169 + sizeof(sel3350_leds_pdata)); 170 + if (IS_ERR(sel3350->leds_pdev)) { 171 + rs = PTR_ERR(sel3350->leds_pdev); 172 + dev_err(&pdev->dev, "Failed registering platform device: %d\n", rs); 173 + goto err_platform; 174 + } 175 + 176 + /* Power Supply A */ 177 + sel3350->ps_a_cfg_data.ps_detect = devm_gpiod_get(&pdev->dev, 178 + SEL_PS_A_DETECT, 179 + GPIOD_IN); 180 + sel3350->ps_a_cfg_data.ps_good = devm_gpiod_get(&pdev->dev, 181 + SEL_PS_A_GOOD, 182 + GPIOD_IN); 183 + ps_cfg.drv_data = &sel3350->ps_a_cfg_data; 184 + sel3350->ps_a = devm_power_supply_register(&pdev->dev, 185 + &sel3350_ps_a_desc, 186 + &ps_cfg); 187 + if (IS_ERR(sel3350->ps_a)) { 188 + rs = PTR_ERR(sel3350->ps_a); 189 + dev_err(&pdev->dev, "Failed registering power supply A: %d\n", rs); 190 + goto err_ps; 191 + } 192 + 193 + /* Power Supply B */ 194 + sel3350->ps_b_cfg_data.ps_detect = devm_gpiod_get(&pdev->dev, 195 + SEL_PS_B_DETECT, 196 + GPIOD_IN); 197 + sel3350->ps_b_cfg_data.ps_good = devm_gpiod_get(&pdev->dev, 198 + SEL_PS_B_GOOD, 199 + GPIOD_IN); 200 + ps_cfg.drv_data = &sel3350->ps_b_cfg_data; 201 + sel3350->ps_b = devm_power_supply_register(&pdev->dev, 202 + &sel3350_ps_b_desc, 203 + &ps_cfg); 204 + if (IS_ERR(sel3350->ps_b)) { 205 + rs = PTR_ERR(sel3350->ps_b); 206 + dev_err(&pdev->dev, "Failed registering power supply B: %d\n", rs); 207 + goto err_ps; 208 + } 209 + 210 + return 0; 211 + 212 + err_ps: 213 + platform_device_unregister(sel3350->leds_pdev); 214 + err_platform: 215 + gpiod_remove_lookup_table(&sel3350_gpios_table); 216 + gpiod_remove_lookup_table(&sel3350_leds_table); 217 + 218 + return rs; 219 + } 220 + 221 + static int sel3350_remove(struct platform_device *pdev) 222 + { 223 + struct sel3350_data *sel3350 = platform_get_drvdata(pdev); 224 + 225 + platform_device_unregister(sel3350->leds_pdev); 226 + gpiod_remove_lookup_table(&sel3350_gpios_table); 227 + gpiod_remove_lookup_table(&sel3350_leds_table); 228 + 229 + return 0; 230 + } 231 + 232 + static const struct acpi_device_id sel3350_device_ids[] = { 233 + { B2093_GPIO_ACPI_ID, 0 }, 234 + { "", 0 }, 235 + }; 236 + MODULE_DEVICE_TABLE(acpi, sel3350_device_ids); 237 + 238 + static struct platform_driver sel3350_platform_driver = { 239 + .probe = sel3350_probe, 240 + .remove = sel3350_remove, 241 + .driver = { 242 + .name = "sel3350-platform", 243 + .acpi_match_table = sel3350_device_ids, 244 + }, 245 + }; 246 + module_platform_driver(sel3350_platform_driver); 247 + 248 + MODULE_AUTHOR("Schweitzer Engineering Laboratories"); 249 + MODULE_DESCRIPTION("SEL-3350 platform driver"); 250 + MODULE_LICENSE("Dual BSD/GPL"); 251 + MODULE_SOFTDEP("pre: pinctrl_broxton leds-gpio");