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 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 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 dev_err(&pdev->dev, "Could not allocate struct sunfire_drvdata\n"); 140 return -ENOMEM; 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 dev_err(&pdev->dev, "Could not register %s LED\n", 155 lp->name); 156 for (i--; i >= 0; i--) 157 led_classdev_unregister(&p->leds[i].led_cdev); 158 return err; 159 } 160 } 161 162 dev_set_drvdata(&pdev->dev, p); 163 164 return 0; 165 } 166 167 static int sunfire_led_generic_remove(struct platform_device *pdev) 168 { 169 struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev); 170 int i; 171 172 for (i = 0; i < NUM_LEDS_PER_BOARD; i++) 173 led_classdev_unregister(&p->leds[i].led_cdev); 174 175 return 0; 176 } 177 178 static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { 179 { 180 .name = "clockboard-left", 181 .handler = clockboard_left_set, 182 }, 183 { 184 .name = "clockboard-middle", 185 .handler = clockboard_middle_set, 186 }, 187 { 188 .name = "clockboard-right", 189 .handler = clockboard_right_set, 190 .default_trigger = "heartbeat", 191 }, 192 }; 193 194 static int sunfire_clockboard_led_probe(struct platform_device *pdev) 195 { 196 return sunfire_led_generic_probe(pdev, clockboard_led_types); 197 } 198 199 static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { 200 { 201 .name = "fhc-left", 202 .handler = fhc_left_set, 203 }, 204 { 205 .name = "fhc-middle", 206 .handler = fhc_middle_set, 207 }, 208 { 209 .name = "fhc-right", 210 .handler = fhc_right_set, 211 .default_trigger = "heartbeat", 212 }, 213 }; 214 215 static int sunfire_fhc_led_probe(struct platform_device *pdev) 216 { 217 return sunfire_led_generic_probe(pdev, fhc_led_types); 218 } 219 220 MODULE_ALIAS("platform:sunfire-clockboard-leds"); 221 MODULE_ALIAS("platform:sunfire-fhc-leds"); 222 223 static struct platform_driver sunfire_clockboard_led_driver = { 224 .probe = sunfire_clockboard_led_probe, 225 .remove = sunfire_led_generic_remove, 226 .driver = { 227 .name = "sunfire-clockboard-leds", 228 .owner = THIS_MODULE, 229 }, 230 }; 231 232 static struct platform_driver sunfire_fhc_led_driver = { 233 .probe = sunfire_fhc_led_probe, 234 .remove = sunfire_led_generic_remove, 235 .driver = { 236 .name = "sunfire-fhc-leds", 237 .owner = THIS_MODULE, 238 }, 239 }; 240 241 static int __init sunfire_leds_init(void) 242 { 243 int err = platform_driver_register(&sunfire_clockboard_led_driver); 244 245 if (err) { 246 pr_err("Could not register clock board LED driver\n"); 247 return err; 248 } 249 250 err = platform_driver_register(&sunfire_fhc_led_driver); 251 if (err) { 252 pr_err("Could not register FHC LED driver\n"); 253 platform_driver_unregister(&sunfire_clockboard_led_driver); 254 } 255 256 return err; 257 } 258 259 static void __exit sunfire_leds_exit(void) 260 { 261 platform_driver_unregister(&sunfire_clockboard_led_driver); 262 platform_driver_unregister(&sunfire_fhc_led_driver); 263 } 264 265 module_init(sunfire_leds_init); 266 module_exit(sunfire_leds_exit); 267