mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-26 04:15:27 +07:00
832f5dacfa
Currently CONFIG_ARCH_HAVE_CUSTOM_GPIO_H is defined for all MIPS machines, and each machine type provides its own gpio.h. However only a handful really implement the GPIO API, most just forward everythings to gpiolib. The Alchemy machine is notable as it provides a system to allow implementing the GPIO API at the board level. But it is not used by any board currently supported, so it can also be removed. For most machine types we can just remove the custom gpio.h, as well as the custom wrappers if some exists. Some of the code found in the wrappers must be moved to the respective GPIO driver. A few more fixes are need in some drivers as they rely on linux/gpio.h to provides some machine specific definitions, or used asm/gpio.h instead of linux/gpio.h for the gpio API. Signed-off-by: Alban Bedel <albeu@free.fr> Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Cc: linux-mips@linux-mips.org Cc: Hauke Mehrtens <hauke@hauke-m.de> Cc: Rafał Miłecki <zajec5@gmail.com> Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> Cc: Tejun Heo <tj@kernel.org> Cc: Alexandre Courbot <gnurou@gmail.com> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com> Cc: Florian Fainelli <florian@openwrt.org> Cc: Manuel Lauss <manuel.lauss@gmail.com> Cc: Joe Perches <joe@perches.com> Cc: Daniel Walter <dwalter@google.com> Cc: Sergey Ryazanov <ryazanov.s.a@gmail.com> Cc: Huacai Chen <chenhc@lemote.com> Cc: James Hartley <james.hartley@imgtec.com> Cc: Andrew Bresticker <abrestic@chromium.org> Cc: Paul Burton <paul.burton@imgtec.com> Cc: Jiri Kosina <jkosina@suse.cz> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Wolfram Sang <wsa@the-dreams.de> Cc: Randy Dunlap <rdunlap@infradead.org> Cc: Varka Bhadram <varkabhadram@gmail.com> Cc: Masanari Iida <standby24x7@gmail.com> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com> Cc: Michael Buesch <m@bues.ch> Cc: abdoulaye berthe <berthe.ab@gmail.com> Cc: linux-kernel@vger.kernel.org Cc: linux-ide@vger.kernel.org Cc: linux-gpio@vger.kernel.org Cc: linux-input@vger.kernel.org Cc: netdev@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/10828/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
254 lines
6.0 KiB
C
254 lines
6.0 KiB
C
/*
|
|
* Alchemy Development Board example suspend userspace interface.
|
|
*
|
|
* (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
|
|
*/
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/kobject.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/sysfs.h>
|
|
#include <asm/mach-au1x00/au1000.h>
|
|
#include <asm/mach-au1x00/gpio-au1000.h>
|
|
#include <asm/mach-db1x00/bcsr.h>
|
|
|
|
/*
|
|
* Generic suspend userspace interface for Alchemy development boards.
|
|
* This code exports a few sysfs nodes under /sys/power/db1x/ which
|
|
* can be used by userspace to en/disable all au1x-provided wakeup
|
|
* sources and configure the timeout after which the the TOYMATCH2 irq
|
|
* is to trigger a wakeup.
|
|
*/
|
|
|
|
|
|
static unsigned long db1x_pm_sleep_secs;
|
|
static unsigned long db1x_pm_wakemsk;
|
|
static unsigned long db1x_pm_last_wakesrc;
|
|
|
|
static int db1x_pm_enter(suspend_state_t state)
|
|
{
|
|
unsigned short bcsrs[16];
|
|
int i, j, hasint;
|
|
|
|
/* save CPLD regs */
|
|
hasint = bcsr_read(BCSR_WHOAMI);
|
|
hasint = BCSR_WHOAMI_BOARD(hasint) >= BCSR_WHOAMI_DB1200;
|
|
j = (hasint) ? BCSR_MASKSET : BCSR_SYSTEM;
|
|
|
|
for (i = BCSR_STATUS; i <= j; i++)
|
|
bcsrs[i] = bcsr_read(i);
|
|
|
|
/* shut off hexleds */
|
|
bcsr_write(BCSR_HEXCLEAR, 3);
|
|
|
|
/* enable GPIO based wakeup */
|
|
alchemy_gpio1_input_enable();
|
|
|
|
/* clear and setup wake cause and source */
|
|
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
|
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
|
|
|
alchemy_wrsys(db1x_pm_wakemsk, AU1000_SYS_WAKEMSK);
|
|
|
|
/* setup 1Hz-timer-based wakeup: wait for reg access */
|
|
while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
|
|
asm volatile ("nop");
|
|
|
|
alchemy_wrsys(alchemy_rdsys(AU1000_SYS_TOYREAD) + db1x_pm_sleep_secs,
|
|
AU1000_SYS_TOYMATCH2);
|
|
|
|
/* wait for value to really hit the register */
|
|
while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
|
|
asm volatile ("nop");
|
|
|
|
/* ...and now the sandman can come! */
|
|
au_sleep();
|
|
|
|
|
|
/* restore CPLD regs */
|
|
for (i = BCSR_STATUS; i <= BCSR_SYSTEM; i++)
|
|
bcsr_write(i, bcsrs[i]);
|
|
|
|
/* restore CPLD int registers */
|
|
if (hasint) {
|
|
bcsr_write(BCSR_INTCLR, 0xffff);
|
|
bcsr_write(BCSR_MASKCLR, 0xffff);
|
|
bcsr_write(BCSR_INTSTAT, 0xffff);
|
|
bcsr_write(BCSR_INTSET, bcsrs[BCSR_INTSET]);
|
|
bcsr_write(BCSR_MASKSET, bcsrs[BCSR_MASKSET]);
|
|
}
|
|
|
|
/* light up hexleds */
|
|
bcsr_write(BCSR_HEXCLEAR, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int db1x_pm_begin(suspend_state_t state)
|
|
{
|
|
if (!db1x_pm_wakemsk) {
|
|
printk(KERN_ERR "db1x: no wakeup source activated!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void db1x_pm_end(void)
|
|
{
|
|
/* read and store wakeup source, the clear the register. To
|
|
* be able to clear it, WAKEMSK must be cleared first.
|
|
*/
|
|
db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
|
|
|
|
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
|
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
|
}
|
|
|
|
static const struct platform_suspend_ops db1x_pm_ops = {
|
|
.valid = suspend_valid_only_mem,
|
|
.begin = db1x_pm_begin,
|
|
.enter = db1x_pm_enter,
|
|
.end = db1x_pm_end,
|
|
};
|
|
|
|
#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
|
|
|
|
static ssize_t db1x_pmattr_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
char *buf)
|
|
{
|
|
int idx;
|
|
|
|
if (ATTRCMP(timer_timeout))
|
|
return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
|
|
|
|
else if (ATTRCMP(timer))
|
|
return sprintf(buf, "%u\n",
|
|
!!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
|
|
|
|
else if (ATTRCMP(wakesrc))
|
|
return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
|
|
|
|
else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
|
|
ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
|
|
ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
|
|
idx = (attr->attr.name)[4] - '0';
|
|
return sprintf(buf, "%d\n",
|
|
!!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
|
|
|
|
} else if (ATTRCMP(wakemsk)) {
|
|
return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
|
|
}
|
|
|
|
return -ENOENT;
|
|
}
|
|
|
|
static ssize_t db1x_pmattr_store(struct kobject *kobj,
|
|
struct kobj_attribute *attr,
|
|
const char *instr,
|
|
size_t bytes)
|
|
{
|
|
unsigned long l;
|
|
int tmp;
|
|
|
|
if (ATTRCMP(timer_timeout)) {
|
|
tmp = kstrtoul(instr, 0, &l);
|
|
if (tmp)
|
|
return tmp;
|
|
|
|
db1x_pm_sleep_secs = l;
|
|
|
|
} else if (ATTRCMP(timer)) {
|
|
if (instr[0] != '0')
|
|
db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
|
|
else
|
|
db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
|
|
|
|
} else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
|
|
ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
|
|
ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
|
|
tmp = (attr->attr.name)[4] - '0';
|
|
if (instr[0] != '0') {
|
|
db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
|
|
} else {
|
|
db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
|
|
}
|
|
|
|
} else if (ATTRCMP(wakemsk)) {
|
|
tmp = kstrtoul(instr, 0, &l);
|
|
if (tmp)
|
|
return tmp;
|
|
|
|
db1x_pm_wakemsk = l & 0x0000003f;
|
|
|
|
} else
|
|
bytes = -ENOENT;
|
|
|
|
return bytes;
|
|
}
|
|
|
|
#define ATTR(x) \
|
|
static struct kobj_attribute x##_attribute = \
|
|
__ATTR(x, 0664, db1x_pmattr_show, \
|
|
db1x_pmattr_store);
|
|
|
|
ATTR(gpio0) /* GPIO-based wakeup enable */
|
|
ATTR(gpio1)
|
|
ATTR(gpio2)
|
|
ATTR(gpio3)
|
|
ATTR(gpio4)
|
|
ATTR(gpio5)
|
|
ATTR(gpio6)
|
|
ATTR(gpio7)
|
|
ATTR(timer) /* TOYMATCH2-based wakeup enable */
|
|
ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */
|
|
ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */
|
|
ATTR(wakemsk) /* direct access to SYS_WAKEMSK */
|
|
|
|
#define ATTR_LIST(x) & x ## _attribute.attr
|
|
static struct attribute *db1x_pmattrs[] = {
|
|
ATTR_LIST(gpio0),
|
|
ATTR_LIST(gpio1),
|
|
ATTR_LIST(gpio2),
|
|
ATTR_LIST(gpio3),
|
|
ATTR_LIST(gpio4),
|
|
ATTR_LIST(gpio5),
|
|
ATTR_LIST(gpio6),
|
|
ATTR_LIST(gpio7),
|
|
ATTR_LIST(timer),
|
|
ATTR_LIST(timer_timeout),
|
|
ATTR_LIST(wakesrc),
|
|
ATTR_LIST(wakemsk),
|
|
NULL, /* terminator */
|
|
};
|
|
|
|
static struct attribute_group db1x_pmattr_group = {
|
|
.name = "db1x",
|
|
.attrs = db1x_pmattrs,
|
|
};
|
|
|
|
/*
|
|
* Initialize suspend interface
|
|
*/
|
|
static int __init pm_init(void)
|
|
{
|
|
/* init TOY to tick at 1Hz if not already done. No need to wait
|
|
* for confirmation since there's plenty of time from here to
|
|
* the next suspend cycle.
|
|
*/
|
|
if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767)
|
|
alchemy_wrsys(32767, AU1000_SYS_TOYTRIM);
|
|
|
|
db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
|
|
|
|
alchemy_wrsys(0, AU1000_SYS_WAKESRC);
|
|
alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
|
|
|
|
suspend_set_ops(&db1x_pm_ops);
|
|
|
|
return sysfs_create_group(power_kobj, &db1x_pmattr_group);
|
|
}
|
|
|
|
late_initcall(pm_init);
|