diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2019-10-17 08:10:23 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2019-10-17 08:10:23 +0200 |
commit | ee6d4a74eb6efa643e7b834b32e04d01c6b29b7f (patch) | |
tree | 2ca84274dfe2e54729bca7dc4bbfe42611a1ee7f /drivers | |
parent | c59d7ab7317014cc14a98c47e91c2b582d5d08a7 (diff) | |
parent | 099b135ac30013dfc4b3310a5177cf2f7a17f3c3 (diff) | |
download | barebox-ee6d4a74eb6efa643e7b834b32e04d01c6b29b7f.tar.gz barebox-ee6d4a74eb6efa643e7b834b32e04d01c6b29b7f.tar.xz |
Merge branch 'for-next/misc'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/regmap/regmap.c | 4 | ||||
-rw-r--r-- | drivers/input/input.c | 2 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 15 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 3 | ||||
-rw-r--r-- | drivers/mfd/da9063.c | 139 | ||||
-rw-r--r-- | drivers/mfd/fintek-superio.c | 122 | ||||
-rw-r--r-- | drivers/mfd/smsc-superio.c | 115 | ||||
-rw-r--r-- | drivers/mfd/superio.c | 98 | ||||
-rw-r--r-- | drivers/mtd/ubi/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 2 | ||||
-rw-r--r-- | drivers/mtd/ubi/ubi.h | 11 | ||||
-rw-r--r-- | drivers/mtd/ubi/wl.c | 8 | ||||
-rw-r--r-- | drivers/net/ag71xx.c | 2 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/chip.c | 36 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/chip.h | 1 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/global1.c | 51 | ||||
-rw-r--r-- | drivers/net/phy/mv88e6xxx/global1.h | 37 | ||||
-rw-r--r-- | drivers/nvmem/core.c | 14 | ||||
-rw-r--r-- | drivers/regulator/pfuze.c | 28 | ||||
-rw-r--r-- | drivers/usb/gadget/f_acm.c | 4 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 9 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/f71808e_wdt.c | 379 |
24 files changed, 1045 insertions, 54 deletions
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 +} 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; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7d924cfca1..f4cc71ef0e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -67,4 +67,19 @@ config MFD_STPMIC1 help Select this to support communication with the 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. + +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 16a74abd77..0c24493e3d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,3 +12,6 @@ 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 +obj-$(CONFIG_FINTEK_SUPERIO) += fintek-superio.o +obj-$(CONFIG_SMSC_SUPERIO) += smsc-superio.o diff --git a/drivers/mfd/da9063.c b/drivers/mfd/da9063.c index 5f77e5935b..b61e764876 100644 --- a/drivers/mfd/da9063.c +++ b/drivers/mfd/da9063.c @@ -15,8 +15,10 @@ #include <common.h> #include <driver.h> +#include <gpio.h> #include <restart.h> #include <i2c/i2c.h> +#include <linux/bitops.h> #include <malloc.h> #include <notifier.h> #include <reset_source.h> @@ -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,17 @@ 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); + return 0; on_error: 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 <superio.h> +#include <init.h> +#include <asm/io.h> +#include <common.h> + +#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); 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 <superio.h> +#include <init.h> +#include <asm/io.h> +#include <common.h> + +#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); 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 <common.h> +#include <superio.h> +#include <regmap.h> + +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/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 @@ -101,14 +101,6 @@ #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 * one with the highest erase counter. But in certain workloads this could lead 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 */ } }; 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 9688dbd1be..b1bffe5cbc 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -11,6 +11,7 @@ #include <of_gpio.h> #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); } } @@ -797,7 +826,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; } @@ -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 <vivien.didelot@savoirfairelinux.com> + */ + +#include <clock.h> +#include <linux/bitfield.h> + +#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 <vivien.didelot@savoirfairelinux.com> + */ + +#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 */ 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); diff --git a/drivers/regulator/pfuze.c b/drivers/regulator/pfuze.c index dc41e8f55b..1950ffb04c 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,19 @@ 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" }, + { .name = "pfuze3001" }, { } }; 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" }, + { .compatible = "fsl,pfuze3001" }, { } }; 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 */ }; 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 <hans@edgington.nl> * + * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> * + * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> * + * Copyright (C) 2019 Ahmad Fatoum <a.fatoum@pengutronix.de> * + * * + ***************************************************************************/ + +#define pr_fmt(fmt) "f71808e_wdt: " fmt + +#include <init.h> +#include <asm/io.h> +#include <linux/bitops.h> +#include <driver.h> +#include <watchdog.h> +#include <printk.h> +#include <reset_source.h> +#include <superio.h> +#include <common.h> + +#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); |