diff options
Diffstat (limited to 'target/linux/mvebu/patches-5.4/025-power-reset-add-driver-for-LinkStation-power-off.patch')
-rw-r--r-- | target/linux/mvebu/patches-5.4/025-power-reset-add-driver-for-LinkStation-power-off.patch | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-5.4/025-power-reset-add-driver-for-LinkStation-power-off.patch b/target/linux/mvebu/patches-5.4/025-power-reset-add-driver-for-LinkStation-power-off.patch new file mode 100644 index 0000000000..64ea26559e --- /dev/null +++ b/target/linux/mvebu/patches-5.4/025-power-reset-add-driver-for-LinkStation-power-off.patch @@ -0,0 +1,207 @@ +From a7f79f99541eff4e6bcae0014eb08d3019337565 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Daniel=20Gonz=C3=A1lez=20Cabanelas?= <dgcbueu@gmail.com> +Date: Wed, 15 Jul 2020 15:35:14 +0200 +Subject: [PATCH] power: reset: add driver for LinkStation power off +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some Buffalo LinkStations perform the power off operation, at restart +time, depending on the state of an output pin (LED2/INTn) at the ethernet +PHY. This pin is also used to wake the machine when a WoL packet is +received by the PHY. + +The driver is required by the Buffalo LinkStation LS421DE (ARM MVEBU), +and other models. Without it, the board remains forever halted if a +power off command is executed, unless the PSU is disconnected and +connected again. + +Add the driver to provide the power off function and also make the WoL +feature to be available. + +Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com> +Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> +--- + drivers/power/reset/Kconfig | 11 ++ + drivers/power/reset/Makefile | 1 + + drivers/power/reset/linkstation-poweroff.c | 136 +++++++++++++++++++++ + 3 files changed, 148 insertions(+) + create mode 100644 drivers/power/reset/linkstation-poweroff.c + +--- a/drivers/power/reset/Kconfig ++++ b/drivers/power/reset/Kconfig +@@ -99,6 +99,17 @@ config POWER_RESET_HISI + help + Reboot support for Hisilicon boards. + ++config POWER_RESET_LINKSTATION ++ tristate "Buffalo LinkStation power-off driver" ++ depends on ARCH_MVEBU || COMPILE_TEST ++ depends on OF_MDIO && PHYLIB ++ help ++ This driver supports turning off some Buffalo LinkStations by ++ setting an output pin at the ethernet PHY to the correct state. ++ It also makes the device compatible with the WoL function. ++ ++ Say Y here if you have a Buffalo LinkStation LS421D/E. ++ + config POWER_RESET_MSM + bool "Qualcomm MSM power-off driver" + depends on ARCH_QCOM +--- a/drivers/power/reset/Makefile ++++ b/drivers/power/reset/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF + obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o + obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o + obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o ++obj-${CONFIG_POWER_RESET_LINKSTATION} += linkstation-poweroff.o + obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o + obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o + obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o +--- /dev/null ++++ b/drivers/power/reset/linkstation-poweroff.c +@@ -0,0 +1,144 @@ ++// SPDX-License-Identifier: GPL-2.0 ++/* ++ * LinkStation power off restart driver ++ * Copyright (C) 2020 Daniel González Cabanelas <dgcbueu@gmail.com> ++ */ ++ ++#include <linux/module.h> ++#include <linux/notifier.h> ++#include <linux/of.h> ++#include <linux/of_mdio.h> ++#include <linux/of_platform.h> ++#include <linux/reboot.h> ++#include <linux/phy.h> ++ ++/* Defines from the eth phy Marvell driver */ ++#define MII_MARVELL_COPPER_PAGE 0 ++#define MII_MARVELL_LED_PAGE 3 ++#define MII_MARVELL_WOL_PAGE 17 ++#define MII_MARVELL_PHY_PAGE 22 ++ ++#define MII_PHY_LED_CTRL 16 ++#define MII_88E1318S_PHY_LED_TCR 18 ++#define MII_88E1318S_PHY_WOL_CTRL 16 ++#define MII_M1011_IEVENT 19 ++ ++#define MII_88E1318S_PHY_LED_TCR_INTn_ENABLE BIT(7) ++#define MII_88E1318S_PHY_LED_TCR_FORCE_INT BIT(15) ++#define MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS BIT(12) ++#define LED2_FORCE_ON (0x8 << 8) ++#define LEDMASK GENMASK(11,8) ++ ++static struct phy_device *phydev; ++ ++static void mvphy_reg_intn(u16 data) ++{ ++ int rc = 0, saved_page; ++ ++ saved_page = phy_select_page(phydev, MII_MARVELL_LED_PAGE); ++ if (saved_page < 0) ++ goto err; ++ ++ /* Force manual LED2 control to let INTn work */ ++ __phy_modify(phydev, MII_PHY_LED_CTRL, LEDMASK, LED2_FORCE_ON); ++ ++ /* Set the LED[2]/INTn pin to the required state */ ++ __phy_modify(phydev, MII_88E1318S_PHY_LED_TCR, ++ MII_88E1318S_PHY_LED_TCR_FORCE_INT, ++ MII_88E1318S_PHY_LED_TCR_INTn_ENABLE | data); ++ ++ if (!data) { ++ /* Clear interrupts to ensure INTn won't be holded in high state */ ++ __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_COPPER_PAGE); ++ __phy_read(phydev, MII_M1011_IEVENT); ++ ++ /* If WOL was enabled and a magic packet was received before powering ++ * off, we won't be able to wake up by sending another magic packet. ++ * Clear WOL status. ++ */ ++ __phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_WOL_PAGE); ++ __phy_set_bits(phydev, MII_88E1318S_PHY_WOL_CTRL, ++ MII_88E1318S_PHY_WOL_CTRL_CLEAR_WOL_STATUS); ++ } ++err: ++ rc = phy_restore_page(phydev, saved_page, rc); ++ if (rc < 0) ++ dev_err(&phydev->mdio.dev, "Write register failed, %d\n", rc); ++ ++ if (!data) { ++ /* Slow down the PHY to save energy */ ++ rc= phy_speed_down(phydev, false); ++ if (rc < 0) ++ dev_err(&phydev->mdio.dev, "PHY speed down failed, %d\n", rc); ++ } ++} ++ ++static int linkstation_reboot_notifier(struct notifier_block *nb, ++ unsigned long action, void *unused) ++{ ++ if (action == SYS_RESTART) ++ mvphy_reg_intn(MII_88E1318S_PHY_LED_TCR_FORCE_INT); ++ ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block linkstation_reboot_nb = { ++ .notifier_call = linkstation_reboot_notifier, ++}; ++ ++static void linkstation_poweroff(void) ++{ ++ unregister_reboot_notifier(&linkstation_reboot_nb); ++ mvphy_reg_intn(0); ++ ++ kernel_restart("Power off"); ++} ++ ++static const struct of_device_id ls_poweroff_of_match[] = { ++ { .compatible = "buffalo,ls421d" }, ++ { .compatible = "buffalo,ls421de" }, ++ { }, ++}; ++ ++static int __init linkstation_poweroff_init(void) ++{ ++ struct mii_bus *bus; ++ struct device_node *dn; ++ ++ dn = of_find_matching_node(NULL, ls_poweroff_of_match); ++ if (!dn) ++ return -ENODEV; ++ of_node_put(dn); ++ ++ dn = of_find_node_by_name(NULL, "mdio"); ++ if (!dn) ++ return -ENODEV; ++ ++ bus = of_mdio_find_bus(dn); ++ of_node_put(dn); ++ if (!bus) ++ return -EPROBE_DEFER; ++ ++ phydev = phy_find_first(bus); ++ if (!phydev) ++ return -EPROBE_DEFER; ++ ++ register_reboot_notifier(&linkstation_reboot_nb); ++ pm_power_off = linkstation_poweroff; ++ ++ pr_info("LinkStation power off driver registered\n"); ++ return 0; ++} ++ ++static void __exit linkstation_poweroff_exit(void) ++{ ++ pm_power_off = NULL; ++ unregister_reboot_notifier(&linkstation_reboot_nb); ++} ++ ++module_init(linkstation_poweroff_init); ++module_exit(linkstation_poweroff_exit); ++ ++MODULE_AUTHOR("Daniel González Cabanelas <dgcbueu@gmail.com>"); ++MODULE_DESCRIPTION("LinkStation power off driver"); ++MODULE_LICENSE("GPL v2"); |