The following patch adds support for the PC Engines APU2 Embedded Board as a profile under the X86_64 target. More information on this board can be found at www.pcengines.ch/apu2c4.htm
Note that this patch is a part of an RFC, and should not be merged yet. Signed-off-by: Chris Blake <chrisrblak...@gmail.com> --- target/linux/x86/64/config-default | 12 + target/linux/x86/64/profiles/001-PCEngines.mk | 20 + target/linux/x86/base-files/etc/board.d/01_leds | 22 ++ target/linux/x86/base-files/etc/board.d/02_network | 26 ++ target/linux/x86/base-files/etc/diag.sh | 37 ++ target/linux/x86/base-files/lib/x86.sh | 13 + target/linux/x86/config-4.4 | 1 + .../linux/x86/files/drivers/gpio/gpio-nct5104d.c | 432 +++++++++++++++++++++ target/linux/x86/files/drivers/leds/leds-apu2.c | 371 ++++++++++++++++++ target/linux/x86/modules.mk | 36 ++ .../x86/patches-4.4/800-add-apu2-led-driver.patch | 29 ++ .../801-sp5100_tco-add-apu2-support.patch | 91 +++++ .../patches-4.4/802-add-nct5104d-gpio-driver.patch | 27 ++ 13 files changed, 1117 insertions(+) create mode 100644 target/linux/x86/64/profiles/001-PCEngines.mk create mode 100755 target/linux/x86/base-files/etc/board.d/01_leds create mode 100755 target/linux/x86/base-files/etc/board.d/02_network create mode 100755 target/linux/x86/base-files/etc/diag.sh create mode 100755 target/linux/x86/base-files/lib/x86.sh create mode 100644 target/linux/x86/files/drivers/gpio/gpio-nct5104d.c create mode 100644 target/linux/x86/files/drivers/leds/leds-apu2.c create mode 100644 target/linux/x86/modules.mk create mode 100644 target/linux/x86/patches-4.4/800-add-apu2-led-driver.patch create mode 100644 target/linux/x86/patches-4.4/801-sp5100_tco-add-apu2-support.patch create mode 100644 target/linux/x86/patches-4.4/802-add-nct5104d-gpio-driver.patch diff --git a/target/linux/x86/64/config-default b/target/linux/x86/64/config-default index 646e773..bbd1b44 100644 --- a/target/linux/x86/64/config-default +++ b/target/linux/x86/64/config-default @@ -74,6 +74,7 @@ CONFIG_CRYPTO_CRCT10DIF=y # CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set # CONFIG_CRYPTO_TWOFISH_X86_64 is not set # CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set +CONFIG_DEVMEM=y # CONFIG_EFI is not set CONFIG_FB=y CONFIG_FB_CMDLINE=y @@ -91,6 +92,14 @@ CONFIG_GART_IOMMU=y CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y CONFIG_GENERIC_CPU=y CONFIG_GENERIC_PENDING_IRQ=y +CONFIG_GPIOLIB=y +# CONFIG_GPIO_104_IDIO_16 is not set +# CONFIG_GPIO_AMDPT is not set +# CONFIG_GPIO_F7188X is not set +# CONFIG_GPIO_INTEL_MID is not set +# CONFIG_GPIO_IT87 is not set +# CONFIG_GPIO_LYNXPOINT is not set +CONFIG_GPIO_SYSFS=y CONFIG_HAVE_ACPI_APEI=y CONFIG_HAVE_ACPI_APEI_NMI=y # CONFIG_HAVE_AOUT is not set @@ -119,6 +128,8 @@ CONFIG_HVC_DRIVER=y CONFIG_HW_RANDOM_AMD=y CONFIG_HW_RANDOM_INTEL=y CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_HWMON=y +#CONFIG_HWMON_DEBUG_CHIP is not set CONFIG_HYPERVISOR_GUEST=y # CONFIG_I7300_IDLE is not set # CONFIG_IA32_EMULATION is not set @@ -135,6 +146,7 @@ CONFIG_ITCO_WDT=y # CONFIG_KVM_DEBUG_FS is not set CONFIG_KVM_GUEST=y # CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_LEDS_APU2=y # CONFIG_LEGACY_VSYSCALL_EMULATE is not set # CONFIG_LEGACY_VSYSCALL_NATIVE is not set CONFIG_LEGACY_VSYSCALL_NONE=y diff --git a/target/linux/x86/64/profiles/001-PCEngines.mk b/target/linux/x86/64/profiles/001-PCEngines.mk new file mode 100644 index 0000000..9ac651b --- /dev/null +++ b/target/linux/x86/64/profiles/001-PCEngines.mk @@ -0,0 +1,20 @@ +# +# Copyright (C) 2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/APU2 + NAME:=PC Engines APU2 + PACKAGES:=flashrom hwclock libsensors lm-sensors wpad-mini \ + kmod-ath9k kmod-ath10k kmod-gpio-button-hotplug kmod-gpio-nct5104d \ + kmod-hwmon-k10temp kmod-leds-gpio kmod-sp5100_tco \ + kmod-usb-core kmod-usb-ohci kmod-usb2 kmod-usb3 + +endef + +define Profile/APU2/Description + PC Engines APU2 Embedded Board +endef +$(eval $(call Profile,APU2)) diff --git a/target/linux/x86/base-files/etc/board.d/01_leds b/target/linux/x86/base-files/etc/board.d/01_leds new file mode 100755 index 0000000..1cd83e3 --- /dev/null +++ b/target/linux/x86/base-files/etc/board.d/01_leds @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (C) 2016 OpenWrt.org +# + +. /lib/functions/uci-defaults.sh +. /lib/x86.sh + +board_config_update + +board=$(x86_board_name) + +case "$board" in +pc-engines-apu2) + ucidef_set_led_wlan "wlan0" "WLAN0" "apu2:green:led2" "phy0tpt" + ucidef_set_led_wlan "wlan1" "WLAN1" "apu2:green:led3" "phy1tpt" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/x86/base-files/etc/board.d/02_network b/target/linux/x86/base-files/etc/board.d/02_network new file mode 100755 index 0000000..f0e23f3 --- /dev/null +++ b/target/linux/x86/base-files/etc/board.d/02_network @@ -0,0 +1,26 @@ +#!/bin/sh +# +# Copyright (C) 2016 OpenWrt.org +# + +. /lib/functions/system.sh +. /lib/functions/uci-defaults.sh +. /lib/x86.sh + +board_config_update + +board=$(x86_board_name) + +case "$board" in +pc-engines-apu2) + ucidef_set_interfaces_lan_wan "eth1" "eth0" + ucidef_set_interface_raw "opt" "eth2" + ;; +*) + ucidef_set_interfaces_lan_wan "eth1" "eth0" + ;; +esac + +board_config_flush + +exit 0 diff --git a/target/linux/x86/base-files/etc/diag.sh b/target/linux/x86/base-files/etc/diag.sh new file mode 100755 index 0000000..27e150d --- /dev/null +++ b/target/linux/x86/base-files/etc/diag.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# Copyright (C) 2016 OpenWrt.org +# + +. /lib/functions/leds.sh +. /lib/x86.sh + +get_status_led() { + case $(x86_board_name) in + pc-engines-apu2) + status_led="apu2:green:power" + ;; + esac +} + +set_state() { + get_status_led + + case "$1" in + preinit) + status_led_blink_preinit + ;; + + failsafe) + status_led_blink_failsafe + ;; + + preinit_regular) + status_led_blink_preinit_regular + ;; + + done) + status_led_on + ;; + esac +} diff --git a/target/linux/x86/base-files/lib/x86.sh b/target/linux/x86/base-files/lib/x86.sh new file mode 100755 index 0000000..53464b6 --- /dev/null +++ b/target/linux/x86/base-files/lib/x86.sh @@ -0,0 +1,13 @@ +#!/bin/sh +# +# Copyright (C) 2016 OpenWrt.org +# + +x86_board_name() { + local name + + [ -f /tmp/sysinfo/board_name ] && name=$(cat /tmp/sysinfo/board_name) + [ -z "$name" ] && name="unknown" + + echo "$name" +} diff --git a/target/linux/x86/config-4.4 b/target/linux/x86/config-4.4 index 894fed0..c6c9bf6 100644 --- a/target/linux/x86/config-4.4 +++ b/target/linux/x86/config-4.4 @@ -229,6 +229,7 @@ CONFIG_ILLEGAL_POINTER_VALUE=0 CONFIG_INITRAMFS_SOURCE="" CONFIG_INPUT=y CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_PCSPKR=y CONFIG_INSTRUCTION_DECODER=y # CONFIG_INTEL_PCH_THERMAL is not set # CONFIG_INTEL_POWERCLAMP is not set diff --git a/target/linux/x86/files/drivers/gpio/gpio-nct5104d.c b/target/linux/x86/files/drivers/gpio/gpio-nct5104d.c new file mode 100644 index 0000000..684037c --- /dev/null +++ b/target/linux/x86/files/drivers/gpio/gpio-nct5104d.c @@ -0,0 +1,432 @@ +/* + * GPIO driver for NCT5104D + * + * Author: Tasanakorn Phaipool <tasanak...@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/gpio.h> + +#define DRVNAME "gpio-nct5104d" + +/* + * Super-I/O registers + */ +#define SIO_LDSEL 0x07 /* Logical device select */ +#define SIO_CHIPID 0x20 /* Chaip ID (2 bytes) */ +#define SIO_GPIO_ENABLE 0x30 /* GPIO enable */ +#define SIO_GPIO1_MODE 0xE0 /* GPIO1 Mode OpenDrain/Push-Pull */ +#define SIO_GPIO2_MODE 0xE1 /* GPIO2 Mode OpenDrain/Push-Pull */ + +#define SIO_LD_GPIO 0x07 /* GPIO logical device */ +#define SIO_LD_GPIO_MODE 0x0F /* GPIO mode control device */ +#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_NCT5104D_ID 0x1061 /* Chip ID */ +#define SIO_PCENGINES_APU_NCT5104D_ID 0xc452 /* Chip ID */ + +enum chips { nct5104d }; + +static const char * const nct5104d_names[] = { + "nct5104d" +}; + +struct nct5104d_sio { + int addr; + enum chips type; +}; + +struct nct5104d_gpio_bank { + struct gpio_chip chip; + unsigned int regbase; + struct nct5104d_gpio_data *data; +}; + +struct nct5104d_gpio_data { + struct nct5104d_sio *sio; + int nr_bank; + struct nct5104d_gpio_bank *bank; +}; + +/* + * Super-I/O functions. + */ + +static inline int superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static int superio_inw(int base, int reg) +{ + int val; + + outb(reg++, base); + val = inb(base + 1) << 8; + outb(reg, base); + val |= inb(base + 1); + + return val; +} + +static inline void superio_outb(int base, int reg, int val) +{ + outb(reg, base); + outb(val, base + 1); +} + +static inline int superio_enter(int base) +{ + /* Don't step on other drivers' I/O space by accident. */ + if (!request_muxed_region(base, 2, DRVNAME)) { + pr_err(DRVNAME "I/O address 0x%04x already in use\n", base); + return -EBUSY; + } + + /* According to the datasheet the key must be send twice. */ + outb(SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); + + return 0; +} + +static inline void superio_select(int base, int ld) +{ + outb(SIO_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); + release_region(base, 2); +} + +/* + * GPIO chip. + */ + +static int nct5104d_gpio_direction_in(struct gpio_chip *chip, unsigned offset); +static int nct5104d_gpio_get(struct gpio_chip *chip, unsigned offset); +static int nct5104d_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value); +static void nct5104d_gpio_set(struct gpio_chip *chip, unsigned offset, int value); + +#define NCT5104D_GPIO_BANK(_base, _ngpio, _regbase) \ + { \ + .chip = { \ + .label = DRVNAME, \ + .owner = THIS_MODULE, \ + .direction_input = nct5104d_gpio_direction_in, \ + .get = nct5104d_gpio_get, \ + .direction_output = nct5104d_gpio_direction_out, \ + .set = nct5104d_gpio_set, \ + .base = _base, \ + .ngpio = _ngpio, \ + .can_sleep = true, \ + }, \ + .regbase = _regbase, \ + } + +#define gpio_dir(base) (base + 0) +#define gpio_data(base) (base + 1) + +static struct nct5104d_gpio_bank nct5104d_gpio_bank[] = { + NCT5104D_GPIO_BANK(0 , 8, 0xE0), + NCT5104D_GPIO_BANK(10, 8, 0xE4) +}; + +static int nct5104d_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + int err; + struct nct5104d_gpio_bank *bank = + container_of(chip, struct nct5104d_gpio_bank, chip); + struct nct5104d_sio *sio = bank->data->sio; + u8 dir; + + err = superio_enter(sio->addr); + if (err) + return err; + superio_select(sio->addr, SIO_LD_GPIO); + + dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + dir |= (1 << offset); + superio_outb(sio->addr, gpio_dir(bank->regbase), dir); + + superio_exit(sio->addr); + + return 0; +} + +static int nct5104d_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + int err; + struct nct5104d_gpio_bank *bank = + container_of(chip, struct nct5104d_gpio_bank, chip); + struct nct5104d_sio *sio = bank->data->sio; + u8 data; + + err = superio_enter(sio->addr); + if (err) + return err; + superio_select(sio->addr, SIO_LD_GPIO); + + data = superio_inb(sio->addr, gpio_data(bank->regbase)); + + superio_exit(sio->addr); + + return !!(data & 1 << offset); +} + +static int nct5104d_gpio_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + int err; + struct nct5104d_gpio_bank *bank = + container_of(chip, struct nct5104d_gpio_bank, chip); + struct nct5104d_sio *sio = bank->data->sio; + u8 dir, data_out; + + err = superio_enter(sio->addr); + if (err) + return err; + superio_select(sio->addr, SIO_LD_GPIO); + + data_out = superio_inb(sio->addr, gpio_data(bank->regbase)); + if (value) + data_out |= (1 << offset); + else + data_out &= ~(1 << offset); + superio_outb(sio->addr, gpio_data(bank->regbase), data_out); + + dir = superio_inb(sio->addr, gpio_dir(bank->regbase)); + dir &= ~(1 << offset); + superio_outb(sio->addr, gpio_dir(bank->regbase), dir); + + superio_exit(sio->addr); + + return 0; +} + +static void nct5104d_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + int err; + struct nct5104d_gpio_bank *bank = + container_of(chip, struct nct5104d_gpio_bank, chip); + struct nct5104d_sio *sio = bank->data->sio; + u8 data_out; + + err = superio_enter(sio->addr); + if (err) + return; + superio_select(sio->addr, SIO_LD_GPIO); + + data_out = superio_inb(sio->addr, gpio_data(bank->regbase)); + if (value) + data_out |= (1 << offset); + else + data_out &= ~(1 << offset); + superio_outb(sio->addr, gpio_data(bank->regbase), data_out); + + superio_exit(sio->addr); +} + +/* + * Platform device and driver. + */ + +static int nct5104d_gpio_probe(struct platform_device *pdev) +{ + int err; + int i; + struct nct5104d_sio *sio = pdev->dev.platform_data; + struct nct5104d_gpio_data *data; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + switch (sio->type) { + case nct5104d: + data->nr_bank = ARRAY_SIZE(nct5104d_gpio_bank); + data->bank = nct5104d_gpio_bank; + break; + default: + return -ENODEV; + } + data->sio = sio; + + platform_set_drvdata(pdev, data); + + /* For each GPIO bank, register a GPIO chip. */ + for (i = 0; i < data->nr_bank; i++) { + struct nct5104d_gpio_bank *bank = &data->bank[i]; + + bank->chip.dev = &pdev->dev; + bank->data = data; + + err = gpiochip_add(&bank->chip); + if (err) { + dev_err(&pdev->dev, + "Failed to register gpiochip %d: %d\n", + i, err); + goto err_gpiochip; + } + } + + return 0; + +err_gpiochip: + for (i = i - 1; i >= 0; i--) { + struct nct5104d_gpio_bank *bank = &data->bank[i]; + + gpiochip_remove(&bank->chip); + } + + return err; +} + +static int nct5104d_gpio_remove(struct platform_device *pdev) +{ + int i; + struct nct5104d_gpio_data *data = platform_get_drvdata(pdev); + + for (i = 0; i < data->nr_bank; i++) { + struct nct5104d_gpio_bank *bank = &data->bank[i]; + gpiochip_remove(&bank->chip); + } + + return 0; +} + +static int __init nct5104d_find(int addr, struct nct5104d_sio *sio) +{ + int err; + u16 devid; + u8 gpio_cfg; + + err = superio_enter(addr); + if (err) + return err; + + err = -ENODEV; + + devid = superio_inw(addr, SIO_CHIPID); + switch (devid) { + case SIO_NCT5104D_ID: + case SIO_PCENGINES_APU_NCT5104D_ID: + sio->type = nct5104d; + /* enable GPIO0 and GPIO1 */ + superio_select(addr, SIO_LD_GPIO); + gpio_cfg = superio_inb(addr, SIO_GPIO_ENABLE); + gpio_cfg |= 0x03; + superio_outb(addr, SIO_GPIO_ENABLE, gpio_cfg); + break; + default: + pr_info(DRVNAME ": Unsupported device 0x%04x\n", devid); + goto err; + } + sio->addr = addr; + err = 0; + + pr_info(DRVNAME ": Found %s at %#x chip id 0x%04x\n", + nct5104d_names[sio->type], + (unsigned int) addr, + (int) superio_inw(addr, SIO_CHIPID)); + + superio_select(sio->addr, SIO_LD_GPIO_MODE); + superio_outb(sio->addr, SIO_GPIO1_MODE, 0x0); + superio_outb(sio->addr, SIO_GPIO2_MODE, 0x0); + +err: + superio_exit(addr); + return err; +} + +static struct platform_device *nct5104d_gpio_pdev; + +static int __init +nct5104d_gpio_device_add(const struct nct5104d_sio *sio) +{ + int err; + + nct5104d_gpio_pdev = platform_device_alloc(DRVNAME, -1); + if (!nct5104d_gpio_pdev) + pr_err(DRVNAME ": Error platform_device_alloc\n"); + if (!nct5104d_gpio_pdev) + return -ENOMEM; + + err = platform_device_add_data(nct5104d_gpio_pdev, + sio, sizeof(*sio)); + if (err) { + pr_err(DRVNAME "Platform data allocation failed\n"); + goto err; + } + + err = platform_device_add(nct5104d_gpio_pdev); + if (err) { + pr_err(DRVNAME "Device addition failed\n"); + goto err; + } + pr_info(DRVNAME ": Device added\n"); + return 0; + +err: + platform_device_put(nct5104d_gpio_pdev); + + return err; +} + +/* + */ + +static struct platform_driver nct5104d_gpio_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = nct5104d_gpio_probe, + .remove = nct5104d_gpio_remove, +}; + +static int __init nct5104d_gpio_init(void) +{ + int err; + struct nct5104d_sio sio; + + if (nct5104d_find(0x2e, &sio) && + nct5104d_find(0x4e, &sio)) + return -ENODEV; + + err = platform_driver_register(&nct5104d_gpio_driver); + if (!err) { + pr_info(DRVNAME ": platform_driver_register\n"); + err = nct5104d_gpio_device_add(&sio); + if (err) + platform_driver_unregister(&nct5104d_gpio_driver); + } + + return err; +} +subsys_initcall(nct5104d_gpio_init); + +static void __exit nct5104d_gpio_exit(void) +{ + platform_device_unregister(nct5104d_gpio_pdev); + platform_driver_unregister(&nct5104d_gpio_driver); +} +module_exit(nct5104d_gpio_exit); + +MODULE_DESCRIPTION("GPIO driver for Super-I/O chips NCT5104D"); +MODULE_AUTHOR("Tasanakorn Phaipool <tasanak...@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/x86/files/drivers/leds/leds-apu2.c b/target/linux/x86/files/drivers/leds/leds-apu2.c new file mode 100644 index 0000000..4c21779 --- /dev/null +++ b/target/linux/x86/files/drivers/leds/leds-apu2.c @@ -0,0 +1,371 @@ +/* + * APU2 LED/BTTN/GPIO Driver + * Copyright (c) 2016 Christian Lamparter <chunkeey (at) googlemail.com> + * + * Based on gpio-apu2.c - AMD FCH GPIO support for PC-Engines APU-2 board + * + * Copyright (c) 2015 Carsten Spiess <fli4l at carsten-spiess.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/miscdevice.h> + #include <linux/gpio.h> + #include <linux/init.h> + #include <linux/pci.h> + #include <linux/ioport.h> + #include <linux/platform_device.h> + #include <linux/uaccess.h> + #include <linux/io.h> + #include <linux/version.h> + #include <linux/dmi.h> + + #include <linux/leds.h> + #include <linux/input.h> + #include <linux/gpio_keys.h> + + #define DEVNAME "gpio-apu2" + + #define FCH_ACPI_MMIO_BASE 0xFED80000 + #define FCH_GPIO_BASE (FCH_ACPI_MMIO_BASE + 0x1500) + #define FCH_GPIO_SIZE 0x300 + + #define APU_NUM_GPIO 4 + + #define GPIO_BIT_DIR 23 + #define GPIO_BIT_WRITE 22 + #define GPIO_BIT_READ 16 + + /* internal variables */ + static struct pci_dev *gpio_apu2_pci; + static DEFINE_SPINLOCK ( gpio_lock); + + /* the watchdog platform device */ + static struct platform_device *gpio_apu2_platform_device; + static struct platform_device *leddev; + static struct platform_device *keydev; + + static const struct pci_device_id gpio_apu2_pci_tbl[] ={ + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, PCI_ANY_ID}, + { 0, } /* End of list */ + }; + MODULE_DEVICE_TABLE ( pci, gpio_apu2_pci_tbl); + + /* EGPIO89=GPIO32, AGPIO68=GPIO57, AGPIO69=GPIO58, AGPIO70=GPIO59 */ + static u8 gpio_offset[APU_NUM_GPIO] = {89, 68, 69, 70}; + + static void __iomem *gpio_addr[APU_NUM_GPIO] = {NULL, NULL, NULL, NULL}; + + static int gpio_apu2_get_dir (struct gpio_chip *chip, unsigned offset) + { + u32 val; + + val = ~ioread32 (gpio_addr[offset]); + + return (val >> GPIO_BIT_DIR) & 1; + } + + static int gpio_apu2_dir_in (struct gpio_chip *gc, unsigned offset) + { + u32 val; + + spin_lock_bh (&gpio_lock); + + val = ioread32 (gpio_addr[offset]); + val &= ~BIT(GPIO_BIT_DIR); + iowrite32 (val, gpio_addr[offset]); + + spin_unlock_bh (&gpio_lock); + + return 0; + } + + static int gpio_apu2_dir_out (struct gpio_chip *chip, unsigned offset, + int value) + { + u32 val; + + spin_lock_bh (&gpio_lock); + + val = ioread32 (gpio_addr[offset]); + val |= BIT(GPIO_BIT_DIR); + iowrite32 (val, gpio_addr[offset]); + + spin_unlock_bh (&gpio_lock); + + return 0; + } + + static int gpio_apu2_get_data (struct gpio_chip *chip, unsigned offset) + { + u32 val; + + val = ioread32 (gpio_addr[offset]); + + return (val >> GPIO_BIT_READ) & 1; + } + + static void gpio_apu2_set_data (struct gpio_chip *chip, unsigned offset, int value) + { + u32 val; + + spin_lock_bh (&gpio_lock); + + val = ioread32 (gpio_addr[offset]); + + if (value) + val |= BIT(GPIO_BIT_WRITE); + else + val &= ~BIT(GPIO_BIT_WRITE); + + iowrite32 (val, gpio_addr[offset]); + + spin_unlock_bh (&gpio_lock); + } + + static struct gpio_chip gpio_apu2_chip = { + .label = DEVNAME, + .owner = THIS_MODULE, + .base = -1, + .ngpio = APU_NUM_GPIO, + .get_direction = gpio_apu2_get_dir, + .direction_input = gpio_apu2_dir_in, + .direction_output = gpio_apu2_dir_out, + .get = gpio_apu2_get_data, + .set = gpio_apu2_set_data, + }; + + /* + * + */ + static int gpio_apu2_probe (struct platform_device *dev) + { + int ret = 0; + int i; + struct pci_dev *pci_dev = NULL; + + /* Match the device name/model */ + if (!dmi_match(DMI_BOARD_VENDOR, "PC Engines") || !dmi_match(DMI_BOARD_NAME, "APU2")) { + pr_err ("%s: APU2 board not detected! Found: %s %s\n", DEVNAME, DMI_BOARD_VENDOR, DMI_BOARD_NAME); + return -EACCES; + } + + /* Match the PCI device */ + for_each_pci_dev (pci_dev) { + if (pci_match_id (gpio_apu2_pci_tbl, pci_dev) != NULL) { + gpio_apu2_pci = pci_dev; + break; + } + } + + if (!gpio_apu2_pci) + return -ENODEV; + + pr_info ("%s: PCI Revision ID: 0x%x\n", DEVNAME, gpio_apu2_pci->revision); + + /* Determine type of southbridge chipset */ + if (gpio_apu2_pci->revision < 0x40) { + return -EACCES; + } + + /* Request memory region for GPIO's */ + if (!devm_request_mem_region (&dev->dev, FCH_GPIO_BASE, + FCH_GPIO_SIZE, DEVNAME)){ + pr_err ("%s: request GPIO mem region failed\n", DEVNAME); + return -ENXIO; + } + + /* Map IO's for GPIO's */ + for (i = 0; i < APU_NUM_GPIO; i++) { + gpio_addr[i] = devm_ioremap (&dev->dev, + FCH_GPIO_BASE + (gpio_offset[i] * sizeof (u32)), sizeof (u32)); + if (!gpio_addr[i]) { + pr_err ("%s: map GPIO%d address failed\n", DEVNAME, gpio_offset[i]); + return -ENXIO; + } + } + + gpio_apu2_chip.dev = &dev->dev; + ret = gpiochip_add (&gpio_apu2_chip); + if (ret) { + pr_err ("%s: adding gpiochip failed\n", DEVNAME); + } + + return ret; + } + + static int gpio_apu2_remove (struct platform_device *dev) + { + #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) + int ret; + ret = gpiochip_remove (&gpio_apu2_chip); + #else /* LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) */ + gpiochip_remove (&gpio_apu2_chip); + #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3,18,0) */ + return 0; + } + + static struct platform_driver gpio_apu2_driver = { + .probe = gpio_apu2_probe, + .remove = gpio_apu2_remove, + .driver = { + .owner = THIS_MODULE, + .name = DEVNAME + } + }; + + static struct gpio_led apu2_leds_gpio[] = { + { + .name = "apu2:green:power", + .gpio = 509, + .active_low = 1, + }, + { + .name = "apu2:green:led2", + .gpio = 510, + .active_low = 1, + }, + { + .name = "apu2:green:led3", + .gpio = 511, + .active_low = 1, + }, + }; + + static struct gpio_keys_button apu2_gpio_keys[] = { + { + .desc = "Reset button", + .type = EV_KEY, + .code = KEY_RESTART, + .debounce_interval = 60, + .gpio = 508, + .active_low = 1, + }, + }; + + static void register_gpio_keys_polled(int id, unsigned poll_interval, + unsigned nbuttons, + struct gpio_keys_button *buttons) + { + struct gpio_keys_platform_data pdata = { }; + int err; + + keydev = platform_device_alloc("gpio-keys-polled", id); + if (!keydev) { + printk(KERN_ERR "Failed to allocate gpio-keys platform device\n"); + return; + } + + pdata.poll_interval = poll_interval; + pdata.nbuttons = nbuttons; + pdata.buttons = buttons; + + err = platform_device_add_data(keydev, &pdata, sizeof(pdata)); + if (err) { + dev_err(&keydev->dev, "failed to add platform data to key driver (%d)", err); + goto err_put_pdev; + } + + err = platform_device_add(keydev); + if (err) { + dev_err(&keydev->dev, "failed to register key platform device (%d)", err); + goto err_put_pdev; + } + + return; + + err_put_pdev: + platform_device_put(keydev); + keydev = NULL; + } + + static void register_leds_gpio(int id, unsigned num_leds, struct gpio_led *leds) + { + struct gpio_led_platform_data pdata = { }; + int err; + + leddev = platform_device_alloc("leds-gpio", id); + if (!leddev) { + printk(KERN_ERR "Failed to allocate leds-gpio platform device\n"); + return; + } + + pdata.num_leds = num_leds; + pdata.leds = leds; + + err = platform_device_add_data(leddev, &pdata, sizeof(pdata)); + if (err) { + dev_err(&leddev->dev, "failed to add platform data to key driver (%d)", err); + goto err_put_pdev; + } + + err = platform_device_add(leddev); + if (err) { + dev_err(&leddev->dev, "failed to register key platform device (%d)", err); + goto err_put_pdev; + } + + return; + + err_put_pdev: + platform_device_put(leddev); + leddev = NULL; + } + + static int __init gpio_apu2_init (void) + { + int err; + + pr_info ("%s: load APU2 LED/GPIO driver module\n", DEVNAME); + + err = platform_driver_register (&gpio_apu2_driver); + if (err) + goto exit; + + gpio_apu2_platform_device = platform_device_register_simple (DEVNAME, -1, NULL, 0); + if (IS_ERR(gpio_apu2_platform_device)) { + err = PTR_ERR(gpio_apu2_platform_device); + goto exit_driver; + } + + pr_info ("%s: APU2 GPIO/LED driver module loaded\n", DEVNAME); + + register_leds_gpio(-1, ARRAY_SIZE(apu2_leds_gpio), apu2_leds_gpio); + register_gpio_keys_polled(-1, 20, ARRAY_SIZE(apu2_gpio_keys), apu2_gpio_keys); + return 0; + + exit_driver: + platform_driver_unregister (&gpio_apu2_driver); + exit: + return err; + } + + static void __exit gpio_apu2_exit (void) + { + platform_device_unregister (gpio_apu2_platform_device); + platform_device_unregister (leddev); + platform_device_unregister (keydev); + platform_driver_unregister (&gpio_apu2_driver); + pr_info ("%s: APU2 GPIO/LED driver module unloaded\n", DEVNAME); + } + + MODULE_AUTHOR ("Carsten Spiess <fli4l at carsten-spiess.de>"); + MODULE_DESCRIPTION("GPIO driver for AMD FCH on PC-Engines APU-2"); + MODULE_LICENSE("GPL"); + + module_init (gpio_apu2_init); + module_exit (gpio_apu2_exit); diff --git a/target/linux/x86/modules.mk b/target/linux/x86/modules.mk new file mode 100644 index 0000000..d53216d --- /dev/null +++ b/target/linux/x86/modules.mk @@ -0,0 +1,36 @@ +# +# Copyright (C) 2016 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define KernelPackage/sp5100_tco + SUBMENU:=$(OTHER_MENU) + TITLE:=SP5100 Watchdog Support + DEPENDS:=@TARGET_x86 + KCONFIG:=CONFIG_SP5100_TCO + FILES:=$(LINUX_DIR)/drivers/watchdog/sp5100_tco.ko + AUTOLOAD:=$(call AutoLoad,50,sp5100_tco,1) +endef + +define KernelPackage/sp5100_tco/description + Kernel module for the SP5100_TCO hardware watchdog. +endef + +$(eval $(call KernelPackage,sp5100_tco)) + +define KernelPackage/gpio-nct5104d + SUBMENU:=$(OTHER_MENU) + TITLE:=nct5104d GPIO Support + DEPENDS:=@TARGET_x86 @GPIO_SUPPORT + KCONFIG:=CONFIG_GPIO_NCT5104D + FILES:=$(LINUX_DIR)/drivers/gpio/gpio-nct5104d.ko + AUTOLOAD:=$(call AutoLoad,60,gpio-nct5104d) +endef + +define KernelPackage/gpio-nct5104d/description + Kernel module for the nct5104d Super I/O chip. +endef + +$(eval $(call KernelPackage,gpio-nct5104d)) diff --git a/target/linux/x86/patches-4.4/800-add-apu2-led-driver.patch b/target/linux/x86/patches-4.4/800-add-apu2-led-driver.patch new file mode 100644 index 0000000..5720fc3 --- /dev/null +++ b/target/linux/x86/patches-4.4/800-add-apu2-led-driver.patch @@ -0,0 +1,29 @@ +--- a/drivers/leds/Kconfig 2016-09-15 01:29:29.000000000 -0500 ++++ b/drivers/leds/Kconfig 2016-09-26 02:36:55.349774421 -0500 +@@ -49,6 +49,15 @@ + help + This option enables support for the LEDs on the AAT1290. + ++config LEDS_APU2 ++ tristate "LED Support for the PCEngines APU2 board" ++ depends on LEDS_CLASS ++ depends on GPIOLIB ++ help ++ This option enables support for the CPU GPIO pins on the PCEngines ++ APU2 board, as well as adds system support for the reset button and ++ front panel LEDs. ++ + config LEDS_BCM6328 + tristate "LED Support for Broadcom BCM6328" + depends on LEDS_CLASS +diff -Naur ./a/Makefile ./b/Makefile +--- a/drivers/leds/Makefile 2016-09-26 02:23:39.787307000 -0500 ++++ b/drivers/leds/Makefile 2016-09-26 02:39:41.669443541 -0500 +@@ -8,6 +8,7 @@ + # LED Platform Drivers + obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o + obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o ++obj-$(CONFIG_LEDS_APU2) += leds-apu2.o + obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o + obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o + obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o diff --git a/target/linux/x86/patches-4.4/801-sp5100_tco-add-apu2-support.patch b/target/linux/x86/patches-4.4/801-sp5100_tco-add-apu2-support.patch new file mode 100644 index 0000000..73c193f --- /dev/null +++ b/target/linux/x86/patches-4.4/801-sp5100_tco-add-apu2-support.patch @@ -0,0 +1,91 @@ +--- a/drivers/watchdog/sp5100_tco.c 2016-09-15 08:29:29.000000000 +0200 ++++ b/drivers/watchdog/sp5100_tco.c 2016-09-15 08:21:53.000000000 +0200 +@@ -73,6 +73,13 @@ + /* + * Some TCO specific functions + */ ++ ++static bool tco_has_sp5100_reg_layout(struct pci_dev *dev) ++{ ++ return dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && ++ dev->revision < 0x40; ++} ++ + static void tco_timer_start(void) + { + u32 val; +@@ -129,7 +136,7 @@ + { + int val; + +- if (sp5100_tco_pci->revision >= 0x40) { ++ if (!tco_has_sp5100_reg_layout(sp5100_tco_pci)) { + /* For SB800 or later */ + /* Set the Watchdog timer resolution to 1 sec */ + outb(SB800_PM_WATCHDOG_CONFIG, SB800_IO_PM_INDEX_REG); +@@ -306,6 +313,10 @@ + static const struct pci_device_id sp5100_tco_pci_tbl[] = { + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, + PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, PCI_ANY_ID, ++ PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, ++ PCI_ANY_ID, }, + { 0, }, /* End of list */ + }; + MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); +@@ -331,21 +342,23 @@ + if (!sp5100_tco_pci) + return 0; + +- pr_info("PCI Revision ID: 0x%x\n", sp5100_tco_pci->revision); ++ pr_info("PCI Vendor ID: 0x%x, Device ID: 0x%x, Revision ID: 0x%x\n", ++ sp5100_tco_pci->vendor, sp5100_tco_pci->device, ++ sp5100_tco_pci->revision); + + /* + * Determine type of southbridge chipset. + */ +- if (sp5100_tco_pci->revision >= 0x40) { +- dev_name = SB800_DEVNAME; +- index_reg = SB800_IO_PM_INDEX_REG; +- data_reg = SB800_IO_PM_DATA_REG; +- base_addr = SB800_PM_WATCHDOG_BASE; +- } else { ++ if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { + dev_name = SP5100_DEVNAME; + index_reg = SP5100_IO_PM_INDEX_REG; + data_reg = SP5100_IO_PM_DATA_REG; + base_addr = SP5100_PM_WATCHDOG_BASE; ++ } else { ++ dev_name = SB800_DEVNAME; ++ index_reg = SB800_IO_PM_INDEX_REG; ++ data_reg = SB800_IO_PM_DATA_REG; ++ base_addr = SB800_PM_WATCHDOG_BASE; + } + + /* Request the IO ports used by this driver */ +@@ -381,7 +394,11 @@ + * Secondly, Find the watchdog timer MMIO address + * from SBResource_MMIO register. + */ +- if (sp5100_tco_pci->revision >= 0x40) { ++ if (tco_has_sp5100_reg_layout(sp5100_tco_pci)) { ++ /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ ++ pci_read_config_dword(sp5100_tco_pci, ++ SP5100_SB_RESOURCE_MMIO_BASE, &val); ++ } else { + /* Read SBResource_MMIO from AcpiMmioEn(PM_Reg: 24h) */ + outb(SB800_PM_ACPI_MMIO_EN+3, SB800_IO_PM_INDEX_REG); + val = inb(SB800_IO_PM_DATA_REG); +@@ -391,10 +408,6 @@ + val = val << 8 | inb(SB800_IO_PM_DATA_REG); + outb(SB800_PM_ACPI_MMIO_EN+0, SB800_IO_PM_INDEX_REG); + val = val << 8 | inb(SB800_IO_PM_DATA_REG); +- } else { +- /* Read SBResource_MMIO from PCI config(PCI_Reg: 9Ch) */ +- pci_read_config_dword(sp5100_tco_pci, +- SP5100_SB_RESOURCE_MMIO_BASE, &val); + } + + /* The SBResource_MMIO is enabled and mapped memory space? */ diff --git a/target/linux/x86/patches-4.4/802-add-nct5104d-gpio-driver.patch b/target/linux/x86/patches-4.4/802-add-nct5104d-gpio-driver.patch new file mode 100644 index 0000000..c48ccf0 --- /dev/null +++ b/target/linux/x86/patches-4.4/802-add-nct5104d-gpio-driver.patch @@ -0,0 +1,27 @@ +--- a/drivers/gpio/Kconfig 2016-09-15 01:29:29.000000000 -0500 ++++ b/drivers/gpio/Kconfig 2016-09-26 02:29:15.714674624 -0500 +@@ -307,6 +307,13 @@ + select GPIO_GENERIC + select GENERIC_IRQ_CHIP + ++config GPIO_NCT5104D ++ tristate "NCT5104D GPIO support" ++ depends on X86 # unconditional access to IO space. ++ select GPIO_GENERIC ++ help ++ Say yes here to support GPIO functionality of NCT5104D super I/O chip. ++ + config GPIO_OCTEON + tristate "Cavium OCTEON GPIO" + depends on GPIOLIB && CAVIUM_OCTEON_SOC +diff -Naur ./a/Makefile ./b/Makefile +--- a/drivers/gpio/Makefile 2016-09-15 01:29:29.000000000 -0500 ++++ b/drivers/gpio/Makefile 2016-09-26 02:25:57.807051231 -0500 +@@ -69,6 +69,7 @@ + obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o + obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o + obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o ++obj-$(CONFIG_GPIO_NCT5104D) += gpio-nct5104d.o + obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o + obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o + obj-$(CONFIG_GPIO_PCA953X) += gpio-pca953x.o -- 2.7.4 _______________________________________________ Lede-dev mailing list Lede-dev@lists.infradead.org http://lists.infradead.org/mailman/listinfo/lede-dev