1 /* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver. 2 * 3 * Copyright (C) 2008 David S. Miller <davem@davemloft.net> 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/init.h> 9 #include <linux/leds.h> 10 #include <linux/io.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 14 #include <asm/fhc.h> 15 #include <asm/upa.h> 16 17 #define DRIVER_NAME "leds-sunfire" 18 #define PFX DRIVER_NAME ": " 19 20 MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); 21 MODULE_DESCRIPTION("Sun Fire LED driver"); 22 MODULE_LICENSE("GPL"); 23 24 struct 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 30 static 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 54 static 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 60 static 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 66 static 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 72 static 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 96 static 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 102 static 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 108 static 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 114 typedef void (*set_handler)(struct led_classdev *, enum led_brightness); 115 struct led_type { 116 const char *name; 117 set_handler handler; 118 const char *default_trigger; 119 }; 120 121 #define NUM_LEDS_PER_BOARD 3 122 struct sunfire_drvdata { 123 struct sunfire_led leds[NUM_LEDS_PER_BOARD]; 124 }; 125 126 static int __devinit 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 printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", 134 pdev->num_resources); 135 err = -EINVAL; 136 goto out; 137 } 138 139 p = kzalloc(sizeof(*p), GFP_KERNEL); 140 if (!p) { 141 printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); 142 err = -ENOMEM; 143 goto out; 144 } 145 146 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { 147 struct led_classdev *lp = &p->leds[i].led_cdev; 148 149 p->leds[i].reg = (void __iomem *) pdev->resource[0].start; 150 lp->name = types[i].name; 151 lp->brightness = LED_FULL; 152 lp->brightness_set = types[i].handler; 153 lp->default_trigger = types[i].default_trigger; 154 155 err = led_classdev_register(&pdev->dev, lp); 156 if (err) { 157 printk(KERN_ERR PFX "Could not register %s LED\n", 158 lp->name); 159 goto out_unregister_led_cdevs; 160 } 161 } 162 163 dev_set_drvdata(&pdev->dev, p); 164 165 return 0; 166 167 out_unregister_led_cdevs: 168 for (i--; i >= 0; i--) 169 led_classdev_unregister(&p->leds[i].led_cdev); 170 kfree(p); 171 out: 172 return err; 173 } 174 175 static int __devexit sunfire_led_generic_remove(struct platform_device *pdev) 176 { 177 struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev); 178 int i; 179 180 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) 181 led_classdev_unregister(&p->leds[i].led_cdev); 182 183 kfree(p); 184 185 return 0; 186 } 187 188 static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { 189 { 190 .name = "clockboard-left", 191 .handler = clockboard_left_set, 192 }, 193 { 194 .name = "clockboard-middle", 195 .handler = clockboard_middle_set, 196 }, 197 { 198 .name = "clockboard-right", 199 .handler = clockboard_right_set, 200 .default_trigger= "heartbeat", 201 }, 202 }; 203 204 static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev) 205 { 206 return sunfire_led_generic_probe(pdev, clockboard_led_types); 207 } 208 209 static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { 210 { 211 .name = "fhc-left", 212 .handler = fhc_left_set, 213 }, 214 { 215 .name = "fhc-middle", 216 .handler = fhc_middle_set, 217 }, 218 { 219 .name = "fhc-right", 220 .handler = fhc_right_set, 221 .default_trigger= "heartbeat", 222 }, 223 }; 224 225 static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev) 226 { 227 return sunfire_led_generic_probe(pdev, fhc_led_types); 228 } 229 230 MODULE_ALIAS("platform:sunfire-clockboard-leds"); 231 MODULE_ALIAS("platform:sunfire-fhc-leds"); 232 233 static struct platform_driver sunfire_clockboard_led_driver = { 234 .probe = sunfire_clockboard_led_probe, 235 .remove = __devexit_p(sunfire_led_generic_remove), 236 .driver = { 237 .name = "sunfire-clockboard-leds", 238 .owner = THIS_MODULE, 239 }, 240 }; 241 242 static struct platform_driver sunfire_fhc_led_driver = { 243 .probe = sunfire_fhc_led_probe, 244 .remove = __devexit_p(sunfire_led_generic_remove), 245 .driver = { 246 .name = "sunfire-fhc-leds", 247 .owner = THIS_MODULE, 248 }, 249 }; 250 251 static int __init sunfire_leds_init(void) 252 { 253 int err = platform_driver_register(&sunfire_clockboard_led_driver); 254 255 if (err) { 256 printk(KERN_ERR PFX "Could not register clock board LED driver\n"); 257 return err; 258 } 259 260 err = platform_driver_register(&sunfire_fhc_led_driver); 261 if (err) { 262 printk(KERN_ERR PFX "Could not register FHC LED driver\n"); 263 platform_driver_unregister(&sunfire_clockboard_led_driver); 264 } 265 266 return err; 267 } 268 269 static void __exit sunfire_leds_exit(void) 270 { 271 platform_driver_unregister(&sunfire_clockboard_led_driver); 272 platform_driver_unregister(&sunfire_fhc_led_driver); 273 } 274 275 module_init(sunfire_leds_init); 276 module_exit(sunfire_leds_exit); 277