brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 1c116aa Raw
222 lines · c
1/*2 * drivers/leds/leds-apu.c3 * Copyright (C) 2017 Alan Mizrahi, alan at mizrahi dot com dot ve4 *5 * Redistribution and use in source and binary forms, with or without6 * modification, are permitted provided that the following conditions are met:7 *8 * 1. Redistributions of source code must retain the above copyright9 *    notice, this list of conditions and the following disclaimer.10 * 2. Redistributions in binary form must reproduce the above copyright11 *    notice, this list of conditions and the following disclaimer in the12 *    documentation and/or other materials provided with the distribution.13 * 3. Neither the names of the copyright holders nor the names of its14 *    contributors may be used to endorse or promote products derived from15 *    this software without specific prior written permission.16 *17 * Alternatively, this software may be distributed under the terms of the18 * GNU General Public License ("GPL") version 2 as published by the Free19 * Software Foundation.20 *21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE31 * POSSIBILITY OF SUCH DAMAGE.32 */33 34#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt35 36#include <linux/dmi.h>37#include <linux/err.h>38#include <linux/init.h>39#include <linux/io.h>40#include <linux/kernel.h>41#include <linux/leds.h>42#include <linux/module.h>43#include <linux/platform_device.h>44 45#define APU1_FCH_ACPI_MMIO_BASE 0xFED8000046#define APU1_FCH_GPIO_BASE      (APU1_FCH_ACPI_MMIO_BASE + 0x01BD)47#define APU1_LEDON              0x0848#define APU1_LEDOFF             0xC849#define APU1_NUM_GPIO           350#define APU1_IOSIZE             sizeof(u8)51 52/* LED access parameters */53struct apu_param {54	void __iomem *addr; /* for ioread/iowrite */55};56 57/* LED private data */58struct apu_led_priv {59	struct led_classdev cdev;60	struct apu_param param;61};62#define cdev_to_priv(c) container_of(c, struct apu_led_priv, cdev)63 64/* LED profile */65struct apu_led_profile {66	const char *name;67	enum led_brightness brightness;68	unsigned long offset; /* for devm_ioremap */69};70 71struct apu_led_pdata {72	struct platform_device *pdev;73	struct apu_led_priv *pled;74	spinlock_t lock;75};76 77static struct apu_led_pdata *apu_led;78 79static const struct apu_led_profile apu1_led_profile[] = {80	{ "apu:green:1", LED_ON,  APU1_FCH_GPIO_BASE + 0 * APU1_IOSIZE },81	{ "apu:green:2", LED_OFF, APU1_FCH_GPIO_BASE + 1 * APU1_IOSIZE },82	{ "apu:green:3", LED_OFF, APU1_FCH_GPIO_BASE + 2 * APU1_IOSIZE },83};84 85static const struct dmi_system_id apu_led_dmi_table[] __initconst = {86	/* PC Engines APU with factory bios "SageBios_PCEngines_APU-45" */87	{88		.ident = "apu",89		.matches = {90			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),91			DMI_MATCH(DMI_PRODUCT_NAME, "APU")92		}93	},94	/* PC Engines APU with "Mainline" bios >= 4.6.8 */95	{96		.ident = "apu",97		.matches = {98			DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),99			DMI_MATCH(DMI_PRODUCT_NAME, "apu1")100		}101	},102	{}103};104MODULE_DEVICE_TABLE(dmi, apu_led_dmi_table);105 106static void apu1_led_brightness_set(struct led_classdev *led, enum led_brightness value)107{108	struct apu_led_priv *pled = cdev_to_priv(led);109 110	spin_lock(&apu_led->lock);111	iowrite8(value ? APU1_LEDON : APU1_LEDOFF, pled->param.addr);112	spin_unlock(&apu_led->lock);113}114 115static int apu_led_config(struct device *dev, struct apu_led_pdata *apuld)116{117	int i;118	int err;119 120	apu_led->pled = devm_kcalloc(dev,121		ARRAY_SIZE(apu1_led_profile), sizeof(struct apu_led_priv),122		GFP_KERNEL);123 124	if (!apu_led->pled)125		return -ENOMEM;126 127	for (i = 0; i < ARRAY_SIZE(apu1_led_profile); i++) {128		struct apu_led_priv *pled = &apu_led->pled[i];129		struct led_classdev *led_cdev = &pled->cdev;130 131		led_cdev->name = apu1_led_profile[i].name;132		led_cdev->brightness = apu1_led_profile[i].brightness;133		led_cdev->max_brightness = 1;134		led_cdev->flags = LED_CORE_SUSPENDRESUME;135		led_cdev->brightness_set = apu1_led_brightness_set;136 137		pled->param.addr = devm_ioremap(dev,138				apu1_led_profile[i].offset, APU1_IOSIZE);139		if (!pled->param.addr) {140			err = -ENOMEM;141			goto error;142		}143 144		err = led_classdev_register(dev, led_cdev);145		if (err)146			goto error;147 148		apu1_led_brightness_set(led_cdev, apu1_led_profile[i].brightness);149	}150 151	return 0;152 153error:154	while (i-- > 0)155		led_classdev_unregister(&apu_led->pled[i].cdev);156 157	return err;158}159 160static int __init apu_led_probe(struct platform_device *pdev)161{162	apu_led = devm_kzalloc(&pdev->dev, sizeof(*apu_led), GFP_KERNEL);163 164	if (!apu_led)165		return -ENOMEM;166 167	apu_led->pdev = pdev;168 169	spin_lock_init(&apu_led->lock);170	return apu_led_config(&pdev->dev, apu_led);171}172 173static struct platform_driver apu_led_driver = {174	.driver = {175		.name = KBUILD_MODNAME,176	},177};178 179static int __init apu_led_init(void)180{181	struct platform_device *pdev;182	int err;183 184	if (!dmi_check_system(apu_led_dmi_table)) {185		pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n");186		return -ENODEV;187	}188 189	pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);190	if (IS_ERR(pdev)) {191		pr_err("Device allocation failed\n");192		return PTR_ERR(pdev);193	}194 195	err = platform_driver_probe(&apu_led_driver, apu_led_probe);196	if (err) {197		pr_err("Probe platform driver failed\n");198		platform_device_unregister(pdev);199	}200 201	return err;202}203 204static void __exit apu_led_exit(void)205{206	int i;207 208	for (i = 0; i < ARRAY_SIZE(apu1_led_profile); i++)209		led_classdev_unregister(&apu_led->pled[i].cdev);210 211	platform_device_unregister(apu_led->pdev);212	platform_driver_unregister(&apu_led_driver);213}214 215module_init(apu_led_init);216module_exit(apu_led_exit);217 218MODULE_AUTHOR("Alan Mizrahi");219MODULE_DESCRIPTION("PC Engines APU1 front LED driver");220MODULE_LICENSE("GPL v2");221MODULE_ALIAS("platform:leds_apu");222