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 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 165 static 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 176 static 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 192 static int sunfire_clockboard_led_probe(struct platform_device *pdev) 193 { 194 return sunfire_led_generic_probe(pdev, clockboard_led_types); 195 } 196 197 static 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 213 static int sunfire_fhc_led_probe(struct platform_device *pdev) 214 { 215 return sunfire_led_generic_probe(pdev, fhc_led_types); 216 } 217 218 MODULE_ALIAS("platform:sunfire-clockboard-leds"); 219 MODULE_ALIAS("platform:sunfire-fhc-leds"); 220 221 static 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 229 static 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 237 static struct platform_driver * const drivers[] = { 238 &sunfire_clockboard_led_driver, 239 &sunfire_fhc_led_driver, 240 }; 241 242 static int __init sunfire_leds_init(void) 243 { 244 return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 245 } 246 247 static void __exit sunfire_leds_exit(void) 248 { 249 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 250 } 251 252 module_init(sunfire_leds_init); 253 module_exit(sunfire_leds_exit); 254