cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-apu.c (6398B)


      1/*
      2 * drivers/leds/leds-apu.c
      3 * Copyright (C) 2017 Alan Mizrahi, alan at mizrahi dot com dot ve
      4 *
      5 * Redistribution and use in source and binary forms, with or without
      6 * modification, are permitted provided that the following conditions are met:
      7 *
      8 * 1. Redistributions of source code must retain the above copyright
      9 *    notice, this list of conditions and the following disclaimer.
     10 * 2. Redistributions in binary form must reproduce the above copyright
     11 *    notice, this list of conditions and the following disclaimer in the
     12 *    documentation and/or other materials provided with the distribution.
     13 * 3. Neither the names of the copyright holders nor the names of its
     14 *    contributors may be used to endorse or promote products derived from
     15 *    this software without specific prior written permission.
     16 *
     17 * Alternatively, this software may be distributed under the terms of the
     18 * GNU General Public License ("GPL") version 2 as published by the Free
     19 * 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, THE
     23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     29 * 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 THE
     31 * POSSIBILITY OF SUCH DAMAGE.
     32 */
     33
     34#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     35
     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 0xFED80000
     46#define APU1_FCH_GPIO_BASE      (APU1_FCH_ACPI_MMIO_BASE + 0x01BD)
     47#define APU1_LEDON              0x08
     48#define APU1_LEDOFF             0xC8
     49#define APU1_NUM_GPIO           3
     50#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_match(DMI_SYS_VENDOR, "PC Engines") &&
    185	      (dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "apu1")))) {
    186		pr_err("No PC Engines APUv1 board detected. For APUv2,3 support, enable CONFIG_PCENGINES_APU2\n");
    187		return -ENODEV;
    188	}
    189
    190	pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
    191	if (IS_ERR(pdev)) {
    192		pr_err("Device allocation failed\n");
    193		return PTR_ERR(pdev);
    194	}
    195
    196	err = platform_driver_probe(&apu_led_driver, apu_led_probe);
    197	if (err) {
    198		pr_err("Probe platform driver failed\n");
    199		platform_device_unregister(pdev);
    200	}
    201
    202	return err;
    203}
    204
    205static void __exit apu_led_exit(void)
    206{
    207	int i;
    208
    209	for (i = 0; i < ARRAY_SIZE(apu1_led_profile); i++)
    210		led_classdev_unregister(&apu_led->pled[i].cdev);
    211
    212	platform_device_unregister(apu_led->pdev);
    213	platform_driver_unregister(&apu_led_driver);
    214}
    215
    216module_init(apu_led_init);
    217module_exit(apu_led_exit);
    218
    219MODULE_AUTHOR("Alan Mizrahi");
    220MODULE_DESCRIPTION("PC Engines APU1 front LED driver");
    221MODULE_LICENSE("GPL v2");
    222MODULE_ALIAS("platform:leds_apu");