summaryrefslogtreecommitdiff
path: root/drivers/power/reset/linkstation-poweroff.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-08-08 07:27:37 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2020-08-08 07:27:37 +0300
commit449dc8c97089a6e09fb2dac4d92b1b7ac0eb7c1e (patch)
tree31db869c221d9a0de519a7c2206374029fa9943e /drivers/power/reset/linkstation-poweroff.c
parentb79675e15a754ca51b9fc631e0961ccdd4ec3fc7 (diff)
parent46cbd0b05799e8234b719d18f3a4b27679c4c92e (diff)
downloadlinux-449dc8c97089a6e09fb2dac4d92b1b7ac0eb7c1e.tar.xz
Merge tag 'for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply
Pull power supply and reset updates from Sebastian Reichel: "Power-supply core: - add COOL/WARM/HOT state from JEITA JISC8712:2015 specification - convert simple-battery DT binding to YAML - add long-life charging mode Battery/charger drivers: - bq25150: new charger driver - bq27xxx: add support for BQ27z561 and BQ28z610 - max17040: support CAPACITY_ALERT_MIN - sbs-battery: add PEC support - wilco-ec: support long-life charging mode - bq25890: fix DT binding - misc. fixes and cleanups Reset drivers: - linkstation: new reset driver" * tag 'for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (32 commits) power: supply: wilco_ec: Add long life charging mode power: supply: bq27xxx_battery: Add the BQ28z610 Battery monitor dt-bindings: power: Add BQ28z610 compatible power: supply: bq27xxx_battery: Add the BQ27Z561 Battery monitor dt-bindings: power: Add BQ27Z561 compatible power: supply: test_power: Fix battery_current initial value power: supply: Fix kerneldoc of power_supply_temp2resist_simple() power: supply: cpcap-battery: Fix kerneldoc of cpcap_battery_read_accumulated() dt-bindings: power: Convert battery.txt to battery.yaml power: supply: rt5033_battery: Fix error code in rt5033_battery_probe() power: supply: max17040: Add POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN power: supply: check if calc_soc succeeded in pm860x_init_battery power: supply: bq2xxxx: Replace HTTP links with HTTPS ones power: reset: add driver for LinkStation power off power: supply: sc27xx: prevent adc * 1000 from overflow math64: New DIV_S64_ROUND_CLOSEST helper power: fix duplicated words in bq2415x_charger.h power: Convert to DEFINE_SHOW_ATTRIBUTE power: reset: keystone-reset: Replace HTTP links with HTTPS ones power: supply: bq25150 introduce the bq25150 ...
Diffstat (limited to 'drivers/power/reset/linkstation-poweroff.c')
-rw-r--r--drivers/power/reset/linkstation-poweroff.c136
1 files changed, 136 insertions, 0 deletions
diff --git a/drivers/power/reset/linkstation-poweroff.c b/drivers/power/reset/linkstation-poweroff.c
new file mode 100644
index 000000000000..39e89baedb5f
--- /dev/null
+++ b/drivers/power/reset/linkstation-poweroff.c
@@ -0,0 +1,136 @@
+// 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);
+}
+
+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;
+
+ 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");