public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Yifeng Li <tomli@tomli.me>
To: Lee Jones <lee.jones@linaro.org>, linux-mips@vger.kernel.org
Cc: Yifeng Li <tomli@tomli.me>, Jiaxun Yang <jiaxun.yang@flygoat.com>,
	Huacai Chen <chenhc@lemote.com>,
	Ralf Baechle <ralf@linux-mips.org>,
	Paul Burton <paul.burton@mips.com>,
	James Hogan <jhogan@kernel.org>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 6/7] mips: loongson64: Support System Control Interrupts for Lemote Yeeloong.
Date: Sun,  3 Mar 2019 01:53:33 +0800	[thread overview]
Message-ID: <20190302175334.5103-7-tomli@tomli.me> (raw)
In-Reply-To: <20190302175334.5103-1-tomli@tomli.me>

The hardware design of Yeeloong laptops is similar to OLPC: low-level
hardware events are processed by the ENE KB3310B Embedded Controller,
which is connected to the AMD CS5536 southbridge through a GPIO port.

When a hardware event occurs, such as a short-circuit on the USB port,
removing the power supply, plugging in a VGA adapter, opening the lid
of the laptop, or pressing a hotkey on the keyboard, the EC sends a
pulse to CS5536, which then fires a System Control Interrupt to notify
the kernel.

In the previous attempted submission, the logic for handling SCI and
hotkeys was tightly-coupled, and the driver was called the "hotkey"
driver, which was misleading. In this implementation of sci.c, we only
handle the minimum number of things that deal with the underlying
platform hardware directly, such as setting up the CS5536 GPIO and IRQ,
handling the the power supply of USB and camera. The vast majority of
events are passed to the subdrivers via a notification chain, thus,
the SCI logic and the specific hotkey handling logic have been decoupled.

Signed-off-by: Yifeng Li <tomli@tomli.me>
---
 arch/mips/loongson64/lemote-2f/Makefile |   2 +-
 arch/mips/loongson64/lemote-2f/sci.c    | 392 ++++++++++++++++++++++++
 2 files changed, 393 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/loongson64/lemote-2f/sci.c

diff --git a/arch/mips/loongson64/lemote-2f/Makefile b/arch/mips/loongson64/lemote-2f/Makefile
index 2b18752424ee..af4a1d347884 100644
--- a/arch/mips/loongson64/lemote-2f/Makefile
+++ b/arch/mips/loongson64/lemote-2f/Makefile
@@ -2,7 +2,7 @@
 # Makefile for lemote loongson2f family machines
 #
 
-obj-y += clock.o machtype.o irq.o reset.o dma.o platform.o
+obj-y += clock.o machtype.o irq.o reset.o dma.o platform.o sci.o
 
 #
 # Suspend Support
diff --git a/arch/mips/loongson64/lemote-2f/sci.c b/arch/mips/loongson64/lemote-2f/sci.c
new file mode 100644
index 000000000000..96e376f00ecc
--- /dev/null
+++ b/arch/mips/loongson64/lemote-2f/sci.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Support for Lemote Yeeloong System Control Interrupts (SCI)
+ *
+ * Copyright (C) 2009 Lemote Inc.
+ * Author: Wu Zhangjin <wuzhangjin@gmail.com>
+ *         Liu Junliang <liujl@lemote.com>
+ *
+ * Copyright (C) 2012, 2013, 2014, 2015 Petr Pisar
+ * Author: Petr Pisar <petr.pisar@atlas.cz> (bugfixes)
+ *
+ * Copyright (C) 2017 Jiaxun Yang
+ * Author: Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * Copyright (C) 2019 Yifeng Li
+ * Author: Yifeng Li <tomli@tomli.me>
+ *
+ * The hardware design of Yeeloong laptops is similar to OLPC: low-level
+ * hardware events are processed by the ENE KB3310B Embedded Controller,
+ * which is connected to the AMD CS5536 southbridge through a GPIO port.
+ *
+ * When a hardware event occurs, such as a short-circuit on the USB port,
+ * removing the power supply, plugging in a VGA adapter, opening the lid
+ * of the laptop, or pressing a hotkey on the keyboard, the EC sends a
+ * pulse to CS5536, which then fires a System Control Interrupt to notify
+ * the kernel.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/power_supply.h>
+#include <linux/backlight.h>
+#include <linux/mfd/yeeloong_kb3310b.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+
+#include <loongson.h>
+#include <cs5536/cs5536.h>
+
+static void usb_power_set(bool power)
+{
+	kb3310b_write(KB3310B_REG_USB0_FLAG, power);
+	kb3310b_write(KB3310B_REG_USB1_FLAG, power);
+	kb3310b_write(KB3310B_REG_USB2_FLAG, power);
+}
+
+static void camera_power_set(bool power)
+{
+	kb3310b_write(KB3310B_REG_CAMERA_CONTROL, power);
+}
+
+/*
+ * Handlers for EC events. We only handles the bare-minimum number of events
+ * directly related to the platform hardware. Other events are reported to
+ * subdrivers and handled by them.
+ */
+static void usb0_handler(void)
+{
+	pr_emerg("USB0 Overcurrent occurred!\n");
+}
+
+static void usb2_handler(void)
+{
+	pr_emerg("USB2 Overcurrent occurred!\n");
+}
+
+static void camera_handler(void)
+{
+	camera_power_set(!kb3310b_read(KB3310B_REG_CAMERA_CONTROL));
+}
+
+/*
+ * SCI notifiers. This notifier reports EC events to various
+ * subdrivers.
+ */
+static BLOCKING_NOTIFIER_HEAD(yeeloong_sci_notifier_list);
+
+int yeeloong_sci_register_notify(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&yeeloong_sci_notifier_list,
+						nb);
+}
+EXPORT_SYMBOL_GPL(yeeloong_sci_register_notify);
+
+int yeeloong_sci_unregister_notify(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&yeeloong_sci_notifier_list,
+						nb);
+}
+EXPORT_SYMBOL_GPL(yeeloong_sci_unregister_notify);
+
+/*
+ * Do not handle or notify other drivers about certain events while we're
+ * going down to reboot or sleep. This avoids unexpected behaviors in the
+ * userspace, such as closing the laptop lid while rebooting.
+ */
+static atomic_t reboot_flag;
+static atomic_t sleep_flag;
+
+static int
+notify_reboot(struct notifier_block *nb, unsigned long event, void *buf)
+{
+	switch (event) {
+	case SYS_RESTART:
+	case SYS_HALT:
+	case SYS_POWER_OFF:
+		atomic_set(&reboot_flag, 1);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static int
+notify_pm(struct notifier_block *nb, unsigned long event, void *buf)
+{
+	switch (event) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_SUSPEND_PREPARE:
+		atomic_inc(&sleep_flag);
+		break;
+	case PM_POST_HIBERNATION:
+	case PM_POST_SUSPEND:
+	case PM_RESTORE_PREPARE:        /* do we need this ?? */
+		atomic_dec(&sleep_flag);
+		break;
+	default:
+		return NOTIFY_DONE;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block reboot_notifier = {
+	.notifier_call = notify_reboot,
+};
+
+static struct notifier_block pm_notifier = {
+	.notifier_call = notify_pm,
+};
+
+static bool is_spurious_event(int event)
+{
+	if (event == KB3310B_EVENT_LID || event == KB3310B_EVENT_SLEEP)
+		return !!(atomic_read(&reboot_flag) | atomic_read(&sleep_flag));
+	else
+		return false;
+}
+
+/*
+ * Invoke the handler of the reported SCI event.
+ */
+static void process_sci_event(int event)
+{
+	if (is_spurious_event(event))
+		return;
+
+	switch (event) {
+	case KB3310B_EVENT_USB_OC0:
+		usb0_handler();
+		break;
+	case KB3310B_EVENT_USB_OC2:
+		usb2_handler();
+		break;
+	case KB3310B_EVENT_CAMERA:
+		camera_handler();
+		break;
+	default:
+		break;
+	}
+
+	/*
+	 * Report this event to other subdrivers, in particular, yeeloong-hotkey
+	 * driver will report the hotkey to userspace.
+	 */
+	blocking_notifier_call_chain(&yeeloong_sci_notifier_list, event, NULL);
+}
+
+/*
+ * Handle the SCI event and perform the needed action.
+ *
+ * A SCI event lasts about 120 microseconds. It means the function must take
+ * longer than 120 microseconds to complete. It has been shown that the function
+ * already takes 3 ms, so no artificial delay is needed.
+ */
+static irqreturn_t sci_irq_handler(int irq, void *dev_id)
+{
+	int ret, event;
+
+	if (irq != KB3310B_SCI_IRQ_NUM)
+		return IRQ_NONE;
+
+	/* query the event number */
+	ret = kb3310b_query_event_num();
+	if (ret < 0)
+		return IRQ_NONE;
+
+	event = kb3310b_get_event_num();
+	if (event < KB3310B_EVENT_START || event > KB3310B_EVENT_END)
+		return IRQ_NONE;
+
+	/* execute the corresponding action */
+	process_sci_event(event);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Program the GPIO and MSR registers on CS5536.
+ *
+ * TODO: Linux kernel already has a cs5535-gpio kernel driver, but that
+ *       driver not adapted for Loongson. It's desirable to convert these
+ *       raw operations to use cs5535-gpio. Also, Loongson has its own
+ *       CS5536 clocksource driver, though Linux already has cs5535-clockevt,
+ *       but only supports clockevent, not clocksource. Clocksource code
+ *       should be merged into cs5535-clockevt and Loongson should use
+ *       that, too.
+ */
+static int setup_ec_sci(void)
+{
+	u32 hi, lo;
+	u32 gpio_base;
+	unsigned long flags;
+	int ret;
+
+	/* Get GPIO base */
+	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
+	gpio_base = lo & 0xff00;
+
+	/*
+	 * HACK: to prevent any interrupts from being fired, we query
+	 * the EC to clear the pending event, and wait for a while to
+	 * miss the interrupt intentionally.
+	 */
+	ret = kb3310b_query_event_num();
+	if (ret)
+		return ret;
+
+	/* wait for a while */
+	mdelay(10);
+
+	/*
+	 * Set GPIO native registers and MSRs for GPIO-27 SCI EVENT PIN
+	 *
+	 * MSR:
+	 *     no primary and LPC.
+	 *     Unrestricted Z Input 8 to IG-10 from Virtual GPIO-0.
+	 *
+	 * GPIO mode:
+	 *     input, pull-up, no-invert, event-count and value 0,
+	 *     no-filter, no-edge. GPIO-27 map to Virtual GPIO-0.
+	 */
+	local_irq_save(flags);
+
+	/* set primary mask */
+	_rdmsr(0x80000024, &hi, &lo);
+	lo &= ~(1 << 10);
+	_wrmsr(0x80000024, hi, lo);
+
+	/* set LPC mask */
+	_rdmsr(0x80000025, &hi, &lo);
+	lo &= ~(1 << 10);
+	_wrmsr(0x80000025, hi, lo);
+
+	/* set Unrestricted Z map */
+	_rdmsr(0x80000023, &hi, &lo);
+	lo |= (0x0a << 0);
+	_wrmsr(0x80000023, hi, lo);
+
+	local_irq_restore(flags);
+
+	asm(".set noreorder\n");
+	outl(0x00000800, (gpio_base | 0xA0));  /* GPIO-27 input enable */
+	outl(0x00000800, (gpio_base | 0xA4));  /* GPIO-27 input invert */
+	outl(0x00000800, (gpio_base | 0xB8));  /* GPIO-27 event-int enable */
+	asm(".set reorder\n");
+
+	return 0;
+}
+
+/*
+ * Setup the IRQ handler for SCI. Must be called after setup_ec_sci().
+ */
+static int setup_sci_interrupt(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = request_threaded_irq(KB3310B_SCI_IRQ_NUM, NULL, &sci_irq_handler,
+					IRQF_ONESHOT, "sci", NULL);
+	if (ret)
+		dev_err(&pdev->dev, "unable to request interrupt!\n");
+
+	return ret;
+}
+
+static int yeeloong_sci_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	camera_power_set(KB3310B_BIT_CAMERA_CONTROL_OFF);
+	usb_power_set(KB3310B_BIT_USB_FLAG_ON);
+
+	ret = register_reboot_notifier(&reboot_notifier);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register reboot_notifier!\n");
+		goto fail;
+	}
+
+	ret = register_pm_notifier(&pm_notifier);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to register pm_notifier!\n");
+		goto fail_reboot;
+	}
+
+	ret = setup_ec_sci();
+	if (ret) {
+		dev_err(&pdev->dev, "unable to setup EC SCI!\n");
+		goto fail_irq;
+	}
+
+	ret = setup_sci_interrupt(pdev);
+	if (ret)
+		goto fail_irq;
+
+	return ret;
+
+fail_irq:
+	free_irq(KB3310B_SCI_IRQ_NUM, NULL);
+	unregister_pm_notifier(&pm_notifier);
+fail_reboot:
+	unregister_reboot_notifier(&reboot_notifier);
+fail:
+	return ret;
+}
+
+#ifdef CONFIG_SUSPEND
+static int
+yeeloong_sci_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	usb_power_set(KB3310B_BIT_USB_FLAG_OFF);
+	camera_power_set(KB3310B_BIT_CAMERA_CONTROL_OFF);
+	return 0;
+}
+
+static int yeeloong_sci_resume(struct platform_device *pdev)
+{
+	int ret;
+
+	usb_power_set(KB3310B_BIT_USB_FLAG_ON);
+
+	ret = setup_ec_sci();
+	if (ret) {
+		dev_err(&pdev->dev, "unable to setup EC SCI!\n");
+		return -EFAULT;
+	}
+
+	/*
+	 * Lid switch and power supply may hawe changed while we were
+	 * asleep, so we generate a KB3310B_EVENT_LID and KB3310B_EVENT_AC_BAT
+	 * events to force the hotkey/battery subdriver to report their new
+	 * states.
+	 */
+	blocking_notifier_call_chain(&yeeloong_sci_notifier_list,
+						KB3310B_EVENT_LID, NULL);
+	blocking_notifier_call_chain(&yeeloong_sci_notifier_list,
+						KB3310B_EVENT_AC_BAT, NULL);
+
+	return 0;
+}
+#endif
+
+static struct platform_driver yeeloong_sci_driver = {
+	.driver = {
+		.name = "yeeloong_sci",
+	},
+	.probe = yeeloong_sci_probe,
+#ifdef CONFIG_SUSPEND
+	.suspend = yeeloong_sci_suspend,
+	.resume = yeeloong_sci_resume,
+#endif
+};
+
+static int __init yeeloong_sci_init(void)
+{
+	return platform_driver_register(&yeeloong_sci_driver);
+}
+arch_initcall(yeeloong_sci_init);
-- 
2.20.1


  parent reply	other threads:[~2019-03-02 17:54 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-03-02 17:53 [PATCH 0/7] Preliminary Platform Driver Support for Lemote Yeeloong Laptops Yifeng Li
2019-03-02 17:53 ` [PATCH 1/7] mfd: yeeloong_kb3310b: support KB3310B EC for Lemote Yeeloong laptops Yifeng Li
     [not found]   ` <201903040432.qyPxOrOQ%fengguang.wu@intel.com>
2019-03-04  0:24     ` Tom Li
2019-03-04  0:37   ` kbuild test robot
2019-03-04 10:14     ` Lee Jones
2019-03-04 12:19       ` Tom Li
2019-03-02 17:53 ` [PATCH 2/7] mips: loongson64: select MFD_YEELOONG_KB3310B for LEMOTE_MACH2F Yifeng Li
2019-03-02 17:53 ` [PATCH 3/7] mips: loongson64: remove ec_kb3310b.c, use MFD driver Yifeng Li
2019-03-02 17:53 ` [PATCH 4/7] mips: loongson64: remove yeeloong_report_lid_status from pm.c Yifeng Li
2019-03-02 17:53 ` [PATCH 5/7] mips: loongson64: register per-board platform drivers for lemote-2f Yifeng Li
2019-03-02 17:53 ` Yifeng Li [this message]
2019-03-02 17:53 ` [PATCH 7/7] MAINTAINERS: add myself as a maintainer of MIPS/Loongson2 platform code Yifeng Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190302175334.5103-7-tomli@tomli.me \
    --to=tomli@tomli.me \
    --cc=chenhc@lemote.com \
    --cc=jhogan@kernel.org \
    --cc=jiaxun.yang@flygoat.com \
    --cc=lee.jones@linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mips@vger.kernel.org \
    --cc=paul.burton@mips.com \
    --cc=ralf@linux-mips.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox