diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/sata_mv.c | 111 | ||||
-rw-r--r-- | drivers/base/driver.c | 30 | ||||
-rw-r--r-- | drivers/block/efi-block-io.c | 42 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk-rpi.c | 85 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6.c | 4 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx8mp.c | 1 | ||||
-rw-r--r-- | drivers/firmware/zynqmp-fpga.c | 13 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/rn5t568.c | 165 | ||||
-rw-r--r-- | drivers/misc/Kconfig | 23 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/storage-by-uuid.c | 199 | ||||
-rw-r--r-- | drivers/mtd/devices/mtdram.c | 35 | ||||
-rw-r--r-- | drivers/of/base.c | 2 | ||||
-rw-r--r-- | drivers/of/of_path.c | 2 | ||||
-rw-r--r-- | drivers/of/platform.c | 3 | ||||
-rw-r--r-- | drivers/power/reset/Kconfig | 3 | ||||
-rw-r--r-- | drivers/serial/serial_cadence.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 1 | ||||
-rw-r--r-- | drivers/watchdog/Kconfig | 6 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/rn5t568_wdt.c | 146 |
24 files changed, 834 insertions, 53 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 3b55c71d67..05b27f1008 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -33,15 +33,22 @@ static void ata_ioports_init(struct ata_ioports *io, /* io->alt_dev_addr is unused */ } -#define REG_WINDOW_CONTROL(n) ((n) * 0x10 + 0x30) -#define REG_WINDOW_BASE(n) ((n) * 0x10 + 0x34) +#define REG_WINDOW_CONTROL(n) ((n) * 0x10 + 0x30) +#define REG_WINDOW_BASE(n) ((n) * 0x10 + 0x34) -#define REG_EDMA_COMMAND(n) ((n) * 0x2000 + 0x2028) +#define REG_EDMA_COMMAND(n) ((n) * 0x2000 + 0x2028) +#define EDMA_EN (1 << 0) /* enable EDMA */ +#define EDMA_DS (1 << 1) /* disable EDMA; self-negated */ #define REG_EDMA_COMMAND__EATARST 0x00000004 - -#define REG_ATA_BASE 0x2100 -#define REG_SSTATUS(n) ((n) * 0x2000 + 0x2300) -#define REG_SCONTROL(n) ((n) * 0x2000 + 0x2308) +#define REG_EDMA_IORDY_TMOUT(n) ((n) * 0x2000 + 0x2034) +#define REG_SATA_IFCFG(n) ((n) * 0x2000 + 0x2050) +#define REG_SATA_IFCFG_GEN2EN (1 << 7) + +#define REG_ATA_BASE 0x2100 +#define REG_SSTATUS(n) ((n) * 0x2000 + 0x2300) +#define REG_SERROR(n) ((n) * 0x2000 + 0x2304) +#define REG_SERROR_MASK 0x03fe0000 +#define REG_SCONTROL(n) ((n) * 0x2000 + 0x2308) #define REG_SCONTROL__DET 0x0000000f #define REG_SCONTROL__DET__INIT 0x00000001 #define REG_SCONTROL__DET__PHYOK 0x00000002 @@ -49,13 +56,49 @@ static void ata_ioports_init(struct ata_ioports *io, #define REG_SCONTROL__IPM__PARTIAL 0x00000100 #define REG_SCONTROL__IPM__SLUMBER 0x00000200 +#define PHY_MODE3 0x310 +#define PHY_MODE4 0x314 /* requires read-after-write */ +#define PHY_MODE9_GEN2 0x398 +#define PHY_MODE9_GEN1 0x39c + +static void mv_soc_65n_phy_errata(void __iomem *base) +{ + u32 reg; + + reg = readl(base + PHY_MODE3); + reg &= ~(0x3 << 27); /* SELMUPF (bits 28:27) to 1 */ + reg |= (0x1 << 27); + reg &= ~(0x3 << 29); /* SELMUPI (bits 30:29) to 1 */ + reg |= (0x1 << 29); + writel(reg, base + PHY_MODE3); + + reg = readl(base + PHY_MODE4); + reg &= ~0x1; /* SATU_OD8 (bit 0) to 0, reserved bit 16 must be set */ + reg |= (0x1 << 16); + writel(reg, base + PHY_MODE4); + + reg = readl(base + PHY_MODE9_GEN2); + reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */ + reg |= 0x8; + reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */ + writel(reg, base + PHY_MODE9_GEN2); + + reg = readl(base + PHY_MODE9_GEN1); + reg &= ~0xf; /* TXAMP[3:0] (bits 3:0) to 8 */ + reg |= 0x8; + reg &= ~(0x1 << 14); /* TXAMP[4] (bit 14) to 0 */ + writel(reg, base + PHY_MODE9_GEN1); +} + static int mv_sata_probe(struct device_d *dev) { struct resource *iores; void __iomem *base; struct ide_port *ide; + u32 try_again = 0; u32 scontrol; int ret, i; + u32 tmp; iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) { @@ -74,6 +117,31 @@ static int mv_sata_probe(struct device_d *dev) writel(0x7fff0e01, base + REG_WINDOW_CONTROL(0)); writel(0, base + REG_WINDOW_BASE(0)); +again: + /* Clear SError */ + writel(0x0, base + REG_SERROR(0)); + /* disable EDMA */ + writel(EDMA_DS, base + REG_EDMA_COMMAND(0)); + /* Wait for the chip to confirm eDMA is off. */ + ret = wait_on_timeout(10 * MSECOND, + (readl(base + REG_EDMA_COMMAND(0)) & EDMA_EN) == 0); + if (ret) { + dev_err(dev, "Failed to wait for eDMA off (sstatus=0x%08x)\n", + readl(base + REG_SSTATUS(0))); + return ret; + } + + /* increase IORdy signal timeout */ + writel(0x800, base + REG_EDMA_IORDY_TMOUT(0)); + /* set GEN2i Speed */ + tmp = readl(base + REG_SATA_IFCFG(0)); + tmp |= REG_SATA_IFCFG_GEN2EN; + writel(tmp, base + REG_SATA_IFCFG(0)); + + mv_soc_65n_phy_errata(base); + + /* strobe for hard-reset */ + writel(REG_EDMA_COMMAND__EATARST, base + REG_EDMA_COMMAND(0)); writel(REG_EDMA_COMMAND__EATARST, base + REG_EDMA_COMMAND(0)); udelay(25); writel(0x0, base + REG_EDMA_COMMAND(0)); @@ -104,10 +172,39 @@ static int mv_sata_probe(struct device_d *dev) dev->priv = ide; + /* enable EDMA */ + writel(EDMA_EN, base + REG_EDMA_COMMAND(0)); + ret = ide_port_register(ide); if (ret) free(ide); + /* + * Under most conditions the above is enough and works as expected. + * With some specific hardware combinations, the setup fails however + * leading to an unusable SATA drive. From the error status bits it + * was not obvious what exactly went wrong. + * The ARMADA-XP datasheet advices to hard-reset the SATA core and + * drive and try again. + * When this happens, just try again multiple times, to give the drive + * some time to reach a stable state. If after 5 (randomly chosen) tries, + * the drive still doesn't work, just give up on it. + */ + tmp = readl(base + REG_SERROR(0)); + if (tmp & REG_SERROR_MASK) { + try_again++; + if (try_again > 5) + return -ENODEV; + dev_dbg(dev, "PHY layer error. Try again. (serror=0x%08x)\n", tmp); + if (ide->port.initialized) { + blockdevice_unregister(&ide->port.blk); + unregister_device(&ide->port.class_dev); + } + + mdelay(100); + goto again; + } + return ret; } diff --git a/drivers/base/driver.c b/drivers/base/driver.c index f54f4d0b37..2347b5c71f 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -270,6 +270,36 @@ int unregister_device(struct device_d *old_dev) } EXPORT_SYMBOL(unregister_device); +/** + * free_device_res - free dynamically allocated device members + * @dev: The device + * + * This frees dynamically allocated resources allocated during device + * lifetime, but not the device itself. + */ +void free_device_res(struct device_d *dev) +{ + free(dev->name); + dev->name = NULL; + free(dev->unique_name); + dev->unique_name = NULL; +} +EXPORT_SYMBOL(free_device_res); + +/** + * free_device - free a device + * @dev: The device + * + * This frees dynamically allocated resources allocated during device + * lifetime and finally the device itself. + */ +void free_device(struct device_d *dev) +{ + free_device_res(dev); + free(dev); +} +EXPORT_SYMBOL(free_device); + /* * Loop over list of deferred devices as long as at least one * device is successfully probed. Devices that again request diff --git a/drivers/block/efi-block-io.c b/drivers/block/efi-block-io.c index ff0e467d2c..086afb378a 100644 --- a/drivers/block/efi-block-io.c +++ b/drivers/block/efi-block-io.c @@ -52,6 +52,7 @@ struct efi_bio_priv { struct device_d *dev; struct block_device blk; u32 media_id; + void (*efi_info)(struct device_d *); }; static int efi_bio_read(struct block_device *blk, void *buffer, sector_t block, @@ -101,35 +102,40 @@ static struct block_device_ops efi_bio_ops = { .flush = efi_bio_flush, }; -static void efi_bio_print_info(struct efi_bio_priv *priv) +static void efi_bio_print_info(struct device_d *dev) { + struct efi_bio_priv *priv = dev->priv; struct efi_block_io_media *media = priv->protocol->media; u64 revision = priv->protocol->revision; - dev_dbg(priv->dev, "revision: 0x%016llx\n", revision); - dev_dbg(priv->dev, "media_id: 0x%08x\n", media->media_id); - dev_dbg(priv->dev, "removable_media: %d\n", media->removable_media); - dev_dbg(priv->dev, "media_present: %d\n", media->media_present); - dev_dbg(priv->dev, "logical_partition: %d\n", media->logical_partition); - dev_dbg(priv->dev, "read_only: %d\n", media->read_only); - dev_dbg(priv->dev, "write_caching: %d\n", media->write_caching); - dev_dbg(priv->dev, "block_size: 0x%08x\n", media->block_size); - dev_dbg(priv->dev, "io_align: 0x%08x\n", media->io_align); - dev_dbg(priv->dev, "last_block: 0x%016llx\n", media->last_block); + printf("Block I/O Media:\n"); + printf(" revision: 0x%016llx\n", revision); + printf(" media_id: 0x%08x\n", media->media_id); + printf(" removable_media: %d\n", media->removable_media); + printf(" media_present: %d\n", media->media_present); + printf(" logical_partition: %d\n", media->logical_partition); + printf(" read_only: %d\n", media->read_only); + printf(" write_caching: %d\n", media->write_caching); + printf(" block_size: 0x%08x\n", media->block_size); + printf(" io_align: 0x%08x\n", media->io_align); + printf(" last_block: 0x%016llx\n", media->last_block); if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION2) return; - dev_dbg(priv->dev, "u64 lowest_aligned_lba: 0x%08llx\n", + printf(" lowest_aligned_lba: 0x%08llx\n", media->lowest_aligned_lba); - dev_dbg(priv->dev, "logical_blocks_per_physical_block: 0x%08x\n", + printf(" logical_blocks_per_physical_block: 0x%08x\n", media->logical_blocks_per_physical_block); if (revision < EFI_BLOCK_IO_PROTOCOL_REVISION3) return; - dev_dbg(priv->dev, "optimal_transfer_length_granularity: 0x%08x\n", + printf(" optimal_transfer_length_granularity: 0x%08x\n", media->optimal_transfer_length_granularity); + + if (priv->efi_info) + priv->efi_info(dev); } static bool is_bio_usbdev(struct efi_device *efidev) @@ -143,6 +149,7 @@ static int efi_bio_probe(struct efi_device *efidev) int instance; struct efi_bio_priv *priv; struct efi_block_io_media *media; + struct device_d *dev = &efidev->dev; priv = xzalloc(sizeof(*priv)); @@ -151,8 +158,13 @@ static int efi_bio_probe(struct efi_device *efidev) if (!priv->protocol) return -ENODEV; + dev->priv = priv; + priv->efi_info = dev->info; + dev->info = efi_bio_print_info; + media = priv->protocol->media; - efi_bio_print_info(priv); + if (__is_defined(DEBUG)) + efi_bio_print_info(dev); priv->dev = &efidev->dev; if (is_bio_usbdev(efidev)) { diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index bab0498d7c..c60b38ff60 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -25,3 +25,4 @@ obj-y += analogbits/ obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_SOC_STARFIVE) += starfive/ obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o +obj-$(CONFIG_MACH_RPI_COMMON) += clk-rpi.o diff --git a/drivers/clk/clk-rpi.c b/drivers/clk/clk-rpi.c new file mode 100644 index 0000000000..59ae8e59ba --- /dev/null +++ b/drivers/clk/clk-rpi.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <linux/clk.h> +#include <io.h> +#include <linux/clkdev.h> +#include <linux/err.h> + +#include <mach/core.h> +#include <mach/mbox.h> +#include <mach/platform.h> +#include <dt-bindings/clock/bcm2835.h> + +#define BCM2711_CLOCK_END (BCM2711_CLOCK_EMMC2 + 1) + +static struct clk *clks[BCM2711_CLOCK_END]; +static struct clk_onecell_data clk_data; + +struct msg_get_clock_rate { + struct bcm2835_mbox_hdr hdr; + struct bcm2835_mbox_tag_get_clock_rate get_clock_rate; + u32 end_tag; +}; + +static struct clk *rpi_register_firmware_clock(u32 clock_id, const char *name) +{ + BCM2835_MBOX_STACK_ALIGN(struct msg_get_clock_rate, msg); + int ret; + + BCM2835_MBOX_INIT_HDR(msg); + BCM2835_MBOX_INIT_TAG(&msg->get_clock_rate, GET_CLOCK_RATE); + msg->get_clock_rate.body.req.clock_id = clock_id; + + ret = bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN, &msg->hdr); + if (ret) + return ERR_PTR(ret); + + return clk_fixed(name, msg->get_clock_rate.body.resp.rate_hz); +} + +static int bcm2835_cprman_probe(struct device_d *dev) +{ + struct clk *clk_cs; + + clks[BCM2835_CLOCK_EMMC] = + rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_EMMC, + "bcm2835_mci0"); + if (IS_ERR(clks[BCM2835_CLOCK_EMMC])) + return PTR_ERR(clks[BCM2835_CLOCK_EMMC]); + + clks[BCM2835_CLOCK_VPU] = + rpi_register_firmware_clock(BCM2835_MBOX_CLOCK_ID_CORE, + "vpu"); + if (IS_ERR(clks[BCM2835_CLOCK_VPU])) + return PTR_ERR(clks[BCM2835_CLOCK_VPU]); + + clks[BCM2835_CLOCK_UART] = clk_fixed("uart0-pl0110", 48 * 1000 * 1000); + clk_register_clkdev(clks[BCM2835_CLOCK_UART], NULL, "uart0-pl0110"); + + clk_cs = clk_fixed("bcm2835-cs", 1 * 1000 * 1000); + clk_register_clkdev(clk_cs, NULL, "bcm2835-cs"); + + clk_data.clks = clks; + clk_data.clk_num = BCM2711_CLOCK_END; + of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, &clk_data); + + return 0; +} + +static __maybe_unused struct of_device_id bcm2835_cprman_dt_ids[] = { + { + .compatible = "brcm,bcm2835-cprman", + }, { + /* sentinel */ + } +}; + +static struct driver_d bcm2835_cprman_driver = { + .probe = bcm2835_cprman_probe, + .name = "bcm2835-cprman", + .of_compatible = DRV_OF_COMPAT(bcm2835_cprman_dt_ids), +}; +core_platform_driver(bcm2835_cprman_driver); diff --git a/drivers/clk/imx/clk-imx6.c b/drivers/clk/imx/clk-imx6.c index 6935a8e852..e3afcf7858 100644 --- a/drivers/clk/imx/clk-imx6.c +++ b/drivers/clk/imx/clk-imx6.c @@ -60,8 +60,8 @@ static inline int cpu_mx6_is_plus(void) /* Audio/Video PLL post dividers don't work on i.MX6q revision 1.0 */ static inline int cpu_has_working_video_pll_post_div(void) { - return !((cpu_is_mx6q() || cpu_is_mx6d()) && - imx_silicon_revision() == IMX_CHIP_REV_1_0); + return !((cpu_mx6_is_mx6q() || cpu_mx6_is_mx6d()) && + __imx6_cpu_revision() == IMX_CHIP_REV_1_0); } /* i.MX6 Quad/Dual/DualLite/Solo are all affected */ diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index be395a6d55..b208ade9e6 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -543,7 +543,6 @@ static int imx8mp_clocks_init(struct device_node *ccm_np) hws[IMX8MP_CLK_MIPI_DSI_ESC_RX] = imx8m_clk_hw_composite_bus("mipi_dsi_esc_rx", imx8mp_mipi_dsi_esc_rx_sels, ccm_base + 0x9200); hws[IMX8MP_CLK_IPG_ROOT] = imx_clk_hw_divider2("ipg_root", "ahb_root", ccm_base + 0x9080, 0, 1); - hws[IMX8MP_CLK_IPG_AUDIO_ROOT] = imx_clk_hw_divider2("ipg_audio_root", "audio_ahb", ccm_base + 0x9180, 0, 1); hws[IMX8MP_CLK_DRAM_ALT] = imx8m_clk_hw_composite("dram_alt", imx8mp_dram_alt_sels, ccm_base + 0xa000); hws[IMX8MP_CLK_DRAM_APB] = imx8m_clk_hw_composite_critical("dram_apb", imx8mp_dram_apb_sels, ccm_base + 0xa080); diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c index a76600d4c9..63d7398fd4 100644 --- a/drivers/firmware/zynqmp-fpga.c +++ b/drivers/firmware/zynqmp-fpga.c @@ -252,13 +252,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) body_length / sizeof(u32)); else memcpy((u32 *)buf_aligned, body, body_length); - - if (mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED) { - buf_size = body_length; - } else { - buf_aligned[body_length / sizeof(*buf_aligned)] = body_length; - buf_size = addr + body_length; - } + buf_aligned[body_length / sizeof(*buf_aligned)] = body_length; addr = dma_map_single(&mgr->dev, buf_aligned, body_length + sizeof(buf_size), DMA_TO_DEVICE); @@ -267,6 +261,11 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) goto err_free_dma; } + if (mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED) + buf_size = body_length; + else + buf_size = addr + body_length; + status = mgr->eemi_ops->fpga_load((u64)addr, buf_size, flags); dma_unmap_single(&mgr->dev, addr, body_length + sizeof(buf_size), DMA_TO_DEVICE); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 4285280602..9d4a82a9bb 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -68,6 +68,12 @@ config MFD_STPMIC1 help Select this to support communication with the STPMIC1. +config MFD_RN568PMIC + depends on I2C + bool "Ricoh RN5T568 MFD driver" + help + Select this to support communication with the Ricoh RN5T568 PMIC. + config MFD_SUPERIO bool diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2bcf90078a..50f54cfcf2 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -13,6 +13,7 @@ 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_RN568PMIC) += rn5t568.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/rn5t568.c b/drivers/mfd/rn5t568.c new file mode 100644 index 0000000000..c1c792cbec --- /dev/null +++ b/drivers/mfd/rn5t568.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MFD core driver for Ricoh RN5T618 PMIC + * Note: Manufacturer is now Nisshinbo Micro Devices Inc. + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + * Copyright (C) 2016 Toradex AG + */ + +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <i2c/i2c.h> +#include <init.h> +#include <of.h> +#include <regmap.h> +#include <reset_source.h> +#include <restart.h> + +#define RN5T568_LSIVER 0x00 +#define RN5T568_OTPVER 0x01 +#define RN5T568_PONHIS 0x09 +# define RN5T568_PONHIS_ON_EXTINPON BIT(3) +# define RN5T568_PONHIS_ON_REPWRPON BIT(1) +# define RN5T568_PONHIS_ON_PWRONPON BIT(0) +#define RN5T568_POFFHIS 0x0a +# define RN5T568_POFFHIS_N_OEPOFF BIT(7) +# define RN5T568_POFFHIS_DCLIMPOFF BIT(6) +# define RN5T568_POFFHIS_WDGPOFF BIT(5) +# define RN5T568_POFFHIS_CPUPOFF BIT(4) +# define RN5T568_POFFHIS_IODETPOFF BIT(3) +# define RN5T568_POFFHIS_VINDETPOFF BIT(2) +# define RN5T568_POFFHIS_TSHUTPOFF BIT(1) +# define RN5T568_POFFHIS_PWRONPOFF BIT(0) +#define RN5T568_SLPCNT 0x0e +# define RN5T568_SLPCNT_SWPPWROFF BIT(0) +#define RN5T568_REPCNT 0x0f +# define RN5T568_REPCNT_OFF_RESETO_16MS 0x30 +# define RN5T568_REPCNT_OFF_REPWRTIM_1000MS 0x06 +# define RN5T568_REPCNT_OFF_REPWRON BIT(0) +#define RN5T568_MAX_REG 0xbc + +struct rn5t568 { + struct restart_handler restart; + struct regmap *regmap; +}; + +static void rn5t568_restart(struct restart_handler *rst) +{ + struct rn5t568 *rn5t568 = container_of(rst, struct rn5t568, restart); + + regmap_write(rn5t568->regmap, RN5T568_SLPCNT, RN5T568_SLPCNT_SWPPWROFF); +} + +static int rn5t568_reset_reason_detect(struct device_d *dev, struct regmap *regmap) +{ + unsigned int reg; + int ret; + + ret = regmap_read(regmap, RN5T568_PONHIS, ®); + if (ret) + return ret; + + dev_dbg(dev, "Power-on history: %x\n", reg); + + if (reg == 0) { + dev_info(dev, "No power-on reason available\n"); + return 0; + } + + if (reg & RN5T568_PONHIS_ON_EXTINPON) { + reset_source_set_device(dev, RESET_POR); + return 0; + } else if (reg & RN5T568_PONHIS_ON_PWRONPON) { + reset_source_set_device(dev, RESET_POR); + return 0; + } else if (!(reg & RN5T568_PONHIS_ON_REPWRPON)) + return -EINVAL; + + ret = regmap_read(regmap, RN5T568_POFFHIS, ®); + if (ret) + return ret; + + dev_dbg(dev, "Power-off history: %x\n", reg); + + if (reg & RN5T568_POFFHIS_PWRONPOFF) + reset_source_set_device(dev, RESET_POR); + else if (reg & RN5T568_POFFHIS_TSHUTPOFF) + reset_source_set_device(dev, RESET_THERM); + else if (reg & RN5T568_POFFHIS_VINDETPOFF) + reset_source_set_device(dev, RESET_BROWNOUT); + else if (reg & RN5T568_POFFHIS_IODETPOFF) + reset_source_set_device(dev, RESET_UKWN); + else if (reg & RN5T568_POFFHIS_CPUPOFF) + reset_source_set_device(dev, RESET_RST); + else if (reg & RN5T568_POFFHIS_WDGPOFF) + reset_source_set_device(dev, RESET_WDG); + else if (reg & RN5T568_POFFHIS_DCLIMPOFF) + reset_source_set_device(dev, RESET_BROWNOUT); + else if (reg & RN5T568_POFFHIS_N_OEPOFF) + reset_source_set_device(dev, RESET_EXT); + else + return -EINVAL; + + return 0; +} + +static const struct regmap_config rn5t568_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RN5T568_MAX_REG, +}; + +static int __init rn5t568_i2c_probe(struct device_d *dev) +{ + struct rn5t568 *pmic_instance; + unsigned char reg[2]; + int ret; + + pmic_instance = xzalloc(sizeof(struct rn5t568)); + pmic_instance->regmap = regmap_init_i2c(to_i2c_client(dev), &rn5t568_regmap_config); + if (IS_ERR(pmic_instance->regmap)) + return PTR_ERR(pmic_instance->regmap); + + ret = regmap_register_cdev(pmic_instance->regmap, NULL); + if (ret) + return ret; + + ret = regmap_bulk_read(pmic_instance->regmap, RN5T568_LSIVER, ®, 2); + if (ret) { + dev_err(dev, "Failed to read PMIC version via I2C\n"); + return ret; + } + + dev_info(dev, "Found NMD RN5T568 LSI %x, OTP: %x\n", reg[0], reg[1]); + + /* Settings used to trigger software reset and by a watchdog trigger */ + regmap_write(pmic_instance->regmap, RN5T568_REPCNT, RN5T568_REPCNT_OFF_RESETO_16MS | + RN5T568_REPCNT_OFF_REPWRTIM_1000MS | RN5T568_REPCNT_OFF_REPWRON); + + pmic_instance->restart.priority = of_get_restart_priority(dev->device_node); + pmic_instance->restart.name = "RN5T568"; + pmic_instance->restart.restart = &rn5t568_restart; + restart_handler_register(&pmic_instance->restart); + dev_dbg(dev, "RN5t: Restart handler with priority %d registered\n", pmic_instance->restart.priority); + + ret = rn5t568_reset_reason_detect(dev, pmic_instance->regmap); + if (ret) + dev_warn(dev, "Failed to query reset reason\n"); + + return of_platform_populate(dev->device_node, NULL, dev); +} + +static __maybe_unused const struct of_device_id rn5t568_of_match[] = { + { .compatible = "ricoh,rn5t568", .data = NULL, }, + { } +}; + +static struct driver_d rn5t568_i2c_driver = { + .name = "rn5t568-i2c", + .probe = rn5t568_i2c_probe, + .of_compatible = DRV_OF_COMPAT(rn5t568_of_match), +}; + +coredevice_i2c_driver(rn5t568_i2c_driver); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 5ab0506cd9..78c9c193d8 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -47,4 +47,27 @@ config STARFIVE_PWRSEQ be accessed over /dev/mem or used from kernels which still depend on bootloader for initialization. +config STORAGE_BY_UUID + bool "storage by UUID" + depends on OFDEVICE + help + This adds a driver which matches to a "barebox,storage-by-uuid" + compatible node. The driver looks for a storage device matching the + given UUID and when found registers a new cdev for the device. + + This driver solved a very specific problem. On EFI the storage devices + are not connected to any device tree node. barebox-state however expects + a node to use as its backend. The obvious solution would be to create + a partition with a specific partuuid and use that for state, in our + special usecase though the storage device is partitioned with a MBR + which doesn't have any space left to create a new partition. As this + driver parses the of partition binding we can use that to create + a partition in an unallocated are of the disk which is then used for + state. + + This driver has the problem that it depends on storage devices which + are not described in the device tree. This means it cannot work with + deep probe. This is not a problem on EFI though. It's a special purpose + driver, it's not recommended for general use. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6326e784fc..986f7b1b38 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_STATE_DRV) += state.o obj-$(CONFIG_DEV_MEM) += mem.o obj-$(CONFIG_UBOOTVAR) += ubootvar.o obj-$(CONFIG_STARFIVE_PWRSEQ) += starfive-pwrseq.o +obj-$(CONFIG_STORAGE_BY_UUID) += storage-by-uuid.o diff --git a/drivers/misc/storage-by-uuid.c b/drivers/misc/storage-by-uuid.c new file mode 100644 index 0000000000..12a06076a2 --- /dev/null +++ b/drivers/misc/storage-by-uuid.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <common.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <malloc.h> +#include <partition.h> +#include <envfs.h> +#include <fs.h> + +static LIST_HEAD(sbu_list); + +struct sbu { + char *uuid; + struct device_d *dev; + struct cdev *rcdev; + struct cdev cdev; + struct list_head list; +}; + +void storage_by_uuid_check_exist(struct cdev *cdev); + +static ssize_t sbu_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, ulong flags) +{ + struct sbu *sbu = cdev->priv; + + return cdev_read(sbu->rcdev, buf, count, offset, flags); +} + +static ssize_t sbu_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, ulong flags) +{ + struct sbu *sbu = cdev->priv; + + return cdev_write(sbu->rcdev, buf, count, offset, flags); +} + +static int sbu_ioctl(struct cdev *cdev, int request, void *buf) +{ + struct sbu *sbu = cdev->priv; + + return cdev_ioctl(sbu->rcdev, request, buf); +} + +static int sbu_open(struct cdev *cdev, unsigned long flags) +{ + struct sbu *sbu = cdev->priv; + + return cdev_open(sbu->rcdev, flags); +} + +static int sbu_close(struct cdev *cdev) +{ + struct sbu *sbu = cdev->priv; + + cdev_close(sbu->rcdev); + + return 0; +} + +static int sbu_flush(struct cdev *cdev) +{ + struct sbu *sbu = cdev->priv; + + return cdev_flush(sbu->rcdev); +} + +static int sbu_erase(struct cdev *cdev, loff_t count, loff_t offset) +{ + struct sbu *sbu = cdev->priv; + + return cdev_erase(sbu->rcdev, count, offset); +} + +static int sbu_protect(struct cdev *cdev, size_t count, loff_t offset, int prot) +{ + struct sbu *sbu = cdev->priv; + + return cdev_protect(sbu->rcdev, count, offset, prot); +} + +static int sbu_discard_range(struct cdev *cdev, loff_t count, loff_t offset) +{ + struct sbu *sbu = cdev->priv; + + return cdev_discard_range(sbu->rcdev, count, offset); +} + +static int sbu_memmap(struct cdev *cdev, void **map, int flags) +{ + struct sbu *sbu = cdev->priv; + + return cdev_memmap(sbu->rcdev, map, flags); +} + +static int sbu_truncate(struct cdev *cdev, size_t size) +{ + struct sbu *sbu = cdev->priv; + + return cdev_truncate(sbu->rcdev, size); +} + +static struct cdev_operations sbu_ops = { + .read = sbu_read, + .write = sbu_write, + .ioctl = sbu_ioctl, + .open = sbu_open, + .close = sbu_close, + .flush = sbu_flush, + .erase = sbu_erase, + .protect = sbu_protect, + .discard_range = sbu_discard_range, + .memmap = sbu_memmap, + .truncate = sbu_truncate, +}; + +static void storage_by_uuid_add_partitions(struct sbu *sbu, struct cdev *rcdev) +{ + int ret; + + if (sbu->rcdev) + return; + + sbu->rcdev = rcdev; + sbu->cdev.name = sbu->uuid; + sbu->cdev.size = rcdev->size; + sbu->cdev.ops = &sbu_ops; + sbu->cdev.dev = sbu->dev; + sbu->cdev.priv = sbu; + + ret = devfs_create(&sbu->cdev); + if (ret) { + dev_err(sbu->dev, "Failed to create cdev: %s\n", strerror(-ret)); + return; + } + + of_parse_partitions(&sbu->cdev, sbu->dev->device_node); +} + +static void check_exist(struct sbu *sbu) +{ + struct cdev *cdev; + + for_each_cdev(cdev) { + if (!strcmp(cdev->uuid, sbu->uuid)) { + dev_dbg(sbu->dev, "Found %s %s\n", cdev->name, cdev->uuid); + storage_by_uuid_add_partitions(sbu, cdev); + } + } +} + +static int sbu_detect(struct device_d *dev) +{ + struct sbu *sbu = dev->priv; + + dev_dbg(dev, "%s\n", __func__); + + check_exist(sbu); + + return 0; +} + +static int storage_by_uuid_probe(struct device_d *dev) +{ + struct sbu *sbu; + int ret; + const char *uuid; + + sbu = xzalloc(sizeof(*sbu)); + + ret = of_property_read_string(dev->device_node, "uuid", &uuid); + if (ret) + return ret; + + sbu->dev = dev; + sbu->uuid = xstrdup(uuid); + + list_add_tail(&sbu->list, &sbu_list); + + check_exist(sbu); + dev->priv = sbu; + dev->detect = sbu_detect; + + return 0; +} + +static struct of_device_id storage_by_uuid_dt_ids[] = { + { + .compatible = "barebox,storage-by-uuid", + }, { + /* sentinel */ + } +}; + +static struct driver_d storage_by_uuid_driver = { + .name = "storage-by-uuid", + .probe = storage_by_uuid_probe, + .of_compatible = storage_by_uuid_dt_ids, +}; +device_platform_driver(storage_by_uuid_driver); diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index ee1cbf792d..abef07d9c0 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -16,11 +16,6 @@ #include <malloc.h> #include <of.h> -struct mtdram_priv_data { - struct mtd_info mtd; - void *base; -}; - static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { memset((char *)mtd->priv + instr->addr, 0xff, instr->len); @@ -43,11 +38,10 @@ static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retle static int mtdram_probe(struct device_d *dev) { + long type; struct resource *iores; - void __iomem *base; int device_id; struct mtd_info *mtd; - struct resource *res; loff_t size; int ret = 0; @@ -60,9 +54,11 @@ static int mtdram_probe(struct device_d *dev) mtd->name = xstrdup(alias); } + type = (long)device_get_match_data(dev); + if (!mtd->name) { device_id = DEVICE_ID_DYNAMIC; - mtd->name = "mtdram"; + mtd->name = type == MTD_RAM ? "mtdram" : "mtdrom"; } iores = dev_request_mem_resource(dev, 0); @@ -70,22 +66,23 @@ static int mtdram_probe(struct device_d *dev) ret = PTR_ERR(iores); goto nobase; } - base = IOMEM(iores->start); - res = dev_get_resource(dev, IORESOURCE_MEM, 0); - size = (unsigned long) resource_size(res); - mtd->priv = base; + mtd->priv = IOMEM(iores->start); + size = (unsigned long) resource_size(iores); - mtd->type = MTD_RAM; + mtd->type = type; mtd->writesize = 1; mtd->writebufsize = 64; - mtd->flags = MTD_CAP_RAM; mtd->size = size; mtd->_read = ram_read; - mtd->_write = ram_write; - mtd->_erase = ram_erase; - mtd->erasesize = 1; + + if (type == MTD_RAM) { + mtd->flags = MTD_CAP_RAM; + mtd->_write = ram_write; + mtd->_erase = ram_erase; + mtd->erasesize = 1; + } mtd->dev.parent = dev; @@ -101,6 +98,10 @@ nobase: static __maybe_unused struct of_device_id mtdram_dt_ids[] = { { .compatible = "mtd-ram", + .data = (void *)MTD_RAM + }, { + .compatible = "mtd-rom", + .data = (void *)MTD_ROM }, { /* sentinel */ } diff --git a/drivers/of/base.c b/drivers/of/base.c index 80465d6d50..2591610c3f 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2383,7 +2383,7 @@ static void of_platform_device_create_root(struct device_node *np) ret = platform_device_register(dev); if (ret) - free(dev); + free_device(dev); } static const struct of_device_id reserved_mem_matches[] = { diff --git a/drivers/of/of_path.c b/drivers/of/of_path.c index 40154bf5e9..05c28bf052 100644 --- a/drivers/of/of_path.c +++ b/drivers/of/of_path.c @@ -31,7 +31,7 @@ struct device_d *of_find_device_by_node_path(const char *path) * * @node: The node to find the cdev for, can be the device or a * partition in the device - * @part: Optionally, a description of a parition of @node. See of_find_path + * @part: Optionally, a description of a partition of @node. See of_find_path * @outpath: if this function returns 0 outpath will contain the path belonging * to the input path description. Must be freed with free(). * @flags: use OF_FIND_PATH_FLAGS_BB to return the .bb device if available diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 3a82809cb3..0e718469db 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -155,7 +155,7 @@ struct device_d *of_platform_device_create(struct device_node *np, np->dev = NULL; - free(dev); + free_device(dev); if (num_reg) free(res); return NULL; @@ -278,6 +278,7 @@ static struct device_d *of_amba_device_create(struct device_node *np) return &dev->dev; amba_err_free: + free_device_res(&dev->dev); free(dev); return NULL; } diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 968d4b8ba4..564b77ecba 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -12,7 +12,8 @@ config SYSCON_REBOOT_MODE help Say y here will enable reboot mode driver. This will get reboot mode arguments and store it in SYSCON mapped - register, then the bootloader can read it to take different + register, then the bootloader can read it and take different + action according to the mode. config NVMEM_REBOOT_MODE bool "Generic NVMEM reboot mode driver" diff --git a/drivers/serial/serial_cadence.c b/drivers/serial/serial_cadence.c index 6cf18aa25a..84dcd1b76b 100644 --- a/drivers/serial/serial_cadence.c +++ b/drivers/serial/serial_cadence.c @@ -231,6 +231,12 @@ static __maybe_unused struct of_device_id cadence_serial_dt_ids[] = { .compatible = "xlnx,xuartps", .data = &cadence_r1p08_data, }, { + .compatible = "cdns,uart-r1p12", + .data = &cadence_r1p08_data, + }, { + .compatible = "xlnx,zynqmp-uart", + .data = &cadence_r1p08_data, + }, { /* sentinel */ } }; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4eede13a11..34a0f004f7 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -534,6 +534,7 @@ void usb_free_device(struct usb_device *usbdev) { dma_free(usbdev->descriptor); dma_free(usbdev->setup_packet); + free_device_res(&usbdev->dev); free(usbdev); } diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2d194fab79..6f209e096e 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -109,6 +109,12 @@ config STPMIC1_WATCHDOG help Enable to support configuration of the stpmic1's built-in watchdog. +config RN568_WATCHDOG + bool "Ricoh RN5t568 PMIC based Watchdog" + depends on MFD_RN568PMIC + help + Enable to support system control via the PMIC based watchdog. + config F71808E_WDT bool "Fintek F718xx, F818xx Super I/O Watchdog" depends on X86 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 0d2f273f78..265ae179f1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -16,6 +16,7 @@ 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_RN568_WATCHDOG) += rn5t568_wdt.o obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o obj-$(CONFIG_ITCO_WDT) += itco_wdt.o diff --git a/drivers/watchdog/rn5t568_wdt.c b/drivers/watchdog/rn5t568_wdt.c new file mode 100644 index 0000000000..a6443609e2 --- /dev/null +++ b/drivers/watchdog/rn5t568_wdt.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Watchdog driver for Ricoh RN5T618 PMIC + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <watchdog.h> +#include <regmap.h> +#include <of.h> + +#define RN5T568_WATCHDOG 0x0b +# define RN5T568_WATCHDOG_WDPWROFFEN BIT(2) +# define RN5T568_WATCHDOG_WDOGTIM_M (BIT(0) | BIT(1)) +#define RN5T568_PWRIREN 0x12 +# define RN5T568_PWRIREN_EN_WDOG BIT(6) +#define RN5T568_PWRIRQ 0x13 +# define RN5T568_PWRIRQ_IR_WDOG BIT(6) + +struct rn5t568_wdt { + struct watchdog wdd; + struct regmap *regmap; + unsigned int timeout; +}; + +struct rn5t568_wdt_tim { + u8 reg_val; + u8 time; +}; + +static const struct rn5t568_wdt_tim rn5t568_wdt_timeout[] = { + { .reg_val = 0, .time = 1, }, + { .reg_val = 1, .time = 8, }, + { .reg_val = 2, .time = 32, }, + { .reg_val = 3, .time = 128, }, +}; + +#define PMIC_WDT_MAX_TIMEOUT 128 + +static int rn5t568_wdt_start(struct regmap *regmap, int idx) +{ + int ret; + + ret = regmap_update_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDOGTIM_M, + rn5t568_wdt_timeout[idx].reg_val); + if (ret) + return ret; + + regmap_clear_bits(regmap, RN5T568_PWRIRQ, RN5T568_PWRIRQ_IR_WDOG); + regmap_set_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG); + + pr_debug("RN5t: Starting the watchdog with %u seconds\n", rn5t568_wdt_timeout[idx].time); + + return regmap_set_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN); +} + +static int rn5t568_wdt_stop(struct regmap *regmap) +{ + int ret; + + ret = regmap_clear_bits(regmap, RN5T568_PWRIREN, RN5T568_PWRIREN_EN_WDOG); + if (ret) + return ret; + + return regmap_clear_bits(regmap, RN5T568_WATCHDOG, RN5T568_WATCHDOG_WDPWROFFEN); +} + +static int rn5t568_wdt_ping(struct regmap *regmap) +{ + unsigned int val; + int ret; + + ret = regmap_read(regmap, RN5T568_WATCHDOG, &val); + if (ret) + return ret; + + return regmap_write(regmap, RN5T568_WATCHDOG, val); +} + +static int rn5t568_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout) +{ + struct rn5t568_wdt *wdt = container_of(wdd, struct rn5t568_wdt, wdd); + int ret, i; + + if (!timeout) + return rn5t568_wdt_stop(wdt->regmap); + + for (i = 0; i < ARRAY_SIZE(rn5t568_wdt_timeout); i++) { + if (timeout < rn5t568_wdt_timeout[i].time) + break; + } + + if (i == ARRAY_SIZE(rn5t568_wdt_timeout)) + return -EINVAL; + + if (wdt->timeout == timeout) + return rn5t568_wdt_ping(wdt->regmap); + + ret = rn5t568_wdt_start(wdt->regmap, i); + if (ret) + return ret; + + wdt->timeout = rn5t568_wdt_timeout[i].time; + + return ret; +} + +static int rn5t568_wdt_probe(struct device_d *dev) +{ + struct rn5t568_wdt *wdt; + struct watchdog *wdd; + unsigned int val; + int ret; + + wdt = xzalloc(sizeof(*wdt)); + + wdt->regmap = dev_get_regmap(dev->parent, NULL); + if (IS_ERR(wdt->regmap)) + return PTR_ERR(wdt->regmap); + + wdd = &wdt->wdd; + wdd->hwdev = dev; + wdd->set_timeout = rn5t568_wdt_set_timeout; + wdd->timeout_max = PMIC_WDT_MAX_TIMEOUT; + + ret = regmap_read(wdt->regmap, RN5T568_WATCHDOG, &val); + if (ret == 0) + wdd->running = val & RN5T568_WATCHDOG_WDPWROFFEN ? + WDOG_HW_RUNNING : WDOG_HW_NOT_RUNNING; + + return watchdog_register(wdd); +} + +static __maybe_unused const struct of_device_id rn5t568_wdt_of_match[] = { + { .compatible = "ricoh,rn5t568-wdt" }, + { /* sentinel */ } +}; + +static struct driver_d rn5t568_wdt_driver = { + .name = "rn5t568-wdt", + .probe = rn5t568_wdt_probe, + .of_compatible = DRV_OF_COMPAT(rn5t568_wdt_of_match), +}; +device_platform_driver(rn5t568_wdt_driver); |