0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0035
0036 #include <linux/dmi.h>
0037 #include <linux/err.h>
0038 #include <linux/init.h>
0039 #include <linux/io.h>
0040 #include <linux/kernel.h>
0041 #include <linux/leds.h>
0042 #include <linux/module.h>
0043 #include <linux/platform_device.h>
0044
0045 #define APU1_FCH_ACPI_MMIO_BASE 0xFED80000
0046 #define APU1_FCH_GPIO_BASE (APU1_FCH_ACPI_MMIO_BASE + 0x01BD)
0047 #define APU1_LEDON 0x08
0048 #define APU1_LEDOFF 0xC8
0049 #define APU1_NUM_GPIO 3
0050 #define APU1_IOSIZE sizeof(u8)
0051
0052
0053 struct apu_param {
0054 void __iomem *addr;
0055 };
0056
0057
0058 struct apu_led_priv {
0059 struct led_classdev cdev;
0060 struct apu_param param;
0061 };
0062 #define cdev_to_priv(c) container_of(c, struct apu_led_priv, cdev)
0063
0064
0065 struct apu_led_profile {
0066 const char *name;
0067 enum led_brightness brightness;
0068 unsigned long offset;
0069 };
0070
0071 struct apu_led_pdata {
0072 struct platform_device *pdev;
0073 struct apu_led_priv *pled;
0074 spinlock_t lock;
0075 };
0076
0077 static struct apu_led_pdata *apu_led;
0078
0079 static const struct apu_led_profile apu1_led_profile[] = {
0080 { "apu:green:1", LED_ON, APU1_FCH_GPIO_BASE + 0 * APU1_IOSIZE },
0081 { "apu:green:2", LED_OFF, APU1_FCH_GPIO_BASE + 1 * APU1_IOSIZE },
0082 { "apu:green:3", LED_OFF, APU1_FCH_GPIO_BASE + 2 * APU1_IOSIZE },
0083 };
0084
0085 static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
0086
0087 {
0088 .ident = "apu",
0089 .matches = {
0090 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0091 DMI_MATCH(DMI_PRODUCT_NAME, "APU")
0092 }
0093 },
0094
0095 {
0096 .ident = "apu",
0097 .matches = {
0098 DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
0099 DMI_MATCH(DMI_PRODUCT_NAME, "apu1")
0100 }
0101 },
0102 {}
0103 };
0104 MODULE_DEVICE_TABLE(dmi, apu_led_dmi_table);
0105
0106 static void apu1_led_brightness_set(struct led_classdev *led, enum led_brightness value)
0107 {
0108 struct apu_led_priv *pled = cdev_to_priv(led);
0109
0110 spin_lock(&apu_led->lock);
0111 iowrite8(value ? APU1_LEDON : APU1_LEDOFF, pled->param.addr);
0112 spin_unlock(&apu_led->lock);
0113 }
0114
0115 static int apu_led_config(struct device *dev, struct apu_led_pdata *apuld)
0116 {
0117 int i;
0118 int err;
0119
0120 apu_led->pled = devm_kcalloc(dev,
0121 ARRAY_SIZE(apu1_led_profile), sizeof(struct apu_led_priv),
0122 GFP_KERNEL);
0123
0124 if (!apu_led->pled)
0125 return -ENOMEM;
0126
0127 for (i = 0; i < ARRAY_SIZE(apu1_led_profile); i++) {
0128 struct apu_led_priv *pled = &apu_led->pled[i];
0129 struct led_classdev *led_cdev = &pled->cdev;
0130
0131 led_cdev->name = apu1_led_profile[i].name;
0132 led_cdev->brightness = apu1_led_profile[i].brightness;
0133 led_cdev->max_brightness = 1;
0134 led_cdev->flags = LED_CORE_SUSPENDRESUME;
0135 led_cdev->brightness_set = apu1_led_brightness_set;
0136
0137 pled->param.addr = devm_ioremap(dev,
0138 apu1_led_profile[i].offset, APU1_IOSIZE);
0139 if (!pled->param.addr) {
0140 err = -ENOMEM;
0141 goto error;
0142 }
0143
0144 err = led_classdev_register(dev, led_cdev);
0145 if (err)
0146 goto error;
0147
0148 apu1_led_brightness_set(led_cdev, apu1_led_profile[i].brightness);
0149 }
0150
0151 return 0;
0152
0153 error:
0154 while (i-- > 0)
0155 led_classdev_unregister(&apu_led->pled[i].cdev);
0156
0157 return err;
0158 }
0159
0160 static int __init apu_led_probe(struct platform_device *pdev)
0161 {
0162 apu_led = devm_kzalloc(&pdev->dev, sizeof(*apu_led), GFP_KERNEL);
0163
0164 if (!apu_led)
0165 return -ENOMEM;
0166
0167 apu_led->pdev = pdev;
0168
0169 spin_lock_init(&apu_led->lock);
0170 return apu_led_config(&pdev->dev, apu_led);
0171 }
0172
0173 static struct platform_driver apu_led_driver = {
0174 .driver = {
0175 .name = KBUILD_MODNAME,
0176 },
0177 };
0178
0179 static int __init apu_led_init(void)
0180 {
0181 struct platform_device *pdev;
0182 int err;
0183
0184 if (!(dmi_match(DMI_SYS_VENDOR, "PC Engines") &&
0185 (dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "apu1")))) {
0186 pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n");
0187 return -ENODEV;
0188 }
0189
0190 pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
0191 if (IS_ERR(pdev)) {
0192 pr_err("Device allocation failed\n");
0193 return PTR_ERR(pdev);
0194 }
0195
0196 err = platform_driver_probe(&apu_led_driver, apu_led_probe);
0197 if (err) {
0198 pr_err("Probe platform driver failed\n");
0199 platform_device_unregister(pdev);
0200 }
0201
0202 return err;
0203 }
0204
0205 static void __exit apu_led_exit(void)
0206 {
0207 int i;
0208
0209 for (i = 0; i < ARRAY_SIZE(apu1_led_profile); i++)
0210 led_classdev_unregister(&apu_led->pled[i].cdev);
0211
0212 platform_device_unregister(apu_led->pdev);
0213 platform_driver_unregister(&apu_led_driver);
0214 }
0215
0216 module_init(apu_led_init);
0217 module_exit(apu_led_exit);
0218
0219 MODULE_AUTHOR("Alan Mizrahi");
0220 MODULE_DESCRIPTION("PC Engines APU1 front LED driver");
0221 MODULE_LICENSE("GPL v2");
0222 MODULE_ALIAS("platform:leds_apu");