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