leds-sunfire.c (5624B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver. 3 * 4 * Copyright (C) 2008 David S. Miller <davem@davemloft.net> 5 */ 6 7#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/init.h> 12#include <linux/leds.h> 13#include <linux/io.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16 17#include <asm/fhc.h> 18#include <asm/upa.h> 19 20MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 21MODULE_DESCRIPTION("Sun Fire LED driver"); 22MODULE_LICENSE("GPL"); 23 24struct sunfire_led { 25 struct led_classdev led_cdev; 26 void __iomem *reg; 27}; 28#define to_sunfire_led(d) container_of(d, struct sunfire_led, led_cdev) 29 30static void __clockboard_set(struct led_classdev *led_cdev, 31 enum led_brightness led_val, u8 bit) 32{ 33 struct sunfire_led *p = to_sunfire_led(led_cdev); 34 u8 reg = upa_readb(p->reg); 35 36 switch (bit) { 37 case CLOCK_CTRL_LLED: 38 if (led_val) 39 reg &= ~bit; 40 else 41 reg |= bit; 42 break; 43 44 default: 45 if (led_val) 46 reg |= bit; 47 else 48 reg &= ~bit; 49 break; 50 } 51 upa_writeb(reg, p->reg); 52} 53 54static void clockboard_left_set(struct led_classdev *led_cdev, 55 enum led_brightness led_val) 56{ 57 __clockboard_set(led_cdev, led_val, CLOCK_CTRL_LLED); 58} 59 60static void clockboard_middle_set(struct led_classdev *led_cdev, 61 enum led_brightness led_val) 62{ 63 __clockboard_set(led_cdev, led_val, CLOCK_CTRL_MLED); 64} 65 66static void clockboard_right_set(struct led_classdev *led_cdev, 67 enum led_brightness led_val) 68{ 69 __clockboard_set(led_cdev, led_val, CLOCK_CTRL_RLED); 70} 71 72static void __fhc_set(struct led_classdev *led_cdev, 73 enum led_brightness led_val, u32 bit) 74{ 75 struct sunfire_led *p = to_sunfire_led(led_cdev); 76 u32 reg = upa_readl(p->reg); 77 78 switch (bit) { 79 case FHC_CONTROL_LLED: 80 if (led_val) 81 reg &= ~bit; 82 else 83 reg |= bit; 84 break; 85 86 default: 87 if (led_val) 88 reg |= bit; 89 else 90 reg &= ~bit; 91 break; 92 } 93 upa_writel(reg, p->reg); 94} 95 96static void fhc_left_set(struct led_classdev *led_cdev, 97 enum led_brightness led_val) 98{ 99 __fhc_set(led_cdev, led_val, FHC_CONTROL_LLED); 100} 101 102static void fhc_middle_set(struct led_classdev *led_cdev, 103 enum led_brightness led_val) 104{ 105 __fhc_set(led_cdev, led_val, FHC_CONTROL_MLED); 106} 107 108static void fhc_right_set(struct led_classdev *led_cdev, 109 enum led_brightness led_val) 110{ 111 __fhc_set(led_cdev, led_val, FHC_CONTROL_RLED); 112} 113 114typedef void (*set_handler)(struct led_classdev *, enum led_brightness); 115struct led_type { 116 const char *name; 117 set_handler handler; 118 const char *default_trigger; 119}; 120 121#define NUM_LEDS_PER_BOARD 3 122struct sunfire_drvdata { 123 struct sunfire_led leds[NUM_LEDS_PER_BOARD]; 124}; 125 126static int sunfire_led_generic_probe(struct platform_device *pdev, 127 struct led_type *types) 128{ 129 struct sunfire_drvdata *p; 130 int i, err; 131 132 if (pdev->num_resources != 1) { 133 dev_err(&pdev->dev, "Wrong number of resources %d, should be 1\n", 134 pdev->num_resources); 135 return -EINVAL; 136 } 137 138 p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); 139 if (!p) 140 return -ENOMEM; 141 142 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { 143 struct led_classdev *lp = &p->leds[i].led_cdev; 144 145 p->leds[i].reg = (void __iomem *) pdev->resource[0].start; 146 lp->name = types[i].name; 147 lp->brightness = LED_FULL; 148 lp->brightness_set = types[i].handler; 149 lp->default_trigger = types[i].default_trigger; 150 151 err = led_classdev_register(&pdev->dev, lp); 152 if (err) { 153 dev_err(&pdev->dev, "Could not register %s LED\n", 154 lp->name); 155 for (i--; i >= 0; i--) 156 led_classdev_unregister(&p->leds[i].led_cdev); 157 return err; 158 } 159 } 160 161 platform_set_drvdata(pdev, p); 162 163 return 0; 164} 165 166static int sunfire_led_generic_remove(struct platform_device *pdev) 167{ 168 struct sunfire_drvdata *p = platform_get_drvdata(pdev); 169 int i; 170 171 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) 172 led_classdev_unregister(&p->leds[i].led_cdev); 173 174 return 0; 175} 176 177static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { 178 { 179 .name = "clockboard-left", 180 .handler = clockboard_left_set, 181 }, 182 { 183 .name = "clockboard-middle", 184 .handler = clockboard_middle_set, 185 }, 186 { 187 .name = "clockboard-right", 188 .handler = clockboard_right_set, 189 .default_trigger = "heartbeat", 190 }, 191}; 192 193static int sunfire_clockboard_led_probe(struct platform_device *pdev) 194{ 195 return sunfire_led_generic_probe(pdev, clockboard_led_types); 196} 197 198static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { 199 { 200 .name = "fhc-left", 201 .handler = fhc_left_set, 202 }, 203 { 204 .name = "fhc-middle", 205 .handler = fhc_middle_set, 206 }, 207 { 208 .name = "fhc-right", 209 .handler = fhc_right_set, 210 .default_trigger = "heartbeat", 211 }, 212}; 213 214static int sunfire_fhc_led_probe(struct platform_device *pdev) 215{ 216 return sunfire_led_generic_probe(pdev, fhc_led_types); 217} 218 219MODULE_ALIAS("platform:sunfire-clockboard-leds"); 220MODULE_ALIAS("platform:sunfire-fhc-leds"); 221 222static struct platform_driver sunfire_clockboard_led_driver = { 223 .probe = sunfire_clockboard_led_probe, 224 .remove = sunfire_led_generic_remove, 225 .driver = { 226 .name = "sunfire-clockboard-leds", 227 }, 228}; 229 230static struct platform_driver sunfire_fhc_led_driver = { 231 .probe = sunfire_fhc_led_probe, 232 .remove = sunfire_led_generic_remove, 233 .driver = { 234 .name = "sunfire-fhc-leds", 235 }, 236}; 237 238static struct platform_driver * const drivers[] = { 239 &sunfire_clockboard_led_driver, 240 &sunfire_fhc_led_driver, 241}; 242 243static int __init sunfire_leds_init(void) 244{ 245 return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 246} 247 248static void __exit sunfire_leds_exit(void) 249{ 250 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 251} 252 253module_init(sunfire_leds_init); 254module_exit(sunfire_leds_exit);