From 003737566584dc1bed4ddfcde17c9d59e58073c1 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 20 Sep 2019 18:31:44 +0200 Subject: regmap: align documentation in comment with code dev_get_regmap has some copy-paste left-over in the comment. Rectify this. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d2f8ec70e4..dc7b4f276f 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -65,7 +65,7 @@ struct regmap *regmap_init(struct device_d *dev, } /* - * regmap_init - initialize and register a regmap + * dev_get_regmap - get a regmap from a device * * @dev: The device the maps is attached to * @name: Optional name for the map. If given it must match. @@ -406,4 +406,4 @@ void regmap_exit(struct regmap *map) } free(map); -} \ No newline at end of file +} -- cgit v1.2.3 From d1210dc63f38e7e6d8147b44b3f6f9518c099306 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Fri, 20 Sep 2019 13:35:19 +0200 Subject: mtd: ubi: Max out wear-leveling threshold Due to the recursive ubi_thread implementation in the barebox, a large amount of wear-leveling can lead to a stack overflow. This was observed during extensive ubi stress tests with the linux kernel and periodic power cycles. We found that if the wear-leveling threshold is exceeded and a large amount of erase blocks need wear-leveling the stack can overflow. The hardware used to observe this was a phyCORE-i.MX 6 with 1GB NAND flash. As the kernel is perfectly capable of handling wear-leveling we can disable wear-leveling in the barebox by maxing out the threshold and removing its Kconfig option. Signed-off-by: Stefan Riedmueller Signed-off-by: Sascha Hauer --- drivers/mtd/ubi/Kconfig | 17 ----------------- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/ubi.h | 11 +++++++++++ drivers/mtd/ubi/wl.c | 8 -------- 4 files changed, 12 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 9a344082b7..ed2f13d14c 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -10,23 +10,6 @@ menuconfig MTD_UBI if MTD_UBI -config MTD_UBI_WL_THRESHOLD - int "UBI wear-leveling threshold" - default 4096 - range 2 65536 - help - This parameter defines the maximum difference between the highest - erase counter value and the lowest erase counter value of eraseblocks - of UBI devices. When this threshold is exceeded, UBI starts performing - wear leveling by means of moving data from eraseblock with low erase - counter to eraseblocks with high erase counter. - - The default value should be OK for SLC NAND flashes, NOR flashes and - other flashes which have eraseblock life-cycle 100000 or more. - However, in case of MLC NAND flashes which typically have eraseblock - life-cycle less than 10000, the threshold should be lessened (e.g., - to 128 or 256, although it does not have to be power of 2). - config MTD_UBI_BEB_LIMIT int "Maximum expected bad eraseblock count per 1024 eraseblocks" default 20 diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 493c778c3f..604fe87e53 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -655,7 +655,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, ubi->vol_count - UBI_INT_VOL_COUNT, UBI_INT_VOL_COUNT, ubi->vtbl_slots); ubi_msg(ubi, "max/mean erase counter: %d/%d, WL threshold: %d, image sequence number: %u", - ubi->max_ec, ubi->mean_ec, CONFIG_MTD_UBI_WL_THRESHOLD, + ubi->max_ec, ubi->mean_ec, UBI_WL_THRESHOLD, ubi->image_seq); ubi_msg(ubi, "available PEBs: %d, total reserved PEBs: %d, PEBs reserved for bad PEB handling: %d", ubi->avail_pebs, ubi->rsvd_pebs, ubi->beb_rsvd_pebs); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 922c1a3c8b..7d07bbf197 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -69,6 +69,17 @@ */ #define UBI_PROT_QUEUE_LEN 10 +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL sub-system starts moving data from used physical + * eraseblocks with low erase counter to free physical eraseblocks with high + * erase counter. + * Extensive wear-leveling in the barebox can lead to stack overflows. Thus + * disable it by setting the threshold to the OS's max configurable value and + * leave wear-leveling to the OS. + */ +#define UBI_WL_THRESHOLD 65536 + /* The volume ID/LEB number/erase counter is unknown */ #define UBI_UNKNOWN -1 diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index cf90ecfb23..013ba3e1ff 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -100,14 +100,6 @@ /* Number of physical eraseblocks reserved for wear-leveling purposes */ #define WL_RESERVED_PEBS 1 -/* - * Maximum difference between two erase counters. If this threshold is - * exceeded, the WL sub-system starts moving data from used physical - * eraseblocks with low erase counter to free physical eraseblocks with high - * erase counter. - */ -#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD - /* * When a physical eraseblock is moved, the WL sub-system has to pick the target * physical eraseblock to move to. The simplest way would be just to pick the -- cgit v1.2.3 From 95a4e202f84995718ce4a689dd60972bc416c133 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 20 Sep 2019 09:58:11 +0200 Subject: input: set console input name as input The input console is usually called /dev/cs0 and devinfo doesn't yield any extra information what this device is about. Rename it for clarity. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/input/input.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/input/input.c b/drivers/input/input.c index 14e44d1378..1e8f6e178e 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -195,6 +195,8 @@ static int input_init(void) ic->console.tstc = input_console_tstc; ic->console.getc = input_console_getc; ic->console.f_active = CONSOLE_STDIN; + ic->console.devid = DEVICE_ID_DYNAMIC; + ic->console.devname = "input"; ic->fifo = kfifo_alloc(32); ic->notifier.notify = input_console_notify; -- cgit v1.2.3 From a805a3cab4e3ef5b8673dd4e4a2fe2f7480a960d Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 20 Sep 2019 09:58:13 +0200 Subject: USB: gadget: ACM: don't announce V.25ter support barebox currently announces support for ITU V.25ter AT commands, but doesn't handle them specially when they arrive. Instead they are passed as is to the sole barebox input console, where it may interfere with valid user input. This is especially annoying as ModemManager probes ttyACM devices that announce their AT command support. So even when not using the ttyACM device at all, the other UART ports are affected. Fix this by ceasing to announce USB_CDC_ACM_PROTO_AT_V25TER as function protocol. After applying this patch, I can't see any spurious AT or ~x~ symbols on the console anymore. Cc: Cc: Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/usb/gadget/f_acm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index cba59b1585..42a2b03ad2 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -104,7 +104,7 @@ acm_iad_descriptor = { .bInterfaceCount = 2, // control + data .bFunctionClass = USB_CLASS_COMM, .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .bFunctionProtocol = USB_CDC_PROTO_NONE, /* .iFunction = DYNAMIC */ }; @@ -116,7 +116,7 @@ static struct usb_interface_descriptor acm_control_interface_desc = { .bNumEndpoints = 1, .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, /* .iInterface = DYNAMIC */ }; -- cgit v1.2.3 From 6e12f17acaa6de4144af816e6d176df8a6b083de Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Mon, 23 Sep 2019 12:12:10 -0700 Subject: net: dsa: mv88e6xxx: Fix error message typo s/MIDO/MDIO Signed-off-by: Andrey Smirnov Signed-off-by: Sascha Hauer --- drivers/net/phy/mv88e6xxx/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index 9688dbd1be..7ae09aa9c3 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -797,7 +797,7 @@ static int mv88e6xxx_probe(struct device_d *dev) err = of_property_read_u32(np, "reg", ®); if (err) { - dev_err(dev, "Couldn't determine switch MIDO address\n"); + dev_err(dev, "Couldn't determine switch MDIO address\n"); return err; } -- cgit v1.2.3 From 3dbc50408b2514125a7170da77912688eb501655 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 24 Sep 2019 18:43:13 -0700 Subject: net: dsa: mv88e6xxx: Wait for EEPROM done after HW reset Based on a unpublished patch from Andrew Lunn: When the switch is hardware reset, it reads the contents of the EEPROM. This can contain instructions for programming values into registers and to perform waits between such programming. Reading the EEPROM can take longer than the 100ms mv88e6xxx_hardware_reset() waits after deasserting the reset GPIO. So poll the EEPROM done bit to ensure it is complete. Signed-off-by: Andrew Lunn orignal patch augmented to have necessary Global 1 plubming, ported to Barebox and slightly changed. Signed-off-by: Andrey Smirnov Cc: Chris Healy Cc: Lucas Stach Signed-off-by: Sascha Hauer --- drivers/net/phy/mv88e6xxx/Makefile | 1 + drivers/net/phy/mv88e6xxx/chip.c | 34 +++++++++++++++++++++++++ drivers/net/phy/mv88e6xxx/chip.h | 1 + drivers/net/phy/mv88e6xxx/global1.c | 51 +++++++++++++++++++++++++++++++++++++ drivers/net/phy/mv88e6xxx/global1.h | 37 +++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 drivers/net/phy/mv88e6xxx/global1.c create mode 100644 drivers/net/phy/mv88e6xxx/global1.h (limited to 'drivers') diff --git a/drivers/net/phy/mv88e6xxx/Makefile b/drivers/net/phy/mv88e6xxx/Makefile index e09ea0aa47..e1d4b1b9d7 100644 --- a/drivers/net/phy/mv88e6xxx/Makefile +++ b/drivers/net/phy/mv88e6xxx/Makefile @@ -1,5 +1,6 @@ obj-y += mv88e6xxx.o mv88e6xxx-objs := chip.o +mv88e6xxx-objs += global1.o mv88e6xxx-objs += global2.o mv88e6xxx-objs += port.o diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index 7ae09aa9c3..b1bffe5cbc 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -11,6 +11,7 @@ #include #include "chip.h" +#include "global1.h" #include "global2.h" #include "port.h" @@ -342,6 +343,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6085", .num_ports = 10, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6085_ops, }, @@ -352,6 +354,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6095/88E6095F", .num_ports = 11, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6095_ops, }, @@ -362,6 +365,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6097/88E6097F", .num_ports = 11, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6097_ops, }, @@ -372,6 +376,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6123", .num_ports = 3, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6123_ops, }, @@ -382,6 +387,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6131", .num_ports = 8, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6131_ops, }, @@ -392,6 +398,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6141_ops, }, @@ -402,6 +409,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6161", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6161_ops, }, @@ -412,6 +420,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6165", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6165_ops, }, @@ -422,6 +431,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6171", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6171_ops, }, @@ -432,6 +442,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6172", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6172_ops, }, @@ -442,6 +453,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6175", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6175_ops, }, @@ -452,6 +464,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6176", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6176_ops, }, @@ -462,6 +475,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6185", .num_ports = 10, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6185_ops, }, @@ -472,6 +486,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6190_ops, }, @@ -482,6 +497,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6190X", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6190x_ops, }, @@ -492,6 +508,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6191", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6191_ops, }, @@ -502,6 +519,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6240", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6240_ops, }, @@ -512,6 +530,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6290", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6290_ops, }, @@ -522,6 +541,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6320", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6320_ops, }, @@ -532,6 +552,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6321", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6321_ops, }, @@ -542,6 +563,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6341", .num_ports = 6, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6341_ops, }, @@ -552,6 +574,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6350", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6350_ops, }, @@ -562,6 +585,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6351", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6351_ops, }, @@ -572,6 +596,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6352", .num_ports = 7, .port_base_addr = 0x10, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6352_ops, }, @@ -582,6 +607,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6390_ops, }, @@ -592,6 +618,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .name = "Marvell 88E6390X", .num_ports = 11, /* 10 + Z80 */ .port_base_addr = 0x0, + .global1_addr = 0x1b, .global2_addr = 0x1c, .ops = &mv88e6390x_ops, }, @@ -741,6 +768,8 @@ static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip) mv88e6xxx_hardware_reset_delay(); gpio_set_active(chip->reset, 0); mv88e6xxx_hardware_reset_delay(); + + mv88e6xxx_g1_wait_eeprom_done(chip); } } @@ -836,6 +865,11 @@ static int mv88e6xxx_probe(struct device_d *dev) */ mv88e6xxx_hardware_reset_delay(); } + /* + * Switch will not return valid data over MDIO until EEPROM is + * loaded + */ + mv88e6xxx_g1_wait_eeprom_done(chip); err = mv88e6xxx_detect(chip); if (err) diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 7548358de0..57f74a39a0 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -34,6 +34,7 @@ struct mv88e6xxx_info { const char *name; unsigned int num_ports; unsigned int port_base_addr; + unsigned int global1_addr; unsigned int global2_addr; const struct mv88e6xxx_ops *ops; diff --git a/drivers/net/phy/mv88e6xxx/global1.c b/drivers/net/phy/mv88e6xxx/global1.c new file mode 100644 index 0000000000..bace5396e9 --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global1.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Marvell 88E6xxx Switch Global (1) Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot + */ + +#include +#include + +#include "chip.h" +#include "global1.h" + +static int mv88e6xxx_g1_read(struct mv88e6xxx_chip *chip, int reg, u16 *val) +{ + int addr = chip->info->global1_addr; + + return mv88e6xxx_read(chip, addr, reg, val); +} + +void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip) +{ + const uint64_t start = get_time_ns(); + const uint64_t timeout = SECOND; + u16 val; + int err; + + /* Wait up to 1 second for the switch to finish reading the + * EEPROM. + */ + while (!is_timeout(start, timeout)) { + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_STS, &val); + if (err) { + dev_err(chip->dev, "Error reading status\n"); + return; + } + + if (val != 0xFFFF && /* switch will return 0xffff until + * EEPROM is loaded + */ + val & BIT(MV88E6XXX_G1_STS_IRQ_EEPROM_DONE)) + return; + + mdelay(2); + } + + dev_err(chip->dev, "Timeout waiting for EEPROM done\n"); +} diff --git a/drivers/net/phy/mv88e6xxx/global1.h b/drivers/net/phy/mv88e6xxx/global1.h new file mode 100644 index 0000000000..a505bae2bf --- /dev/null +++ b/drivers/net/phy/mv88e6xxx/global1.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Marvell 88E6xxx Switch Global (1) Registers support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2016-2017 Savoir-faire Linux Inc. + * Vivien Didelot + */ + +#ifndef _MV88E6XXX_GLOBAL1_H +#define _MV88E6XXX_GLOBAL1_H + +#include "chip.h" + +/* Offset 0x00: Switch Global Status Register */ +#define MV88E6XXX_G1_STS 0x00 +#define MV88E6352_G1_STS_PPU_STATE 0x8000 +#define MV88E6185_G1_STS_PPU_STATE_MASK 0xc000 +#define MV88E6185_G1_STS_PPU_STATE_DISABLED_RST 0x0000 +#define MV88E6185_G1_STS_PPU_STATE_INITIALIZING 0x4000 +#define MV88E6185_G1_STS_PPU_STATE_DISABLED 0x8000 +#define MV88E6185_G1_STS_PPU_STATE_POLLING 0xc000 +#define MV88E6XXX_G1_STS_INIT_READY 0x0800 +#define MV88E6XXX_G1_STS_IRQ_AVB 8 +#define MV88E6XXX_G1_STS_IRQ_DEVICE 7 +#define MV88E6XXX_G1_STS_IRQ_STATS 6 +#define MV88E6XXX_G1_STS_IRQ_VTU_PROB 5 +#define MV88E6XXX_G1_STS_IRQ_VTU_DONE 4 +#define MV88E6XXX_G1_STS_IRQ_ATU_PROB 3 +#define MV88E6XXX_G1_STS_IRQ_ATU_DONE 2 +#define MV88E6XXX_G1_STS_IRQ_TCAM_DONE 1 +#define MV88E6XXX_G1_STS_IRQ_EEPROM_DONE 0 + +void mv88e6xxx_g1_wait_eeprom_done(struct mv88e6xxx_chip *chip); + +#endif /* _MV88E6XXX_GLOBAL1_H */ -- cgit v1.2.3 From 1c27d11de850da52ce2899ae57cc1e411bc97917 Mon Sep 17 00:00:00 2001 From: Stefan Riedmueller Date: Fri, 27 Sep 2019 10:34:41 +0200 Subject: nvmem: Fix read/write access to partition devices Partition devices are not directly associated with the nvmem instance but via their master cdev. Thus reading and writing needs to be handled via the master. Signed-off-by: Stefan Riedmueller Signed-off-by: Sascha Hauer --- drivers/nvmem/core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 25924872ef..06e1414769 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -57,9 +57,14 @@ int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, unsigned long flags) { - struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + struct nvmem_device *nvmem; ssize_t retlen; + if (cdev->master) + nvmem = container_of(cdev->master, struct nvmem_device, cdev); + else + nvmem = container_of(cdev, struct nvmem_device, cdev); + dev_dbg(cdev->dev, "read ofs: 0x%08llx count: 0x%08zx\n", offset, count); @@ -71,9 +76,14 @@ static ssize_t nvmem_cdev_read(struct cdev *cdev, void *buf, size_t count, static ssize_t nvmem_cdev_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, unsigned long flags) { - struct nvmem_device *nvmem = container_of(cdev, struct nvmem_device, cdev); + struct nvmem_device *nvmem; ssize_t retlen; + if (cdev->master) + nvmem = container_of(cdev->master, struct nvmem_device, cdev); + else + nvmem = container_of(cdev, struct nvmem_device, cdev); + dev_dbg(cdev->dev, "write ofs: 0x%08llx count: 0x%08zx\n", offset, count); -- cgit v1.2.3 From c2cf9442bbaae379a29ff4c4d00870e204759282 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Tue, 1 Oct 2019 10:15:09 +0200 Subject: MIPS: net: ag71xx/ar9331: partially sync network support with upstream Ethernet support for ar9331 is upstream now. So, drop every thing what is provided by upstream devicetree and rename compatible in the driver. barebox network driver will need more work to be upstream compliant. For example we should not request or touch the gmac register directly. Since currently it is not clear how this should be implemented, patch the upstream dts with reg-names = "ge0", "gmac". Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9331-dptechnics-dpt-module.dts | 6 +----- arch/mips/dts/ar9331.dtsi | 15 ++++++--------- arch/mips/dts/tplink-mr3020.dts | 2 +- drivers/net/ag71xx.c | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/arch/mips/dts/ar9331-dptechnics-dpt-module.dts b/arch/mips/dts/ar9331-dptechnics-dpt-module.dts index 315589aab9..cbaf5ab39e 100644 --- a/arch/mips/dts/ar9331-dptechnics-dpt-module.dts +++ b/arch/mips/dts/ar9331-dptechnics-dpt-module.dts @@ -15,7 +15,7 @@ art@0 { compatible = "qca,art-ar9331", "qca,art"; device-path = &spiflash_art; - barebox,provide-mac-address = <&mac0>; + barebox,provide-mac-address = <ð0>; }; }; @@ -48,7 +48,3 @@ reg = <0x7f0000 0x10000>; }; }; - -&mac0 { - status = "okay"; -}; diff --git a/arch/mips/dts/ar9331.dtsi b/arch/mips/dts/ar9331.dtsi index 42baae1e89..72f029754e 100644 --- a/arch/mips/dts/ar9331.dtsi +++ b/arch/mips/dts/ar9331.dtsi @@ -5,14 +5,11 @@ reg = <0x18060008 0x8>; clocks = <&pll ATH79_CLK_CPU>; }; - - mac0: mac@19000000 { - compatible = "qca,ar9331-ge0"; - reg = <0x18070000 0x00000100>, - <0x19000000 0x01000000>; - reg-names = "gmac", "ge0"; - phy-mode = "mii"; - status = "disabled"; - }; }; }; + +ð0 { + reg = <0x19000000 0x200>, + <0x18070000 0x00000100>; + reg-names = "ge0", "gmac"; +}; diff --git a/arch/mips/dts/tplink-mr3020.dts b/arch/mips/dts/tplink-mr3020.dts index e30eae1578..c6ae154f4f 100644 --- a/arch/mips/dts/tplink-mr3020.dts +++ b/arch/mips/dts/tplink-mr3020.dts @@ -28,6 +28,6 @@ }; }; -&mac0 { +ð0 { status = "okay"; }; diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c index 0565c90490..70aaa60f1a 100644 --- a/drivers/net/ag71xx.c +++ b/drivers/net/ag71xx.c @@ -667,7 +667,7 @@ static void ag71xx_remove(struct device_d *dev) } static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { - { .compatible = "qca,ar9331-ge0", .data = &ag71xx_cfg_ar9331_ge0, }, + { .compatible = "qca,ar9330-eth", .data = &ag71xx_cfg_ar9331_ge0, }, { .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, }, { /* sentinel */ } }; -- cgit v1.2.3 From bd7d8325627c5918a090a0c03e1554583b4a8a63 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 11 Sep 2019 17:00:18 +0200 Subject: mfd: da9063: add support to populate subdevs The upstream dt-bindings abstracts the DA9063/2 PMIC MFD by multiple of-subnodes and the linux-mfd core creates platform-devices for each node. Due to the lack of a mfd fw we need to add the platform-devices by our own to reuse the upstream bindings. Signed-off-by: Marco Felsch Signed-off-by: Sascha Hauer --- drivers/mfd/da9063.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 5f77e5935b..ac737637a2 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -262,6 +262,9 @@ static int da9063_probe(struct device_d *dev) restart_handler_register(&priv->restart); + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) + return of_platform_populate(dev->device_node, NULL, dev); + return 0; on_error: -- cgit v1.2.3 From feee4b63804858c140c147f73625aa9b1ebc323f Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Wed, 11 Sep 2019 17:00:19 +0200 Subject: gpio: add DA9062 MFD gpio support The DA9062 PMIC is a mfd device which have 5 gpios. These can be configured to work as input/output or to have an alternate function. This commit adds the support to configure the gpios as input or output. Signed-off-by: Marco Felsch Signed-off-by: Sascha Hauer --- drivers/mfd/da9063.c | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index ac737637a2..b61e764876 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -15,8 +15,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -25,6 +27,7 @@ struct da9063 { struct restart_handler restart; struct watchdog wd; + struct gpio_chip gpio; struct i2c_client *client; /* dummy client for accessing bank #1 */ struct i2c_client *client1; @@ -61,6 +64,19 @@ struct da9063 { /* DA9063_REG_CONTROL_I (addr=0x10e) */ #define DA9062_WATCHDOG_SD BIT(3) +#define DA9062AA_STATUS_B 0x002 +#define DA9062AA_GPIO_0_1 0x015 +#define DA9062AA_GPIO_MODE0_4 0x01D + +/* DA9062AA_GPIO_0_1 (addr=0x015) */ +#define DA9062AA_GPIO0_PIN_MASK 0x03 + +#define DA9062_PIN_SHIFT(offset) (4 * (offset % 2)) +#define DA9062_PIN_ALTERNATE 0x00 /* gpio alternate mode */ +#define DA9062_PIN_GPI 0x01 /* gpio in */ +#define DA9062_PIN_GPO_OD 0x02 /* gpio out open-drain */ +#define DA9062_PIN_GPO_PP 0x03 /* gpio out push-pull */ + struct da906x_device_data { int (*init)(struct da9063 *priv); }; @@ -104,6 +120,118 @@ static int da906x_reg_update(struct da9063 *priv, unsigned int reg, return 0; } +static inline struct da9063 *to_da9063(struct gpio_chip *chip) +{ + return container_of(chip, struct da9063, gpio); +} + +static int da9063_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + u8 mask, mode; + + mode = DA9062_PIN_GPI << DA9062_PIN_SHIFT(offset); + mask = DA9062AA_GPIO0_PIN_MASK << DA9062_PIN_SHIFT(offset); + + return da906x_reg_update(priv, DA9062AA_GPIO_0_1 + (offset >> 1), + mask, mode); +} + +static int da9063_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct da9063 *priv = to_da9063(chip); + + return da906x_reg_update(priv, DA9062AA_GPIO_MODE0_4, BIT(offset), + value << offset); +} + +static int da9063_gpio_get_pin_mode(struct da9063 *priv, unsigned offset) +{ + int ret; + u8 val; + + ret = i2c_read_reg(priv->client, DA9062AA_GPIO_0_1 + (offset >> 1), + &val, 1); + if (ret < 0) + return ret; + + val >>= DA9062_PIN_SHIFT(offset); + val &= DA9062AA_GPIO0_PIN_MASK; + + return val; +} + +static int da9063_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + int gpio_dir; + int ret; + u8 val; + + gpio_dir = da9063_gpio_get_pin_mode(priv, offset); + if (gpio_dir < 0) + return gpio_dir; + + switch (gpio_dir) { + case DA9062_PIN_ALTERNATE: + return -ENOTSUPP; + case DA9062_PIN_GPI: + ret = i2c_read_reg(priv->client, DA9062AA_STATUS_B, &val, 1); + if (ret < 0) + return ret; + break; + case DA9062_PIN_GPO_OD: + /* falltrough */ + case DA9062_PIN_GPO_PP: + ret = i2c_read_reg(priv->client, DA9062AA_GPIO_MODE0_4, &val, 1); + if (ret < 0) + return ret; + } + + return val & BIT(offset); +} + +static int da9063_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +{ + struct da9063 *priv = to_da9063(chip); + int gpio_dir; + + gpio_dir = da9063_gpio_get_pin_mode(priv, offset); + if (gpio_dir < 0) + return gpio_dir; + + switch (gpio_dir) { + case DA9062_PIN_ALTERNATE: + return -ENOTSUPP; + case DA9062_PIN_GPI: + return 1; + case DA9062_PIN_GPO_OD: + /* falltrough */ + case DA9062_PIN_GPO_PP: + return 0; + } + + return -EINVAL; +} + +static void da9063_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct da9063 *priv = to_da9063(chip); + + da906x_reg_update(priv, DA9062AA_GPIO_MODE0_4, BIT(offset), + value << offset); + +} + +static struct gpio_ops da9063_gpio_ops = { + .direction_input = da9063_gpio_direction_input, + .direction_output = da9063_gpio_direction_output, + .get_direction = da9063_gpio_get_direction, + .get = da9063_gpio_get, + .set = da9063_gpio_set, +}; + static int da9063_watchdog_ping(struct da9063 *priv) { int ret; @@ -262,6 +390,14 @@ static int da9063_probe(struct device_d *dev) restart_handler_register(&priv->restart); + priv->gpio.base = -1; + priv->gpio.ngpio = 5; + priv->gpio.ops = &da9063_gpio_ops; + priv->gpio.dev = dev; + ret = gpiochip_add(&priv->gpio); + if (ret) + goto on_error; + if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) return of_platform_populate(dev->device_node, NULL, dev); -- cgit v1.2.3 From 6461b659717a6e5aa45f805c5de78babee674fa8 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sat, 12 Oct 2019 16:16:31 +0200 Subject: regulator: pfuze: remove unused driver_data We pass along driver_data that goes unused. Drop it. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/regulator/pfuze.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c index dc41e8f55b..eeb787cdab 100644 --- a/drivers/regulator/pfuze.c +++ b/drivers/regulator/pfuze.c @@ -161,16 +161,11 @@ static const struct regmap_config pfuze_regmap_i2c_config = { static int __init pfuze_probe(struct device_d *dev) { - struct pfuze_devtype *devtype; int ret; if (pfuze_dev) return -EBUSY; - ret = dev_get_drvdata(dev, (const void **)&devtype); - if (ret) - return ret; - pfuze_dev = xzalloc(sizeof(*pfuze_dev)); pfuze_dev->dev = dev; @@ -192,26 +187,17 @@ static int __init pfuze_probe(struct device_d *dev) return 0; } -static struct pfuze_devtype pfuze100_devtype = { -}; - -static struct pfuze_devtype pfuze200_devtype = { -}; - -static struct pfuze_devtype pfuze3000_devtype = { -}; - static struct platform_device_id pfuze_ids[] = { - { .name = "pfuze100", .driver_data = (ulong)&pfuze100_devtype, }, - { .name = "pfuze200", .driver_data = (ulong)&pfuze200_devtype, }, - { .name = "pfuze3000", .driver_data = (ulong)&pfuze3000_devtype, }, + { .name = "pfuze100" }, + { .name = "pfuze200" }, + { .name = "pfuze3000" }, { } }; static __maybe_unused struct of_device_id pfuze_dt_ids[] = { - { .compatible = "fsl,pfuze100", .data = &pfuze100_devtype, }, - { .compatible = "fsl,pfuze200", .data = &pfuze200_devtype, }, - { .compatible = "fsl,pfuze3000", .data = &pfuze3000_devtype, }, + { .compatible = "fsl,pfuze100" }, + { .compatible = "fsl,pfuze200" }, + { .compatible = "fsl,pfuze3000" }, { } }; -- cgit v1.2.3 From 6fa1b91f6e7596472c5197f6960e3e14b9c30795 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Sat, 12 Oct 2019 16:16:32 +0200 Subject: regulator: pfuze: add device_id of pfuze3001 This enables easy access to the pfuze3001's registers via regmap. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/regulator/pfuze.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c index eeb787cdab..1950ffb04c 100644 --- a/drivers/regulator/pfuze.c +++ b/drivers/regulator/pfuze.c @@ -191,6 +191,7 @@ static struct platform_device_id pfuze_ids[] = { { .name = "pfuze100" }, { .name = "pfuze200" }, { .name = "pfuze3000" }, + { .name = "pfuze3001" }, { } }; @@ -198,6 +199,7 @@ static __maybe_unused struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100" }, { .compatible = "fsl,pfuze200" }, { .compatible = "fsl,pfuze3000" }, + { .compatible = "fsl,pfuze3001" }, { } }; -- cgit v1.2.3 From efd517c35f122bb08ebc1fc29e741a5211553acc Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 11 Oct 2019 18:27:50 +0200 Subject: mfd: add basic Super I/O chip helpers Super I/O chips are ICs common to x86 that are used for interfacing to low-bandwidth peripherals. They often contain serial ports, watchdog timers and hardware monitoring units. They are usually addressable via one of two I/O port pairs, either 0x2e-0x2f or 0x4e-0x4f, but they don't typically respond to reads from their range unless a device-specific 'password' has been poked in. After this is done, they are read and written in the same manner however. On Linux, these devices aren't subject to any device/driver model. Each driver for some function (e.g. watchdog or GPIO) duplicates the device probe in the module_init and board-specific configuration is handled via module parameters. Lets do it a bit fancier in barebox and add a helper to register chips and a regmap for the control and configuration registers as well as a helper to register child devices for each function contained within the Super I/O chip. Board-specific configuration, e.g. which pin to use as a watchdog reset, can then be realized using barebox device-specific parameters. The regmap will be more of a debugging aid, however. For ease of porting from Linux, it's expected that access to the I/O ports won't happen via the regmap. For this reason, the new header offers functions to read/write these chips' registers as well. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/mfd/Kconfig | 3 ++ drivers/mfd/Makefile | 1 + drivers/mfd/superio.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/superio.h | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 drivers/mfd/superio.c create mode 100644 include/superio.h (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7d924cfca1..bd6f14a59f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -67,4 +67,7 @@ config MFD_STPMIC1 help Select this to support communication with the STPMIC1. +config MFD_SUPERIO + bool + endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 16a74abd77..690788eefb 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_MFD_TWL4030) += twl4030.o obj-$(CONFIG_MFD_TWL6030) += twl6030.o obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o +obj-$(CONFIG_MFD_SUPERIO) += superio.o diff --git a/drivers/mfd/superio.c b/drivers/mfd/superio.c new file mode 100644 index 0000000000..0f08d56cb3 --- /dev/null +++ b/drivers/mfd/superio.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "superio: " fmt + +#include +#include +#include + +struct device_d *superio_func_add(struct superio_chip *siochip, const char *name) +{ + struct device_d *dev; + int ret; + + dev = device_alloc(name, DEVICE_ID_DYNAMIC); + dev->parent = siochip->dev; + + + ret = platform_device_register(dev); + if (ret) + return NULL; + + return dev; +} +EXPORT_SYMBOL(superio_func_add) + +static int superio_reg_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct superio_chip *siochip = ctx; + + siochip->enter(siochip->sioaddr); + + *val = superio_inb(siochip->sioaddr, reg); + + siochip->exit(siochip->sioaddr); + + return 0; +} + +static int superio_reg_write(void *ctx, unsigned int reg, unsigned int val) +{ + struct superio_chip *siochip = ctx; + + siochip->enter(siochip->sioaddr); + + superio_outb(siochip->sioaddr, reg, val); + + siochip->exit(siochip->sioaddr); + + return 0; +} + +static struct regmap_bus superio_regmap_bus = { + .reg_write = superio_reg_write, + .reg_read = superio_reg_read, +}; + +static struct regmap_config superio_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .reg_stride = 1, + .max_register = 0xff, +}; + +void superio_chip_add(struct superio_chip *siochip) +{ + struct regmap *regmap; + char *chipname; + char str[5]; + int ret; + + chipname = xasprintf("superio-%04x:%04x@%02x", + siochip->vid, siochip->devid, siochip->sioaddr); + siochip->dev = add_generic_device(chipname, DEVICE_ID_SINGLE, NULL, + siochip->sioaddr, 2, IORESOURCE_IO, + NULL); + + siochip->dev->priv = siochip; + + sprintf(str, "%04x", siochip->vid); + dev_add_param_fixed(siochip->dev, "vendor", str); + sprintf(str, "%04x", siochip->devid); + dev_add_param_fixed(siochip->dev, "device", str); + + regmap = regmap_init(siochip->dev, &superio_regmap_bus, siochip, + &superio_regmap_config); + if (IS_ERR(regmap)) + pr_warn("creating %s regmap failed: %s\n", + chipname, strerror(-PTR_ERR(regmap))); + + ret = regmap_register_cdev(regmap, chipname); + if (ret) + pr_warn("registering %s regmap cdev failed: %s\n", + chipname, strerror(-ret)); +} +EXPORT_SYMBOL(superio_chip_add) diff --git a/include/superio.h b/include/superio.h new file mode 100644 index 0000000000..12bff58b6b --- /dev/null +++ b/include/superio.h @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#ifndef _SUPERIO_H_ +#define _SUPERIO_H_ + +#include +#include +#include +#include + +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_DEVREV 0x22 /* Device revision */ +#define SIO_REG_MANID 0x23 /* Vendor ID (2 bytes) */ + +static inline u8 superio_inb(u16 base, u8 reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static inline u16 superio_inw(u16 base, u8 reg) +{ + u16 val; + val = superio_inb(base, reg) << 8; + val |= superio_inb(base, reg + 1); + return val; +} + +static inline void superio_outb(u16 base, u8 reg, u8 val) +{ + outb(reg, base); + outb(val, base + 1); +} + +static inline void superio_set_bit(u16 base, u8 reg, unsigned bit) +{ + unsigned long val = superio_inb(base, reg); + __set_bit(bit, &val); + superio_outb(base, reg, val); +} + +static inline void superio_clear_bit(u16 base, u8 reg, unsigned bit) +{ + unsigned long val = superio_inb(base, reg); + __clear_bit(bit, &val); + superio_outb(base, reg, val); +} + +struct superio_chip { + struct device_d *dev; + u16 vid; + u16 devid; + u16 sioaddr; + void (*enter)(u16 sioaddr); + void (*exit)(u16 sioaddr); +}; + +void superio_chip_add(struct superio_chip *chip); +struct device_d *superio_func_add(struct superio_chip *chip, const char *name); + +#endif -- cgit v1.2.3 From ddb95cd90368f4d7ff11d6018cd0f24064e5bd28 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 11 Oct 2019 18:27:51 +0200 Subject: mfd: superio: add Fintek MFD driver Super I/O chips require a password to unlock access to the I/O ports. Add a driver that pokes the password and registers the appropriate GPIO and Watchdog devices as well as a regmap reflecting the Super I/O chip. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/mfd/Kconfig | 6 +++ drivers/mfd/Makefile | 1 + drivers/mfd/fintek-superio.c | 122 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 drivers/mfd/fintek-superio.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index bd6f14a59f..e2c74a575d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -70,4 +70,10 @@ config MFD_STPMIC1 config MFD_SUPERIO bool +config FINTEK_SUPERIO + bool "Fintek Super I/O chip" + select MFD_SUPERIO + help + Select this to probe for IO-port connected Fintek Super I/O chips. + endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 690788eefb..59b401dd2e 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_MFD_TWL6030) += twl6030.o obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o obj-$(CONFIG_MFD_SUPERIO) += superio.o +obj-$(CONFIG_FINTEK_SUPERIO) += fintek-superio.o diff --git a/drivers/mfd/fintek-superio.c b/drivers/mfd/fintek-superio.c new file mode 100644 index 0000000000..60785bce27 --- /dev/null +++ b/drivers/mfd/fintek-superio.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "fintek-superio: " fmt + +#include +#include +#include +#include + +#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ + +#define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ + +#define SIO_F71808_ID 0x0901 +#define SIO_F71858_ID 0x0507 +#define SIO_F71862_ID 0x0601 +#define SIO_F71868_ID 0x1106 +#define SIO_F71869_ID 0x0814 +#define SIO_F71869A_ID 0x1007 +#define SIO_F71882_ID 0x0541 +#define SIO_F71889_ID 0x0723 +#define SIO_F71889A_ID 0x1005 +#define SIO_F81865_ID 0x0704 +#define SIO_F81866_ID 0x1010 + +static void superio_enter(u16 sioaddr) +{ + /* according to the datasheet the key must be sent twice! */ + outb(SIO_UNLOCK_KEY, sioaddr); + outb(SIO_UNLOCK_KEY, sioaddr); +} + +static void superio_exit(u16 sioaddr) +{ + outb(SIO_LOCK_KEY, sioaddr); +} + +static void fintek_superio_find(u16 sioaddr) +{ + struct superio_chip *chip; + u16 vid; + + superio_enter(sioaddr); + + vid = superio_inw(sioaddr, SIO_REG_MANID); + if (vid != SIO_FINTEK_ID) { + pr_debug("Not a Fintek device (port=0x%02x, vid=0x%04x)\n", + sioaddr, vid); + return; + } + + chip = xzalloc(sizeof(*chip)); + + chip->devid = superio_inw(sioaddr, SIO_REG_DEVID); + chip->vid = vid; + chip->sioaddr = sioaddr; + chip->enter = superio_enter; + chip->exit = superio_exit; + + superio_chip_add(chip); + + switch (chip->devid) { + case SIO_F71808_ID: + superio_func_add(chip, "f71808fg_wdt"); + break; + case SIO_F71862_ID: + superio_func_add(chip, "f71862fg_wdt"); + break; + case SIO_F71868_ID: + superio_func_add(chip, "f71868_wdt"); + break; + case SIO_F71869_ID: + superio_func_add(chip, "f71869_wdt"); + superio_func_add(chip, "gpio-f71869"); + break; + case SIO_F71869A_ID: + superio_func_add(chip, "f71869_wdt"); + superio_func_add(chip, "gpio-f71869a"); + break; + case SIO_F71882_ID: + superio_func_add(chip, "f71882fg_wdt"); + superio_func_add(chip, "gpio-f71882fg"); + break; + case SIO_F71889_ID: + superio_func_add(chip, "f71889fg_wdt"); + superio_func_add(chip, "gpio-f71889f"); + break; + case SIO_F71889A_ID: + superio_func_add(chip, "f71889fg_wdt"); + superio_func_add(chip, "gpio-f71889a"); + break; + case SIO_F71858_ID: + /* Confirmed (by datasheet) not to have a watchdog. */ + break; + case SIO_F81865_ID: + superio_func_add(chip, "f81865_wdt"); + break; + case SIO_F81866_ID: + superio_func_add(chip, "f81866_wdt"); + superio_func_add(chip, "gpio-f81866"); + break; + default: + pr_info("Unrecognized Fintek device: 0x%04x\n", chip->devid); + } + + superio_exit(sioaddr); +} + +static int fintek_superio_detect(void) +{ + fintek_superio_find(0x2e); + fintek_superio_find(0x4e); + + return 0; +} +coredevice_initcall(fintek_superio_detect); -- cgit v1.2.3 From d1cd3da95e191a4303551f5dc8e508a472a99849 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 11 Oct 2019 18:27:52 +0200 Subject: watchdog: add support for Fintek F718xx and, F818xx Super I/O This is an adaptation of the Linux v5.3 f71808e_wdt driver for the watchdog component of the Fintek Super I/O chips. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/watchdog/Kconfig | 9 + drivers/watchdog/Makefile | 1 + drivers/watchdog/f71808e_wdt.c | 379 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 drivers/watchdog/f71808e_wdt.c (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fbaab896d4..45dd41a2a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -96,4 +96,13 @@ config STPMIC1_WATCHDOG help Enable to support configuration of the stpmic1's built-in watchdog. +config F71808E_WDT + bool "Fintek F718xx, F818xx Super I/O Watchdog" + depends on X86 + depends on FINTEK_SUPERIO + help + This is the driver for the hardware watchdog on the Fintek F71808E, + F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866 + Super I/O controllers. + endif diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 1fbd780885..63efc2a87e 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o +obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c new file mode 100644 index 0000000000..4f881a1d02 --- /dev/null +++ b/drivers/watchdog/f71808e_wdt.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/*************************************************************************** + * Copyright (C) 2006 by Hans Edgington * + * Copyright (C) 2007-2009 Hans de Goede * + * Copyright (C) 2010 Giel van Schijndel * + * Copyright (C) 2019 Ahmad Fatoum * + * * + ***************************************************************************/ + +#define pr_fmt(fmt) "f71808e_wdt: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical 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_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVREV 0x22 /* Device revision */ +#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */ +#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */ +#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */ +#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */ +#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */ +#define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define F71808FG_REG_WDO_CONF 0xf0 +#define F71808FG_REG_WDT_CONF 0xf5 +#define F71808FG_REG_WD_TIME 0xf6 + +#define F71808FG_FLAG_WDOUT_EN 7 + +#define F71808FG_FLAG_WDTMOUT_STS 6 +#define F71808FG_FLAG_WD_EN 5 +#define F71808FG_FLAG_WD_PULSE 4 +#define F71808FG_FLAG_WD_UNIT 3 + +#define F81865_REG_WDO_CONF 0xfa +#define F81865_FLAG_WDOUT_EN 0 + +/* Default values */ +#define WATCHDOG_MAX_TIMEOUT (60 * 255) + +enum pulse_width { + PULSE_WIDTH_LEVEL, PULSE_WIDTH_1MS, + PULSE_WIDTH_LOW, PULSE_WIDTH_MID, PULSE_WIDTH_HIGH +}; + +const char *pulse_width_names[] = { "level", "1", "25", "125", "5000" }; +const char *pulse_width_names_f71868[] = { "level", "1", "30", "150", "6000" }; + +enum wdtrst_pin { + WDTRST_PIN_56, WDTRST_PIN_63, +}; + +const char *f71862fg_pin_names[] = { "56", "63" }; + +enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg, + f81865, f81866}; + +struct f71808e_wdt; + +struct f71808e_variant_data { + enum chips type; + void (*pinconf)(struct f71808e_wdt *wd); +}; + +struct f71808e_wdt { + struct watchdog wdd; + u16 sioaddr; + const struct f71808e_variant_data *variant; + unsigned int timeout; + u8 timer_val; /* content for the wd_time register */ + char minutes_mode; + int pulse_width; + int f71862fg_pin; +}; + +static inline struct f71808e_wdt *to_f71808e_wdt(struct watchdog *wdd) +{ + return container_of(wdd, struct f71808e_wdt, wdd); +} + +static inline bool has_f81865_wdo_conf(struct f71808e_wdt *wd) +{ + return wd->variant->type == f81865 || wd->variant->type == f81866; +} + +static inline void superio_enter(u16 base) +{ + /* according to the datasheet the key must be sent twice! */ + outb(SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); +} + +static inline void superio_select(u16 base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(u16 base) +{ + outb(SIO_LOCK_KEY, base); +} + +static void f71808e_wdt_keepalive(struct f71808e_wdt *wd) +{ + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + if (wd->minutes_mode) + /* select minutes for timer units */ + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_UNIT); + else + /* select seconds for timer units */ + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_UNIT); + + /* Set timer value */ + superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME, + wd->timer_val); + + superio_exit(wd->sioaddr); +} + +static void f71808e_wdt_start(struct f71808e_wdt *wd) +{ + /* Make sure we don't die as soon as the watchdog is enabled below */ + f71808e_wdt_keepalive(wd); + + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + /* Watchdog pin configuration */ + wd->variant->pinconf(wd); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0); + + if (has_f81865_wdo_conf(wd)) + superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF, + F81865_FLAG_WDOUT_EN); + else + superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF, + F71808FG_FLAG_WDOUT_EN); + + superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_EN); + + if (wd->pulse_width > 0) { + /* Select "pulse" output mode with given duration */ + u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); + + /* Set WD_PSWIDTH bits (1:0) */ + wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_width & 0x03); + /* Set WD_PULSE to "pulse" mode */ + wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); + + superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf); + } else { + /* Select "level" output mode */ + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_PULSE); + } + + superio_exit(wd->sioaddr); +} + +static void f71808e_wdt_stop(struct f71808e_wdt *wd) +{ + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF, + F71808FG_FLAG_WD_EN); + + superio_exit(wd->sioaddr); +} + +static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int new_timeout) +{ + struct f71808e_wdt *wd = to_f71808e_wdt(wdd); + + if (!new_timeout) { + f71808e_wdt_stop(wd); + return 0; + } + + if (wd->timeout != new_timeout) { + if (new_timeout > 0xff) { + wd->timer_val = DIV_ROUND_UP(new_timeout, 60); + wd->minutes_mode = true; + } else { + wd->timer_val = new_timeout; + wd->minutes_mode = false; + } + + f71808e_wdt_start(wd); + wd->timeout = new_timeout; + } + + f71808e_wdt_keepalive(wd); + return 0; +} + +static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev) +{ + struct watchdog *wdd = &wd->wdd; + const char * const *names = pulse_width_names; + int wdt_conf; + int ret; + + superio_enter(wd->sioaddr); + + superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT); + + wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF); + + superio_exit(wd->sioaddr); + + if (wd->variant->type == f71868) + names = pulse_width_names_f71868; + + wd->pulse_width = PULSE_WIDTH_MID; /* either 125ms or 150ms */ + + dev_add_param_enum(dev, "pulse_width_ms", NULL, NULL, + &wd->pulse_width, names, + ARRAY_SIZE(pulse_width_names), + wd); + + if (wd->variant->type == f71862fg) { + wd->f71862fg_pin = WDTRST_PIN_63; + + dev_add_param_enum(dev, "wdtrst_pin", NULL, NULL, + &wd->f71862fg_pin, f71862fg_pin_names, + ARRAY_SIZE(f71862fg_pin_names), + wd); + } + + wdd->hwdev = dev; + wdd->set_timeout = &f71808e_wdt_set_timeout; + wdd->timeout_max = WATCHDOG_MAX_TIMEOUT; + + if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS)) + reset_source_set_priority(RESET_WDG, + RESET_SOURCE_DEFAULT_PRIORITY); + + dev_info(dev, "reset reason: %s\n", reset_source_name()); + + ret = watchdog_register(wdd); + if (ret) + return ret; + + superio_enter(wd->sioaddr); + dev_info(dev, "revision %d probed.\n", + superio_inb(wd->sioaddr, SIO_REG_DEVREV)); + superio_exit(wd->sioaddr); + + return 0; +} + +static void f71808fg_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3); + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3); +} +static void f71862fg_pinconf(struct f71808e_wdt *wd) +{ + u16 ioaddr = wd->sioaddr; + + if (wd->f71862fg_pin == WDTRST_PIN_63) { + /* SPI must be disabled first to use this pin! */ + superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6); + superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4); + } else if (wd->f71862fg_pin == WDTRST_PIN_56) { + superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1); + } +} +static void f71868_pinconf(struct f71808e_wdt *wd) +{ + /* GPIO14 --> WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4); +} +static void f71882fg_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 56 to WDTRST# */ + superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1); +} +static void f71889fg_pinconf(struct f71808e_wdt *wd) +{ + /* set pin 40 to WDTRST# */ + superio_outb(wd->sioaddr, SIO_REG_MFUNCT3, + superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf); +} +static void f81865_pinconf(struct f71808e_wdt *wd) +{ + /* Set pin 70 to WDTRST# */ + superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5); +} +static void f81866_pinconf(struct f71808e_wdt *wd) +{ + /* + * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0. + * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch: + * BIT5: 0 -> WDTRST# + * 1 -> GPIO15 + */ + u8 tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL); + tmp &= ~(BIT(3) | BIT(0)); + tmp |= BIT(2); + superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp); + + superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5); +} + +static struct f71808e_variant_data f71808fg_data = { .type = f71808fg, .pinconf = f71808fg_pinconf }; +static struct f71808e_variant_data f71862fg_data = { .type = f71862fg, .pinconf = f71862fg_pinconf }; +static struct f71808e_variant_data f71868_data = { .type = f71868, .pinconf = f71868_pinconf }; +static struct f71808e_variant_data f71869_data = { .type = f71869, .pinconf = f71868_pinconf }; +static struct f71808e_variant_data f71882fg_data = { .type = f71882fg, .pinconf = f71882fg_pinconf }; +static struct f71808e_variant_data f71889fg_data = { .type = f71889fg, .pinconf = f71889fg_pinconf }; +static struct f71808e_variant_data f81865_data = { .type = f81865, .pinconf = f81865_pinconf }; +static struct f71808e_variant_data f81866_data = { .type = f81866, .pinconf = f81866_pinconf }; + +static struct platform_device_id f71808e_wdt_ids[] = { + { .name = "f71808fg_wdt", .driver_data = (unsigned long)&f71808fg_data }, + { .name = "f71862fg_wdt", .driver_data = (unsigned long)&f71862fg_data }, + { .name = "f71868_wdt", .driver_data = (unsigned long)&f71868_data }, + { .name = "f71869_wdt", .driver_data = (unsigned long)&f71869_data }, + { .name = "f71882fg_wdt", .driver_data = (unsigned long)&f71882fg_data }, + { .name = "f71889fg_wdt", .driver_data = (unsigned long)&f71889fg_data }, + { .name = "f81865_wdt", .driver_data = (unsigned long)&f81865_data }, + { .name = "f81866_wdt", .driver_data = (unsigned long)&f81866_data }, + { /* sentinel */ }, +}; + +static int f71808e_probe(struct device_d *dev) +{ + struct f71808e_wdt *wd; + struct resource *res; + int ret; + + wd = xzalloc(sizeof(*wd)); + + ret = dev_get_drvdata(dev, (const void **)&wd->variant); + if (ret) + return ret; + + res = dev_get_resource(dev->parent, IORESOURCE_IO, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + wd->sioaddr = res->start; + + return f71808e_wdt_init(wd, dev); +} + +static struct driver_d f71808e_wdt_driver = { + .probe = f71808e_probe, + .name = "f71808e_wdt", + .id_table = f71808e_wdt_ids, +}; + +device_platform_driver(f71808e_wdt_driver); -- cgit v1.2.3 From eaf020f1bb5204aecc5c820f8dd020369f32b383 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Fri, 11 Oct 2019 18:27:53 +0200 Subject: mfd: superio: add base SMSC MFD driver The SMSC FDC37C93xAPM is the Super I/O chip on the Dell Latitude 7490. This adds device detection for it and its siblings, so device drivers can be written against it or init scripts can use its regmap interface. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer --- drivers/mfd/Kconfig | 6 +++ drivers/mfd/Makefile | 1 + drivers/mfd/smsc-superio.c | 115 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 drivers/mfd/smsc-superio.c (limited to 'drivers') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index e2c74a575d..f4cc71ef0e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -76,4 +76,10 @@ config FINTEK_SUPERIO help Select this to probe for IO-port connected Fintek Super I/O chips. +config SMSC_SUPERIO + bool "SMSC Super I/O chip" + select MFD_SUPERIO + help + Select this to probe for IO-port connected SMSC Super I/O chips. + endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 59b401dd2e..0c24493e3d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_RAVE_SP_CORE) += rave-sp.o obj-$(CONFIG_MFD_STPMIC1) += stpmic1.o obj-$(CONFIG_MFD_SUPERIO) += superio.o obj-$(CONFIG_FINTEK_SUPERIO) += fintek-superio.o +obj-$(CONFIG_SMSC_SUPERIO) += smsc-superio.o diff --git a/drivers/mfd/smsc-superio.c b/drivers/mfd/smsc-superio.c new file mode 100644 index 0000000000..349c878cef --- /dev/null +++ b/drivers/mfd/smsc-superio.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Ahmad Fatoum, Pengutronix + */ + +#define pr_fmt(fmt) "smsc-superio: " fmt + +#include +#include +#include +#include + +#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */ +#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */ + +#define SMSC_ID 0x10b8 /* Standard Microsystems Corp PCI ID */ + +static void superio_enter(u16 sioaddr) +{ + outb(SIO_UNLOCK_KEY, sioaddr); + mdelay(1); + outb(SIO_UNLOCK_KEY, sioaddr); +} + +static void superio_exit(u16 sioaddr) +{ + outb(SIO_LOCK_KEY, sioaddr); +} + +static void smsc_superio_find(u16 sioaddr, u16 id_reg) +{ + struct superio_chip *chip; + u16 devid; + + superio_enter(sioaddr); + + devid = superio_inw(sioaddr, id_reg); + switch(devid >> 8) { + case 0x02: + case 0x03: + case 0x07: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0e: + case 0x14: + case 0x30: + case 0x40: + case 0x42: + case 0x43: + case 0x44: + case 0x46: + case 0x47: + case 0x4c: + case 0x4d: + case 0x51: + case 0x52: + case 0x54: + case 0x56: + case 0x57: + case 0x59: + case 0x5d: + case 0x5f: + case 0x60: + case 0x62: + case 0x67: + case 0x6b: + case 0x6e: + case 0x6f: + case 0x74: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7c: + case 0x7d: + case 0x7f: + case 0x81: + case 0x83: + case 0x85: + case 0x86: + case 0x89: + case 0x8c: + case 0x90: + break; + default: + pr_debug("Not a SMSC device (port=0x%02x, devid=0x%04x)\n", + sioaddr, devid); + return; + } + + chip = xzalloc(sizeof(*chip)); + + chip->devid = devid; + chip->vid = SMSC_ID; + chip->sioaddr = sioaddr; + chip->enter = superio_enter; + chip->exit = superio_exit; + + superio_chip_add(chip); + + superio_exit(sioaddr); +} + +static int smsc_superio_detect(void) +{ + u16 ports[] = { 0x2e, 0x4e, 0x162e, 0x164e, 0x3f0, 0x370 }; + + for (int i = 0; i < ARRAY_SIZE(ports); i++) + smsc_superio_find(ports[i], SIO_REG_DEVID); + + return 0; +} +coredevice_initcall(smsc_superio_detect); -- cgit v1.2.3