summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-10-17 08:10:23 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2019-10-17 08:10:23 +0200
commitee6d4a74eb6efa643e7b834b32e04d01c6b29b7f (patch)
tree2ca84274dfe2e54729bca7dc4bbfe42611a1ee7f /drivers
parentc59d7ab7317014cc14a98c47e91c2b582d5d08a7 (diff)
parent099b135ac30013dfc4b3310a5177cf2f7a17f3c3 (diff)
downloadbarebox-ee6d4a74eb6efa643e7b834b32e04d01c6b29b7f.tar.gz
barebox-ee6d4a74eb6efa643e7b834b32e04d01c6b29b7f.tar.xz
Merge branch 'for-next/misc'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/regmap/regmap.c4
-rw-r--r--drivers/input/input.c2
-rw-r--r--drivers/mfd/Kconfig15
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/da9063.c139
-rw-r--r--drivers/mfd/fintek-superio.c122
-rw-r--r--drivers/mfd/smsc-superio.c115
-rw-r--r--drivers/mfd/superio.c98
-rw-r--r--drivers/mtd/ubi/Kconfig17
-rw-r--r--drivers/mtd/ubi/build.c2
-rw-r--r--drivers/mtd/ubi/ubi.h11
-rw-r--r--drivers/mtd/ubi/wl.c8
-rw-r--r--drivers/net/ag71xx.c2
-rw-r--r--drivers/net/phy/mv88e6xxx/Makefile1
-rw-r--r--drivers/net/phy/mv88e6xxx/chip.c36
-rw-r--r--drivers/net/phy/mv88e6xxx/chip.h1
-rw-r--r--drivers/net/phy/mv88e6xxx/global1.c51
-rw-r--r--drivers/net/phy/mv88e6xxx/global1.h37
-rw-r--r--drivers/nvmem/core.c14
-rw-r--r--drivers/regulator/pfuze.c28
-rw-r--r--drivers/usb/gadget/f_acm.c4
-rw-r--r--drivers/watchdog/Kconfig9
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/f71808e_wdt.c379
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", &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);