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