diff options
Diffstat (limited to 'drivers')
571 files changed, 21561 insertions, 8950 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 14e68f202f81..a3320f93616d 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -1404,7 +1404,6 @@ static const struct attribute_group acpi_nfit_attribute_group = { }; static const struct attribute_group *acpi_nfit_attribute_groups[] = { - &nvdimm_bus_attribute_group, &acpi_nfit_attribute_group, NULL, }; @@ -1698,8 +1697,6 @@ static const struct attribute_group acpi_nfit_dimm_attribute_group = { }; static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = { - &nvdimm_attribute_group, - &nd_device_attribute_group, &acpi_nfit_dimm_attribute_group, NULL, }; @@ -2197,10 +2194,6 @@ static const struct attribute_group acpi_nfit_region_attribute_group = { }; static const struct attribute_group *acpi_nfit_region_attribute_groups[] = { - &nd_region_attribute_group, - &nd_mapping_attribute_group, - &nd_device_attribute_group, - &nd_numa_attribute_group, &acpi_nfit_region_attribute_group, NULL, }; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a606262da9d6..e9bc9fcc7ea5 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -6054,7 +6054,7 @@ const struct file_operations binder_fops = { .owner = THIS_MODULE, .poll = binder_poll, .unlocked_ioctl = binder_ioctl, - .compat_ioctl = binder_ioctl, + .compat_ioctl = compat_ptr_ioctl, .mmap = binder_mmap, .open = binder_open, .flush = binder_flush, diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index bfc617cc8ac5..948d2c6557f3 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -11,8 +11,8 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/ahci_platform.h> +#include <linux/gpio/consumer.h> #include <linux/of_device.h> -#include <linux/of_gpio.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> #include <linux/libata.h> @@ -100,7 +100,7 @@ struct imx_ahci_priv { struct clk *phy_pclk0; struct clk *phy_pclk1; void __iomem *phy_base; - int clkreq_gpio; + struct gpio_desc *clkreq_gpiod; struct regmap *gpr; bool no_device; bool first_time; @@ -980,7 +980,6 @@ static struct scsi_host_template ahci_platform_sht = { static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) { - int ret; struct resource *phy_res; struct platform_device *pdev = imxpriv->ahci_pdev; struct device_node *np = dev->of_node; @@ -1033,20 +1032,12 @@ static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) } /* Fetch GPIO, then enable the external OSC */ - imxpriv->clkreq_gpio = of_get_named_gpio(np, "clkreq-gpio", 0); - if (gpio_is_valid(imxpriv->clkreq_gpio)) { - ret = devm_gpio_request_one(dev, imxpriv->clkreq_gpio, - GPIOF_OUT_INIT_LOW, - "SATA CLKREQ"); - if (ret == -EBUSY) { - dev_info(dev, "clkreq had been initialized.\n"); - } else if (ret) { - dev_err(dev, "%d unable to get clkreq.\n", ret); - return ret; - } - } else if (imxpriv->clkreq_gpio == -EPROBE_DEFER) { - return imxpriv->clkreq_gpio; - } + imxpriv->clkreq_gpiod = devm_gpiod_get_optional(dev, "clkreq", + GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(imxpriv->clkreq_gpiod)) + return PTR_ERR(imxpriv->clkreq_gpiod); + if (imxpriv->clkreq_gpiod) + gpiod_set_consumer_name(imxpriv->clkreq_gpiod, "SATA CLKREQ"); return 0; } diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index bd7d3bb8b890..1553d41f0b91 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -857,7 +857,7 @@ static void fd_calibrate( void ) } if (ATARIHW_PRESENT(FDCSPEED)) - dma_wd.fdc_speed = 0; /* always seek with 8 Mhz */; + dma_wd.fdc_speed = 0; /* always seek with 8 Mhz */ DPRINT(("fd_calibrate\n")); SET_IRQ_HANDLER( fd_calibrate_done ); /* we can't verify, since the speed may be incorrect */ diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index f86cea4c0f8d..840c3aef3c5c 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -884,7 +884,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what, start_new_tl_epoch(connection); mod_rq_state(req, m, 0, RQ_NET_OK|RQ_NET_DONE); break; - }; + } return rv; } diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 76457003f140..ee67bf929fac 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2663,6 +2663,28 @@ static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, return ret; } +#ifdef CONFIG_COMPAT +static int pkt_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + /* compatible */ + case CDROMEJECT: + case CDROMMULTISESSION: + case CDROMREADTOCENTRY: + case SCSI_IOCTL_SEND_COMMAND: + return pkt_ioctl(bdev, mode, cmd, (unsigned long)compat_ptr(arg)); + + + /* FIXME: no handler so far */ + case CDROM_LAST_WRITTEN: + /* handled in compat_blkdev_driver_ioctl */ + case CDROM_SEND_PACKET: + default: + return -ENOIOCTLCMD; + } +} +#endif + static unsigned int pkt_check_events(struct gendisk *disk, unsigned int clearing) { @@ -2684,6 +2706,9 @@ static const struct block_device_operations pktcdvd_ops = { .open = pkt_open, .release = pkt_close, .ioctl = pkt_ioctl, +#ifdef CONFIG_COMPAT + .ioctl = pkt_compat_ioctl, +#endif .check_events = pkt_check_events, }; diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 6b2fd630de85..571612e233fe 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -634,7 +634,7 @@ static int generic_request(struct vdc_port *port, u8 op, void *buf, int len) case VD_OP_GET_EFI: case VD_OP_SET_EFI: return -EOPNOTSUPP; - }; + } map_perm |= LDC_MAP_SHADOW | LDC_MAP_DIRECT | LDC_MAP_IO; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index ac42ae4651ce..eebdcbef0578 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -996,6 +996,12 @@ static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks) tracks->xa = 0; tracks->error = 0; cd_dbg(CD_COUNT_TRACKS, "entering cdrom_count_tracks\n"); + + if (!CDROM_CAN(CDC_PLAY_AUDIO)) { + tracks->error = CDS_NO_INFO; + return; + } + /* Grab the TOC header so we can see how many tracks there are */ ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCHDR, &header); if (ret) { @@ -1162,7 +1168,8 @@ int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev, ret = open_for_data(cdi); if (ret) goto err; - cdrom_mmc3_profile(cdi); + if (CDROM_CAN(CDC_GENERIC_PACKET)) + cdrom_mmc3_profile(cdi); if (mode & FMODE_WRITE) { ret = -EROFS; if (cdrom_open_write(cdi)) @@ -2882,6 +2889,9 @@ int cdrom_get_last_written(struct cdrom_device_info *cdi, long *last_written) it doesn't give enough information or fails. then we return the toc contents. */ use_toc: + if (!CDROM_CAN(CDC_PLAY_AUDIO)) + return -ENOSYS; + toc.cdte_format = CDROM_MSF; toc.cdte_track = CDROM_LEADOUT; if ((ret = cdi->ops->audio_ioctl(cdi, CDROMREADTOCENTRY, &toc))) diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 74c6d1f34132..55986e10a124 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -893,6 +893,7 @@ static const struct file_operations ipmi_wdog_fops = { .poll = ipmi_poll, .write = ipmi_write, .unlocked_ioctl = ipmi_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync, diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 34bb88fe0b0a..2c2381a806ae 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -678,14 +678,6 @@ static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } -#ifdef CONFIG_COMPAT -static long pp_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - static int pp_open(struct inode *inode, struct file *file) { unsigned int minor = iminor(inode); @@ -794,9 +786,7 @@ static const struct file_operations pp_fops = { .write = pp_write, .poll = pp_poll, .unlocked_ioctl = pp_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = pp_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = pp_open, .release = pp_release, }; diff --git a/drivers/char/random.c b/drivers/char/random.c index 01b8868b9bed..909e0c3d82ea 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -2166,6 +2166,7 @@ const struct file_operations random_fops = { .write = random_write, .poll = random_poll, .unlocked_ioctl = random_ioctl, + .compat_ioctl = compat_ptr_ioctl, .fasync = random_fasync, .llseek = noop_llseek, }; diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 2f6e087ec496..91c772e38bb5 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -670,20 +670,10 @@ static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl, } } -#ifdef CONFIG_COMPAT -static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations vtpmx_fops = { .owner = THIS_MODULE, .unlocked_ioctl = vtpmx_fops_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vtpmx_fops_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 3259426f01dc..4df9b40d6342 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -919,6 +919,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, .pos = *ppos, .u.data = &sgl, }; + unsigned int occupancy; /* * Rproc_serial does not yet support splice. To support splice @@ -929,21 +930,18 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, if (is_rproc_serial(port->out_vq->vdev)) return -EINVAL; - /* - * pipe->nrbufs == 0 means there are no data to transfer, - * so this returns just 0 for no data. - */ pipe_lock(pipe); - if (!pipe->nrbufs) { - ret = 0; + ret = 0; + if (pipe_empty(pipe->head, pipe->tail)) goto error_out; - } ret = wait_port_writable(port, filp->f_flags & O_NONBLOCK); if (ret < 0) goto error_out; - buf = alloc_buf(port->portdev->vdev, 0, pipe->nrbufs); + occupancy = pipe_occupancy(pipe->head, pipe->tail); + buf = alloc_buf(port->portdev->vdev, 0, occupancy); + if (!buf) { ret = -ENOMEM; goto error_out; @@ -951,7 +949,7 @@ static ssize_t port_fops_splice_write(struct pipe_inode_info *pipe, sgl.n = 0; sgl.len = 0; - sgl.size = pipe->nrbufs; + sgl.size = occupancy; sgl.sg = buf->sg; sg_init_table(sgl.sg, sgl.size); ret = __splice_from_pipe(pipe, &sd, pipe_to_sg); diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index c44247d0b83e..dc920daa6dbb 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -136,6 +136,13 @@ config COMMON_CLK_SI570 This driver supports Silicon Labs 570/571/598/599 programmable clock generators. +config COMMON_CLK_BM1880 + bool "Clock driver for Bitmain BM1880 SoC" + depends on ARCH_BITMAIN || COMPILE_TEST + default ARCH_BITMAIN + help + This driver supports the clocks on Bitmain BM1880 SoC. + config COMMON_CLK_CDCE706 tristate "Clock driver for TI CDCE706 clock synthesizer" depends on I2C diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 0138fb14e6f8..0696a0c1ab58 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o obj-$(CONFIG_COMMON_CLK_BD718XX) += clk-bd718x7.o +obj-$(CONFIG_COMMON_CLK_BM1880) += clk-bm1880.o obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c index fac0ca56d42d..15dc4cd86d76 100644 --- a/drivers/clk/at91/sckc.c +++ b/drivers/clk/at91/sckc.c @@ -487,8 +487,7 @@ static void __init of_sam9x60_sckc_setup(struct device_node *np) if (IS_ERR(slow_osc)) goto unregister_slow_rc; - clk_data = kzalloc(sizeof(*clk_data) + (2 * sizeof(struct clk_hw *)), - GFP_KERNEL); + clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL); if (!clk_data) goto unregister_slow_osc; diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c index 71c2e9519ca8..e9da0e69bf6c 100644 --- a/drivers/clk/axs10x/i2s_pll_clock.c +++ b/drivers/clk/axs10x/i2s_pll_clock.c @@ -172,14 +172,12 @@ static int i2s_pll_clk_probe(struct platform_device *pdev) struct clk *clk; struct i2s_pll_clk *pll_clk; struct clk_init_data init; - struct resource *mem; pll_clk = devm_kzalloc(dev, sizeof(*pll_clk), GFP_KERNEL); if (!pll_clk) return -ENOMEM; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pll_clk->base = devm_ioremap_resource(dev, mem); + pll_clk->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pll_clk->base)) return PTR_ERR(pll_clk->base); diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c index aba787b2e771..500345d99adb 100644 --- a/drivers/clk/axs10x/pll_clock.c +++ b/drivers/clk/axs10x/pll_clock.c @@ -221,7 +221,6 @@ static int axs10x_pll_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const char *parent_name; struct axs10x_pll_clk *pll_clk; - struct resource *mem; struct clk_init_data init = { }; int ret; @@ -229,13 +228,11 @@ static int axs10x_pll_clk_probe(struct platform_device *pdev) if (!pll_clk) return -ENOMEM; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pll_clk->base = devm_ioremap_resource(dev, mem); + pll_clk->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pll_clk->base)) return PTR_ERR(pll_clk->base); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - pll_clk->lock = devm_ioremap_resource(dev, mem); + pll_clk->lock = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(pll_clk->lock)) return PTR_ERR(pll_clk->lock); diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c index b6d07ca0164f..290a2846a86b 100644 --- a/drivers/clk/bcm/clk-bcm2835-aux.c +++ b/drivers/clk/bcm/clk-bcm2835-aux.c @@ -19,7 +19,6 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev) struct clk_hw_onecell_data *onecell; const char *parent; struct clk *parent_clk; - struct resource *res; void __iomem *reg, *gate; parent_clk = devm_clk_get(dev, NULL); @@ -27,8 +26,7 @@ static int bcm2835_aux_clk_probe(struct platform_device *pdev) return PTR_ERR(parent_clk); parent = __clk_get_name(parent_clk); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(dev, res); + reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(reg)) return PTR_ERR(reg); diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 802e488fd3c3..ded13ccf768e 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -2192,7 +2192,6 @@ static int bcm2835_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct clk_hw **hws; struct bcm2835_cprman *cprman; - struct resource *res; const struct bcm2835_clk_desc *desc; const size_t asize = ARRAY_SIZE(clk_desc_array); const struct cprman_plat_data *pdata; @@ -2211,8 +2210,7 @@ static int bcm2835_clk_probe(struct platform_device *pdev) spin_lock_init(&cprman->regs_lock); cprman->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - cprman->regs = devm_ioremap_resource(dev, res); + cprman->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cprman->regs)) return PTR_ERR(cprman->regs); diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c index abf06fb6453e..411ff5fb2c07 100644 --- a/drivers/clk/clk-aspeed.c +++ b/drivers/clk/clk-aspeed.c @@ -14,7 +14,7 @@ #include "clk-aspeed.h" -#define ASPEED_NUM_CLKS 36 +#define ASPEED_NUM_CLKS 38 #define ASPEED_RESET2_OFFSET 32 @@ -28,6 +28,7 @@ #define AST2400_HPLL_BYPASS_EN BIT(17) #define ASPEED_MISC_CTRL 0x2c #define UART_DIV13_EN BIT(12) +#define ASPEED_MAC_CLK_DLY 0x48 #define ASPEED_STRAP 0x70 #define CLKIN_25MHZ_EN BIT(23) #define AST2400_CLK_SOURCE_SEL BIT(18) @@ -462,6 +463,30 @@ static int aspeed_clk_probe(struct platform_device *pdev) return PTR_ERR(hw); aspeed_clk_data->hws[ASPEED_CLK_MAC] = hw; + if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-scu")) { + /* RMII 50MHz RCLK */ + hw = clk_hw_register_fixed_rate(dev, "mac12rclk", "hpll", 0, + 50000000); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + /* RMII1 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac1rclk", "mac12rclk", 0, + scu_base + ASPEED_MAC_CLK_DLY, 29, 0, + &aspeed_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_MAC1RCLK] = hw; + + /* RMII2 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac2rclk", "mac12rclk", 0, + scu_base + ASPEED_MAC_CLK_DLY, 30, 0, + &aspeed_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_clk_data->hws[ASPEED_CLK_MAC2RCLK] = hw; + } + /* LPC Host (LHCLK) clock divider */ hw = clk_hw_register_divider_table(dev, "lhclk", "hpll", 0, scu_base + ASPEED_CLK_SELECTION, 20, 3, 0, diff --git a/drivers/clk/clk-ast2600.c b/drivers/clk/clk-ast2600.c index b1318e6b655b..392d01705b97 100644 --- a/drivers/clk/clk-ast2600.c +++ b/drivers/clk/clk-ast2600.c @@ -15,7 +15,7 @@ #include "clk-aspeed.h" -#define ASPEED_G6_NUM_CLKS 67 +#define ASPEED_G6_NUM_CLKS 71 #define ASPEED_G6_SILICON_REV 0x004 @@ -40,6 +40,9 @@ #define ASPEED_G6_STRAP1 0x500 +#define ASPEED_MAC12_CLK_DLY 0x340 +#define ASPEED_MAC34_CLK_DLY 0x350 + /* Globally visible clocks */ static DEFINE_SPINLOCK(aspeed_g6_clk_lock); @@ -116,8 +119,6 @@ static const struct aspeed_gate_data aspeed_g6_gates[] = { [ASPEED_CLK_GATE_FSICLK] = { 62, 59, "fsiclk-gate", NULL, 0 }, /* FSI */ }; -static const char * const eclk_parent_names[] = { "mpll", "hpll", "dpll" }; - static const struct clk_div_table ast2600_eclk_div_table[] = { { 0x0, 2 }, { 0x1, 2 }, @@ -486,6 +487,11 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev) return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_SDIO] = hw; + /* MAC1/2 RMII 50MHz RCLK */ + hw = clk_hw_register_fixed_rate(dev, "mac12rclk", "hpll", 0, 50000000); + if (IS_ERR(hw)) + return PTR_ERR(hw); + /* MAC1/2 AHB bus clock divider */ hw = clk_hw_register_divider_table(dev, "mac12", "hpll", 0, scu_g6_base + ASPEED_G6_CLK_SELECTION1, 16, 3, 0, @@ -495,6 +501,27 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev) return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_MAC12] = hw; + /* RMII1 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac1rclk", "mac12rclk", 0, + scu_g6_base + ASPEED_MAC12_CLK_DLY, 29, 0, + &aspeed_g6_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_g6_clk_data->hws[ASPEED_CLK_MAC1RCLK] = hw; + + /* RMII2 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac2rclk", "mac12rclk", 0, + scu_g6_base + ASPEED_MAC12_CLK_DLY, 30, 0, + &aspeed_g6_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_g6_clk_data->hws[ASPEED_CLK_MAC2RCLK] = hw; + + /* MAC1/2 RMII 50MHz RCLK */ + hw = clk_hw_register_fixed_rate(dev, "mac34rclk", "hclk", 0, 50000000); + if (IS_ERR(hw)) + return PTR_ERR(hw); + /* MAC3/4 AHB bus clock divider */ hw = clk_hw_register_divider_table(dev, "mac34", "hpll", 0, scu_g6_base + 0x310, 24, 3, 0, @@ -504,6 +531,22 @@ static int aspeed_g6_clk_probe(struct platform_device *pdev) return PTR_ERR(hw); aspeed_g6_clk_data->hws[ASPEED_CLK_MAC34] = hw; + /* RMII3 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac3rclk", "mac34rclk", 0, + scu_g6_base + ASPEED_MAC34_CLK_DLY, 29, 0, + &aspeed_g6_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_g6_clk_data->hws[ASPEED_CLK_MAC3RCLK] = hw; + + /* RMII4 50MHz (RCLK) output enable */ + hw = clk_hw_register_gate(dev, "mac4rclk", "mac34rclk", 0, + scu_g6_base + ASPEED_MAC34_CLK_DLY, 30, 0, + &aspeed_g6_clk_lock); + if (IS_ERR(hw)) + return PTR_ERR(hw); + aspeed_g6_clk_data->hws[ASPEED_CLK_MAC4RCLK] = hw; + /* LPC Host (LHCLK) clock divider */ hw = clk_hw_register_divider_table(dev, "lhclk", "hpll", 0, scu_g6_base + ASPEED_G6_CLK_SELECTION1, 20, 3, 0, diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c index ae6e5baee330..00926c587390 100644 --- a/drivers/clk/clk-bd718x7.c +++ b/drivers/clk/clk-bd718x7.c @@ -133,3 +133,4 @@ module_platform_driver(bd71837_clk); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("BD71837/BD71847/BD70528 chip clk driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd718xx-clk"); diff --git a/drivers/clk/clk-bm1880.c b/drivers/clk/clk-bm1880.c new file mode 100644 index 000000000000..4cd175afce9b --- /dev/null +++ b/drivers/clk/clk-bm1880.c @@ -0,0 +1,969 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bitmain BM1880 SoC clock driver + * + * Copyright (c) 2019 Linaro Ltd. + * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + */ + +#include <linux/clk-provider.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <dt-bindings/clock/bm1880-clock.h> + +#define BM1880_CLK_MPLL_CTL 0x00 +#define BM1880_CLK_SPLL_CTL 0x04 +#define BM1880_CLK_FPLL_CTL 0x08 +#define BM1880_CLK_DDRPLL_CTL 0x0c + +#define BM1880_CLK_ENABLE0 0x00 +#define BM1880_CLK_ENABLE1 0x04 +#define BM1880_CLK_SELECT 0x20 +#define BM1880_CLK_DIV0 0x40 +#define BM1880_CLK_DIV1 0x44 +#define BM1880_CLK_DIV2 0x48 +#define BM1880_CLK_DIV3 0x4c +#define BM1880_CLK_DIV4 0x50 +#define BM1880_CLK_DIV5 0x54 +#define BM1880_CLK_DIV6 0x58 +#define BM1880_CLK_DIV7 0x5c +#define BM1880_CLK_DIV8 0x60 +#define BM1880_CLK_DIV9 0x64 +#define BM1880_CLK_DIV10 0x68 +#define BM1880_CLK_DIV11 0x6c +#define BM1880_CLK_DIV12 0x70 +#define BM1880_CLK_DIV13 0x74 +#define BM1880_CLK_DIV14 0x78 +#define BM1880_CLK_DIV15 0x7c +#define BM1880_CLK_DIV16 0x80 +#define BM1880_CLK_DIV17 0x84 +#define BM1880_CLK_DIV18 0x88 +#define BM1880_CLK_DIV19 0x8c +#define BM1880_CLK_DIV20 0x90 +#define BM1880_CLK_DIV21 0x94 +#define BM1880_CLK_DIV22 0x98 +#define BM1880_CLK_DIV23 0x9c +#define BM1880_CLK_DIV24 0xa0 +#define BM1880_CLK_DIV25 0xa4 +#define BM1880_CLK_DIV26 0xa8 +#define BM1880_CLK_DIV27 0xac +#define BM1880_CLK_DIV28 0xb0 + +#define to_bm1880_pll_clk(_hw) container_of(_hw, struct bm1880_pll_hw_clock, hw) +#define to_bm1880_div_clk(_hw) container_of(_hw, struct bm1880_div_hw_clock, hw) + +static DEFINE_SPINLOCK(bm1880_clk_lock); + +struct bm1880_clock_data { + void __iomem *pll_base; + void __iomem *sys_base; + struct clk_hw_onecell_data hw_data; +}; + +struct bm1880_gate_clock { + unsigned int id; + const char *name; + const char *parent; + u32 gate_reg; + s8 gate_shift; + unsigned long flags; +}; + +struct bm1880_mux_clock { + unsigned int id; + const char *name; + const char * const *parents; + s8 num_parents; + u32 reg; + s8 shift; + unsigned long flags; +}; + +struct bm1880_div_clock { + unsigned int id; + const char *name; + u32 reg; + u8 shift; + u8 width; + u32 initval; + const struct clk_div_table *table; + unsigned long flags; +}; + +struct bm1880_div_hw_clock { + struct bm1880_div_clock div; + void __iomem *base; + spinlock_t *lock; + struct clk_hw hw; + struct clk_init_data init; +}; + +struct bm1880_composite_clock { + unsigned int id; + const char *name; + const char *parent; + const char * const *parents; + unsigned int num_parents; + unsigned long flags; + + u32 gate_reg; + u32 mux_reg; + u32 div_reg; + + s8 gate_shift; + s8 mux_shift; + s8 div_shift; + s8 div_width; + s16 div_initval; + const struct clk_div_table *table; +}; + +struct bm1880_pll_clock { + unsigned int id; + const char *name; + u32 reg; + unsigned long flags; +}; + +struct bm1880_pll_hw_clock { + struct bm1880_pll_clock pll; + void __iomem *base; + struct clk_hw hw; + struct clk_init_data init; +}; + +static const struct clk_ops bm1880_pll_ops; +static const struct clk_ops bm1880_clk_div_ops; + +#define GATE_DIV(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, \ + _div_shift, _div_width, _div_initval, _table, \ + _flags) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .div_reg = _div_reg, \ + .div_shift = _div_shift, \ + .div_width = _div_width, \ + .div_initval = _div_initval, \ + .table = _table, \ + .mux_shift = -1, \ + .flags = _flags, \ + } + +#define GATE_MUX(_id, _name, _parents, _gate_reg, _gate_shift, \ + _mux_reg, _mux_shift, _flags) { \ + .id = _id, \ + .parents = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .name = _name, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .div_shift = -1, \ + .mux_reg = _mux_reg, \ + .mux_shift = _mux_shift, \ + .flags = _flags, \ + } + +#define CLK_PLL(_id, _name, _parent, _reg, _flags) { \ + .pll.id = _id, \ + .pll.name = _name, \ + .pll.reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, _parent, \ + &bm1880_pll_ops, \ + _flags), \ + } + +#define CLK_DIV(_id, _name, _parent, _reg, _shift, _width, _initval, \ + _table, _flags) { \ + .div.id = _id, \ + .div.name = _name, \ + .div.reg = _reg, \ + .div.shift = _shift, \ + .div.width = _width, \ + .div.initval = _initval, \ + .div.table = _table, \ + .hw.init = CLK_HW_INIT_HW(_name, _parent, \ + &bm1880_clk_div_ops, \ + _flags), \ + } + +static struct clk_parent_data bm1880_pll_parent[] = { + { .fw_name = "osc", .name = "osc" }, +}; + +/* + * All PLL clocks are marked as CRITICAL, hence they are very crucial + * for the functioning of the SoC + */ +static struct bm1880_pll_hw_clock bm1880_pll_clks[] = { + CLK_PLL(BM1880_CLK_MPLL, "clk_mpll", bm1880_pll_parent, + BM1880_CLK_MPLL_CTL, 0), + CLK_PLL(BM1880_CLK_SPLL, "clk_spll", bm1880_pll_parent, + BM1880_CLK_SPLL_CTL, 0), + CLK_PLL(BM1880_CLK_FPLL, "clk_fpll", bm1880_pll_parent, + BM1880_CLK_FPLL_CTL, 0), + CLK_PLL(BM1880_CLK_DDRPLL, "clk_ddrpll", bm1880_pll_parent, + BM1880_CLK_DDRPLL_CTL, 0), +}; + +/* + * Clocks marked as CRITICAL are needed for the proper functioning + * of the SoC. + */ +static const struct bm1880_gate_clock bm1880_gate_clks[] = { + { BM1880_CLK_AHB_ROM, "clk_ahb_rom", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 2, 0 }, + { BM1880_CLK_AXI_SRAM, "clk_axi_sram", "clk_axi1", + BM1880_CLK_ENABLE0, 3, 0 }, + /* + * Since this clock is sourcing the DDR memory, let's mark it as + * critical to avoid gating. + */ + { BM1880_CLK_DDR_AXI, "clk_ddr_axi", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 4, CLK_IS_CRITICAL }, + { BM1880_CLK_APB_EFUSE, "clk_apb_efuse", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 6, 0 }, + { BM1880_CLK_AXI5_EMMC, "clk_axi5_emmc", "clk_axi5", + BM1880_CLK_ENABLE0, 7, 0 }, + { BM1880_CLK_AXI5_SD, "clk_axi5_sd", "clk_axi5", + BM1880_CLK_ENABLE0, 10, 0 }, + { BM1880_CLK_AXI4_ETH0, "clk_axi4_eth0", "clk_axi4", + BM1880_CLK_ENABLE0, 14, 0 }, + { BM1880_CLK_AXI4_ETH1, "clk_axi4_eth1", "clk_axi4", + BM1880_CLK_ENABLE0, 16, 0 }, + { BM1880_CLK_AXI1_GDMA, "clk_axi1_gdma", "clk_axi1", + BM1880_CLK_ENABLE0, 17, 0 }, + /* Don't gate GPIO clocks as it is not owned by the GPIO driver */ + { BM1880_CLK_APB_GPIO, "clk_apb_gpio", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 18, CLK_IGNORE_UNUSED }, + { BM1880_CLK_APB_GPIO_INTR, "clk_apb_gpio_intr", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 19, CLK_IGNORE_UNUSED }, + { BM1880_CLK_AXI1_MINER, "clk_axi1_miner", "clk_axi1", + BM1880_CLK_ENABLE0, 21, 0 }, + { BM1880_CLK_AHB_SF, "clk_ahb_sf", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 22, 0 }, + /* + * Not sure which module this clock is sourcing but gating this clock + * prevents the system from booting. So, let's mark it as critical. + */ + { BM1880_CLK_SDMA_AXI, "clk_sdma_axi", "clk_axi5", + BM1880_CLK_ENABLE0, 23, CLK_IS_CRITICAL }, + { BM1880_CLK_APB_I2C, "clk_apb_i2c", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 25, 0 }, + { BM1880_CLK_APB_WDT, "clk_apb_wdt", "clk_mux_axi6", + BM1880_CLK_ENABLE0, 26, 0 }, + { BM1880_CLK_APB_JPEG, "clk_apb_jpeg", "clk_axi6", + BM1880_CLK_ENABLE0, 27, 0 }, + { BM1880_CLK_AXI5_NF, "clk_axi5_nf", "clk_axi5", + BM1880_CLK_ENABLE0, 29, 0 }, + { BM1880_CLK_APB_NF, "clk_apb_nf", "clk_axi6", + BM1880_CLK_ENABLE0, 30, 0 }, + { BM1880_CLK_APB_PWM, "clk_apb_pwm", "clk_mux_axi6", + BM1880_CLK_ENABLE1, 0, 0 }, + { BM1880_CLK_RV, "clk_rv", "clk_mux_rv", + BM1880_CLK_ENABLE1, 1, 0 }, + { BM1880_CLK_APB_SPI, "clk_apb_spi", "clk_mux_axi6", + BM1880_CLK_ENABLE1, 2, 0 }, + { BM1880_CLK_UART_500M, "clk_uart_500m", "clk_div_uart_500m", + BM1880_CLK_ENABLE1, 4, 0 }, + { BM1880_CLK_APB_UART, "clk_apb_uart", "clk_axi6", + BM1880_CLK_ENABLE1, 5, 0 }, + { BM1880_CLK_APB_I2S, "clk_apb_i2s", "clk_axi6", + BM1880_CLK_ENABLE1, 6, 0 }, + { BM1880_CLK_AXI4_USB, "clk_axi4_usb", "clk_axi4", + BM1880_CLK_ENABLE1, 7, 0 }, + { BM1880_CLK_APB_USB, "clk_apb_usb", "clk_axi6", + BM1880_CLK_ENABLE1, 8, 0 }, + { BM1880_CLK_12M_USB, "clk_12m_usb", "clk_div_12m_usb", + BM1880_CLK_ENABLE1, 11, 0 }, + { BM1880_CLK_APB_VIDEO, "clk_apb_video", "clk_axi6", + BM1880_CLK_ENABLE1, 12, 0 }, + { BM1880_CLK_APB_VPP, "clk_apb_vpp", "clk_axi6", + BM1880_CLK_ENABLE1, 15, 0 }, + { BM1880_CLK_AXI6, "clk_axi6", "clk_mux_axi6", + BM1880_CLK_ENABLE1, 21, 0 }, +}; + +static const char * const clk_a53_parents[] = { "clk_spll", "clk_mpll" }; +static const char * const clk_rv_parents[] = { "clk_div_1_rv", "clk_div_0_rv" }; +static const char * const clk_axi1_parents[] = { "clk_div_1_axi1", "clk_div_0_axi1" }; +static const char * const clk_axi6_parents[] = { "clk_div_1_axi6", "clk_div_0_axi6" }; + +static const struct bm1880_mux_clock bm1880_mux_clks[] = { + { BM1880_CLK_MUX_RV, "clk_mux_rv", clk_rv_parents, 2, + BM1880_CLK_SELECT, 1, 0 }, + { BM1880_CLK_MUX_AXI6, "clk_mux_axi6", clk_axi6_parents, 2, + BM1880_CLK_SELECT, 3, 0 }, +}; + +static const struct clk_div_table bm1880_div_table_0[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, + { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, + { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, + { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, + { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, + { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, + { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, + { 0, 0 } +}; + +static const struct clk_div_table bm1880_div_table_1[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, + { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, + { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, + { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, + { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, + { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, + { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, + { 127, 128 }, { 0, 0 } +}; + +static const struct clk_div_table bm1880_div_table_2[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, + { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, + { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, + { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, + { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, + { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, + { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, + { 127, 128 }, { 255, 256 }, { 0, 0 } +}; + +static const struct clk_div_table bm1880_div_table_3[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, + { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, + { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, + { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, + { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, + { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, + { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, + { 127, 128 }, { 255, 256 }, { 511, 512 }, { 0, 0 } +}; + +static const struct clk_div_table bm1880_div_table_4[] = { + { 0, 1 }, { 1, 2 }, { 2, 3 }, { 3, 4 }, + { 4, 5 }, { 5, 6 }, { 6, 7 }, { 7, 8 }, + { 8, 9 }, { 9, 10 }, { 10, 11 }, { 11, 12 }, + { 12, 13 }, { 13, 14 }, { 14, 15 }, { 15, 16 }, + { 16, 17 }, { 17, 18 }, { 18, 19 }, { 19, 20 }, + { 20, 21 }, { 21, 22 }, { 22, 23 }, { 23, 24 }, + { 24, 25 }, { 25, 26 }, { 26, 27 }, { 27, 28 }, + { 28, 29 }, { 29, 30 }, { 30, 31 }, { 31, 32 }, + { 127, 128 }, { 255, 256 }, { 511, 512 }, { 65535, 65536 }, + { 0, 0 } +}; + +/* + * Clocks marked as CRITICAL are needed for the proper functioning + * of the SoC. + */ +static struct bm1880_div_hw_clock bm1880_div_clks[] = { + CLK_DIV(BM1880_CLK_DIV_0_RV, "clk_div_0_rv", &bm1880_pll_clks[1].hw, + BM1880_CLK_DIV12, 16, 5, 1, bm1880_div_table_0, 0), + CLK_DIV(BM1880_CLK_DIV_1_RV, "clk_div_1_rv", &bm1880_pll_clks[2].hw, + BM1880_CLK_DIV13, 16, 5, 1, bm1880_div_table_0, 0), + CLK_DIV(BM1880_CLK_DIV_UART_500M, "clk_div_uart_500m", &bm1880_pll_clks[2].hw, + BM1880_CLK_DIV15, 16, 7, 3, bm1880_div_table_1, 0), + CLK_DIV(BM1880_CLK_DIV_0_AXI1, "clk_div_0_axi1", &bm1880_pll_clks[0].hw, + BM1880_CLK_DIV21, 16, 5, 2, bm1880_div_table_0, + 0), + CLK_DIV(BM1880_CLK_DIV_1_AXI1, "clk_div_1_axi1", &bm1880_pll_clks[2].hw, + BM1880_CLK_DIV22, 16, 5, 3, bm1880_div_table_0, + 0), + CLK_DIV(BM1880_CLK_DIV_0_AXI6, "clk_div_0_axi6", &bm1880_pll_clks[2].hw, + BM1880_CLK_DIV27, 16, 5, 15, bm1880_div_table_0, + 0), + CLK_DIV(BM1880_CLK_DIV_1_AXI6, "clk_div_1_axi6", &bm1880_pll_clks[0].hw, + BM1880_CLK_DIV28, 16, 5, 11, bm1880_div_table_0, + 0), + CLK_DIV(BM1880_CLK_DIV_12M_USB, "clk_div_12m_usb", &bm1880_pll_clks[2].hw, + BM1880_CLK_DIV18, 16, 7, 125, bm1880_div_table_1, 0), +}; + +/* + * Clocks marked as CRITICAL are all needed for the proper functioning + * of the SoC. + */ +static struct bm1880_composite_clock bm1880_composite_clks[] = { + /* + * Since clk_a53 and clk_50m_a53 clocks are sourcing the CPU core, + * let's mark them as critical to avoid gating. + */ + GATE_MUX(BM1880_CLK_A53, "clk_a53", clk_a53_parents, + BM1880_CLK_ENABLE0, 0, BM1880_CLK_SELECT, 0, + CLK_IS_CRITICAL), + GATE_DIV(BM1880_CLK_50M_A53, "clk_50m_a53", "clk_fpll", + BM1880_CLK_ENABLE0, 1, BM1880_CLK_DIV0, 16, 5, 30, + bm1880_div_table_0, CLK_IS_CRITICAL), + GATE_DIV(BM1880_CLK_EFUSE, "clk_efuse", "clk_fpll", + BM1880_CLK_ENABLE0, 5, BM1880_CLK_DIV1, 16, 7, 60, + bm1880_div_table_1, 0), + GATE_DIV(BM1880_CLK_EMMC, "clk_emmc", "clk_fpll", + BM1880_CLK_ENABLE0, 8, BM1880_CLK_DIV2, 16, 5, 15, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_100K_EMMC, "clk_100k_emmc", "clk_div_12m_usb", + BM1880_CLK_ENABLE0, 9, BM1880_CLK_DIV3, 16, 8, 120, + bm1880_div_table_2, 0), + GATE_DIV(BM1880_CLK_SD, "clk_sd", "clk_fpll", + BM1880_CLK_ENABLE0, 11, BM1880_CLK_DIV4, 16, 5, 15, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_100K_SD, "clk_100k_sd", "clk_div_12m_usb", + BM1880_CLK_ENABLE0, 12, BM1880_CLK_DIV5, 16, 8, 120, + bm1880_div_table_2, 0), + GATE_DIV(BM1880_CLK_500M_ETH0, "clk_500m_eth0", "clk_fpll", + BM1880_CLK_ENABLE0, 13, BM1880_CLK_DIV6, 16, 5, 3, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_500M_ETH1, "clk_500m_eth1", "clk_fpll", + BM1880_CLK_ENABLE0, 15, BM1880_CLK_DIV7, 16, 5, 3, + bm1880_div_table_0, 0), + /* Don't gate GPIO clocks as it is not owned by the GPIO driver */ + GATE_DIV(BM1880_CLK_GPIO_DB, "clk_gpio_db", "clk_div_12m_usb", + BM1880_CLK_ENABLE0, 20, BM1880_CLK_DIV8, 16, 16, 120, + bm1880_div_table_4, CLK_IGNORE_UNUSED), + GATE_DIV(BM1880_CLK_SDMA_AUD, "clk_sdma_aud", "clk_fpll", + BM1880_CLK_ENABLE0, 24, BM1880_CLK_DIV9, 16, 7, 61, + bm1880_div_table_1, 0), + GATE_DIV(BM1880_CLK_JPEG_AXI, "clk_jpeg_axi", "clk_fpll", + BM1880_CLK_ENABLE0, 28, BM1880_CLK_DIV10, 16, 5, 4, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_NF, "clk_nf", "clk_fpll", + BM1880_CLK_ENABLE0, 31, BM1880_CLK_DIV11, 16, 5, 30, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_TPU_AXI, "clk_tpu_axi", "clk_spll", + BM1880_CLK_ENABLE1, 3, BM1880_CLK_DIV14, 16, 5, 1, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_125M_USB, "clk_125m_usb", "clk_fpll", + BM1880_CLK_ENABLE1, 9, BM1880_CLK_DIV16, 16, 5, 12, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_33K_USB, "clk_33k_usb", "clk_div_12m_usb", + BM1880_CLK_ENABLE1, 10, BM1880_CLK_DIV17, 16, 9, 363, + bm1880_div_table_3, 0), + GATE_DIV(BM1880_CLK_VIDEO_AXI, "clk_video_axi", "clk_fpll", + BM1880_CLK_ENABLE1, 13, BM1880_CLK_DIV19, 16, 5, 4, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_VPP_AXI, "clk_vpp_axi", "clk_fpll", + BM1880_CLK_ENABLE1, 14, BM1880_CLK_DIV20, 16, 5, 4, + bm1880_div_table_0, 0), + GATE_MUX(BM1880_CLK_AXI1, "clk_axi1", clk_axi1_parents, + BM1880_CLK_ENABLE1, 15, BM1880_CLK_SELECT, 2, 0), + GATE_DIV(BM1880_CLK_AXI2, "clk_axi2", "clk_fpll", + BM1880_CLK_ENABLE1, 17, BM1880_CLK_DIV23, 16, 5, 3, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_AXI3, "clk_axi3", "clk_mux_rv", + BM1880_CLK_ENABLE1, 18, BM1880_CLK_DIV24, 16, 5, 2, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_AXI4, "clk_axi4", "clk_fpll", + BM1880_CLK_ENABLE1, 19, BM1880_CLK_DIV25, 16, 5, 6, + bm1880_div_table_0, 0), + GATE_DIV(BM1880_CLK_AXI5, "clk_axi5", "clk_fpll", + BM1880_CLK_ENABLE1, 20, BM1880_CLK_DIV26, 16, 5, 15, + bm1880_div_table_0, 0), +}; + +static unsigned long bm1880_pll_rate_calc(u32 regval, unsigned long parent_rate) +{ + u64 numerator; + u32 fbdiv, fref, refdiv; + u32 postdiv1, postdiv2, denominator; + + fbdiv = (regval >> 16) & 0xfff; + fref = parent_rate; + refdiv = regval & 0x1f; + postdiv1 = (regval >> 8) & 0x7; + postdiv2 = (regval >> 12) & 0x7; + + numerator = parent_rate * fbdiv; + denominator = refdiv * postdiv1 * postdiv2; + do_div(numerator, denominator); + + return (unsigned long)numerator; +} + +static unsigned long bm1880_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bm1880_pll_hw_clock *pll_hw = to_bm1880_pll_clk(hw); + unsigned long rate; + u32 regval; + + regval = readl(pll_hw->base + pll_hw->pll.reg); + rate = bm1880_pll_rate_calc(regval, parent_rate); + + return rate; +} + +static const struct clk_ops bm1880_pll_ops = { + .recalc_rate = bm1880_pll_recalc_rate, +}; + +static struct clk_hw *bm1880_clk_register_pll(struct bm1880_pll_hw_clock *pll_clk, + void __iomem *sys_base) +{ + struct clk_hw *hw; + int err; + + pll_clk->base = sys_base; + hw = &pll_clk->hw; + + err = clk_hw_register(NULL, hw); + if (err) + return ERR_PTR(err); + + return hw; +} + +static void bm1880_clk_unregister_pll(struct clk_hw *hw) +{ + struct bm1880_pll_hw_clock *pll_hw = to_bm1880_pll_clk(hw); + + clk_hw_unregister(hw); + kfree(pll_hw); +} + +static int bm1880_clk_register_plls(struct bm1880_pll_hw_clock *clks, + int num_clks, + struct bm1880_clock_data *data) +{ + struct clk_hw *hw; + void __iomem *pll_base = data->pll_base; + int i; + + for (i = 0; i < num_clks; i++) { + struct bm1880_pll_hw_clock *bm1880_clk = &clks[i]; + + hw = bm1880_clk_register_pll(bm1880_clk, pll_base); + if (IS_ERR(hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, bm1880_clk->pll.name); + goto err_clk; + } + + data->hw_data.hws[clks[i].pll.id] = hw; + } + + return 0; + +err_clk: + while (i--) + bm1880_clk_unregister_pll(data->hw_data.hws[clks[i].pll.id]); + + return PTR_ERR(hw); +} + +static int bm1880_clk_register_mux(const struct bm1880_mux_clock *clks, + int num_clks, + struct bm1880_clock_data *data) +{ + struct clk_hw *hw; + void __iomem *sys_base = data->sys_base; + int i; + + for (i = 0; i < num_clks; i++) { + hw = clk_hw_register_mux(NULL, clks[i].name, + clks[i].parents, + clks[i].num_parents, + clks[i].flags, + sys_base + clks[i].reg, + clks[i].shift, 1, 0, + &bm1880_clk_lock); + if (IS_ERR(hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err_clk; + } + + data->hw_data.hws[clks[i].id] = hw; + } + + return 0; + +err_clk: + while (i--) + clk_hw_unregister_mux(data->hw_data.hws[clks[i].id]); + + return PTR_ERR(hw); +} + +static unsigned long bm1880_clk_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); + struct bm1880_div_clock *div = &div_hw->div; + void __iomem *reg_addr = div_hw->base + div->reg; + unsigned int val; + unsigned long rate; + + if (!(readl(reg_addr) & BIT(3))) { + val = div->initval; + } else { + val = readl(reg_addr) >> div->shift; + val &= clk_div_mask(div->width); + } + + rate = divider_recalc_rate(hw, parent_rate, val, div->table, + div->flags, div->width); + + return rate; +} + +static long bm1880_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); + struct bm1880_div_clock *div = &div_hw->div; + void __iomem *reg_addr = div_hw->base + div->reg; + + if (div->flags & CLK_DIVIDER_READ_ONLY) { + u32 val; + + val = readl(reg_addr) >> div->shift; + val &= clk_div_mask(div->width); + + return divider_ro_round_rate(hw, rate, prate, div->table, + div->width, div->flags, + val); + } + + return divider_round_rate(hw, rate, prate, div->table, + div->width, div->flags); +} + +static int bm1880_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); + struct bm1880_div_clock *div = &div_hw->div; + void __iomem *reg_addr = div_hw->base + div->reg; + unsigned long flags = 0; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, div->table, + div->width, div_hw->div.flags); + if (value < 0) + return value; + + if (div_hw->lock) + spin_lock_irqsave(div_hw->lock, flags); + else + __acquire(div_hw->lock); + + val = readl(reg_addr); + val &= ~(clk_div_mask(div->width) << div_hw->div.shift); + val |= (u32)value << div->shift; + writel(val, reg_addr); + + if (div_hw->lock) + spin_unlock_irqrestore(div_hw->lock, flags); + else + __release(div_hw->lock); + + return 0; +} + +static const struct clk_ops bm1880_clk_div_ops = { + .recalc_rate = bm1880_clk_div_recalc_rate, + .round_rate = bm1880_clk_div_round_rate, + .set_rate = bm1880_clk_div_set_rate, +}; + +static struct clk_hw *bm1880_clk_register_div(struct bm1880_div_hw_clock *div_clk, + void __iomem *sys_base) +{ + struct clk_hw *hw; + int err; + + div_clk->div.flags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO; + div_clk->base = sys_base; + div_clk->lock = &bm1880_clk_lock; + + hw = &div_clk->hw; + err = clk_hw_register(NULL, hw); + if (err) + return ERR_PTR(err); + + return hw; +} + +static void bm1880_clk_unregister_div(struct clk_hw *hw) +{ + struct bm1880_div_hw_clock *div_hw = to_bm1880_div_clk(hw); + + clk_hw_unregister(hw); + kfree(div_hw); +} + +static int bm1880_clk_register_divs(struct bm1880_div_hw_clock *clks, + int num_clks, + struct bm1880_clock_data *data) +{ + struct clk_hw *hw; + void __iomem *sys_base = data->sys_base; + unsigned int i, id; + + for (i = 0; i < num_clks; i++) { + struct bm1880_div_hw_clock *bm1880_clk = &clks[i]; + + hw = bm1880_clk_register_div(bm1880_clk, sys_base); + if (IS_ERR(hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, bm1880_clk->div.name); + goto err_clk; + } + + id = clks[i].div.id; + data->hw_data.hws[id] = hw; + } + + return 0; + +err_clk: + while (i--) + bm1880_clk_unregister_div(data->hw_data.hws[clks[i].div.id]); + + return PTR_ERR(hw); +} + +static int bm1880_clk_register_gate(const struct bm1880_gate_clock *clks, + int num_clks, + struct bm1880_clock_data *data) +{ + struct clk_hw *hw; + void __iomem *sys_base = data->sys_base; + int i; + + for (i = 0; i < num_clks; i++) { + hw = clk_hw_register_gate(NULL, clks[i].name, + clks[i].parent, + clks[i].flags, + sys_base + clks[i].gate_reg, + clks[i].gate_shift, 0, + &bm1880_clk_lock); + if (IS_ERR(hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + goto err_clk; + } + + data->hw_data.hws[clks[i].id] = hw; + } + + return 0; + +err_clk: + while (i--) + clk_hw_unregister_gate(data->hw_data.hws[clks[i].id]); + + return PTR_ERR(hw); +} + +static struct clk_hw *bm1880_clk_register_composite(struct bm1880_composite_clock *clks, + void __iomem *sys_base) +{ + struct clk_hw *hw; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct bm1880_div_hw_clock *div_hws = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (clks->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = sys_base + clks->mux_reg; + mux->mask = 1; + mux->shift = clks->mux_shift; + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + mux->lock = &bm1880_clk_lock; + + parent_names = clks->parents; + num_parents = clks->num_parents; + } else { + parent = clks->parent; + parent_names = &parent; + num_parents = 1; + } + + if (clks->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = sys_base + clks->gate_reg; + gate->bit_idx = clks->gate_shift; + gate->lock = &bm1880_clk_lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (clks->div_shift >= 0) { + div_hws = kzalloc(sizeof(*div_hws), GFP_KERNEL); + if (!div_hws) { + ret = -ENOMEM; + goto err_out; + } + + div_hws->base = sys_base; + div_hws->div.reg = clks->div_reg; + div_hws->div.shift = clks->div_shift; + div_hws->div.width = clks->div_width; + div_hws->div.table = clks->table; + div_hws->div.initval = clks->div_initval; + div_hws->lock = &bm1880_clk_lock; + div_hws->div.flags = CLK_DIVIDER_ONE_BASED | + CLK_DIVIDER_ALLOW_ZERO; + + div_hw = &div_hws->hw; + div_ops = &bm1880_clk_div_ops; + } + + hw = clk_hw_register_composite(NULL, clks->name, parent_names, + num_parents, mux_hw, mux_ops, div_hw, + div_ops, gate_hw, gate_ops, + clks->flags); + + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_out; + } + + return hw; + +err_out: + kfree(div_hws); + kfree(gate); + kfree(mux); + + return ERR_PTR(ret); +} + +static int bm1880_clk_register_composites(struct bm1880_composite_clock *clks, + int num_clks, + struct bm1880_clock_data *data) +{ + struct clk_hw *hw; + void __iomem *sys_base = data->sys_base; + int i; + + for (i = 0; i < num_clks; i++) { + struct bm1880_composite_clock *bm1880_clk = &clks[i]; + + hw = bm1880_clk_register_composite(bm1880_clk, sys_base); + if (IS_ERR(hw)) { + pr_err("%s: failed to register clock %s\n", + __func__, bm1880_clk->name); + goto err_clk; + } + + data->hw_data.hws[clks[i].id] = hw; + } + + return 0; + +err_clk: + while (i--) + clk_hw_unregister_composite(data->hw_data.hws[clks[i].id]); + + return PTR_ERR(hw); +} + +static int bm1880_clk_probe(struct platform_device *pdev) +{ + struct bm1880_clock_data *clk_data; + void __iomem *pll_base, *sys_base; + struct device *dev = &pdev->dev; + struct resource *res; + int num_clks, i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pll_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pll_base)) + return PTR_ERR(pll_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + sys_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(sys_base)) + return PTR_ERR(sys_base); + + num_clks = ARRAY_SIZE(bm1880_pll_clks) + + ARRAY_SIZE(bm1880_div_clks) + + ARRAY_SIZE(bm1880_mux_clks) + + ARRAY_SIZE(bm1880_composite_clks) + + ARRAY_SIZE(bm1880_gate_clks); + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hw_data.hws, + num_clks), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->pll_base = pll_base; + clk_data->sys_base = sys_base; + + for (i = 0; i < num_clks; i++) + clk_data->hw_data.hws[i] = ERR_PTR(-ENOENT); + + clk_data->hw_data.num = num_clks; + + bm1880_clk_register_plls(bm1880_pll_clks, + ARRAY_SIZE(bm1880_pll_clks), + clk_data); + + bm1880_clk_register_divs(bm1880_div_clks, + ARRAY_SIZE(bm1880_div_clks), + clk_data); + + bm1880_clk_register_mux(bm1880_mux_clks, + ARRAY_SIZE(bm1880_mux_clks), + clk_data); + + bm1880_clk_register_composites(bm1880_composite_clks, + ARRAY_SIZE(bm1880_composite_clks), + clk_data); + + bm1880_clk_register_gate(bm1880_gate_clks, + ARRAY_SIZE(bm1880_gate_clks), + clk_data); + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + &clk_data->hw_data); +} + +static const struct of_device_id bm1880_of_match[] = { + { .compatible = "bitmain,bm1880-clk", }, + {} +}; +MODULE_DEVICE_TABLE(of, bm1880_of_match); + +static struct platform_driver bm1880_clk_driver = { + .driver = { + .name = "bm1880-clk", + .of_match_table = bm1880_of_match, + }, + .probe = bm1880_clk_probe, +}; +module_platform_driver(bm1880_clk_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_DESCRIPTION("Clock driver for Bitmain BM1880 SoC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 4f13a681ddfc..3e9c3e608769 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -207,7 +207,7 @@ struct clk_hw *clk_hw_register_composite(struct device *dev, const char *name, unsigned long flags) { struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_composite *composite; struct clk_ops *clk_composite_ops; int ret; @@ -343,3 +343,14 @@ void clk_unregister_composite(struct clk *clk) clk_unregister(clk); kfree(composite); } + +void clk_hw_unregister_composite(struct clk_hw *hw) +{ + struct clk_composite *composite; + + composite = to_clk_composite(hw); + + clk_hw_unregister(hw); + kfree(composite); +} +EXPORT_SYMBOL_GPL(clk_hw_unregister_composite); diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 3f9ff78c4a2a..098b2b01f0af 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -471,7 +471,7 @@ static struct clk_hw *_register_divider(struct device *dev, const char *name, { struct clk_divider *div; struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; int ret; if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index a7e4aef7a376..2c4486c09040 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -58,7 +58,7 @@ struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev, { struct clk_fixed_rate *fixed; struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; int ret; /* allocate fixed-rate clock */ diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 1b99fc962745..670053c58c1a 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -141,7 +141,7 @@ struct clk_hw *clk_hw_register_gate(struct device *dev, const char *name, { struct clk_gate *gate; struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; int ret; if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 9d930edd6516..13304cf5f2a8 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -280,7 +280,7 @@ static int gpio_clk_driver_probe(struct platform_device *pdev) else clk = clk_register_gpio_gate(&pdev->dev, node->name, parent_names ? parent_names[0] : NULL, gpiod, - 0); + CLK_SET_RATE_PARENT); if (IS_ERR(clk)) return PTR_ERR(clk); diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 66e91f740508..570b6e5b603b 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -153,7 +153,7 @@ struct clk_hw *clk_hw_register_mux_table(struct device *dev, const char *name, { struct clk_mux *mux; struct clk_hw *hw; - struct clk_init_data init; + struct clk_init_data init = {}; u8 width = 0; int ret; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 1c677d7f7f53..b68e200829f2 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1187,7 +1187,7 @@ static void clk_core_disable_unprepare(struct clk_core *core) clk_core_unprepare_lock(core); } -static void clk_unprepare_unused_subtree(struct clk_core *core) +static void __init clk_unprepare_unused_subtree(struct clk_core *core) { struct clk_core *child; @@ -1217,7 +1217,7 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) clk_pm_runtime_put(core); } -static void clk_disable_unused_subtree(struct clk_core *core) +static void __init clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; unsigned long flags; @@ -1263,7 +1263,7 @@ unprepare_out: clk_core_disable_unprepare(core->parent); } -static bool clk_ignore_unused; +static bool clk_ignore_unused __initdata; static int __init clk_ignore_unused_setup(char *__unused) { clk_ignore_unused = true; @@ -1271,7 +1271,7 @@ static int __init clk_ignore_unused_setup(char *__unused) } __setup("clk_ignore_unused", clk_ignore_unused_setup); -static int clk_disable_unused(void) +static int __init clk_disable_unused(void) { struct clk_core *core; @@ -1674,6 +1674,24 @@ static int clk_fetch_parent_index(struct clk_core *core, return i; } +/** + * clk_hw_get_parent_index - return the index of the parent clock + * @hw: clk_hw associated with the clk being consumed + * + * Fetches and returns the index of parent clock. Returns -EINVAL if the given + * clock does not have a current parent. + */ +int clk_hw_get_parent_index(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + if (WARN_ON(parent == NULL)) + return -EINVAL; + + return clk_fetch_parent_index(hw->core, parent->core); +} +EXPORT_SYMBOL_GPL(clk_hw_get_parent_index); + /* * Update the orphan status of @core and all its children. */ @@ -3879,6 +3897,7 @@ void clk_unregister(struct clk *clk) __func__, clk->core->name); kref_put(&clk->core->ref, __clk_release); + free_clk(clk); unlock: clk_prepare_unlock(); } diff --git a/drivers/clk/davinci/pll.c b/drivers/clk/davinci/pll.c index 1ac11b6a47a3..8a23d5dfd1f8 100644 --- a/drivers/clk/davinci/pll.c +++ b/drivers/clk/davinci/pll.c @@ -910,7 +910,6 @@ static int davinci_pll_probe(struct platform_device *pdev) struct davinci_pll_platform_data *pdata; const struct of_device_id *of_id; davinci_pll_init pll_init = NULL; - struct resource *res; void __iomem *base; of_id = of_match_device(davinci_pll_of_match, dev); @@ -930,8 +929,7 @@ static int davinci_pll_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/davinci/psc.c b/drivers/clk/davinci/psc.c index 5b69e24a224f..7387e7f6276e 100644 --- a/drivers/clk/davinci/psc.c +++ b/drivers/clk/davinci/psc.c @@ -531,7 +531,6 @@ static int davinci_psc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *of_id; const struct davinci_psc_init_data *init_data = NULL; - struct resource *res; void __iomem *base; int ret; @@ -546,8 +545,7 @@ static int davinci_psc_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/hisilicon/clk-hi3660.c b/drivers/clk/hisilicon/clk-hi3660.c index 5b3ad26dcc77..41f61726ab19 100644 --- a/drivers/clk/hisilicon/clk-hi3660.c +++ b/drivers/clk/hisilicon/clk-hi3660.c @@ -333,49 +333,49 @@ static const struct hisi_mux_clock hi3660_crgctrl_mux_clks[] = { static const struct hisi_divider_clock hi3660_crgctrl_divider_clks[] = { { HI3660_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0", - CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb0, 4, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_UART1, "clk_div_uart1", "clk_andgt_uart1", - CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb0, 8, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth", - CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb0, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_MMC, "clk_div_mmc", "clk_andgt_mmc", - CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb4, 3, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd", - CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb8, 0, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0", - CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xbc, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0", - CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xbc, 10, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio", - CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xc0, 0, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1", - CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xc0, 8, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi", - CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xc4, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_VENC, "clk_div_venc", "clk_andgt_venc", - CLK_SET_RATE_PARENT, 0xc8, 6, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xc8, 6, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_VDEC, "clk_div_vdec", "clk_andgt_vdec", - CLK_SET_RATE_PARENT, 0xcc, 0, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xcc, 0, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_vivobus_andgt", - CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xd0, 7, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xe8, 4, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_UFSPHY, "clk_div_ufsphy_cfg", "clk_gate_ufsphy_gt", - CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xe8, 9, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus", - CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xec, 0, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus", - CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xec, 2, 1, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus", - CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xec, 3, 1, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_UFSPERI, "clk_div_ufsperi", "clk_gate_ufs_subsys", - CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xec, 14, 1, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_AOMM, "clk_div_aomm", "clk_aomm_andgt", - CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x100, 7, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_ISP_SNCLK, "clk_isp_snclk_div", "clk_isp_snclk_fac", - CLK_SET_RATE_PARENT, 0x108, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x108, 0, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_mux_ioperi", - CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x108, 11, 4, CLK_DIVIDER_HIWORD_MASK, }, }; /* clk_pmuctrl */ @@ -420,13 +420,13 @@ static const struct hisi_gate_clock hi3660_sctrl_gate_clks[] = { { HI3660_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "clk_sw_mmbuf", CLK_SET_RATE_PARENT, 0x258, 7, CLK_GATE_HIWORD_MASK, }, { HI3660_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x260, 11, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_FLL_MMBUF_ANDGT, "clk_fll_mmbuf_andgt", "clk_fll_src", - CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x260, 12, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_SYS_MMBUF_ANDGT, "clk_sys_mmbuf_andgt", "clkin_sys", - CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x260, 13, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_GATE_PCIEPHY_GT, "clk_gate_pciephy_gt", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x268, 11, CLK_DIVIDER_HIWORD_MASK, }, }; static const char *const @@ -446,13 +446,13 @@ static const struct hisi_mux_clock hi3660_sctrl_mux_clks[] = { static const struct hisi_divider_clock hi3660_sctrl_divider_clks[] = { { HI3660_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt", - CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x258, 10, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt", - CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x258, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3660_CLK_DIV_PCIEPHY, "clk_div_pciephy", "clk_gate_pciephy_gt", - CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x268, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, }; /* clk_iomcu */ diff --git a/drivers/clk/hisilicon/clk-hi3670.c b/drivers/clk/hisilicon/clk-hi3670.c index fd8c837a6ea3..4d05a71683a5 100644 --- a/drivers/clk/hisilicon/clk-hi3670.c +++ b/drivers/clk/hisilicon/clk-hi3670.c @@ -295,61 +295,61 @@ static const struct hisi_gate_clock hi3670_crgctrl_gate_sep_clks[] = { static const struct hisi_gate_clock hi3670_crgctrl_gate_clks[] = { { HI3670_AUTODIV_SYSBUS, "autodiv_sysbus", "clk_div_sysbus", - CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x404, 5, CLK_GATE_HIWORD_MASK, }, { HI3670_AUTODIV_EMMC0BUS, "autodiv_emmc0bus", "autodiv_sysbus", - CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x404, 1, CLK_GATE_HIWORD_MASK, }, { HI3670_PCLK_ANDGT_MMC1_PCIE, "pclk_andgt_mmc1_pcie", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xf8, 13, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xf8, 13, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_GATE_VCODECBUS_GT, "clk_gate_vcodecbus_gt", "clk_mux_vcodecbus", - CLK_SET_RATE_PARENT, 0x0F0, 8, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0F0, 8, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_SD, "clk_andgt_sd", "clk_mux_sd_pll", - CLK_SET_RATE_PARENT, 0xF4, 3, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 3, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_SD_SYS_GT, "clk_sd_sys_gt", "clkin_sys", - CLK_SET_RATE_PARENT, 0xF4, 5, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 5, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_SDIO, "clk_andgt_sdio", "clk_mux_sdio_pll", - CLK_SET_RATE_PARENT, 0xF4, 8, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 8, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_SDIO_SYS_GT, "clk_sdio_sys_gt", "clkin_sys", - CLK_SET_RATE_PARENT, 0xF4, 6, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 6, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_A53HPM_ANDGT, "clk_a53hpm_andgt", "clk_mux_a53hpm", - CLK_SET_RATE_PARENT, 0x0F4, 7, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0F4, 7, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_320M_PLL_GT, "clk_320m_pll_gt", "clk_mux_320m", - CLK_SET_RATE_PARENT, 0xF8, 10, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF8, 10, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_UARTH, "clk_andgt_uarth", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xF4, 11, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 11, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_UARTL, "clk_andgt_uartl", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xF4, 10, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 10, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_UART0, "clk_andgt_uart0", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xF4, 9, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 9, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_SPI, "clk_andgt_spi", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xF4, 13, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 13, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_PCIEAXI, "clk_andgt_pcieaxi", "clk_mux_pcieaxi", - CLK_SET_RATE_PARENT, 0xfc, 15, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xfc, 15, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_DIV_AO_ASP_GT, "clk_div_ao_asp_gt", "clk_mux_ao_asp", - CLK_SET_RATE_PARENT, 0xF4, 4, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 4, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_GATE_CSI_TRANS, "clk_gate_csi_trans", "clk_ppll2", - CLK_SET_RATE_PARENT, 0xF4, 14, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 14, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_GATE_DSI_TRANS, "clk_gate_dsi_trans", "clk_ppll2", - CLK_SET_RATE_PARENT, 0xF4, 1, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF4, 1, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_PTP, "clk_andgt_ptp", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xF8, 5, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF8, 5, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_OUT0, "clk_andgt_out0", "clk_ppll0", - CLK_SET_RATE_PARENT, 0xF0, 10, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF0, 10, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_OUT1, "clk_andgt_out1", "clk_ppll0", - CLK_SET_RATE_PARENT, 0xF0, 11, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF0, 11, CLK_GATE_HIWORD_MASK, }, { HI3670_CLKGT_DP_AUDIO_PLL_AO, "clkgt_dp_audio_pll_ao", "clk_ppll6", - CLK_SET_RATE_PARENT, 0xF8, 15, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF8, 15, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_VDEC, "clk_andgt_vdec", "clk_mux_vdec", - CLK_SET_RATE_PARENT, 0xF0, 13, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF0, 13, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_VENC, "clk_andgt_venc", "clk_mux_venc", - CLK_SET_RATE_PARENT, 0xF0, 9, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xF0, 9, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ISP_SNCLK_ANGT, "clk_isp_snclk_angt", "clk_div_a53hpm", - CLK_SET_RATE_PARENT, 0x108, 2, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x108, 2, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_RXDPHY, "clk_andgt_rxdphy", "clk_div_a53hpm", - CLK_SET_RATE_PARENT, 0x0F0, 12, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0F0, 12, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_ICS, "clk_andgt_ics", "clk_mux_ics", - CLK_SET_RATE_PARENT, 0xf0, 14, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xf0, 14, CLK_GATE_HIWORD_MASK, }, { HI3670_AUTODIV_DMABUS, "autodiv_dmabus", "autodiv_sysbus", - CLK_SET_RATE_PARENT, 0x404, 3, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x404, 3, CLK_GATE_HIWORD_MASK, }, }; static const char *const @@ -485,57 +485,57 @@ static const struct hisi_mux_clock hi3670_crgctrl_mux_clks[] = { static const struct hisi_divider_clock hi3670_crgctrl_divider_clks[] = { { HI3670_CLK_DIV_CFGBUS, "clk_div_cfgbus", "clk_div_sysbus", - CLK_SET_RATE_PARENT, 0xEC, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xEC, 0, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_MMC0BUS, "clk_div_mmc0bus", "autodiv_emmc0bus", - CLK_SET_RATE_PARENT, 0x0EC, 2, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0EC, 2, 1, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_MMC1BUS, "clk_div_mmc1bus", "clk_div_sysbus", - CLK_SET_RATE_PARENT, 0x0EC, 3, 1, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0EC, 3, 1, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_PCLK_DIV_MMC1_PCIE, "pclk_div_mmc1_pcie", "pclk_andgt_mmc1_pcie", - CLK_SET_RATE_PARENT, 0xb4, 6, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb4, 6, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_VCODECBUS, "clk_div_vcodecbus", "clk_gate_vcodecbus_gt", - CLK_SET_RATE_PARENT, 0x0BC, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x0BC, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_SD, "clk_div_sd", "clk_andgt_sd", - CLK_SET_RATE_PARENT, 0xB8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xB8, 0, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_SDIO, "clk_div_sdio", "clk_andgt_sdio", - CLK_SET_RATE_PARENT, 0xC0, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xC0, 0, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_UARTH, "clk_div_uarth", "clk_andgt_uarth", - CLK_SET_RATE_PARENT, 0xB0, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xB0, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_UARTL, "clk_div_uartl", "clk_andgt_uartl", - CLK_SET_RATE_PARENT, 0xB0, 8, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xB0, 8, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_UART0, "clk_div_uart0", "clk_andgt_uart0", - CLK_SET_RATE_PARENT, 0xB0, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xB0, 4, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_I2C, "clk_div_i2c", "clk_div_320m", - CLK_SET_RATE_PARENT, 0xE8, 4, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xE8, 4, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_SPI, "clk_div_spi", "clk_andgt_spi", - CLK_SET_RATE_PARENT, 0xC4, 12, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xC4, 12, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_PCIEAXI, "clk_div_pcieaxi", "clk_andgt_pcieaxi", - CLK_SET_RATE_PARENT, 0xb4, 0, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xb4, 0, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_AO_ASP, "clk_div_ao_asp", "clk_div_ao_asp_gt", - CLK_SET_RATE_PARENT, 0x108, 6, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x108, 6, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_CSI_TRANS, "clk_div_csi_trans", "clk_gate_csi_trans", - CLK_SET_RATE_PARENT, 0xD4, 0, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xD4, 0, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_DSI_TRANS, "clk_div_dsi_trans", "clk_gate_dsi_trans", - CLK_SET_RATE_PARENT, 0xD4, 10, 5, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xD4, 10, 5, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_PTP, "clk_div_ptp", "clk_andgt_ptp", - CLK_SET_RATE_PARENT, 0xD8, 0, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xD8, 0, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_CLKOUT0_PLL, "clk_div_clkout0_pll", "clk_andgt_out0", - CLK_SET_RATE_PARENT, 0xe0, 4, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xe0, 4, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_CLKOUT1_PLL, "clk_div_clkout1_pll", "clk_andgt_out1", - CLK_SET_RATE_PARENT, 0xe0, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xe0, 10, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLKDIV_DP_AUDIO_PLL_AO, "clkdiv_dp_audio_pll_ao", "clkgt_dp_audio_pll_ao", - CLK_SET_RATE_PARENT, 0xBC, 11, 4, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xBC, 11, 4, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_VDEC, "clk_div_vdec", "clk_andgt_vdec", - CLK_SET_RATE_PARENT, 0xC4, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xC4, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_VENC, "clk_div_venc", "clk_andgt_venc", - CLK_SET_RATE_PARENT, 0xC0, 8, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xC0, 8, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_ISP_SNCLK_DIV0, "clk_isp_snclk_div0", "clk_isp_snclk_fac", - CLK_SET_RATE_PARENT, 0x108, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x108, 0, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_ISP_SNCLK_DIV1, "clk_isp_snclk_div1", "clk_isp_snclk_fac", - CLK_SET_RATE_PARENT, 0x10C, 14, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x10C, 14, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_ISP_SNCLK_DIV2, "clk_isp_snclk_div2", "clk_isp_snclk_fac", - CLK_SET_RATE_PARENT, 0x10C, 11, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x10C, 11, 2, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_ICS, "clk_div_ics", "clk_andgt_ics", - CLK_SET_RATE_PARENT, 0xE4, 9, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0xE4, 9, 6, CLK_DIVIDER_HIWORD_MASK, }, }; /* clk_pmuctrl */ @@ -608,12 +608,12 @@ static const struct hisi_gate_clock hi3670_sctrl_gate_sep_clks[] = { static const struct hisi_gate_clock hi3670_sctrl_gate_clks[] = { { HI3670_CLK_ANDGT_IOPERI, "clk_andgt_ioperi", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x270, 6, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x270, 6, CLK_GATE_HIWORD_MASK, }, { HI3670_CLKANDGT_ASP_SUBSYS_PERI, "clkandgt_asp_subsys_peri", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x268, 3, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x268, 3, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANGT_ASP_SUBSYS, "clk_angt_asp_subsys", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x258, 0, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x258, 0, CLK_GATE_HIWORD_MASK, }, }; static const char *const @@ -650,19 +650,19 @@ static const struct hisi_mux_clock hi3670_sctrl_mux_clks[] = { static const struct hisi_divider_clock hi3670_sctrl_divider_clks[] = { { HI3670_CLK_DIV_AOBUS, "clk_div_aobus", "clk_ppll0", - CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x254, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_UFS_SUBSYS, "clk_div_ufs_subsys", "clk_mux_ufs_subsys", - CLK_SET_RATE_PARENT, 0x274, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x274, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_IOPERI, "clk_div_ioperi", "clk_andgt_ioperi", - CLK_SET_RATE_PARENT, 0x270, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x270, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_CLKOUT0_TCXO, "clk_div_clkout0_tcxo", "clkin_sys", - CLK_SET_RATE_PARENT, 0x254, 6, 3, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x254, 6, 3, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_CLKOUT1_TCXO, "clk_div_clkout1_tcxo", "clkin_sys", - CLK_SET_RATE_PARENT, 0x254, 9, 3, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x254, 9, 3, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_ASP_SUBSYS_PERI_DIV, "clk_asp_subsys_peri_div", "clkandgt_asp_subsys_peri", - CLK_SET_RATE_PARENT, 0x268, 0, 3, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x268, 0, 3, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_ASP_SUBSYS, "clk_div_asp_subsys", "clk_angt_asp_subsys", - CLK_SET_RATE_PARENT, 0x250, 0, 3, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x250, 0, 3, CLK_DIVIDER_HIWORD_MASK, }, }; /* clk_iomcu */ @@ -732,17 +732,17 @@ static const struct hisi_gate_clock hi3670_media1_gate_sep_clks[] = { static const struct hisi_gate_clock hi3670_media1_gate_clks[] = { { HI3670_CLK_GATE_VIVOBUS_ANDGT, "clk_gate_vivobus_andgt", "clk_mux_vivobus", - CLK_SET_RATE_PARENT, 0x84, 3, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 3, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_EDC0, "clk_andgt_edc0", "clk_mux_edc0", - CLK_SET_RATE_PARENT, 0x84, 7, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 7, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_LDI0, "clk_andgt_ldi0", "clk_mux_ldi0", - CLK_SET_RATE_PARENT, 0x84, 9, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 9, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_ANDGT_LDI1, "clk_andgt_ldi1", "clk_mux_ldi1", - CLK_SET_RATE_PARENT, 0x84, 8, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 8, CLK_GATE_HIWORD_MASK, }, { HI3670_CLK_MMBUF_PLL_ANDGT, "clk_mmbuf_pll_andgt", "clk_sw_mmbuf", - CLK_SET_RATE_PARENT, 0x84, 14, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 14, CLK_GATE_HIWORD_MASK, }, { HI3670_PCLK_MMBUF_ANDGT, "pclk_mmbuf_andgt", "aclk_div_mmbuf", - CLK_SET_RATE_PARENT, 0x84, 15, CLK_GATE_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x84, 15, CLK_GATE_HIWORD_MASK, }, }; static const char *const @@ -799,17 +799,17 @@ static const struct hisi_mux_clock hi3670_media1_mux_clks[] = { static const struct hisi_divider_clock hi3670_media1_divider_clks[] = { { HI3670_CLK_DIV_VIVOBUS, "clk_div_vivobus", "clk_gate_vivobus_andgt", - CLK_SET_RATE_PARENT, 0x74, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x74, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_EDC0, "clk_div_edc0", "clk_andgt_edc0", - CLK_SET_RATE_PARENT, 0x68, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x68, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_LDI0, "clk_div_ldi0", "clk_andgt_ldi0", - CLK_SET_RATE_PARENT, 0x60, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x60, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_CLK_DIV_LDI1, "clk_div_ldi1", "clk_andgt_ldi1", - CLK_SET_RATE_PARENT, 0x64, 0, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x64, 0, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_ACLK_DIV_MMBUF, "aclk_div_mmbuf", "clk_mmbuf_pll_andgt", - CLK_SET_RATE_PARENT, 0x7C, 10, 6, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x7C, 10, 6, CLK_DIVIDER_HIWORD_MASK, }, { HI3670_PCLK_DIV_MMBUF, "pclk_div_mmbuf", "pclk_mmbuf_andgt", - CLK_SET_RATE_PARENT, 0x78, 0, 2, CLK_DIVIDER_HIWORD_MASK, 0, }, + CLK_SET_RATE_PARENT, 0x78, 0, 2, CLK_DIVIDER_HIWORD_MASK, }, }; /* clk_media2 */ diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index b2c5b6bbb1c1..e7cdf72d4b06 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -86,7 +86,8 @@ static void __init hi6220_clk_ao_init(struct device_node *np) hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); } -CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); +/* Allow reset driver to probe as well */ +CLK_OF_DECLARE_DRIVER(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); /* clocks in sysctrl */ diff --git a/drivers/clk/hisilicon/reset.c b/drivers/clk/hisilicon/reset.c index 2e22fea2a2e7..93cee17db8b1 100644 --- a/drivers/clk/hisilicon/reset.c +++ b/drivers/clk/hisilicon/reset.c @@ -90,14 +90,12 @@ static const struct reset_control_ops hisi_reset_ops = { struct hisi_reset_controller *hisi_reset_init(struct platform_device *pdev) { struct hisi_reset_controller *rstc; - struct resource *res; rstc = devm_kmalloc(&pdev->dev, sizeof(*rstc), GFP_KERNEL); if (!rstc) return NULL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rstc->membase = devm_ioremap_resource(&pdev->dev, res); + rstc->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rstc->membase)) return NULL; diff --git a/drivers/clk/imgtec/clk-boston.c b/drivers/clk/imgtec/clk-boston.c index 33ab4ff61165..b00cbd045af5 100644 --- a/drivers/clk/imgtec/clk-boston.c +++ b/drivers/clk/imgtec/clk-boston.c @@ -58,8 +58,7 @@ static void __init clk_boston_setup(struct device_node *np) cpu_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV); cpu_freq = mult_frac(in_freq, mul, cpu_div); - onecell = kzalloc(sizeof(*onecell) + - (BOSTON_CLK_COUNT * sizeof(struct clk_hw *)), + onecell = kzalloc(struct_size(onecell, hws, BOSTON_CLK_COUNT), GFP_KERNEL); if (!onecell) return; diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c index 5f3e92c09a5e..8e8288bda4d0 100644 --- a/drivers/clk/imx/clk-imx6sll.c +++ b/drivers/clk/imx/clk-imx6sll.c @@ -107,12 +107,12 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) hws[IMX6SLL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); - hws[IMX6SLL_CLK_CKIL] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ckil")); - hws[IMX6SLL_CLK_OSC] = __clk_get_hw(of_clk_get_by_name(ccm_node, "osc")); + hws[IMX6SLL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6SLL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); /* ipp_di clock is external input */ - hws[IMX6SLL_CLK_IPP_DI0] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di0")); - hws[IMX6SLL_CLK_IPP_DI1] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di1")); + hws[IMX6SLL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6SLL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); np = of_find_compatible_node(NULL, NULL, "fsl,imx6sll-anatop"); base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index c4685c01929a..89ba71271e5c 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -139,16 +139,16 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) hws[IMX6SX_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); - hws[IMX6SX_CLK_CKIL] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ckil")); - hws[IMX6SX_CLK_OSC] = __clk_get_hw(of_clk_get_by_name(ccm_node, "osc")); + hws[IMX6SX_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6SX_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); /* ipp_di clock is external input */ - hws[IMX6SX_CLK_IPP_DI0] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di0")); - hws[IMX6SX_CLK_IPP_DI1] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di1")); + hws[IMX6SX_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6SX_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); /* Clock source from external clock via CLK1/2 PAD */ - hws[IMX6SX_CLK_ANACLK1] = __clk_get_hw(of_clk_get_by_name(ccm_node, "anaclk1")); - hws[IMX6SX_CLK_ANACLK2] = __clk_get_hw(of_clk_get_by_name(ccm_node, "anaclk2")); + hws[IMX6SX_CLK_ANACLK1] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk1"); + hws[IMX6SX_CLK_ANACLK2] = imx_obtain_fixed_clk_hw(ccm_node, "anaclk2"); np = of_find_compatible_node(NULL, NULL, "fsl,imx6sx-anatop"); base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index bc931988fe7b..dafc8806b03e 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -126,12 +126,12 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) hws[IMX6UL_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); - hws[IMX6UL_CLK_CKIL] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ckil")); - hws[IMX6UL_CLK_OSC] = __clk_get_hw(of_clk_get_by_name(ccm_node, "osc")); + hws[IMX6UL_CLK_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); + hws[IMX6UL_CLK_OSC] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); /* ipp_di clock is external input */ - hws[IMX6UL_CLK_IPP_DI0] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di0")); - hws[IMX6UL_CLK_IPP_DI1] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ipp_di1")); + hws[IMX6UL_CLK_IPP_DI0] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di0"); + hws[IMX6UL_CLK_IPP_DI1] = imx_obtain_fixed_clk_hw(ccm_node, "ipp_di1"); np = of_find_compatible_node(NULL, NULL, "fsl,imx6ul-anatop"); base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index fbea774ef687..0c9f7adb41ae 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -403,8 +403,8 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) hws = clk_hw_data->hws; hws[IMX7D_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); - hws[IMX7D_OSC_24M_CLK] = __clk_get_hw(of_clk_get_by_name(ccm_node, "osc")); - hws[IMX7D_CKIL] = __clk_get_hw(of_clk_get_by_name(ccm_node, "ckil")); + hws[IMX7D_OSC_24M_CLK] = imx_obtain_fixed_clk_hw(ccm_node, "osc"); + hws[IMX7D_CKIL] = imx_obtain_fixed_clk_hw(ccm_node, "ckil"); np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-anatop"); base = of_iomap(np, 0); diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c index 2022d9bead91..3fdf3d494f0a 100644 --- a/drivers/clk/imx/clk-imx7ulp.c +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -24,11 +24,11 @@ static const char * const spll_pfd_sels[] = { "spll_pfd0", "spll_pfd1", "spll_pf static const char * const spll_sels[] = { "spll", "spll_pfd_sel", }; static const char * const apll_pfd_sels[] = { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", }; static const char * const apll_sels[] = { "apll", "apll_pfd_sel", }; -static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", }; -static const char * const ddr_sels[] = { "apll_pfd_sel", "upll", }; +static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "dummy", }; +static const char * const ddr_sels[] = { "apll_pfd_sel", "dummy", "dummy", "dummy", }; static const char * const nic_sels[] = { "firc", "ddr_clk", }; static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", }; -static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", }; +static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "dummy", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", }; static const char * const arm_sels[] = { "divcore", "dummy", "dummy", "hsrun_divcore", }; /* used by sosc/sirc/firc/ddr/spll/apll dividers */ @@ -75,7 +75,6 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) clks[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc"); clks[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc"); clks[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc"); - clks[IMX7ULP_CLK_MIPI_PLL] = imx_obtain_fixed_clk_hw(np, "mpll"); clks[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll"); /* SCG1 */ @@ -118,7 +117,7 @@ static void __init imx7ulp_clk_scg1_init(struct device_node *np) clks[IMX7ULP_CLK_SYS_SEL] = imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels)); clks[IMX7ULP_CLK_HSRUN_SYS_SEL] = imx_clk_hw_mux2("hsrun_scs_sel", base + 0x1c, 24, 4, scs_sels, ARRAY_SIZE(scs_sels)); clks[IMX7ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels)); - clks[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + clks[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 2, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); clks[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT); clks[IMX7ULP_CLK_HSRUN_CORE_DIV] = imx_clk_hw_divider_flags("hsrun_divcore", "hsrun_scs_sel", base + 0x1c, 16, 4, CLK_SET_RATE_PARENT); diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 172589e94f60..030b15d7c0ce 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -26,73 +26,6 @@ static u32 share_count_disp; static u32 share_count_pdm; static u32 share_count_nand; -static const struct imx_pll14xx_rate_table imx8mm_pll1416x_tbl[] = { - PLL_1416X_RATE(1800000000U, 225, 3, 0), - PLL_1416X_RATE(1600000000U, 200, 3, 0), - PLL_1416X_RATE(1200000000U, 300, 3, 1), - PLL_1416X_RATE(1000000000U, 250, 3, 1), - PLL_1416X_RATE(800000000U, 200, 3, 1), - PLL_1416X_RATE(750000000U, 250, 2, 2), - PLL_1416X_RATE(700000000U, 350, 3, 2), - PLL_1416X_RATE(600000000U, 300, 3, 2), -}; - -static const struct imx_pll14xx_rate_table imx8mm_audiopll_tbl[] = { - PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), - PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), -}; - -static const struct imx_pll14xx_rate_table imx8mm_videopll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), - PLL_1443X_RATE(594000000U, 198, 2, 2, 0), -}; - -static const struct imx_pll14xx_rate_table imx8mm_drampll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), -}; - -static struct imx_pll14xx_clk imx8mm_audio_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_audiopll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_audiopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_video_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_videopll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_videopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_dram_pll = { - .type = PLL_1443X, - .rate_table = imx8mm_drampll_tbl, - .rate_count = ARRAY_SIZE(imx8mm_drampll_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_arm_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_gpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_vpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mm_sys_pll = { - .type = PLL_1416X, - .rate_table = imx8mm_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mm_pll1416x_tbl), -}; - static const char *pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char *audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; static const char *audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; @@ -101,8 +34,6 @@ static const char *dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; static const char *gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; static const char *vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; static const char *arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; -static const char *sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; -static const char *sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", }; static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; /* CCM ROOT */ @@ -392,20 +323,18 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) clks[IMX8MM_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MM_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MM_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MM_SYS_PLL1_REF_SEL] = imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MM_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx8mm_audio_pll); - clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mm_audio_pll); - clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mm_video_pll); - clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mm_dram_pll); - clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mm_gpu_pll); - clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mm_vpu_pll); - clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mm_arm_pll); - clks[IMX8MM_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mm_sys_pll); - clks[IMX8MM_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mm_sys_pll); - clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mm_sys_pll); + clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); + clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); + clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); + clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_pll); + clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); + clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); + clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); + clks[IMX8MM_SYS_PLL1] = imx_clk_fixed("sys_pll1", 800000000); + clks[IMX8MM_SYS_PLL2] = imx_clk_fixed("sys_pll2", 1000000000); + clks[IMX8MM_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll); /* PLL bypass out */ clks[IMX8MM_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); @@ -415,8 +344,6 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) clks[IMX8MM_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MM_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MM_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[IMX8MM_SYS_PLL1_BYPASS] = imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 28, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT); - clks[IMX8MM_SYS_PLL2_BYPASS] = imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 28, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MM_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); /* PLL out gate */ @@ -427,29 +354,48 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) clks[IMX8MM_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); clks[IMX8MM_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11); clks[IMX8MM_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); - clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11); - clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11); clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); - /* SYS PLL fixed output */ - clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); - clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); - clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); - clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); - clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); - clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); - clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); - clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + /* SYS PLL1 fixed output */ + clks[IMX8MM_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1", base + 0x94, 27); + clks[IMX8MM_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1", base + 0x94, 25); + clks[IMX8MM_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1", base + 0x94, 23); + clks[IMX8MM_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1", base + 0x94, 21); + clks[IMX8MM_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1", base + 0x94, 19); + clks[IMX8MM_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1", base + 0x94, 17); + clks[IMX8MM_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1", base + 0x94, 15); + clks[IMX8MM_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1", base + 0x94, 13); + clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1", base + 0x94, 11); + + clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20); + clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10); + clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8); + clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6); + clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5); + clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4); + clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3); + clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2); clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); - clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); - clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); - clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); - clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); - clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); - clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); - clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); - clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + /* SYS PLL2 fixed output */ + clks[IMX8MM_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2", base + 0x104, 27); + clks[IMX8MM_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2", base + 0x104, 25); + clks[IMX8MM_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2", base + 0x104, 23); + clks[IMX8MM_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2", base + 0x104, 21); + clks[IMX8MM_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2", base + 0x104, 19); + clks[IMX8MM_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2", base + 0x104, 17); + clks[IMX8MM_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2", base + 0x104, 15); + clks[IMX8MM_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2", base + 0x104, 13); + clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2", base + 0x104, 11); + + clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20); + clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10); + clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8); + clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6); + clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5); + clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4); + clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3); + clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2); clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); np = dev->of_node; diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 58b5acee3830..9f5a5a56b45e 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -25,89 +25,6 @@ static u32 share_count_disp; static u32 share_count_pdm; static u32 share_count_nand; -enum { - ARM_PLL, - GPU_PLL, - VPU_PLL, - SYS_PLL1, - SYS_PLL2, - SYS_PLL3, - DRAM_PLL, - AUDIO_PLL1, - AUDIO_PLL2, - VIDEO_PLL2, - NR_PLLS, -}; - -static const struct imx_pll14xx_rate_table imx8mn_pll1416x_tbl[] = { - PLL_1416X_RATE(1800000000U, 225, 3, 0), - PLL_1416X_RATE(1600000000U, 200, 3, 0), - PLL_1416X_RATE(1500000000U, 375, 3, 1), - PLL_1416X_RATE(1400000000U, 350, 3, 1), - PLL_1416X_RATE(1200000000U, 300, 3, 1), - PLL_1416X_RATE(1000000000U, 250, 3, 1), - PLL_1416X_RATE(800000000U, 200, 3, 1), - PLL_1416X_RATE(750000000U, 250, 2, 2), - PLL_1416X_RATE(700000000U, 350, 3, 2), - PLL_1416X_RATE(600000000U, 300, 3, 2), -}; - -static const struct imx_pll14xx_rate_table imx8mn_audiopll_tbl[] = { - PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), - PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), -}; - -static const struct imx_pll14xx_rate_table imx8mn_videopll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), - PLL_1443X_RATE(594000000U, 198, 2, 2, 0), -}; - -static const struct imx_pll14xx_rate_table imx8mn_drampll_tbl[] = { - PLL_1443X_RATE(650000000U, 325, 3, 2, 0), -}; - -static struct imx_pll14xx_clk imx8mn_audio_pll = { - .type = PLL_1443X, - .rate_table = imx8mn_audiopll_tbl, - .rate_count = ARRAY_SIZE(imx8mn_audiopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_video_pll = { - .type = PLL_1443X, - .rate_table = imx8mn_videopll_tbl, - .rate_count = ARRAY_SIZE(imx8mn_videopll_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_dram_pll = { - .type = PLL_1443X, - .rate_table = imx8mn_drampll_tbl, - .rate_count = ARRAY_SIZE(imx8mn_drampll_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_arm_pll = { - .type = PLL_1416X, - .rate_table = imx8mn_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_gpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mn_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_vpu_pll = { - .type = PLL_1416X, - .rate_table = imx8mn_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), -}; - -static struct imx_pll14xx_clk imx8mn_sys_pll = { - .type = PLL_1416X, - .rate_table = imx8mn_pll1416x_tbl, - .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), -}; - static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; @@ -116,8 +33,6 @@ static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_se static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; -static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; -static const char * const sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", }; static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", @@ -405,20 +320,18 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MN_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MN_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MN_SYS_PLL1_REF_SEL] = imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MN_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MN_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MN_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx8mn_audio_pll); - clks[IMX8MN_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mn_audio_pll); - clks[IMX8MN_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mn_video_pll); - clks[IMX8MN_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mn_dram_pll); - clks[IMX8MN_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mn_gpu_pll); - clks[IMX8MN_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mn_vpu_pll); - clks[IMX8MN_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mn_arm_pll); - clks[IMX8MN_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mn_sys_pll); - clks[IMX8MN_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mn_sys_pll); - clks[IMX8MN_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mn_sys_pll); + clks[IMX8MN_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); + clks[IMX8MN_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); + clks[IMX8MN_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); + clks[IMX8MN_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_pll); + clks[IMX8MN_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); + clks[IMX8MN_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); + clks[IMX8MN_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); + clks[IMX8MN_SYS_PLL1] = imx_clk_fixed("sys_pll1", 800000000); + clks[IMX8MN_SYS_PLL2] = imx_clk_fixed("sys_pll2", 1000000000); + clks[IMX8MN_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx_1416x_pll); /* PLL bypass out */ clks[IMX8MN_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); @@ -428,8 +341,6 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MN_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MN_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); - clks[IMX8MN_SYS_PLL1_BYPASS] = imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 28, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT); - clks[IMX8MN_SYS_PLL2_BYPASS] = imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 28, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT); clks[IMX8MN_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); /* PLL out gate */ @@ -440,29 +351,48 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); clks[IMX8MN_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11); clks[IMX8MN_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); - clks[IMX8MN_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11); - clks[IMX8MN_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11); clks[IMX8MN_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); - /* SYS PLL fixed output */ - clks[IMX8MN_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); - clks[IMX8MN_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); - clks[IMX8MN_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); - clks[IMX8MN_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); - clks[IMX8MN_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); - clks[IMX8MN_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); - clks[IMX8MN_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); - clks[IMX8MN_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + /* SYS PLL1 fixed output */ + clks[IMX8MN_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1", base + 0x94, 27); + clks[IMX8MN_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1", base + 0x94, 25); + clks[IMX8MN_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1", base + 0x94, 23); + clks[IMX8MN_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1", base + 0x94, 21); + clks[IMX8MN_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1", base + 0x94, 19); + clks[IMX8MN_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1", base + 0x94, 17); + clks[IMX8MN_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1", base + 0x94, 15); + clks[IMX8MN_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1", base + 0x94, 13); + clks[IMX8MN_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1", base + 0x94, 11); + + clks[IMX8MN_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20); + clks[IMX8MN_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10); + clks[IMX8MN_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8); + clks[IMX8MN_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6); + clks[IMX8MN_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5); + clks[IMX8MN_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4); + clks[IMX8MN_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3); + clks[IMX8MN_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2); clks[IMX8MN_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); - clks[IMX8MN_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); - clks[IMX8MN_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); - clks[IMX8MN_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); - clks[IMX8MN_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); - clks[IMX8MN_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); - clks[IMX8MN_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); - clks[IMX8MN_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); - clks[IMX8MN_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + /* SYS PLL2 fixed output */ + clks[IMX8MN_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2", base + 0x104, 27); + clks[IMX8MN_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2", base + 0x104, 25); + clks[IMX8MN_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2", base + 0x104, 23); + clks[IMX8MN_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2", base + 0x104, 21); + clks[IMX8MN_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2", base + 0x104, 19); + clks[IMX8MN_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2", base + 0x104, 17); + clks[IMX8MN_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2", base + 0x104, 15); + clks[IMX8MN_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2", base + 0x104, 13); + clks[IMX8MN_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2", base + 0x104, 11); + + clks[IMX8MN_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20); + clks[IMX8MN_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10); + clks[IMX8MN_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8); + clks[IMX8MN_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6); + clks[IMX8MN_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5); + clks[IMX8MN_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4); + clks[IMX8MN_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3); + clks[IMX8MN_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2); clks[IMX8MN_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); np = dev->of_node; diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 41fc9c63356e..5f10a606d836 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -34,10 +34,9 @@ static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; -static const char * const sys1_pll_out_sels[] = {"sys1_pll1_ref_sel", }; -static const char * const sys2_pll_out_sels[] = {"sys1_pll1_ref_sel", "sys2_pll1_ref_sel", }; -static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", "sys2_pll1_ref_sel", }; +static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", }; static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", }; +static const char * const video2_pll_out_sels[] = {"video2_pll1_ref_sel", }; /* CCM ROOT */ static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", @@ -307,10 +306,9 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MQ_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", base + 0x8, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MQ_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", base + 0x10, 16, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MQ_SYS1_PLL1_REF_SEL] = imx_clk_mux("sys1_pll1_ref_sel", base + 0x30, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); - clks[IMX8MQ_SYS2_PLL1_REF_SEL] = imx_clk_mux("sys2_pll1_ref_sel", base + 0x3c, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MQ_SYS3_PLL1_REF_SEL] = imx_clk_mux("sys3_pll1_ref_sel", base + 0x48, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MQ_DRAM_PLL1_REF_SEL] = imx_clk_mux("dram_pll1_ref_sel", base + 0x60, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MQ_VIDEO2_PLL1_REF_SEL] = imx_clk_mux("video2_pll1_ref_sel", base + 0x54, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MQ_ARM_PLL_REF_DIV] = imx_clk_divider("arm_pll_ref_div", "arm_pll_ref_sel", base + 0x28, 5, 6); clks[IMX8MQ_GPU_PLL_REF_DIV] = imx_clk_divider("gpu_pll_ref_div", "gpu_pll_ref_sel", base + 0x18, 5, 6); @@ -342,30 +340,53 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21); clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21); - clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL); - clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL); - clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL); + clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_fixed("sys1_pll_out", 800000000); + clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_fixed("sys2_pll_out", 1000000000); + clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 0, base + 0x48, CLK_IS_CRITICAL); clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL); - /* SYS PLL fixed output */ - clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20); - clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10); - clks[IMX8MQ_SYS1_PLL_100M] = imx_clk_fixed_factor("sys1_pll_100m", "sys1_pll_out", 1, 8); - clks[IMX8MQ_SYS1_PLL_133M] = imx_clk_fixed_factor("sys1_pll_133m", "sys1_pll_out", 1, 6); - clks[IMX8MQ_SYS1_PLL_160M] = imx_clk_fixed_factor("sys1_pll_160m", "sys1_pll_out", 1, 5); - clks[IMX8MQ_SYS1_PLL_200M] = imx_clk_fixed_factor("sys1_pll_200m", "sys1_pll_out", 1, 4); - clks[IMX8MQ_SYS1_PLL_266M] = imx_clk_fixed_factor("sys1_pll_266m", "sys1_pll_out", 1, 3); - clks[IMX8MQ_SYS1_PLL_400M] = imx_clk_fixed_factor("sys1_pll_400m", "sys1_pll_out", 1, 2); - clks[IMX8MQ_SYS1_PLL_800M] = imx_clk_fixed_factor("sys1_pll_800m", "sys1_pll_out", 1, 1); - - clks[IMX8MQ_SYS2_PLL_50M] = imx_clk_fixed_factor("sys2_pll_50m", "sys2_pll_out", 1, 20); - clks[IMX8MQ_SYS2_PLL_100M] = imx_clk_fixed_factor("sys2_pll_100m", "sys2_pll_out", 1, 10); - clks[IMX8MQ_SYS2_PLL_125M] = imx_clk_fixed_factor("sys2_pll_125m", "sys2_pll_out", 1, 8); - clks[IMX8MQ_SYS2_PLL_166M] = imx_clk_fixed_factor("sys2_pll_166m", "sys2_pll_out", 1, 6); - clks[IMX8MQ_SYS2_PLL_200M] = imx_clk_fixed_factor("sys2_pll_200m", "sys2_pll_out", 1, 5); - clks[IMX8MQ_SYS2_PLL_250M] = imx_clk_fixed_factor("sys2_pll_250m", "sys2_pll_out", 1, 4); - clks[IMX8MQ_SYS2_PLL_333M] = imx_clk_fixed_factor("sys2_pll_333m", "sys2_pll_out", 1, 3); - clks[IMX8MQ_SYS2_PLL_500M] = imx_clk_fixed_factor("sys2_pll_500m", "sys2_pll_out", 1, 2); - clks[IMX8MQ_SYS2_PLL_1000M] = imx_clk_fixed_factor("sys2_pll_1000m", "sys2_pll_out", 1, 1); + clks[IMX8MQ_VIDEO2_PLL_OUT] = imx_clk_sccg_pll("video2_pll_out", video2_pll_out_sels, ARRAY_SIZE(video2_pll_out_sels), 0, 0, 0, base + 0x54, 0); + + /* SYS PLL1 fixed output */ + clks[IMX8MQ_SYS1_PLL_40M_CG] = imx_clk_gate("sys1_pll_40m_cg", "sys1_pll_out", base + 0x30, 9); + clks[IMX8MQ_SYS1_PLL_80M_CG] = imx_clk_gate("sys1_pll_80m_cg", "sys1_pll_out", base + 0x30, 11); + clks[IMX8MQ_SYS1_PLL_100M_CG] = imx_clk_gate("sys1_pll_100m_cg", "sys1_pll_out", base + 0x30, 13); + clks[IMX8MQ_SYS1_PLL_133M_CG] = imx_clk_gate("sys1_pll_133m_cg", "sys1_pll_out", base + 0x30, 15); + clks[IMX8MQ_SYS1_PLL_160M_CG] = imx_clk_gate("sys1_pll_160m_cg", "sys1_pll_out", base + 0x30, 17); + clks[IMX8MQ_SYS1_PLL_200M_CG] = imx_clk_gate("sys1_pll_200m_cg", "sys1_pll_out", base + 0x30, 19); + clks[IMX8MQ_SYS1_PLL_266M_CG] = imx_clk_gate("sys1_pll_266m_cg", "sys1_pll_out", base + 0x30, 21); + clks[IMX8MQ_SYS1_PLL_400M_CG] = imx_clk_gate("sys1_pll_400m_cg", "sys1_pll_out", base + 0x30, 23); + clks[IMX8MQ_SYS1_PLL_800M_CG] = imx_clk_gate("sys1_pll_800m_cg", "sys1_pll_out", base + 0x30, 25); + + clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_40m_cg", 1, 20); + clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_80m_cg", 1, 10); + clks[IMX8MQ_SYS1_PLL_100M] = imx_clk_fixed_factor("sys1_pll_100m", "sys1_pll_100m_cg", 1, 8); + clks[IMX8MQ_SYS1_PLL_133M] = imx_clk_fixed_factor("sys1_pll_133m", "sys1_pll_133m_cg", 1, 6); + clks[IMX8MQ_SYS1_PLL_160M] = imx_clk_fixed_factor("sys1_pll_160m", "sys1_pll_160m_cg", 1, 5); + clks[IMX8MQ_SYS1_PLL_200M] = imx_clk_fixed_factor("sys1_pll_200m", "sys1_pll_200m_cg", 1, 4); + clks[IMX8MQ_SYS1_PLL_266M] = imx_clk_fixed_factor("sys1_pll_266m", "sys1_pll_266m_cg", 1, 3); + clks[IMX8MQ_SYS1_PLL_400M] = imx_clk_fixed_factor("sys1_pll_400m", "sys1_pll_400m_cg", 1, 2); + clks[IMX8MQ_SYS1_PLL_800M] = imx_clk_fixed_factor("sys1_pll_800m", "sys1_pll_800m_cg", 1, 1); + + /* SYS PLL2 fixed output */ + clks[IMX8MQ_SYS2_PLL_50M_CG] = imx_clk_gate("sys2_pll_50m_cg", "sys2_pll_out", base + 0x3c, 9); + clks[IMX8MQ_SYS2_PLL_100M_CG] = imx_clk_gate("sys2_pll_100m_cg", "sys2_pll_out", base + 0x3c, 11); + clks[IMX8MQ_SYS2_PLL_125M_CG] = imx_clk_gate("sys2_pll_125m_cg", "sys2_pll_out", base + 0x3c, 13); + clks[IMX8MQ_SYS2_PLL_166M_CG] = imx_clk_gate("sys2_pll_166m_cg", "sys2_pll_out", base + 0x3c, 15); + clks[IMX8MQ_SYS2_PLL_200M_CG] = imx_clk_gate("sys2_pll_200m_cg", "sys2_pll_out", base + 0x3c, 17); + clks[IMX8MQ_SYS2_PLL_250M_CG] = imx_clk_gate("sys2_pll_250m_cg", "sys2_pll_out", base + 0x3c, 19); + clks[IMX8MQ_SYS2_PLL_333M_CG] = imx_clk_gate("sys2_pll_333m_cg", "sys2_pll_out", base + 0x3c, 21); + clks[IMX8MQ_SYS2_PLL_500M_CG] = imx_clk_gate("sys2_pll_500m_cg", "sys2_pll_out", base + 0x3c, 23); + clks[IMX8MQ_SYS2_PLL_1000M_CG] = imx_clk_gate("sys2_pll_1000m_cg", "sys2_pll_out", base + 0x3c, 25); + + clks[IMX8MQ_SYS2_PLL_50M] = imx_clk_fixed_factor("sys2_pll_50m", "sys2_pll_50m_cg", 1, 20); + clks[IMX8MQ_SYS2_PLL_100M] = imx_clk_fixed_factor("sys2_pll_100m", "sys2_pll_100m_cg", 1, 10); + clks[IMX8MQ_SYS2_PLL_125M] = imx_clk_fixed_factor("sys2_pll_125m", "sys2_pll_125m_cg", 1, 8); + clks[IMX8MQ_SYS2_PLL_166M] = imx_clk_fixed_factor("sys2_pll_166m", "sys2_pll_166m_cg", 1, 6); + clks[IMX8MQ_SYS2_PLL_200M] = imx_clk_fixed_factor("sys2_pll_200m", "sys2_pll_200m_cg", 1, 5); + clks[IMX8MQ_SYS2_PLL_250M] = imx_clk_fixed_factor("sys2_pll_250m", "sys2_pll_250m_cg", 1, 4); + clks[IMX8MQ_SYS2_PLL_333M] = imx_clk_fixed_factor("sys2_pll_333m", "sys2_pll_333m_cg", 1, 3); + clks[IMX8MQ_SYS2_PLL_500M] = imx_clk_fixed_factor("sys2_pll_500m", "sys2_pll_500m_cg", 1, 2); + clks[IMX8MQ_SYS2_PLL_1000M] = imx_clk_fixed_factor("sys2_pll_1000m", "sys2_pll_1000m_cg", 1, 1); np = dev->of_node; base = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 7a815ec76aa5..5c458199060a 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -41,6 +41,38 @@ struct clk_pll14xx { #define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw) +static const struct imx_pll14xx_rate_table imx_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1500000000U, 375, 3, 1), + PLL_1416X_RATE(1400000000U, 350, 3, 1), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +static const struct imx_pll14xx_rate_table imx_pll1443x_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), + PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), + PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), +}; + +struct imx_pll14xx_clk imx_1443x_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), +}; + +struct imx_pll14xx_clk imx_1416x_pll = { + .type = PLL_1416X, + .rate_table = imx_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), +}; + static const struct imx_pll14xx_rate_table *imx_get_pll_settings( struct clk_pll14xx *pll, unsigned long rate) { @@ -112,43 +144,17 @@ static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw, return fvco; } -static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate, +static inline bool clk_pll14xx_mp_change(const struct imx_pll14xx_rate_table *rate, u32 pll_div) { u32 old_mdiv, old_pdiv; - old_mdiv = (pll_div >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div >> PDIV_SHIFT) & PDIV_MASK; + old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT; + old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT; return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv; } -static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate, - u32 pll_div_ctl0, u32 pll_div_ctl1) -{ - u32 old_mdiv, old_pdiv, old_kdiv; - - old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; - old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; - - return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || - rate->kdiv != old_kdiv; -} - -static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate, - u32 pll_div_ctl0, u32 pll_div_ctl1) -{ - u32 old_mdiv, old_pdiv, old_kdiv; - - old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; - old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; - - return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || - rate->kdiv != old_kdiv; -} - static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) { u32 val; @@ -174,7 +180,7 @@ static int clk_pll1416x_set_rate(struct clk_hw *hw, unsigned long drate, tmp = readl_relaxed(pll->base + 4); - if (!clk_pll1416x_mp_change(rate, tmp)) { + if (!clk_pll14xx_mp_change(rate, tmp)) { tmp &= ~(SDIV_MASK) << SDIV_SHIFT; tmp |= rate->sdiv << SDIV_SHIFT; writel_relaxed(tmp, pll->base + 4); @@ -239,13 +245,15 @@ static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate, } tmp = readl_relaxed(pll->base + 4); - div_val = readl_relaxed(pll->base + 8); - if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) { + if (!clk_pll14xx_mp_change(rate, tmp)) { tmp &= ~(SDIV_MASK) << SDIV_SHIFT; tmp |= rate->sdiv << SDIV_SHIFT; writel_relaxed(tmp, pll->base + 4); + tmp = rate->kdiv << KDIV_SHIFT; + writel_relaxed(tmp, pll->base + 8); + return 0; } diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index f7a389a50401..bc5bb6ac8636 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -50,6 +50,9 @@ struct imx_pll14xx_clk { int flags; }; +extern struct imx_pll14xx_clk imx_1416x_pll; +extern struct imx_pll14xx_clk imx_1443x_pll; + #define imx_clk_cpu(name, parent_name, div, mux, pll, step) \ imx_clk_hw_cpu(name, parent_name, div, mux, pll, step)->clk diff --git a/drivers/clk/ingenic/Kconfig b/drivers/clk/ingenic/Kconfig index 1cb489959a99..b4555b465ea6 100644 --- a/drivers/clk/ingenic/Kconfig +++ b/drivers/clk/ingenic/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only menu "Ingenic SoCs drivers" - depends on MIPS + depends on MIPS || COMPILE_TEST config INGENIC_CGU_COMMON bool @@ -45,6 +45,16 @@ config INGENIC_CGU_JZ4780 If building for a JZ4780 SoC, you want to say Y here. +config INGENIC_CGU_X1000 + bool "Ingenic X1000 CGU driver" + default MACH_X1000 + select INGENIC_CGU_COMMON + help + Support the clocks provided by the CGU hardware on Ingenic X1000 + and compatible SoCs. + + If building for a X1000 SoC, you want to say Y here. + config INGENIC_TCU_CLK bool "Ingenic JZ47xx TCU clocks driver" default MACH_INGENIC diff --git a/drivers/clk/ingenic/Makefile b/drivers/clk/ingenic/Makefile index 097220b05131..8b1dad9b74a7 100644 --- a/drivers/clk/ingenic/Makefile +++ b/drivers/clk/ingenic/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_INGENIC_CGU_JZ4740) += jz4740-cgu.o obj-$(CONFIG_INGENIC_CGU_JZ4725B) += jz4725b-cgu.o obj-$(CONFIG_INGENIC_CGU_JZ4770) += jz4770-cgu.o obj-$(CONFIG_INGENIC_CGU_JZ4780) += jz4780-cgu.o +obj-$(CONFIG_INGENIC_CGU_X1000) += x1000-cgu.o obj-$(CONFIG_INGENIC_TCU_CLK) += tcu.o diff --git a/drivers/clk/ingenic/tcu.c b/drivers/clk/ingenic/tcu.c index a1a5f9cb439e..ad7daa494fd4 100644 --- a/drivers/clk/ingenic/tcu.c +++ b/drivers/clk/ingenic/tcu.c @@ -358,8 +358,7 @@ static int __init ingenic_tcu_probe(struct device_node *np) } } - tcu->clocks = kzalloc(sizeof(*tcu->clocks) + - sizeof(*tcu->clocks->hws) * TCU_CLK_COUNT, + tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT), GFP_KERNEL); if (!tcu->clocks) { ret = -ENOMEM; diff --git a/drivers/clk/ingenic/x1000-cgu.c b/drivers/clk/ingenic/x1000-cgu.c new file mode 100644 index 000000000000..b22d87b3f555 --- /dev/null +++ b/drivers/clk/ingenic/x1000-cgu.c @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * X1000 SoC CGU driver + * Copyright (c) 2019 Zhou Yanjie <zhouyanjie@zoho.com> + */ + +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <dt-bindings/clock/x1000-cgu.h> +#include "cgu.h" +#include "pm.h" + +/* CGU register offsets */ +#define CGU_REG_CPCCR 0x00 +#define CGU_REG_APLL 0x10 +#define CGU_REG_MPLL 0x14 +#define CGU_REG_CLKGR 0x20 +#define CGU_REG_OPCR 0x24 +#define CGU_REG_DDRCDR 0x2c +#define CGU_REG_MACCDR 0x54 +#define CGU_REG_I2SCDR 0x60 +#define CGU_REG_LPCDR 0x64 +#define CGU_REG_MSC0CDR 0x68 +#define CGU_REG_I2SCDR1 0x70 +#define CGU_REG_SSICDR 0x74 +#define CGU_REG_CIMCDR 0x7c +#define CGU_REG_PCMCDR 0x84 +#define CGU_REG_MSC1CDR 0xa4 +#define CGU_REG_CMP_INTR 0xb0 +#define CGU_REG_CMP_INTRE 0xb4 +#define CGU_REG_DRCG 0xd0 +#define CGU_REG_CPCSR 0xd4 +#define CGU_REG_PCMCDR1 0xe0 +#define CGU_REG_MACPHYC 0xe8 + +/* bits within the OPCR register */ +#define OPCR_SPENDN0 BIT(7) +#define OPCR_SPENDN1 BIT(6) + +static struct ingenic_cgu *cgu; + +static const s8 pll_od_encoding[8] = { + 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, +}; + +static const struct ingenic_cgu_clk_info x1000_cgu_clocks[] = { + + /* External clocks */ + + [X1000_CLK_EXCLK] = { "ext", CGU_CLK_EXT }, + [X1000_CLK_RTCLK] = { "rtc", CGU_CLK_EXT }, + + /* PLLs */ + + [X1000_CLK_APLL] = { + "apll", CGU_CLK_PLL, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .pll = { + .reg = CGU_REG_APLL, + .m_shift = 24, + .m_bits = 7, + .m_offset = 1, + .n_shift = 18, + .n_bits = 5, + .n_offset = 1, + .od_shift = 16, + .od_bits = 2, + .od_max = 8, + .od_encoding = pll_od_encoding, + .bypass_bit = 9, + .enable_bit = 8, + .stable_bit = 10, + }, + }, + + [X1000_CLK_MPLL] = { + "mpll", CGU_CLK_PLL, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .pll = { + .reg = CGU_REG_MPLL, + .m_shift = 24, + .m_bits = 7, + .m_offset = 1, + .n_shift = 18, + .n_bits = 5, + .n_offset = 1, + .od_shift = 16, + .od_bits = 2, + .od_max = 8, + .od_encoding = pll_od_encoding, + .bypass_bit = 6, + .enable_bit = 7, + .stable_bit = 0, + }, + }, + + /* Muxes & dividers */ + + [X1000_CLK_SCLKA] = { + "sclk_a", CGU_CLK_MUX, + .parents = { -1, X1000_CLK_EXCLK, X1000_CLK_APLL, -1 }, + .mux = { CGU_REG_CPCCR, 30, 2 }, + }, + + [X1000_CLK_CPUMUX] = { + "cpu_mux", CGU_CLK_MUX, + .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 }, + .mux = { CGU_REG_CPCCR, 28, 2 }, + }, + + [X1000_CLK_CPU] = { + "cpu", CGU_CLK_DIV, + .parents = { X1000_CLK_CPUMUX, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 }, + }, + + [X1000_CLK_L2CACHE] = { + "l2cache", CGU_CLK_DIV, + .parents = { X1000_CLK_CPUMUX, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 }, + }, + + [X1000_CLK_AHB0] = { + "ahb0", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 }, + .mux = { CGU_REG_CPCCR, 26, 2 }, + .div = { CGU_REG_CPCCR, 8, 1, 4, 21, -1, -1 }, + }, + + [X1000_CLK_AHB2PMUX] = { + "ahb2_apb_mux", CGU_CLK_MUX, + .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 }, + .mux = { CGU_REG_CPCCR, 24, 2 }, + }, + + [X1000_CLK_AHB2] = { + "ahb2", CGU_CLK_DIV, + .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 12, 1, 4, 20, -1, -1 }, + }, + + [X1000_CLK_PCLK] = { + "pclk", CGU_CLK_DIV, + .parents = { X1000_CLK_AHB2PMUX, -1, -1, -1 }, + .div = { CGU_REG_CPCCR, 16, 1, 4, 20, -1, -1 }, + }, + + [X1000_CLK_DDR] = { + "ddr", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { -1, X1000_CLK_SCLKA, X1000_CLK_MPLL, -1 }, + .mux = { CGU_REG_DDRCDR, 30, 2 }, + .div = { CGU_REG_DDRCDR, 0, 1, 4, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR, 31 }, + }, + + [X1000_CLK_MAC] = { + "mac", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL}, + .mux = { CGU_REG_MACCDR, 31, 1 }, + .div = { CGU_REG_MACCDR, 0, 1, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR, 25 }, + }, + + [X1000_CLK_MSCMUX] = { + "msc_mux", CGU_CLK_MUX, + .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL}, + .mux = { CGU_REG_MSC0CDR, 31, 1 }, + }, + + [X1000_CLK_MSC0] = { + "msc0", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { X1000_CLK_MSCMUX, -1, -1, -1 }, + .div = { CGU_REG_MSC0CDR, 0, 2, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR, 4 }, + }, + + [X1000_CLK_MSC1] = { + "msc1", CGU_CLK_DIV | CGU_CLK_GATE, + .parents = { X1000_CLK_MSCMUX, -1, -1, -1 }, + .div = { CGU_REG_MSC1CDR, 0, 2, 8, 29, 28, 27 }, + .gate = { CGU_REG_CLKGR, 5 }, + }, + + [X1000_CLK_SSIPLL] = { + "ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV, + .parents = { X1000_CLK_SCLKA, X1000_CLK_MPLL, -1, -1 }, + .mux = { CGU_REG_SSICDR, 31, 1 }, + .div = { CGU_REG_SSICDR, 0, 1, 8, 29, 28, 27 }, + }, + + [X1000_CLK_SSIMUX] = { + "ssi_mux", CGU_CLK_MUX, + .parents = { X1000_CLK_EXCLK, X1000_CLK_SSIPLL, -1, -1 }, + .mux = { CGU_REG_SSICDR, 30, 1 }, + }, + + /* Gate-only clocks */ + + [X1000_CLK_SFC] = { + "sfc", CGU_CLK_GATE, + .parents = { X1000_CLK_SSIPLL, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 2 }, + }, + + [X1000_CLK_I2C0] = { + "i2c0", CGU_CLK_GATE, + .parents = { X1000_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 7 }, + }, + + [X1000_CLK_I2C1] = { + "i2c1", CGU_CLK_GATE, + .parents = { X1000_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 8 }, + }, + + [X1000_CLK_I2C2] = { + "i2c2", CGU_CLK_GATE, + .parents = { X1000_CLK_PCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 9 }, + }, + + [X1000_CLK_UART0] = { + "uart0", CGU_CLK_GATE, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 14 }, + }, + + [X1000_CLK_UART1] = { + "uart1", CGU_CLK_GATE, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 15 }, + }, + + [X1000_CLK_UART2] = { + "uart2", CGU_CLK_GATE, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 16 }, + }, + + [X1000_CLK_SSI] = { + "ssi", CGU_CLK_GATE, + .parents = { X1000_CLK_SSIMUX, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 19 }, + }, + + [X1000_CLK_PDMA] = { + "pdma", CGU_CLK_GATE, + .parents = { X1000_CLK_EXCLK, -1, -1, -1 }, + .gate = { CGU_REG_CLKGR, 21 }, + }, +}; + +static void __init x1000_cgu_init(struct device_node *np) +{ + int retval; + + cgu = ingenic_cgu_new(x1000_cgu_clocks, + ARRAY_SIZE(x1000_cgu_clocks), np); + if (!cgu) { + pr_err("%s: failed to initialise CGU\n", __func__); + return; + } + + retval = ingenic_cgu_register_clocks(cgu); + if (retval) { + pr_err("%s: failed to register CGU Clocks\n", __func__); + return; + } + + ingenic_cgu_register_syscore_ops(cgu); +} +CLK_OF_DECLARE(x1000_cgu, "ingenic,x1000-cgu", x1000_cgu_init); diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c index 354c26f663b4..a3bd9a107209 100644 --- a/drivers/clk/mediatek/clk-mt2712.c +++ b/drivers/clk/mediatek/clk-mt2712.c @@ -1306,9 +1306,8 @@ static int clk_mt2712_top_probe(struct platform_device *pdev) int r, i; struct device_node *node = pdev->dev.of_node; void __iomem *base; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { pr_err("%s(): ioremap failed\n", __func__); return PTR_ERR(base); @@ -1394,9 +1393,8 @@ static int clk_mt2712_mcu_probe(struct platform_device *pdev) int r; struct device_node *node = pdev->dev.of_node; void __iomem *base; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { pr_err("%s(): ioremap failed\n", __func__); return PTR_ERR(base); diff --git a/drivers/clk/mediatek/clk-mt6779.c b/drivers/clk/mediatek/clk-mt6779.c index 608a9a6621a3..9766cccf5844 100644 --- a/drivers/clk/mediatek/clk-mt6779.c +++ b/drivers/clk/mediatek/clk-mt6779.c @@ -1225,12 +1225,11 @@ static int clk_mt6779_apmixed_probe(struct platform_device *pdev) static int clk_mt6779_top_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base; struct clk_onecell_data *clk_data; struct device_node *node = pdev->dev.of_node; - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/mediatek/clk-mt6797.c b/drivers/clk/mediatek/clk-mt6797.c index f62b0428da0e..f35389a11af1 100644 --- a/drivers/clk/mediatek/clk-mt6797.c +++ b/drivers/clk/mediatek/clk-mt6797.c @@ -385,9 +385,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) struct clk_onecell_data *clk_data; void __iomem *base; struct device_node *node = pdev->dev.of_node; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/mediatek/clk-mt7622.c b/drivers/clk/mediatek/clk-mt7622.c index 8190dab179d7..ef5947e15c75 100644 --- a/drivers/clk/mediatek/clk-mt7622.c +++ b/drivers/clk/mediatek/clk-mt7622.c @@ -614,9 +614,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) struct clk_onecell_data *clk_data; void __iomem *base; struct device_node *node = pdev->dev.of_node; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -695,9 +694,8 @@ static int mtk_pericfg_init(struct platform_device *pdev) void __iomem *base; int r; struct device_node *node = pdev->dev.of_node; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/mediatek/clk-mt7629.c b/drivers/clk/mediatek/clk-mt7629.c index d6233994af5a..b73bdf152836 100644 --- a/drivers/clk/mediatek/clk-mt7629.c +++ b/drivers/clk/mediatek/clk-mt7629.c @@ -574,9 +574,8 @@ static int mtk_topckgen_init(struct platform_device *pdev) struct clk_onecell_data *clk_data; void __iomem *base; struct device_node *node = pdev->dev.of_node; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -626,9 +625,8 @@ static int mtk_pericfg_init(struct platform_device *pdev) void __iomem *base; int r; struct device_node *node = pdev->dev.of_node; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/mediatek/clk-mt8183.c b/drivers/clk/mediatek/clk-mt8183.c index 51c8d5c9a030..5046852eb0fd 100644 --- a/drivers/clk/mediatek/clk-mt8183.c +++ b/drivers/clk/mediatek/clk-mt8183.c @@ -1189,11 +1189,10 @@ CLK_OF_DECLARE_DRIVER(mt8183_topckgen, "mediatek,mt8183-topckgen", static int clk_mt8183_top_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); void __iomem *base; struct device_node *node = pdev->dev.of_node; - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -1262,9 +1261,8 @@ static int clk_mt8183_mcu_probe(struct platform_device *pdev) struct clk_onecell_data *clk_data; struct device_node *node = pdev->dev.of_node; void __iomem *base; - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index 18b23cdf679c..53715e36326c 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -20,12 +20,7 @@ #include "clk-phase.h" #include "sclk-div.h" -#define AUD_MST_IN_COUNT 8 -#define AUD_SLV_SCLK_COUNT 10 -#define AUD_SLV_LRCLK_COUNT 10 - -#define AUD_GATE(_name, _reg, _bit, _phws, _iflags) \ -struct clk_regmap aud_##_name = { \ +#define AUD_GATE(_name, _reg, _bit, _pname, _iflags) { \ .data = &(struct clk_regmap_gate_data){ \ .offset = (_reg), \ .bit_idx = (_bit), \ @@ -33,14 +28,13 @@ struct clk_regmap aud_##_name = { \ .hw.init = &(struct clk_init_data) { \ .name = "aud_"#_name, \ .ops = &clk_regmap_gate_ops, \ - .parent_hws = (const struct clk_hw *[]) { &_phws.hw }, \ + .parent_names = (const char *[]){ #_pname }, \ .num_parents = 1, \ .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \ }, \ } -#define AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pdata, _iflags) \ -struct clk_regmap aud_##_name = { \ +#define AUD_MUX(_name, _reg, _mask, _shift, _dflags, _pdata, _iflags) { \ .data = &(struct clk_regmap_mux_data){ \ .offset = (_reg), \ .mask = (_mask), \ @@ -56,8 +50,7 @@ struct clk_regmap aud_##_name = { \ }, \ } -#define AUD_DIV(_name, _reg, _shift, _width, _dflags, _phws, _iflags) \ -struct clk_regmap aud_##_name = { \ +#define AUD_DIV(_name, _reg, _shift, _width, _dflags, _pname, _iflags) { \ .data = &(struct clk_regmap_div_data){ \ .offset = (_reg), \ .shift = (_shift), \ @@ -67,137 +60,27 @@ struct clk_regmap aud_##_name = { \ .hw.init = &(struct clk_init_data){ \ .name = "aud_"#_name, \ .ops = &clk_regmap_divider_ops, \ - .parent_hws = (const struct clk_hw *[]) { &_phws.hw }, \ + .parent_names = (const char *[]){ #_pname }, \ .num_parents = 1, \ .flags = (_iflags), \ }, \ } -#define AUD_PCLK_GATE(_name, _bit) \ -struct clk_regmap aud_##_name = { \ +#define AUD_PCLK_GATE(_name, _reg, _bit) { \ .data = &(struct clk_regmap_gate_data){ \ - .offset = (AUDIO_CLK_GATE_EN), \ + .offset = (_reg), \ .bit_idx = (_bit), \ }, \ .hw.init = &(struct clk_init_data) { \ .name = "aud_"#_name, \ .ops = &clk_regmap_gate_ops, \ - .parent_data = &(const struct clk_parent_data) { \ - .fw_name = "pclk", \ - }, \ + .parent_names = (const char *[]){ "aud_top" }, \ .num_parents = 1, \ }, \ } -/* Audio peripheral clocks */ -static AUD_PCLK_GATE(ddr_arb, 0); -static AUD_PCLK_GATE(pdm, 1); -static AUD_PCLK_GATE(tdmin_a, 2); -static AUD_PCLK_GATE(tdmin_b, 3); -static AUD_PCLK_GATE(tdmin_c, 4); -static AUD_PCLK_GATE(tdmin_lb, 5); -static AUD_PCLK_GATE(tdmout_a, 6); -static AUD_PCLK_GATE(tdmout_b, 7); -static AUD_PCLK_GATE(tdmout_c, 8); -static AUD_PCLK_GATE(frddr_a, 9); -static AUD_PCLK_GATE(frddr_b, 10); -static AUD_PCLK_GATE(frddr_c, 11); -static AUD_PCLK_GATE(toddr_a, 12); -static AUD_PCLK_GATE(toddr_b, 13); -static AUD_PCLK_GATE(toddr_c, 14); -static AUD_PCLK_GATE(loopback, 15); -static AUD_PCLK_GATE(spdifin, 16); -static AUD_PCLK_GATE(spdifout, 17); -static AUD_PCLK_GATE(resample, 18); -static AUD_PCLK_GATE(power_detect, 19); -static AUD_PCLK_GATE(spdifout_b, 21); - -/* Audio Master Clocks */ -static const struct clk_parent_data mst_mux_parent_data[] = { - { .fw_name = "mst_in0", }, - { .fw_name = "mst_in1", }, - { .fw_name = "mst_in2", }, - { .fw_name = "mst_in3", }, - { .fw_name = "mst_in4", }, - { .fw_name = "mst_in5", }, - { .fw_name = "mst_in6", }, - { .fw_name = "mst_in7", }, -}; - -#define AUD_MST_MUX(_name, _reg, _flag) \ - AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \ - mst_mux_parent_data, 0) - -#define AUD_MST_MCLK_MUX(_name, _reg) \ - AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST) - -#define AUD_MST_SYS_MUX(_name, _reg) \ - AUD_MST_MUX(_name, _reg, 0) - -static AUD_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL); -static AUD_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL); -static AUD_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL); -static AUD_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL); -static AUD_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL); -static AUD_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL); -static AUD_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); -static AUD_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); -static AUD_MST_SYS_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); -static AUD_MST_SYS_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); -static AUD_MST_MCLK_MUX(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); - -#define AUD_MST_DIV(_name, _reg, _flag) \ - AUD_DIV(_name##_div, _reg, 0, 16, _flag, \ - aud_##_name##_sel, CLK_SET_RATE_PARENT) \ - -#define AUD_MST_MCLK_DIV(_name, _reg) \ - AUD_MST_DIV(_name, _reg, CLK_DIVIDER_ROUND_CLOSEST) - -#define AUD_MST_SYS_DIV(_name, _reg) \ - AUD_MST_DIV(_name, _reg, 0) - -static AUD_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL); -static AUD_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL); -static AUD_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL); -static AUD_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL); -static AUD_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL); -static AUD_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL); -static AUD_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); -static AUD_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); -static AUD_MST_SYS_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); -static AUD_MST_SYS_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); -static AUD_MST_MCLK_DIV(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); - -#define AUD_MST_MCLK_GATE(_name, _reg) \ - AUD_GATE(_name, _reg, 31, aud_##_name##_div, \ - CLK_SET_RATE_PARENT) - -static AUD_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL); -static AUD_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL); -static AUD_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL); -static AUD_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL); -static AUD_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL); -static AUD_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL); -static AUD_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); -static AUD_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); -static AUD_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); -static AUD_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); -static AUD_MST_MCLK_GATE(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); - -/* Sample Clocks */ -#define AUD_MST_SCLK_PRE_EN(_name, _reg) \ - AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \ - aud_mst_##_name##_mclk, 0) - -static AUD_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0); -static AUD_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0); -static AUD_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0); -static AUD_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0); -static AUD_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0); -static AUD_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0); #define AUD_SCLK_DIV(_name, _reg, _div_shift, _div_width, \ - _hi_shift, _hi_width, _phws, _iflags) \ -struct clk_regmap aud_##_name = { \ + _hi_shift, _hi_width, _pname, _iflags) { \ .data = &(struct meson_sclk_div_data) { \ .div = { \ .reg_off = (_reg), \ @@ -213,38 +96,14 @@ struct clk_regmap aud_##_name = { \ .hw.init = &(struct clk_init_data) { \ .name = "aud_"#_name, \ .ops = &meson_sclk_div_ops, \ - .parent_hws = (const struct clk_hw *[]) { &_phws.hw }, \ + .parent_names = (const char *[]){ #_pname }, \ .num_parents = 1, \ .flags = (_iflags), \ }, \ } -#define AUD_MST_SCLK_DIV(_name, _reg) \ - AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \ - aud_mst_##_name##_sclk_pre_en, \ - CLK_SET_RATE_PARENT) - -static AUD_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); -static AUD_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); -static AUD_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); -static AUD_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); -static AUD_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); -static AUD_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); - -#define AUD_MST_SCLK_POST_EN(_name, _reg) \ - AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \ - aud_mst_##_name##_sclk_div, CLK_SET_RATE_PARENT) - -static AUD_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0); -static AUD_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0); -static AUD_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0); -static AUD_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0); -static AUD_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0); -static AUD_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0); - #define AUD_TRIPHASE(_name, _reg, _width, _shift0, _shift1, _shift2, \ - _phws, _iflags) \ -struct clk_regmap aud_##_name = { \ + _pname, _iflags) { \ .data = &(struct meson_clk_triphase_data) { \ .ph0 = { \ .reg_off = (_reg), \ @@ -265,52 +124,91 @@ struct clk_regmap aud_##_name = { \ .hw.init = &(struct clk_init_data) { \ .name = "aud_"#_name, \ .ops = &meson_clk_triphase_ops, \ - .parent_hws = (const struct clk_hw *[]) { &_phws.hw }, \ + .parent_names = (const char *[]){ #_pname }, \ .num_parents = 1, \ .flags = CLK_DUTY_CYCLE_PARENT | (_iflags), \ }, \ } +#define AUD_PHASE(_name, _reg, _width, _shift, _pname, _iflags) { \ + .data = &(struct meson_clk_phase_data) { \ + .ph = { \ + .reg_off = (_reg), \ + .shift = (_shift), \ + .width = (_width), \ + }, \ + }, \ + .hw.init = &(struct clk_init_data) { \ + .name = "aud_"#_name, \ + .ops = &meson_clk_phase_ops, \ + .parent_names = (const char *[]){ #_pname }, \ + .num_parents = 1, \ + .flags = (_iflags), \ + }, \ +} + +/* Audio Master Clocks */ +static const struct clk_parent_data mst_mux_parent_data[] = { + { .fw_name = "mst_in0", }, + { .fw_name = "mst_in1", }, + { .fw_name = "mst_in2", }, + { .fw_name = "mst_in3", }, + { .fw_name = "mst_in4", }, + { .fw_name = "mst_in5", }, + { .fw_name = "mst_in6", }, + { .fw_name = "mst_in7", }, +}; + +#define AUD_MST_MUX(_name, _reg, _flag) \ + AUD_MUX(_name##_sel, _reg, 0x7, 24, _flag, \ + mst_mux_parent_data, 0) +#define AUD_MST_DIV(_name, _reg, _flag) \ + AUD_DIV(_name##_div, _reg, 0, 16, _flag, \ + aud_##_name##_sel, CLK_SET_RATE_PARENT) +#define AUD_MST_MCLK_GATE(_name, _reg) \ + AUD_GATE(_name, _reg, 31, aud_##_name##_div, \ + CLK_SET_RATE_PARENT) + +#define AUD_MST_MCLK_MUX(_name, _reg) \ + AUD_MST_MUX(_name, _reg, CLK_MUX_ROUND_CLOSEST) +#define AUD_MST_MCLK_DIV(_name, _reg) \ + AUD_MST_DIV(_name, _reg, CLK_DIVIDER_ROUND_CLOSEST) + +#define AUD_MST_SYS_MUX(_name, _reg) \ + AUD_MST_MUX(_name, _reg, 0) +#define AUD_MST_SYS_DIV(_name, _reg) \ + AUD_MST_DIV(_name, _reg, 0) + +/* Sample Clocks */ +#define AUD_MST_SCLK_PRE_EN(_name, _reg) \ + AUD_GATE(mst_##_name##_sclk_pre_en, _reg, 31, \ + aud_mst_##_name##_mclk, 0) +#define AUD_MST_SCLK_DIV(_name, _reg) \ + AUD_SCLK_DIV(mst_##_name##_sclk_div, _reg, 20, 10, 0, 0, \ + aud_mst_##_name##_sclk_pre_en, \ + CLK_SET_RATE_PARENT) +#define AUD_MST_SCLK_POST_EN(_name, _reg) \ + AUD_GATE(mst_##_name##_sclk_post_en, _reg, 30, \ + aud_mst_##_name##_sclk_div, CLK_SET_RATE_PARENT) #define AUD_MST_SCLK(_name, _reg) \ AUD_TRIPHASE(mst_##_name##_sclk, _reg, 1, 0, 2, 4, \ aud_mst_##_name##_sclk_post_en, CLK_SET_RATE_PARENT) -static AUD_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1); -static AUD_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1); -static AUD_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1); -static AUD_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1); -static AUD_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1); -static AUD_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1); - #define AUD_MST_LRCLK_DIV(_name, _reg) \ AUD_SCLK_DIV(mst_##_name##_lrclk_div, _reg, 0, 10, 10, 10, \ - aud_mst_##_name##_sclk_post_en, 0) \ - -static AUD_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); -static AUD_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); -static AUD_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); -static AUD_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); -static AUD_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); -static AUD_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); - + aud_mst_##_name##_sclk_post_en, 0) #define AUD_MST_LRCLK(_name, _reg) \ AUD_TRIPHASE(mst_##_name##_lrclk, _reg, 1, 1, 3, 5, \ aud_mst_##_name##_lrclk_div, CLK_SET_RATE_PARENT) -static AUD_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1); -static AUD_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1); -static AUD_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1); -static AUD_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1); -static AUD_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1); -static AUD_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1); - +/* TDM bit clock sources */ static const struct clk_parent_data tdm_sclk_parent_data[] = { - { .hw = &aud_mst_a_sclk.hw, }, - { .hw = &aud_mst_b_sclk.hw, }, - { .hw = &aud_mst_c_sclk.hw, }, - { .hw = &aud_mst_d_sclk.hw, }, - { .hw = &aud_mst_e_sclk.hw, }, - { .hw = &aud_mst_f_sclk.hw, }, + { .name = "aud_mst_a_sclk", .index = -1, }, + { .name = "aud_mst_b_sclk", .index = -1, }, + { .name = "aud_mst_c_sclk", .index = -1, }, + { .name = "aud_mst_d_sclk", .index = -1, }, + { .name = "aud_mst_e_sclk", .index = -1, }, + { .name = "aud_mst_f_sclk", .index = -1, }, { .fw_name = "slv_sclk0", }, { .fw_name = "slv_sclk1", }, { .fw_name = "slv_sclk2", }, @@ -323,78 +221,14 @@ static const struct clk_parent_data tdm_sclk_parent_data[] = { { .fw_name = "slv_sclk9", }, }; -#define AUD_TDM_SCLK_MUX(_name, _reg) \ - AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \ - CLK_MUX_ROUND_CLOSEST, \ - tdm_sclk_parent_data, 0) - -static AUD_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL); -static AUD_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL); -static AUD_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL); -static AUD_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); -static AUD_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL); -static AUD_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL); -static AUD_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL); - -#define AUD_TDM_SCLK_PRE_EN(_name, _reg) \ - AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \ - aud_tdm##_name##_sclk_sel, CLK_SET_RATE_PARENT) - -static AUD_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); -static AUD_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); -static AUD_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); -static AUD_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); -static AUD_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); -static AUD_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); -static AUD_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); - -#define AUD_TDM_SCLK_POST_EN(_name, _reg) \ - AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \ - aud_tdm##_name##_sclk_pre_en, CLK_SET_RATE_PARENT) - -static AUD_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); -static AUD_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); -static AUD_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); -static AUD_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); -static AUD_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); -static AUD_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); -static AUD_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); - -#define AUD_TDM_SCLK(_name, _reg) \ - struct clk_regmap aud_tdm##_name##_sclk = { \ - .data = &(struct meson_clk_phase_data) { \ - .ph = { \ - .reg_off = (_reg), \ - .shift = 29, \ - .width = 1, \ - }, \ - }, \ - .hw.init = &(struct clk_init_data) { \ - .name = "aud_tdm"#_name"_sclk", \ - .ops = &meson_clk_phase_ops, \ - .parent_hws = (const struct clk_hw *[]) { \ - &aud_tdm##_name##_sclk_post_en.hw \ - }, \ - .num_parents = 1, \ - .flags = CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT, \ - }, \ -} - -static AUD_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL); -static AUD_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL); -static AUD_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL); -static AUD_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); -static AUD_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); -static AUD_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); -static AUD_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); - +/* TDM sample clock sources */ static const struct clk_parent_data tdm_lrclk_parent_data[] = { - { .hw = &aud_mst_a_lrclk.hw, }, - { .hw = &aud_mst_b_lrclk.hw, }, - { .hw = &aud_mst_c_lrclk.hw, }, - { .hw = &aud_mst_d_lrclk.hw, }, - { .hw = &aud_mst_e_lrclk.hw, }, - { .hw = &aud_mst_f_lrclk.hw, }, + { .name = "aud_mst_a_lrclk", .index = -1, }, + { .name = "aud_mst_b_lrclk", .index = -1, }, + { .name = "aud_mst_c_lrclk", .index = -1, }, + { .name = "aud_mst_d_lrclk", .index = -1, }, + { .name = "aud_mst_e_lrclk", .index = -1, }, + { .name = "aud_mst_f_lrclk", .index = -1, }, { .fw_name = "slv_lrclk0", }, { .fw_name = "slv_lrclk1", }, { .fw_name = "slv_lrclk2", }, @@ -407,69 +241,536 @@ static const struct clk_parent_data tdm_lrclk_parent_data[] = { { .fw_name = "slv_lrclk9", }, }; -#define AUD_TDM_LRLCK(_name, _reg) \ - AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \ - CLK_MUX_ROUND_CLOSEST, \ - tdm_lrclk_parent_data, 0) +#define AUD_TDM_SCLK_MUX(_name, _reg) \ + AUD_MUX(tdm##_name##_sclk_sel, _reg, 0xf, 24, \ + CLK_MUX_ROUND_CLOSEST, tdm_sclk_parent_data, 0) +#define AUD_TDM_SCLK_PRE_EN(_name, _reg) \ + AUD_GATE(tdm##_name##_sclk_pre_en, _reg, 31, \ + aud_tdm##_name##_sclk_sel, CLK_SET_RATE_PARENT) +#define AUD_TDM_SCLK_POST_EN(_name, _reg) \ + AUD_GATE(tdm##_name##_sclk_post_en, _reg, 30, \ + aud_tdm##_name##_sclk_pre_en, CLK_SET_RATE_PARENT) +#define AUD_TDM_SCLK(_name, _reg) \ + AUD_PHASE(tdm##_name##_sclk, _reg, 1, 29, \ + aud_tdm##_name##_sclk_post_en, \ + CLK_DUTY_CYCLE_PARENT | CLK_SET_RATE_PARENT) + +#define AUD_TDM_LRLCK(_name, _reg) \ + AUD_MUX(tdm##_name##_lrclk, _reg, 0xf, 20, \ + CLK_MUX_ROUND_CLOSEST, tdm_lrclk_parent_data, 0) + +/* Pad master clock sources */ +static const struct clk_parent_data mclk_pad_ctrl_parent_data[] = { + { .name = "aud_mst_a_mclk", .index = -1, }, + { .name = "aud_mst_b_mclk", .index = -1, }, + { .name = "aud_mst_c_mclk", .index = -1, }, + { .name = "aud_mst_d_mclk", .index = -1, }, + { .name = "aud_mst_e_mclk", .index = -1, }, + { .name = "aud_mst_f_mclk", .index = -1, }, +}; + +/* Pad bit clock sources */ +static const struct clk_parent_data sclk_pad_ctrl_parent_data[] = { + { .name = "aud_mst_a_sclk", .index = -1, }, + { .name = "aud_mst_b_sclk", .index = -1, }, + { .name = "aud_mst_c_sclk", .index = -1, }, + { .name = "aud_mst_d_sclk", .index = -1, }, + { .name = "aud_mst_e_sclk", .index = -1, }, + { .name = "aud_mst_f_sclk", .index = -1, }, +}; -static AUD_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL); -static AUD_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL); -static AUD_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL); -static AUD_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); -static AUD_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); -static AUD_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); -static AUD_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); +/* Pad sample clock sources */ +static const struct clk_parent_data lrclk_pad_ctrl_parent_data[] = { + { .name = "aud_mst_a_lrclk", .index = -1, }, + { .name = "aud_mst_b_lrclk", .index = -1, }, + { .name = "aud_mst_c_lrclk", .index = -1, }, + { .name = "aud_mst_d_lrclk", .index = -1, }, + { .name = "aud_mst_e_lrclk", .index = -1, }, + { .name = "aud_mst_f_lrclk", .index = -1, }, +}; -/* G12a Pad control */ #define AUD_TDM_PAD_CTRL(_name, _reg, _shift, _parents) \ - AUD_MUX(tdm_##_name, _reg, 0x7, _shift, 0, _parents, \ + AUD_MUX(_name, _reg, 0x7, _shift, 0, _parents, \ CLK_SET_RATE_NO_REPARENT) -static const struct clk_parent_data mclk_pad_ctrl_parent_data[] = { - { .hw = &aud_mst_a_mclk.hw }, - { .hw = &aud_mst_b_mclk.hw }, - { .hw = &aud_mst_c_mclk.hw }, - { .hw = &aud_mst_d_mclk.hw }, - { .hw = &aud_mst_e_mclk.hw }, - { .hw = &aud_mst_f_mclk.hw }, +/* Common Clocks */ +static struct clk_regmap ddr_arb = + AUD_PCLK_GATE(ddr_arb, AUDIO_CLK_GATE_EN, 0); +static struct clk_regmap pdm = + AUD_PCLK_GATE(pdm, AUDIO_CLK_GATE_EN, 1); +static struct clk_regmap tdmin_a = + AUD_PCLK_GATE(tdmin_a, AUDIO_CLK_GATE_EN, 2); +static struct clk_regmap tdmin_b = + AUD_PCLK_GATE(tdmin_b, AUDIO_CLK_GATE_EN, 3); +static struct clk_regmap tdmin_c = + AUD_PCLK_GATE(tdmin_c, AUDIO_CLK_GATE_EN, 4); +static struct clk_regmap tdmin_lb = + AUD_PCLK_GATE(tdmin_lb, AUDIO_CLK_GATE_EN, 5); +static struct clk_regmap tdmout_a = + AUD_PCLK_GATE(tdmout_a, AUDIO_CLK_GATE_EN, 6); +static struct clk_regmap tdmout_b = + AUD_PCLK_GATE(tdmout_b, AUDIO_CLK_GATE_EN, 7); +static struct clk_regmap tdmout_c = + AUD_PCLK_GATE(tdmout_c, AUDIO_CLK_GATE_EN, 8); +static struct clk_regmap frddr_a = + AUD_PCLK_GATE(frddr_a, AUDIO_CLK_GATE_EN, 9); +static struct clk_regmap frddr_b = + AUD_PCLK_GATE(frddr_b, AUDIO_CLK_GATE_EN, 10); +static struct clk_regmap frddr_c = + AUD_PCLK_GATE(frddr_c, AUDIO_CLK_GATE_EN, 11); +static struct clk_regmap toddr_a = + AUD_PCLK_GATE(toddr_a, AUDIO_CLK_GATE_EN, 12); +static struct clk_regmap toddr_b = + AUD_PCLK_GATE(toddr_b, AUDIO_CLK_GATE_EN, 13); +static struct clk_regmap toddr_c = + AUD_PCLK_GATE(toddr_c, AUDIO_CLK_GATE_EN, 14); +static struct clk_regmap loopback = + AUD_PCLK_GATE(loopback, AUDIO_CLK_GATE_EN, 15); +static struct clk_regmap spdifin = + AUD_PCLK_GATE(spdifin, AUDIO_CLK_GATE_EN, 16); +static struct clk_regmap spdifout = + AUD_PCLK_GATE(spdifout, AUDIO_CLK_GATE_EN, 17); +static struct clk_regmap resample = + AUD_PCLK_GATE(resample, AUDIO_CLK_GATE_EN, 18); +static struct clk_regmap power_detect = + AUD_PCLK_GATE(power_detect, AUDIO_CLK_GATE_EN, 19); + +static struct clk_regmap spdifout_clk_sel = + AUD_MST_MCLK_MUX(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static struct clk_regmap pdm_dclk_sel = + AUD_MST_MCLK_MUX(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static struct clk_regmap spdifin_clk_sel = + AUD_MST_SYS_MUX(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static struct clk_regmap pdm_sysclk_sel = + AUD_MST_SYS_MUX(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); +static struct clk_regmap spdifout_b_clk_sel = + AUD_MST_MCLK_MUX(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); + +static struct clk_regmap spdifout_clk_div = + AUD_MST_MCLK_DIV(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static struct clk_regmap pdm_dclk_div = + AUD_MST_MCLK_DIV(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static struct clk_regmap spdifin_clk_div = + AUD_MST_SYS_DIV(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static struct clk_regmap pdm_sysclk_div = + AUD_MST_SYS_DIV(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); +static struct clk_regmap spdifout_b_clk_div = + AUD_MST_MCLK_DIV(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); + +static struct clk_regmap spdifout_clk = + AUD_MST_MCLK_GATE(spdifout_clk, AUDIO_CLK_SPDIFOUT_CTRL); +static struct clk_regmap spdifin_clk = + AUD_MST_MCLK_GATE(spdifin_clk, AUDIO_CLK_SPDIFIN_CTRL); +static struct clk_regmap pdm_dclk = + AUD_MST_MCLK_GATE(pdm_dclk, AUDIO_CLK_PDMIN_CTRL0); +static struct clk_regmap pdm_sysclk = + AUD_MST_MCLK_GATE(pdm_sysclk, AUDIO_CLK_PDMIN_CTRL1); +static struct clk_regmap spdifout_b_clk = + AUD_MST_MCLK_GATE(spdifout_b_clk, AUDIO_CLK_SPDIFOUT_B_CTRL); + +static struct clk_regmap mst_a_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(a, AUDIO_MST_A_SCLK_CTRL0); +static struct clk_regmap mst_b_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(b, AUDIO_MST_B_SCLK_CTRL0); +static struct clk_regmap mst_c_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(c, AUDIO_MST_C_SCLK_CTRL0); +static struct clk_regmap mst_d_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(d, AUDIO_MST_D_SCLK_CTRL0); +static struct clk_regmap mst_e_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(e, AUDIO_MST_E_SCLK_CTRL0); +static struct clk_regmap mst_f_sclk_pre_en = + AUD_MST_SCLK_PRE_EN(f, AUDIO_MST_F_SCLK_CTRL0); + +static struct clk_regmap mst_a_sclk_div = + AUD_MST_SCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); +static struct clk_regmap mst_b_sclk_div = + AUD_MST_SCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); +static struct clk_regmap mst_c_sclk_div = + AUD_MST_SCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); +static struct clk_regmap mst_d_sclk_div = + AUD_MST_SCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); +static struct clk_regmap mst_e_sclk_div = + AUD_MST_SCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); +static struct clk_regmap mst_f_sclk_div = + AUD_MST_SCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); + +static struct clk_regmap mst_a_sclk_post_en = + AUD_MST_SCLK_POST_EN(a, AUDIO_MST_A_SCLK_CTRL0); +static struct clk_regmap mst_b_sclk_post_en = + AUD_MST_SCLK_POST_EN(b, AUDIO_MST_B_SCLK_CTRL0); +static struct clk_regmap mst_c_sclk_post_en = + AUD_MST_SCLK_POST_EN(c, AUDIO_MST_C_SCLK_CTRL0); +static struct clk_regmap mst_d_sclk_post_en = + AUD_MST_SCLK_POST_EN(d, AUDIO_MST_D_SCLK_CTRL0); +static struct clk_regmap mst_e_sclk_post_en = + AUD_MST_SCLK_POST_EN(e, AUDIO_MST_E_SCLK_CTRL0); +static struct clk_regmap mst_f_sclk_post_en = + AUD_MST_SCLK_POST_EN(f, AUDIO_MST_F_SCLK_CTRL0); + +static struct clk_regmap mst_a_sclk = + AUD_MST_SCLK(a, AUDIO_MST_A_SCLK_CTRL1); +static struct clk_regmap mst_b_sclk = + AUD_MST_SCLK(b, AUDIO_MST_B_SCLK_CTRL1); +static struct clk_regmap mst_c_sclk = + AUD_MST_SCLK(c, AUDIO_MST_C_SCLK_CTRL1); +static struct clk_regmap mst_d_sclk = + AUD_MST_SCLK(d, AUDIO_MST_D_SCLK_CTRL1); +static struct clk_regmap mst_e_sclk = + AUD_MST_SCLK(e, AUDIO_MST_E_SCLK_CTRL1); +static struct clk_regmap mst_f_sclk = + AUD_MST_SCLK(f, AUDIO_MST_F_SCLK_CTRL1); + +static struct clk_regmap mst_a_lrclk_div = + AUD_MST_LRCLK_DIV(a, AUDIO_MST_A_SCLK_CTRL0); +static struct clk_regmap mst_b_lrclk_div = + AUD_MST_LRCLK_DIV(b, AUDIO_MST_B_SCLK_CTRL0); +static struct clk_regmap mst_c_lrclk_div = + AUD_MST_LRCLK_DIV(c, AUDIO_MST_C_SCLK_CTRL0); +static struct clk_regmap mst_d_lrclk_div = + AUD_MST_LRCLK_DIV(d, AUDIO_MST_D_SCLK_CTRL0); +static struct clk_regmap mst_e_lrclk_div = + AUD_MST_LRCLK_DIV(e, AUDIO_MST_E_SCLK_CTRL0); +static struct clk_regmap mst_f_lrclk_div = + AUD_MST_LRCLK_DIV(f, AUDIO_MST_F_SCLK_CTRL0); + +static struct clk_regmap mst_a_lrclk = + AUD_MST_LRCLK(a, AUDIO_MST_A_SCLK_CTRL1); +static struct clk_regmap mst_b_lrclk = + AUD_MST_LRCLK(b, AUDIO_MST_B_SCLK_CTRL1); +static struct clk_regmap mst_c_lrclk = + AUD_MST_LRCLK(c, AUDIO_MST_C_SCLK_CTRL1); +static struct clk_regmap mst_d_lrclk = + AUD_MST_LRCLK(d, AUDIO_MST_D_SCLK_CTRL1); +static struct clk_regmap mst_e_lrclk = + AUD_MST_LRCLK(e, AUDIO_MST_E_SCLK_CTRL1); +static struct clk_regmap mst_f_lrclk = + AUD_MST_LRCLK(f, AUDIO_MST_F_SCLK_CTRL1); + +static struct clk_regmap tdmin_a_sclk_sel = + AUD_TDM_SCLK_MUX(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static struct clk_regmap tdmin_b_sclk_sel = + AUD_TDM_SCLK_MUX(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static struct clk_regmap tdmin_c_sclk_sel = + AUD_TDM_SCLK_MUX(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static struct clk_regmap tdmin_lb_sclk_sel = + AUD_TDM_SCLK_MUX(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static struct clk_regmap tdmout_a_sclk_sel = + AUD_TDM_SCLK_MUX(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static struct clk_regmap tdmout_b_sclk_sel = + AUD_TDM_SCLK_MUX(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static struct clk_regmap tdmout_c_sclk_sel = + AUD_TDM_SCLK_MUX(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +static struct clk_regmap tdmin_a_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static struct clk_regmap tdmin_b_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static struct clk_regmap tdmin_c_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static struct clk_regmap tdmin_lb_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static struct clk_regmap tdmout_a_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static struct clk_regmap tdmout_b_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static struct clk_regmap tdmout_c_sclk_pre_en = + AUD_TDM_SCLK_PRE_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +static struct clk_regmap tdmin_a_sclk_post_en = + AUD_TDM_SCLK_POST_EN(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static struct clk_regmap tdmin_b_sclk_post_en = + AUD_TDM_SCLK_POST_EN(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static struct clk_regmap tdmin_c_sclk_post_en = + AUD_TDM_SCLK_POST_EN(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static struct clk_regmap tdmin_lb_sclk_post_en = + AUD_TDM_SCLK_POST_EN(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static struct clk_regmap tdmout_a_sclk_post_en = + AUD_TDM_SCLK_POST_EN(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static struct clk_regmap tdmout_b_sclk_post_en = + AUD_TDM_SCLK_POST_EN(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static struct clk_regmap tdmout_c_sclk_post_en = + AUD_TDM_SCLK_POST_EN(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +static struct clk_regmap tdmin_a_sclk = + AUD_TDM_SCLK(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static struct clk_regmap tdmin_b_sclk = + AUD_TDM_SCLK(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static struct clk_regmap tdmin_c_sclk = + AUD_TDM_SCLK(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static struct clk_regmap tdmin_lb_sclk = + AUD_TDM_SCLK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static struct clk_regmap tdmout_a_sclk = + AUD_TDM_SCLK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static struct clk_regmap tdmout_b_sclk = + AUD_TDM_SCLK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static struct clk_regmap tdmout_c_sclk = + AUD_TDM_SCLK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +static struct clk_regmap tdmin_a_lrclk = + AUD_TDM_LRLCK(in_a, AUDIO_CLK_TDMIN_A_CTRL); +static struct clk_regmap tdmin_b_lrclk = + AUD_TDM_LRLCK(in_b, AUDIO_CLK_TDMIN_B_CTRL); +static struct clk_regmap tdmin_c_lrclk = + AUD_TDM_LRLCK(in_c, AUDIO_CLK_TDMIN_C_CTRL); +static struct clk_regmap tdmin_lb_lrclk = + AUD_TDM_LRLCK(in_lb, AUDIO_CLK_TDMIN_LB_CTRL); +static struct clk_regmap tdmout_a_lrclk = + AUD_TDM_LRLCK(out_a, AUDIO_CLK_TDMOUT_A_CTRL); +static struct clk_regmap tdmout_b_lrclk = + AUD_TDM_LRLCK(out_b, AUDIO_CLK_TDMOUT_B_CTRL); +static struct clk_regmap tdmout_c_lrclk = + AUD_TDM_LRLCK(out_c, AUDIO_CLK_TDMOUT_C_CTRL); + +/* AXG/G12A Clocks */ +static struct clk_hw axg_aud_top = { + .init = &(struct clk_init_data) { + /* Provide aud_top signal name on axg and g12a */ + .name = "aud_top", + .ops = &(const struct clk_ops) {}, + .parent_data = &(const struct clk_parent_data) { + .fw_name = "pclk", + }, + .num_parents = 1, + }, }; -static AUD_TDM_PAD_CTRL(mclk_pad_0, AUDIO_MST_PAD_CTRL0, 0, - mclk_pad_ctrl_parent_data); -static AUD_TDM_PAD_CTRL(mclk_pad_1, AUDIO_MST_PAD_CTRL0, 4, - mclk_pad_ctrl_parent_data); +static struct clk_regmap mst_a_mclk_sel = + AUD_MST_MCLK_MUX(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static struct clk_regmap mst_b_mclk_sel = + AUD_MST_MCLK_MUX(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static struct clk_regmap mst_c_mclk_sel = + AUD_MST_MCLK_MUX(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static struct clk_regmap mst_d_mclk_sel = + AUD_MST_MCLK_MUX(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static struct clk_regmap mst_e_mclk_sel = + AUD_MST_MCLK_MUX(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static struct clk_regmap mst_f_mclk_sel = + AUD_MST_MCLK_MUX(mst_f_mclk, AUDIO_MCLK_F_CTRL); + +static struct clk_regmap mst_a_mclk_div = + AUD_MST_MCLK_DIV(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static struct clk_regmap mst_b_mclk_div = + AUD_MST_MCLK_DIV(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static struct clk_regmap mst_c_mclk_div = + AUD_MST_MCLK_DIV(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static struct clk_regmap mst_d_mclk_div = + AUD_MST_MCLK_DIV(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static struct clk_regmap mst_e_mclk_div = + AUD_MST_MCLK_DIV(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static struct clk_regmap mst_f_mclk_div = + AUD_MST_MCLK_DIV(mst_f_mclk, AUDIO_MCLK_F_CTRL); + +static struct clk_regmap mst_a_mclk = + AUD_MST_MCLK_GATE(mst_a_mclk, AUDIO_MCLK_A_CTRL); +static struct clk_regmap mst_b_mclk = + AUD_MST_MCLK_GATE(mst_b_mclk, AUDIO_MCLK_B_CTRL); +static struct clk_regmap mst_c_mclk = + AUD_MST_MCLK_GATE(mst_c_mclk, AUDIO_MCLK_C_CTRL); +static struct clk_regmap mst_d_mclk = + AUD_MST_MCLK_GATE(mst_d_mclk, AUDIO_MCLK_D_CTRL); +static struct clk_regmap mst_e_mclk = + AUD_MST_MCLK_GATE(mst_e_mclk, AUDIO_MCLK_E_CTRL); +static struct clk_regmap mst_f_mclk = + AUD_MST_MCLK_GATE(mst_f_mclk, AUDIO_MCLK_F_CTRL); + +/* G12a clocks */ +static struct clk_regmap g12a_tdm_mclk_pad_0 = AUD_TDM_PAD_CTRL( + mclk_pad_0, AUDIO_MST_PAD_CTRL0, 0, mclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_mclk_pad_1 = AUD_TDM_PAD_CTRL( + mclk_pad_1, AUDIO_MST_PAD_CTRL0, 4, mclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_lrclk_pad_0 = AUD_TDM_PAD_CTRL( + lrclk_pad_0, AUDIO_MST_PAD_CTRL1, 16, lrclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_lrclk_pad_1 = AUD_TDM_PAD_CTRL( + lrclk_pad_1, AUDIO_MST_PAD_CTRL1, 20, lrclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_lrclk_pad_2 = AUD_TDM_PAD_CTRL( + lrclk_pad_2, AUDIO_MST_PAD_CTRL1, 24, lrclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_sclk_pad_0 = AUD_TDM_PAD_CTRL( + sclk_pad_0, AUDIO_MST_PAD_CTRL1, 0, sclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_sclk_pad_1 = AUD_TDM_PAD_CTRL( + sclk_pad_1, AUDIO_MST_PAD_CTRL1, 4, sclk_pad_ctrl_parent_data); +static struct clk_regmap g12a_tdm_sclk_pad_2 = AUD_TDM_PAD_CTRL( + sclk_pad_2, AUDIO_MST_PAD_CTRL1, 8, sclk_pad_ctrl_parent_data); + +/* G12a/SM1 clocks */ +static struct clk_regmap toram = + AUD_PCLK_GATE(toram, AUDIO_CLK_GATE_EN, 20); +static struct clk_regmap spdifout_b = + AUD_PCLK_GATE(spdifout_b, AUDIO_CLK_GATE_EN, 21); +static struct clk_regmap eqdrc = + AUD_PCLK_GATE(eqdrc, AUDIO_CLK_GATE_EN, 22); + +/* SM1 Clocks */ +static struct clk_regmap sm1_clk81_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = AUDIO_CLK81_EN, + .bit_idx = 31, + }, + .hw.init = &(struct clk_init_data) { + .name = "aud_clk81_en", + .ops = &clk_regmap_gate_ops, + .parent_data = &(const struct clk_parent_data) { + .fw_name = "pclk", + }, + .num_parents = 1, + }, +}; -static const struct clk_parent_data lrclk_pad_ctrl_parent_data[] = { - { .hw = &aud_mst_a_lrclk.hw }, - { .hw = &aud_mst_b_lrclk.hw }, - { .hw = &aud_mst_c_lrclk.hw }, - { .hw = &aud_mst_d_lrclk.hw }, - { .hw = &aud_mst_e_lrclk.hw }, - { .hw = &aud_mst_f_lrclk.hw }, +static struct clk_regmap sm1_sysclk_a_div = { + .data = &(struct clk_regmap_div_data){ + .offset = AUDIO_CLK81_CTRL, + .shift = 0, + .width = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "aud_sysclk_a_div", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_clk81_en.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, }; -static AUD_TDM_PAD_CTRL(lrclk_pad_0, AUDIO_MST_PAD_CTRL1, 16, - lrclk_pad_ctrl_parent_data); -static AUD_TDM_PAD_CTRL(lrclk_pad_1, AUDIO_MST_PAD_CTRL1, 20, - lrclk_pad_ctrl_parent_data); -static AUD_TDM_PAD_CTRL(lrclk_pad_2, AUDIO_MST_PAD_CTRL1, 24, - lrclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_sysclk_a_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = AUDIO_CLK81_CTRL, + .bit_idx = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "aud_sysclk_a_en", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_sysclk_a_div.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; -static const struct clk_parent_data sclk_pad_ctrl_parent_data[] = { - { .hw = &aud_mst_a_sclk.hw }, - { .hw = &aud_mst_b_sclk.hw }, - { .hw = &aud_mst_c_sclk.hw }, - { .hw = &aud_mst_d_sclk.hw }, - { .hw = &aud_mst_e_sclk.hw }, - { .hw = &aud_mst_f_sclk.hw }, +static struct clk_regmap sm1_sysclk_b_div = { + .data = &(struct clk_regmap_div_data){ + .offset = AUDIO_CLK81_CTRL, + .shift = 16, + .width = 8, + }, + .hw.init = &(struct clk_init_data) { + .name = "aud_sysclk_b_div", + .ops = &clk_regmap_divider_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_clk81_en.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, }; -static AUD_TDM_PAD_CTRL(sclk_pad_0, AUDIO_MST_PAD_CTRL1, 0, - sclk_pad_ctrl_parent_data); -static AUD_TDM_PAD_CTRL(sclk_pad_1, AUDIO_MST_PAD_CTRL1, 4, - sclk_pad_ctrl_parent_data); -static AUD_TDM_PAD_CTRL(sclk_pad_2, AUDIO_MST_PAD_CTRL1, 8, - sclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_sysclk_b_en = { + .data = &(struct clk_regmap_gate_data){ + .offset = AUDIO_CLK81_CTRL, + .bit_idx = 24, + }, + .hw.init = &(struct clk_init_data) { + .name = "aud_sysclk_b_en", + .ops = &clk_regmap_gate_ops, + .parent_hws = (const struct clk_hw *[]) { + &sm1_sysclk_b_div.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + }, +}; + +static const struct clk_hw *sm1_aud_top_parents[] = { + &sm1_sysclk_a_en.hw, + &sm1_sysclk_b_en.hw, +}; + +static struct clk_regmap sm1_aud_top = { + .data = &(struct clk_regmap_mux_data){ + .offset = AUDIO_CLK81_CTRL, + .mask = 0x1, + .shift = 31, + }, + .hw.init = &(struct clk_init_data){ + .name = "aud_top", + .ops = &clk_regmap_mux_ops, + .parent_hws = sm1_aud_top_parents, + .num_parents = ARRAY_SIZE(sm1_aud_top_parents), + .flags = CLK_SET_RATE_NO_REPARENT, + }, +}; + +static struct clk_regmap resample_b = + AUD_PCLK_GATE(resample_b, AUDIO_CLK_GATE_EN, 26); +static struct clk_regmap tovad = + AUD_PCLK_GATE(tovad, AUDIO_CLK_GATE_EN, 27); +static struct clk_regmap locker = + AUD_PCLK_GATE(locker, AUDIO_CLK_GATE_EN, 28); +static struct clk_regmap spdifin_lb = + AUD_PCLK_GATE(spdifin_lb, AUDIO_CLK_GATE_EN, 29); +static struct clk_regmap frddr_d = + AUD_PCLK_GATE(frddr_d, AUDIO_CLK_GATE_EN1, 0); +static struct clk_regmap toddr_d = + AUD_PCLK_GATE(toddr_d, AUDIO_CLK_GATE_EN1, 1); +static struct clk_regmap loopback_b = + AUD_PCLK_GATE(loopback_b, AUDIO_CLK_GATE_EN1, 2); + +static struct clk_regmap sm1_mst_a_mclk_sel = + AUD_MST_MCLK_MUX(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL); +static struct clk_regmap sm1_mst_b_mclk_sel = + AUD_MST_MCLK_MUX(mst_b_mclk, AUDIO_SM1_MCLK_B_CTRL); +static struct clk_regmap sm1_mst_c_mclk_sel = + AUD_MST_MCLK_MUX(mst_c_mclk, AUDIO_SM1_MCLK_C_CTRL); +static struct clk_regmap sm1_mst_d_mclk_sel = + AUD_MST_MCLK_MUX(mst_d_mclk, AUDIO_SM1_MCLK_D_CTRL); +static struct clk_regmap sm1_mst_e_mclk_sel = + AUD_MST_MCLK_MUX(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL); +static struct clk_regmap sm1_mst_f_mclk_sel = + AUD_MST_MCLK_MUX(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL); + +static struct clk_regmap sm1_mst_a_mclk_div = + AUD_MST_MCLK_DIV(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL); +static struct clk_regmap sm1_mst_b_mclk_div = + AUD_MST_MCLK_DIV(mst_b_mclk, AUDIO_SM1_MCLK_B_CTRL); +static struct clk_regmap sm1_mst_c_mclk_div = + AUD_MST_MCLK_DIV(mst_c_mclk, AUDIO_SM1_MCLK_C_CTRL); +static struct clk_regmap sm1_mst_d_mclk_div = + AUD_MST_MCLK_DIV(mst_d_mclk, AUDIO_SM1_MCLK_D_CTRL); +static struct clk_regmap sm1_mst_e_mclk_div = + AUD_MST_MCLK_DIV(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL); +static struct clk_regmap sm1_mst_f_mclk_div = + AUD_MST_MCLK_DIV(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL); + +static struct clk_regmap sm1_mst_a_mclk = + AUD_MST_MCLK_GATE(mst_a_mclk, AUDIO_SM1_MCLK_A_CTRL); +static struct clk_regmap sm1_mst_b_mclk = + AUD_MST_MCLK_GATE(mst_b_mclk, AUDIO_SM1_MCLK_B_CTRL); +static struct clk_regmap sm1_mst_c_mclk = + AUD_MST_MCLK_GATE(mst_c_mclk, AUDIO_SM1_MCLK_C_CTRL); +static struct clk_regmap sm1_mst_d_mclk = + AUD_MST_MCLK_GATE(mst_d_mclk, AUDIO_SM1_MCLK_D_CTRL); +static struct clk_regmap sm1_mst_e_mclk = + AUD_MST_MCLK_GATE(mst_e_mclk, AUDIO_SM1_MCLK_E_CTRL); +static struct clk_regmap sm1_mst_f_mclk = + AUD_MST_MCLK_GATE(mst_f_mclk, AUDIO_SM1_MCLK_F_CTRL); + +static struct clk_regmap sm1_tdm_mclk_pad_0 = AUD_TDM_PAD_CTRL( + tdm_mclk_pad_0, AUDIO_SM1_MST_PAD_CTRL0, 0, mclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_mclk_pad_1 = AUD_TDM_PAD_CTRL( + tdm_mclk_pad_1, AUDIO_SM1_MST_PAD_CTRL0, 4, mclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_lrclk_pad_0 = AUD_TDM_PAD_CTRL( + tdm_lrclk_pad_0, AUDIO_SM1_MST_PAD_CTRL1, 16, lrclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_lrclk_pad_1 = AUD_TDM_PAD_CTRL( + tdm_lrclk_pad_1, AUDIO_SM1_MST_PAD_CTRL1, 20, lrclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_lrclk_pad_2 = AUD_TDM_PAD_CTRL( + tdm_lrclk_pad_2, AUDIO_SM1_MST_PAD_CTRL1, 24, lrclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_sclk_pad_0 = AUD_TDM_PAD_CTRL( + tdm_sclk_pad_0, AUDIO_SM1_MST_PAD_CTRL1, 0, sclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_sclk_pad_1 = AUD_TDM_PAD_CTRL( + tdm_sclk_pad_1, AUDIO_SM1_MST_PAD_CTRL1, 4, sclk_pad_ctrl_parent_data); +static struct clk_regmap sm1_tdm_sclk_pad_2 = AUD_TDM_PAD_CTRL( + tdm_sclk_pad_2, AUDIO_SM1_MST_PAD_CTRL1, 8, sclk_pad_ctrl_parent_data); /* * Array of all clocks provided by this provider @@ -477,127 +778,128 @@ static AUD_TDM_PAD_CTRL(sclk_pad_2, AUDIO_MST_PAD_CTRL1, 8, */ static struct clk_hw_onecell_data axg_audio_hw_onecell_data = { .hws = { - [AUD_CLKID_DDR_ARB] = &aud_ddr_arb.hw, - [AUD_CLKID_PDM] = &aud_pdm.hw, - [AUD_CLKID_TDMIN_A] = &aud_tdmin_a.hw, - [AUD_CLKID_TDMIN_B] = &aud_tdmin_b.hw, - [AUD_CLKID_TDMIN_C] = &aud_tdmin_c.hw, - [AUD_CLKID_TDMIN_LB] = &aud_tdmin_lb.hw, - [AUD_CLKID_TDMOUT_A] = &aud_tdmout_a.hw, - [AUD_CLKID_TDMOUT_B] = &aud_tdmout_b.hw, - [AUD_CLKID_TDMOUT_C] = &aud_tdmout_c.hw, - [AUD_CLKID_FRDDR_A] = &aud_frddr_a.hw, - [AUD_CLKID_FRDDR_B] = &aud_frddr_b.hw, - [AUD_CLKID_FRDDR_C] = &aud_frddr_c.hw, - [AUD_CLKID_TODDR_A] = &aud_toddr_a.hw, - [AUD_CLKID_TODDR_B] = &aud_toddr_b.hw, - [AUD_CLKID_TODDR_C] = &aud_toddr_c.hw, - [AUD_CLKID_LOOPBACK] = &aud_loopback.hw, - [AUD_CLKID_SPDIFIN] = &aud_spdifin.hw, - [AUD_CLKID_SPDIFOUT] = &aud_spdifout.hw, - [AUD_CLKID_RESAMPLE] = &aud_resample.hw, - [AUD_CLKID_POWER_DETECT] = &aud_power_detect.hw, - [AUD_CLKID_MST_A_MCLK_SEL] = &aud_mst_a_mclk_sel.hw, - [AUD_CLKID_MST_B_MCLK_SEL] = &aud_mst_b_mclk_sel.hw, - [AUD_CLKID_MST_C_MCLK_SEL] = &aud_mst_c_mclk_sel.hw, - [AUD_CLKID_MST_D_MCLK_SEL] = &aud_mst_d_mclk_sel.hw, - [AUD_CLKID_MST_E_MCLK_SEL] = &aud_mst_e_mclk_sel.hw, - [AUD_CLKID_MST_F_MCLK_SEL] = &aud_mst_f_mclk_sel.hw, - [AUD_CLKID_MST_A_MCLK_DIV] = &aud_mst_a_mclk_div.hw, - [AUD_CLKID_MST_B_MCLK_DIV] = &aud_mst_b_mclk_div.hw, - [AUD_CLKID_MST_C_MCLK_DIV] = &aud_mst_c_mclk_div.hw, - [AUD_CLKID_MST_D_MCLK_DIV] = &aud_mst_d_mclk_div.hw, - [AUD_CLKID_MST_E_MCLK_DIV] = &aud_mst_e_mclk_div.hw, - [AUD_CLKID_MST_F_MCLK_DIV] = &aud_mst_f_mclk_div.hw, - [AUD_CLKID_MST_A_MCLK] = &aud_mst_a_mclk.hw, - [AUD_CLKID_MST_B_MCLK] = &aud_mst_b_mclk.hw, - [AUD_CLKID_MST_C_MCLK] = &aud_mst_c_mclk.hw, - [AUD_CLKID_MST_D_MCLK] = &aud_mst_d_mclk.hw, - [AUD_CLKID_MST_E_MCLK] = &aud_mst_e_mclk.hw, - [AUD_CLKID_MST_F_MCLK] = &aud_mst_f_mclk.hw, - [AUD_CLKID_SPDIFOUT_CLK_SEL] = &aud_spdifout_clk_sel.hw, - [AUD_CLKID_SPDIFOUT_CLK_DIV] = &aud_spdifout_clk_div.hw, - [AUD_CLKID_SPDIFOUT_CLK] = &aud_spdifout_clk.hw, - [AUD_CLKID_SPDIFIN_CLK_SEL] = &aud_spdifin_clk_sel.hw, - [AUD_CLKID_SPDIFIN_CLK_DIV] = &aud_spdifin_clk_div.hw, - [AUD_CLKID_SPDIFIN_CLK] = &aud_spdifin_clk.hw, - [AUD_CLKID_PDM_DCLK_SEL] = &aud_pdm_dclk_sel.hw, - [AUD_CLKID_PDM_DCLK_DIV] = &aud_pdm_dclk_div.hw, - [AUD_CLKID_PDM_DCLK] = &aud_pdm_dclk.hw, - [AUD_CLKID_PDM_SYSCLK_SEL] = &aud_pdm_sysclk_sel.hw, - [AUD_CLKID_PDM_SYSCLK_DIV] = &aud_pdm_sysclk_div.hw, - [AUD_CLKID_PDM_SYSCLK] = &aud_pdm_sysclk.hw, - [AUD_CLKID_MST_A_SCLK_PRE_EN] = &aud_mst_a_sclk_pre_en.hw, - [AUD_CLKID_MST_B_SCLK_PRE_EN] = &aud_mst_b_sclk_pre_en.hw, - [AUD_CLKID_MST_C_SCLK_PRE_EN] = &aud_mst_c_sclk_pre_en.hw, - [AUD_CLKID_MST_D_SCLK_PRE_EN] = &aud_mst_d_sclk_pre_en.hw, - [AUD_CLKID_MST_E_SCLK_PRE_EN] = &aud_mst_e_sclk_pre_en.hw, - [AUD_CLKID_MST_F_SCLK_PRE_EN] = &aud_mst_f_sclk_pre_en.hw, - [AUD_CLKID_MST_A_SCLK_DIV] = &aud_mst_a_sclk_div.hw, - [AUD_CLKID_MST_B_SCLK_DIV] = &aud_mst_b_sclk_div.hw, - [AUD_CLKID_MST_C_SCLK_DIV] = &aud_mst_c_sclk_div.hw, - [AUD_CLKID_MST_D_SCLK_DIV] = &aud_mst_d_sclk_div.hw, - [AUD_CLKID_MST_E_SCLK_DIV] = &aud_mst_e_sclk_div.hw, - [AUD_CLKID_MST_F_SCLK_DIV] = &aud_mst_f_sclk_div.hw, - [AUD_CLKID_MST_A_SCLK_POST_EN] = &aud_mst_a_sclk_post_en.hw, - [AUD_CLKID_MST_B_SCLK_POST_EN] = &aud_mst_b_sclk_post_en.hw, - [AUD_CLKID_MST_C_SCLK_POST_EN] = &aud_mst_c_sclk_post_en.hw, - [AUD_CLKID_MST_D_SCLK_POST_EN] = &aud_mst_d_sclk_post_en.hw, - [AUD_CLKID_MST_E_SCLK_POST_EN] = &aud_mst_e_sclk_post_en.hw, - [AUD_CLKID_MST_F_SCLK_POST_EN] = &aud_mst_f_sclk_post_en.hw, - [AUD_CLKID_MST_A_SCLK] = &aud_mst_a_sclk.hw, - [AUD_CLKID_MST_B_SCLK] = &aud_mst_b_sclk.hw, - [AUD_CLKID_MST_C_SCLK] = &aud_mst_c_sclk.hw, - [AUD_CLKID_MST_D_SCLK] = &aud_mst_d_sclk.hw, - [AUD_CLKID_MST_E_SCLK] = &aud_mst_e_sclk.hw, - [AUD_CLKID_MST_F_SCLK] = &aud_mst_f_sclk.hw, - [AUD_CLKID_MST_A_LRCLK_DIV] = &aud_mst_a_lrclk_div.hw, - [AUD_CLKID_MST_B_LRCLK_DIV] = &aud_mst_b_lrclk_div.hw, - [AUD_CLKID_MST_C_LRCLK_DIV] = &aud_mst_c_lrclk_div.hw, - [AUD_CLKID_MST_D_LRCLK_DIV] = &aud_mst_d_lrclk_div.hw, - [AUD_CLKID_MST_E_LRCLK_DIV] = &aud_mst_e_lrclk_div.hw, - [AUD_CLKID_MST_F_LRCLK_DIV] = &aud_mst_f_lrclk_div.hw, - [AUD_CLKID_MST_A_LRCLK] = &aud_mst_a_lrclk.hw, - [AUD_CLKID_MST_B_LRCLK] = &aud_mst_b_lrclk.hw, - [AUD_CLKID_MST_C_LRCLK] = &aud_mst_c_lrclk.hw, - [AUD_CLKID_MST_D_LRCLK] = &aud_mst_d_lrclk.hw, - [AUD_CLKID_MST_E_LRCLK] = &aud_mst_e_lrclk.hw, - [AUD_CLKID_MST_F_LRCLK] = &aud_mst_f_lrclk.hw, - [AUD_CLKID_TDMIN_A_SCLK_SEL] = &aud_tdmin_a_sclk_sel.hw, - [AUD_CLKID_TDMIN_B_SCLK_SEL] = &aud_tdmin_b_sclk_sel.hw, - [AUD_CLKID_TDMIN_C_SCLK_SEL] = &aud_tdmin_c_sclk_sel.hw, - [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &aud_tdmin_lb_sclk_sel.hw, - [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &aud_tdmout_a_sclk_sel.hw, - [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &aud_tdmout_b_sclk_sel.hw, - [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &aud_tdmout_c_sclk_sel.hw, - [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &aud_tdmin_a_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &aud_tdmin_b_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &aud_tdmin_c_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &aud_tdmin_lb_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &aud_tdmout_a_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &aud_tdmout_b_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &aud_tdmout_c_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &aud_tdmin_a_sclk_post_en.hw, - [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &aud_tdmin_b_sclk_post_en.hw, - [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &aud_tdmin_c_sclk_post_en.hw, - [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &aud_tdmin_lb_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &aud_tdmout_a_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &aud_tdmout_b_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &aud_tdmout_c_sclk_post_en.hw, - [AUD_CLKID_TDMIN_A_SCLK] = &aud_tdmin_a_sclk.hw, - [AUD_CLKID_TDMIN_B_SCLK] = &aud_tdmin_b_sclk.hw, - [AUD_CLKID_TDMIN_C_SCLK] = &aud_tdmin_c_sclk.hw, - [AUD_CLKID_TDMIN_LB_SCLK] = &aud_tdmin_lb_sclk.hw, - [AUD_CLKID_TDMOUT_A_SCLK] = &aud_tdmout_a_sclk.hw, - [AUD_CLKID_TDMOUT_B_SCLK] = &aud_tdmout_b_sclk.hw, - [AUD_CLKID_TDMOUT_C_SCLK] = &aud_tdmout_c_sclk.hw, - [AUD_CLKID_TDMIN_A_LRCLK] = &aud_tdmin_a_lrclk.hw, - [AUD_CLKID_TDMIN_B_LRCLK] = &aud_tdmin_b_lrclk.hw, - [AUD_CLKID_TDMIN_C_LRCLK] = &aud_tdmin_c_lrclk.hw, - [AUD_CLKID_TDMIN_LB_LRCLK] = &aud_tdmin_lb_lrclk.hw, - [AUD_CLKID_TDMOUT_A_LRCLK] = &aud_tdmout_a_lrclk.hw, - [AUD_CLKID_TDMOUT_B_LRCLK] = &aud_tdmout_b_lrclk.hw, - [AUD_CLKID_TDMOUT_C_LRCLK] = &aud_tdmout_c_lrclk.hw, + [AUD_CLKID_DDR_ARB] = &ddr_arb.hw, + [AUD_CLKID_PDM] = &pdm.hw, + [AUD_CLKID_TDMIN_A] = &tdmin_a.hw, + [AUD_CLKID_TDMIN_B] = &tdmin_b.hw, + [AUD_CLKID_TDMIN_C] = &tdmin_c.hw, + [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw, + [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw, + [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw, + [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw, + [AUD_CLKID_FRDDR_A] = &frddr_a.hw, + [AUD_CLKID_FRDDR_B] = &frddr_b.hw, + [AUD_CLKID_FRDDR_C] = &frddr_c.hw, + [AUD_CLKID_TODDR_A] = &toddr_a.hw, + [AUD_CLKID_TODDR_B] = &toddr_b.hw, + [AUD_CLKID_TODDR_C] = &toddr_c.hw, + [AUD_CLKID_LOOPBACK] = &loopback.hw, + [AUD_CLKID_SPDIFIN] = &spdifin.hw, + [AUD_CLKID_SPDIFOUT] = &spdifout.hw, + [AUD_CLKID_RESAMPLE] = &resample.hw, + [AUD_CLKID_POWER_DETECT] = &power_detect.hw, + [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw, + [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw, + [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw, + [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw, + [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw, + [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw, + [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw, + [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw, + [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw, + [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw, + [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw, + [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw, + [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw, + [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw, + [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw, + [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw, + [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw, + [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw, + [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw, + [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw, + [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw, + [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw, + [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw, + [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw, + [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw, + [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw, + [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw, + [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw, + [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw, + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw, + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw, + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw, + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw, + [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw, + [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw, + [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw, + [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw, + [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw, + [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw, + [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw, + [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw, + [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw, + [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw, + [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw, + [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw, + [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw, + [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw, + [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw, + [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw, + [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw, + [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw, + [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw, + [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw, + [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw, + [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw, + [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw, + [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw, + [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw, + [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw, + [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw, + [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw, + [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw, + [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw, + [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw, + [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw, + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw, + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw, + [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw, + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw, + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw, + [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw, + [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw, + [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw, + [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw, + [AUD_CLKID_TDMOUT_A_SCLK] = &tdmout_a_sclk.hw, + [AUD_CLKID_TDMOUT_B_SCLK] = &tdmout_b_sclk.hw, + [AUD_CLKID_TDMOUT_C_SCLK] = &tdmout_c_sclk.hw, + [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw, + [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw, + [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw, + [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw, + [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw, + [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw, + [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw, + [AUD_CLKID_TOP] = &axg_aud_top, [NR_CLKS] = NULL, }, .num = NR_CLKS, @@ -609,284 +911,596 @@ static struct clk_hw_onecell_data axg_audio_hw_onecell_data = { */ static struct clk_hw_onecell_data g12a_audio_hw_onecell_data = { .hws = { - [AUD_CLKID_DDR_ARB] = &aud_ddr_arb.hw, - [AUD_CLKID_PDM] = &aud_pdm.hw, - [AUD_CLKID_TDMIN_A] = &aud_tdmin_a.hw, - [AUD_CLKID_TDMIN_B] = &aud_tdmin_b.hw, - [AUD_CLKID_TDMIN_C] = &aud_tdmin_c.hw, - [AUD_CLKID_TDMIN_LB] = &aud_tdmin_lb.hw, - [AUD_CLKID_TDMOUT_A] = &aud_tdmout_a.hw, - [AUD_CLKID_TDMOUT_B] = &aud_tdmout_b.hw, - [AUD_CLKID_TDMOUT_C] = &aud_tdmout_c.hw, - [AUD_CLKID_FRDDR_A] = &aud_frddr_a.hw, - [AUD_CLKID_FRDDR_B] = &aud_frddr_b.hw, - [AUD_CLKID_FRDDR_C] = &aud_frddr_c.hw, - [AUD_CLKID_TODDR_A] = &aud_toddr_a.hw, - [AUD_CLKID_TODDR_B] = &aud_toddr_b.hw, - [AUD_CLKID_TODDR_C] = &aud_toddr_c.hw, - [AUD_CLKID_LOOPBACK] = &aud_loopback.hw, - [AUD_CLKID_SPDIFIN] = &aud_spdifin.hw, - [AUD_CLKID_SPDIFOUT] = &aud_spdifout.hw, - [AUD_CLKID_RESAMPLE] = &aud_resample.hw, - [AUD_CLKID_POWER_DETECT] = &aud_power_detect.hw, - [AUD_CLKID_SPDIFOUT_B] = &aud_spdifout_b.hw, - [AUD_CLKID_MST_A_MCLK_SEL] = &aud_mst_a_mclk_sel.hw, - [AUD_CLKID_MST_B_MCLK_SEL] = &aud_mst_b_mclk_sel.hw, - [AUD_CLKID_MST_C_MCLK_SEL] = &aud_mst_c_mclk_sel.hw, - [AUD_CLKID_MST_D_MCLK_SEL] = &aud_mst_d_mclk_sel.hw, - [AUD_CLKID_MST_E_MCLK_SEL] = &aud_mst_e_mclk_sel.hw, - [AUD_CLKID_MST_F_MCLK_SEL] = &aud_mst_f_mclk_sel.hw, - [AUD_CLKID_MST_A_MCLK_DIV] = &aud_mst_a_mclk_div.hw, - [AUD_CLKID_MST_B_MCLK_DIV] = &aud_mst_b_mclk_div.hw, - [AUD_CLKID_MST_C_MCLK_DIV] = &aud_mst_c_mclk_div.hw, - [AUD_CLKID_MST_D_MCLK_DIV] = &aud_mst_d_mclk_div.hw, - [AUD_CLKID_MST_E_MCLK_DIV] = &aud_mst_e_mclk_div.hw, - [AUD_CLKID_MST_F_MCLK_DIV] = &aud_mst_f_mclk_div.hw, - [AUD_CLKID_MST_A_MCLK] = &aud_mst_a_mclk.hw, - [AUD_CLKID_MST_B_MCLK] = &aud_mst_b_mclk.hw, - [AUD_CLKID_MST_C_MCLK] = &aud_mst_c_mclk.hw, - [AUD_CLKID_MST_D_MCLK] = &aud_mst_d_mclk.hw, - [AUD_CLKID_MST_E_MCLK] = &aud_mst_e_mclk.hw, - [AUD_CLKID_MST_F_MCLK] = &aud_mst_f_mclk.hw, - [AUD_CLKID_SPDIFOUT_CLK_SEL] = &aud_spdifout_clk_sel.hw, - [AUD_CLKID_SPDIFOUT_CLK_DIV] = &aud_spdifout_clk_div.hw, - [AUD_CLKID_SPDIFOUT_CLK] = &aud_spdifout_clk.hw, - [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &aud_spdifout_b_clk_sel.hw, - [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &aud_spdifout_b_clk_div.hw, - [AUD_CLKID_SPDIFOUT_B_CLK] = &aud_spdifout_b_clk.hw, - [AUD_CLKID_SPDIFIN_CLK_SEL] = &aud_spdifin_clk_sel.hw, - [AUD_CLKID_SPDIFIN_CLK_DIV] = &aud_spdifin_clk_div.hw, - [AUD_CLKID_SPDIFIN_CLK] = &aud_spdifin_clk.hw, - [AUD_CLKID_PDM_DCLK_SEL] = &aud_pdm_dclk_sel.hw, - [AUD_CLKID_PDM_DCLK_DIV] = &aud_pdm_dclk_div.hw, - [AUD_CLKID_PDM_DCLK] = &aud_pdm_dclk.hw, - [AUD_CLKID_PDM_SYSCLK_SEL] = &aud_pdm_sysclk_sel.hw, - [AUD_CLKID_PDM_SYSCLK_DIV] = &aud_pdm_sysclk_div.hw, - [AUD_CLKID_PDM_SYSCLK] = &aud_pdm_sysclk.hw, - [AUD_CLKID_MST_A_SCLK_PRE_EN] = &aud_mst_a_sclk_pre_en.hw, - [AUD_CLKID_MST_B_SCLK_PRE_EN] = &aud_mst_b_sclk_pre_en.hw, - [AUD_CLKID_MST_C_SCLK_PRE_EN] = &aud_mst_c_sclk_pre_en.hw, - [AUD_CLKID_MST_D_SCLK_PRE_EN] = &aud_mst_d_sclk_pre_en.hw, - [AUD_CLKID_MST_E_SCLK_PRE_EN] = &aud_mst_e_sclk_pre_en.hw, - [AUD_CLKID_MST_F_SCLK_PRE_EN] = &aud_mst_f_sclk_pre_en.hw, - [AUD_CLKID_MST_A_SCLK_DIV] = &aud_mst_a_sclk_div.hw, - [AUD_CLKID_MST_B_SCLK_DIV] = &aud_mst_b_sclk_div.hw, - [AUD_CLKID_MST_C_SCLK_DIV] = &aud_mst_c_sclk_div.hw, - [AUD_CLKID_MST_D_SCLK_DIV] = &aud_mst_d_sclk_div.hw, - [AUD_CLKID_MST_E_SCLK_DIV] = &aud_mst_e_sclk_div.hw, - [AUD_CLKID_MST_F_SCLK_DIV] = &aud_mst_f_sclk_div.hw, - [AUD_CLKID_MST_A_SCLK_POST_EN] = &aud_mst_a_sclk_post_en.hw, - [AUD_CLKID_MST_B_SCLK_POST_EN] = &aud_mst_b_sclk_post_en.hw, - [AUD_CLKID_MST_C_SCLK_POST_EN] = &aud_mst_c_sclk_post_en.hw, - [AUD_CLKID_MST_D_SCLK_POST_EN] = &aud_mst_d_sclk_post_en.hw, - [AUD_CLKID_MST_E_SCLK_POST_EN] = &aud_mst_e_sclk_post_en.hw, - [AUD_CLKID_MST_F_SCLK_POST_EN] = &aud_mst_f_sclk_post_en.hw, - [AUD_CLKID_MST_A_SCLK] = &aud_mst_a_sclk.hw, - [AUD_CLKID_MST_B_SCLK] = &aud_mst_b_sclk.hw, - [AUD_CLKID_MST_C_SCLK] = &aud_mst_c_sclk.hw, - [AUD_CLKID_MST_D_SCLK] = &aud_mst_d_sclk.hw, - [AUD_CLKID_MST_E_SCLK] = &aud_mst_e_sclk.hw, - [AUD_CLKID_MST_F_SCLK] = &aud_mst_f_sclk.hw, - [AUD_CLKID_MST_A_LRCLK_DIV] = &aud_mst_a_lrclk_div.hw, - [AUD_CLKID_MST_B_LRCLK_DIV] = &aud_mst_b_lrclk_div.hw, - [AUD_CLKID_MST_C_LRCLK_DIV] = &aud_mst_c_lrclk_div.hw, - [AUD_CLKID_MST_D_LRCLK_DIV] = &aud_mst_d_lrclk_div.hw, - [AUD_CLKID_MST_E_LRCLK_DIV] = &aud_mst_e_lrclk_div.hw, - [AUD_CLKID_MST_F_LRCLK_DIV] = &aud_mst_f_lrclk_div.hw, - [AUD_CLKID_MST_A_LRCLK] = &aud_mst_a_lrclk.hw, - [AUD_CLKID_MST_B_LRCLK] = &aud_mst_b_lrclk.hw, - [AUD_CLKID_MST_C_LRCLK] = &aud_mst_c_lrclk.hw, - [AUD_CLKID_MST_D_LRCLK] = &aud_mst_d_lrclk.hw, - [AUD_CLKID_MST_E_LRCLK] = &aud_mst_e_lrclk.hw, - [AUD_CLKID_MST_F_LRCLK] = &aud_mst_f_lrclk.hw, - [AUD_CLKID_TDMIN_A_SCLK_SEL] = &aud_tdmin_a_sclk_sel.hw, - [AUD_CLKID_TDMIN_B_SCLK_SEL] = &aud_tdmin_b_sclk_sel.hw, - [AUD_CLKID_TDMIN_C_SCLK_SEL] = &aud_tdmin_c_sclk_sel.hw, - [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &aud_tdmin_lb_sclk_sel.hw, - [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &aud_tdmout_a_sclk_sel.hw, - [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &aud_tdmout_b_sclk_sel.hw, - [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &aud_tdmout_c_sclk_sel.hw, - [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &aud_tdmin_a_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &aud_tdmin_b_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &aud_tdmin_c_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &aud_tdmin_lb_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &aud_tdmout_a_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &aud_tdmout_b_sclk_pre_en.hw, - [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &aud_tdmout_c_sclk_pre_en.hw, - [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &aud_tdmin_a_sclk_post_en.hw, - [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &aud_tdmin_b_sclk_post_en.hw, - [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &aud_tdmin_c_sclk_post_en.hw, - [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &aud_tdmin_lb_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &aud_tdmout_a_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &aud_tdmout_b_sclk_post_en.hw, - [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &aud_tdmout_c_sclk_post_en.hw, - [AUD_CLKID_TDMIN_A_SCLK] = &aud_tdmin_a_sclk.hw, - [AUD_CLKID_TDMIN_B_SCLK] = &aud_tdmin_b_sclk.hw, - [AUD_CLKID_TDMIN_C_SCLK] = &aud_tdmin_c_sclk.hw, - [AUD_CLKID_TDMIN_LB_SCLK] = &aud_tdmin_lb_sclk.hw, - [AUD_CLKID_TDMOUT_A_SCLK] = &aud_tdmout_a_sclk.hw, - [AUD_CLKID_TDMOUT_B_SCLK] = &aud_tdmout_b_sclk.hw, - [AUD_CLKID_TDMOUT_C_SCLK] = &aud_tdmout_c_sclk.hw, - [AUD_CLKID_TDMIN_A_LRCLK] = &aud_tdmin_a_lrclk.hw, - [AUD_CLKID_TDMIN_B_LRCLK] = &aud_tdmin_b_lrclk.hw, - [AUD_CLKID_TDMIN_C_LRCLK] = &aud_tdmin_c_lrclk.hw, - [AUD_CLKID_TDMIN_LB_LRCLK] = &aud_tdmin_lb_lrclk.hw, - [AUD_CLKID_TDMOUT_A_LRCLK] = &aud_tdmout_a_lrclk.hw, - [AUD_CLKID_TDMOUT_B_LRCLK] = &aud_tdmout_b_lrclk.hw, - [AUD_CLKID_TDMOUT_C_LRCLK] = &aud_tdmout_c_lrclk.hw, - [AUD_CLKID_TDM_MCLK_PAD0] = &aud_tdm_mclk_pad_0.hw, - [AUD_CLKID_TDM_MCLK_PAD1] = &aud_tdm_mclk_pad_1.hw, - [AUD_CLKID_TDM_LRCLK_PAD0] = &aud_tdm_lrclk_pad_0.hw, - [AUD_CLKID_TDM_LRCLK_PAD1] = &aud_tdm_lrclk_pad_1.hw, - [AUD_CLKID_TDM_LRCLK_PAD2] = &aud_tdm_lrclk_pad_2.hw, - [AUD_CLKID_TDM_SCLK_PAD0] = &aud_tdm_sclk_pad_0.hw, - [AUD_CLKID_TDM_SCLK_PAD1] = &aud_tdm_sclk_pad_1.hw, - [AUD_CLKID_TDM_SCLK_PAD2] = &aud_tdm_sclk_pad_2.hw, + [AUD_CLKID_DDR_ARB] = &ddr_arb.hw, + [AUD_CLKID_PDM] = &pdm.hw, + [AUD_CLKID_TDMIN_A] = &tdmin_a.hw, + [AUD_CLKID_TDMIN_B] = &tdmin_b.hw, + [AUD_CLKID_TDMIN_C] = &tdmin_c.hw, + [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw, + [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw, + [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw, + [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw, + [AUD_CLKID_FRDDR_A] = &frddr_a.hw, + [AUD_CLKID_FRDDR_B] = &frddr_b.hw, + [AUD_CLKID_FRDDR_C] = &frddr_c.hw, + [AUD_CLKID_TODDR_A] = &toddr_a.hw, + [AUD_CLKID_TODDR_B] = &toddr_b.hw, + [AUD_CLKID_TODDR_C] = &toddr_c.hw, + [AUD_CLKID_LOOPBACK] = &loopback.hw, + [AUD_CLKID_SPDIFIN] = &spdifin.hw, + [AUD_CLKID_SPDIFOUT] = &spdifout.hw, + [AUD_CLKID_RESAMPLE] = &resample.hw, + [AUD_CLKID_POWER_DETECT] = &power_detect.hw, + [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw, + [AUD_CLKID_MST_A_MCLK_SEL] = &mst_a_mclk_sel.hw, + [AUD_CLKID_MST_B_MCLK_SEL] = &mst_b_mclk_sel.hw, + [AUD_CLKID_MST_C_MCLK_SEL] = &mst_c_mclk_sel.hw, + [AUD_CLKID_MST_D_MCLK_SEL] = &mst_d_mclk_sel.hw, + [AUD_CLKID_MST_E_MCLK_SEL] = &mst_e_mclk_sel.hw, + [AUD_CLKID_MST_F_MCLK_SEL] = &mst_f_mclk_sel.hw, + [AUD_CLKID_MST_A_MCLK_DIV] = &mst_a_mclk_div.hw, + [AUD_CLKID_MST_B_MCLK_DIV] = &mst_b_mclk_div.hw, + [AUD_CLKID_MST_C_MCLK_DIV] = &mst_c_mclk_div.hw, + [AUD_CLKID_MST_D_MCLK_DIV] = &mst_d_mclk_div.hw, + [AUD_CLKID_MST_E_MCLK_DIV] = &mst_e_mclk_div.hw, + [AUD_CLKID_MST_F_MCLK_DIV] = &mst_f_mclk_div.hw, + [AUD_CLKID_MST_A_MCLK] = &mst_a_mclk.hw, + [AUD_CLKID_MST_B_MCLK] = &mst_b_mclk.hw, + [AUD_CLKID_MST_C_MCLK] = &mst_c_mclk.hw, + [AUD_CLKID_MST_D_MCLK] = &mst_d_mclk.hw, + [AUD_CLKID_MST_E_MCLK] = &mst_e_mclk.hw, + [AUD_CLKID_MST_F_MCLK] = &mst_f_mclk.hw, + [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw, + [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw, + [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw, + [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw, + [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw, + [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw, + [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw, + [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw, + [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw, + [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw, + [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw, + [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw, + [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw, + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw, + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw, + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw, + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw, + [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw, + [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw, + [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw, + [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw, + [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw, + [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw, + [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw, + [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw, + [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw, + [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw, + [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw, + [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw, + [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw, + [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw, + [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw, + [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw, + [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw, + [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw, + [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw, + [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw, + [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw, + [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw, + [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw, + [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw, + [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw, + [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw, + [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw, + [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw, + [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw, + [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw, + [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw, + [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw, + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw, + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw, + [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw, + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw, + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw, + [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw, + [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw, + [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw, + [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw, + [AUD_CLKID_TDMOUT_A_SCLK] = &tdmout_a_sclk.hw, + [AUD_CLKID_TDMOUT_B_SCLK] = &tdmout_b_sclk.hw, + [AUD_CLKID_TDMOUT_C_SCLK] = &tdmout_c_sclk.hw, + [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw, + [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw, + [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw, + [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw, + [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw, + [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw, + [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw, + [AUD_CLKID_TDM_MCLK_PAD0] = &g12a_tdm_mclk_pad_0.hw, + [AUD_CLKID_TDM_MCLK_PAD1] = &g12a_tdm_mclk_pad_1.hw, + [AUD_CLKID_TDM_LRCLK_PAD0] = &g12a_tdm_lrclk_pad_0.hw, + [AUD_CLKID_TDM_LRCLK_PAD1] = &g12a_tdm_lrclk_pad_1.hw, + [AUD_CLKID_TDM_LRCLK_PAD2] = &g12a_tdm_lrclk_pad_2.hw, + [AUD_CLKID_TDM_SCLK_PAD0] = &g12a_tdm_sclk_pad_0.hw, + [AUD_CLKID_TDM_SCLK_PAD1] = &g12a_tdm_sclk_pad_1.hw, + [AUD_CLKID_TDM_SCLK_PAD2] = &g12a_tdm_sclk_pad_2.hw, + [AUD_CLKID_TOP] = &axg_aud_top, [NR_CLKS] = NULL, }, .num = NR_CLKS, }; +/* + * Array of all SM1 clocks provided by this provider + * The input clocks of the controller will be populated at runtime + */ +static struct clk_hw_onecell_data sm1_audio_hw_onecell_data = { + .hws = { + [AUD_CLKID_DDR_ARB] = &ddr_arb.hw, + [AUD_CLKID_PDM] = &pdm.hw, + [AUD_CLKID_TDMIN_A] = &tdmin_a.hw, + [AUD_CLKID_TDMIN_B] = &tdmin_b.hw, + [AUD_CLKID_TDMIN_C] = &tdmin_c.hw, + [AUD_CLKID_TDMIN_LB] = &tdmin_lb.hw, + [AUD_CLKID_TDMOUT_A] = &tdmout_a.hw, + [AUD_CLKID_TDMOUT_B] = &tdmout_b.hw, + [AUD_CLKID_TDMOUT_C] = &tdmout_c.hw, + [AUD_CLKID_FRDDR_A] = &frddr_a.hw, + [AUD_CLKID_FRDDR_B] = &frddr_b.hw, + [AUD_CLKID_FRDDR_C] = &frddr_c.hw, + [AUD_CLKID_TODDR_A] = &toddr_a.hw, + [AUD_CLKID_TODDR_B] = &toddr_b.hw, + [AUD_CLKID_TODDR_C] = &toddr_c.hw, + [AUD_CLKID_LOOPBACK] = &loopback.hw, + [AUD_CLKID_SPDIFIN] = &spdifin.hw, + [AUD_CLKID_SPDIFOUT] = &spdifout.hw, + [AUD_CLKID_RESAMPLE] = &resample.hw, + [AUD_CLKID_SPDIFOUT_B] = &spdifout_b.hw, + [AUD_CLKID_MST_A_MCLK_SEL] = &sm1_mst_a_mclk_sel.hw, + [AUD_CLKID_MST_B_MCLK_SEL] = &sm1_mst_b_mclk_sel.hw, + [AUD_CLKID_MST_C_MCLK_SEL] = &sm1_mst_c_mclk_sel.hw, + [AUD_CLKID_MST_D_MCLK_SEL] = &sm1_mst_d_mclk_sel.hw, + [AUD_CLKID_MST_E_MCLK_SEL] = &sm1_mst_e_mclk_sel.hw, + [AUD_CLKID_MST_F_MCLK_SEL] = &sm1_mst_f_mclk_sel.hw, + [AUD_CLKID_MST_A_MCLK_DIV] = &sm1_mst_a_mclk_div.hw, + [AUD_CLKID_MST_B_MCLK_DIV] = &sm1_mst_b_mclk_div.hw, + [AUD_CLKID_MST_C_MCLK_DIV] = &sm1_mst_c_mclk_div.hw, + [AUD_CLKID_MST_D_MCLK_DIV] = &sm1_mst_d_mclk_div.hw, + [AUD_CLKID_MST_E_MCLK_DIV] = &sm1_mst_e_mclk_div.hw, + [AUD_CLKID_MST_F_MCLK_DIV] = &sm1_mst_f_mclk_div.hw, + [AUD_CLKID_MST_A_MCLK] = &sm1_mst_a_mclk.hw, + [AUD_CLKID_MST_B_MCLK] = &sm1_mst_b_mclk.hw, + [AUD_CLKID_MST_C_MCLK] = &sm1_mst_c_mclk.hw, + [AUD_CLKID_MST_D_MCLK] = &sm1_mst_d_mclk.hw, + [AUD_CLKID_MST_E_MCLK] = &sm1_mst_e_mclk.hw, + [AUD_CLKID_MST_F_MCLK] = &sm1_mst_f_mclk.hw, + [AUD_CLKID_SPDIFOUT_CLK_SEL] = &spdifout_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_CLK_DIV] = &spdifout_clk_div.hw, + [AUD_CLKID_SPDIFOUT_CLK] = &spdifout_clk.hw, + [AUD_CLKID_SPDIFOUT_B_CLK_SEL] = &spdifout_b_clk_sel.hw, + [AUD_CLKID_SPDIFOUT_B_CLK_DIV] = &spdifout_b_clk_div.hw, + [AUD_CLKID_SPDIFOUT_B_CLK] = &spdifout_b_clk.hw, + [AUD_CLKID_SPDIFIN_CLK_SEL] = &spdifin_clk_sel.hw, + [AUD_CLKID_SPDIFIN_CLK_DIV] = &spdifin_clk_div.hw, + [AUD_CLKID_SPDIFIN_CLK] = &spdifin_clk.hw, + [AUD_CLKID_PDM_DCLK_SEL] = &pdm_dclk_sel.hw, + [AUD_CLKID_PDM_DCLK_DIV] = &pdm_dclk_div.hw, + [AUD_CLKID_PDM_DCLK] = &pdm_dclk.hw, + [AUD_CLKID_PDM_SYSCLK_SEL] = &pdm_sysclk_sel.hw, + [AUD_CLKID_PDM_SYSCLK_DIV] = &pdm_sysclk_div.hw, + [AUD_CLKID_PDM_SYSCLK] = &pdm_sysclk.hw, + [AUD_CLKID_MST_A_SCLK_PRE_EN] = &mst_a_sclk_pre_en.hw, + [AUD_CLKID_MST_B_SCLK_PRE_EN] = &mst_b_sclk_pre_en.hw, + [AUD_CLKID_MST_C_SCLK_PRE_EN] = &mst_c_sclk_pre_en.hw, + [AUD_CLKID_MST_D_SCLK_PRE_EN] = &mst_d_sclk_pre_en.hw, + [AUD_CLKID_MST_E_SCLK_PRE_EN] = &mst_e_sclk_pre_en.hw, + [AUD_CLKID_MST_F_SCLK_PRE_EN] = &mst_f_sclk_pre_en.hw, + [AUD_CLKID_MST_A_SCLK_DIV] = &mst_a_sclk_div.hw, + [AUD_CLKID_MST_B_SCLK_DIV] = &mst_b_sclk_div.hw, + [AUD_CLKID_MST_C_SCLK_DIV] = &mst_c_sclk_div.hw, + [AUD_CLKID_MST_D_SCLK_DIV] = &mst_d_sclk_div.hw, + [AUD_CLKID_MST_E_SCLK_DIV] = &mst_e_sclk_div.hw, + [AUD_CLKID_MST_F_SCLK_DIV] = &mst_f_sclk_div.hw, + [AUD_CLKID_MST_A_SCLK_POST_EN] = &mst_a_sclk_post_en.hw, + [AUD_CLKID_MST_B_SCLK_POST_EN] = &mst_b_sclk_post_en.hw, + [AUD_CLKID_MST_C_SCLK_POST_EN] = &mst_c_sclk_post_en.hw, + [AUD_CLKID_MST_D_SCLK_POST_EN] = &mst_d_sclk_post_en.hw, + [AUD_CLKID_MST_E_SCLK_POST_EN] = &mst_e_sclk_post_en.hw, + [AUD_CLKID_MST_F_SCLK_POST_EN] = &mst_f_sclk_post_en.hw, + [AUD_CLKID_MST_A_SCLK] = &mst_a_sclk.hw, + [AUD_CLKID_MST_B_SCLK] = &mst_b_sclk.hw, + [AUD_CLKID_MST_C_SCLK] = &mst_c_sclk.hw, + [AUD_CLKID_MST_D_SCLK] = &mst_d_sclk.hw, + [AUD_CLKID_MST_E_SCLK] = &mst_e_sclk.hw, + [AUD_CLKID_MST_F_SCLK] = &mst_f_sclk.hw, + [AUD_CLKID_MST_A_LRCLK_DIV] = &mst_a_lrclk_div.hw, + [AUD_CLKID_MST_B_LRCLK_DIV] = &mst_b_lrclk_div.hw, + [AUD_CLKID_MST_C_LRCLK_DIV] = &mst_c_lrclk_div.hw, + [AUD_CLKID_MST_D_LRCLK_DIV] = &mst_d_lrclk_div.hw, + [AUD_CLKID_MST_E_LRCLK_DIV] = &mst_e_lrclk_div.hw, + [AUD_CLKID_MST_F_LRCLK_DIV] = &mst_f_lrclk_div.hw, + [AUD_CLKID_MST_A_LRCLK] = &mst_a_lrclk.hw, + [AUD_CLKID_MST_B_LRCLK] = &mst_b_lrclk.hw, + [AUD_CLKID_MST_C_LRCLK] = &mst_c_lrclk.hw, + [AUD_CLKID_MST_D_LRCLK] = &mst_d_lrclk.hw, + [AUD_CLKID_MST_E_LRCLK] = &mst_e_lrclk.hw, + [AUD_CLKID_MST_F_LRCLK] = &mst_f_lrclk.hw, + [AUD_CLKID_TDMIN_A_SCLK_SEL] = &tdmin_a_sclk_sel.hw, + [AUD_CLKID_TDMIN_B_SCLK_SEL] = &tdmin_b_sclk_sel.hw, + [AUD_CLKID_TDMIN_C_SCLK_SEL] = &tdmin_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_LB_SCLK_SEL] = &tdmin_lb_sclk_sel.hw, + [AUD_CLKID_TDMOUT_A_SCLK_SEL] = &tdmout_a_sclk_sel.hw, + [AUD_CLKID_TDMOUT_B_SCLK_SEL] = &tdmout_b_sclk_sel.hw, + [AUD_CLKID_TDMOUT_C_SCLK_SEL] = &tdmout_c_sclk_sel.hw, + [AUD_CLKID_TDMIN_A_SCLK_PRE_EN] = &tdmin_a_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_PRE_EN] = &tdmin_b_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_PRE_EN] = &tdmin_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_PRE_EN] = &tdmin_lb_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_PRE_EN] = &tdmout_a_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_PRE_EN] = &tdmout_b_sclk_pre_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_PRE_EN] = &tdmout_c_sclk_pre_en.hw, + [AUD_CLKID_TDMIN_A_SCLK_POST_EN] = &tdmin_a_sclk_post_en.hw, + [AUD_CLKID_TDMIN_B_SCLK_POST_EN] = &tdmin_b_sclk_post_en.hw, + [AUD_CLKID_TDMIN_C_SCLK_POST_EN] = &tdmin_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_LB_SCLK_POST_EN] = &tdmin_lb_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_A_SCLK_POST_EN] = &tdmout_a_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_B_SCLK_POST_EN] = &tdmout_b_sclk_post_en.hw, + [AUD_CLKID_TDMOUT_C_SCLK_POST_EN] = &tdmout_c_sclk_post_en.hw, + [AUD_CLKID_TDMIN_A_SCLK] = &tdmin_a_sclk.hw, + [AUD_CLKID_TDMIN_B_SCLK] = &tdmin_b_sclk.hw, + [AUD_CLKID_TDMIN_C_SCLK] = &tdmin_c_sclk.hw, + [AUD_CLKID_TDMIN_LB_SCLK] = &tdmin_lb_sclk.hw, + [AUD_CLKID_TDMOUT_A_SCLK] = &tdmout_a_sclk.hw, + [AUD_CLKID_TDMOUT_B_SCLK] = &tdmout_b_sclk.hw, + [AUD_CLKID_TDMOUT_C_SCLK] = &tdmout_c_sclk.hw, + [AUD_CLKID_TDMIN_A_LRCLK] = &tdmin_a_lrclk.hw, + [AUD_CLKID_TDMIN_B_LRCLK] = &tdmin_b_lrclk.hw, + [AUD_CLKID_TDMIN_C_LRCLK] = &tdmin_c_lrclk.hw, + [AUD_CLKID_TDMIN_LB_LRCLK] = &tdmin_lb_lrclk.hw, + [AUD_CLKID_TDMOUT_A_LRCLK] = &tdmout_a_lrclk.hw, + [AUD_CLKID_TDMOUT_B_LRCLK] = &tdmout_b_lrclk.hw, + [AUD_CLKID_TDMOUT_C_LRCLK] = &tdmout_c_lrclk.hw, + [AUD_CLKID_TDM_MCLK_PAD0] = &sm1_tdm_mclk_pad_0.hw, + [AUD_CLKID_TDM_MCLK_PAD1] = &sm1_tdm_mclk_pad_1.hw, + [AUD_CLKID_TDM_LRCLK_PAD0] = &sm1_tdm_lrclk_pad_0.hw, + [AUD_CLKID_TDM_LRCLK_PAD1] = &sm1_tdm_lrclk_pad_1.hw, + [AUD_CLKID_TDM_LRCLK_PAD2] = &sm1_tdm_lrclk_pad_2.hw, + [AUD_CLKID_TDM_SCLK_PAD0] = &sm1_tdm_sclk_pad_0.hw, + [AUD_CLKID_TDM_SCLK_PAD1] = &sm1_tdm_sclk_pad_1.hw, + [AUD_CLKID_TDM_SCLK_PAD2] = &sm1_tdm_sclk_pad_2.hw, + [AUD_CLKID_TOP] = &sm1_aud_top.hw, + [AUD_CLKID_TORAM] = &toram.hw, + [AUD_CLKID_EQDRC] = &eqdrc.hw, + [AUD_CLKID_RESAMPLE_B] = &resample_b.hw, + [AUD_CLKID_TOVAD] = &tovad.hw, + [AUD_CLKID_LOCKER] = &locker.hw, + [AUD_CLKID_SPDIFIN_LB] = &spdifin_lb.hw, + [AUD_CLKID_FRDDR_D] = &frddr_d.hw, + [AUD_CLKID_TODDR_D] = &toddr_d.hw, + [AUD_CLKID_LOOPBACK_B] = &loopback_b.hw, + [AUD_CLKID_CLK81_EN] = &sm1_clk81_en.hw, + [AUD_CLKID_SYSCLK_A_DIV] = &sm1_sysclk_a_div.hw, + [AUD_CLKID_SYSCLK_A_EN] = &sm1_sysclk_a_en.hw, + [AUD_CLKID_SYSCLK_B_DIV] = &sm1_sysclk_b_div.hw, + [AUD_CLKID_SYSCLK_B_EN] = &sm1_sysclk_b_en.hw, + [NR_CLKS] = NULL, + }, + .num = NR_CLKS, +}; + + /* Convenience table to populate regmap in .probe() * Note that this table is shared between both AXG and G12A, * with spdifout_b clocks being exclusive to G12A. Since those * clocks are not declared within the AXG onecell table, we do not * feel the need to have separate AXG/G12A regmap tables. */ -static struct clk_regmap *const aud_clk_regmaps[] = { - &aud_ddr_arb, - &aud_pdm, - &aud_tdmin_a, - &aud_tdmin_b, - &aud_tdmin_c, - &aud_tdmin_lb, - &aud_tdmout_a, - &aud_tdmout_b, - &aud_tdmout_c, - &aud_frddr_a, - &aud_frddr_b, - &aud_frddr_c, - &aud_toddr_a, - &aud_toddr_b, - &aud_toddr_c, - &aud_loopback, - &aud_spdifin, - &aud_spdifout, - &aud_resample, - &aud_power_detect, - &aud_spdifout_b, - &aud_mst_a_mclk_sel, - &aud_mst_b_mclk_sel, - &aud_mst_c_mclk_sel, - &aud_mst_d_mclk_sel, - &aud_mst_e_mclk_sel, - &aud_mst_f_mclk_sel, - &aud_mst_a_mclk_div, - &aud_mst_b_mclk_div, - &aud_mst_c_mclk_div, - &aud_mst_d_mclk_div, - &aud_mst_e_mclk_div, - &aud_mst_f_mclk_div, - &aud_mst_a_mclk, - &aud_mst_b_mclk, - &aud_mst_c_mclk, - &aud_mst_d_mclk, - &aud_mst_e_mclk, - &aud_mst_f_mclk, - &aud_spdifout_clk_sel, - &aud_spdifout_clk_div, - &aud_spdifout_clk, - &aud_spdifin_clk_sel, - &aud_spdifin_clk_div, - &aud_spdifin_clk, - &aud_pdm_dclk_sel, - &aud_pdm_dclk_div, - &aud_pdm_dclk, - &aud_pdm_sysclk_sel, - &aud_pdm_sysclk_div, - &aud_pdm_sysclk, - &aud_mst_a_sclk_pre_en, - &aud_mst_b_sclk_pre_en, - &aud_mst_c_sclk_pre_en, - &aud_mst_d_sclk_pre_en, - &aud_mst_e_sclk_pre_en, - &aud_mst_f_sclk_pre_en, - &aud_mst_a_sclk_div, - &aud_mst_b_sclk_div, - &aud_mst_c_sclk_div, - &aud_mst_d_sclk_div, - &aud_mst_e_sclk_div, - &aud_mst_f_sclk_div, - &aud_mst_a_sclk_post_en, - &aud_mst_b_sclk_post_en, - &aud_mst_c_sclk_post_en, - &aud_mst_d_sclk_post_en, - &aud_mst_e_sclk_post_en, - &aud_mst_f_sclk_post_en, - &aud_mst_a_sclk, - &aud_mst_b_sclk, - &aud_mst_c_sclk, - &aud_mst_d_sclk, - &aud_mst_e_sclk, - &aud_mst_f_sclk, - &aud_mst_a_lrclk_div, - &aud_mst_b_lrclk_div, - &aud_mst_c_lrclk_div, - &aud_mst_d_lrclk_div, - &aud_mst_e_lrclk_div, - &aud_mst_f_lrclk_div, - &aud_mst_a_lrclk, - &aud_mst_b_lrclk, - &aud_mst_c_lrclk, - &aud_mst_d_lrclk, - &aud_mst_e_lrclk, - &aud_mst_f_lrclk, - &aud_tdmin_a_sclk_sel, - &aud_tdmin_b_sclk_sel, - &aud_tdmin_c_sclk_sel, - &aud_tdmin_lb_sclk_sel, - &aud_tdmout_a_sclk_sel, - &aud_tdmout_b_sclk_sel, - &aud_tdmout_c_sclk_sel, - &aud_tdmin_a_sclk_pre_en, - &aud_tdmin_b_sclk_pre_en, - &aud_tdmin_c_sclk_pre_en, - &aud_tdmin_lb_sclk_pre_en, - &aud_tdmout_a_sclk_pre_en, - &aud_tdmout_b_sclk_pre_en, - &aud_tdmout_c_sclk_pre_en, - &aud_tdmin_a_sclk_post_en, - &aud_tdmin_b_sclk_post_en, - &aud_tdmin_c_sclk_post_en, - &aud_tdmin_lb_sclk_post_en, - &aud_tdmout_a_sclk_post_en, - &aud_tdmout_b_sclk_post_en, - &aud_tdmout_c_sclk_post_en, - &aud_tdmin_a_sclk, - &aud_tdmin_b_sclk, - &aud_tdmin_c_sclk, - &aud_tdmin_lb_sclk, - &aud_tdmout_a_sclk, - &aud_tdmout_b_sclk, - &aud_tdmout_c_sclk, - &aud_tdmin_a_lrclk, - &aud_tdmin_b_lrclk, - &aud_tdmin_c_lrclk, - &aud_tdmin_lb_lrclk, - &aud_tdmout_a_lrclk, - &aud_tdmout_b_lrclk, - &aud_tdmout_c_lrclk, - &aud_spdifout_b_clk_sel, - &aud_spdifout_b_clk_div, - &aud_spdifout_b_clk, - &aud_tdm_mclk_pad_0, - &aud_tdm_mclk_pad_1, - &aud_tdm_lrclk_pad_0, - &aud_tdm_lrclk_pad_1, - &aud_tdm_lrclk_pad_2, - &aud_tdm_sclk_pad_0, - &aud_tdm_sclk_pad_1, - &aud_tdm_sclk_pad_2, +static struct clk_regmap *const axg_clk_regmaps[] = { + &ddr_arb, + &pdm, + &tdmin_a, + &tdmin_b, + &tdmin_c, + &tdmin_lb, + &tdmout_a, + &tdmout_b, + &tdmout_c, + &frddr_a, + &frddr_b, + &frddr_c, + &toddr_a, + &toddr_b, + &toddr_c, + &loopback, + &spdifin, + &spdifout, + &resample, + &power_detect, + &spdifout_b, + &mst_a_mclk_sel, + &mst_b_mclk_sel, + &mst_c_mclk_sel, + &mst_d_mclk_sel, + &mst_e_mclk_sel, + &mst_f_mclk_sel, + &mst_a_mclk_div, + &mst_b_mclk_div, + &mst_c_mclk_div, + &mst_d_mclk_div, + &mst_e_mclk_div, + &mst_f_mclk_div, + &mst_a_mclk, + &mst_b_mclk, + &mst_c_mclk, + &mst_d_mclk, + &mst_e_mclk, + &mst_f_mclk, + &spdifout_clk_sel, + &spdifout_clk_div, + &spdifout_clk, + &spdifin_clk_sel, + &spdifin_clk_div, + &spdifin_clk, + &pdm_dclk_sel, + &pdm_dclk_div, + &pdm_dclk, + &pdm_sysclk_sel, + &pdm_sysclk_div, + &pdm_sysclk, + &mst_a_sclk_pre_en, + &mst_b_sclk_pre_en, + &mst_c_sclk_pre_en, + &mst_d_sclk_pre_en, + &mst_e_sclk_pre_en, + &mst_f_sclk_pre_en, + &mst_a_sclk_div, + &mst_b_sclk_div, + &mst_c_sclk_div, + &mst_d_sclk_div, + &mst_e_sclk_div, + &mst_f_sclk_div, + &mst_a_sclk_post_en, + &mst_b_sclk_post_en, + &mst_c_sclk_post_en, + &mst_d_sclk_post_en, + &mst_e_sclk_post_en, + &mst_f_sclk_post_en, + &mst_a_sclk, + &mst_b_sclk, + &mst_c_sclk, + &mst_d_sclk, + &mst_e_sclk, + &mst_f_sclk, + &mst_a_lrclk_div, + &mst_b_lrclk_div, + &mst_c_lrclk_div, + &mst_d_lrclk_div, + &mst_e_lrclk_div, + &mst_f_lrclk_div, + &mst_a_lrclk, + &mst_b_lrclk, + &mst_c_lrclk, + &mst_d_lrclk, + &mst_e_lrclk, + &mst_f_lrclk, + &tdmin_a_sclk_sel, + &tdmin_b_sclk_sel, + &tdmin_c_sclk_sel, + &tdmin_lb_sclk_sel, + &tdmout_a_sclk_sel, + &tdmout_b_sclk_sel, + &tdmout_c_sclk_sel, + &tdmin_a_sclk_pre_en, + &tdmin_b_sclk_pre_en, + &tdmin_c_sclk_pre_en, + &tdmin_lb_sclk_pre_en, + &tdmout_a_sclk_pre_en, + &tdmout_b_sclk_pre_en, + &tdmout_c_sclk_pre_en, + &tdmin_a_sclk_post_en, + &tdmin_b_sclk_post_en, + &tdmin_c_sclk_post_en, + &tdmin_lb_sclk_post_en, + &tdmout_a_sclk_post_en, + &tdmout_b_sclk_post_en, + &tdmout_c_sclk_post_en, + &tdmin_a_sclk, + &tdmin_b_sclk, + &tdmin_c_sclk, + &tdmin_lb_sclk, + &tdmout_a_sclk, + &tdmout_b_sclk, + &tdmout_c_sclk, + &tdmin_a_lrclk, + &tdmin_b_lrclk, + &tdmin_c_lrclk, + &tdmin_lb_lrclk, + &tdmout_a_lrclk, + &tdmout_b_lrclk, + &tdmout_c_lrclk, + &spdifout_b_clk_sel, + &spdifout_b_clk_div, + &spdifout_b_clk, + &g12a_tdm_mclk_pad_0, + &g12a_tdm_mclk_pad_1, + &g12a_tdm_lrclk_pad_0, + &g12a_tdm_lrclk_pad_1, + &g12a_tdm_lrclk_pad_2, + &g12a_tdm_sclk_pad_0, + &g12a_tdm_sclk_pad_1, + &g12a_tdm_sclk_pad_2, + &toram, + &eqdrc, +}; + +static struct clk_regmap *const sm1_clk_regmaps[] = { + &ddr_arb, + &pdm, + &tdmin_a, + &tdmin_b, + &tdmin_c, + &tdmin_lb, + &tdmout_a, + &tdmout_b, + &tdmout_c, + &frddr_a, + &frddr_b, + &frddr_c, + &toddr_a, + &toddr_b, + &toddr_c, + &loopback, + &spdifin, + &spdifout, + &resample, + &spdifout_b, + &sm1_mst_a_mclk_sel, + &sm1_mst_b_mclk_sel, + &sm1_mst_c_mclk_sel, + &sm1_mst_d_mclk_sel, + &sm1_mst_e_mclk_sel, + &sm1_mst_f_mclk_sel, + &sm1_mst_a_mclk_div, + &sm1_mst_b_mclk_div, + &sm1_mst_c_mclk_div, + &sm1_mst_d_mclk_div, + &sm1_mst_e_mclk_div, + &sm1_mst_f_mclk_div, + &sm1_mst_a_mclk, + &sm1_mst_b_mclk, + &sm1_mst_c_mclk, + &sm1_mst_d_mclk, + &sm1_mst_e_mclk, + &sm1_mst_f_mclk, + &spdifout_clk_sel, + &spdifout_clk_div, + &spdifout_clk, + &spdifin_clk_sel, + &spdifin_clk_div, + &spdifin_clk, + &pdm_dclk_sel, + &pdm_dclk_div, + &pdm_dclk, + &pdm_sysclk_sel, + &pdm_sysclk_div, + &pdm_sysclk, + &mst_a_sclk_pre_en, + &mst_b_sclk_pre_en, + &mst_c_sclk_pre_en, + &mst_d_sclk_pre_en, + &mst_e_sclk_pre_en, + &mst_f_sclk_pre_en, + &mst_a_sclk_div, + &mst_b_sclk_div, + &mst_c_sclk_div, + &mst_d_sclk_div, + &mst_e_sclk_div, + &mst_f_sclk_div, + &mst_a_sclk_post_en, + &mst_b_sclk_post_en, + &mst_c_sclk_post_en, + &mst_d_sclk_post_en, + &mst_e_sclk_post_en, + &mst_f_sclk_post_en, + &mst_a_sclk, + &mst_b_sclk, + &mst_c_sclk, + &mst_d_sclk, + &mst_e_sclk, + &mst_f_sclk, + &mst_a_lrclk_div, + &mst_b_lrclk_div, + &mst_c_lrclk_div, + &mst_d_lrclk_div, + &mst_e_lrclk_div, + &mst_f_lrclk_div, + &mst_a_lrclk, + &mst_b_lrclk, + &mst_c_lrclk, + &mst_d_lrclk, + &mst_e_lrclk, + &mst_f_lrclk, + &tdmin_a_sclk_sel, + &tdmin_b_sclk_sel, + &tdmin_c_sclk_sel, + &tdmin_lb_sclk_sel, + &tdmout_a_sclk_sel, + &tdmout_b_sclk_sel, + &tdmout_c_sclk_sel, + &tdmin_a_sclk_pre_en, + &tdmin_b_sclk_pre_en, + &tdmin_c_sclk_pre_en, + &tdmin_lb_sclk_pre_en, + &tdmout_a_sclk_pre_en, + &tdmout_b_sclk_pre_en, + &tdmout_c_sclk_pre_en, + &tdmin_a_sclk_post_en, + &tdmin_b_sclk_post_en, + &tdmin_c_sclk_post_en, + &tdmin_lb_sclk_post_en, + &tdmout_a_sclk_post_en, + &tdmout_b_sclk_post_en, + &tdmout_c_sclk_post_en, + &tdmin_a_sclk, + &tdmin_b_sclk, + &tdmin_c_sclk, + &tdmin_lb_sclk, + &tdmout_a_sclk, + &tdmout_b_sclk, + &tdmout_c_sclk, + &tdmin_a_lrclk, + &tdmin_b_lrclk, + &tdmin_c_lrclk, + &tdmin_lb_lrclk, + &tdmout_a_lrclk, + &tdmout_b_lrclk, + &tdmout_c_lrclk, + &spdifout_b_clk_sel, + &spdifout_b_clk_div, + &spdifout_b_clk, + &sm1_tdm_mclk_pad_0, + &sm1_tdm_mclk_pad_1, + &sm1_tdm_lrclk_pad_0, + &sm1_tdm_lrclk_pad_1, + &sm1_tdm_lrclk_pad_2, + &sm1_tdm_sclk_pad_0, + &sm1_tdm_sclk_pad_1, + &sm1_tdm_sclk_pad_2, + &sm1_aud_top, + &toram, + &eqdrc, + &resample_b, + &tovad, + &locker, + &spdifin_lb, + &frddr_d, + &toddr_d, + &loopback_b, + &sm1_clk81_en, + &sm1_sysclk_a_div, + &sm1_sysclk_a_en, + &sm1_sysclk_b_div, + &sm1_sysclk_b_en, }; static int devm_clk_get_enable(struct device *dev, char *id) @@ -1001,10 +1615,12 @@ static const struct regmap_config axg_audio_regmap_cfg = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .max_register = AUDIO_CLK_PDMIN_CTRL1, + .max_register = AUDIO_CLK_SPDIFOUT_B_CTRL, }; struct audioclk_data { + struct clk_regmap *const *regmap_clks; + unsigned int regmap_clk_num; struct clk_hw_onecell_data *hw_onecell_data; unsigned int reset_offset; unsigned int reset_num; @@ -1016,7 +1632,6 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) const struct audioclk_data *data; struct axg_audio_reset_data *rst; struct regmap *map; - struct resource *res; void __iomem *regs; struct clk_hw *hw; int ret, i; @@ -1025,8 +1640,7 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) if (!data) return -EINVAL; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - regs = devm_ioremap_resource(dev, res); + regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -1048,8 +1662,8 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) } /* Populate regmap for the regmap backed clocks */ - for (i = 0; i < ARRAY_SIZE(aud_clk_regmaps); i++) - aud_clk_regmaps[i]->map = map; + for (i = 0; i < data->regmap_clk_num; i++) + data->regmap_clks[i]->map = map; /* Take care to skip the registered input clocks */ for (i = AUD_CLKID_DDR_ARB; i < data->hw_onecell_data->num; i++) { @@ -1093,15 +1707,27 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) } static const struct audioclk_data axg_audioclk_data = { + .regmap_clks = axg_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), .hw_onecell_data = &axg_audio_hw_onecell_data, }; static const struct audioclk_data g12a_audioclk_data = { + .regmap_clks = axg_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(axg_clk_regmaps), .hw_onecell_data = &g12a_audio_hw_onecell_data, .reset_offset = AUDIO_SW_RESET, .reset_num = 26, }; +static const struct audioclk_data sm1_audioclk_data = { + .regmap_clks = sm1_clk_regmaps, + .regmap_clk_num = ARRAY_SIZE(sm1_clk_regmaps), + .hw_onecell_data = &sm1_audio_hw_onecell_data, + .reset_offset = AUDIO_SM1_SW_RESET0, + .reset_num = 39, +}; + static const struct of_device_id clkc_match_table[] = { { .compatible = "amlogic,axg-audio-clkc", @@ -1109,6 +1735,9 @@ static const struct of_device_id clkc_match_table[] = { }, { .compatible = "amlogic,g12a-audio-clkc", .data = &g12a_audioclk_data + }, { + .compatible = "amlogic,sm1-audio-clkc", + .data = &sm1_audioclk_data }, {} }; MODULE_DEVICE_TABLE(of, clkc_match_table); @@ -1122,6 +1751,6 @@ static struct platform_driver axg_audio_driver = { }; module_platform_driver(axg_audio_driver); -MODULE_DESCRIPTION("Amlogic AXG/G12A Audio Clock driver"); +MODULE_DESCRIPTION("Amlogic AXG/G12A/SM1 Audio Clock driver"); MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/meson/axg-audio.h b/drivers/clk/meson/axg-audio.h index c00e28b2e1a9..fd65a7d0704b 100644 --- a/drivers/clk/meson/axg-audio.h +++ b/drivers/clk/meson/axg-audio.h @@ -50,6 +50,20 @@ #define AUDIO_CLK_PDMIN_CTRL1 0x0B0 #define AUDIO_CLK_SPDIFOUT_B_CTRL 0x0B4 +/* SM1 introduce new register and some shifts :( */ +#define AUDIO_CLK_GATE_EN1 0x004 +#define AUDIO_SM1_MCLK_A_CTRL 0x008 +#define AUDIO_SM1_MCLK_B_CTRL 0x00C +#define AUDIO_SM1_MCLK_C_CTRL 0x010 +#define AUDIO_SM1_MCLK_D_CTRL 0x014 +#define AUDIO_SM1_MCLK_E_CTRL 0x018 +#define AUDIO_SM1_MCLK_F_CTRL 0x01C +#define AUDIO_SM1_MST_PAD_CTRL0 0x020 +#define AUDIO_SM1_MST_PAD_CTRL1 0x024 +#define AUDIO_SM1_SW_RESET0 0x028 +#define AUDIO_SM1_SW_RESET1 0x02C +#define AUDIO_CLK81_CTRL 0x030 +#define AUDIO_CLK81_EN 0x034 /* * CLKID index values * These indices are entirely contrived and do not map onto the hardware. @@ -115,10 +129,15 @@ #define AUD_CLKID_TDMOUT_C_SCLK_POST_EN 150 #define AUD_CLKID_SPDIFOUT_B_CLK_SEL 153 #define AUD_CLKID_SPDIFOUT_B_CLK_DIV 154 +#define AUD_CLKID_CLK81_EN 173 +#define AUD_CLKID_SYSCLK_A_DIV 174 +#define AUD_CLKID_SYSCLK_B_DIV 175 +#define AUD_CLKID_SYSCLK_A_EN 176 +#define AUD_CLKID_SYSCLK_B_EN 177 /* include the CLKIDs which are part of the DT bindings */ #include <dt-bindings/clock/axg-audio-clkc.h> -#define NR_CLKS 163 +#define NR_CLKS 178 #endif /*__AXG_AUDIO_CLKC_H */ diff --git a/drivers/clk/mvebu/ap-cpu-clk.c b/drivers/clk/mvebu/ap-cpu-clk.c index af5e5acad370..6b394302c76a 100644 --- a/drivers/clk/mvebu/ap-cpu-clk.c +++ b/drivers/clk/mvebu/ap-cpu-clk.c @@ -274,8 +274,8 @@ static int ap_cpu_clock_probe(struct platform_device *pdev) if (!ap_cpu_clk) return -ENOMEM; - ap_cpu_data = devm_kzalloc(dev, sizeof(*ap_cpu_data) + - sizeof(struct clk_hw *) * nclusters, + ap_cpu_data = devm_kzalloc(dev, struct_size(ap_cpu_data, hws, + nclusters), GFP_KERNEL); if (!ap_cpu_data) return -ENOMEM; diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c index 5fc6d486a381..f5746f9ea929 100644 --- a/drivers/clk/mvebu/armada-37xx-periph.c +++ b/drivers/clk/mvebu/armada-37xx-periph.c @@ -303,6 +303,7 @@ PERIPH_CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, clk_table1); PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6); PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12); PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18); +static PERIPH_GATE(pcie, 14); static struct clk_periph_data data_sb[] = { REF_CLK_MUX_DD(gbe_50), @@ -318,6 +319,7 @@ static struct clk_periph_data data_sb[] = { REF_CLK_FULL_DD(sdio), REF_CLK_FULL_DD(usb32_usb2_sys), REF_CLK_FULL_DD(usb32_ss_sys), + REF_CLK_GATE(pcie, "gbe_core"), { }, }; @@ -712,8 +714,8 @@ static int __maybe_unused armada_3700_periph_clock_resume(struct device *dev) } static const struct dev_pm_ops armada_3700_periph_clock_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(armada_3700_periph_clock_suspend, - armada_3700_periph_clock_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(armada_3700_periph_clock_suspend, + armada_3700_periph_clock_resume) }; static int armada_3700_periph_clock_probe(struct platform_device *pdev) diff --git a/drivers/clk/mvebu/armada-xp.c b/drivers/clk/mvebu/armada-xp.c index fa1568279c23..45665655a258 100644 --- a/drivers/clk/mvebu/armada-xp.c +++ b/drivers/clk/mvebu/armada-xp.c @@ -50,12 +50,6 @@ static u32 __init axp_get_tclk_freq(void __iomem *sar) return 250000000; } -/* MV98DX3236 TCLK frequency is fixed to 200MHz */ -static u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar) -{ - return 200000000; -} - static const u32 axp_cpu_freqs[] __initconst = { 1000000000, 1066000000, @@ -93,12 +87,6 @@ static u32 __init axp_get_cpu_freq(void __iomem *sar) return cpu_freq; } -/* MV98DX3236 CLK frequency is fixed to 800MHz */ -static u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar) -{ - return 800000000; -} - static const int axp_nbclk_ratios[32][2] __initconst = { {0, 1}, {1, 2}, {2, 2}, {2, 2}, {1, 2}, {1, 2}, {1, 1}, {2, 3}, @@ -168,11 +156,6 @@ static const struct coreclk_soc_desc axp_coreclks = { .num_ratios = ARRAY_SIZE(axp_coreclk_ratios), }; -static const struct coreclk_soc_desc mv98dx3236_coreclks = { - .get_tclk_freq = mv98dx3236_get_tclk_freq, - .get_cpu_freq = mv98dx3236_get_cpu_freq, -}; - /* * Clock Gating Control */ @@ -210,15 +193,6 @@ static const struct clk_gating_soc_desc axp_gating_desc[] __initconst = { { } }; -static const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = { - { "ge1", NULL, 3, 0 }, - { "ge0", NULL, 4, 0 }, - { "pex00", NULL, 5, 0 }, - { "sdio", NULL, 17, 0 }, - { "xor0", NULL, 22, 0 }, - { } -}; - static void __init axp_clk_init(struct device_node *np) { struct device_node *cgnp = diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c index 808463276145..84c8900542e4 100644 --- a/drivers/clk/mvebu/cp110-system-controller.c +++ b/drivers/clk/mvebu/cp110-system-controller.c @@ -235,8 +235,8 @@ static int cp110_syscon_common_probe(struct platform_device *pdev, if (ret) return ret; - cp110_clk_data = devm_kzalloc(dev, sizeof(*cp110_clk_data) + - sizeof(struct clk_hw *) * CP110_CLK_NUM, + cp110_clk_data = devm_kzalloc(dev, struct_size(cp110_clk_data, hws, + CP110_CLK_NUM), GFP_KERNEL); if (!cp110_clk_data) return -ENOMEM; diff --git a/drivers/clk/pxa/clk-pxa27x.c b/drivers/clk/pxa/clk-pxa27x.c index 287fdeae7c7c..7b123105b5de 100644 --- a/drivers/clk/pxa/clk-pxa27x.c +++ b/drivers/clk/pxa/clk-pxa27x.c @@ -459,6 +459,7 @@ struct dummy_clk { }; static struct dummy_clk dummy_clks[] __initdata = { DUMMY_CLK(NULL, "pxa27x-gpio", "osc_32_768khz"), + DUMMY_CLK(NULL, "pxa-rtc", "osc_32_768khz"), DUMMY_CLK(NULL, "sa1100-rtc", "osc_32_768khz"), DUMMY_CLK("UARTCLK", "pxa2xx-ir", "STUART"), }; diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 32dbb4f09492..3b33ef129274 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -220,6 +220,15 @@ config MSM_GCC_8998 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, UFS, SD/eMMC, PCIe, etc. +config MSM_GPUCC_8998 + tristate "MSM8998 Graphics Clock Controller" + select MSM_GCC_8998 + select QCOM_GDSC + help + Support for the graphics clock controller on MSM8998 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config QCS_GCC_404 tristate "QCS404 Global Clock Controller" help @@ -227,6 +236,15 @@ config QCS_GCC_404 Say Y if you want to use multimedia devices or peripheral devices such as UART, SPI, I2C, USB, SD/eMMC, PCIe etc. +config SC_GCC_7180 + tristate "SC7180 Global Clock Controller" + select QCOM_GDSC + depends on COMMON_CLK_QCOM + help + Support for the global clock controller on SC7180 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + I2C, USB, UFS, SDCC, etc. + config SDM_CAMCC_845 tristate "SDM845 Camera Clock Controller" select SDM_GCC_845 @@ -248,6 +266,14 @@ config QCS_TURING_404 Support for the Turing Clock Controller on QCS404, provides clocks and resets for the Turing subsystem. +config QCS_Q6SSTOP_404 + tristate "QCS404 Q6SSTOP Clock Controller" + select QCS_GCC_404 + help + Support for the Q6SSTOP clock controller on QCS404 devices. + Say Y if you want to use the Q6SSTOP branch clocks of the WCSS clock + controller to reset the Q6SSTOP subsystem. + config SDM_GCC_845 tristate "SDM845 Global Clock Controller" select QCOM_GDSC diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 4a813b4055d0..d899661d0f44 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_MSM_GCC_8994) += gcc-msm8994.o obj-$(CONFIG_MSM_GCC_8996) += gcc-msm8996.o obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8998) += gcc-msm8998.o +obj-$(CONFIG_MSM_GPUCC_8998) += gpucc-msm8998.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8996) += mmcc-msm8996.o @@ -42,7 +43,9 @@ obj-$(CONFIG_QCOM_CLK_RPM) += clk-rpm.o obj-$(CONFIG_QCOM_CLK_RPMH) += clk-rpmh.o obj-$(CONFIG_QCOM_CLK_SMD_RPM) += clk-smd-rpm.o obj-$(CONFIG_QCS_GCC_404) += gcc-qcs404.o +obj-$(CONFIG_QCS_Q6SSTOP_404) += q6sstop-qcs404.o obj-$(CONFIG_QCS_TURING_404) += turingcc-qcs404.o +obj-$(CONFIG_SC_GCC_7180) += gcc-sc7180.o obj-$(CONFIG_SDM_CAMCC_845) += camcc-sdm845.o obj-$(CONFIG_SDM_DISPCC_845) += dispcc-sdm845.o obj-$(CONFIG_SDM_GCC_660) += gcc-sdm660.o diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index c25b57c3cbc8..78358b81d249 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -168,7 +168,7 @@ struct clk_rcg_dfs_data { }; #define DEFINE_RCG_DFS(r) \ - { .rcg = &r##_src, .init = &r##_init } + { .rcg = &r, .init = &r##_init } extern int qcom_cc_register_rcg_dfs(struct regmap *regmap, const struct clk_rcg_dfs_data *rcgs, diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index b98b81ef43a1..8f4b9bec2956 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -206,7 +206,7 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, break; default: return -EINVAL; - }; + } if (!f) return -EINVAL; @@ -220,6 +220,8 @@ static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f, if (clk_flags & CLK_SET_RATE_PARENT) { rate = f->freq; if (f->pre_div) { + if (!rate) + rate = req->rate; rate /= 2; rate *= f->pre_div + 1; } @@ -319,7 +321,7 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, break; default: return -EINVAL; - }; + } if (!f) return -EINVAL; diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 96a36f6ff667..2dbbe47e8d4f 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -334,13 +334,14 @@ static const struct clk_ops clk_rpmh_bcm_ops = { .recalc_rate = clk_rpmh_bcm_recalc_rate, }; -/* Resource name must match resource id present in cmd-db. */ +/* Resource name must match resource id present in cmd-db */ DEFINE_CLK_RPMH_ARC(sdm845, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2); DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2); DEFINE_CLK_RPMH_VRM(sdm845, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk1, rf_clk1_ao, "rfclka1", 1); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk2, rf_clk2_ao, "rfclka2", 1); DEFINE_CLK_RPMH_VRM(sdm845, rf_clk3, rf_clk3_ao, "rfclka3", 1); +DEFINE_CLK_RPMH_VRM(sm8150, rf_clk3, rf_clk3_ao, "rfclka3", 1); DEFINE_CLK_RPMH_BCM(sdm845, ipa, "IP0"); static struct clk_hw *sdm845_rpmh_clocks[] = { @@ -364,26 +365,19 @@ static const struct clk_rpmh_desc clk_rpmh_sdm845 = { .num_clks = ARRAY_SIZE(sdm845_rpmh_clocks), }; -DEFINE_CLK_RPMH_ARC(sm8150, bi_tcxo, bi_tcxo_ao, "xo.lvl", 0x3, 2); -DEFINE_CLK_RPMH_VRM(sm8150, ln_bb_clk2, ln_bb_clk2_ao, "lnbclka2", 2); -DEFINE_CLK_RPMH_VRM(sm8150, ln_bb_clk3, ln_bb_clk3_ao, "lnbclka3", 2); -DEFINE_CLK_RPMH_VRM(sm8150, rf_clk1, rf_clk1_ao, "rfclka1", 1); -DEFINE_CLK_RPMH_VRM(sm8150, rf_clk2, rf_clk2_ao, "rfclka2", 1); -DEFINE_CLK_RPMH_VRM(sm8150, rf_clk3, rf_clk3_ao, "rfclka3", 1); - static struct clk_hw *sm8150_rpmh_clocks[] = { - [RPMH_CXO_CLK] = &sm8150_bi_tcxo.hw, - [RPMH_CXO_CLK_A] = &sm8150_bi_tcxo_ao.hw, - [RPMH_LN_BB_CLK2] = &sm8150_ln_bb_clk2.hw, - [RPMH_LN_BB_CLK2_A] = &sm8150_ln_bb_clk2_ao.hw, - [RPMH_LN_BB_CLK3] = &sm8150_ln_bb_clk3.hw, - [RPMH_LN_BB_CLK3_A] = &sm8150_ln_bb_clk3_ao.hw, - [RPMH_RF_CLK1] = &sm8150_rf_clk1.hw, - [RPMH_RF_CLK1_A] = &sm8150_rf_clk1_ao.hw, - [RPMH_RF_CLK2] = &sm8150_rf_clk2.hw, - [RPMH_RF_CLK2_A] = &sm8150_rf_clk2_ao.hw, - [RPMH_RF_CLK3] = &sm8150_rf_clk3.hw, - [RPMH_RF_CLK3_A] = &sm8150_rf_clk3_ao.hw, + [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw, + [RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw, + [RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw, + [RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw, + [RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw, + [RPMH_LN_BB_CLK3_A] = &sdm845_ln_bb_clk3_ao.hw, + [RPMH_RF_CLK1] = &sdm845_rf_clk1.hw, + [RPMH_RF_CLK1_A] = &sdm845_rf_clk1_ao.hw, + [RPMH_RF_CLK2] = &sdm845_rf_clk2.hw, + [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw, + [RPMH_RF_CLK3] = &sdm845_rf_clk3.hw, + [RPMH_RF_CLK3_A] = &sdm845_rf_clk3_ao.hw, }; static const struct clk_rpmh_desc clk_rpmh_sm8150 = { @@ -391,6 +385,24 @@ static const struct clk_rpmh_desc clk_rpmh_sm8150 = { .num_clks = ARRAY_SIZE(sm8150_rpmh_clocks), }; +static struct clk_hw *sc7180_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &sdm845_bi_tcxo.hw, + [RPMH_CXO_CLK_A] = &sdm845_bi_tcxo_ao.hw, + [RPMH_LN_BB_CLK2] = &sdm845_ln_bb_clk2.hw, + [RPMH_LN_BB_CLK2_A] = &sdm845_ln_bb_clk2_ao.hw, + [RPMH_LN_BB_CLK3] = &sdm845_ln_bb_clk3.hw, + [RPMH_LN_BB_CLK3_A] = &sdm845_ln_bb_clk3_ao.hw, + [RPMH_RF_CLK1] = &sdm845_rf_clk1.hw, + [RPMH_RF_CLK1_A] = &sdm845_rf_clk1_ao.hw, + [RPMH_RF_CLK2] = &sdm845_rf_clk2.hw, + [RPMH_RF_CLK2_A] = &sdm845_rf_clk2_ao.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_sc7180 = { + .clks = sc7180_rpmh_clocks, + .num_clks = ARRAY_SIZE(sc7180_rpmh_clocks), +}; + static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -471,6 +483,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) static const struct of_device_id clk_rpmh_match_table[] = { { .compatible = "qcom,sdm845-rpmh-clk", .data = &clk_rpmh_sdm845}, { .compatible = "qcom,sm8150-rpmh-clk", .data = &clk_rpmh_sm8150}, + { .compatible = "qcom,sc7180-rpmh-clk", .data = &clk_rpmh_sc7180}, { } }; MODULE_DEVICE_TABLE(of, clk_rpmh_match_table); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index fef5e8157061..930fa4a4c52a 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -648,6 +648,7 @@ static const struct rpm_smd_clk_desc rpm_clk_qcs404 = { }; /* msm8998 */ +DEFINE_CLK_SMD_RPM(msm8998, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); DEFINE_CLK_SMD_RPM(msm8998, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); DEFINE_CLK_SMD_RPM(msm8998, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); DEFINE_CLK_SMD_RPM(msm8998, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); @@ -670,6 +671,8 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk2_pin, rf_clk2_a_pin, 5); DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8998, rf_clk3, rf_clk3_a, 6); DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8998, rf_clk3_pin, rf_clk3_a_pin, 6); static struct clk_smd_rpm *msm8998_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8998_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8998_pcnoc_a_clk, [RPM_SMD_SNOC_CLK] = &msm8998_snoc_clk, [RPM_SMD_SNOC_A_CLK] = &msm8998_snoc_a_clk, [RPM_SMD_CNOC_CLK] = &msm8998_cnoc_clk, diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index 28ddc747d703..60d2a78d1395 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -29,6 +29,9 @@ struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate) if (!f) return NULL; + if (!f->freq) + return f; + for (; f->freq; f++) if (rate <= f->freq) return f; @@ -218,7 +221,7 @@ static struct clk_hw *qcom_cc_clk_hw_get(struct of_phandle_args *clkspec, return ERR_PTR(-EINVAL); } - return cc->rclks[idx] ? &cc->rclks[idx]->hw : ERR_PTR(-ENOENT); + return cc->rclks[idx] ? &cc->rclks[idx]->hw : NULL; } int qcom_cc_really_probe(struct platform_device *pdev, diff --git a/drivers/clk/qcom/gcc-msm8998.c b/drivers/clk/qcom/gcc-msm8998.c index 091acd59c1d6..cf31b5d03270 100644 --- a/drivers/clk/qcom/gcc-msm8998.c +++ b/drivers/clk/qcom/gcc-msm8998.c @@ -1266,6 +1266,72 @@ static struct clk_branch gcc_bimc_mss_q6_axi_clk = { }, }; +static struct clk_branch gcc_mss_cfg_ahb_clk = { + .halt_reg = 0x8a000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8a000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mss_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mss_snoc_axi_clk = { + .halt_reg = 0x8a03c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8a03c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mss_snoc_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mss_mnoc_bimc_axi_clk = { + .halt_reg = 0x8a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8a004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mss_mnoc_bimc_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52004, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mss_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x5200c, + .enable_mask = BIT(2), + .hw.init = &(struct clk_init_data){ + .name = "gcc_mss_gpll0_div_clk_src", + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_blsp1_ahb_clk = { .halt_reg = 0x17004, .halt_check = BRANCH_HALT_VOTED, @@ -2832,6 +2898,11 @@ static struct clk_regmap *gcc_msm8998_clocks[] = { [GCC_USB3_CLKREF_CLK] = &gcc_usb3_clkref_clk.clkr, [GCC_PCIE_CLKREF_CLK] = &gcc_pcie_clkref_clk.clkr, [GCC_RX1_USB2_CLKREF_CLK] = &gcc_rx1_usb2_clkref_clk.clkr, + [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_MSS_GPLL0_DIV_CLK_SRC] = &gcc_mss_gpll0_div_clk_src.clkr, + [GCC_MSS_SNOC_AXI_CLK] = &gcc_mss_snoc_axi_clk.clkr, + [GCC_MSS_MNOC_BIMC_AXI_CLK] = &gcc_mss_mnoc_bimc_axi_clk.clkr, }; static struct gdsc *gcc_msm8998_gdscs[] = { @@ -2928,6 +2999,7 @@ static const struct qcom_reset_map gcc_msm8998_resets[] = { [GCC_GPU_BCR] = { 0x71000 }, [GCC_SPSS_BCR] = { 0x72000 }, [GCC_OBT_ODT_BCR] = { 0x73000 }, + [GCC_MSS_RESTART] = { 0x79000 }, [GCC_VS_BCR] = { 0x7a000 }, [GCC_MSS_VS_RESET] = { 0x7a100 }, [GCC_GPU_VS_RESET] = { 0x7a104 }, diff --git a/drivers/clk/qcom/gcc-sc7180.c b/drivers/clk/qcom/gcc-sc7180.c new file mode 100644 index 000000000000..38424e63bcae --- /dev/null +++ b/drivers/clk/qcom/gcc-sc7180.c @@ -0,0 +1,2450 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,gcc-sc7180.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_GPLL0_OUT_EVEN, + P_GPLL0_OUT_MAIN, + P_GPLL1_OUT_MAIN, + P_GPLL4_OUT_MAIN, + P_GPLL6_OUT_MAIN, + P_GPLL7_OUT_MAIN, + P_SLEEP_CLK, +}; + +static struct clk_alpha_pll gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpll0", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + .name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpll0_out_even", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static struct clk_fixed_factor gcc_pll0_main_div_cdiv = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data){ + .name = "gcc_pll0_main_div_cdiv", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_alpha_pll gpll1 = { + .offset = 0x01000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(1), + .hw.init = &(struct clk_init_data){ + .name = "gpll1", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + .name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll4 = { + .offset = 0x76000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gpll4", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + .name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll6 = { + .offset = 0x13000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "gpll6", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + .name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, + }, +}; + +static struct clk_alpha_pll gpll7 = { + .offset = 0x27000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gpll7", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo", + .name = "bi_tcxo", + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct clk_parent_data gcc_parent_data_0_ao[] = { + { .fw_name = "bi_tcxo_ao", .name = "bi_tcxo_ao" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL6_OUT_MAIN, 2 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll6.clkr.hw }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL1_OUT_MAIN, 4 }, + { P_GPLL4_OUT_MAIN, 5 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll1.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .fw_name = "sleep_clk", .name = "sleep_clk" }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL7_OUT_MAIN, 3 }, + { P_GPLL0_OUT_EVEN, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll7.clkr.hw }, + { .hw = &gpll0_out_even.clkr.hw }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .fw_name = "bi_tcxo", .name = "bi_tcxo" }, + { .hw = &gpll0.clkr.hw }, + { .fw_name = "sleep_clk", .name = "sleep_clk" }, + { .fw_name = "core_bi_pll_test_se", .name = "core_bi_pll_test_se" }, +}; + +static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_cpuss_ahb_clk_src = { + .cmd_rcgr = 0x48014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_cpuss_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_ahb_clk_src", + .parent_data = gcc_parent_data_0_ao, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), + F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(200000000, P_GPLL0_OUT_EVEN, 1.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x64004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x65004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_gp3_clk_src = { + .cmd_rcgr = 0x66004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_GPLL0_OUT_EVEN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x33010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qspi_core_clk_src[] = { + F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0), + F(150000000, P_GPLL0_OUT_EVEN, 2, 0, 0), + F(300000000, P_GPLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qspi_core_clk_src = { + .cmd_rcgr = 0x4b00c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_qspi_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_qspi_core_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = 6, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = { + F(7372800, P_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GPLL0_OUT_EVEN, 1, 4, 25), + F(64000000, P_GPLL0_OUT_EVEN, 1, 16, 75), + F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(102400000, P_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GPLL0_OUT_EVEN, 2.5, 0, 0), + F(128000000, P_GPLL6_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { + .name = "gcc_qupv3_wrap0_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { + .cmd_rcgr = 0x17034, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { + .name = "gcc_qupv3_wrap0_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { + .cmd_rcgr = 0x17164, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { + .name = "gcc_qupv3_wrap0_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { + .cmd_rcgr = 0x17294, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { + .name = "gcc_qupv3_wrap0_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { + .cmd_rcgr = 0x173c4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { + .name = "gcc_qupv3_wrap0_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { + .cmd_rcgr = 0x174f4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { + .name = "gcc_qupv3_wrap0_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { + .cmd_rcgr = 0x17624, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { + .name = "gcc_qupv3_wrap1_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x18018, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { + .name = "gcc_qupv3_wrap1_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x18148, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s2_clk_src_init = { + .name = "gcc_qupv3_wrap1_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s2_clk_src = { + .cmd_rcgr = 0x18278, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { + .name = "gcc_qupv3_wrap1_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x183a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { + .name = "gcc_qupv3_wrap1_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x184d8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { + .name = "gcc_qupv3_wrap1_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x18608, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, +}; + + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_BI_TCXO, 16, 3, 25), + F(400000, P_BI_TCXO, 12, 1, 4), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(20000000, P_GPLL0_OUT_EVEN, 5, 1, 3), + F(25000000, P_GPLL0_OUT_EVEN, 6, 1, 2), + F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0), + F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x12028, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(150000000, P_GPLL0_OUT_EVEN, 2, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(300000000, P_GPLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x12010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(202000000, P_GPLL7_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { + .cmd_rcgr = 0x1400c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = 5, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0), + F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GPLL0_OUT_EVEN, 3, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x77020, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_axi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(37500000, P_GPLL0_OUT_EVEN, 8, 0, 0), + F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0), + F(150000000, P_GPLL0_OUT_EVEN, 2, 0, 0), + F(300000000, P_GPLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { + .cmd_rcgr = 0x77048, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_ice_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { + .cmd_rcgr = 0x77098, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_ufs_phy_phy_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_phy_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_core_clk_src[] = { + F(37500000, P_GPLL0_OUT_EVEN, 8, 0, 0), + F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0), + F(150000000, P_GPLL0_OUT_EVEN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { + .cmd_rcgr = 0x77060, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_ufs_phy_unipro_core_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_unipro_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, P_GPLL0_OUT_EVEN, 4.5, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { + .cmd_rcgr = 0xf01c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_prim_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_mock_utmi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(20000000, P_GPLL0_OUT_EVEN, 15, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0xf034, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_mock_utmi_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_prim_mock_utmi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb3_prim_phy_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0xf060, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_usb3_prim_phy_aux_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_prim_phy_aux_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = 4, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { + .halt_reg = 0x82024, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x82024, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x82024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_aggre_ufs_phy_axi_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_usb3_prim_axi_clk = { + .halt_reg = 0x8201c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8201c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_aggre_usb3_prim_axi_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_ahb_clk = { + .halt_reg = 0xb008, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0xb008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xb008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_camera_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_hf_axi_clk = { + .halt_reg = 0xb020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_camera_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_throttle_hf_axi_clk = { + .halt_reg = 0xb080, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0xb080, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xb080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_camera_throttle_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_xo_clk = { + .halt_reg = 0xb02c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb02c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_camera_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_ahb_clk = { + .halt_reg = 0x4100c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x4100c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(3), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_axi_clk = { + .halt_reg = 0x41008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(4), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ce1_clk = { + .halt_reg = 0x41004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(5), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ce1_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb3_prim_axi_clk = { + .halt_reg = 0x502c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x502c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cfg_noc_usb3_prim_axi_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* For CPUSS functionality the AHB clock needs to be left enabled */ +static struct clk_branch gcc_cpuss_ahb_clk = { + .halt_reg = 0x48000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(21), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_ahb_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_cpuss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cpuss_rbcpr_clk = { + .halt_reg = 0x48008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x48008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_cpuss_rbcpr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ddrss_gpu_axi_clk = { + .halt_reg = 0x4452c, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x4452c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ddrss_gpu_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(18), + .hw.init = &(struct clk_init_data){ + .name = "gcc_disp_gpll0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(19), + .hw.init = &(struct clk_init_data){ + .name = "gcc_disp_gpll0_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_pll0_main_div_cdiv.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_hf_axi_clk = { + .halt_reg = 0xb024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_disp_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_throttle_hf_axi_clk = { + .halt_reg = 0xb084, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0xb084, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xb084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_disp_throttle_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_disp_xo_clk = { + .halt_reg = 0xb030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_disp_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x64000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x64000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x65000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x65000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp2_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x66000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x66000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gp3_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_gp3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gpu_gpll0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(16), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gpu_gpll0_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_pll0_main_div_cdiv.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_memnoc_gfx_clk = { + .halt_reg = 0x7100c, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x7100c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gpu_memnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_snoc_dvm_gfx_clk = { + .halt_reg = 0x71018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x71018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_gpu_snoc_dvm_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_axi_clk = { + .halt_reg = 0x4d008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4d008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_bwmon_axi_clk = { + .halt_reg = 0x73008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x73008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_bwmon_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_bwmon_dma_cfg_ahb_clk = { + .halt_reg = 0x73018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x73018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_bwmon_dma_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_bwmon_dsp_cfg_ahb_clk = { + .halt_reg = 0x7301c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x7301c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_bwmon_dsp_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_cfg_ahb_clk = { + .halt_reg = 0x4d004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x4d004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x4d004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_dma_clk = { + .halt_reg = 0x4d1a0, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x4d1a0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x4d1a0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_dma_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(25), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_gpll0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_npu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(26), + .hw.init = &(struct clk_init_data){ + .name = "gcc_npu_gpll0_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_pll0_main_div_cdiv.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x3300c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3300c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm2_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x33004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x33004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x33008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_prng_ahb_clk = { + .halt_reg = 0x34004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x34004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(13), + .hw.init = &(struct clk_init_data){ + .name = "gcc_prng_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qspi_cnoc_periph_ahb_clk = { + .halt_reg = 0x4b004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x4b004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x4b004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qspi_cnoc_periph_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qspi_core_clk = { + .halt_reg = 0x4b008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4b008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qspi_core_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qspi_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_core_2x_clk = { + .halt_reg = 0x17014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(9), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_core_clk = { + .halt_reg = 0x1700c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(8), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s0_clk = { + .halt_reg = 0x17030, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(10), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s1_clk = { + .halt_reg = 0x17160, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(11), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s2_clk = { + .halt_reg = 0x17290, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(12), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s2_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s3_clk = { + .halt_reg = 0x173c0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(13), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s3_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s4_clk = { + .halt_reg = 0x174f0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(14), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s4_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap0_s5_clk = { + .halt_reg = 0x17620, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(15), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap0_s5_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap0_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x18004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(18), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_clk = { + .halt_reg = 0x18008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(19), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s0_clk = { + .halt_reg = 0x18014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(22), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s1_clk = { + .halt_reg = 0x18144, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(23), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s2_clk = { + .halt_reg = 0x18274, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(24), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s2_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s3_clk = { + .halt_reg = 0x183a4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(25), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s3_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s4_clk = { + .halt_reg = 0x184d4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(26), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s4_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s5_clk = { + .halt_reg = 0x18604, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(27), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap1_s5_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_qupv3_wrap1_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_0_m_ahb_clk = { + .halt_reg = 0x17004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(6), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap_0_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_0_s_ahb_clk = { + .halt_reg = 0x17008, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x17008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(7), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap_0_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_m_ahb_clk = { + .halt_reg = 0x1800c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(20), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap_1_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_s_ahb_clk = { + .halt_reg = 0x18010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x18010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(21), + .hw.init = &(struct clk_init_data){ + .name = "gcc_qupv3_wrap_1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x12008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x1200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1200c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_apps_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x12040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc1_ice_core_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x14008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x14004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sdcc2_apps_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_sdcc2_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +/* For CPUSS functionality the SYS NOC clock needs to be left enabled */ +static struct clk_branch gcc_sys_noc_cpuss_ahb_clk = { + .halt_reg = 0x4144, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_sys_noc_cpuss_ahb_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_cpuss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_mem_clkref_clk = { + .halt_reg = 0x8c000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8c000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_mem_clkref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ahb_clk = { + .halt_reg = 0x77014, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x77014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_axi_clk = { + .halt_reg = 0x77038, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x77038, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_axi_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ice_core_clk = { + .halt_reg = 0x77090, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x77090, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77090, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_ice_core_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_ufs_phy_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_phy_aux_clk = { + .halt_reg = 0x77094, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x77094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77094, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_phy_aux_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_ufs_phy_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_0_clk = { + .halt_reg = 0x7701c, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x7701c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_rx_symbol_0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_tx_symbol_0_clk = { + .halt_reg = 0x77018, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x77018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_tx_symbol_0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_unipro_core_clk = { + .halt_reg = 0x7708c, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x7708c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7708c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_ufs_phy_unipro_core_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_ufs_phy_unipro_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_master_clk = { + .halt_reg = 0xf010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_prim_master_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_mock_utmi_clk = { + .halt_reg = 0xf018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf018, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_prim_mock_utmi_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = + &gcc_usb30_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_sleep_clk = { + .halt_reg = 0xf014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb30_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_clkref_clk = { + .halt_reg = 0x8c010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8c010, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_prim_clkref_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_aux_clk = { + .halt_reg = 0xf050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_prim_phy_aux_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0xf054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xf054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_prim_phy_com_aux_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0xf058, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0xf058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb3_prim_phy_pipe_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb_phy_cfg_ahb2phy_clk = { + .halt_reg = 0x6a004, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0x6a004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x6a004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_usb_phy_cfg_ahb2phy_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi_clk = { + .halt_reg = 0xb01c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb01c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_video_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(20), + .hw.init = &(struct clk_init_data){ + .name = "gcc_video_gpll0_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &gcc_pll0_main_div_cdiv.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_throttle_axi_clk = { + .halt_reg = 0xb07c, + .halt_check = BRANCH_HALT, + .hwcg_reg = 0xb07c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0xb07c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_video_throttle_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_xo_clk = { + .halt_reg = 0xb028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0xb028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_video_xo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc ufs_phy_gdsc = { + .gdscr = 0x77004, + .pd = { + .name = "ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc usb30_prim_gdsc = { + .gdscr = 0x0f004, + .pd = { + .name = "usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc = { + .gdscr = 0x7d040, + .pd = { + .name = "hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON | VOTABLE, +}; + +static struct gdsc hlos1_vote_mmnoc_mmu_tbu_sf_gdsc = { + .gdscr = 0x7d044, + .pd = { + .name = "hlos1_vote_mmnoc_mmu_tbu_sf_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON | VOTABLE, +}; + +static struct gdsc *gcc_sc7180_gdscs[] = { + [UFS_PHY_GDSC] = &ufs_phy_gdsc, + [USB30_PRIM_GDSC] = &usb30_prim_gdsc, + [HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC] = + &hlos1_vote_mmnoc_mmu_tbu_hf0_gdsc, + [HLOS1_VOTE_MMNOC_MMU_TBU_SF_GDSC] = + &hlos1_vote_mmnoc_mmu_tbu_sf_gdsc, +}; + + +static struct clk_hw *gcc_sc7180_hws[] = { + [GCC_GPLL0_MAIN_DIV_CDIV] = &gcc_pll0_main_div_cdiv.hw, +}; + +static struct clk_regmap *gcc_sc7180_clocks[] = { + [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr, + [GCC_AGGRE_USB3_PRIM_AXI_CLK] = &gcc_aggre_usb3_prim_axi_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CAMERA_AHB_CLK] = &gcc_camera_ahb_clk.clkr, + [GCC_CAMERA_HF_AXI_CLK] = &gcc_camera_hf_axi_clk.clkr, + [GCC_CAMERA_THROTTLE_HF_AXI_CLK] = &gcc_camera_throttle_hf_axi_clk.clkr, + [GCC_CAMERA_XO_CLK] = &gcc_camera_xo_clk.clkr, + [GCC_CE1_AHB_CLK] = &gcc_ce1_ahb_clk.clkr, + [GCC_CE1_AXI_CLK] = &gcc_ce1_axi_clk.clkr, + [GCC_CE1_CLK] = &gcc_ce1_clk.clkr, + [GCC_CFG_NOC_USB3_PRIM_AXI_CLK] = &gcc_cfg_noc_usb3_prim_axi_clk.clkr, + [GCC_CPUSS_AHB_CLK] = &gcc_cpuss_ahb_clk.clkr, + [GCC_CPUSS_AHB_CLK_SRC] = &gcc_cpuss_ahb_clk_src.clkr, + [GCC_CPUSS_RBCPR_CLK] = &gcc_cpuss_rbcpr_clk.clkr, + [GCC_DDRSS_GPU_AXI_CLK] = &gcc_ddrss_gpu_axi_clk.clkr, + [GCC_DISP_GPLL0_CLK_SRC] = &gcc_disp_gpll0_clk_src.clkr, + [GCC_DISP_GPLL0_DIV_CLK_SRC] = &gcc_disp_gpll0_div_clk_src.clkr, + [GCC_DISP_HF_AXI_CLK] = &gcc_disp_hf_axi_clk.clkr, + [GCC_DISP_THROTTLE_HF_AXI_CLK] = &gcc_disp_throttle_hf_axi_clk.clkr, + [GCC_DISP_XO_CLK] = &gcc_disp_xo_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr, + [GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr, + [GCC_GPU_MEMNOC_GFX_CLK] = &gcc_gpu_memnoc_gfx_clk.clkr, + [GCC_GPU_SNOC_DVM_GFX_CLK] = &gcc_gpu_snoc_dvm_gfx_clk.clkr, + [GCC_NPU_AXI_CLK] = &gcc_npu_axi_clk.clkr, + [GCC_NPU_BWMON_AXI_CLK] = &gcc_npu_bwmon_axi_clk.clkr, + [GCC_NPU_BWMON_DMA_CFG_AHB_CLK] = &gcc_npu_bwmon_dma_cfg_ahb_clk.clkr, + [GCC_NPU_BWMON_DSP_CFG_AHB_CLK] = &gcc_npu_bwmon_dsp_cfg_ahb_clk.clkr, + [GCC_NPU_CFG_AHB_CLK] = &gcc_npu_cfg_ahb_clk.clkr, + [GCC_NPU_DMA_CLK] = &gcc_npu_dma_clk.clkr, + [GCC_NPU_GPLL0_CLK_SRC] = &gcc_npu_gpll0_clk_src.clkr, + [GCC_NPU_GPLL0_DIV_CLK_SRC] = &gcc_npu_gpll0_div_clk_src.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr, + [GCC_QSPI_CNOC_PERIPH_AHB_CLK] = &gcc_qspi_cnoc_periph_ahb_clk.clkr, + [GCC_QSPI_CORE_CLK] = &gcc_qspi_core_clk.clkr, + [GCC_QSPI_CORE_CLK_SRC] = &gcc_qspi_core_clk_src.clkr, + [GCC_QUPV3_WRAP0_CORE_2X_CLK] = &gcc_qupv3_wrap0_core_2x_clk.clkr, + [GCC_QUPV3_WRAP0_CORE_CLK] = &gcc_qupv3_wrap0_core_clk.clkr, + [GCC_QUPV3_WRAP0_S0_CLK] = &gcc_qupv3_wrap0_s0_clk.clkr, + [GCC_QUPV3_WRAP0_S0_CLK_SRC] = &gcc_qupv3_wrap0_s0_clk_src.clkr, + [GCC_QUPV3_WRAP0_S1_CLK] = &gcc_qupv3_wrap0_s1_clk.clkr, + [GCC_QUPV3_WRAP0_S1_CLK_SRC] = &gcc_qupv3_wrap0_s1_clk_src.clkr, + [GCC_QUPV3_WRAP0_S2_CLK] = &gcc_qupv3_wrap0_s2_clk.clkr, + [GCC_QUPV3_WRAP0_S2_CLK_SRC] = &gcc_qupv3_wrap0_s2_clk_src.clkr, + [GCC_QUPV3_WRAP0_S3_CLK] = &gcc_qupv3_wrap0_s3_clk.clkr, + [GCC_QUPV3_WRAP0_S3_CLK_SRC] = &gcc_qupv3_wrap0_s3_clk_src.clkr, + [GCC_QUPV3_WRAP0_S4_CLK] = &gcc_qupv3_wrap0_s4_clk.clkr, + [GCC_QUPV3_WRAP0_S4_CLK_SRC] = &gcc_qupv3_wrap0_s4_clk_src.clkr, + [GCC_QUPV3_WRAP0_S5_CLK] = &gcc_qupv3_wrap0_s5_clk.clkr, + [GCC_QUPV3_WRAP0_S5_CLK_SRC] = &gcc_qupv3_wrap0_s5_clk_src.clkr, + [GCC_QUPV3_WRAP1_CORE_2X_CLK] = &gcc_qupv3_wrap1_core_2x_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_CLK] = &gcc_qupv3_wrap1_core_clk.clkr, + [GCC_QUPV3_WRAP1_S0_CLK] = &gcc_qupv3_wrap1_s0_clk.clkr, + [GCC_QUPV3_WRAP1_S0_CLK_SRC] = &gcc_qupv3_wrap1_s0_clk_src.clkr, + [GCC_QUPV3_WRAP1_S1_CLK] = &gcc_qupv3_wrap1_s1_clk.clkr, + [GCC_QUPV3_WRAP1_S1_CLK_SRC] = &gcc_qupv3_wrap1_s1_clk_src.clkr, + [GCC_QUPV3_WRAP1_S2_CLK] = &gcc_qupv3_wrap1_s2_clk.clkr, + [GCC_QUPV3_WRAP1_S2_CLK_SRC] = &gcc_qupv3_wrap1_s2_clk_src.clkr, + [GCC_QUPV3_WRAP1_S3_CLK] = &gcc_qupv3_wrap1_s3_clk.clkr, + [GCC_QUPV3_WRAP1_S3_CLK_SRC] = &gcc_qupv3_wrap1_s3_clk_src.clkr, + [GCC_QUPV3_WRAP1_S4_CLK] = &gcc_qupv3_wrap1_s4_clk.clkr, + [GCC_QUPV3_WRAP1_S4_CLK_SRC] = &gcc_qupv3_wrap1_s4_clk_src.clkr, + [GCC_QUPV3_WRAP1_S5_CLK] = &gcc_qupv3_wrap1_s5_clk.clkr, + [GCC_QUPV3_WRAP1_S5_CLK_SRC] = &gcc_qupv3_wrap1_s5_clk_src.clkr, + [GCC_QUPV3_WRAP_0_M_AHB_CLK] = &gcc_qupv3_wrap_0_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_0_S_AHB_CLK] = &gcc_qupv3_wrap_0_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_1_M_AHB_CLK] = &gcc_qupv3_wrap_1_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_1_S_AHB_CLK] = &gcc_qupv3_wrap_1_s_ahb_clk.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr, + [GCC_SYS_NOC_CPUSS_AHB_CLK] = &gcc_sys_noc_cpuss_ahb_clk.clkr, + [GCC_UFS_MEM_CLKREF_CLK] = &gcc_ufs_mem_clkref_clk.clkr, + [GCC_UFS_PHY_AHB_CLK] = &gcc_ufs_phy_ahb_clk.clkr, + [GCC_UFS_PHY_AXI_CLK] = &gcc_ufs_phy_axi_clk.clkr, + [GCC_UFS_PHY_AXI_CLK_SRC] = &gcc_ufs_phy_axi_clk_src.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK] = &gcc_ufs_phy_ice_core_clk.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &gcc_ufs_phy_ice_core_clk_src.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK] = &gcc_ufs_phy_phy_aux_clk.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &gcc_ufs_phy_phy_aux_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &gcc_ufs_phy_rx_symbol_0_clk.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &gcc_ufs_phy_tx_symbol_0_clk.clkr, + [GCC_UFS_PHY_UNIPRO_CORE_CLK] = &gcc_ufs_phy_unipro_core_clk.clkr, + [GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC] = + &gcc_ufs_phy_unipro_core_clk_src.clkr, + [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK_SRC] = &gcc_usb30_prim_master_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK] = &gcc_usb30_prim_mock_utmi_clk.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC] = + &gcc_usb30_prim_mock_utmi_clk_src.clkr, + [GCC_USB30_PRIM_SLEEP_CLK] = &gcc_usb30_prim_sleep_clk.clkr, + [GCC_USB3_PRIM_CLKREF_CLK] = &gcc_usb3_prim_clkref_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK] = &gcc_usb3_prim_phy_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &gcc_usb3_prim_phy_aux_clk_src.clkr, + [GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &gcc_usb3_prim_phy_com_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK] = &gcc_usb3_prim_phy_pipe_clk.clkr, + [GCC_USB_PHY_CFG_AHB2PHY_CLK] = &gcc_usb_phy_cfg_ahb2phy_clk.clkr, + [GCC_VIDEO_AXI_CLK] = &gcc_video_axi_clk.clkr, + [GCC_VIDEO_GPLL0_DIV_CLK_SRC] = &gcc_video_gpll0_div_clk_src.clkr, + [GCC_VIDEO_THROTTLE_AXI_CLK] = &gcc_video_throttle_axi_clk.clkr, + [GCC_VIDEO_XO_CLK] = &gcc_video_xo_clk.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL0_OUT_EVEN] = &gpll0_out_even.clkr, + [GPLL6] = &gpll6.clkr, + [GPLL7] = &gpll7.clkr, + [GPLL4] = &gpll4.clkr, + [GPLL1] = &gpll1.clkr, +}; + +static const struct qcom_reset_map gcc_sc7180_resets[] = { + [GCC_QUSB2PHY_PRIM_BCR] = { 0x26000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x26004 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0xf000 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_USB3_PHY_SEC_BCR] = { 0x5000c }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3PHY_PHY_SEC_BCR] = { 0x50010 }, + [GCC_USB3_DP_PHY_SEC_BCR] = { 0x50014 }, + [GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 }, +}; + +static struct clk_rcg_dfs_data gcc_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), +}; + +static const struct regmap_config gcc_sc7180_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x18208c, + .fast_io = true, +}; + +static const struct qcom_cc_desc gcc_sc7180_desc = { + .config = &gcc_sc7180_regmap_config, + .clk_hws = gcc_sc7180_hws, + .num_clk_hws = ARRAY_SIZE(gcc_sc7180_hws), + .clks = gcc_sc7180_clocks, + .num_clks = ARRAY_SIZE(gcc_sc7180_clocks), + .resets = gcc_sc7180_resets, + .num_resets = ARRAY_SIZE(gcc_sc7180_resets), + .gdscs = gcc_sc7180_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_sc7180_gdscs), +}; + +static const struct of_device_id gcc_sc7180_match_table[] = { + { .compatible = "qcom,gcc-sc7180" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_sc7180_match_table); + +static int gcc_sc7180_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + int ret; + + regmap = qcom_cc_map(pdev, &gcc_sc7180_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* + * Disable the GPLL0 active input to MM blocks, NPU + * and GPU via MISC registers. + */ + regmap_update_bits(regmap, 0x09ffc, 0x3, 0x3); + regmap_update_bits(regmap, 0x4d110, 0x3, 0x3); + regmap_update_bits(regmap, 0x71028, 0x3, 0x3); + + /* + * Keep the clocks always-ON + * GCC_CPUSS_GNOC_CLK, GCC_VIDEO_AHB_CLK, GCC_DISP_AHB_CLK + * GCC_GPU_CFG_AHB_CLK + */ + regmap_update_bits(regmap, 0x48004, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0x0b004, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0x0b00c, BIT(0), BIT(0)); + regmap_update_bits(regmap, 0x71004, BIT(0), BIT(0)); + + ret = qcom_cc_register_rcg_dfs(regmap, gcc_dfs_clocks, + ARRAY_SIZE(gcc_dfs_clocks)); + if (ret) + return ret; + + return qcom_cc_really_probe(pdev, &gcc_sc7180_desc, regmap); +} + +static struct platform_driver gcc_sc7180_driver = { + .probe = gcc_sc7180_probe, + .driver = { + .name = "gcc-sc7180", + .of_match_table = gcc_sc7180_match_table, + }, +}; + +static int __init gcc_sc7180_init(void) +{ + return platform_driver_register(&gcc_sc7180_driver); +} +core_initcall(gcc_sc7180_init); + +static void __exit gcc_sc7180_exit(void) +{ + platform_driver_unregister(&gcc_sc7180_driver); +} +module_exit(gcc_sc7180_exit); + +MODULE_DESCRIPTION("QTI GCC SC7180 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c index 95be125c3bdd..d2142fe46a8e 100644 --- a/drivers/clk/qcom/gcc-sdm845.c +++ b/drivers/clk/qcom/gcc-sdm845.c @@ -408,7 +408,7 @@ static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = { { } }; -static struct clk_init_data gcc_qupv3_wrap0_s0_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { .name = "gcc_qupv3_wrap0_s0_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -421,10 +421,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s0_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s0_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s1_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { .name = "gcc_qupv3_wrap0_s1_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -437,10 +437,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s1_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s1_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s2_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { .name = "gcc_qupv3_wrap0_s2_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -453,10 +453,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s2_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s2_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s2_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s3_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { .name = "gcc_qupv3_wrap0_s3_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -469,10 +469,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s3_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s3_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s4_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { .name = "gcc_qupv3_wrap0_s4_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -485,10 +485,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s4_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s4_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s5_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { .name = "gcc_qupv3_wrap0_s5_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -501,10 +501,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s5_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s5_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s6_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s6_clk_src_init = { .name = "gcc_qupv3_wrap0_s6_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -517,10 +517,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s6_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s6_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s6_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap0_s7_clk_init = { +static struct clk_init_data gcc_qupv3_wrap0_s7_clk_src_init = { .name = "gcc_qupv3_wrap0_s7_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -533,10 +533,10 @@ static struct clk_rcg2 gcc_qupv3_wrap0_s7_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap0_s7_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap0_s7_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s0_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { .name = "gcc_qupv3_wrap1_s0_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -549,10 +549,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s1_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { .name = "gcc_qupv3_wrap1_s1_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -565,10 +565,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s2_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s2_clk_src_init = { .name = "gcc_qupv3_wrap1_s2_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -581,10 +581,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s2_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s2_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s2_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s3_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { .name = "gcc_qupv3_wrap1_s3_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -597,10 +597,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s4_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { .name = "gcc_qupv3_wrap1_s4_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -613,10 +613,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s5_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { .name = "gcc_qupv3_wrap1_s5_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -629,10 +629,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s6_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s6_clk_src_init = { .name = "gcc_qupv3_wrap1_s6_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -645,10 +645,10 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_src_init, }; -static struct clk_init_data gcc_qupv3_wrap1_s7_clk_init = { +static struct clk_init_data gcc_qupv3_wrap1_s7_clk_src_init = { .name = "gcc_qupv3_wrap1_s7_clk_src", .parent_names = gcc_parent_names_0, .num_parents = 4, @@ -661,7 +661,7 @@ static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { .hid_width = 5, .parent_map = gcc_parent_map_0, .freq_tbl = ftbl_gcc_qupv3_wrap0_s0_clk_src, - .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_init, + .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_src_init, }; static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { @@ -3577,22 +3577,22 @@ static const struct of_device_id gcc_sdm845_match_table[] = { MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table); static const struct clk_rcg_dfs_data gcc_dfs_clocks[] = { - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s6_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap0_s7_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s2_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk), - DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap0_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk_src), }; static int gcc_sdm845_probe(struct platform_device *pdev) diff --git a/drivers/clk/qcom/gpucc-msm8998.c b/drivers/clk/qcom/gpucc-msm8998.c new file mode 100644 index 000000000000..e5e2492b20c5 --- /dev/null +++ b/drivers/clk/qcom/gpucc-msm8998.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019, Jeffrey Hugo + */ + +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/clock/qcom,gpucc-msm8998.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-alpha-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "reset.h" +#include "gdsc.h" + +enum { + P_XO, + P_GPLL0, + P_GPUPLL0_OUT_EVEN, +}; + +/* Instead of going directly to the block, XO is routed through this branch */ +static struct clk_branch gpucc_cxo_clk = { + .halt_reg = 0x1020, + .clkr = { + .enable_reg = 0x1020, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpucc_cxo_clk", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "xo", + .name = "xo" + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_IS_CRITICAL, + }, + }, +}; + +static const struct clk_div_table post_div_table_fabia_even[] = { + { 0x0, 1 }, + { 0x1, 2 }, + { 0x3, 4 }, + { 0x7, 8 }, + { } +}; + +static struct clk_alpha_pll gpupll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpupll0", + .parent_hws = (const struct clk_hw *[]){ &gpucc_cxo_clk.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_fabia_ops, + }, +}; + +static struct clk_alpha_pll_postdiv gpupll0_out_even = { + .offset = 0x0, + .post_div_shift = 8, + .post_div_table = post_div_table_fabia_even, + .num_post_div = ARRAY_SIZE(post_div_table_fabia_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_FABIA], + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpupll0_out_even", + .parent_hws = (const struct clk_hw *[]){ &gpupll0.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_fabia_ops, + }, +}; + +static const struct parent_map gpu_xo_gpll0_map[] = { + { P_XO, 0 }, + { P_GPLL0, 5 }, +}; + +static const struct clk_parent_data gpu_xo_gpll0[] = { + { .hw = &gpucc_cxo_clk.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, +}; + +static const struct parent_map gpu_xo_gpupll0_map[] = { + { P_XO, 0 }, + { P_GPUPLL0_OUT_EVEN, 1 }, +}; + +static const struct clk_parent_data gpu_xo_gpupll0[] = { + { .hw = &gpucc_cxo_clk.clkr.hw }, + { .hw = &gpupll0_out_even.clkr.hw }, +}; + +static const struct freq_tbl ftbl_rbcpr_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0, 12, 0, 0), + { } +}; + +static struct clk_rcg2 rbcpr_clk_src = { + .cmd_rcgr = 0x1030, + .hid_width = 5, + .parent_map = gpu_xo_gpll0_map, + .freq_tbl = ftbl_rbcpr_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbcpr_clk_src", + .parent_data = gpu_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gfx3d_clk_src[] = { + { .src = P_GPUPLL0_OUT_EVEN, .pre_div = 3 }, + { } +}; + +static struct clk_rcg2 gfx3d_clk_src = { + .cmd_rcgr = 0x1070, + .hid_width = 5, + .parent_map = gpu_xo_gpupll0_map, + .freq_tbl = ftbl_gfx3d_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gfx3d_clk_src", + .parent_data = gpu_xo_gpupll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE, + }, +}; + +static const struct freq_tbl ftbl_rbbmtimer_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 rbbmtimer_clk_src = { + .cmd_rcgr = 0x10b0, + .hid_width = 5, + .parent_map = gpu_xo_gpll0_map, + .freq_tbl = ftbl_rbbmtimer_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "rbbmtimer_clk_src", + .parent_data = gpu_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gfx3d_isense_clk_src[] = { + F(19200000, P_XO, 1, 0, 0), + F(40000000, P_GPLL0, 15, 0, 0), + F(200000000, P_GPLL0, 3, 0, 0), + F(300000000, P_GPLL0, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gfx3d_isense_clk_src = { + .cmd_rcgr = 0x1100, + .hid_width = 5, + .parent_map = gpu_xo_gpll0_map, + .freq_tbl = ftbl_gfx3d_isense_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gfx3d_isense_clk_src", + .parent_data = gpu_xo_gpll0, + .num_parents = 2, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch rbcpr_clk = { + .halt_reg = 0x1054, + .clkr = { + .enable_reg = 0x1054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "rbcpr_clk", + .parent_hws = (const struct clk_hw *[]){ &rbcpr_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch gfx3d_clk = { + .halt_reg = 0x1098, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gfx3d_clk", + .parent_hws = (const struct clk_hw *[]){ &gfx3d_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch rbbmtimer_clk = { + .halt_reg = 0x10d0, + .clkr = { + .enable_reg = 0x10d0, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "rbbmtimer_clk", + .parent_hws = (const struct clk_hw *[]){ &rbbmtimer_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_branch2_ops, + .flags = CLK_SET_RATE_PARENT, + }, + }, +}; + +static struct clk_branch gfx3d_isense_clk = { + .halt_reg = 0x1124, + .clkr = { + .enable_reg = 0x1124, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gfx3d_isense_clk", + .parent_hws = (const struct clk_hw *[]){ &gfx3d_isense_clk_src.clkr.hw }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cx_gdsc = { + .gdscr = 0x1004, + .pd = { + .name = "gpu_cx", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc gpu_gx_gdsc = { + .gdscr = 0x1094, + .clamp_io_ctrl = 0x130, + .pd = { + .name = "gpu_gx", + }, + .parent = &gpu_cx_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, + .flags = CLAMP_IO | AON_RESET, +}; + +static struct clk_regmap *gpucc_msm8998_clocks[] = { + [GPUPLL0] = &gpupll0.clkr, + [GPUPLL0_OUT_EVEN] = &gpupll0_out_even.clkr, + [RBCPR_CLK_SRC] = &rbcpr_clk_src.clkr, + [GFX3D_CLK_SRC] = &gfx3d_clk_src.clkr, + [RBBMTIMER_CLK_SRC] = &rbbmtimer_clk_src.clkr, + [GFX3D_ISENSE_CLK_SRC] = &gfx3d_isense_clk_src.clkr, + [RBCPR_CLK] = &rbcpr_clk.clkr, + [GFX3D_CLK] = &gfx3d_clk.clkr, + [RBBMTIMER_CLK] = &rbbmtimer_clk.clkr, + [GFX3D_ISENSE_CLK] = &gfx3d_isense_clk.clkr, + [GPUCC_CXO_CLK] = &gpucc_cxo_clk.clkr, +}; + +static struct gdsc *gpucc_msm8998_gdscs[] = { + [GPU_CX_GDSC] = &gpu_cx_gdsc, + [GPU_GX_GDSC] = &gpu_gx_gdsc, +}; + +static const struct qcom_reset_map gpucc_msm8998_resets[] = { + [GPU_CX_BCR] = { 0x1000 }, + [RBCPR_BCR] = { 0x1050 }, + [GPU_GX_BCR] = { 0x1090 }, + [GPU_ISENSE_BCR] = { 0x1120 }, +}; + +static const struct regmap_config gpucc_msm8998_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9000, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpucc_msm8998_desc = { + .config = &gpucc_msm8998_regmap_config, + .clks = gpucc_msm8998_clocks, + .num_clks = ARRAY_SIZE(gpucc_msm8998_clocks), + .resets = gpucc_msm8998_resets, + .num_resets = ARRAY_SIZE(gpucc_msm8998_resets), + .gdscs = gpucc_msm8998_gdscs, + .num_gdscs = ARRAY_SIZE(gpucc_msm8998_gdscs), +}; + +static const struct of_device_id gpucc_msm8998_match_table[] = { + { .compatible = "qcom,msm8998-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpucc_msm8998_match_table); + +static int gpucc_msm8998_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &gpucc_msm8998_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* force periph logic on to avoid perf counter corruption */ + regmap_write_bits(regmap, gfx3d_clk.clkr.enable_reg, BIT(13), BIT(13)); + /* tweak droop detector (GPUCC_GPU_DD_WRAP_CTRL) to reduce leakage */ + regmap_write_bits(regmap, gfx3d_clk.clkr.enable_reg, BIT(0), BIT(0)); + + return qcom_cc_really_probe(pdev, &gpucc_msm8998_desc, regmap); +} + +static struct platform_driver gpucc_msm8998_driver = { + .probe = gpucc_msm8998_probe, + .driver = { + .name = "gpucc-msm8998", + .of_match_table = gpucc_msm8998_match_table, + }, +}; +module_platform_driver(gpucc_msm8998_driver); + +MODULE_DESCRIPTION("QCOM GPUCC MSM8998 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/q6sstop-qcs404.c b/drivers/clk/qcom/q6sstop-qcs404.c new file mode 100644 index 000000000000..723f932fbf7d --- /dev/null +++ b/drivers/clk/qcom/q6sstop-qcs404.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_clock.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,q6sstopcc-qcs404.h> + +#include "clk-regmap.h" +#include "clk-branch.h" +#include "common.h" +#include "reset.h" + +static struct clk_branch lcc_ahbfabric_cbc_clk = { + .halt_reg = 0x1b004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1b004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_ahbfabric_cbc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lcc_q6ss_ahbs_cbc_clk = { + .halt_reg = 0x22000, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x22000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_q6ss_ahbs_cbc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lcc_q6ss_tcm_slave_cbc_clk = { + .halt_reg = 0x1c000, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x1c000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_q6ss_tcm_slave_cbc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lcc_q6ss_ahbm_cbc_clk = { + .halt_reg = 0x22004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x22004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_q6ss_ahbm_cbc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lcc_q6ss_axim_cbc_clk = { + .halt_reg = 0x1c004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x1c004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_q6ss_axim_cbc_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch lcc_q6ss_bcr_sleep_clk = { + .halt_reg = 0x6004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x6004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "lcc_q6ss_bcr_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +/* TCSR clock */ +static struct clk_branch tcsr_lcc_csr_cbcr_clk = { + .halt_reg = 0x8008, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x8008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "tcsr_lcc_csr_cbcr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct regmap_config q6sstop_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static struct clk_regmap *q6sstop_qcs404_clocks[] = { + [LCC_AHBFABRIC_CBC_CLK] = &lcc_ahbfabric_cbc_clk.clkr, + [LCC_Q6SS_AHBS_CBC_CLK] = &lcc_q6ss_ahbs_cbc_clk.clkr, + [LCC_Q6SS_TCM_SLAVE_CBC_CLK] = &lcc_q6ss_tcm_slave_cbc_clk.clkr, + [LCC_Q6SS_AHBM_CBC_CLK] = &lcc_q6ss_ahbm_cbc_clk.clkr, + [LCC_Q6SS_AXIM_CBC_CLK] = &lcc_q6ss_axim_cbc_clk.clkr, + [LCC_Q6SS_BCR_SLEEP_CLK] = &lcc_q6ss_bcr_sleep_clk.clkr, +}; + +static const struct qcom_reset_map q6sstop_qcs404_resets[] = { + [Q6SSTOP_BCR_RESET] = { 0x6000 }, +}; + +static const struct qcom_cc_desc q6sstop_qcs404_desc = { + .config = &q6sstop_regmap_config, + .clks = q6sstop_qcs404_clocks, + .num_clks = ARRAY_SIZE(q6sstop_qcs404_clocks), + .resets = q6sstop_qcs404_resets, + .num_resets = ARRAY_SIZE(q6sstop_qcs404_resets), +}; + +static struct clk_regmap *tcsr_qcs404_clocks[] = { + [TCSR_Q6SS_LCC_CBCR_CLK] = &tcsr_lcc_csr_cbcr_clk.clkr, +}; + +static const struct qcom_cc_desc tcsr_qcs404_desc = { + .config = &q6sstop_regmap_config, + .clks = tcsr_qcs404_clocks, + .num_clks = ARRAY_SIZE(tcsr_qcs404_clocks), +}; + +static const struct of_device_id q6sstopcc_qcs404_match_table[] = { + { .compatible = "qcom,qcs404-q6sstopcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, q6sstopcc_qcs404_match_table); + +static int q6sstopcc_qcs404_probe(struct platform_device *pdev) +{ + const struct qcom_cc_desc *desc; + int ret; + + pm_runtime_enable(&pdev->dev); + ret = pm_clk_create(&pdev->dev); + if (ret) + goto disable_pm_runtime; + + ret = pm_clk_add(&pdev->dev, NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to acquire iface clock\n"); + goto destroy_pm_clk; + } + + q6sstop_regmap_config.name = "q6sstop_tcsr"; + desc = &tcsr_qcs404_desc; + + ret = qcom_cc_probe_by_index(pdev, 1, desc); + if (ret) + goto destroy_pm_clk; + + q6sstop_regmap_config.name = "q6sstop_cc"; + desc = &q6sstop_qcs404_desc; + + ret = qcom_cc_probe_by_index(pdev, 0, desc); + if (ret) + goto destroy_pm_clk; + + return 0; + +destroy_pm_clk: + pm_clk_destroy(&pdev->dev); + +disable_pm_runtime: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int q6sstopcc_qcs404_remove(struct platform_device *pdev) +{ + pm_clk_destroy(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct dev_pm_ops q6sstopcc_pm_ops = { + SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL) +}; + +static struct platform_driver q6sstopcc_qcs404_driver = { + .probe = q6sstopcc_qcs404_probe, + .remove = q6sstopcc_qcs404_remove, + .driver = { + .name = "qcs404-q6sstopcc", + .of_match_table = q6sstopcc_qcs404_match_table, + .pm = &q6sstopcc_pm_ops, + }, +}; + +module_platform_driver(q6sstopcc_qcs404_driver); + +MODULE_DESCRIPTION("QTI QCS404 Q6SSTOP Clock Controller Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/renesas/Kconfig b/drivers/clk/renesas/Kconfig index b879e3e3a6b4..4cd846bc98cc 100644 --- a/drivers/clk/renesas/Kconfig +++ b/drivers/clk/renesas/Kconfig @@ -12,6 +12,7 @@ config CLK_RENESAS select CLK_R8A7745 if ARCH_R8A7745 select CLK_R8A77470 if ARCH_R8A77470 select CLK_R8A774A1 if ARCH_R8A774A1 + select CLK_R8A774B1 if ARCH_R8A774B1 select CLK_R8A774C0 if ARCH_R8A774C0 select CLK_R8A7778 if ARCH_R8A7778 select CLK_R8A7779 if ARCH_R8A7779 @@ -20,7 +21,8 @@ config CLK_RENESAS select CLK_R8A7792 if ARCH_R8A7792 select CLK_R8A7794 if ARCH_R8A7794 select CLK_R8A7795 if ARCH_R8A7795 - select CLK_R8A7796 if ARCH_R8A7796 + select CLK_R8A77960 if ARCH_R8A77960 || ARCH_R8A7796 + select CLK_R8A77961 if ARCH_R8A77961 select CLK_R8A77965 if ARCH_R8A77965 select CLK_R8A77970 if ARCH_R8A77970 select CLK_R8A77980 if ARCH_R8A77980 @@ -31,17 +33,6 @@ config CLK_RENESAS if CLK_RENESAS -config CLK_RENESAS_LEGACY - bool "Legacy DT clock support" - depends on CLK_R8A7790 || CLK_R8A7791 || CLK_R8A7792 || CLK_R8A7794 - help - Enable backward compatibility with old device trees describing a - hierarchical representation of the various CPG and MSTP clocks. - - Say Y if you want your kernel to work with old DTBs. - It is safe to say N if you use the DTS that is supplied with the - current kernel source tree. - # SoC config CLK_EMEV2 bool "Emma Mobile EV2 clock support" if COMPILE_TEST @@ -80,6 +71,10 @@ config CLK_R8A774A1 bool "RZ/G2M clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG +config CLK_R8A774B1 + bool "RZ/G2N clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + config CLK_R8A774C0 bool "RZ/G2E clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG @@ -94,24 +89,20 @@ config CLK_R8A7779 config CLK_R8A7790 bool "R-Car H2 clock support" if COMPILE_TEST - select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7791 bool "R-Car M2-W/N clock support" if COMPILE_TEST - select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 config CLK_R8A7792 bool "R-Car V2H clock support" if COMPILE_TEST - select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG config CLK_R8A7794 bool "R-Car E2 clock support" if COMPILE_TEST - select CLK_RCAR_GEN2 if CLK_RENESAS_LEGACY select CLK_RCAR_GEN2_CPG select CLK_RENESAS_DIV6 @@ -119,10 +110,14 @@ config CLK_R8A7795 bool "R-Car H3 clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG -config CLK_R8A7796 +config CLK_R8A77960 bool "R-Car M3-W clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG +config CLK_R8A77961 + bool "R-Car M3-W+ clock support" if COMPILE_TEST + select CLK_RCAR_GEN3_CPG + config CLK_R8A77965 bool "R-Car M3-N clock support" if COMPILE_TEST select CLK_RCAR_GEN3_CPG @@ -155,11 +150,6 @@ config CLK_SH73A0 # Family -config CLK_RCAR_GEN2 - bool "R-Car Gen2 legacy clock support" if COMPILE_TEST - select CLK_RENESAS_CPG_MSTP - select CLK_RENESAS_DIV6 - config CLK_RCAR_GEN2_CPG bool "R-Car Gen2 CPG clock support" if COMPILE_TEST select CLK_RENESAS_CPG_MSSR diff --git a/drivers/clk/renesas/Makefile b/drivers/clk/renesas/Makefile index c793e3cc9452..4a722bc5aac7 100644 --- a/drivers/clk/renesas/Makefile +++ b/drivers/clk/renesas/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CLK_R8A7743) += r8a7743-cpg-mssr.o obj-$(CONFIG_CLK_R8A7745) += r8a7745-cpg-mssr.o obj-$(CONFIG_CLK_R8A77470) += r8a77470-cpg-mssr.o obj-$(CONFIG_CLK_R8A774A1) += r8a774a1-cpg-mssr.o +obj-$(CONFIG_CLK_R8A774B1) += r8a774b1-cpg-mssr.o obj-$(CONFIG_CLK_R8A774C0) += r8a774c0-cpg-mssr.o obj-$(CONFIG_CLK_R8A7778) += clk-r8a7778.o obj-$(CONFIG_CLK_R8A7779) += clk-r8a7779.o @@ -17,7 +18,8 @@ obj-$(CONFIG_CLK_R8A7791) += r8a7791-cpg-mssr.o obj-$(CONFIG_CLK_R8A7792) += r8a7792-cpg-mssr.o obj-$(CONFIG_CLK_R8A7794) += r8a7794-cpg-mssr.o obj-$(CONFIG_CLK_R8A7795) += r8a7795-cpg-mssr.o -obj-$(CONFIG_CLK_R8A7796) += r8a7796-cpg-mssr.o +obj-$(CONFIG_CLK_R8A77960) += r8a7796-cpg-mssr.o +obj-$(CONFIG_CLK_R8A77961) += r8a7796-cpg-mssr.o obj-$(CONFIG_CLK_R8A77965) += r8a77965-cpg-mssr.o obj-$(CONFIG_CLK_R8A77970) += r8a77970-cpg-mssr.o obj-$(CONFIG_CLK_R8A77980) += r8a77980-cpg-mssr.o @@ -27,7 +29,6 @@ obj-$(CONFIG_CLK_R9A06G032) += r9a06g032-clocks.o obj-$(CONFIG_CLK_SH73A0) += clk-sh73a0.o # Family -obj-$(CONFIG_CLK_RCAR_GEN2) += clk-rcar-gen2.o obj-$(CONFIG_CLK_RCAR_GEN2_CPG) += rcar-gen2-cpg.o obj-$(CONFIG_CLK_RCAR_GEN3_CPG) += rcar-gen3-cpg.o obj-$(CONFIG_CLK_RCAR_USB2_CLOCK_SEL) += rcar-usb2-clock-sel.o diff --git a/drivers/clk/renesas/clk-mstp.c b/drivers/clk/renesas/clk-mstp.c index e326e6dc09fc..003e9ce45757 100644 --- a/drivers/clk/renesas/clk-mstp.c +++ b/drivers/clk/renesas/clk-mstp.c @@ -189,10 +189,8 @@ static void __init cpg_mstp_clocks_init(struct device_node *np) unsigned int i; group = kzalloc(struct_size(group, clks, MSTP_MAX_CLOCKS), GFP_KERNEL); - if (group == NULL) { - kfree(group); + if (!group) return; - } clks = group->clks; spin_lock_init(&group->lock); diff --git a/drivers/clk/renesas/clk-rcar-gen2.c b/drivers/clk/renesas/clk-rcar-gen2.c deleted file mode 100644 index da9fe3f032eb..000000000000 --- a/drivers/clk/renesas/clk-rcar-gen2.c +++ /dev/null @@ -1,457 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * rcar_gen2 Core CPG Clocks - * - * Copyright (C) 2013 Ideas On Board SPRL - * - * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> - */ - -#include <linux/clk-provider.h> -#include <linux/clk/renesas.h> -#include <linux/init.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/math64.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/soc/renesas/rcar-rst.h> - -struct rcar_gen2_cpg { - struct clk_onecell_data data; - spinlock_t lock; - void __iomem *reg; -}; - -#define CPG_FRQCRB 0x00000004 -#define CPG_FRQCRB_KICK BIT(31) -#define CPG_SDCKCR 0x00000074 -#define CPG_PLL0CR 0x000000d8 -#define CPG_FRQCRC 0x000000e0 -#define CPG_FRQCRC_ZFC_MASK (0x1f << 8) -#define CPG_FRQCRC_ZFC_SHIFT 8 -#define CPG_ADSPCKCR 0x0000025c -#define CPG_RCANCKCR 0x00000270 - -/* ----------------------------------------------------------------------------- - * Z Clock - * - * Traits of this clock: - * prepare - clk_prepare only ensures that parents are prepared - * enable - clk_enable only ensures that parents are enabled - * rate - rate is adjustable. clk->rate = parent->rate * mult / 32 - * parent - fixed parent. No clk_set_parent support - */ - -struct cpg_z_clk { - struct clk_hw hw; - void __iomem *reg; - void __iomem *kick_reg; -}; - -#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw) - -static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct cpg_z_clk *zclk = to_z_clk(hw); - unsigned int mult; - unsigned int val; - - val = (readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK) >> CPG_FRQCRC_ZFC_SHIFT; - mult = 32 - val; - - return div_u64((u64)parent_rate * mult, 32); -} - -static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - unsigned long prate = *parent_rate; - unsigned int mult; - - if (!prate) - prate = 1; - - mult = div_u64((u64)rate * 32, prate); - mult = clamp(mult, 1U, 32U); - - return *parent_rate / 32 * mult; -} - -static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct cpg_z_clk *zclk = to_z_clk(hw); - unsigned int mult; - u32 val, kick; - unsigned int i; - - mult = div_u64((u64)rate * 32, parent_rate); - mult = clamp(mult, 1U, 32U); - - if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) - return -EBUSY; - - val = readl(zclk->reg); - val &= ~CPG_FRQCRC_ZFC_MASK; - val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; - writel(val, zclk->reg); - - /* - * Set KICK bit in FRQCRB to update hardware setting and wait for - * clock change completion. - */ - kick = readl(zclk->kick_reg); - kick |= CPG_FRQCRB_KICK; - writel(kick, zclk->kick_reg); - - /* - * Note: There is no HW information about the worst case latency. - * - * Using experimental measurements, it seems that no more than - * ~10 iterations are needed, independently of the CPU rate. - * Since this value might be dependent on external xtal rate, pll1 - * rate or even the other emulation clocks rate, use 1000 as a - * "super" safe value. - */ - for (i = 1000; i; i--) { - if (!(readl(zclk->kick_reg) & CPG_FRQCRB_KICK)) - return 0; - - cpu_relax(); - } - - return -ETIMEDOUT; -} - -static const struct clk_ops cpg_z_clk_ops = { - .recalc_rate = cpg_z_clk_recalc_rate, - .round_rate = cpg_z_clk_round_rate, - .set_rate = cpg_z_clk_set_rate, -}; - -static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg) -{ - static const char *parent_name = "pll0"; - struct clk_init_data init; - struct cpg_z_clk *zclk; - struct clk *clk; - - zclk = kzalloc(sizeof(*zclk), GFP_KERNEL); - if (!zclk) - return ERR_PTR(-ENOMEM); - - init.name = "z"; - init.ops = &cpg_z_clk_ops; - init.flags = 0; - init.parent_names = &parent_name; - init.num_parents = 1; - - zclk->reg = cpg->reg + CPG_FRQCRC; - zclk->kick_reg = cpg->reg + CPG_FRQCRB; - zclk->hw.init = &init; - - clk = clk_register(NULL, &zclk->hw); - if (IS_ERR(clk)) - kfree(zclk); - - return clk; -} - -static struct clk * __init cpg_rcan_clk_register(struct rcar_gen2_cpg *cpg, - struct device_node *np) -{ - const char *parent_name = of_clk_get_parent_name(np, 1); - struct clk_fixed_factor *fixed; - struct clk_gate *gate; - struct clk *clk; - - fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); - if (!fixed) - return ERR_PTR(-ENOMEM); - - fixed->mult = 1; - fixed->div = 6; - - gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) { - kfree(fixed); - return ERR_PTR(-ENOMEM); - } - - gate->reg = cpg->reg + CPG_RCANCKCR; - gate->bit_idx = 8; - gate->flags = CLK_GATE_SET_TO_DISABLE; - gate->lock = &cpg->lock; - - clk = clk_register_composite(NULL, "rcan", &parent_name, 1, NULL, NULL, - &fixed->hw, &clk_fixed_factor_ops, - &gate->hw, &clk_gate_ops, 0); - if (IS_ERR(clk)) { - kfree(gate); - kfree(fixed); - } - - return clk; -} - -/* ADSP divisors */ -static const struct clk_div_table cpg_adsp_div_table[] = { - { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, - { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, - { 10, 36 }, { 11, 48 }, { 0, 0 }, -}; - -static struct clk * __init cpg_adsp_clk_register(struct rcar_gen2_cpg *cpg) -{ - const char *parent_name = "pll1"; - struct clk_divider *div; - struct clk_gate *gate; - struct clk *clk; - - div = kzalloc(sizeof(*div), GFP_KERNEL); - if (!div) - return ERR_PTR(-ENOMEM); - - div->reg = cpg->reg + CPG_ADSPCKCR; - div->width = 4; - div->table = cpg_adsp_div_table; - div->lock = &cpg->lock; - - gate = kzalloc(sizeof(*gate), GFP_KERNEL); - if (!gate) { - kfree(div); - return ERR_PTR(-ENOMEM); - } - - gate->reg = cpg->reg + CPG_ADSPCKCR; - gate->bit_idx = 8; - gate->flags = CLK_GATE_SET_TO_DISABLE; - gate->lock = &cpg->lock; - - clk = clk_register_composite(NULL, "adsp", &parent_name, 1, NULL, NULL, - &div->hw, &clk_divider_ops, - &gate->hw, &clk_gate_ops, 0); - if (IS_ERR(clk)) { - kfree(gate); - kfree(div); - } - - return clk; -} - -/* ----------------------------------------------------------------------------- - * CPG Clock Data - */ - -/* - * MD EXTAL PLL0 PLL1 PLL3 - * 14 13 19 (MHz) *1 *1 - *--------------------------------------------------- - * 0 0 0 15 x 1 x172/2 x208/2 x106 - * 0 0 1 15 x 1 x172/2 x208/2 x88 - * 0 1 0 20 x 1 x130/2 x156/2 x80 - * 0 1 1 20 x 1 x130/2 x156/2 x66 - * 1 0 0 26 / 2 x200/2 x240/2 x122 - * 1 0 1 26 / 2 x200/2 x240/2 x102 - * 1 1 0 30 / 2 x172/2 x208/2 x106 - * 1 1 1 30 / 2 x172/2 x208/2 x88 - * - * *1 : Table 7.6 indicates VCO output (PLLx = VCO/2) - */ -#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \ - (((md) & BIT(13)) >> 12) | \ - (((md) & BIT(19)) >> 19)) -struct cpg_pll_config { - unsigned int extal_div; - unsigned int pll1_mult; - unsigned int pll3_mult; - unsigned int pll0_mult; /* For R-Car V2H and E2 only */ -}; - -static const struct cpg_pll_config cpg_pll_configs[8] __initconst = { - { 1, 208, 106, 200 }, { 1, 208, 88, 200 }, - { 1, 156, 80, 150 }, { 1, 156, 66, 150 }, - { 2, 240, 122, 230 }, { 2, 240, 102, 230 }, - { 2, 208, 106, 200 }, { 2, 208, 88, 200 }, -}; - -/* SDHI divisors */ -static const struct clk_div_table cpg_sdh_div_table[] = { - { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, - { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 }, - { 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 }, -}; - -static const struct clk_div_table cpg_sd01_div_table[] = { - { 4, 8 }, - { 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 }, - { 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 }, -}; - -/* ----------------------------------------------------------------------------- - * Initialization - */ - -static u32 cpg_mode __initdata; - -static const char * const pll0_mult_match[] = { - "renesas,r8a7792-cpg-clocks", - "renesas,r8a7794-cpg-clocks", - NULL -}; - -static struct clk * __init -rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg, - const struct cpg_pll_config *config, - const char *name) -{ - const struct clk_div_table *table = NULL; - const char *parent_name; - unsigned int shift; - unsigned int mult = 1; - unsigned int div = 1; - - if (!strcmp(name, "main")) { - parent_name = of_clk_get_parent_name(np, 0); - div = config->extal_div; - } else if (!strcmp(name, "pll0")) { - /* PLL0 is a configurable multiplier clock. Register it as a - * fixed factor clock for now as there's no generic multiplier - * clock implementation and we currently have no need to change - * the multiplier value. - */ - if (of_device_compatible_match(np, pll0_mult_match)) { - /* R-Car V2H and E2 do not have PLL0CR */ - mult = config->pll0_mult; - div = 3; - } else { - u32 value = readl(cpg->reg + CPG_PLL0CR); - mult = ((value >> 24) & ((1 << 7) - 1)) + 1; - } - parent_name = "main"; - } else if (!strcmp(name, "pll1")) { - parent_name = "main"; - mult = config->pll1_mult / 2; - } else if (!strcmp(name, "pll3")) { - parent_name = "main"; - mult = config->pll3_mult; - } else if (!strcmp(name, "lb")) { - parent_name = "pll1"; - div = cpg_mode & BIT(18) ? 36 : 24; - } else if (!strcmp(name, "qspi")) { - parent_name = "pll1_div2"; - div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2) - ? 8 : 10; - } else if (!strcmp(name, "sdh")) { - parent_name = "pll1"; - table = cpg_sdh_div_table; - shift = 8; - } else if (!strcmp(name, "sd0")) { - parent_name = "pll1"; - table = cpg_sd01_div_table; - shift = 4; - } else if (!strcmp(name, "sd1")) { - parent_name = "pll1"; - table = cpg_sd01_div_table; - shift = 0; - } else if (!strcmp(name, "z")) { - return cpg_z_clk_register(cpg); - } else if (!strcmp(name, "rcan")) { - return cpg_rcan_clk_register(cpg, np); - } else if (!strcmp(name, "adsp")) { - return cpg_adsp_clk_register(cpg); - } else { - return ERR_PTR(-EINVAL); - } - - if (!table) - return clk_register_fixed_factor(NULL, name, parent_name, 0, - mult, div); - else - return clk_register_divider_table(NULL, name, parent_name, 0, - cpg->reg + CPG_SDCKCR, shift, - 4, 0, table, &cpg->lock); -} - -/* - * Reset register definitions. - */ -#define MODEMR 0xe6160060 - -static u32 __init rcar_gen2_read_mode_pins(void) -{ - void __iomem *modemr = ioremap_nocache(MODEMR, 4); - u32 mode; - - BUG_ON(!modemr); - mode = ioread32(modemr); - iounmap(modemr); - - return mode; -} - -static void __init rcar_gen2_cpg_clocks_init(struct device_node *np) -{ - const struct cpg_pll_config *config; - struct rcar_gen2_cpg *cpg; - struct clk **clks; - unsigned int i; - int num_clks; - - if (rcar_rst_read_mode_pins(&cpg_mode)) { - /* Backward-compatibility with old DT */ - pr_warn("%pOF: failed to obtain mode pins from RST\n", np); - cpg_mode = rcar_gen2_read_mode_pins(); - } - - num_clks = of_property_count_strings(np, "clock-output-names"); - if (num_clks < 0) { - pr_err("%s: failed to count clocks\n", __func__); - return; - } - - cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); - clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); - if (cpg == NULL || clks == NULL) { - /* We're leaking memory on purpose, there's no point in cleaning - * up as the system won't boot anyway. - */ - return; - } - - spin_lock_init(&cpg->lock); - - cpg->data.clks = clks; - cpg->data.clk_num = num_clks; - - cpg->reg = of_iomap(np, 0); - if (WARN_ON(cpg->reg == NULL)) - return; - - config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; - - for (i = 0; i < num_clks; ++i) { - const char *name; - struct clk *clk; - - of_property_read_string_index(np, "clock-output-names", i, - &name); - - clk = rcar_gen2_cpg_register_clock(np, cpg, config, name); - if (IS_ERR(clk)) - pr_err("%s: failed to register %pOFn %s clock (%ld)\n", - __func__, np, name, PTR_ERR(clk)); - else - cpg->data.clks[i] = clk; - } - - of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); - - cpg_mstp_add_clk_domain(np); -} -CLK_OF_DECLARE(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks", - rcar_gen2_cpg_clocks_init); diff --git a/drivers/clk/renesas/r8a774b1-cpg-mssr.c b/drivers/clk/renesas/r8a774b1-cpg-mssr.c new file mode 100644 index 000000000000..c9af70917312 --- /dev/null +++ b/drivers/clk/renesas/r8a774b1-cpg-mssr.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * r8a774b1 Clock Pulse Generator / Module Standby and Software Reset + * + * Copyright (C) 2019 Renesas Electronics Corp. + * + * Based on r8a7796-cpg-mssr.c + * + * Copyright (C) 2016 Glider bvba + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/soc/renesas/rcar-rst.h> + +#include <dt-bindings/clock/r8a774b1-cpg-mssr.h> + +#include "renesas-cpg-mssr.h" +#include "rcar-gen3-cpg.h" + +enum clk_ids { + /* Core Clock Outputs exported to DT */ + LAST_DT_CORE_CLK = R8A774B1_CLK_CANFD, + + /* External Input Clocks */ + CLK_EXTAL, + CLK_EXTALR, + + /* Internal Core Clocks */ + CLK_MAIN, + CLK_PLL0, + CLK_PLL1, + CLK_PLL3, + CLK_PLL4, + CLK_PLL1_DIV2, + CLK_PLL1_DIV4, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLK_RINT, + + /* Module Clocks */ + MOD_CLK_BASE +}; + +static const struct cpg_core_clk r8a774b1_core_clks[] __initconst = { + /* External Clock Inputs */ + DEF_INPUT("extal", CLK_EXTAL), + DEF_INPUT("extalr", CLK_EXTALR), + + /* Internal Core Clocks */ + DEF_BASE(".main", CLK_MAIN, CLK_TYPE_GEN3_MAIN, CLK_EXTAL), + DEF_BASE(".pll0", CLK_PLL0, CLK_TYPE_GEN3_PLL0, CLK_MAIN), + DEF_BASE(".pll1", CLK_PLL1, CLK_TYPE_GEN3_PLL1, CLK_MAIN), + DEF_BASE(".pll3", CLK_PLL3, CLK_TYPE_GEN3_PLL3, CLK_MAIN), + DEF_BASE(".pll4", CLK_PLL4, CLK_TYPE_GEN3_PLL4, CLK_MAIN), + + DEF_FIXED(".pll1_div2", CLK_PLL1_DIV2, CLK_PLL1, 2, 1), + DEF_FIXED(".pll1_div4", CLK_PLL1_DIV4, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s0", CLK_S0, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED(".s1", CLK_S1, CLK_PLL1_DIV2, 3, 1), + DEF_FIXED(".s2", CLK_S2, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED(".s3", CLK_S3, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED(".sdsrc", CLK_SDSRC, CLK_PLL1_DIV2, 2, 1), + + DEF_GEN3_OSC(".r", CLK_RINT, CLK_EXTAL, 32), + + /* Core Clock Outputs */ + DEF_GEN3_Z("z", R8A774B1_CLK_Z, CLK_TYPE_GEN3_Z, CLK_PLL0, 2, 8), + DEF_FIXED("ztr", R8A774B1_CLK_ZTR, CLK_PLL1_DIV2, 6, 1), + DEF_FIXED("ztrd2", R8A774B1_CLK_ZTRD2, CLK_PLL1_DIV2, 12, 1), + DEF_FIXED("zt", R8A774B1_CLK_ZT, CLK_PLL1_DIV2, 4, 1), + DEF_FIXED("zx", R8A774B1_CLK_ZX, CLK_PLL1_DIV2, 2, 1), + DEF_FIXED("s0d1", R8A774B1_CLK_S0D1, CLK_S0, 1, 1), + DEF_FIXED("s0d2", R8A774B1_CLK_S0D2, CLK_S0, 2, 1), + DEF_FIXED("s0d3", R8A774B1_CLK_S0D3, CLK_S0, 3, 1), + DEF_FIXED("s0d4", R8A774B1_CLK_S0D4, CLK_S0, 4, 1), + DEF_FIXED("s0d6", R8A774B1_CLK_S0D6, CLK_S0, 6, 1), + DEF_FIXED("s0d8", R8A774B1_CLK_S0D8, CLK_S0, 8, 1), + DEF_FIXED("s0d12", R8A774B1_CLK_S0D12, CLK_S0, 12, 1), + DEF_FIXED("s1d2", R8A774B1_CLK_S1D2, CLK_S1, 2, 1), + DEF_FIXED("s1d4", R8A774B1_CLK_S1D4, CLK_S1, 4, 1), + DEF_FIXED("s2d1", R8A774B1_CLK_S2D1, CLK_S2, 1, 1), + DEF_FIXED("s2d2", R8A774B1_CLK_S2D2, CLK_S2, 2, 1), + DEF_FIXED("s2d4", R8A774B1_CLK_S2D4, CLK_S2, 4, 1), + DEF_FIXED("s3d1", R8A774B1_CLK_S3D1, CLK_S3, 1, 1), + DEF_FIXED("s3d2", R8A774B1_CLK_S3D2, CLK_S3, 2, 1), + DEF_FIXED("s3d4", R8A774B1_CLK_S3D4, CLK_S3, 4, 1), + + DEF_GEN3_SD("sd0", R8A774B1_CLK_SD0, CLK_SDSRC, 0x074), + DEF_GEN3_SD("sd1", R8A774B1_CLK_SD1, CLK_SDSRC, 0x078), + DEF_GEN3_SD("sd2", R8A774B1_CLK_SD2, CLK_SDSRC, 0x268), + DEF_GEN3_SD("sd3", R8A774B1_CLK_SD3, CLK_SDSRC, 0x26c), + + DEF_FIXED("cl", R8A774B1_CLK_CL, CLK_PLL1_DIV2, 48, 1), + DEF_FIXED("cp", R8A774B1_CLK_CP, CLK_EXTAL, 2, 1), + DEF_FIXED("cpex", R8A774B1_CLK_CPEX, CLK_EXTAL, 2, 1), + + DEF_DIV6P1("canfd", R8A774B1_CLK_CANFD, CLK_PLL1_DIV4, 0x244), + DEF_DIV6P1("csi0", R8A774B1_CLK_CSI0, CLK_PLL1_DIV4, 0x00c), + DEF_DIV6P1("mso", R8A774B1_CLK_MSO, CLK_PLL1_DIV4, 0x014), + DEF_DIV6P1("hdmi", R8A774B1_CLK_HDMI, CLK_PLL1_DIV4, 0x250), + + DEF_GEN3_OSC("osc", R8A774B1_CLK_OSC, CLK_EXTAL, 8), + + DEF_BASE("r", R8A774B1_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT), +}; + +static const struct mssr_mod_clk r8a774b1_mod_clks[] __initconst = { + DEF_MOD("tmu4", 121, R8A774B1_CLK_S0D6), + DEF_MOD("tmu3", 122, R8A774B1_CLK_S3D2), + DEF_MOD("tmu2", 123, R8A774B1_CLK_S3D2), + DEF_MOD("tmu1", 124, R8A774B1_CLK_S3D2), + DEF_MOD("tmu0", 125, R8A774B1_CLK_CP), + DEF_MOD("fdp1-0", 119, R8A774B1_CLK_S0D1), + DEF_MOD("scif5", 202, R8A774B1_CLK_S3D4), + DEF_MOD("scif4", 203, R8A774B1_CLK_S3D4), + DEF_MOD("scif3", 204, R8A774B1_CLK_S3D4), + DEF_MOD("scif1", 206, R8A774B1_CLK_S3D4), + DEF_MOD("scif0", 207, R8A774B1_CLK_S3D4), + DEF_MOD("msiof3", 208, R8A774B1_CLK_MSO), + DEF_MOD("msiof2", 209, R8A774B1_CLK_MSO), + DEF_MOD("msiof1", 210, R8A774B1_CLK_MSO), + DEF_MOD("msiof0", 211, R8A774B1_CLK_MSO), + DEF_MOD("sys-dmac2", 217, R8A774B1_CLK_S3D1), + DEF_MOD("sys-dmac1", 218, R8A774B1_CLK_S3D1), + DEF_MOD("sys-dmac0", 219, R8A774B1_CLK_S0D3), + DEF_MOD("cmt3", 300, R8A774B1_CLK_R), + DEF_MOD("cmt2", 301, R8A774B1_CLK_R), + DEF_MOD("cmt1", 302, R8A774B1_CLK_R), + DEF_MOD("cmt0", 303, R8A774B1_CLK_R), + DEF_MOD("tpu0", 304, R8A774B1_CLK_S3D4), + DEF_MOD("scif2", 310, R8A774B1_CLK_S3D4), + DEF_MOD("sdif3", 311, R8A774B1_CLK_SD3), + DEF_MOD("sdif2", 312, R8A774B1_CLK_SD2), + DEF_MOD("sdif1", 313, R8A774B1_CLK_SD1), + DEF_MOD("sdif0", 314, R8A774B1_CLK_SD0), + DEF_MOD("pcie1", 318, R8A774B1_CLK_S3D1), + DEF_MOD("pcie0", 319, R8A774B1_CLK_S3D1), + DEF_MOD("usb3-if0", 328, R8A774B1_CLK_S3D1), + DEF_MOD("usb-dmac0", 330, R8A774B1_CLK_S3D1), + DEF_MOD("usb-dmac1", 331, R8A774B1_CLK_S3D1), + DEF_MOD("rwdt", 402, R8A774B1_CLK_R), + DEF_MOD("intc-ex", 407, R8A774B1_CLK_CP), + DEF_MOD("intc-ap", 408, R8A774B1_CLK_S0D3), + DEF_MOD("audmac1", 501, R8A774B1_CLK_S1D2), + DEF_MOD("audmac0", 502, R8A774B1_CLK_S1D2), + DEF_MOD("hscif4", 516, R8A774B1_CLK_S3D1), + DEF_MOD("hscif3", 517, R8A774B1_CLK_S3D1), + DEF_MOD("hscif2", 518, R8A774B1_CLK_S3D1), + DEF_MOD("hscif1", 519, R8A774B1_CLK_S3D1), + DEF_MOD("hscif0", 520, R8A774B1_CLK_S3D1), + DEF_MOD("thermal", 522, R8A774B1_CLK_CP), + DEF_MOD("pwm", 523, R8A774B1_CLK_S0D12), + DEF_MOD("fcpvd1", 602, R8A774B1_CLK_S0D2), + DEF_MOD("fcpvd0", 603, R8A774B1_CLK_S0D2), + DEF_MOD("fcpvb0", 607, R8A774B1_CLK_S0D1), + DEF_MOD("fcpvi0", 611, R8A774B1_CLK_S0D1), + DEF_MOD("fcpf0", 615, R8A774B1_CLK_S0D1), + DEF_MOD("fcpcs", 619, R8A774B1_CLK_S0D2), + DEF_MOD("vspd1", 622, R8A774B1_CLK_S0D2), + DEF_MOD("vspd0", 623, R8A774B1_CLK_S0D2), + DEF_MOD("vspb", 626, R8A774B1_CLK_S0D1), + DEF_MOD("vspi0", 631, R8A774B1_CLK_S0D1), + DEF_MOD("ehci1", 702, R8A774B1_CLK_S3D2), + DEF_MOD("ehci0", 703, R8A774B1_CLK_S3D2), + DEF_MOD("hsusb", 704, R8A774B1_CLK_S3D2), + DEF_MOD("csi20", 714, R8A774B1_CLK_CSI0), + DEF_MOD("csi40", 716, R8A774B1_CLK_CSI0), + DEF_MOD("du3", 721, R8A774B1_CLK_S2D1), + DEF_MOD("du1", 723, R8A774B1_CLK_S2D1), + DEF_MOD("du0", 724, R8A774B1_CLK_S2D1), + DEF_MOD("lvds", 727, R8A774B1_CLK_S2D1), + DEF_MOD("hdmi0", 729, R8A774B1_CLK_HDMI), + DEF_MOD("vin7", 804, R8A774B1_CLK_S0D2), + DEF_MOD("vin6", 805, R8A774B1_CLK_S0D2), + DEF_MOD("vin5", 806, R8A774B1_CLK_S0D2), + DEF_MOD("vin4", 807, R8A774B1_CLK_S0D2), + DEF_MOD("vin3", 808, R8A774B1_CLK_S0D2), + DEF_MOD("vin2", 809, R8A774B1_CLK_S0D2), + DEF_MOD("vin1", 810, R8A774B1_CLK_S0D2), + DEF_MOD("vin0", 811, R8A774B1_CLK_S0D2), + DEF_MOD("etheravb", 812, R8A774B1_CLK_S0D6), + DEF_MOD("sata0", 815, R8A774B1_CLK_S3D2), + DEF_MOD("gpio7", 905, R8A774B1_CLK_S3D4), + DEF_MOD("gpio6", 906, R8A774B1_CLK_S3D4), + DEF_MOD("gpio5", 907, R8A774B1_CLK_S3D4), + DEF_MOD("gpio4", 908, R8A774B1_CLK_S3D4), + DEF_MOD("gpio3", 909, R8A774B1_CLK_S3D4), + DEF_MOD("gpio2", 910, R8A774B1_CLK_S3D4), + DEF_MOD("gpio1", 911, R8A774B1_CLK_S3D4), + DEF_MOD("gpio0", 912, R8A774B1_CLK_S3D4), + DEF_MOD("can-fd", 914, R8A774B1_CLK_S3D2), + DEF_MOD("can-if1", 915, R8A774B1_CLK_S3D4), + DEF_MOD("can-if0", 916, R8A774B1_CLK_S3D4), + DEF_MOD("i2c6", 918, R8A774B1_CLK_S0D6), + DEF_MOD("i2c5", 919, R8A774B1_CLK_S0D6), + DEF_MOD("i2c-dvfs", 926, R8A774B1_CLK_CP), + DEF_MOD("i2c4", 927, R8A774B1_CLK_S0D6), + DEF_MOD("i2c3", 928, R8A774B1_CLK_S0D6), + DEF_MOD("i2c2", 929, R8A774B1_CLK_S3D2), + DEF_MOD("i2c1", 930, R8A774B1_CLK_S3D2), + DEF_MOD("i2c0", 931, R8A774B1_CLK_S3D2), + DEF_MOD("ssi-all", 1005, R8A774B1_CLK_S3D4), + DEF_MOD("ssi9", 1006, MOD_CLK_ID(1005)), + DEF_MOD("ssi8", 1007, MOD_CLK_ID(1005)), + DEF_MOD("ssi7", 1008, MOD_CLK_ID(1005)), + DEF_MOD("ssi6", 1009, MOD_CLK_ID(1005)), + DEF_MOD("ssi5", 1010, MOD_CLK_ID(1005)), + DEF_MOD("ssi4", 1011, MOD_CLK_ID(1005)), + DEF_MOD("ssi3", 1012, MOD_CLK_ID(1005)), + DEF_MOD("ssi2", 1013, MOD_CLK_ID(1005)), + DEF_MOD("ssi1", 1014, MOD_CLK_ID(1005)), + DEF_MOD("ssi0", 1015, MOD_CLK_ID(1005)), + DEF_MOD("scu-all", 1017, R8A774B1_CLK_S3D4), + DEF_MOD("scu-dvc1", 1018, MOD_CLK_ID(1017)), + DEF_MOD("scu-dvc0", 1019, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu1-mix1", 1020, MOD_CLK_ID(1017)), + DEF_MOD("scu-ctu0-mix0", 1021, MOD_CLK_ID(1017)), + DEF_MOD("scu-src9", 1022, MOD_CLK_ID(1017)), + DEF_MOD("scu-src8", 1023, MOD_CLK_ID(1017)), + DEF_MOD("scu-src7", 1024, MOD_CLK_ID(1017)), + DEF_MOD("scu-src6", 1025, MOD_CLK_ID(1017)), + DEF_MOD("scu-src5", 1026, MOD_CLK_ID(1017)), + DEF_MOD("scu-src4", 1027, MOD_CLK_ID(1017)), + DEF_MOD("scu-src3", 1028, MOD_CLK_ID(1017)), + DEF_MOD("scu-src2", 1029, MOD_CLK_ID(1017)), + DEF_MOD("scu-src1", 1030, MOD_CLK_ID(1017)), + DEF_MOD("scu-src0", 1031, MOD_CLK_ID(1017)), +}; + +static const unsigned int r8a774b1_crit_mod_clks[] __initconst = { + MOD_CLK_ID(408), /* INTC-AP (GIC) */ +}; + +/* + * CPG Clock Data + */ + +/* + * MD EXTAL PLL0 PLL1 PLL3 PLL4 OSC + * 14 13 19 17 (MHz) + *----------------------------------------------------------------- + * 0 0 0 0 16.66 x 1 x180 x192 x192 x144 /16 + * 0 0 0 1 16.66 x 1 x180 x192 x128 x144 /16 + * 0 0 1 0 Prohibited setting + * 0 0 1 1 16.66 x 1 x180 x192 x192 x144 /16 + * 0 1 0 0 20 x 1 x150 x160 x160 x120 /19 + * 0 1 0 1 20 x 1 x150 x160 x106 x120 /19 + * 0 1 1 0 Prohibited setting + * 0 1 1 1 20 x 1 x150 x160 x160 x120 /19 + * 1 0 0 0 25 x 1 x120 x128 x128 x96 /24 + * 1 0 0 1 25 x 1 x120 x128 x84 x96 /24 + * 1 0 1 0 Prohibited setting + * 1 0 1 1 25 x 1 x120 x128 x128 x96 /24 + * 1 1 0 0 33.33 / 2 x180 x192 x192 x144 /32 + * 1 1 0 1 33.33 / 2 x180 x192 x128 x144 /32 + * 1 1 1 0 Prohibited setting + * 1 1 1 1 33.33 / 2 x180 x192 x192 x144 /32 + */ +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 11) | \ + (((md) & BIT(13)) >> 11) | \ + (((md) & BIT(19)) >> 18) | \ + (((md) & BIT(17)) >> 17)) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { + /* EXTAL div PLL1 mult/div PLL3 mult/div OSC prediv */ + { 1, 192, 1, 192, 1, 16, }, + { 1, 192, 1, 128, 1, 16, }, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, 16, }, + { 1, 160, 1, 160, 1, 19, }, + { 1, 160, 1, 106, 1, 19, }, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, 19, }, + { 1, 128, 1, 128, 1, 24, }, + { 1, 128, 1, 84, 1, 24, }, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, 24, }, + { 2, 192, 1, 192, 1, 32, }, + { 2, 192, 1, 128, 1, 32, }, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, 32, }, +}; + +static int __init r8a774b1_cpg_mssr_init(struct device *dev) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + u32 cpg_mode; + int error; + + error = rcar_rst_read_mode_pins(&cpg_mode); + if (error) + return error; + + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)]; + if (!cpg_pll_config->extal_div) { + dev_err(dev, "Prohibited setting (cpg_mode=0x%x)\n", cpg_mode); + return -EINVAL; + } + + return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode); +} + +const struct cpg_mssr_info r8a774b1_cpg_mssr_info __initconst = { + /* Core Clocks */ + .core_clks = r8a774b1_core_clks, + .num_core_clks = ARRAY_SIZE(r8a774b1_core_clks), + .last_dt_core_clk = LAST_DT_CORE_CLK, + .num_total_core_clks = MOD_CLK_BASE, + + /* Module Clocks */ + .mod_clks = r8a774b1_mod_clks, + .num_mod_clks = ARRAY_SIZE(r8a774b1_mod_clks), + .num_hw_mod_clks = 12 * 32, + + /* Critical Module Clocks */ + .crit_mod_clks = r8a774b1_crit_mod_clks, + .num_crit_mod_clks = ARRAY_SIZE(r8a774b1_crit_mod_clks), + + /* Callbacks */ + .init = r8a774b1_cpg_mssr_init, + .cpg_clk_register = rcar_gen3_cpg_clk_register, +}; diff --git a/drivers/clk/renesas/r8a7796-cpg-mssr.c b/drivers/clk/renesas/r8a7796-cpg-mssr.c index 90cc6a102602..e8420d3ada94 100644 --- a/drivers/clk/renesas/r8a7796-cpg-mssr.c +++ b/drivers/clk/renesas/r8a7796-cpg-mssr.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 /* - * r8a7796 Clock Pulse Generator / Module Standby and Software Reset + * r8a7796 (R-Car M3-W/W+) Clock Pulse Generator / Module Standby and Software + * Reset * - * Copyright (C) 2016 Glider bvba - * Copyright (C) 2018 Renesas Electronics Corp. + * Copyright (C) 2016-2019 Glider bvba + * Copyright (C) 2018-2019 Renesas Electronics Corp. * * Based on r8a7795-cpg-mssr.c * @@ -14,6 +15,7 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/of.h> #include <linux/soc/renesas/rcar-rst.h> #include <dt-bindings/clock/r8a7796-cpg-mssr.h> @@ -116,7 +118,7 @@ static const struct cpg_core_clk r8a7796_core_clks[] __initconst = { DEF_BASE("r", R8A7796_CLK_R, CLK_TYPE_GEN3_R, CLK_RINT), }; -static const struct mssr_mod_clk r8a7796_mod_clks[] __initconst = { +static struct mssr_mod_clk r8a7796_mod_clks[] __initdata = { DEF_MOD("fdp1-0", 119, R8A7796_CLK_S0D1), DEF_MOD("scif5", 202, R8A7796_CLK_S3D4), DEF_MOD("scif4", 203, R8A7796_CLK_S3D4), @@ -304,6 +306,14 @@ static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] __initconst = { { 2, 192, 1, 192, 1, 32, }, }; + /* + * Fixups for R-Car M3-W+ + */ + +static const unsigned int r8a77961_mod_nullify[] __initconst = { + MOD_CLK_ID(617), /* FCPCI0 */ +}; + static int __init r8a7796_cpg_mssr_init(struct device *dev) { const struct rcar_gen3_cpg_pll_config *cpg_pll_config; @@ -320,6 +330,12 @@ static int __init r8a7796_cpg_mssr_init(struct device *dev) return -EINVAL; } + if (of_device_is_compatible(dev->of_node, "renesas,r8a77961-cpg-mssr")) + mssr_mod_nullify(r8a7796_mod_clks, + ARRAY_SIZE(r8a7796_mod_clks), + r8a77961_mod_nullify, + ARRAY_SIZE(r8a77961_mod_nullify)); + return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode); } diff --git a/drivers/clk/renesas/r8a77965-cpg-mssr.c b/drivers/clk/renesas/r8a77965-cpg-mssr.c index b4e8c5b7d515..b3af4da2ca74 100644 --- a/drivers/clk/renesas/r8a77965-cpg-mssr.c +++ b/drivers/clk/renesas/r8a77965-cpg-mssr.c @@ -323,7 +323,7 @@ static int __init r8a77965_cpg_mssr_init(struct device *dev) } return rcar_gen3_cpg_init(cpg_pll_config, CLK_EXTALR, cpg_mode); -}; +} const struct cpg_mssr_info r8a77965_cpg_mssr_info __initconst = { /* Core Clocks */ diff --git a/drivers/clk/renesas/rcar-gen2-cpg.c b/drivers/clk/renesas/rcar-gen2-cpg.c index f596a2dafcf4..d4fa3dc3e2a2 100644 --- a/drivers/clk/renesas/rcar-gen2-cpg.c +++ b/drivers/clk/renesas/rcar-gen2-cpg.c @@ -63,19 +63,22 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, return div_u64((u64)parent_rate * mult, 32); } -static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int cpg_z_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long prate = *parent_rate; - unsigned int mult; + unsigned long prate = req->best_parent_rate; + unsigned int min_mult, max_mult, mult; - if (!prate) - prate = 1; + min_mult = max(div64_ul(req->min_rate * 32ULL, prate), 1ULL); + max_mult = min(div64_ul(req->max_rate * 32ULL, prate), 32ULL); + if (max_mult < min_mult) + return -EINVAL; - mult = div_u64((u64)rate * 32, prate); - mult = clamp(mult, 1U, 32U); + mult = div64_ul(req->rate * 32ULL, prate); + mult = clamp(mult, min_mult, max_mult); - return *parent_rate / 32 * mult; + req->rate = div_u64((u64)prate * mult, 32); + return 0; } static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -86,7 +89,7 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, u32 val, kick; unsigned int i; - mult = div_u64((u64)rate * 32, parent_rate); + mult = div64_ul(rate * 32ULL, parent_rate); mult = clamp(mult, 1U, 32U); if (readl(zclk->kick_reg) & CPG_FRQCRB_KICK) @@ -126,7 +129,7 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops cpg_z_clk_ops = { .recalc_rate = cpg_z_clk_recalc_rate, - .round_rate = cpg_z_clk_round_rate, + .determine_rate = cpg_z_clk_determine_rate, .set_rate = cpg_z_clk_set_rate, }; diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c index d25c8ba00a65..c97b647db9b6 100644 --- a/drivers/clk/renesas/rcar-gen3-cpg.c +++ b/drivers/clk/renesas/rcar-gen3-cpg.c @@ -114,18 +114,24 @@ static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw, 32 * zclk->fixed_div); } -static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int cpg_z_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct cpg_z_clk *zclk = to_z_clk(hw); + unsigned int min_mult, max_mult, mult; unsigned long prate; - unsigned int mult; - prate = *parent_rate / zclk->fixed_div; - mult = div_u64(rate * 32ULL, prate); - mult = clamp(mult, 1U, 32U); + prate = req->best_parent_rate / zclk->fixed_div; + min_mult = max(div64_ul(req->min_rate * 32ULL, prate), 1ULL); + max_mult = min(div64_ul(req->max_rate * 32ULL, prate), 32ULL); + if (max_mult < min_mult) + return -EINVAL; + + mult = div64_ul(req->rate * 32ULL, prate); + mult = clamp(mult, min_mult, max_mult); - return (u64)prate * mult / 32; + req->rate = div_u64((u64)prate * mult, 32); + return 0; } static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, @@ -172,7 +178,7 @@ static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate, static const struct clk_ops cpg_z_clk_ops = { .recalc_rate = cpg_z_clk_recalc_rate, - .round_rate = cpg_z_clk_round_rate, + .determine_rate = cpg_z_clk_determine_rate, .set_rate = cpg_z_clk_set_rate, }; @@ -309,44 +315,44 @@ static unsigned long cpg_sd_clock_recalc_rate(struct clk_hw *hw, clock->div_table[clock->cur_div_idx].div); } -static unsigned int cpg_sd_clock_calc_div(struct sd_clock *clock, - unsigned long rate, - unsigned long parent_rate) +static int cpg_sd_clock_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - unsigned long calc_rate, diff, diff_min = ULONG_MAX; - unsigned int i, best_div = 0; + unsigned long best_rate = ULONG_MAX, diff_min = ULONG_MAX; + struct sd_clock *clock = to_sd_clock(hw); + unsigned long calc_rate, diff; + unsigned int i; for (i = 0; i < clock->div_num; i++) { - calc_rate = DIV_ROUND_CLOSEST(parent_rate, + calc_rate = DIV_ROUND_CLOSEST(req->best_parent_rate, clock->div_table[i].div); - diff = calc_rate > rate ? calc_rate - rate : rate - calc_rate; + if (calc_rate < req->min_rate || calc_rate > req->max_rate) + continue; + + diff = calc_rate > req->rate ? calc_rate - req->rate + : req->rate - calc_rate; if (diff < diff_min) { - best_div = clock->div_table[i].div; + best_rate = calc_rate; diff_min = diff; } } - return best_div; -} - -static long cpg_sd_clock_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, *parent_rate); + if (best_rate == ULONG_MAX) + return -EINVAL; - return DIV_ROUND_CLOSEST(*parent_rate, div); + req->rate = best_rate; + return 0; } static int cpg_sd_clock_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) + unsigned long parent_rate) { struct sd_clock *clock = to_sd_clock(hw); - unsigned int div = cpg_sd_clock_calc_div(clock, rate, parent_rate); unsigned int i; for (i = 0; i < clock->div_num; i++) - if (div == clock->div_table[i].div) + if (rate == DIV_ROUND_CLOSEST(parent_rate, + clock->div_table[i].div)) break; if (i >= clock->div_num) @@ -366,7 +372,7 @@ static const struct clk_ops cpg_sd_clock_ops = { .disable = cpg_sd_clock_disable, .is_enabled = cpg_sd_clock_is_enabled, .recalc_rate = cpg_sd_clock_recalc_rate, - .round_rate = cpg_sd_clock_round_rate, + .determine_rate = cpg_sd_clock_determine_rate, .set_rate = cpg_sd_clock_set_rate, }; diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 132cc96895e3..a2663fbbd7a5 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -702,6 +702,12 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a774a1_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A774B1 + { + .compatible = "renesas,r8a774b1-cpg-mssr", + .data = &r8a774b1_cpg_mssr_info, + }, +#endif #ifdef CONFIG_CLK_R8A774C0 { .compatible = "renesas,r8a774c0-cpg-mssr", @@ -743,12 +749,18 @@ static const struct of_device_id cpg_mssr_match[] = { .data = &r8a7795_cpg_mssr_info, }, #endif -#ifdef CONFIG_CLK_R8A7796 +#ifdef CONFIG_CLK_R8A77960 { .compatible = "renesas,r8a7796-cpg-mssr", .data = &r8a7796_cpg_mssr_info, }, #endif +#ifdef CONFIG_CLK_R8A77961 + { + .compatible = "renesas,r8a77961-cpg-mssr", + .data = &r8a7796_cpg_mssr_info, + }, +#endif #ifdef CONFIG_CLK_R8A77965 { .compatible = "renesas,r8a77965-cpg-mssr", diff --git a/drivers/clk/renesas/renesas-cpg-mssr.h b/drivers/clk/renesas/renesas-cpg-mssr.h index 4ddcdf3bfb95..3b852ba0ecec 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.h +++ b/drivers/clk/renesas/renesas-cpg-mssr.h @@ -159,6 +159,7 @@ extern const struct cpg_mssr_info r8a7743_cpg_mssr_info; extern const struct cpg_mssr_info r8a7745_cpg_mssr_info; extern const struct cpg_mssr_info r8a77470_cpg_mssr_info; extern const struct cpg_mssr_info r8a774a1_cpg_mssr_info; +extern const struct cpg_mssr_info r8a774b1_cpg_mssr_info; extern const struct cpg_mssr_info r8a774c0_cpg_mssr_info; extern const struct cpg_mssr_info r8a7790_cpg_mssr_info; extern const struct cpg_mssr_info r8a7791_cpg_mssr_info; diff --git a/drivers/clk/rockchip/clk-half-divider.c b/drivers/clk/rockchip/clk-half-divider.c index ba9f00dc9740..b333fc28c94b 100644 --- a/drivers/clk/rockchip/clk-half-divider.c +++ b/drivers/clk/rockchip/clk-half-divider.c @@ -139,12 +139,11 @@ static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops clk_half_divider_ops = { +static const struct clk_ops clk_half_divider_ops = { .recalc_rate = clk_half_divider_recalc_rate, .round_rate = clk_half_divider_round_rate, .set_rate = clk_half_divider_set_rate, }; -EXPORT_SYMBOL_GPL(clk_half_divider_ops); /** * Register a clock branch. diff --git a/drivers/clk/rockchip/clk-px30.c b/drivers/clk/rockchip/clk-px30.c index 3a501896b280..6fb9c98b7d24 100644 --- a/drivers/clk/rockchip/clk-px30.c +++ b/drivers/clk/rockchip/clk-px30.c @@ -167,6 +167,10 @@ PNAME(mux_uart5_p) = { "clk_uart5_src", "clk_uart5_np5", "clk_uart5_frac" }; PNAME(mux_cif_out_p) = { "xin24m", "dummy_cpll", "npll", "usb480m" }; PNAME(mux_dclk_vopb_p) = { "dclk_vopb_src", "dclk_vopb_frac", "xin24m" }; PNAME(mux_dclk_vopl_p) = { "dclk_vopl_src", "dclk_vopl_frac", "xin24m" }; +PNAME(mux_nandc_p) = { "clk_nandc_div", "clk_nandc_div50" }; +PNAME(mux_sdio_p) = { "clk_sdio_div", "clk_sdio_div50" }; +PNAME(mux_emmc_p) = { "clk_emmc_div", "clk_emmc_div50" }; +PNAME(mux_sdmmc_p) = { "clk_sdmmc_div", "clk_sdmmc_div50" }; PNAME(mux_gmac_p) = { "clk_gmac_src", "gmac_clkin" }; PNAME(mux_gmac_rmii_sel_p) = { "clk_gmac_rx_tx_div20", "clk_gmac_rx_tx_div2" }; PNAME(mux_rtc32k_pmu_p) = { "xin32k", "pmu_pvtm_32k", "clk_rtc32k_frac", }; @@ -460,16 +464,40 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = { /* PD_MMC_NAND */ GATE(HCLK_MMC_NAND, "hclk_mmc_nand", "hclk_peri_pre", 0, PX30_CLKGATE_CON(6), 0, GFLAGS), - COMPOSITE(SCLK_NANDC, "clk_nandc", mux_gpll_cpll_npll_p, 0, + COMPOSITE(SCLK_NANDC_DIV, "clk_nandc_div", mux_gpll_cpll_npll_p, 0, PX30_CLKSEL_CON(15), 6, 2, MFLAGS, 0, 5, DFLAGS, + PX30_CLKGATE_CON(5), 11, GFLAGS), + COMPOSITE(SCLK_NANDC_DIV50, "clk_nandc_div50", mux_gpll_cpll_npll_p, 0, + PX30_CLKSEL_CON(15), 6, 2, MFLAGS, 8, 5, DFLAGS, + PX30_CLKGATE_CON(5), 12, GFLAGS), + COMPOSITE_NODIV(SCLK_NANDC, "clk_nandc", mux_nandc_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(15), 15, 1, MFLAGS, PX30_CLKGATE_CON(5), 13, GFLAGS), - COMPOSITE(SCLK_SDIO, "clk_sdio", mux_gpll_cpll_npll_xin24m_p, 0, + COMPOSITE(SCLK_SDIO_DIV, "clk_sdio_div", mux_gpll_cpll_npll_xin24m_p, 0, PX30_CLKSEL_CON(18), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 1, GFLAGS), + COMPOSITE_DIV_OFFSET(SCLK_SDIO_DIV50, "clk_sdio_div50", + mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(18), 14, 2, MFLAGS, + PX30_CLKSEL_CON(19), 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 2, GFLAGS), + COMPOSITE_NODIV(SCLK_SDIO, "clk_sdio", mux_sdio_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(19), 15, 1, MFLAGS, PX30_CLKGATE_CON(6), 3, GFLAGS), - COMPOSITE(SCLK_EMMC, "clk_emmc", mux_gpll_cpll_npll_xin24m_p, 0, + COMPOSITE(SCLK_EMMC_DIV, "clk_emmc_div", mux_gpll_cpll_npll_xin24m_p, 0, PX30_CLKSEL_CON(20), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 4, GFLAGS), + COMPOSITE_DIV_OFFSET(SCLK_EMMC_DIV50, "clk_emmc_div50", mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(20), 14, 2, MFLAGS, + PX30_CLKSEL_CON(21), 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 5, GFLAGS), + COMPOSITE_NODIV(SCLK_EMMC, "clk_emmc", mux_emmc_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(21), 15, 1, MFLAGS, PX30_CLKGATE_CON(6), 6, GFLAGS), COMPOSITE(SCLK_SFC, "clk_sfc", mux_gpll_cpll_p, 0, @@ -494,8 +522,16 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = { /* PD_SDCARD */ GATE(0, "hclk_sdmmc_pre", "hclk_peri_pre", 0, PX30_CLKGATE_CON(6), 12, GFLAGS), - COMPOSITE(SCLK_SDMMC, "clk_sdmmc", mux_gpll_cpll_npll_xin24m_p, 0, + COMPOSITE(SCLK_SDMMC_DIV, "clk_sdmmc_div", mux_gpll_cpll_npll_xin24m_p, 0, PX30_CLKSEL_CON(16), 14, 2, MFLAGS, 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 13, GFLAGS), + COMPOSITE_DIV_OFFSET(SCLK_SDMMC_DIV50, "clk_sdmmc_div50", mux_gpll_cpll_npll_xin24m_p, 0, + PX30_CLKSEL_CON(16), 14, 2, MFLAGS, + PX30_CLKSEL_CON(17), 0, 8, DFLAGS, + PX30_CLKGATE_CON(6), 14, GFLAGS), + COMPOSITE_NODIV(SCLK_SDMMC, "clk_sdmmc", mux_sdmmc_p, + CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, + PX30_CLKSEL_CON(17), 15, 1, MFLAGS, PX30_CLKGATE_CON(6), 15, GFLAGS), /* PD_USB */ @@ -763,29 +799,29 @@ static struct rockchip_clk_branch px30_clk_branches[] __initdata = { GATE(0, "pclk_ddrphy", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 3, GFLAGS), GATE(PCLK_MIPIDSIPHY, "pclk_mipidsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 4, GFLAGS), GATE(PCLK_MIPICSIPHY, "pclk_mipicsiphy", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 5, GFLAGS), - GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 6, GFLAGS), + GATE(PCLK_USB_GRF, "pclk_usb_grf", "pclk_top_pre", 0, PX30_CLKGATE_CON(16), 6, GFLAGS), GATE(0, "pclk_cpu_hoost", "pclk_top_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(16), 7, GFLAGS), /* PD_VI */ - GATE(0, "aclk_vi_niu", "aclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(4), 15, GFLAGS), + GATE(0, "aclk_vi_niu", "aclk_vi_pre", 0, PX30_CLKGATE_CON(4), 15, GFLAGS), GATE(ACLK_CIF, "aclk_cif", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 1, GFLAGS), GATE(ACLK_ISP, "aclk_isp", "aclk_vi_pre", 0, PX30_CLKGATE_CON(5), 3, GFLAGS), - GATE(0, "hclk_vi_niu", "hclk_vi_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(5), 0, GFLAGS), + GATE(0, "hclk_vi_niu", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 0, GFLAGS), GATE(HCLK_CIF, "hclk_cif", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 2, GFLAGS), GATE(HCLK_ISP, "hclk_isp", "hclk_vi_pre", 0, PX30_CLKGATE_CON(5), 4, GFLAGS), /* PD_VO */ - GATE(0, "aclk_vo_niu", "aclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 0, GFLAGS), + GATE(0, "aclk_vo_niu", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 0, GFLAGS), GATE(ACLK_VOPB, "aclk_vopb", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 3, GFLAGS), GATE(ACLK_RGA, "aclk_rga", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 7, GFLAGS), GATE(ACLK_VOPL, "aclk_vopl", "aclk_vo_pre", 0, PX30_CLKGATE_CON(3), 5, GFLAGS), - GATE(0, "hclk_vo_niu", "hclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 1, GFLAGS), + GATE(0, "hclk_vo_niu", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 1, GFLAGS), GATE(HCLK_VOPB, "hclk_vopb", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 4, GFLAGS), GATE(HCLK_RGA, "hclk_rga", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 8, GFLAGS), GATE(HCLK_VOPL, "hclk_vopl", "hclk_vo_pre", 0, PX30_CLKGATE_CON(3), 6, GFLAGS), - GATE(0, "pclk_vo_niu", "pclk_vo_pre", CLK_IGNORE_UNUSED, PX30_CLKGATE_CON(3), 2, GFLAGS), + GATE(0, "pclk_vo_niu", "pclk_vo_pre", 0, PX30_CLKGATE_CON(3), 2, GFLAGS), GATE(PCLK_MIPI_DSI, "pclk_mipi_dsi", "pclk_vo_pre", 0, PX30_CLKGATE_CON(3), 9, GFLAGS), /* PD_BUS */ @@ -940,7 +976,7 @@ static struct rockchip_clk_branch px30_clk_pmu_branches[] __initdata = { GATE(0, "pclk_cru_pmu", "pclk_pmu_pre", CLK_IGNORE_UNUSED, PX30_PMU_CLKGATE_CON(0), 8, GFLAGS), }; -static const char *const px30_pmucru_critical_clocks[] __initconst = { +static const char *const px30_cru_critical_clocks[] __initconst = { "aclk_bus_pre", "pclk_bus_pre", "hclk_bus_pre", @@ -950,10 +986,16 @@ static const char *const px30_pmucru_critical_clocks[] __initconst = { "pclk_top_pre", "pclk_pmu_pre", "hclk_usb_niu", + "pclk_vo_niu", + "aclk_vo_niu", + "hclk_vo_niu", + "aclk_vi_niu", + "hclk_vi_niu", "pll_npll", "usb480m", "clk_uart2", "pclk_uart2", + "pclk_usb_grf", }; static void __init px30_clk_init(struct device_node *np) @@ -985,6 +1027,9 @@ static void __init px30_clk_init(struct device_node *np) &px30_cpuclk_data, px30_cpuclk_rates, ARRAY_SIZE(px30_cpuclk_rates)); + rockchip_clk_protect_critical(px30_cru_critical_clocks, + ARRAY_SIZE(px30_cru_critical_clocks)); + rockchip_register_softrst(np, 12, reg_base + PX30_SOFTRST_CON(0), ROCKCHIP_SOFTRST_HIWORD_MASK); @@ -1017,9 +1062,6 @@ static void __init px30_pmu_clk_init(struct device_node *np) rockchip_clk_register_branches(ctx, px30_clk_pmu_branches, ARRAY_SIZE(px30_clk_pmu_branches)); - rockchip_clk_protect_critical(px30_pmucru_critical_clocks, - ARRAY_SIZE(px30_pmucru_critical_clocks)); - rockchip_clk_of_add_provider(np, ctx); } CLK_OF_DECLARE(px30_cru_pmu, "rockchip,px30-pmucru", px30_pmu_clk_init); diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 31466cd1842f..3a991ca1ee36 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -165,6 +165,8 @@ static const unsigned long exynos5x_clk_regs[] __initconst = { GATE_BUS_CPU, GATE_SCLK_CPU, CLKOUT_CMU_CPU, + APLL_CON0, + KPLL_CON0, CPLL_CON0, DPLL_CON0, EPLL_CON0, @@ -611,7 +613,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { MUX(0, "mout_aclk66", mout_group1_p, SRC_TOP1, 8, 2), MUX(0, "mout_aclk166", mout_group1_p, SRC_TOP1, 24, 2), - MUX(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1), + MUX_F(0, "mout_aclk_g3d", mout_group5_p, SRC_TOP2, 16, 1, + CLK_SET_RATE_PARENT, 0), MUX(0, "mout_user_aclk400_isp", mout_user_aclk400_isp_p, SRC_TOP3, 0, 1), @@ -653,8 +656,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { SRC_TOP5, 8, 1), MUX(0, "mout_user_aclk266_g2d", mout_user_aclk266_g2d_p, SRC_TOP5, 12, 1), - MUX(CLK_MOUT_G3D, "mout_user_aclk_g3d", mout_user_aclk_g3d_p, - SRC_TOP5, 16, 1), + MUX_F(CLK_MOUT_G3D, "mout_user_aclk_g3d", mout_user_aclk_g3d_p, + SRC_TOP5, 16, 1, CLK_SET_RATE_PARENT, 0), MUX(0, "mout_user_aclk300_jpeg", mout_user_aclk300_jpeg_p, SRC_TOP5, 20, 1), MUX(CLK_MOUT_USER_ACLK300_DISP1, "mout_user_aclk300_disp1", @@ -663,7 +666,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { mout_user_aclk300_gscl_p, SRC_TOP5, 28, 1), MUX(0, "mout_sclk_mpll", mout_mpll_p, SRC_TOP6, 0, 1), - MUX(CLK_MOUT_VPLL, "mout_sclk_vpll", mout_vpll_p, SRC_TOP6, 4, 1), + MUX_F(CLK_MOUT_VPLL, "mout_sclk_vpll", mout_vpll_p, SRC_TOP6, 4, 1, + CLK_SET_RATE_PARENT, 0), MUX(CLK_MOUT_SCLK_SPLL, "mout_sclk_spll", mout_spll_p, SRC_TOP6, 8, 1), MUX(0, "mout_sclk_ipll", mout_ipll_p, SRC_TOP6, 12, 1), MUX(0, "mout_sclk_rpll", mout_rpll_p, SRC_TOP6, 16, 1), @@ -707,7 +711,8 @@ static const struct samsung_mux_clock exynos5x_mux_clks[] __initconst = { SRC_TOP12, 8, 1), MUX(0, "mout_sw_aclk266_g2d", mout_sw_aclk266_g2d_p, SRC_TOP12, 12, 1), - MUX(0, "mout_sw_aclk_g3d", mout_sw_aclk_g3d_p, SRC_TOP12, 16, 1), + MUX_F(0, "mout_sw_aclk_g3d", mout_sw_aclk_g3d_p, SRC_TOP12, 16, 1, + CLK_SET_RATE_PARENT, 0), MUX(0, "mout_sw_aclk300_jpeg", mout_sw_aclk300_jpeg_p, SRC_TOP12, 20, 1), MUX(CLK_MOUT_SW_ACLK300, "mout_sw_aclk300_disp1", @@ -804,8 +809,8 @@ static const struct samsung_div_clock exynos5x_div_clks[] __initconst = { DIV_TOP2, 8, 3), DIV(CLK_DOUT_ACLK266_G2D, "dout_aclk266_g2d", "mout_aclk266_g2d", DIV_TOP2, 12, 3), - DIV(CLK_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, - 16, 3), + DIV_F(CLK_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_aclk_g3d", DIV_TOP2, + 16, 3, CLK_SET_RATE_PARENT, 0), DIV(CLK_DOUT_ACLK300_JPEG, "dout_aclk300_jpeg", "mout_aclk300_jpeg", DIV_TOP2, 20, 3), DIV(CLK_DOUT_ACLK300_DISP1, "dout_aclk300_disp1", @@ -1253,7 +1258,8 @@ static struct exynos5_subcmu_reg_dump exynos5x_gsc_suspend_regs[] = { }; static const struct samsung_gate_clock exynos5x_g3d_gate_clks[] __initconst = { - GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, 0, 0), + GATE(CLK_G3D, "g3d", "mout_user_aclk_g3d", GATE_IP_G3D, 9, + CLK_SET_RATE_PARENT, 0), }; static struct exynos5_subcmu_reg_dump exynos5x_g3d_suspend_regs[] = { @@ -1437,6 +1443,17 @@ static const struct samsung_pll_rate_table exynos5420_epll_24mhz_tbl[] = { PLL_36XX_RATE(24 * MHZ, 32768001U, 131, 3, 5, 4719), }; +static const struct samsung_pll_rate_table exynos5420_vpll_24mhz_tbl[] = { + PLL_35XX_RATE(24 * MHZ, 600000000U, 200, 2, 2), + PLL_35XX_RATE(24 * MHZ, 543000000U, 181, 2, 2), + PLL_35XX_RATE(24 * MHZ, 480000000U, 160, 2, 2), + PLL_35XX_RATE(24 * MHZ, 420000000U, 140, 2, 2), + PLL_35XX_RATE(24 * MHZ, 350000000U, 175, 3, 2), + PLL_35XX_RATE(24 * MHZ, 266000000U, 266, 3, 3), + PLL_35XX_RATE(24 * MHZ, 177000000U, 118, 2, 3), + PLL_35XX_RATE(24 * MHZ, 100000000U, 200, 3, 4), +}; + static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = { [apll] = PLL(pll_2550, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), @@ -1561,6 +1578,7 @@ static void __init exynos5x_clk_init(struct device_node *np, exynos5x_plls[apll].rate_table = exynos5420_pll2550x_24mhz_tbl; exynos5x_plls[epll].rate_table = exynos5420_epll_24mhz_tbl; exynos5x_plls[kpll].rate_table = exynos5420_pll2550x_24mhz_tbl; + exynos5x_plls[vpll].rate_table = exynos5420_vpll_24mhz_tbl; } if (soc == EXYNOS5420) diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index 1281672cb00e..7dad9098e897 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -238,7 +238,6 @@ static SIMPLE_DEV_PM_OPS(s3c24xx_dclk_pm_ops, static int s3c24xx_dclk_probe(struct platform_device *pdev) { struct s3c24xx_dclk *s3c24xx_dclk; - struct resource *mem; struct s3c24xx_dclk_drv_data *dclk_variant; struct clk_hw **clk_table; int ret, i; @@ -257,8 +256,7 @@ static int s3c24xx_dclk_probe(struct platform_device *pdev) platform_set_drvdata(pdev, s3c24xx_dclk); spin_lock_init(&s3c24xx_dclk->dclk_lock); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - s3c24xx_dclk->base = devm_ioremap_resource(&pdev->dev, mem); + s3c24xx_dclk->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(s3c24xx_dclk->base)) return PTR_ERR(s3c24xx_dclk->base); diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index e544a38106dd..dad31308c071 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -60,8 +60,7 @@ struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np, struct samsung_clk_provider *ctx; int i; - ctx = kzalloc(sizeof(struct samsung_clk_provider) + - sizeof(*ctx->clk_data.hws) * nr_clks, GFP_KERNEL); + ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL); if (!ctx) panic("could not allocate clock provider context.\n"); diff --git a/drivers/clk/sprd/common.c b/drivers/clk/sprd/common.c index 9d56eac43832..c0af4779892b 100644 --- a/drivers/clk/sprd/common.c +++ b/drivers/clk/sprd/common.c @@ -42,17 +42,15 @@ int sprd_clk_regmap_init(struct platform_device *pdev, void __iomem *base; struct device_node *node = pdev->dev.of_node; struct regmap *regmap; - struct resource *res; if (of_find_property(node, "sprd,syscon", NULL)) { regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon"); - if (IS_ERR_OR_NULL(regmap)) { + if (IS_ERR(regmap)) { pr_err("%s: failed to get syscon regmap\n", __func__); return PTR_ERR(regmap); } } else { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c index d89353a3cdec..f2497d0a4683 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c @@ -203,12 +203,21 @@ static struct ccu_nkmp pll_hsic_clk = { * hardcode it to match with the clock names. */ #define SUN50I_H6_PLL_AUDIO_REG 0x078 + +static struct ccu_sdm_setting pll_audio_sdm_table[] = { + { .rate = 541900800, .pattern = 0xc001288d, .m = 1, .n = 22 }, + { .rate = 589824000, .pattern = 0xc00126e9, .m = 1, .n = 24 }, +}; + static struct ccu_nm pll_audio_base_clk = { .enable = BIT(31), .lock = BIT(28), .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ + .sdm = _SUNXI_CCU_SDM(pll_audio_sdm_table, + BIT(24), 0x178, BIT(31)), .common = { + .features = CCU_FEATURE_SIGMA_DELTA_MOD, .reg = 0x078, .hw.init = CLK_HW_INIT("pll-audio-base", "osc24M", &ccu_nm_ops, @@ -290,7 +299,7 @@ static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents, 0x670, 0, 3, /* M */ 24, 1, /* mux */ BIT(31), /* gate */ - 0); + CLK_SET_RATE_PARENT); static SUNXI_CCU_GATE(bus_gpu_clk, "bus-gpu", "psi-ahb1-ahb2", 0x67c, BIT(0), 0); @@ -753,12 +762,12 @@ static const struct clk_hw *clk_parent_pll_audio[] = { }; /* - * The divider of pll-audio is fixed to 8 now, as pll-audio-4x has a - * fixed post-divider 2. + * The divider of pll-audio is fixed to 24 for now, so 24576000 and 22579200 + * rates can be set exactly in conjunction with sigma-delta modulation. */ static CLK_FIXED_FACTOR_HWS(pll_audio_clk, "pll-audio", clk_parent_pll_audio, - 8, 1, CLK_SET_RATE_PARENT); + 24, 1, CLK_SET_RATE_PARENT); static CLK_FIXED_FACTOR_HWS(pll_audio_2x_clk, "pll-audio-2x", clk_parent_pll_audio, 4, 1, CLK_SET_RATE_PARENT); @@ -1215,12 +1224,12 @@ static int sun50i_h6_ccu_probe(struct platform_device *pdev) } /* - * Force the post-divider of pll-audio to 8 and the output divider - * of it to 1, to make the clock name represents the real frequency. + * Force the post-divider of pll-audio to 12 and the output divider + * of it to 2, so 24576000 and 22579200 rates can be set exactly. */ val = readl(reg + SUN50I_H6_PLL_AUDIO_REG); val &= ~(GENMASK(21, 16) | BIT(0)); - writel(val | (7 << 16), reg + SUN50I_H6_PLL_AUDIO_REG); + writel(val | (11 << 16) | BIT(0), reg + SUN50I_H6_PLL_AUDIO_REG); /* * First clock parent (osc32K) is unusable for CEC. But since there diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.h b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h index b6e2680ef354..d8c38447e11b 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.h +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.h @@ -48,10 +48,6 @@ /* Some more module clocks are exported */ -#define CLK_MBUS 113 - -/* And the GPU module clock is exported */ - #define CLK_NUMBER_H3 (CLK_GPU + 1) #define CLK_NUMBER_H5 (CLK_BUS_SCR1 + 1) diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 4812e45c2214..df966ca06788 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -17,7 +17,9 @@ obj-y += clk-tegra-fixed.o obj-y += clk-tegra-super-gen4.o obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o diff --git a/drivers/clk/tegra/clk-dfll.c b/drivers/clk/tegra/clk-dfll.c index f8688c2ddf1a..c051d92c2bbf 100644 --- a/drivers/clk/tegra/clk-dfll.c +++ b/drivers/clk/tegra/clk-dfll.c @@ -1487,6 +1487,7 @@ static int dfll_init(struct tegra_dfll *td) td->last_unrounded_rate = 0; pm_runtime_enable(td->dev); + pm_runtime_irq_safe(td->dev); pm_runtime_get_sync(td->dev); dfll_set_mode(td, DFLL_DISABLED); @@ -1513,6 +1514,61 @@ di_err1: return ret; } +/** + * tegra_dfll_suspend - check DFLL is disabled + * @dev: DFLL device * + * + * DFLL clock should be disabled by the CPUFreq driver. So, make + * sure it is disabled and disable all clocks needed by the DFLL. + */ +int tegra_dfll_suspend(struct device *dev) +{ + struct tegra_dfll *td = dev_get_drvdata(dev); + + if (dfll_is_running(td)) { + dev_err(td->dev, "DFLL still enabled while suspending\n"); + return -EBUSY; + } + + reset_control_assert(td->dvco_rst); + + return 0; +} +EXPORT_SYMBOL(tegra_dfll_suspend); + +/** + * tegra_dfll_resume - reinitialize DFLL on resume + * @dev: DFLL instance + * + * DFLL is disabled and reset during suspend and resume. + * So, reinitialize the DFLL IP block back for use. + * DFLL clock is enabled later in closed loop mode by CPUFreq + * driver before switching its clock source to DFLL output. + */ +int tegra_dfll_resume(struct device *dev) +{ + struct tegra_dfll *td = dev_get_drvdata(dev); + + reset_control_deassert(td->dvco_rst); + + pm_runtime_get_sync(td->dev); + + dfll_set_mode(td, DFLL_DISABLED); + dfll_set_default_params(td); + + if (td->soc->init_clock_trimmers) + td->soc->init_clock_trimmers(); + + dfll_set_open_loop_config(td); + + dfll_init_out_if(td); + + pm_runtime_put_sync(td->dev); + + return 0; +} +EXPORT_SYMBOL(tegra_dfll_resume); + /* * DT data fetch */ diff --git a/drivers/clk/tegra/clk-dfll.h b/drivers/clk/tegra/clk-dfll.h index 1b14ebe7268b..fb209eb5f365 100644 --- a/drivers/clk/tegra/clk-dfll.h +++ b/drivers/clk/tegra/clk-dfll.h @@ -42,5 +42,7 @@ int tegra_dfll_register(struct platform_device *pdev, struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev); int tegra_dfll_runtime_suspend(struct device *dev); int tegra_dfll_runtime_resume(struct device *dev); +int tegra_dfll_suspend(struct device *dev); +int tegra_dfll_resume(struct device *dev); #endif /* __DRIVERS_CLK_TEGRA_CLK_DFLL_H */ diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index e76731fb7d69..ca0de5f11f84 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -109,10 +109,21 @@ static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static void clk_divider_restore_context(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned long parent_rate = clk_hw_get_rate(parent); + unsigned long rate = clk_hw_get_rate(hw); + + if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0) + WARN_ON(1); +} + const struct clk_ops tegra_clk_frac_div_ops = { .recalc_rate = clk_frac_div_recalc_rate, .set_rate = clk_frac_div_set_rate, .round_rate = clk_frac_div_round_rate, + .restore_context = clk_divider_restore_context, }; struct clk *tegra_clk_register_divider(const char *name, diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index ea39caf3d762..745f9faa98d8 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -403,20 +403,16 @@ static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, } timing->parent_index = 0xff; - for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { - if (!strcmp(emc_parent_clk_names[i], - __clk_get_name(timing->parent))) { - timing->parent_index = i; - break; - } - } - if (timing->parent_index == 0xff) { + i = match_string(emc_parent_clk_names, ARRAY_SIZE(emc_parent_clk_names), + __clk_get_name(timing->parent)); + if (i < 0) { pr_err("timing %pOF: %s is not a valid parent\n", node, __clk_get_name(timing->parent)); clk_put(timing->parent); return -EINVAL; } + timing->parent_index = i; return 0; } diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index de466b4446da..c4faebd32760 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -236,9 +236,9 @@ enum clk_id { tegra_clk_soc_therm, tegra_clk_soc_therm_8, tegra_clk_sor0, - tegra_clk_sor0_lvds, + tegra_clk_sor0_out, tegra_clk_sor1, - tegra_clk_sor1_src, + tegra_clk_sor1_out, tegra_clk_spdif, tegra_clk_spdif_2x, tegra_clk_spdif_in, diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c index 58437da25156..67620c7ecd9e 100644 --- a/drivers/clk/tegra/clk-periph.c +++ b/drivers/clk/tegra/clk-periph.c @@ -3,6 +3,7 @@ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. */ +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/export.h> #include <linux/slab.h> @@ -99,6 +100,23 @@ static void clk_periph_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } +static void clk_periph_restore_context(struct clk_hw *hw) +{ + struct tegra_clk_periph *periph = to_clk_periph(hw); + const struct clk_ops *div_ops = periph->div_ops; + struct clk_hw *div_hw = &periph->divider.hw; + int parent_id; + + parent_id = clk_hw_get_parent_index(hw); + if (WARN_ON(parent_id < 0)) + return; + + if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV)) + div_ops->restore_context(div_hw); + + clk_periph_set_parent(hw, parent_id); +} + const struct clk_ops tegra_clk_periph_ops = { .get_parent = clk_periph_get_parent, .set_parent = clk_periph_set_parent, @@ -108,6 +126,7 @@ const struct clk_ops tegra_clk_periph_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, .disable = clk_periph_disable, + .restore_context = clk_periph_restore_context, }; static const struct clk_ops tegra_clk_periph_nodiv_ops = { @@ -116,6 +135,7 @@ static const struct clk_ops tegra_clk_periph_nodiv_ops = { .is_enabled = clk_periph_is_enabled, .enable = clk_periph_enable, .disable = clk_periph_disable, + .restore_context = clk_periph_restore_context, }; static const struct clk_ops tegra_clk_periph_no_gate_ops = { @@ -124,6 +144,7 @@ static const struct clk_ops tegra_clk_periph_no_gate_ops = { .recalc_rate = clk_periph_recalc_rate, .round_rate = clk_periph_round_rate, .set_rate = clk_periph_set_rate, + .restore_context = clk_periph_restore_context, }; static struct clk *_tegra_clk_register_periph(const char *name, diff --git a/drivers/clk/tegra/clk-pll-out.c b/drivers/clk/tegra/clk-pll-out.c index 35f2bf00e1e6..d8bf89a81e6d 100644 --- a/drivers/clk/tegra/clk-pll-out.c +++ b/drivers/clk/tegra/clk-pll-out.c @@ -69,10 +69,19 @@ static void clk_pll_out_disable(struct clk_hw *hw) spin_unlock_irqrestore(pll_out->lock, flags); } +static void tegra_clk_pll_out_restore_context(struct clk_hw *hw) +{ + if (!__clk_get_enable_count(hw->clk)) + clk_pll_out_disable(hw); + else + clk_pll_out_enable(hw); +} + const struct clk_ops tegra_clk_pll_out_ops = { .is_enabled = clk_pll_out_is_enabled, .enable = clk_pll_out_enable, .disable = clk_pll_out_disable, + .restore_context = tegra_clk_pll_out_restore_context, }; struct clk *tegra_clk_register_pll_out(const char *name, diff --git a/drivers/clk/tegra/clk-pll.c b/drivers/clk/tegra/clk-pll.c index 1583f5fc992f..531c2b3d814e 100644 --- a/drivers/clk/tegra/clk-pll.c +++ b/drivers/clk/tegra/clk-pll.c @@ -1008,6 +1008,27 @@ static unsigned long clk_plle_recalc_rate(struct clk_hw *hw, return rate; } +static void tegra_clk_pll_restore_context(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned long parent_rate = clk_hw_get_rate(parent); + unsigned long rate = clk_hw_get_rate(hw); + + if (clk_pll_is_enabled(hw)) + return; + + if (pll->params->set_defaults) + pll->params->set_defaults(pll); + + clk_pll_set_rate(hw, rate, parent_rate); + + if (!__clk_get_enable_count(hw->clk)) + clk_pll_disable(hw); + else + clk_pll_enable(hw); +} + const struct clk_ops tegra_clk_pll_ops = { .is_enabled = clk_pll_is_enabled, .enable = clk_pll_enable, @@ -1015,6 +1036,7 @@ const struct clk_ops tegra_clk_pll_ops = { .recalc_rate = clk_pll_recalc_rate, .round_rate = clk_pll_round_rate, .set_rate = clk_pll_set_rate, + .restore_context = tegra_clk_pll_restore_context, }; const struct clk_ops tegra_clk_plle_ops = { @@ -1802,6 +1824,27 @@ out: return ret; } + +static void _clk_plle_tegra_init_parent(struct tegra_clk_pll *pll) +{ + u32 val, val_aux; + + /* ensure parent is set to pll_ref */ + val = pll_readl_base(pll); + val_aux = pll_readl(pll->params->aux_reg, pll); + + if (val & PLL_BASE_ENABLE) { + if ((val_aux & PLLE_AUX_PLLRE_SEL) || + (val_aux & PLLE_AUX_PLLP_SEL)) + WARN(1, "pll_e enabled with unsupported parent %s\n", + (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : + "pll_re_vco"); + } else { + val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL); + pll_writel(val_aux, pll->params->aux_reg, pll); + fence_udelay(1, pll->clk_base); + } +} #endif static struct tegra_clk_pll *_tegra_init_pll(void __iomem *clk_base, @@ -2214,27 +2257,12 @@ struct clk *tegra_clk_register_plle_tegra114(const char *name, { struct tegra_clk_pll *pll; struct clk *clk; - u32 val, val_aux; pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); if (IS_ERR(pll)) return ERR_CAST(pll); - /* ensure parent is set to pll_re_vco */ - - val = pll_readl_base(pll); - val_aux = pll_readl(pll_params->aux_reg, pll); - - if (val & PLL_BASE_ENABLE) { - if ((val_aux & PLLE_AUX_PLLRE_SEL) || - (val_aux & PLLE_AUX_PLLP_SEL)) - WARN(1, "pll_e enabled with unsupported parent %s\n", - (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : - "pll_re_vco"); - } else { - val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL); - pll_writel(val_aux, pll_params->aux_reg, pll); - } + _clk_plle_tegra_init_parent(pll); clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_plle_tegra114_ops); @@ -2276,6 +2304,7 @@ static const struct clk_ops tegra_clk_pllss_ops = { .recalc_rate = clk_pll_recalc_rate, .round_rate = clk_pll_ramp_round_rate, .set_rate = clk_pllxc_set_rate, + .restore_context = tegra_clk_pll_restore_context, }; struct clk *tegra_clk_register_pllss(const char *name, const char *parent_name, @@ -2520,11 +2549,19 @@ out: spin_unlock_irqrestore(pll->lock, flags); } +static void tegra_clk_plle_t210_restore_context(struct clk_hw *hw) +{ + struct tegra_clk_pll *pll = to_clk_pll(hw); + + _clk_plle_tegra_init_parent(pll); +} + static const struct clk_ops tegra_clk_plle_tegra210_ops = { .is_enabled = clk_plle_tegra210_is_enabled, .enable = clk_plle_tegra210_enable, .disable = clk_plle_tegra210_disable, .recalc_rate = clk_pll_recalc_rate, + .restore_context = tegra_clk_plle_t210_restore_context, }; struct clk *tegra_clk_register_plle_tegra210(const char *name, @@ -2535,27 +2572,12 @@ struct clk *tegra_clk_register_plle_tegra210(const char *name, { struct tegra_clk_pll *pll; struct clk *clk; - u32 val, val_aux; pll = _tegra_init_pll(clk_base, NULL, pll_params, lock); if (IS_ERR(pll)) return ERR_CAST(pll); - /* ensure parent is set to pll_re_vco */ - - val = pll_readl_base(pll); - val_aux = pll_readl(pll_params->aux_reg, pll); - - if (val & PLLE_BASE_ENABLE) { - if ((val_aux & PLLE_AUX_PLLRE_SEL) || - (val_aux & PLLE_AUX_PLLP_SEL)) - WARN(1, "pll_e enabled with unsupported parent %s\n", - (val_aux & PLLE_AUX_PLLP_SEL) ? "pllp_out0" : - "pll_re_vco"); - } else { - val_aux &= ~(PLLE_AUX_PLLRE_SEL | PLLE_AUX_PLLP_SEL); - pll_writel(val_aux, pll_params->aux_reg, pll); - } + _clk_plle_tegra_init_parent(pll); clk = _tegra_clk_register_pll(pll, name, parent_name, flags, &tegra_clk_plle_tegra210_ops); diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c index a5cd3e31dbae..316912d3b1a4 100644 --- a/drivers/clk/tegra/clk-sdmmc-mux.c +++ b/drivers/clk/tegra/clk-sdmmc-mux.c @@ -194,6 +194,21 @@ static void clk_sdmmc_mux_disable(struct clk_hw *hw) gate_ops->disable(gate_hw); } +static void clk_sdmmc_mux_restore_context(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + unsigned long parent_rate = clk_hw_get_rate(parent); + unsigned long rate = clk_hw_get_rate(hw); + int parent_id; + + parent_id = clk_hw_get_parent_index(hw); + if (WARN_ON(parent_id < 0)) + return; + + clk_sdmmc_mux_set_parent(hw, parent_id); + clk_sdmmc_mux_set_rate(hw, rate, parent_rate); +} + static const struct clk_ops tegra_clk_sdmmc_mux_ops = { .get_parent = clk_sdmmc_mux_get_parent, .set_parent = clk_sdmmc_mux_set_parent, @@ -203,6 +218,7 @@ static const struct clk_ops tegra_clk_sdmmc_mux_ops = { .is_enabled = clk_sdmmc_mux_is_enabled, .enable = clk_sdmmc_mux_enable, .disable = clk_sdmmc_mux_disable, + .restore_context = clk_sdmmc_mux_restore_context, }; struct clk *tegra_clk_register_sdmmc_mux_div(const char *name, diff --git a/drivers/clk/tegra/clk-super.c b/drivers/clk/tegra/clk-super.c index 39ef31b46df5..6099c6e9acd4 100644 --- a/drivers/clk/tegra/clk-super.c +++ b/drivers/clk/tegra/clk-super.c @@ -28,6 +28,9 @@ #define super_state_to_src_shift(m, s) ((m->width * s)) #define super_state_to_src_mask(m) (((1 << m->width) - 1)) +#define CCLK_SRC_PLLP_OUT0 4 +#define CCLK_SRC_PLLP_OUT4 5 + static u8 clk_super_get_parent(struct clk_hw *hw) { struct tegra_clk_super_mux *mux = to_clk_super_mux(hw); @@ -97,12 +100,23 @@ static int clk_super_set_parent(struct clk_hw *hw, u8 index) if (index == mux->div2_index) index = mux->pllx_index; } + + /* enable PLLP branches to CPU before selecting PLLP source */ + if ((mux->flags & TEGRA210_CPU_CLK) && + (index == CCLK_SRC_PLLP_OUT0 || index == CCLK_SRC_PLLP_OUT4)) + tegra_clk_set_pllp_out_cpu(true); + val &= ~((super_state_to_src_mask(mux)) << shift); val |= (index & (super_state_to_src_mask(mux))) << shift; writel_relaxed(val, mux->reg); udelay(2); + /* disable PLLP branches to CPU if not used */ + if ((mux->flags & TEGRA210_CPU_CLK) && + index != CCLK_SRC_PLLP_OUT0 && index != CCLK_SRC_PLLP_OUT4) + tegra_clk_set_pllp_out_cpu(false); + out: if (mux->lock) spin_unlock_irqrestore(mux->lock, flags); @@ -110,9 +124,21 @@ out: return err; } +static void clk_super_mux_restore_context(struct clk_hw *hw) +{ + int parent_id; + + parent_id = clk_hw_get_parent_index(hw); + if (WARN_ON(parent_id < 0)) + return; + + clk_super_set_parent(hw, parent_id); +} + static const struct clk_ops tegra_clk_super_mux_ops = { .get_parent = clk_super_get_parent, .set_parent = clk_super_set_parent, + .restore_context = clk_super_mux_restore_context, }; static long clk_super_round_rate(struct clk_hw *hw, unsigned long rate, @@ -148,12 +174,27 @@ static int clk_super_set_rate(struct clk_hw *hw, unsigned long rate, return super->div_ops->set_rate(div_hw, rate, parent_rate); } +static void clk_super_restore_context(struct clk_hw *hw) +{ + struct tegra_clk_super_mux *super = to_clk_super_mux(hw); + struct clk_hw *div_hw = &super->frac_div.hw; + int parent_id; + + parent_id = clk_hw_get_parent_index(hw); + if (WARN_ON(parent_id < 0)) + return; + + super->div_ops->restore_context(div_hw); + clk_super_set_parent(hw, parent_id); +} + const struct clk_ops tegra_clk_super_ops = { .get_parent = clk_super_get_parent, .set_parent = clk_super_set_parent, .set_rate = clk_super_set_rate, .round_rate = clk_super_round_rate, .recalc_rate = clk_super_recalc_rate, + .restore_context = clk_super_restore_context, }; struct clk *tegra_clk_register_super_mux(const char *name, diff --git a/drivers/clk/tegra/clk-tegra-fixed.c b/drivers/clk/tegra/clk-tegra-fixed.c index 8d91b2b191cf..7c6c8abfcde6 100644 --- a/drivers/clk/tegra/clk-tegra-fixed.c +++ b/drivers/clk/tegra/clk-tegra-fixed.c @@ -17,6 +17,10 @@ #define OSC_CTRL 0x50 #define OSC_CTRL_OSC_FREQ_SHIFT 28 #define OSC_CTRL_PLL_REF_DIV_SHIFT 26 +#define OSC_CTRL_MASK (0x3f2 | \ + (0xf << OSC_CTRL_OSC_FREQ_SHIFT)) + +static u32 osc_ctrl_ctx; int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks, unsigned long *input_freqs, unsigned int num, @@ -29,6 +33,7 @@ int __init tegra_osc_clk_init(void __iomem *clk_base, struct tegra_clk *clks, unsigned osc_idx; val = readl_relaxed(clk_base + OSC_CTRL); + osc_ctrl_ctx = val & OSC_CTRL_MASK; osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT; if (osc_idx < num) @@ -96,3 +101,13 @@ void __init tegra_fixed_clk_init(struct tegra_clk *tegra_clks) *dt_clk = clk; } } + +void tegra_clk_osc_resume(void __iomem *clk_base) +{ + u32 val; + + val = readl_relaxed(clk_base + OSC_CTRL) & ~OSC_CTRL_MASK; + val |= osc_ctrl_ctx; + writel_relaxed(val, clk_base + OSC_CTRL); + fence_udelay(2, clk_base); +} diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 1ed85f120a1b..0d07c0ba49b6 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -262,7 +262,6 @@ static DEFINE_SPINLOCK(PLLP_OUTA_lock); static DEFINE_SPINLOCK(PLLP_OUTB_lock); static DEFINE_SPINLOCK(PLLP_OUTC_lock); -static DEFINE_SPINLOCK(sor0_lock); #define MUX_I2S_SPDIF(_id) \ static const char *mux_pllaout0_##_id##_2x_pllp_clkm[] = { "pll_a_out0", \ @@ -587,11 +586,6 @@ static u32 mux_pllp_pllre_clkm_idx[] = { [0] = 0, [1] = 2, [2] = 3, }; -static const char *mux_clkm_plldp_sor0lvds[] = { - "clk_m", "pll_dp", "sor0_lvds", -}; -#define mux_clkm_plldp_sor0lvds_idx NULL - static const char * const mux_dmic1[] = { "pll_a_out0", "dmic1_sync_clk", "pll_p", "clk_m" }; @@ -731,14 +725,12 @@ static struct tegra_periph_init_data periph_clks[] = { MUX8("hdmi_audio", mux_pllp3_pllc_clkm, CLK_SOURCE_HDMI_AUDIO, 176, TEGRA_PERIPH_NO_RESET, tegra_clk_hdmi_audio), MUX8("clk72mhz", mux_pllp3_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz), MUX8("clk72mhz", mux_pllp_out3_pllp_pllc_clkm, CLK_SOURCE_CLK72MHZ, 177, TEGRA_PERIPH_NO_RESET, tegra_clk_clk72Mhz_8), - MUX8_NOGATE_LOCK("sor0_lvds", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_SOR0, tegra_clk_sor0_lvds, &sor0_lock), MUX_FLAGS("csite", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite, CLK_IGNORE_UNUSED), MUX_FLAGS("csite", mux_pllp_pllre_clkm, CLK_SOURCE_CSITE, 73, TEGRA_PERIPH_ON_APB, tegra_clk_csite_8, CLK_IGNORE_UNUSED), NODIV("disp1", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1, NULL), NODIV("disp1", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP1, 29, 7, 27, 0, tegra_clk_disp1_8, NULL), NODIV("disp2", mux_pllp_pllm_plld_plla_pllc_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2, NULL), NODIV("disp2", mux_pllp_plld_plld2_clkm, CLK_SOURCE_DISP2, 29, 7, 26, 0, tegra_clk_disp2_8, NULL), - NODIV("sor0", mux_clkm_plldp_sor0lvds, CLK_SOURCE_SOR0, 14, 3, 182, 0, tegra_clk_sor0, &sor0_lock), UART("uarta", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTA, 6, tegra_clk_uarta), UART("uartb", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTB, 7, tegra_clk_uartb), UART("uartc", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_UARTC, 55, tegra_clk_uartc), diff --git a/drivers/clk/tegra/clk-tegra-super-gen4.c b/drivers/clk/tegra/clk-tegra-super-gen4.c index cdfe7c9697e1..5760c978bef7 100644 --- a/drivers/clk/tegra/clk-tegra-super-gen4.c +++ b/drivers/clk/tegra/clk-tegra-super-gen4.c @@ -180,7 +180,7 @@ static void __init tegra_super_clk_init(void __iomem *clk_base, gen_info->num_cclk_g_parents, CLK_SET_RATE_PARENT, clk_base + CCLKG_BURST_POLICY, - 0, 4, 8, 0, NULL); + TEGRA210_CPU_CLK, 4, 8, 0, NULL); } else { clk = tegra_clk_register_super_mux("cclk_g", gen_info->cclk_g_parents, @@ -196,6 +196,11 @@ static void __init tegra_super_clk_init(void __iomem *clk_base, dt_clk = tegra_lookup_dt_id(tegra_clk_cclk_lp, tegra_clks); if (dt_clk) { if (gen_info->gen == gen5) { + /* + * TEGRA210_CPU_CLK flag is not needed for cclk_lp as + * cluster switching is not currently supported on + * Tegra210 and also cpu_lp is not used. + */ clk = tegra_clk_register_super_mux("cclk_lp", gen_info->cclk_lp_parents, gen_info->num_cclk_lp_parents, diff --git a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c index e84b6d52cbbd..2ac2679d696d 100644 --- a/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c +++ b/drivers/clk/tegra/clk-tegra124-dfll-fcpu.c @@ -631,6 +631,7 @@ static int tegra124_dfll_fcpu_remove(struct platform_device *pdev) static const struct dev_pm_ops tegra124_dfll_pm_ops = { SET_RUNTIME_PM_OPS(tegra_dfll_runtime_suspend, tegra_dfll_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tegra_dfll_suspend, tegra_dfll_resume) }; static struct platform_driver tegra124_dfll_fcpu_driver = { diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 0224fdc4766f..b3110d5b5a6c 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -27,6 +27,7 @@ #define CLK_SOURCE_CSITE 0x1d4 #define CLK_SOURCE_EMC 0x19c +#define CLK_SOURCE_SOR0 0x414 #define RST_DFLL_DVCO 0x2f4 #define DVFS_DFLL_RESET_SHIFT 0 @@ -91,6 +92,22 @@ /* Tegra CPU clock and reset control regs */ #define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 +#define MASK(x) (BIT(x) - 1) + +#define MUX8_NOGATE_LOCK(_name, _parents, _offset, _clk_id, _lock) \ + TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset, \ + 29, MASK(3), 0, 0, 8, 1, TEGRA_DIVIDER_ROUND_UP,\ + 0, TEGRA_PERIPH_NO_GATE, _clk_id,\ + _parents##_idx, 0, _lock) + +#define NODIV(_name, _parents, _offset, \ + _mux_shift, _mux_mask, _clk_num, \ + _gate_flags, _clk_id, _lock) \ + TEGRA_INIT_DATA_TABLE(_name, NULL, NULL, _parents, _offset,\ + _mux_shift, _mux_mask, 0, 0, 0, 0, 0,\ + _clk_num, (_gate_flags) | TEGRA_PERIPH_NO_DIV,\ + _clk_id, _parents##_idx, 0, _lock) + #ifdef CONFIG_PM_SLEEP static struct cpu_clk_suspend_context { u32 clk_csite_src; @@ -110,6 +127,7 @@ static DEFINE_SPINLOCK(pll_e_lock); static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_u_lock); static DEFINE_SPINLOCK(emc_lock); +static DEFINE_SPINLOCK(sor0_lock); /* possible OSC frequencies in Hz */ static unsigned long tegra124_input_freq[] = { @@ -829,7 +847,7 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_adx1] = { .dt_id = TEGRA124_CLK_ADX1, .present = true }, [tegra_clk_dpaux] = { .dt_id = TEGRA124_CLK_DPAUX, .present = true }, [tegra_clk_sor0] = { .dt_id = TEGRA124_CLK_SOR0, .present = true }, - [tegra_clk_sor0_lvds] = { .dt_id = TEGRA124_CLK_SOR0_LVDS, .present = true }, + [tegra_clk_sor0_out] = { .dt_id = TEGRA124_CLK_SOR0_OUT, .present = true }, [tegra_clk_gpu] = { .dt_id = TEGRA124_CLK_GPU, .present = true }, [tegra_clk_amx1] = { .dt_id = TEGRA124_CLK_AMX1, .present = true }, [tegra_clk_uartb] = { .dt_id = TEGRA124_CLK_UARTB, .present = true }, @@ -987,12 +1005,33 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "hda2hdmi", .dt_id = TEGRA124_CLK_HDA2HDMI }, }; +static const char * const sor0_parents[] = { + "pll_p_out0", "pll_m_out0", "pll_d_out0", "pll_a_out0", "pll_c_out0", + "pll_d2_out0", "clk_m", +}; + +static const char * const sor0_out_parents[] = { + "clk_m", "sor0_pad_clkout", +}; + +static struct tegra_periph_init_data tegra124_periph[] = { + TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents, + CLK_SOURCE_SOR0, 29, 0x7, 0, 0, 0, 0, + 0, 182, 0, tegra_clk_sor0, NULL, 0, + &sor0_lock), + TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents, + CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0, + 0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out, + NULL, 0, &sor0_lock), +}; + static struct clk **clks; static __init void tegra124_periph_clk_init(void __iomem *clk_base, void __iomem *pmc_base) { struct clk *clk; + unsigned int i; /* xusb_ss_div2 */ clk = clk_register_fixed_factor(NULL, "xusb_ss_div2", "xusb_ss_src", 0, @@ -1033,6 +1072,20 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, clk_register_clkdev(clk, "cml1", NULL); clks[TEGRA124_CLK_CML1] = clk; + for (i = 0; i < ARRAY_SIZE(tegra124_periph); i++) { + struct tegra_periph_init_data *init = &tegra124_periph[i]; + struct clk **clkp; + + clkp = tegra_lookup_dt_id(init->clk_id, tegra124_clks); + if (!clkp) { + pr_warn("clock %u not found\n", init->clk_id); + continue; + } + + clk = tegra_clk_register_periph_data(clk_base, init); + *clkp = clk; + } + tegra_periph_clk_init(clk_base, pmc_base, tegra124_clks, &pll_p_params); } diff --git a/drivers/clk/tegra/clk-tegra20-emc.c b/drivers/clk/tegra/clk-tegra20-emc.c new file mode 100644 index 000000000000..03bf0009a33c --- /dev/null +++ b/drivers/clk/tegra/clk-tegra20-emc.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Based on drivers/clk/tegra/clk-emc.c + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Author: Dmitry Osipenko <digetx@gmail.com> + * Copyright (C) 2019 GRATE-DRIVER project + */ + +#define pr_fmt(fmt) "tegra-emc-clk: " fmt + +#include <linux/bits.h> +#include <linux/clk-provider.h> +#include <linux/clk/tegra.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include "clk.h" + +#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0) +#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30) +#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30 + +#define MC_EMC_SAME_FREQ BIT(16) +#define USE_PLLM_UD BIT(29) + +#define EMC_SRC_PLL_M 0 +#define EMC_SRC_PLL_C 1 +#define EMC_SRC_PLL_P 2 +#define EMC_SRC_CLK_M 3 + +static const char * const emc_parent_clk_names[] = { + "pll_m", "pll_c", "pll_p", "clk_m", +}; + +struct tegra_clk_emc { + struct clk_hw hw; + void __iomem *reg; + bool mc_same_freq; + bool want_low_jitter; + + tegra20_clk_emc_round_cb *round_cb; + void *cb_arg; +}; + +static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw) +{ + return container_of(hw, struct tegra_clk_emc, hw); +} + +static unsigned long emc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + u32 val, div; + + val = readl_relaxed(emc->reg); + div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; + + return DIV_ROUND_UP(parent_rate * 2, div + 2); +} + +static u8 emc_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + + return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; +} + +static int emc_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + u32 val, div; + + val = readl_relaxed(emc->reg); + val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK; + val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; + + div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; + + if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) + val |= USE_PLLM_UD; + else + val &= ~USE_PLLM_UD; + + if (emc->mc_same_freq) + val |= MC_EMC_SAME_FREQ; + else + val &= ~MC_EMC_SAME_FREQ; + + writel_relaxed(val, emc->reg); + + fence_udelay(1, emc->reg); + + return 0; +} + +static int emc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + unsigned int index; + u32 val, div; + + div = div_frac_get(rate, parent_rate, 8, 1, 0); + + val = readl_relaxed(emc->reg); + val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; + val |= div; + + index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; + + if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) + val |= USE_PLLM_UD; + else + val &= ~USE_PLLM_UD; + + if (emc->mc_same_freq) + val |= MC_EMC_SAME_FREQ; + else + val &= ~MC_EMC_SAME_FREQ; + + writel_relaxed(val, emc->reg); + + fence_udelay(1, emc->reg); + + return 0; +} + +static int emc_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate, + u8 index) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + u32 val, div; + + div = div_frac_get(rate, parent_rate, 8, 1, 0); + + val = readl_relaxed(emc->reg); + + val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK; + val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT; + + val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK; + val |= div; + + if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter) + val |= USE_PLLM_UD; + else + val &= ~USE_PLLM_UD; + + if (emc->mc_same_freq) + val |= MC_EMC_SAME_FREQ; + else + val &= ~MC_EMC_SAME_FREQ; + + writel_relaxed(val, emc->reg); + + fence_udelay(1, emc->reg); + + return 0; +} + +static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct tegra_clk_emc *emc = to_tegra_clk_emc(hw); + struct clk_hw *parent_hw; + unsigned long divided_rate; + unsigned long parent_rate; + unsigned int i; + long emc_rate; + int div; + + emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate, + emc->cb_arg); + if (emc_rate < 0) + return emc_rate; + + for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { + parent_hw = clk_hw_get_parent_by_index(hw, i); + + if (req->best_parent_hw == parent_hw) + parent_rate = req->best_parent_rate; + else + parent_rate = clk_hw_get_rate(parent_hw); + + if (emc_rate > parent_rate) + continue; + + div = div_frac_get(emc_rate, parent_rate, 8, 1, 0); + divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2); + + if (divided_rate != emc_rate) + continue; + + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent_hw; + req->rate = emc_rate; + break; + } + + if (i == ARRAY_SIZE(emc_parent_clk_names)) { + pr_err_once("can't find parent for rate %lu emc_rate %lu\n", + req->rate, emc_rate); + return -EINVAL; + } + + return 0; +} + +static const struct clk_ops tegra_clk_emc_ops = { + .recalc_rate = emc_recalc_rate, + .get_parent = emc_get_parent, + .set_parent = emc_set_parent, + .set_rate = emc_set_rate, + .set_rate_and_parent = emc_set_rate_and_parent, + .determine_rate = emc_determine_rate, +}; + +void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb, + void *cb_arg) +{ + struct clk *clk = __clk_lookup("emc"); + struct tegra_clk_emc *emc; + struct clk_hw *hw; + + if (clk) { + hw = __clk_get_hw(clk); + emc = to_tegra_clk_emc(hw); + + emc->round_cb = round_cb; + emc->cb_arg = cb_arg; + } +} + +bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw) +{ + return to_tegra_clk_emc(emc_hw)->round_cb != NULL; +} + +struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter) +{ + struct tegra_clk_emc *emc; + struct clk_init_data init; + struct clk *clk; + + emc = kzalloc(sizeof(*emc), GFP_KERNEL); + if (!emc) + return NULL; + + /* + * EMC stands for External Memory Controller. + * + * We don't want EMC clock to be disabled ever by gating its + * parent and whatnot because system is busted immediately in that + * case, hence the clock is marked as critical. + */ + init.name = "emc"; + init.ops = &tegra_clk_emc_ops; + init.flags = CLK_IS_CRITICAL; + init.parent_names = emc_parent_clk_names; + init.num_parents = ARRAY_SIZE(emc_parent_clk_names); + + emc->reg = ioaddr; + emc->hw.init = &init; + emc->want_low_jitter = low_jitter; + + clk = clk_register(NULL, &emc->hw); + if (IS_ERR(clk)) { + kfree(emc); + return NULL; + } + + return clk; +} + +int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same) +{ + struct tegra_clk_emc *emc; + struct clk_hw *hw; + + if (!emc_clk) + return -EINVAL; + + hw = __clk_get_hw(emc_clk); + emc = to_tegra_clk_emc(hw); + emc->mc_same_freq = same; + + return 0; +} diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index bcd871134f45..4d8222f5c638 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -130,8 +130,6 @@ static struct cpu_clk_suspend_context { static void __iomem *clk_base; static void __iomem *pmc_base; -static DEFINE_SPINLOCK(emc_lock); - #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \ @@ -760,7 +758,6 @@ static const char *pwm_parents[] = { "pll_p", "pll_c", "audio", "clk_m", static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" }; static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c", "clk_m" }; -static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" }; static struct tegra_periph_init_data tegra_periph_clk_list[] = { TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1), @@ -787,41 +784,6 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = { TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2), }; -static void __init tegra20_emc_clk_init(void) -{ - const u32 use_pllm_ud = BIT(29); - struct clk *clk; - u32 emc_reg; - - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), - CLK_SET_RATE_NO_REPARENT, - clk_base + CLK_SOURCE_EMC, - 30, 2, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, - &emc_lock); - clks[TEGRA20_CLK_MC] = clk; - - /* un-divided pll_m_out0 is currently unsupported */ - emc_reg = readl_relaxed(clk_base + CLK_SOURCE_EMC); - if (emc_reg & use_pllm_ud) { - pr_err("%s: un-divided PllM_out0 used as clock source\n", - __func__); - return; - } - - /* - * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at - * the same time due to a HW bug, this won't happen because we're - * defining 'emc_mux' and 'emc' as distinct clocks. - */ - clk = tegra_clk_register_divider("emc", "emc_mux", - clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL, - TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock); - clks[TEGRA20_CLK_EMC] = clk; -} - static void __init tegra20_periph_clk_init(void) { struct tegra_periph_init_data *data; @@ -835,7 +797,13 @@ static void __init tegra20_periph_clk_init(void) clks[TEGRA20_CLK_AC97] = clk; /* emc */ - tegra20_emc_clk_init(); + clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false); + + clks[TEGRA20_CLK_EMC] = clk; + + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, + NULL); + clks[TEGRA20_CLK_MC] = clk; /* dsi */ clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, @@ -987,6 +955,7 @@ static void tegra20_cpu_clock_suspend(void) static void tegra20_cpu_clock_resume(void) { unsigned int reg, policy; + u32 misc, base; /* Is CPU complex already running on PLLX? */ reg = readl(clk_base + CCLK_BURST_POLICY); @@ -1000,15 +969,21 @@ static void tegra20_cpu_clock_resume(void) BUG(); if (reg != CCLK_BURST_POLICY_PLLX) { - /* restore PLLX settings if CPU is on different PLL */ - writel(tegra20_cpu_clk_sctx.pllx_misc, - clk_base + PLLX_MISC); - writel(tegra20_cpu_clk_sctx.pllx_base, - clk_base + PLLX_BASE); - - /* wait for PLL stabilization if PLLX was enabled */ - if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30)) - udelay(300); + misc = readl_relaxed(clk_base + PLLX_MISC); + base = readl_relaxed(clk_base + PLLX_BASE); + + if (misc != tegra20_cpu_clk_sctx.pllx_misc || + base != tegra20_cpu_clk_sctx.pllx_base) { + /* restore PLLX settings if CPU is on different PLL */ + writel(tegra20_cpu_clk_sctx.pllx_misc, + clk_base + PLLX_MISC); + writel(tegra20_cpu_clk_sctx.pllx_base, + clk_base + PLLX_BASE); + + /* wait for PLL stabilization if PLLX was enabled */ + if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30)) + udelay(300); + } } /* @@ -1115,6 +1090,8 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec, if (IS_ERR(clk)) return clk; + hw = __clk_get_hw(clk); + /* * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent * clock is created by the pinctrl driver. It is possible for clk user @@ -1124,13 +1101,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec, */ if (clkspec->args[0] == TEGRA20_CLK_CDEV1 || clkspec->args[0] == TEGRA20_CLK_CDEV2) { - hw = __clk_get_hw(clk); - parent_hw = clk_hw_get_parent(hw); if (!parent_hw) return ERR_PTR(-EPROBE_DEFER); } + if (clkspec->args[0] == TEGRA20_CLK_EMC) { + if (!tegra20_clk_emc_driver_available(hw)) + return ERR_PTR(-EPROBE_DEFER); + } + return clk; } diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index df172d5772d7..762cd186f714 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -9,13 +9,13 @@ #include <linux/clkdev.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/syscore_ops.h> #include <linux/delay.h> #include <linux/export.h> #include <linux/mutex.h> #include <linux/clk/tegra.h> #include <dt-bindings/clock/tegra210-car.h> #include <dt-bindings/reset/tegra210-car.h> -#include <linux/iopoll.h> #include <linux/sizes.h> #include <soc/tegra/pmc.h> @@ -33,6 +33,7 @@ #define CLK_SOURCE_CSITE 0x1d4 #define CLK_SOURCE_EMC 0x19c #define CLK_SOURCE_SOR1 0x410 +#define CLK_SOURCE_SOR0 0x414 #define CLK_SOURCE_LA 0x1f8 #define CLK_SOURCE_SDMMC2 0x154 #define CLK_SOURCE_SDMMC4 0x164 @@ -220,11 +221,15 @@ #define CLK_M_DIVISOR_SHIFT 2 #define CLK_M_DIVISOR_MASK 0x3 +#define CLK_MASK_ARM 0x44 +#define MISC_CLK_ENB 0x48 + #define RST_DFLL_DVCO 0x2f4 #define DVFS_DFLL_RESET_SHIFT 0 #define CLK_RST_CONTROLLER_RST_DEV_Y_SET 0x2a8 #define CLK_RST_CONTROLLER_RST_DEV_Y_CLR 0x2ac +#define CPU_SOFTRST_CTRL 0x380 #define LVL2_CLK_GATE_OVRA 0xf8 #define LVL2_CLK_GATE_OVRC 0x3a0 @@ -298,6 +303,7 @@ static DEFINE_SPINLOCK(pll_d_lock); static DEFINE_SPINLOCK(pll_e_lock); static DEFINE_SPINLOCK(pll_re_lock); static DEFINE_SPINLOCK(pll_u_lock); +static DEFINE_SPINLOCK(sor0_lock); static DEFINE_SPINLOCK(sor1_lock); static DEFINE_SPINLOCK(emc_lock); static DEFINE_MUTEX(lvl2_ovr_lock); @@ -2351,9 +2357,9 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_dpaux] = { .dt_id = TEGRA210_CLK_DPAUX, .present = true }, [tegra_clk_dpaux1] = { .dt_id = TEGRA210_CLK_DPAUX1, .present = true }, [tegra_clk_sor0] = { .dt_id = TEGRA210_CLK_SOR0, .present = true }, - [tegra_clk_sor0_lvds] = { .dt_id = TEGRA210_CLK_SOR0_LVDS, .present = true }, + [tegra_clk_sor0_out] = { .dt_id = TEGRA210_CLK_SOR0_OUT, .present = true }, [tegra_clk_sor1] = { .dt_id = TEGRA210_CLK_SOR1, .present = true }, - [tegra_clk_sor1_src] = { .dt_id = TEGRA210_CLK_SOR1_SRC, .present = true }, + [tegra_clk_sor1_out] = { .dt_id = TEGRA210_CLK_SOR1_OUT, .present = true }, [tegra_clk_gpu] = { .dt_id = TEGRA210_CLK_GPU, .present = true }, [tegra_clk_pll_g_ref] = { .dt_id = TEGRA210_CLK_PLL_G_REF, .present = true, }, [tegra_clk_uartb_8] = { .dt_id = TEGRA210_CLK_UARTB, .present = true }, @@ -2551,7 +2557,6 @@ static struct tegra_devclk devclks[] __initdata = { { .con_id = "pll_c4_out2", .dt_id = TEGRA210_CLK_PLL_C4_OUT2 }, { .con_id = "pll_c4_out3", .dt_id = TEGRA210_CLK_PLL_C4_OUT3 }, { .con_id = "dpaux", .dt_id = TEGRA210_CLK_DPAUX }, - { .con_id = "sor0", .dt_id = TEGRA210_CLK_SOR0 }, }; static struct tegra_audio_clk_info tegra210_audio_plls[] = { @@ -2825,6 +2830,7 @@ static int tegra210_enable_pllu(void) struct tegra_clk_pll_freq_table *fentry; struct tegra_clk_pll pllu; u32 reg; + int ret; for (fentry = pll_u_freq_table; fentry->input_rate; fentry++) { if (fentry->input_rate == pll_ref_freq) @@ -2841,7 +2847,7 @@ static int tegra210_enable_pllu(void) reg = readl_relaxed(clk_base + pllu.params->ext_misc_reg[0]); reg &= ~BIT(pllu.params->iddq_bit_idx); writel_relaxed(reg, clk_base + pllu.params->ext_misc_reg[0]); - udelay(5); + fence_udelay(5, clk_base); reg = readl_relaxed(clk_base + PLLU_BASE); reg &= ~GENMASK(20, 0); @@ -2849,13 +2855,18 @@ static int tegra210_enable_pllu(void) reg |= fentry->n << 8; reg |= fentry->p << 16; writel(reg, clk_base + PLLU_BASE); - udelay(1); + fence_udelay(1, clk_base); reg |= PLL_ENABLE; writel(reg, clk_base + PLLU_BASE); - readl_relaxed_poll_timeout_atomic(clk_base + PLLU_BASE, reg, - reg & PLL_BASE_LOCK, 2, 1000); - if (!(reg & PLL_BASE_LOCK)) { + /* + * During clocks resume, same PLLU init and enable sequence get + * executed. So, readx_poll_timeout_atomic can't be used here as it + * uses ktime_get() and timekeeping resume doesn't happen by that + * time. So, using tegra210_wait_for_mask for PLL LOCK. + */ + ret = tegra210_wait_for_mask(&pllu, PLLU_BASE, PLL_BASE_LOCK); + if (ret) { pr_err("Timed out waiting for PLL_U to lock\n"); return -ETIMEDOUT; } @@ -2895,12 +2906,12 @@ static int tegra210_init_pllu(void) reg = readl_relaxed(clk_base + XUSB_PLL_CFG0); reg &= ~XUSB_PLL_CFG0_PLLU_LOCK_DLY_MASK; writel_relaxed(reg, clk_base + XUSB_PLL_CFG0); - udelay(1); + fence_udelay(1, clk_base); reg = readl_relaxed(clk_base + PLLU_HW_PWRDN_CFG0); reg |= PLLU_HW_PWRDN_CFG0_SEQ_ENABLE; writel_relaxed(reg, clk_base + PLLU_HW_PWRDN_CFG0); - udelay(1); + fence_udelay(1, clk_base); reg = readl_relaxed(clk_base + PLLU_BASE); reg &= ~PLLU_BASE_CLKENABLE_USB; @@ -2915,6 +2926,39 @@ static int tegra210_init_pllu(void) return 0; } +/* + * The SOR hardware blocks are driven by two clocks: a module clock that is + * used to access registers and a pixel clock that is sourced from the same + * pixel clock that also drives the head attached to the SOR. The module + * clock is typically called sorX (with X being the SOR instance) and the + * pixel clock is called sorX_out. The source for the SOR pixel clock is + * referred to as the "parent" clock. + * + * On Tegra186 and newer, clocks are provided by the BPMP. Unfortunately the + * BPMP implementation for the SOR clocks doesn't exactly match the above in + * some aspects. For example, the SOR module is really clocked by the pad or + * sor_safe clocks, but BPMP models the sorX clock as being sourced by the + * pixel clocks. Conversely the sorX_out clock is sourced by the sor_safe or + * pad clocks on BPMP. + * + * In order to allow the display driver to deal with all SoC generations in + * a unified way, implement the BPMP semantics in this driver. + */ + +static const char * const sor0_parents[] = { + "pll_d_out0", +}; + +static const char * const sor0_out_parents[] = { + "sor_safe", "sor0_pad_clkout", +}; + +static const char * const sor1_parents[] = { + "pll_p", "pll_d_out0", "pll_d2_out0", "clk_m", +}; + +static u32 sor1_parents_idx[] = { 0, 2, 5, 6 }; + static const char * const sor1_out_parents[] = { /* * Bit 0 of the mux selects sor1_pad_clkout, irrespective of bit 1, so @@ -2923,20 +2967,31 @@ static const char * const sor1_out_parents[] = { * these bits to 0b11. While not an invalid setting, code should * always set the bits to 0b01 to select sor1_pad_clkout. */ - "sor_safe", "sor1_pad_clkout", "sor1", "sor1_pad_clkout", + "sor_safe", "sor1_pad_clkout", "sor1_out", "sor1_pad_clkout", }; -static const char * const sor1_parents[] = { - "pll_p", "pll_d_out0", "pll_d2_out0", "clk_m", -}; - -static u32 sor1_parents_idx[] = { 0, 2, 5, 6 }; - static struct tegra_periph_init_data tegra210_periph[] = { + /* + * On Tegra210, the sor0 clock doesn't have a mux it bitfield 31:29, + * but it is hardwired to the pll_d_out0 clock. + */ + TEGRA_INIT_DATA_TABLE("sor0", NULL, NULL, sor0_parents, + CLK_SOURCE_SOR0, 29, 0x0, 0, 0, 0, 0, + 0, 182, 0, tegra_clk_sor0, NULL, 0, + &sor0_lock), + TEGRA_INIT_DATA_TABLE("sor0_out", NULL, NULL, sor0_out_parents, + CLK_SOURCE_SOR0, 14, 0x1, 0, 0, 0, 0, + 0, 0, TEGRA_PERIPH_NO_GATE, tegra_clk_sor0_out, + NULL, 0, &sor0_lock), TEGRA_INIT_DATA_TABLE("sor1", NULL, NULL, sor1_parents, CLK_SOURCE_SOR1, 29, 0x7, 0, 0, 8, 1, - TEGRA_DIVIDER_ROUND_UP, 183, 0, tegra_clk_sor1, - sor1_parents_idx, 0, &sor1_lock), + TEGRA_DIVIDER_ROUND_UP, 183, 0, + tegra_clk_sor1, sor1_parents_idx, 0, + &sor1_lock), + TEGRA_INIT_DATA_TABLE("sor1_out", NULL, NULL, sor1_out_parents, + CLK_SOURCE_SOR1, 14, 0x3, 0, 0, 0, 0, + 0, 0, TEGRA_PERIPH_NO_GATE, + tegra_clk_sor1_out, NULL, 0, &sor1_lock), }; static const char * const la_parents[] = { @@ -2969,12 +3024,6 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 1, 17, 207); clks[TEGRA210_CLK_DPAUX1] = clk; - clk = clk_register_mux_table(NULL, "sor1_out", sor1_out_parents, - ARRAY_SIZE(sor1_out_parents), 0, - clk_base + CLK_SOURCE_SOR1, 14, 0x3, - 0, NULL, &sor1_lock); - clks[TEGRA210_CLK_SOR1_OUT] = clk; - /* pll_d_dsi_out */ clk = clk_register_gate(NULL, "pll_d_dsi_out", "pll_d_out0", 0, clk_base + PLLD_MISC0, 21, 0, &pll_d_lock); @@ -3287,6 +3336,77 @@ static void tegra210_disable_cpu_clock(u32 cpu) } #ifdef CONFIG_PM_SLEEP +#define car_readl(_base, _off) readl_relaxed(clk_base + (_base) + ((_off) * 4)) +#define car_writel(_val, _base, _off) \ + writel_relaxed(_val, clk_base + (_base) + ((_off) * 4)) + +static u32 spare_reg_ctx, misc_clk_enb_ctx, clk_msk_arm_ctx; +static u32 cpu_softrst_ctx[3]; + +static int tegra210_clk_suspend(void) +{ + unsigned int i; + + clk_save_context(); + + /* + * Save the bootloader configured clock registers SPARE_REG0, + * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL. + */ + spare_reg_ctx = readl_relaxed(clk_base + SPARE_REG0); + misc_clk_enb_ctx = readl_relaxed(clk_base + MISC_CLK_ENB); + clk_msk_arm_ctx = readl_relaxed(clk_base + CLK_MASK_ARM); + + for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++) + cpu_softrst_ctx[i] = car_readl(CPU_SOFTRST_CTRL, i); + + tegra_clk_periph_suspend(); + return 0; +} + +static void tegra210_clk_resume(void) +{ + unsigned int i; + + tegra_clk_osc_resume(clk_base); + + /* + * Restore the bootloader configured clock registers SPARE_REG0, + * MISC_CLK_ENB, CLK_MASK_ARM, CPU_SOFTRST_CTRL from saved context. + */ + writel_relaxed(spare_reg_ctx, clk_base + SPARE_REG0); + writel_relaxed(misc_clk_enb_ctx, clk_base + MISC_CLK_ENB); + writel_relaxed(clk_msk_arm_ctx, clk_base + CLK_MASK_ARM); + + for (i = 0; i < ARRAY_SIZE(cpu_softrst_ctx); i++) + car_writel(cpu_softrst_ctx[i], CPU_SOFTRST_CTRL, i); + + /* + * Tegra clock programming sequence recommends peripheral clock to + * be enabled prior to changing its clock source and divider to + * prevent glitchless frequency switch. + * So, enable all peripheral clocks before restoring their source + * and dividers. + */ + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_L, clk_base + CLK_OUT_ENB_L); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_H, clk_base + CLK_OUT_ENB_H); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_U, clk_base + CLK_OUT_ENB_U); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_V, clk_base + CLK_OUT_ENB_V); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_W, clk_base + CLK_OUT_ENB_W); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_X, clk_base + CLK_OUT_ENB_X); + writel_relaxed(TEGRA210_CLK_ENB_VLD_MSK_Y, clk_base + CLK_OUT_ENB_Y); + + /* wait for all writes to happen to have all the clocks enabled */ + fence_udelay(2, clk_base); + + /* restore PLLs and all peripheral clock rates */ + tegra210_init_pllu(); + clk_restore_context(); + + /* restore saved context of peripheral clocks and reset state */ + tegra_clk_periph_resume(); +} + static void tegra210_cpu_clock_suspend(void) { /* switch coresite to clk_m, save off original source */ @@ -3302,6 +3422,13 @@ static void tegra210_cpu_clock_resume(void) } #endif +static struct syscore_ops tegra_clk_syscore_ops = { +#ifdef CONFIG_PM_SLEEP + .suspend = tegra210_clk_suspend, + .resume = tegra210_clk_resume, +#endif +}; + static struct tegra_cpu_car_ops tegra210_cpu_car_ops = { .wait_for_reset = tegra210_wait_cpu_in_reset, .disable_clock = tegra210_disable_cpu_clock, @@ -3586,5 +3713,7 @@ static void __init tegra210_clock_init(struct device_node *np) tegra210_mbist_clk_init(); tegra_cpu_car_ops = &tegra210_cpu_car_ops; + + register_syscore_ops(&tegra_clk_syscore_ops); } CLK_OF_DECLARE(tegra210, "nvidia,tegra210-car", tegra210_clock_init); diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 7b4c6a488527..c8bc18e4d7e5 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -151,7 +151,6 @@ static unsigned long input_freq; static DEFINE_SPINLOCK(cml_lock); static DEFINE_SPINLOCK(pll_d_lock); -static DEFINE_SPINLOCK(emc_lock); #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ _clk_num, _gate_flags, _clk_id) \ @@ -808,7 +807,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = { [tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true }, [tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true }, [tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = true }, + [tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = false }, }; static const char *pll_e_parents[] = { "pll_ref", "pll_p" }; @@ -995,7 +994,6 @@ static void __init tegra30_super_clk_init(void) static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p", "clk_m" }; static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" }; -static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" }; static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p", "clk_m" }; static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" }; @@ -1044,14 +1042,12 @@ static void __init tegra30_periph_clk_init(void) clks[TEGRA30_CLK_AFI] = clk; /* emc */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), - CLK_SET_RATE_NO_REPARENT, - clk_base + CLK_SOURCE_EMC, - 30, 2, 0, &emc_lock); + clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, true); + + clks[TEGRA30_CLK_EMC] = clk; - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, - &emc_lock); + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, + NULL); clks[TEGRA30_CLK_MC] = clk; /* cml0 */ @@ -1167,6 +1163,7 @@ static void tegra30_cpu_clock_suspend(void) static void tegra30_cpu_clock_resume(void) { unsigned int reg, policy; + u32 misc, base; /* Is CPU complex already running on PLLX? */ reg = readl(clk_base + CLK_RESET_CCLK_BURST); @@ -1180,15 +1177,21 @@ static void tegra30_cpu_clock_resume(void) BUG(); if (reg != CLK_RESET_CCLK_BURST_POLICY_PLLX) { - /* restore PLLX settings if CPU is on different PLL */ - writel(tegra30_cpu_clk_sctx.pllx_misc, - clk_base + CLK_RESET_PLLX_MISC); - writel(tegra30_cpu_clk_sctx.pllx_base, - clk_base + CLK_RESET_PLLX_BASE); - - /* wait for PLL stabilization if PLLX was enabled */ - if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30)) - udelay(300); + misc = readl_relaxed(clk_base + CLK_RESET_PLLX_MISC); + base = readl_relaxed(clk_base + CLK_RESET_PLLX_BASE); + + if (misc != tegra30_cpu_clk_sctx.pllx_misc || + base != tegra30_cpu_clk_sctx.pllx_base) { + /* restore PLLX settings if CPU is on different PLL */ + writel(tegra30_cpu_clk_sctx.pllx_misc, + clk_base + CLK_RESET_PLLX_MISC); + writel(tegra30_cpu_clk_sctx.pllx_base, + clk_base + CLK_RESET_PLLX_BASE); + + /* wait for PLL stabilization if PLLX was enabled */ + if (tegra30_cpu_clk_sctx.pllx_base & (1 << 30)) + udelay(300); + } } /* @@ -1302,6 +1305,26 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = { { "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" }, }; +static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec, + void *data) +{ + struct clk_hw *hw; + struct clk *clk; + + clk = of_clk_src_onecell_get(clkspec, data); + if (IS_ERR(clk)) + return clk; + + hw = __clk_get_hw(clk); + + if (clkspec->args[0] == TEGRA30_CLK_EMC) { + if (!tegra20_clk_emc_driver_available(hw)) + return ERR_PTR(-EPROBE_DEFER); + } + + return clk; +} + static void __init tegra30_clock_init(struct device_node *np) { struct device_node *node; @@ -1345,7 +1368,7 @@ static void __init tegra30_clock_init(struct device_node *np) tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX); - tegra_add_of_provider(np, of_clk_src_onecell_get); + tegra_add_of_provider(np, tegra30_clk_src_onecell_get); tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_clk_apply_init_table = tegra30_clock_apply_init_table; diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 573e3c967ae1..e6bd6d1ea012 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -16,56 +16,13 @@ #include "clk.h" -#define CLK_OUT_ENB_L 0x010 -#define CLK_OUT_ENB_H 0x014 -#define CLK_OUT_ENB_U 0x018 -#define CLK_OUT_ENB_V 0x360 -#define CLK_OUT_ENB_W 0x364 -#define CLK_OUT_ENB_X 0x280 -#define CLK_OUT_ENB_Y 0x298 -#define CLK_OUT_ENB_SET_L 0x320 -#define CLK_OUT_ENB_CLR_L 0x324 -#define CLK_OUT_ENB_SET_H 0x328 -#define CLK_OUT_ENB_CLR_H 0x32c -#define CLK_OUT_ENB_SET_U 0x330 -#define CLK_OUT_ENB_CLR_U 0x334 -#define CLK_OUT_ENB_SET_V 0x440 -#define CLK_OUT_ENB_CLR_V 0x444 -#define CLK_OUT_ENB_SET_W 0x448 -#define CLK_OUT_ENB_CLR_W 0x44c -#define CLK_OUT_ENB_SET_X 0x284 -#define CLK_OUT_ENB_CLR_X 0x288 -#define CLK_OUT_ENB_SET_Y 0x29c -#define CLK_OUT_ENB_CLR_Y 0x2a0 - -#define RST_DEVICES_L 0x004 -#define RST_DEVICES_H 0x008 -#define RST_DEVICES_U 0x00C -#define RST_DEVICES_V 0x358 -#define RST_DEVICES_W 0x35C -#define RST_DEVICES_X 0x28C -#define RST_DEVICES_Y 0x2a4 -#define RST_DEVICES_SET_L 0x300 -#define RST_DEVICES_CLR_L 0x304 -#define RST_DEVICES_SET_H 0x308 -#define RST_DEVICES_CLR_H 0x30c -#define RST_DEVICES_SET_U 0x310 -#define RST_DEVICES_CLR_U 0x314 -#define RST_DEVICES_SET_V 0x430 -#define RST_DEVICES_CLR_V 0x434 -#define RST_DEVICES_SET_W 0x438 -#define RST_DEVICES_CLR_W 0x43c -#define RST_DEVICES_SET_X 0x290 -#define RST_DEVICES_CLR_X 0x294 -#define RST_DEVICES_SET_Y 0x2a8 -#define RST_DEVICES_CLR_Y 0x2ac - /* Global data of Tegra CPU CAR ops */ static struct tegra_cpu_car_ops dummy_car_ops; struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; int *periph_clk_enb_refcnt; static int periph_banks; +static u32 *periph_state_ctx; static struct clk **clks; static int clk_num; static struct clk_onecell_data clk_data; @@ -199,6 +156,65 @@ const struct tegra_clk_periph_regs *get_reg_bank(int clkid) } } +void tegra_clk_set_pllp_out_cpu(bool enable) +{ + u32 val; + + val = readl_relaxed(clk_base + CLK_OUT_ENB_Y); + if (enable) + val |= CLK_ENB_PLLP_OUT_CPU; + else + val &= ~CLK_ENB_PLLP_OUT_CPU; + + writel_relaxed(val, clk_base + CLK_OUT_ENB_Y); +} + +void tegra_clk_periph_suspend(void) +{ + unsigned int i, idx; + + idx = 0; + for (i = 0; i < periph_banks; i++, idx++) + periph_state_ctx[idx] = + readl_relaxed(clk_base + periph_regs[i].enb_reg); + + for (i = 0; i < periph_banks; i++, idx++) + periph_state_ctx[idx] = + readl_relaxed(clk_base + periph_regs[i].rst_reg); +} + +void tegra_clk_periph_resume(void) +{ + unsigned int i, idx; + + idx = 0; + for (i = 0; i < periph_banks; i++, idx++) + writel_relaxed(periph_state_ctx[idx], + clk_base + periph_regs[i].enb_reg); + /* + * All non-boot peripherals will be in reset state on resume. + * Wait for 5us of reset propagation delay before de-asserting + * the peripherals based on the saved context. + */ + fence_udelay(5, clk_base); + + for (i = 0; i < periph_banks; i++, idx++) + writel_relaxed(periph_state_ctx[idx], + clk_base + periph_regs[i].rst_reg); + + fence_udelay(2, clk_base); +} + +static int tegra_clk_periph_ctx_init(int banks) +{ + periph_state_ctx = kcalloc(2 * banks, sizeof(*periph_state_ctx), + GFP_KERNEL); + if (!periph_state_ctx) + return -ENOMEM; + + return 0; +} + struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks) { clk_base = regs; @@ -220,6 +236,14 @@ struct clk ** __init tegra_clk_init(void __iomem *regs, int num, int banks) clk_num = num; + if (IS_ENABLED(CONFIG_PM_SLEEP)) { + if (tegra_clk_periph_ctx_init(banks)) { + kfree(periph_clk_enb_refcnt); + kfree(clks); + return NULL; + } + } + return clks; } diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 905bf1096558..416a6b09f6a3 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -10,6 +10,65 @@ #include <linux/clkdev.h> #include <linux/delay.h> +#define CLK_OUT_ENB_L 0x010 +#define CLK_OUT_ENB_H 0x014 +#define CLK_OUT_ENB_U 0x018 +#define CLK_OUT_ENB_V 0x360 +#define CLK_OUT_ENB_W 0x364 +#define CLK_OUT_ENB_X 0x280 +#define CLK_OUT_ENB_Y 0x298 +#define CLK_ENB_PLLP_OUT_CPU BIT(31) +#define CLK_OUT_ENB_SET_L 0x320 +#define CLK_OUT_ENB_CLR_L 0x324 +#define CLK_OUT_ENB_SET_H 0x328 +#define CLK_OUT_ENB_CLR_H 0x32c +#define CLK_OUT_ENB_SET_U 0x330 +#define CLK_OUT_ENB_CLR_U 0x334 +#define CLK_OUT_ENB_SET_V 0x440 +#define CLK_OUT_ENB_CLR_V 0x444 +#define CLK_OUT_ENB_SET_W 0x448 +#define CLK_OUT_ENB_CLR_W 0x44c +#define CLK_OUT_ENB_SET_X 0x284 +#define CLK_OUT_ENB_CLR_X 0x288 +#define CLK_OUT_ENB_SET_Y 0x29c +#define CLK_OUT_ENB_CLR_Y 0x2a0 + +#define RST_DEVICES_L 0x004 +#define RST_DEVICES_H 0x008 +#define RST_DEVICES_U 0x00C +#define RST_DEVICES_V 0x358 +#define RST_DEVICES_W 0x35C +#define RST_DEVICES_X 0x28C +#define RST_DEVICES_Y 0x2a4 +#define RST_DEVICES_SET_L 0x300 +#define RST_DEVICES_CLR_L 0x304 +#define RST_DEVICES_SET_H 0x308 +#define RST_DEVICES_CLR_H 0x30c +#define RST_DEVICES_SET_U 0x310 +#define RST_DEVICES_CLR_U 0x314 +#define RST_DEVICES_SET_V 0x430 +#define RST_DEVICES_CLR_V 0x434 +#define RST_DEVICES_SET_W 0x438 +#define RST_DEVICES_CLR_W 0x43c +#define RST_DEVICES_SET_X 0x290 +#define RST_DEVICES_CLR_X 0x294 +#define RST_DEVICES_SET_Y 0x2a8 +#define RST_DEVICES_CLR_Y 0x2ac + +/* + * Tegra CLK_OUT_ENB registers have some undefined bits which are not used and + * any accidental write of 1 to these bits can cause PSLVERR. + * So below are the valid mask defines for each CLK_OUT_ENB register used to + * turn ON only the valid clocks. + */ +#define TEGRA210_CLK_ENB_VLD_MSK_L 0xdcd7dff9 +#define TEGRA210_CLK_ENB_VLD_MSK_H 0x87d1f3e7 +#define TEGRA210_CLK_ENB_VLD_MSK_U 0xf3fed3fa +#define TEGRA210_CLK_ENB_VLD_MSK_V 0xffc18cfb +#define TEGRA210_CLK_ENB_VLD_MSK_W 0x793fb7ff +#define TEGRA210_CLK_ENB_VLD_MSK_X 0x3fe66fff +#define TEGRA210_CLK_ENB_VLD_MSK_Y 0xfc1fc7ff + /** * struct tegra_clk_sync_source - external clock source from codec * @@ -669,6 +728,9 @@ struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, * Flags: * TEGRA_DIVIDER_2 - LP cluster has additional divider. This flag indicates * that this is LP cluster clock. + * TEGRA210_CPU_CLK - This flag is used to identify CPU cluster for gen5 + * super mux parent using PLLP branches. To use PLLP branches to CPU, need + * to configure additional bit PLLP_OUT_CPU in the clock registers. */ struct tegra_clk_super_mux { struct clk_hw hw; @@ -685,6 +747,7 @@ struct tegra_clk_super_mux { #define to_clk_super_mux(_hw) container_of(_hw, struct tegra_clk_super_mux, hw) #define TEGRA_DIVIDER_2 BIT(0) +#define TEGRA210_CPU_CLK BIT(1) extern const struct clk_ops tegra_clk_super_ops; struct clk *tegra_clk_register_super_mux(const char *name, @@ -829,6 +892,10 @@ u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate); int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div); int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, u8 frac_width, u8 flags); +void tegra_clk_osc_resume(void __iomem *clk_base); +void tegra_clk_set_pllp_out_cpu(bool enable); +void tegra_clk_periph_suspend(void); +void tegra_clk_periph_resume(void); /* Combined read fence with delay */ @@ -838,4 +905,7 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, udelay(delay); \ } while (0) +bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw); +struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter); + #endif /* TEGRA_CLK_H */ diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index fdfb90058504..bb2f2836dab2 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -194,15 +194,8 @@ static const char *ti_adpll_clk_get_name(struct ti_adpll_data *d, if (err) return NULL; } else { - const char *base_name = "adpll"; - char *buf; - - buf = devm_kzalloc(d->dev, 8 + 1 + strlen(base_name) + 1 + - strlen(postfix), GFP_KERNEL); - if (!buf) - return NULL; - sprintf(buf, "%08lx.%s.%s", d->pa, base_name, postfix); - name = buf; + name = devm_kasprintf(d->dev, GFP_KERNEL, "%08lx.adpll.%s", + d->pa, postfix); } return name; diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c index a360d3109555..e001b9bcb6bf 100644 --- a/drivers/clk/ti/clk-33xx.c +++ b/drivers/clk/ti/clk-33xx.c @@ -107,7 +107,7 @@ static const struct omap_clkctrl_reg_data am3_l4hs_clkctrl_regs[] __initconst = }; static const struct omap_clkctrl_reg_data am3_pruss_ocp_clkctrl_regs[] __initconst = { - { AM3_PRUSS_OCP_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP, "pruss_ocp_gclk" }, + { AM3_PRUSS_OCP_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "pruss_ocp_gclk" }, { 0 }, }; @@ -217,7 +217,7 @@ static const struct omap_clkctrl_reg_data am3_l4_rtc_clkctrl_regs[] __initconst }; static const struct omap_clkctrl_reg_data am3_gfx_l3_clkctrl_regs[] __initconst = { - { AM3_GFX_L3_GFX_CLKCTRL, NULL, CLKF_SW_SUP, "gfx_fck_div_ck" }, + { AM3_GFX_L3_GFX_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "gfx_fck_div_ck" }, { 0 }, }; diff --git a/drivers/clk/ti/clk-43xx.c b/drivers/clk/ti/clk-43xx.c index 2782d91838ac..af3e7805769e 100644 --- a/drivers/clk/ti/clk-43xx.c +++ b/drivers/clk/ti/clk-43xx.c @@ -73,7 +73,7 @@ static const struct omap_clkctrl_reg_data am4_mpu_clkctrl_regs[] __initconst = { }; static const struct omap_clkctrl_reg_data am4_gfx_l3_clkctrl_regs[] __initconst = { - { AM4_GFX_L3_GFX_CLKCTRL, NULL, CLKF_SW_SUP, "gfx_fck_div_ck" }, + { AM4_GFX_L3_GFX_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "gfx_fck_div_ck" }, { 0 }, }; @@ -126,7 +126,7 @@ static const struct omap_clkctrl_reg_data am4_l3s_clkctrl_regs[] __initconst = { }; static const struct omap_clkctrl_reg_data am4_pruss_ocp_clkctrl_regs[] __initconst = { - { AM4_PRUSS_OCP_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP, "pruss_ocp_gclk" }, + { AM4_PRUSS_OCP_PRUSS_CLKCTRL, NULL, CLKF_SW_SUP | CLKF_NO_IDLEST, "pruss_ocp_gclk" }, { 0 }, }; diff --git a/drivers/clk/ti/clk-44xx.c b/drivers/clk/ti/clk-44xx.c index b10ed0429091..2b4dab632318 100644 --- a/drivers/clk/ti/clk-44xx.c +++ b/drivers/clk/ti/clk-44xx.c @@ -37,7 +37,7 @@ static const struct omap_clkctrl_reg_data omap4_mpuss_clkctrl_regs[] __initconst }; static const struct omap_clkctrl_reg_data omap4_tesla_clkctrl_regs[] __initconst = { - { OMAP4_DSP_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_iva_m4x2_ck" }, + { OMAP4_DSP_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_iva_m4x2_ck" }, { 0 }, }; @@ -219,7 +219,7 @@ static const struct omap_clkctrl_reg_data omap4_l3_2_clkctrl_regs[] __initconst }; static const struct omap_clkctrl_reg_data omap4_ducati_clkctrl_regs[] __initconst = { - { OMAP4_IPU_CLKCTRL, NULL, CLKF_HW_SUP, "ducati_clk_mux_ck" }, + { OMAP4_IPU_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "ducati_clk_mux_ck" }, { 0 }, }; diff --git a/drivers/clk/ti/clk-54xx.c b/drivers/clk/ti/clk-54xx.c index e675e27f1203..c9608e5d993a 100644 --- a/drivers/clk/ti/clk-54xx.c +++ b/drivers/clk/ti/clk-54xx.c @@ -31,7 +31,7 @@ static const struct omap_clkctrl_reg_data omap5_mpu_clkctrl_regs[] __initconst = }; static const struct omap_clkctrl_reg_data omap5_dsp_clkctrl_regs[] __initconst = { - { OMAP5_MMU_DSP_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_iva_h11x2_ck" }, + { OMAP5_MMU_DSP_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_iva_h11x2_ck" }, { 0 }, }; @@ -145,7 +145,7 @@ static const struct omap_clkctrl_reg_data omap5_l3main2_clkctrl_regs[] __initcon }; static const struct omap_clkctrl_reg_data omap5_ipu_clkctrl_regs[] __initconst = { - { OMAP5_MMU_IPU_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_core_h22x2_ck" }, + { OMAP5_MMU_IPU_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_core_h22x2_ck" }, { 0 }, }; @@ -286,6 +286,12 @@ static const struct omap_clkctrl_reg_data omap5_l4per_clkctrl_regs[] __initconst { 0 }, }; +static const struct omap_clkctrl_reg_data omap5_iva_clkctrl_regs[] __initconst = { + { OMAP5_IVA_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_iva_h12x2_ck" }, + { OMAP5_SL2IF_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_iva_h12x2_ck" }, + { 0 }, +}; + static const char * const omap5_dss_dss_clk_parents[] __initconst = { "dpll_per_h12x2_ck", NULL, @@ -502,6 +508,7 @@ const struct omap_clkctrl_data omap5_clkctrl_data[] __initconst = { { 0x4a008d20, omap5_l4cfg_clkctrl_regs }, { 0x4a008e20, omap5_l3instr_clkctrl_regs }, { 0x4a009020, omap5_l4per_clkctrl_regs }, + { 0x4a009220, omap5_iva_clkctrl_regs }, { 0x4a009420, omap5_dss_clkctrl_regs }, { 0x4a009520, omap5_gpu_clkctrl_regs }, { 0x4a009620, omap5_l3init_clkctrl_regs }, diff --git a/drivers/clk/ti/clk-7xx.c b/drivers/clk/ti/clk-7xx.c index 9dd6185a4b4e..5f46782cebeb 100644 --- a/drivers/clk/ti/clk-7xx.c +++ b/drivers/clk/ti/clk-7xx.c @@ -25,7 +25,7 @@ static const struct omap_clkctrl_reg_data dra7_mpu_clkctrl_regs[] __initconst = }; static const struct omap_clkctrl_reg_data dra7_dsp1_clkctrl_regs[] __initconst = { - { DRA7_DSP1_MMU0_DSP1_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_dsp_m2_ck" }, + { DRA7_DSP1_MMU0_DSP1_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_dsp_m2_ck" }, { 0 }, }; @@ -41,7 +41,7 @@ static const struct omap_clkctrl_bit_data dra7_mmu_ipu1_bit_data[] __initconst = }; static const struct omap_clkctrl_reg_data dra7_ipu1_clkctrl_regs[] __initconst = { - { DRA7_IPU1_MMU_IPU1_CLKCTRL, dra7_mmu_ipu1_bit_data, CLKF_HW_SUP, "ipu1-clkctrl:0000:24" }, + { DRA7_IPU1_MMU_IPU1_CLKCTRL, dra7_mmu_ipu1_bit_data, CLKF_HW_SUP | CLKF_NO_IDLEST, "ipu1-clkctrl:0000:24" }, { 0 }, }; @@ -137,7 +137,7 @@ static const struct omap_clkctrl_reg_data dra7_ipu_clkctrl_regs[] __initconst = }; static const struct omap_clkctrl_reg_data dra7_dsp2_clkctrl_regs[] __initconst = { - { DRA7_DSP2_MMU0_DSP2_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_dsp_m2_ck" }, + { DRA7_DSP2_MMU0_DSP2_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_dsp_m2_ck" }, { 0 }, }; @@ -164,7 +164,7 @@ static const struct omap_clkctrl_reg_data dra7_l3main1_clkctrl_regs[] __initcons }; static const struct omap_clkctrl_reg_data dra7_ipu2_clkctrl_regs[] __initconst = { - { DRA7_IPU2_MMU_IPU2_CLKCTRL, NULL, CLKF_HW_SUP, "dpll_core_h22x2_ck" }, + { DRA7_IPU2_MMU_IPU2_CLKCTRL, NULL, CLKF_HW_SUP | CLKF_NO_IDLEST, "dpll_core_h22x2_ck" }, { 0 }, }; diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index b0c0690a5a12..17b9a761242f 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -24,7 +24,7 @@ #include <linux/timekeeping.h> #include "clock.h" -#define NO_IDLEST 0x1 +#define NO_IDLEST 0 #define OMAP4_MODULEMODE_MASK 0x3 @@ -34,6 +34,9 @@ #define OMAP4_IDLEST_MASK (0x3 << 16) #define OMAP4_IDLEST_SHIFT 16 +#define OMAP4_STBYST_MASK BIT(18) +#define OMAP4_STBYST_SHIFT 18 + #define CLKCTRL_IDLEST_FUNCTIONAL 0x0 #define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 #define CLKCTRL_IDLEST_DISABLED 0x3 @@ -159,7 +162,7 @@ static int _omap4_clkctrl_clk_enable(struct clk_hw *hw) ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); - if (clk->flags & NO_IDLEST) + if (test_bit(NO_IDLEST, &clk->flags)) return 0; /* Wait until module is enabled */ @@ -188,7 +191,7 @@ static void _omap4_clkctrl_clk_disable(struct clk_hw *hw) ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); - if (clk->flags & NO_IDLEST) + if (test_bit(NO_IDLEST, &clk->flags)) goto exit; /* Wait until module is disabled */ @@ -381,7 +384,7 @@ _ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, div_data->max_div, div_flags, - &div->width, &div->table)) { + div)) { pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, node, offset, data->bit); kfree(div); @@ -597,7 +600,7 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) if (reg_data->flags & CLKF_HW_SUP) hw->enable_bit = MODULEMODE_HWCTRL; if (reg_data->flags & CLKF_NO_IDLEST) - hw->flags |= NO_IDLEST; + set_bit(NO_IDLEST, &hw->flags); if (reg_data->clkdm_name) hw->clkdm_name = reg_data->clkdm_name; @@ -623,7 +626,7 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) init.ops = &omap4_clkctrl_clk_ops; hw->hw.init = &init; - clk = ti_clk_register(NULL, &hw->hw, init.name); + clk = ti_clk_register_omap_hw(NULL, &hw->hw, init.name); if (IS_ERR_OR_NULL(clk)) goto cleanup; @@ -648,3 +651,33 @@ cleanup: } CLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", _ti_omap4_clkctrl_setup); + +/** + * ti_clk_is_in_standby - Check if clkctrl clock is in standby or not + * @clk: clock to check standby status for + * + * Finds whether the provided clock is in standby mode or not. Returns + * true if the provided clock is a clkctrl type clock and it is in standby, + * false otherwise. + */ +bool ti_clk_is_in_standby(struct clk *clk) +{ + struct clk_hw *hw; + struct clk_hw_omap *hwclk; + u32 val; + + hw = __clk_get_hw(clk); + + if (!omap2_clk_is_hw_omap(hw)) + return false; + + hwclk = to_clk_hw_omap(hw); + + val = ti_clk_ll_ops->clk_readl(&hwclk->enable_reg); + + if (val & OMAP4_STBYST_MASK) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(ti_clk_is_in_standby); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index e4b8392ff63c..e6995c04001e 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -20,9 +20,11 @@ struct clk_omap_divider { struct clk_hw hw; struct clk_omap_reg reg; u8 shift; - u8 width; u8 flags; s8 latch; + u16 min; + u16 max; + u16 mask; const struct clk_div_table *table; u32 context; }; @@ -220,8 +222,7 @@ void ti_clk_latch(struct clk_omap_reg *reg, s8 shift); struct clk_hw *ti_clk_build_component_mux(struct ti_clk_mux *setup); int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, - u8 flags, u8 *width, - const struct clk_div_table **table); + u8 flags, struct clk_omap_divider *div); int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 6cb863c13648..28080df92f72 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -26,30 +26,6 @@ #undef pr_fmt #define pr_fmt(fmt) "%s: " fmt, __func__ -#define div_mask(d) ((1 << ((d)->width)) - 1) - -static unsigned int _get_table_maxdiv(const struct clk_div_table *table) -{ - unsigned int maxdiv = 0; - const struct clk_div_table *clkt; - - for (clkt = table; clkt->div; clkt++) - if (clkt->div > maxdiv) - maxdiv = clkt->div; - return maxdiv; -} - -static unsigned int _get_maxdiv(struct clk_omap_divider *divider) -{ - if (divider->flags & CLK_DIVIDER_ONE_BASED) - return div_mask(divider); - if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) - return 1 << div_mask(divider); - if (divider->table) - return _get_table_maxdiv(divider->table); - return div_mask(divider) + 1; -} - static unsigned int _get_table_div(const struct clk_div_table *table, unsigned int val) { @@ -61,6 +37,34 @@ static unsigned int _get_table_div(const struct clk_div_table *table, return 0; } +static void _setup_mask(struct clk_omap_divider *divider) +{ + u16 mask; + u32 max_val; + const struct clk_div_table *clkt; + + if (divider->table) { + max_val = 0; + + for (clkt = divider->table; clkt->div; clkt++) + if (clkt->val > max_val) + max_val = clkt->val; + } else { + max_val = divider->max; + + if (!(divider->flags & CLK_DIVIDER_ONE_BASED) && + !(divider->flags & CLK_DIVIDER_POWER_OF_TWO)) + max_val--; + } + + if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) + mask = fls(max_val) - 1; + else + mask = max_val; + + divider->mask = (1 << fls(mask)) - 1; +} + static unsigned int _get_div(struct clk_omap_divider *divider, unsigned int val) { if (divider->flags & CLK_DIVIDER_ONE_BASED) @@ -101,7 +105,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw, unsigned int div, val; val = ti_clk_ll_ops->clk_readl(÷r->reg) >> divider->shift; - val &= div_mask(divider); + val &= divider->mask; div = _get_div(divider, val); if (!div) { @@ -180,7 +184,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, if (!rate) rate = 1; - maxdiv = _get_maxdiv(divider); + maxdiv = divider->max; if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { parent_rate = *best_parent_rate; @@ -219,7 +223,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, } if (!bestdiv) { - bestdiv = _get_maxdiv(divider); + bestdiv = divider->max; *best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1); } @@ -249,17 +253,16 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, divider = to_clk_omap_divider(hw); div = DIV_ROUND_UP(parent_rate, rate); - value = _get_val(divider, div); - if (value > div_mask(divider)) - value = div_mask(divider); + if (div > divider->max) + div = divider->max; + if (div < divider->min) + div = divider->min; - if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { - val = div_mask(divider) << (divider->shift + 16); - } else { - val = ti_clk_ll_ops->clk_readl(÷r->reg); - val &= ~(div_mask(divider) << divider->shift); - } + value = _get_val(divider, div); + + val = ti_clk_ll_ops->clk_readl(÷r->reg); + val &= ~(divider->mask << divider->shift); val |= value << divider->shift; ti_clk_ll_ops->clk_writel(val, ÷r->reg); @@ -280,7 +283,7 @@ static int clk_divider_save_context(struct clk_hw *hw) u32 val; val = ti_clk_ll_ops->clk_readl(÷r->reg) >> divider->shift; - divider->context = val & div_mask(divider); + divider->context = val & divider->mask; return 0; } @@ -297,7 +300,7 @@ static void clk_divider_restore_context(struct clk_hw *hw) u32 val; val = ti_clk_ll_ops->clk_readl(÷r->reg); - val &= ~(div_mask(divider) << divider->shift); + val &= ~(divider->mask << divider->shift); val |= divider->context << divider->shift; ti_clk_ll_ops->clk_writel(val, ÷r->reg); } @@ -310,47 +313,26 @@ const struct clk_ops ti_clk_divider_ops = { .restore_context = clk_divider_restore_context, }; -static struct clk *_register_divider(struct device *dev, const char *name, - const char *parent_name, - unsigned long flags, - struct clk_omap_reg *reg, - u8 shift, u8 width, s8 latch, - u8 clk_divider_flags, - const struct clk_div_table *table) +static struct clk *_register_divider(struct device_node *node, + u32 flags, + struct clk_omap_divider *div) { - struct clk_omap_divider *div; struct clk *clk; struct clk_init_data init; + const char *parent_name; - if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { - if (width + shift > 16) { - pr_warn("divider value exceeds LOWORD field\n"); - return ERR_PTR(-EINVAL); - } - } - - /* allocate the divider */ - div = kzalloc(sizeof(*div), GFP_KERNEL); - if (!div) - return ERR_PTR(-ENOMEM); + parent_name = of_clk_get_parent_name(node, 0); - init.name = name; + init.name = node->name; init.ops = &ti_clk_divider_ops; init.flags = flags; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); - /* struct clk_divider assignments */ - memcpy(&div->reg, reg, sizeof(*reg)); - div->shift = shift; - div->width = width; - div->latch = latch; - div->flags = clk_divider_flags; div->hw.init = &init; - div->table = table; /* register the clock */ - clk = ti_clk_register(dev, &div->hw, name); + clk = ti_clk_register(NULL, &div->hw, node->name); if (IS_ERR(clk)) kfree(div); @@ -359,34 +341,17 @@ static struct clk *_register_divider(struct device *dev, const char *name, } int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, - u8 flags, u8 *width, - const struct clk_div_table **table) + u8 flags, struct clk_omap_divider *divider) { int valid_div = 0; - u32 val; - int div; int i; struct clk_div_table *tmp; + u16 min_div = 0; if (!div_table) { - if (flags & CLKF_INDEX_STARTS_AT_ONE) - val = 1; - else - val = 0; - - div = 1; - - while (div < max_div) { - if (flags & CLKF_INDEX_POWER_OF_TWO) - div <<= 1; - else - div++; - val++; - } - - *width = fls(val); - *table = NULL; - + divider->min = 1; + divider->max = max_div; + _setup_mask(divider); return 0; } @@ -403,30 +368,32 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, num_dividers = i; tmp = kcalloc(valid_div + 1, sizeof(*tmp), GFP_KERNEL); - if (!tmp) { - *table = ERR_PTR(-ENOMEM); + if (!tmp) return -ENOMEM; - } valid_div = 0; - *width = 0; for (i = 0; i < num_dividers; i++) if (div_table[i] > 0) { tmp[valid_div].div = div_table[i]; tmp[valid_div].val = i; valid_div++; - *width = i; + if (div_table[i] > max_div) + max_div = div_table[i]; + if (!min_div || div_table[i] < min_div) + min_div = div_table[i]; } - *width = fls(*width); - *table = tmp; + divider->min = min_div; + divider->max = max_div; + divider->table = tmp; + _setup_mask(divider); return 0; } -static struct clk_div_table * -__init ti_clk_get_div_table(struct device_node *node) +static int __init ti_clk_get_div_table(struct device_node *node, + struct clk_omap_divider *div) { struct clk_div_table *table; const __be32 *divspec; @@ -438,7 +405,7 @@ __init ti_clk_get_div_table(struct device_node *node) divspec = of_get_property(node, "ti,dividers", &num_div); if (!divspec) - return NULL; + return 0; num_div /= 4; @@ -453,13 +420,12 @@ __init ti_clk_get_div_table(struct device_node *node) if (!valid_div) { pr_err("no valid dividers for %pOFn table\n", node); - return ERR_PTR(-EINVAL); + return -EINVAL; } table = kcalloc(valid_div + 1, sizeof(*table), GFP_KERNEL); - if (!table) - return ERR_PTR(-ENOMEM); + return -ENOMEM; valid_div = 0; @@ -472,19 +438,20 @@ __init ti_clk_get_div_table(struct device_node *node) } } - return table; + div->table = table; + + return 0; } -static int _get_divider_width(struct device_node *node, - const struct clk_div_table *table, - u8 flags) +static int _populate_divider_min_max(struct device_node *node, + struct clk_omap_divider *divider) { - u32 min_div; - u32 max_div; - u32 val = 0; - u32 div; + u32 min_div = 0; + u32 max_div = 0; + u32 val; + const struct clk_div_table *clkt; - if (!table) { + if (!divider->table) { /* Clk divider table not provided, determine min/max divs */ if (of_property_read_u32(node, "ti,min-div", &min_div)) min_div = 1; @@ -493,75 +460,62 @@ static int _get_divider_width(struct device_node *node, pr_err("no max-div for %pOFn!\n", node); return -EINVAL; } - - /* Determine bit width for the field */ - if (flags & CLK_DIVIDER_ONE_BASED) - val = 1; - - div = min_div; - - while (div < max_div) { - if (flags & CLK_DIVIDER_POWER_OF_TWO) - div <<= 1; - else - div++; - val++; - } } else { - div = 0; - while (table[div].div) { - val = table[div].val; - div++; + for (clkt = divider->table; clkt->div; clkt++) { + val = clkt->div; + if (val > max_div) + max_div = val; + if (!min_div || val < min_div) + min_div = val; } } - return fls(val); + divider->min = min_div; + divider->max = max_div; + _setup_mask(divider); + + return 0; } static int __init ti_clk_divider_populate(struct device_node *node, - struct clk_omap_reg *reg, const struct clk_div_table **table, - u32 *flags, u8 *div_flags, u8 *width, u8 *shift, s8 *latch) + struct clk_omap_divider *div, + u32 *flags) { u32 val; int ret; - ret = ti_clk_get_reg_addr(node, 0, reg); + ret = ti_clk_get_reg_addr(node, 0, &div->reg); if (ret) return ret; if (!of_property_read_u32(node, "ti,bit-shift", &val)) - *shift = val; + div->shift = val; else - *shift = 0; + div->shift = 0; - if (latch) { - if (!of_property_read_u32(node, "ti,latch-bit", &val)) - *latch = val; - else - *latch = -EINVAL; - } + if (!of_property_read_u32(node, "ti,latch-bit", &val)) + div->latch = val; + else + div->latch = -EINVAL; *flags = 0; - *div_flags = 0; + div->flags = 0; if (of_property_read_bool(node, "ti,index-starts-at-one")) - *div_flags |= CLK_DIVIDER_ONE_BASED; + div->flags |= CLK_DIVIDER_ONE_BASED; if (of_property_read_bool(node, "ti,index-power-of-two")) - *div_flags |= CLK_DIVIDER_POWER_OF_TWO; + div->flags |= CLK_DIVIDER_POWER_OF_TWO; if (of_property_read_bool(node, "ti,set-rate-parent")) *flags |= CLK_SET_RATE_PARENT; - *table = ti_clk_get_div_table(node); - - if (IS_ERR(*table)) - return PTR_ERR(*table); - - *width = _get_divider_width(node, *table, *div_flags); + ret = ti_clk_get_div_table(node, div); + if (ret) + return ret; - return 0; + return _populate_divider_min_max(node, div); } /** @@ -573,24 +527,17 @@ static int __init ti_clk_divider_populate(struct device_node *node, static void __init of_ti_divider_clk_setup(struct device_node *node) { struct clk *clk; - const char *parent_name; - struct clk_omap_reg reg; - u8 clk_divider_flags = 0; - u8 width = 0; - u8 shift = 0; - s8 latch = -EINVAL; - const struct clk_div_table *table = NULL; u32 flags = 0; + struct clk_omap_divider *div; - parent_name = of_clk_get_parent_name(node, 0); + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return; - if (ti_clk_divider_populate(node, ®, &table, &flags, - &clk_divider_flags, &width, &shift, &latch)) + if (ti_clk_divider_populate(node, div, &flags)) goto cleanup; - clk = _register_divider(NULL, node->name, parent_name, flags, ®, - shift, width, latch, clk_divider_flags, table); - + clk = _register_divider(node, flags, div); if (!IS_ERR(clk)) { of_clk_add_provider(node, of_clk_src_simple_get, clk); of_ti_clk_autoidle_setup(node); @@ -598,22 +545,21 @@ static void __init of_ti_divider_clk_setup(struct device_node *node) } cleanup: - kfree(table); + kfree(div->table); + kfree(div); } CLK_OF_DECLARE(divider_clk, "ti,divider-clock", of_ti_divider_clk_setup); static void __init of_ti_composite_divider_clk_setup(struct device_node *node) { struct clk_omap_divider *div; - u32 val; + u32 tmp; div = kzalloc(sizeof(*div), GFP_KERNEL); if (!div) return; - if (ti_clk_divider_populate(node, &div->reg, &div->table, &val, - &div->flags, &div->width, &div->shift, - NULL) < 0) + if (ti_clk_divider_populate(node, div, &tmp)) goto cleanup; if (!ti_clk_add_component(node, &div->hw, CLK_COMPONENT_TYPE_DIVIDER)) diff --git a/drivers/clk/uniphier/clk-uniphier-core.c b/drivers/clk/uniphier/clk-uniphier-core.c index c6aaca73cf86..12380236d7ab 100644 --- a/drivers/clk/uniphier/clk-uniphier-core.c +++ b/drivers/clk/uniphier/clk-uniphier-core.c @@ -64,8 +64,7 @@ static int uniphier_clk_probe(struct platform_device *pdev) for (p = data; p->name; p++) clk_num = max(clk_num, p->idx + 1); - hw_data = devm_kzalloc(dev, - sizeof(*hw_data) + clk_num * sizeof(struct clk_hw *), + hw_data = devm_kzalloc(dev, struct_size(hw_data, hws, clk_num), GFP_KERNEL); if (!hw_data) return -ENOMEM; diff --git a/drivers/crypto/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/qat/qat_common/adf_ctl_drv.c index abc7a7f64d64..ef0e482ee04f 100644 --- a/drivers/crypto/qat/qat_common/adf_ctl_drv.c +++ b/drivers/crypto/qat/qat_common/adf_ctl_drv.c @@ -68,7 +68,7 @@ static long adf_ctl_ioctl(struct file *fp, unsigned int cmd, unsigned long arg); static const struct file_operations adf_ctl_ops = { .owner = THIS_MODULE, .unlocked_ioctl = adf_ctl_ioctl, - .compat_ioctl = adf_ctl_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; struct adf_ctl_drv_info { diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c index eccdda1f7b71..46e46047a1f7 100644 --- a/drivers/dax/bus.c +++ b/drivers/dax/bus.c @@ -309,7 +309,7 @@ static ssize_t resource_show(struct device *dev, return sprintf(buf, "%#llx\n", dev_dax_resource(dev_dax)); } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -322,6 +322,13 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(modalias); +static ssize_t numa_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", dev_to_node(dev)); +} +static DEVICE_ATTR_RO(numa_node); + static umode_t dev_dax_visible(struct kobject *kobj, struct attribute *a, int n) { struct device *dev = container_of(kobj, struct device, kobj); @@ -329,8 +336,8 @@ static umode_t dev_dax_visible(struct kobject *kobj, struct attribute *a, int n) if (a == &dev_attr_target_node.attr && dev_dax_target_node(dev_dax) < 0) return 0; - if (a == &dev_attr_resource.attr) - return 0400; + if (a == &dev_attr_numa_node.attr && !IS_ENABLED(CONFIG_NUMA)) + return 0; return a->mode; } @@ -339,6 +346,7 @@ static struct attribute *dev_dax_attributes[] = { &dev_attr_size.attr, &dev_attr_target_node.attr, &dev_attr_resource.attr, + &dev_attr_numa_node.attr, NULL, }; @@ -373,6 +381,11 @@ static void dev_dax_release(struct device *dev) kfree(dev_dax); } +static const struct device_type dev_dax_type = { + .release = dev_dax_release, + .groups = dax_attribute_groups, +}; + static void unregister_dev_dax(void *dev) { struct dev_dax *dev_dax = to_dev_dax(dev); @@ -430,8 +443,7 @@ struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id, else dev->class = dax_class; dev->parent = parent; - dev->groups = dax_attribute_groups; - dev->release = dev_dax_release; + dev->type = &dev_dax_type; dev_set_name(dev, "dax%d.%d", dax_region->id, id); rc = device_add(dev); diff --git a/drivers/dax/pmem/core.c b/drivers/dax/pmem/core.c index 6eb6dfdf19bf..2bedf8414fff 100644 --- a/drivers/dax/pmem/core.c +++ b/drivers/dax/pmem/core.c @@ -25,20 +25,20 @@ struct dev_dax *__dax_pmem_probe(struct device *dev, enum dev_dax_subsys subsys) ndns = nvdimm_namespace_common_probe(dev); if (IS_ERR(ndns)) return ERR_CAST(ndns); - nsio = to_nd_namespace_io(&ndns->dev); /* parse the 'pfn' info block via ->rw_bytes */ - rc = devm_nsio_enable(dev, nsio); + rc = devm_namespace_enable(dev, ndns, nd_info_block_reserve()); if (rc) return ERR_PTR(rc); rc = nvdimm_setup_pfn(nd_pfn, &pgmap); if (rc) return ERR_PTR(rc); - devm_nsio_disable(dev, nsio); + devm_namespace_disable(dev, ndns); /* reserve the metadata area, device-dax will reserve the data */ pfn_sb = nd_pfn->pfn_sb; offset = le64_to_cpu(pfn_sb->dataoff); + nsio = to_nd_namespace_io(&ndns->dev); if (!devm_request_mem_region(dev, nsio->res.start, offset, dev_name(&ndns->dev))) { dev_warn(dev, "could not reserve metadata\n"); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index d377b4ca66bf..ce41cd9b758a 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -415,9 +415,7 @@ static const struct file_operations dma_buf_fops = { .llseek = dma_buf_llseek, .poll = dma_buf_poll, .unlocked_ioctl = dma_buf_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = dma_buf_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .show_fdinfo = dma_buf_show_fdinfo, }; diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 6713cfb1995c..348b3a9170fa 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c @@ -408,5 +408,5 @@ const struct file_operations sw_sync_debugfs_fops = { .open = sw_sync_debugfs_open, .release = sw_sync_debugfs_release, .unlocked_ioctl = sw_sync_ioctl, - .compat_ioctl = sw_sync_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 25c5c071645b..76fb072c22dc 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -480,5 +480,5 @@ static const struct file_operations sync_file_fops = { .release = sync_file_release, .poll = sync_file_poll, .unlocked_ioctl = sync_file_ioctl, - .compat_ioctl = sync_file_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index 1da7ba18d399..c777088f5828 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -1646,14 +1646,6 @@ static long fw_device_op_ioctl(struct file *file, return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); } -#ifdef CONFIG_COMPAT -static long fw_device_op_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg)); -} -#endif - static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) { struct client *client = file->private_data; @@ -1795,7 +1787,5 @@ const struct file_operations fw_device_ops = { .mmap = fw_device_op_mmap, .release = fw_device_op_release, .poll = fw_device_op_poll, -#ifdef CONFIG_COMPAT - .compat_ioctl = fw_device_op_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 92d0ff63b3ea..8adffd42f8cb 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -120,6 +120,14 @@ config GPIO_ASPEED help Say Y here to support Aspeed AST2400 and AST2500 GPIO controllers. +config GPIO_ASPEED_SGPIO + bool "Aspeed SGPIO support" + depends on (ARCH_ASPEED || COMPILE_TEST) && OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y here to support Aspeed AST2500 SGPIO functionality. + config GPIO_ATH79 tristate "Atheros AR71XX/AR724X/AR913X GPIO support" default y if ATH79 @@ -147,6 +155,15 @@ config GPIO_BCM_KONA help Turn on GPIO support for Broadcom "Kona" chips. +config GPIO_BCM_XGS_IPROC + tristate "BRCM XGS iProc GPIO support" + depends on OF_GPIO && (ARCH_BCM_IPROC || COMPILE_TEST) + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + default ARCH_BCM_IPROC + help + Say yes here to enable GPIO support for Broadcom XGS iProc SoCs. + config GPIO_BRCMSTB tristate "BRCMSTB GPIO support" default y if (ARCH_BRCMSTB || BMIPS_GENERIC) @@ -435,6 +452,15 @@ config GPIO_RCAR help Say yes here to support GPIO on Renesas R-Car SoCs. +config GPIO_RDA + bool "RDA Micro GPIO controller support" + depends on ARCH_RDA || COMPILE_TEST + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y here to support RDA Micro GPIO controller. + config GPIO_REG bool help @@ -531,6 +557,7 @@ config GPIO_TEGRA186 depends on ARCH_TEGRA_186_SOC || COMPILE_TEST depends on OF_GPIO select GPIOLIB_IRQCHIP + select IRQ_DOMAIN_HIERARCHY help Say yes here to support GPIO pins on NVIDIA Tegra186 SoCs. @@ -1320,7 +1347,7 @@ config GPIO_BT8XX The card needs to be physically altered for using it as a GPIO card. For more information on how to build a GPIO card from a BT8xx TV card, see the documentation file at - Documentation/driver-api/bt8xxgpio.rst + Documentation/driver-api/gpio/bt8xxgpio.rst If unsure, say N. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d2fd19c15bae..34eb8b2b12dd 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -32,8 +32,10 @@ obj-$(CONFIG_GPIO_AMD_FCH) += gpio-amd-fch.o obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o +obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD70528) += gpio-bd70528.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o @@ -115,6 +117,7 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o +obj-$(CONFIG_GPIO_RDA) += gpio-rda.o obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o obj-$(CONFIG_GPIO_REG) += gpio-reg.o obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 9c048f10c9ad..76f8c7ff18ff 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -80,6 +80,10 @@ Work items: - Look over and identify any remaining easily converted drivers and dry-code conversions to MMIO GPIO for maintainers to test +- Expand the MMIO GPIO or write a new library for regmap-based I/O + helpers for GPIO drivers on regmap that simply use offsets + 0..n in some register to drive GPIO lines + - Expand the MMIO GPIO or write a new library for port-mapped I/O helpers (x86 inb()/outb()) and convert port-mapped I/O drivers to use this with dry-coding and sending to maintainers to test diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index a44fa8af5b0d..400c09b905f8 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -59,7 +59,10 @@ static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned offset) const unsigned port = offset / 8; const unsigned mask = BIT(offset % 8); - return !!(dio48egpio->io_state[port] & mask); + if (dio48egpio->io_state[port] & mask) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index ff53887bdaa8..c50329ab493a 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -53,7 +53,7 @@ struct idi_48_gpio { static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { - return 1; + return GPIO_LINE_DIRECTION_IN; } static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -65,7 +65,7 @@ static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); unsigned i; - const unsigned register_offset[6] = { 0, 1, 2, 4, 5, 6 }; + static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; unsigned base_offset; unsigned mask; diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 8d2f51cd9d91..5752d9dab148 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -51,9 +51,9 @@ struct idio_16_gpio { static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { if (offset > 15) - return 1; + return GPIO_LINE_DIRECTION_IN; - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 83a2286d93f6..173e06758e6c 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -77,7 +77,10 @@ static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned offset) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - return !(priv->flags & MMIO_74XX_DIR_OUT); + if (priv->flags & MMIO_74XX_DIR_OUT) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) diff --git a/drivers/gpio/gpio-amd-fch.c b/drivers/gpio/gpio-amd-fch.c index 181df1581df5..4e44ba4d7423 100644 --- a/drivers/gpio/gpio-amd-fch.c +++ b/drivers/gpio/gpio-amd-fch.c @@ -92,7 +92,7 @@ static int amd_fch_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) ret = (readl_relaxed(ptr) & AMD_FCH_GPIO_FLAG_DIRECTION); spin_unlock_irqrestore(&priv->lock, flags); - return ret; + return ret ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } static void amd_fch_gpio_set(struct gpio_chip *gc, diff --git a/drivers/gpio/sgpio-aspeed.c b/drivers/gpio/gpio-aspeed-sgpio.c index 7e99860ca447..7e99860ca447 100644 --- a/drivers/gpio/sgpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed-sgpio.c diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 09e53c5f3b0a..f1037b61f763 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -487,10 +487,10 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) u32 val; if (!have_input(gpio, offset)) - return 0; + return GPIO_LINE_DIRECTION_OUT; if (!have_output(gpio, offset)) - return 1; + return GPIO_LINE_DIRECTION_IN; spin_lock_irqsave(&gpio->lock, flags); @@ -498,8 +498,7 @@ static int aspeed_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) spin_unlock_irqrestore(&gpio->lock, flags); - return !val; - + return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } static inline int irqd_to_aspeed_gpio_data(struct irq_data *d, diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index f1a5ea9b3de2..53fae02c40ad 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -226,7 +226,6 @@ static int ath79_gpio_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct ath79_gpio_ctrl *ctrl; struct gpio_irq_chip *girq; - struct resource *res; u32 ath79_gpio_count; bool oe_inverted; int err; @@ -256,12 +255,9 @@ static int ath79_gpio_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - ctrl->base = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!ctrl->base) - return -ENOMEM; + ctrl->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctrl->base)) + return PTR_ERR(ctrl->base); raw_spin_lock_init(&ctrl->lock); err = bgpio_init(&ctrl->gc, dev, 4, diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 9fa6d3a967d2..4122683eb1f9 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -127,7 +127,7 @@ static int bcm_kona_gpio_get_dir(struct gpio_chip *chip, unsigned gpio) u32 val; val = readl(reg_base + GPIO_CONTROL(gpio)) & GPIO_GPCTR0_IOTR_MASK; - return !!val; + return val ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; } static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) @@ -144,7 +144,7 @@ static void bcm_kona_gpio_set(struct gpio_chip *chip, unsigned gpio, int value) raw_spin_lock_irqsave(&kona_gpio->lock, flags); /* this function only applies to output pin */ - if (bcm_kona_gpio_get_dir(chip, gpio) == 1) + if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN) goto out; reg_offset = value ? GPIO_OUT_SET(bank_id) : GPIO_OUT_CLEAR(bank_id); @@ -170,7 +170,7 @@ static int bcm_kona_gpio_get(struct gpio_chip *chip, unsigned gpio) reg_base = kona_gpio->reg_base; raw_spin_lock_irqsave(&kona_gpio->lock, flags); - if (bcm_kona_gpio_get_dir(chip, gpio) == 1) + if (bcm_kona_gpio_get_dir(chip, gpio) == GPIO_LINE_DIRECTION_IN) reg_offset = GPIO_IN_STATUS(bank_id); else reg_offset = GPIO_OUT_STATUS(bank_id); diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c index 4ba4d4a67881..45b3da8da336 100644 --- a/drivers/gpio/gpio-bd70528.c +++ b/drivers/gpio/gpio-bd70528.c @@ -54,8 +54,10 @@ static int bd70528_get_direction(struct gpio_chip *chip, unsigned int offset) dev_err(bdgpio->chip.dev, "Could not read gpio direction\n"); return ret; } + if (val & BD70528_GPIO_OUT_EN_MASK) + return GPIO_LINE_DIRECTION_OUT; - return !(val & BD70528_GPIO_OUT_EN_MASK); + return GPIO_LINE_DIRECTION_IN; } static int bd70528_gpio_set_config(struct gpio_chip *chip, unsigned int offset, @@ -166,9 +168,9 @@ static int bd70528_gpio_get(struct gpio_chip *chip, unsigned int offset) * locking would make no sense. */ ret = bd70528_get_direction(chip, offset); - if (ret == 0) + if (ret == GPIO_LINE_DIRECTION_OUT) ret = bd70528_gpio_get_o(bdgpio, offset); - else if (ret == 1) + else if (ret == GPIO_LINE_DIRECTION_IN) ret = bd70528_gpio_get_i(bdgpio, offset); else dev_err(bdgpio->chip.dev, "failed to read GPIO direction\n"); @@ -230,3 +232,4 @@ module_platform_driver(bd70528_gpio); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_DESCRIPTION("BD70528 voltage regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-gpio"); diff --git a/drivers/gpio/gpio-bd9571mwv.c b/drivers/gpio/gpio-bd9571mwv.c index 5224a946e8ab..c0abc9c6851b 100644 --- a/drivers/gpio/gpio-bd9571mwv.c +++ b/drivers/gpio/gpio-bd9571mwv.c @@ -37,8 +37,10 @@ static int bd9571mwv_gpio_get_direction(struct gpio_chip *chip, ret = regmap_read(gpio->bd->regmap, BD9571MWV_GPIO_DIR, &val); if (ret < 0) return ret; + if (val & BIT(offset)) + return GPIO_LINE_DIRECTION_IN; - return val & BIT(offset); + return GPIO_LINE_DIRECTION_OUT; } static int bd9571mwv_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 8a33c2fc174d..26b40c8b8a12 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -200,9 +200,9 @@ static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset) struct dln2_gpio *dln2 = gpiochip_get_data(chip); if (test_bit(offset, dln2->output_enabled)) - return 0; + return GPIO_LINE_DIRECTION_OUT; - return 1; + return GPIO_LINE_DIRECTION_IN; } static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) @@ -214,7 +214,7 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) if (dir < 0) return dir; - if (dir == 1) + if (dir == GPIO_LINE_DIRECTION_IN) return dln2_gpio_pin_get_in_val(dln2, offset); return dln2_gpio_pin_get_out_val(dln2, offset); diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 620f25b7efb4..17a243c528ad 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -269,13 +269,12 @@ static void em_gio_irq_domain_remove(void *data) static int em_gio_probe(struct platform_device *pdev) { struct em_gio_priv *p; - struct resource *io[2], *irq[2]; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; struct device *dev = &pdev->dev; const char *name = dev_name(dev); unsigned int ngpios; - int ret; + int irq[2], ret; p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); if (!p) @@ -285,25 +284,21 @@ static int em_gio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, p); spin_lock_init(&p->sense_lock); - io[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0); - io[1] = platform_get_resource(pdev, IORESOURCE_MEM, 1); - irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + irq[0] = platform_get_irq(pdev, 0); + if (irq[0] < 0) + return irq[0]; - if (!io[0] || !io[1] || !irq[0] || !irq[1]) { - dev_err(dev, "missing IRQ or IOMEM\n"); - return -EINVAL; - } + irq[1] = platform_get_irq(pdev, 1); + if (irq[1] < 0) + return irq[1]; - p->base0 = devm_ioremap_nocache(dev, io[0]->start, - resource_size(io[0])); - if (!p->base0) - return -ENOMEM; + p->base0 = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(p->base0)) + return PTR_ERR(p->base0); - p->base1 = devm_ioremap_nocache(dev, io[1]->start, - resource_size(io[1])); - if (!p->base1) - return -ENOMEM; + p->base1 = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(p->base1)) + return PTR_ERR(p->base1); if (of_property_read_u32(dev->of_node, "ngpios", &ngpios)) { dev_err(dev, "Missing ngpios OF property\n"); @@ -326,7 +321,7 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->ngpio = ngpios; irq_chip = &p->irq_chip; - irq_chip->name = name; + irq_chip->name = "gpio-em"; irq_chip->irq_mask = em_gio_irq_disable; irq_chip->irq_unmask = em_gio_irq_enable; irq_chip->irq_set_type = em_gio_irq_set_type; @@ -346,14 +341,12 @@ static int em_gio_probe(struct platform_device *pdev) if (ret) return ret; - if (devm_request_irq(dev, irq[0]->start, - em_gio_irq_handler, 0, name, p)) { + if (devm_request_irq(dev, irq[0], em_gio_irq_handler, 0, name, p)) { dev_err(dev, "failed to request low IRQ\n"); return -ENOENT; } - if (devm_request_irq(dev, irq[1]->start, - em_gio_irq_handler, 0, name, p)) { + if (devm_request_irq(dev, irq[1], em_gio_irq_handler, 0, name, p)) { dev_err(dev, "failed to request high IRQ\n"); return -ENOENT; } diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index fae327d5b06e..da1ef0b1c291 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -77,7 +77,10 @@ static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; unsigned int bit = (offset + exar_gpio->first_pin) % 8; - return !!(exar_get(chip, addr) & BIT(bit)); + if (exar_get(chip, addr) & BIT(bit)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int exar_get_value(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index fdc639f856f1..cadd02993539 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -250,7 +250,10 @@ static int f7188x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) superio_exit(sio->addr); - return !(dir & 1 << offset); + if (dir & 1 << offset) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 78a1db24e931..c22d6f94129c 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -52,7 +52,10 @@ static int gpiomm_gpio_get_direction(struct gpio_chip *chip, const unsigned int port = offset / 8; const unsigned int mask = BIT(offset % 8); - return !!(gpiommgpio->io_state[port] & mask); + if (gpiommgpio->io_state[port] & mask) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int gpiomm_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index 6eb56f7ab9c9..a40bd56673fe 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -220,7 +220,10 @@ static int egpio_get_direction(struct gpio_chip *chip, unsigned offset) egpio = gpiochip_get_data(chip); - return !test_bit(offset, &egpio->is_out); + if (test_bit(offset, &egpio->is_out)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static void egpio_write_cache(struct egpio_info *ei) @@ -265,7 +268,6 @@ static int __init egpio_probe(struct platform_device *pdev) struct gpio_chip *chip; unsigned int irq, irq_end; int i; - int ret; /* Initialize ei data structure. */ ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL); @@ -275,28 +277,24 @@ static int __init egpio_probe(struct platform_device *pdev) spin_lock_init(&ei->lock); /* Find chained irq */ - ret = -EINVAL; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res) ei->chained_irq = res->start; /* Map egpio chip into virtual address space. */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - goto fail; - ei->base_addr = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!ei->base_addr) - goto fail; - pr_debug("EGPIO phys=%08x virt=%p\n", (u32)res->start, ei->base_addr); + ei->base_addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ei->base_addr)) + return PTR_ERR(ei->base_addr); if ((pdata->bus_width != 16) && (pdata->bus_width != 32)) - goto fail; + return -EINVAL; + ei->bus_shift = fls(pdata->bus_width - 1) - 3; pr_debug("bus_shift = %d\n", ei->bus_shift); if ((pdata->reg_width != 8) && (pdata->reg_width != 16)) - goto fail; + return -EINVAL; + ei->reg_shift = fls(pdata->reg_width - 1); pr_debug("reg_shift = %d\n", ei->reg_shift); @@ -308,10 +306,9 @@ static int __init egpio_probe(struct platform_device *pdev) ei->chip = devm_kcalloc(&pdev->dev, ei->nchips, sizeof(struct egpio_chip), GFP_KERNEL); - if (!ei->chip) { - ret = -ENOMEM; - goto fail; - } + if (!ei->chip) + return -ENOMEM; + for (i = 0; i < ei->nchips; i++) { ei->chip[i].reg_start = pdata->chip[i].reg_start; ei->chip[i].cached_values = pdata->chip[i].initial_values; @@ -321,10 +318,9 @@ static int __init egpio_probe(struct platform_device *pdev) chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "htc-egpio-%d", i); - if (!chip->label) { - ret = -ENOMEM; - goto fail; - } + if (!chip->label) + return -ENOMEM; + chip->parent = &pdev->dev; chip->owner = THIS_MODULE; chip->get = egpio_get; @@ -366,10 +362,6 @@ static int __init egpio_probe(struct platform_device *pdev) } return 0; - -fail: - printk(KERN_ERR "EGPIO failed to setup\n"); - return ret; } #ifdef CONFIG_PM diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 90bf7742f9b0..2f086d0aa1f4 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -159,7 +159,10 @@ static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr) static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr) { - return ichx_read_bit(GPIO_IO_SEL, nr); + if (ichx_read_bit(GPIO_IO_SEL, nr)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index ef51638f3f75..4ea15f08e0f4 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -104,7 +104,10 @@ static int kempld_gpio_get_direction(struct gpio_chip *chip, unsigned offset) struct kempld_gpio_data *gpio = gpiochip_get_data(chip); struct kempld_device_data *pld = gpio->pld; - return !kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset); + if (kempld_gpio_get_bit(pld, KEMPLD_GPIO_DIR_NUM(offset), offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int kempld_gpio_pincount(struct kempld_device_data *pld) diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 801995dd9b26..70fad87ff2db 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -33,7 +33,7 @@ static int lp873x_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { /* This device is output only */ - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int lp873x_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index a121c8f10610..e1244520cf7d 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -57,7 +57,10 @@ static int lp87565_gpio_get_direction(struct gpio_chip *chip, if (ret < 0) return ret; - return !(val & BIT(offset)); + if (val & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int lp87565_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index e9e47c0d5be7..490ce7bae25e 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -164,6 +164,12 @@ static int lp_irq_type(struct irq_data *d, unsigned type) value |= TRIG_SEL_BIT | INT_INV_BIT; outl(value, reg); + + if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + else if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(d, handle_level_irq); + spin_unlock_irqrestore(&lg->lock, flags); return 0; diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 7086f8b5388f..8f38303fcbc4 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -34,7 +34,10 @@ static int madera_gpio_get_direction(struct gpio_chip *chip, if (ret < 0) return ret; - return !!(val & MADERA_GP1_DIR_MASK); + if (val & MADERA_GP1_DIR_MASK) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int madera_gpio_direction_in(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index 4b4b2ceb82fc..0696d5a21431 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -94,7 +94,7 @@ DECLARE_CRC8_TABLE(max3191x_crc8); static int max3191x_get_direction(struct gpio_chip *gpio, unsigned int offset) { - return 1; /* always in */ + return GPIO_LINE_DIRECTION_IN; /* always in */ } static int max3191x_direction_input(struct gpio_chip *gpio, unsigned int offset) diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 642c6321c22a..313bd02dd893 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -18,109 +18,115 @@ struct max77620_gpio { struct gpio_chip gpio_chip; struct regmap *rmap; struct device *dev; + struct mutex buslock; /* irq_bus_lock */ + unsigned int irq_type[8]; + bool irq_enabled[8]; }; -static const struct regmap_irq max77620_gpio_irqs[] = { - [0] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 0, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [1] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 1, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [2] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 2, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [3] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 3, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [4] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 4, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [5] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 5, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [6] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 6, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, - [7] = { - .reg_offset = 0, - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, - .type = { - .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, - .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, - .type_reg_offset = 7, - .types_supported = IRQ_TYPE_EDGE_BOTH, - }, - }, -}; +static irqreturn_t max77620_gpio_irqhandler(int irq, void *data) +{ + struct max77620_gpio *gpio = data; + unsigned int value, offset; + unsigned long pending; + int err; + + err = regmap_read(gpio->rmap, MAX77620_REG_IRQ_LVL2_GPIO, &value); + if (err < 0) { + dev_err(gpio->dev, "REG_IRQ_LVL2_GPIO read failed: %d\n", err); + return IRQ_NONE; + } + + pending = value; + + for_each_set_bit(offset, &pending, 8) { + unsigned int virq; + + virq = irq_find_mapping(gpio->gpio_chip.irq.domain, offset); + handle_nested_irq(virq); + } + + return IRQ_HANDLED; +} + +static void max77620_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + gpio->irq_enabled[data->hwirq] = false; +} -static const struct regmap_irq_chip max77620_gpio_irq_chip = { - .name = "max77620-gpio", - .irqs = max77620_gpio_irqs, - .num_irqs = ARRAY_SIZE(max77620_gpio_irqs), - .num_regs = 1, - .num_type_reg = 8, - .irq_reg_stride = 1, - .type_reg_stride = 1, - .status_base = MAX77620_REG_IRQ_LVL2_GPIO, - .type_base = MAX77620_REG_GPIO0, +static void max77620_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + gpio->irq_enabled[data->hwirq] = true; +} + +static int max77620_gpio_set_irq_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + unsigned int irq_type; + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + irq_type = MAX77620_CNFG_GPIO_INT_RISING; + break; + + case IRQ_TYPE_EDGE_FALLING: + irq_type = MAX77620_CNFG_GPIO_INT_FALLING; + break; + + case IRQ_TYPE_EDGE_BOTH: + irq_type = MAX77620_CNFG_GPIO_INT_RISING | + MAX77620_CNFG_GPIO_INT_FALLING; + break; + + default: + return -EINVAL; + } + + gpio->irq_type[data->hwirq] = irq_type; + + return 0; +} + +static void max77620_gpio_bus_lock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + + mutex_lock(&gpio->buslock); +} + +static void max77620_gpio_bus_sync_unlock(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct max77620_gpio *gpio = gpiochip_get_data(chip); + unsigned int value, offset = data->hwirq; + int err; + + value = gpio->irq_enabled[offset] ? gpio->irq_type[offset] : 0; + + err = regmap_update_bits(gpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_INT_MASK, value); + if (err < 0) + dev_err(chip->parent, "failed to update interrupt mask: %d\n", + err); + + mutex_unlock(&gpio->buslock); +} + +static struct irq_chip max77620_gpio_irqchip = { + .name = "max77620-gpio", + .irq_mask = max77620_gpio_irq_mask, + .irq_unmask = max77620_gpio_irq_unmask, + .irq_set_type = max77620_gpio_set_irq_type, + .irq_bus_lock = max77620_gpio_bus_lock, + .irq_bus_sync_unlock = max77620_gpio_bus_sync_unlock, + .flags = IRQCHIP_MASK_ON_SUSPEND, }; static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) @@ -254,14 +260,6 @@ static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, return -ENOTSUPP; } -static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) -{ - struct max77620_gpio *mgpio = gpiochip_get_data(gc); - struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent); - - return regmap_irq_get_virq(chip->gpio_irq_data, offset); -} - static int max77620_gpio_probe(struct platform_device *pdev) { struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -287,7 +285,6 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; mgpio->gpio_chip.set = max77620_gpio_set; mgpio->gpio_chip.set_config = max77620_gpio_set_config; - mgpio->gpio_chip.to_irq = max77620_gpio_to_irq; mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; mgpio->gpio_chip.base = -1; @@ -303,15 +300,21 @@ static int max77620_gpio_probe(struct platform_device *pdev) return ret; } - ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, gpio_irq, - IRQF_ONESHOT, -1, - &max77620_gpio_irq_chip, - &chip->gpio_irq_data); + mutex_init(&mgpio->buslock); + + gpiochip_irqchip_add_nested(&mgpio->gpio_chip, &max77620_gpio_irqchip, + 0, handle_edge_irq, IRQ_TYPE_NONE); + + ret = request_threaded_irq(gpio_irq, NULL, max77620_gpio_irqhandler, + IRQF_ONESHOT, "max77620-gpio", mgpio); if (ret < 0) { - dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); + dev_err(&pdev->dev, "failed to request IRQ: %d\n", ret); return ret; } + gpiochip_set_nested_irqchip(&mgpio->gpio_chip, &max77620_gpio_irqchip, + gpio_irq); + return 0; } diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 3302125e5265..48918a016cd8 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -162,7 +162,10 @@ static int mrfld_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { void __iomem *gpdr = gpio_reg(chip, offset, GPDR); - return !(readl(gpdr) & BIT(offset % 32)); + if (readl(gpdr) & BIT(offset % 32)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int mrfld_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, @@ -362,8 +365,9 @@ static void mrfld_irq_handler(struct irq_desc *desc) chained_irq_exit(irqchip, desc); } -static void mrfld_irq_init_hw(struct mrfld_gpio *priv) +static int mrfld_irq_init_hw(struct gpio_chip *chip) { + struct mrfld_gpio *priv = gpiochip_get_data(chip); void __iomem *reg; unsigned int base; @@ -375,6 +379,8 @@ static void mrfld_irq_init_hw(struct mrfld_gpio *priv) reg = gpio_reg(&priv->chip, base, GFER); writel(0, reg); } + + return 0; } static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv) @@ -393,14 +399,36 @@ static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv) return name; } -static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) +static int mrfld_gpio_add_pin_ranges(struct gpio_chip *chip) { + struct mrfld_gpio *priv = gpiochip_get_data(chip); const struct mrfld_gpio_pinrange *range; const char *pinctrl_dev_name; + unsigned int i; + int retval; + + pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv); + for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) { + range = &mrfld_gpio_ranges[i]; + retval = gpiochip_add_pin_range(&priv->chip, pinctrl_dev_name, + range->gpio_base, + range->pin_base, + range->npins); + if (retval) { + dev_err(priv->dev, "failed to add GPIO pin range\n"); + return retval; + } + } + + return 0; +} + +static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct gpio_irq_chip *girq; struct mrfld_gpio *priv; u32 gpio_base, irq_base; void __iomem *base; - unsigned int i; int retval; retval = pcim_enable_device(pdev); @@ -441,42 +469,31 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id priv->chip.base = gpio_base; priv->chip.ngpio = MRFLD_NGPIO; priv->chip.can_sleep = false; + priv->chip.add_pin_ranges = mrfld_gpio_add_pin_ranges; raw_spin_lock_init(&priv->lock); - pci_set_drvdata(pdev, priv); + girq = &priv->chip.irq; + girq->chip = &mrfld_irqchip; + girq->init_hw = mrfld_irq_init_hw; + girq->parent_handler = mrfld_irq_handler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = pdev->irq; + girq->first = irq_base; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { dev_err(&pdev->dev, "gpiochip_add error %d\n", retval); return retval; } - pinctrl_dev_name = mrfld_gpio_get_pinctrl_dev_name(priv); - for (i = 0; i < ARRAY_SIZE(mrfld_gpio_ranges); i++) { - range = &mrfld_gpio_ranges[i]; - retval = gpiochip_add_pin_range(&priv->chip, - pinctrl_dev_name, - range->gpio_base, - range->pin_base, - range->npins); - if (retval) { - dev_err(&pdev->dev, "failed to add GPIO pin range\n"); - return retval; - } - } - - retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base, - handle_bad_irq, IRQ_TYPE_NONE); - if (retval) { - dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n"); - return retval; - } - - mrfld_irq_init_hw(priv); - - gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq, - mrfld_irq_handler); - + pci_set_drvdata(pdev, priv); return 0; } diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index 6f904c874678..f729e3e9e983 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -370,15 +370,23 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) { /* Return 0 if output, 1 if input */ - if (gc->bgpio_dir_unreadable) - return !(gc->bgpio_dir & bgpio_line2mask(gc, gpio)); - if (gc->reg_dir_out) - return !(gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio)); + if (gc->bgpio_dir_unreadable) { + if (gc->bgpio_dir & bgpio_line2mask(gc, gpio)) + return GPIO_LINE_DIRECTION_OUT; + return GPIO_LINE_DIRECTION_IN; + } + + if (gc->reg_dir_out) { + if (gc->read_reg(gc->reg_dir_out) & bgpio_line2mask(gc, gpio)) + return GPIO_LINE_DIRECTION_OUT; + return GPIO_LINE_DIRECTION_IN; + } + if (gc->reg_dir_in) - return !!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio)); + if (!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio))) + return GPIO_LINE_DIRECTION_OUT; - /* This should not happen */ - return 1; + return GPIO_LINE_DIRECTION_IN; } static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 213aedc97dc2..56d647a30e3e 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -34,14 +34,9 @@ #define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__) -enum { - GPIO_MOCKUP_DIR_IN = 0, - GPIO_MOCKUP_DIR_OUT = 1, -}; - /* * struct gpio_pin_status - structure describing a GPIO status - * @dir: Configures direction of gpio as "in" or "out", 0=in, 1=out + * @dir: Configures direction of gpio as "in" or "out" * @value: Configures status of the gpio as 0(low) or 1(high) */ struct gpio_mockup_line_status { @@ -146,13 +141,68 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc, mutex_unlock(&chip->lock); } +static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip, + unsigned int offset, int value) +{ + struct gpio_desc *desc; + struct gpio_chip *gc; + struct irq_sim *sim; + int curr, irq, irq_type; + + gc = &chip->gc; + desc = &gc->gpiodev->descs[offset]; + sim = &chip->irqsim; + + mutex_lock(&chip->lock); + + if (test_bit(FLAG_REQUESTED, &desc->flags) && + !test_bit(FLAG_IS_OUT, &desc->flags)) { + curr = __gpio_mockup_get(chip, offset); + if (curr == value) + goto out; + + irq = irq_sim_irqnum(sim, offset); + irq_type = irq_get_trigger_type(irq); + + if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || + (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) + irq_sim_fire(sim, offset); + } + + /* Change the value unless we're actively driving the line. */ + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + !test_bit(FLAG_IS_OUT, &desc->flags)) + __gpio_mockup_set(chip, offset, value); + +out: + chip->lines[offset].pull = value; + mutex_unlock(&chip->lock); + return 0; +} + +static int gpio_mockup_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long config) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_UP: + return gpio_mockup_apply_pull(chip, offset, 1); + case PIN_CONFIG_BIAS_PULL_DOWN: + return gpio_mockup_apply_pull(chip, offset, 0); + default: + break; + } + return -ENOTSUPP; +} + static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, int value) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); mutex_lock(&chip->lock); - chip->lines[offset].dir = GPIO_MOCKUP_DIR_OUT; + chip->lines[offset].dir = GPIO_LINE_DIRECTION_OUT; __gpio_mockup_set(chip, offset, value); mutex_unlock(&chip->lock); @@ -164,7 +214,7 @@ static int gpio_mockup_dirin(struct gpio_chip *gc, unsigned int offset) struct gpio_mockup_chip *chip = gpiochip_get_data(gc); mutex_lock(&chip->lock); - chip->lines[offset].dir = GPIO_MOCKUP_DIR_IN; + chip->lines[offset].dir = GPIO_LINE_DIRECTION_IN; mutex_unlock(&chip->lock); return 0; @@ -226,12 +276,8 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file, size_t size, loff_t *ppos) { struct gpio_mockup_dbgfs_private *priv; - int rv, val, curr, irq, irq_type; - struct gpio_mockup_chip *chip; + int rv, val; struct seq_file *sfile; - struct gpio_desc *desc; - struct gpio_chip *gc; - struct irq_sim *sim; if (*ppos != 0) return -EINVAL; @@ -244,35 +290,9 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file, sfile = file->private_data; priv = sfile->private; - chip = priv->chip; - gc = &chip->gc; - desc = &gc->gpiodev->descs[priv->offset]; - sim = &chip->irqsim; - - mutex_lock(&chip->lock); - - if (test_bit(FLAG_REQUESTED, &desc->flags) && - !test_bit(FLAG_IS_OUT, &desc->flags)) { - curr = __gpio_mockup_get(chip, priv->offset); - if (curr == val) - goto out; - - irq = irq_sim_irqnum(sim, priv->offset); - irq_type = irq_get_trigger_type(irq); - - if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || - (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) - irq_sim_fire(sim, priv->offset); - } - - /* Change the value unless we're actively driving the line. */ - if (!test_bit(FLAG_REQUESTED, &desc->flags) || - !test_bit(FLAG_IS_OUT, &desc->flags)) - __gpio_mockup_set(chip, priv->offset, val); - -out: - chip->lines[priv->offset].pull = val; - mutex_unlock(&chip->lock); + rv = gpio_mockup_apply_pull(priv->chip, priv->offset, val); + if (rv) + return rv; return size; } @@ -418,6 +438,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; + gc->set_config = gpio_mockup_set_config; gc->to_irq = gpio_mockup_to_irq; gc->free = gpio_mockup_free; diff --git a/drivers/gpio/gpio-moxtet.c b/drivers/gpio/gpio-moxtet.c index 3fd729994a38..8299909318f4 100644 --- a/drivers/gpio/gpio-moxtet.c +++ b/drivers/gpio/gpio-moxtet.c @@ -78,9 +78,9 @@ static int moxtet_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) /* All lines are hard wired to be either input or output, not both. */ if (chip->desc->in_mask & BIT(offset)) - return 1; + return GPIO_LINE_DIRECTION_IN; else if (chip->desc->out_mask & BIT(offset)) - return 0; + return GPIO_LINE_DIRECTION_OUT; else return -EINVAL; } diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 16a47de29c94..f1e164cecff8 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -22,6 +22,7 @@ #include <linux/irq.h> #include <linux/gpio/driver.h> #include <linux/bitops.h> +#include <linux/interrupt.h> #define MPC8XXX_GPIO_PINS 32 @@ -127,20 +128,19 @@ static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset) return -ENXIO; } -static void mpc8xxx_gpio_irq_cascade(struct irq_desc *desc) +static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data) { - struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_desc_get_handler_data(desc); - struct irq_chip *chip = irq_desc_get_chip(desc); + struct mpc8xxx_gpio_chip *mpc8xxx_gc = data; struct gpio_chip *gc = &mpc8xxx_gc->gc; - unsigned int mask; + unsigned long mask; + int i; mask = gc->read_reg(mpc8xxx_gc->regs + GPIO_IER) & gc->read_reg(mpc8xxx_gc->regs + GPIO_IMR); - if (mask) - generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, - 32 - ffs(mask))); - if (chip->irq_eoi) - chip->irq_eoi(&desc->irq_data); + for_each_set_bit(i, &mask, 32) + generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq, 31 - i)); + + return IRQ_HANDLED; } static void mpc8xxx_irq_unmask(struct irq_data *d) @@ -377,7 +377,8 @@ static int mpc8xxx_probe(struct platform_device *pdev) * It's assumed that only a single type of gpio controller is available * on the current machine, so overwriting global data is fine. */ - mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type; + if (devtype->irq_set_type) + mpc8xxx_irq_chip.irq_set_type = devtype->irq_set_type; if (devtype->gpio_dir_out) gc->direction_output = devtype->gpio_dir_out; @@ -386,6 +387,9 @@ static int mpc8xxx_probe(struct platform_device *pdev) gc->to_irq = mpc8xxx_gpio_to_irq; + if (of_device_is_compatible(np, "fsl,qoriq-gpio")) + gc->write_reg(mpc8xxx_gc->regs + GPIO_IBE, 0xffffffff); + ret = gpiochip_add_data(gc, mpc8xxx_gc); if (ret) { pr_err("%pOF: GPIO chip registration failed with status %d\n", @@ -409,8 +413,16 @@ static int mpc8xxx_probe(struct platform_device *pdev) if (devtype->gpio_dir_in_init) devtype->gpio_dir_in_init(gc); - irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, - mpc8xxx_gpio_irq_cascade, mpc8xxx_gc); + ret = devm_request_irq(&pdev->dev, mpc8xxx_gc->irqn, + mpc8xxx_gpio_irq_cascade, + IRQF_NO_THREAD | IRQF_SHARED, "gpio-cascade", + mpc8xxx_gc); + if (ret) { + dev_err(&pdev->dev, "%s: failed to devm_request_irq(%d), ret = %d\n", + np->full_name, mpc8xxx_gc->irqn, ret); + goto err; + } + return 0; err: iounmap(mpc8xxx_gc->regs); diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 2f0f50336b9a..993bbeb3c006 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -384,7 +384,10 @@ static int mvebu_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) regmap_read(mvchip->regs, GPIO_IO_CONF_OFF + mvchip->offset, &u); - return !!(u & BIT(pin)); + if (u & BIT(pin)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int mvebu_gpio_to_irq(struct gpio_chip *chip, unsigned int pin) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 7907a8755866..c77d474185f3 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -411,6 +411,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; + int irq_count; int irq_base; int err; @@ -426,9 +427,15 @@ static int mxc_gpio_probe(struct platform_device *pdev) if (IS_ERR(port->base)) return PTR_ERR(port->base); - port->irq_high = platform_get_irq(pdev, 1); - if (port->irq_high < 0) - port->irq_high = 0; + irq_count = platform_irq_count(pdev); + if (irq_count < 0) + return irq_count; + + if (irq_count > 1) { + port->irq_high = platform_get_irq(pdev, 1); + if (port->irq_high < 0) + port->irq_high = 0; + } port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 5e5437a2c607..c4a314c68555 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -248,7 +248,10 @@ static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset) u32 dir; dir = readl(port->base + PINCTRL_DOE(port)); - return !(dir & mask); + if (dir & mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static const struct platform_device_id mxs_gpio_ids[] = { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index d0f27084a942..3bd8adaeed9e 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -805,8 +805,10 @@ static int omap_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { struct gpio_bank *bank = gpiochip_get_data(chip); - return !!(readl_relaxed(bank->base + bank->regs->direction) & - BIT(offset)); + if (readl_relaxed(bank->base + bank->regs->direction) & BIT(offset)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int omap_gpio_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index de5d1383f28d..82122c3c688a 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -449,7 +449,10 @@ static int pca953x_gpio_get_direction(struct gpio_chip *gc, unsigned off) if (ret < 0) return ret; - return !!(reg_val & bit); + if (reg_val & bit) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static void pca953x_gpio_set_multiple(struct gpio_chip *gc, diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 5aa136a6a3e0..df51dd08bdfe 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -61,9 +61,9 @@ static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { if (offset > 15) - return 1; + return GPIO_LINE_DIRECTION_IN; - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int idio_16_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c index 52f1647a46fd..44c1e4fc489f 100644 --- a/drivers/gpio/gpio-pcie-idio-24.c +++ b/drivers/gpio/gpio-pcie-idio-24.c @@ -104,15 +104,18 @@ static int idio_24_gpio_get_direction(struct gpio_chip *chip, /* FET Outputs */ if (offset < 24) - return 0; + return GPIO_LINE_DIRECTION_OUT; /* Isolated Inputs */ if (offset < 48) - return 1; + return GPIO_LINE_DIRECTION_IN; /* TTL/CMOS I/O */ /* OUT MODE = 1 when TTL/CMOS Output Mode is set */ - return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask); + if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int idio_24_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index f809a5a8e9eb..1331b2a94679 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -65,7 +65,7 @@ static int pisosr_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { /* This device always input */ - return 1; + return GPIO_LINE_DIRECTION_IN; } static int pisosr_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 722ce5cf861e..5df7782e348f 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -63,7 +63,10 @@ static int pl061_get_direction(struct gpio_chip *gc, unsigned offset) { struct pl061 *pl061 = gpiochip_get_data(gc); - return !(readb(pl061->base + GPIODIR) & BIT(offset)); + if (readb(pl061->base + GPIODIR) & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-raspberrypi-exp.c b/drivers/gpio/gpio-raspberrypi-exp.c index b77ea16ffa03..bb100e0124e6 100644 --- a/drivers/gpio/gpio-raspberrypi-exp.c +++ b/drivers/gpio/gpio-raspberrypi-exp.c @@ -147,7 +147,10 @@ static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off) get.gpio); return ret ? ret : -EIO; } - return !get.direction; + if (get.direction) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 187984d26f47..f800b250971c 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -279,7 +279,10 @@ static int gpio_rcar_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpio_rcar_priv *p = gpiochip_get_data(chip); - return !(gpio_rcar_read(p, INOUTSEL) & BIT(offset)); + if (gpio_rcar_read(p, INOUTSEL) & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) @@ -483,7 +486,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) gpio_chip->ngpio = npins; irq_chip = &p->irq_chip; - irq_chip->name = name; + irq_chip->name = "gpio-rcar"; irq_chip->parent_device = dev; irq_chip->irq_mask = gpio_rcar_irq_disable; irq_chip->irq_unmask = gpio_rcar_irq_enable; diff --git a/drivers/gpio/gpio-rda.c b/drivers/gpio/gpio-rda.c new file mode 100644 index 000000000000..28dcbb58b76b --- /dev/null +++ b/drivers/gpio/gpio-rda.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * RDA Micro GPIO driver + * + * Copyright (C) 2012 RDA Micro Inc. + * Copyright (C) 2019 Manivannan Sadhasivam + */ + +#include <linux/bitops.h> +#include <linux/gpio/driver.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +#define RDA_GPIO_OEN_VAL 0x00 +#define RDA_GPIO_OEN_SET_OUT 0x04 +#define RDA_GPIO_OEN_SET_IN 0x08 +#define RDA_GPIO_VAL 0x0c +#define RDA_GPIO_SET 0x10 +#define RDA_GPIO_CLR 0x14 +#define RDA_GPIO_INT_CTRL_SET 0x18 +#define RDA_GPIO_INT_CTRL_CLR 0x1c +#define RDA_GPIO_INT_CLR 0x20 +#define RDA_GPIO_INT_STATUS 0x24 + +#define RDA_GPIO_IRQ_RISE_SHIFT 0 +#define RDA_GPIO_IRQ_FALL_SHIFT 8 +#define RDA_GPIO_DEBOUCE_SHIFT 16 +#define RDA_GPIO_LEVEL_SHIFT 24 + +#define RDA_GPIO_IRQ_MASK 0xff + +/* Each bank consists of 32 GPIOs */ +#define RDA_GPIO_BANK_NR 32 + +struct rda_gpio { + struct gpio_chip chip; + void __iomem *base; + spinlock_t lock; + struct irq_chip irq_chip; + int irq; +}; + +static inline void rda_gpio_update(struct gpio_chip *chip, unsigned int offset, + u16 reg, int val) +{ + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&rda_gpio->lock, flags); + tmp = readl_relaxed(base + reg); + + if (val) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + + writel_relaxed(tmp, base + reg); + spin_unlock_irqrestore(&rda_gpio->lock, flags); +} + +static void rda_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + u32 offset = irqd_to_hwirq(data); + u32 value; + + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); +} + +static void rda_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + + rda_gpio_update(chip, offset, RDA_GPIO_INT_CLR, 1); +} + +static int rda_gpio_set_irq(struct gpio_chip *chip, u32 offset, + unsigned int flow_type) +{ + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + void __iomem *base = rda_gpio->base; + u32 value; + + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + /* Set rising edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_EDGE_FALLING: + /* Set falling edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_EDGE_BOTH: + /* Set both edge trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + value |= BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + + /* Switch to edge trigger interrupt */ + value = BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_CLR); + break; + + case IRQ_TYPE_LEVEL_HIGH: + /* Set high level trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_RISE_SHIFT; + + /* Switch to level trigger interrupt */ + value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + break; + + case IRQ_TYPE_LEVEL_LOW: + /* Set low level trigger */ + value = BIT(offset) << RDA_GPIO_IRQ_FALL_SHIFT; + + /* Switch to level trigger interrupt */ + value |= BIT(offset) << RDA_GPIO_LEVEL_SHIFT; + writel_relaxed(value, base + RDA_GPIO_INT_CTRL_SET); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void rda_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + u32 trigger = irqd_get_trigger_type(data); + + rda_gpio_set_irq(chip, offset, trigger); +} + +static int rda_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(data); + u32 offset = irqd_to_hwirq(data); + int ret; + + ret = rda_gpio_set_irq(chip, offset, flow_type); + if (ret) + return ret; + + if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + irq_set_handler_locked(data, handle_level_irq); + else if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + irq_set_handler_locked(data, handle_edge_irq); + + return 0; +} + +static void rda_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_desc_get_handler_data(desc); + struct irq_chip *ic = irq_desc_get_chip(desc); + struct rda_gpio *rda_gpio = gpiochip_get_data(chip); + unsigned long status; + u32 n, girq; + + chained_irq_enter(ic, desc); + + status = readl_relaxed(rda_gpio->base + RDA_GPIO_INT_STATUS); + /* Only lower 8 bits are capable of generating interrupts */ + status &= RDA_GPIO_IRQ_MASK; + + for_each_set_bit(n, &status, RDA_GPIO_BANK_NR) { + girq = irq_find_mapping(chip->irq.domain, n); + generic_handle_irq(girq); + } + + chained_irq_exit(ic, desc); +} + +static int rda_gpio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct gpio_irq_chip *girq; + struct rda_gpio *rda_gpio; + u32 ngpios; + int ret; + + rda_gpio = devm_kzalloc(dev, sizeof(*rda_gpio), GFP_KERNEL); + if (!rda_gpio) + return -ENOMEM; + + ret = device_property_read_u32(dev, "ngpios", &ngpios); + if (ret < 0) + return ret; + + /* + * Not all ports have interrupt capability. For instance, on + * RDA8810PL, GPIOC doesn't support interrupt. So we must handle + * those also. + */ + rda_gpio->irq = platform_get_irq(pdev, 0); + + rda_gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rda_gpio->base)) + return PTR_ERR(rda_gpio->base); + + spin_lock_init(&rda_gpio->lock); + + ret = bgpio_init(&rda_gpio->chip, dev, 4, + rda_gpio->base + RDA_GPIO_VAL, + rda_gpio->base + RDA_GPIO_SET, + rda_gpio->base + RDA_GPIO_CLR, + rda_gpio->base + RDA_GPIO_OEN_SET_OUT, + rda_gpio->base + RDA_GPIO_OEN_SET_IN, + BGPIOF_READ_OUTPUT_REG_SET); + if (ret) { + dev_err(dev, "bgpio_init failed\n"); + return ret; + } + + rda_gpio->chip.label = dev_name(dev); + rda_gpio->chip.ngpio = ngpios; + rda_gpio->chip.base = -1; + rda_gpio->chip.parent = dev; + rda_gpio->chip.of_node = np; + + if (rda_gpio->irq >= 0) { + rda_gpio->irq_chip.name = "rda-gpio", + rda_gpio->irq_chip.irq_ack = rda_gpio_irq_ack, + rda_gpio->irq_chip.irq_mask = rda_gpio_irq_mask, + rda_gpio->irq_chip.irq_unmask = rda_gpio_irq_unmask, + rda_gpio->irq_chip.irq_set_type = rda_gpio_irq_set_type, + rda_gpio->irq_chip.flags = IRQCHIP_SKIP_SET_WAKE, + + girq = &rda_gpio->chip.irq; + girq->chip = &rda_gpio->irq_chip; + girq->handler = handle_bad_irq; + girq->default_type = IRQ_TYPE_NONE; + girq->parent_handler = rda_gpio_irq_handler; + girq->parent_handler_data = rda_gpio; + girq->num_parents = 1; + girq->parents = devm_kcalloc(dev, 1, + sizeof(*girq->parents), + GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + girq->parents[0] = rda_gpio->irq; + } + + platform_set_drvdata(pdev, rda_gpio); + + return devm_gpiochip_add_data(dev, &rda_gpio->chip, rda_gpio); +} + +static const struct of_device_id rda_gpio_of_match[] = { + { .compatible = "rda,8810pl-gpio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rda_gpio_of_match); + +static struct platform_driver rda_gpio_driver = { + .probe = rda_gpio_probe, + .driver = { + .name = "rda-gpio", + .of_match_table = rda_gpio_of_match, + }, +}; + +module_platform_driver_probe(rda_gpio_driver, rda_gpio_probe); + +MODULE_DESCRIPTION("RDA Micro GPIO driver"); +MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c index fdc7a9d5b382..d35169bde25a 100644 --- a/drivers/gpio/gpio-reg.c +++ b/drivers/gpio/gpio-reg.c @@ -26,7 +26,8 @@ static int gpio_reg_get_direction(struct gpio_chip *gc, unsigned offset) { struct gpio_reg *r = to_gpio_reg(gc); - return r->direction & BIT(offset) ? 1 : 0; + return r->direction & BIT(offset) ? GPIO_LINE_DIRECTION_IN : + GPIO_LINE_DIRECTION_OUT; } static int gpio_reg_direction_output(struct gpio_chip *gc, unsigned offset, diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 46b7cf23fb0f..edff5e81489f 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -53,7 +53,10 @@ static int sa1100_get_direction(struct gpio_chip *chip, unsigned offset) { void __iomem *gpdr = sa1100_gpio_chip(chip)->membase + R_GPDR; - return !(readl_relaxed(gpdr) & BIT(offset)); + if (readl_relaxed(gpdr) & BIT(offset)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int sa1100_direction_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-sama5d2-piobu.c b/drivers/gpio/gpio-sama5d2-piobu.c index 7d718557092e..b04c561f3280 100644 --- a/drivers/gpio/gpio-sama5d2-piobu.c +++ b/drivers/gpio/gpio-sama5d2-piobu.c @@ -119,7 +119,8 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip, if (ret < 0) return ret; - return (ret == PIOBU_IN) ? 1 : 0; + return (ret == PIOBU_IN) ? GPIO_LINE_DIRECTION_IN : + GPIO_LINE_DIRECTION_OUT; } /** @@ -154,9 +155,9 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin) /* if pin is input, read value from PDS else read from SOD */ int ret = sama5d2_piobu_get_direction(chip, pin); - if (ret == 1) + if (ret == GPIO_LINE_DIRECTION_IN) ret = sama5d2_piobu_read_value(chip, pin, PIOBU_PDS); - else if (!ret) + else if (ret == GPIO_LINE_DIRECTION_OUT) ret = sama5d2_piobu_read_value(chip, pin, PIOBU_SOD); if (ret < 0) diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c index fb143f28c386..c65f35b68202 100644 --- a/drivers/gpio/gpio-sch.c +++ b/drivers/gpio/gpio-sch.c @@ -127,7 +127,10 @@ static int sch_gpio_get_direction(struct gpio_chip *gc, unsigned gpio_num) { struct sch_gpio *sch = gpiochip_get_data(gc); - return sch_gpio_reg_get(sch, gpio_num, GIO); + if (sch_gpio_reg_get(sch, gpio_num, GIO)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static const struct gpio_chip sch_gpio_chip = { diff --git a/drivers/gpio/gpio-sch311x.c b/drivers/gpio/gpio-sch311x.c index 8ecf336c9af4..da01e1cad7cb 100644 --- a/drivers/gpio/gpio-sch311x.c +++ b/drivers/gpio/gpio-sch311x.c @@ -228,7 +228,10 @@ static int sch311x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) data = inb(block->runtime_reg + block->config_regs[offset]); spin_unlock(&block->lock); - return !!(data & SCH311X_GPIO_CONF_DIR); + if (data & SCH311X_GPIO_CONF_DIR) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int sch311x_gpio_set_config(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c index 006a7e6a75f2..311f66757b92 100644 --- a/drivers/gpio/gpio-siox.c +++ b/drivers/gpio/gpio-siox.c @@ -203,9 +203,9 @@ static int gpio_siox_direction_output(struct gpio_chip *chip, static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned int offset) { if (offset < 12) - return 1; /* input */ + return GPIO_LINE_DIRECTION_IN; else - return 0; /* output */ + return GPIO_LINE_DIRECTION_OUT; } static int gpio_siox_probe(struct siox_device *sdevice) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 994d542daf53..542706a852e6 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -84,7 +84,10 @@ static int stmpe_gpio_get_direction(struct gpio_chip *chip, if (ret < 0) return ret; - return !(ret & mask); + if (ret & mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int stmpe_gpio_direction_output(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 75b1135b383a..6be0684cfa49 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -97,7 +97,10 @@ static int tc3589x_gpio_get_direction(struct gpio_chip *chip, if (ret < 0) return ret; - return !(ret & BIT(pos)); + if (ret & BIT(pos)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int tc3589x_gpio_set_config(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 8a01d3694b28..6fdfe4c5303e 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -215,7 +215,10 @@ static int tegra_gpio_get_direction(struct gpio_chip *chip, oe = tegra_gpio_readl(tgi, GPIO_OE(tgi, offset)); - return !(oe & pin_mask); + if (oe & pin_mask) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int tegra_gpio_set_debounce(struct gpio_chip *chip, unsigned int offset, diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index ef40fbe923cf..55b43b7ce88d 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -15,6 +15,14 @@ #include <dt-bindings/gpio/tegra186-gpio.h> #include <dt-bindings/gpio/tegra194-gpio.h> +/* security registers */ +#define TEGRA186_GPIO_CTL_SCR 0x0c +#define TEGRA186_GPIO_CTL_SCR_SEC_WEN BIT(28) +#define TEGRA186_GPIO_CTL_SCR_SEC_REN BIT(27) + +#define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4) + +/* control registers */ #define TEGRA186_GPIO_ENABLE_CONFIG 0x00 #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) #define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1) @@ -24,6 +32,7 @@ #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE (0x3 << 2) #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK (0x3 << 2) #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL BIT(4) +#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE BIT(5) #define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT BIT(6) #define TEGRA186_GPIO_DEBOUNCE_CONTROL 0x04 @@ -44,15 +53,16 @@ struct tegra_gpio_port { const char *name; - unsigned int offset; + unsigned int bank; + unsigned int port; unsigned int pins; - unsigned int irq; }; struct tegra_gpio_soc { const struct tegra_gpio_port *ports; unsigned int num_ports; const char *name; + unsigned int instance; }; struct tegra_gpio { @@ -63,6 +73,7 @@ struct tegra_gpio { const struct tegra_gpio_soc *soc; + void __iomem *secure; void __iomem *base; }; @@ -89,12 +100,15 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, unsigned int pin) { const struct tegra_gpio_port *port; + unsigned int offset; port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; - return gpio->base + port->offset + pin * 0x20; + offset = port->bank * 0x1000 + port->port * 0x200; + + return gpio->base + offset + pin * 0x20; } static int tegra186_gpio_get_direction(struct gpio_chip *chip, @@ -110,9 +124,9 @@ static int tegra186_gpio_get_direction(struct gpio_chip *chip, value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); if (value & TEGRA186_GPIO_ENABLE_CONFIG_OUT) - return 0; + return GPIO_LINE_DIRECTION_OUT; - return 1; + return GPIO_LINE_DIRECTION_IN; } static int tegra186_gpio_direction_input(struct gpio_chip *chip, @@ -204,6 +218,42 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); } +static int tegra186_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + u32 debounce, value; + void __iomem *base; + + base = tegra186_gpio_get_base(gpio, offset); + if (base == NULL) + return -ENXIO; + + if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + return -ENOTSUPP; + + debounce = pinconf_to_config_argument(config); + + /* + * The Tegra186 GPIO controller supports a maximum of 255 ms debounce + * time. + */ + if (debounce > 255000) + return -EINVAL; + + debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); + + value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); + writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); + + value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE; + writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + return 0; +} + static int tegra186_gpio_of_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) @@ -327,7 +377,7 @@ static int tegra186_irq_set_type(struct irq_data *data, unsigned int type) else irq_set_handler_locked(data, handle_edge_irq); - return 0; + return irq_chip_set_type_parent(data, type); } static void tegra186_gpio_irq(struct irq_desc *desc) @@ -342,12 +392,14 @@ static void tegra186_gpio_irq(struct irq_desc *desc) for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; - void __iomem *base = gpio->base + port->offset; unsigned int pin, irq; unsigned long value; + void __iomem *base; - /* skip ports that are not associated with this controller */ - if (parent != gpio->irq[port->irq]) + base = gpio->base + port->bank * 0x1000 + port->port * 0x200; + + /* skip ports that are not associated with this bank */ + if (parent != gpio->irq[port->bank]) goto skip; value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); @@ -367,46 +419,119 @@ skip: chained_irq_exit(chip, desc); } -static int tegra186_gpio_irq_domain_xlate(struct irq_domain *domain, - struct device_node *np, - const u32 *spec, unsigned int size, - unsigned long *hwirq, - unsigned int *type) +static int tegra186_gpio_irq_domain_translate(struct irq_domain *domain, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) { struct tegra_gpio *gpio = gpiochip_get_data(domain->host_data); unsigned int port, pin, i, offset = 0; - if (size < 2) + if (WARN_ON(gpio->gpio.of_gpio_n_cells < 2)) + return -EINVAL; + + if (WARN_ON(fwspec->param_count < gpio->gpio.of_gpio_n_cells)) return -EINVAL; - port = spec[0] / 8; - pin = spec[0] % 8; + port = fwspec->param[0] / 8; + pin = fwspec->param[0] % 8; - if (port >= gpio->soc->num_ports) { - dev_err(gpio->gpio.parent, "invalid port number: %u\n", port); + if (port >= gpio->soc->num_ports) return -EINVAL; - } for (i = 0; i < port; i++) offset += gpio->soc->ports[i].pins; - *type = spec[1] & IRQ_TYPE_SENSE_MASK; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; *hwirq = offset + pin; return 0; } -static const struct irq_domain_ops tegra186_gpio_irq_domain_ops = { - .map = gpiochip_irq_map, - .unmap = gpiochip_irq_unmap, - .xlate = tegra186_gpio_irq_domain_xlate, +static void tegra186_gpio_populate_parent_fwspec(struct gpio_chip *chip, + struct irq_fwspec *fwspec, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + + fwspec->param_count = 3; + fwspec->param[0] = gpio->soc->instance; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; +} + +static int tegra186_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int hwirq, + unsigned int type, + unsigned int *parent_hwirq, + unsigned int *parent_type) +{ + *parent_hwirq = chip->irq.child_offset_to_irq(chip, hwirq); + *parent_type = type; + + return 0; +} + +static unsigned int tegra186_gpio_child_offset_to_irq(struct gpio_chip *chip, + unsigned int offset) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + unsigned int i; + + for (i = 0; i < gpio->soc->num_ports; i++) { + if (offset < gpio->soc->ports[i].pins) + break; + + offset -= gpio->soc->ports[i].pins; + } + + return offset + i * 8; +} + +static const struct of_device_id tegra186_pmc_of_match[] = { + { .compatible = "nvidia,tegra186-pmc" }, + { .compatible = "nvidia,tegra194-pmc" }, + { /* sentinel */ } }; +static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +{ + unsigned int i, j; + u32 value; + + for (i = 0; i < gpio->soc->num_ports; i++) { + const struct tegra_gpio_port *port = &gpio->soc->ports[i]; + unsigned int offset, p = port->port; + void __iomem *base; + + base = gpio->secure + port->bank * 0x1000 + 0x800; + + value = readl(base + TEGRA186_GPIO_CTL_SCR); + + /* + * For controllers that haven't been locked down yet, make + * sure to program the default interrupt route mapping. + */ + if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 && + (value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) { + for (j = 0; j < 8; j++) { + offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); + + value = readl(base + offset); + value = BIT(port->pins) - 1; + writel(value, base + offset); + } + } + } +} + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; struct gpio_irq_chip *irq; struct tegra_gpio *gpio; + struct device_node *np; char **names; int err; @@ -416,6 +541,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->soc = of_device_get_match_data(&pdev->dev); + gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); + if (IS_ERR(gpio->secure)) + return PTR_ERR(gpio->secure); + gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); @@ -447,6 +576,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.direction_output = tegra186_gpio_direction_output; gpio->gpio.get = tegra186_gpio_get, gpio->gpio.set = tegra186_gpio_set; + gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.base = -1; @@ -485,10 +615,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->intc.irq_mask = tegra186_irq_mask; gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; + gpio->intc.irq_set_wake = irq_chip_set_wake_parent; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; - irq->domain_ops = &tegra186_gpio_irq_domain_ops; + irq->fwnode = of_node_to_fwnode(pdev->dev.of_node); + irq->child_to_parent_hwirq = tegra186_gpio_child_to_parent_hwirq; + irq->populate_parent_fwspec = tegra186_gpio_populate_parent_fwspec; + irq->child_offset_to_irq = tegra186_gpio_child_offset_to_irq; + irq->child_irq_domain_ops.translate = tegra186_gpio_irq_domain_translate; irq->handler = handle_simple_irq; irq->default_type = IRQ_TYPE_NONE; irq->parent_handler = tegra186_gpio_irq; @@ -496,6 +631,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev) irq->num_parents = gpio->num_irq; irq->parents = gpio->irq; + np = of_find_matching_node(NULL, tegra186_pmc_of_match); + if (np) { + irq->parent_domain = irq_find_host(np); + of_node_put(np); + + if (!irq->parent_domain) + return -EPROBE_DEFER; + } + + tegra186_gpio_init_route_mapping(gpio); + irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) @@ -505,7 +651,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) const struct tegra_gpio_port *port = &gpio->soc->ports[i]; for (j = 0; j < port->pins; j++) - irq->map[offset + j] = irq->parents[port->irq]; + irq->map[offset + j] = irq->parents[port->bank]; offset += port->pins; } @@ -524,136 +670,140 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } -#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA186_MAIN_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA186_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra186_main_ports[] = { - TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2), - TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3), - TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3), - TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3), - TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2), - TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2), - TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4), - TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1), - TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0), - TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5), - TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5), - TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1), - TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5), - TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0), - TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0), - TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4), - TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), - TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), - TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0), - TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1), - TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), - TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), - TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), + TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7), + TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7), + TEGRA186_MAIN_GPIO_PORT( C, 3, 1, 7), + TEGRA186_MAIN_GPIO_PORT( D, 3, 2, 6), + TEGRA186_MAIN_GPIO_PORT( E, 2, 1, 8), + TEGRA186_MAIN_GPIO_PORT( F, 2, 2, 6), + TEGRA186_MAIN_GPIO_PORT( G, 4, 1, 6), + TEGRA186_MAIN_GPIO_PORT( H, 1, 0, 7), + TEGRA186_MAIN_GPIO_PORT( I, 0, 4, 8), + TEGRA186_MAIN_GPIO_PORT( J, 5, 0, 8), + TEGRA186_MAIN_GPIO_PORT( K, 5, 1, 1), + TEGRA186_MAIN_GPIO_PORT( L, 1, 1, 8), + TEGRA186_MAIN_GPIO_PORT( M, 5, 3, 6), + TEGRA186_MAIN_GPIO_PORT( N, 0, 0, 7), + TEGRA186_MAIN_GPIO_PORT( O, 0, 1, 4), + TEGRA186_MAIN_GPIO_PORT( P, 4, 0, 7), + TEGRA186_MAIN_GPIO_PORT( Q, 0, 2, 6), + TEGRA186_MAIN_GPIO_PORT( R, 0, 5, 6), + TEGRA186_MAIN_GPIO_PORT( T, 0, 3, 4), + TEGRA186_MAIN_GPIO_PORT( X, 1, 2, 8), + TEGRA186_MAIN_GPIO_PORT( Y, 1, 3, 7), + TEGRA186_MAIN_GPIO_PORT(BB, 2, 3, 2), + TEGRA186_MAIN_GPIO_PORT(CC, 5, 2, 4), }; static const struct tegra_gpio_soc tegra186_main_soc = { .num_ports = ARRAY_SIZE(tegra186_main_ports), .ports = tegra186_main_ports, .name = "tegra186-gpio", + .instance = 0, }; -#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA186_AON_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA186_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra186_aon_ports[] = { - TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0), - TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0), - TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0), - TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0), - TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0), - TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0), - TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0), - TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0), + TEGRA186_AON_GPIO_PORT( S, 0, 1, 5), + TEGRA186_AON_GPIO_PORT( U, 0, 2, 6), + TEGRA186_AON_GPIO_PORT( V, 0, 4, 8), + TEGRA186_AON_GPIO_PORT( W, 0, 5, 8), + TEGRA186_AON_GPIO_PORT( Z, 0, 7, 4), + TEGRA186_AON_GPIO_PORT(AA, 0, 6, 8), + TEGRA186_AON_GPIO_PORT(EE, 0, 3, 3), + TEGRA186_AON_GPIO_PORT(FF, 0, 0, 5), }; static const struct tegra_gpio_soc tegra186_aon_soc = { .num_ports = ARRAY_SIZE(tegra186_aon_ports), .ports = tegra186_aon_ports, .name = "tegra186-gpio-aon", + .instance = 1, }; -#define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA194_MAIN_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA194_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra194_main_ports[] = { - TEGRA194_MAIN_GPIO_PORT( A, 0x1400, 8, 1), - TEGRA194_MAIN_GPIO_PORT( B, 0x4e00, 2, 4), - TEGRA194_MAIN_GPIO_PORT( C, 0x4600, 8, 4), - TEGRA194_MAIN_GPIO_PORT( D, 0x4800, 4, 4), - TEGRA194_MAIN_GPIO_PORT( E, 0x4a00, 8, 4), - TEGRA194_MAIN_GPIO_PORT( F, 0x4c00, 6, 4), - TEGRA194_MAIN_GPIO_PORT( G, 0x4000, 8, 4), - TEGRA194_MAIN_GPIO_PORT( H, 0x4200, 8, 4), - TEGRA194_MAIN_GPIO_PORT( I, 0x4400, 5, 4), - TEGRA194_MAIN_GPIO_PORT( J, 0x5200, 6, 5), - TEGRA194_MAIN_GPIO_PORT( K, 0x3000, 8, 3), - TEGRA194_MAIN_GPIO_PORT( L, 0x3200, 4, 3), - TEGRA194_MAIN_GPIO_PORT( M, 0x2600, 8, 2), - TEGRA194_MAIN_GPIO_PORT( N, 0x2800, 3, 2), - TEGRA194_MAIN_GPIO_PORT( O, 0x5000, 6, 5), - TEGRA194_MAIN_GPIO_PORT( P, 0x2a00, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Q, 0x2c00, 8, 2), - TEGRA194_MAIN_GPIO_PORT( R, 0x2e00, 6, 2), - TEGRA194_MAIN_GPIO_PORT( S, 0x3600, 8, 3), - TEGRA194_MAIN_GPIO_PORT( T, 0x3800, 8, 3), - TEGRA194_MAIN_GPIO_PORT( U, 0x3a00, 1, 3), - TEGRA194_MAIN_GPIO_PORT( V, 0x1000, 8, 1), - TEGRA194_MAIN_GPIO_PORT( W, 0x1200, 2, 1), - TEGRA194_MAIN_GPIO_PORT( X, 0x2000, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Y, 0x2200, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Z, 0x2400, 8, 2), - TEGRA194_MAIN_GPIO_PORT(FF, 0x3400, 2, 3), - TEGRA194_MAIN_GPIO_PORT(GG, 0x0000, 2, 0) + TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8), + TEGRA194_MAIN_GPIO_PORT( B, 4, 7, 2), + TEGRA194_MAIN_GPIO_PORT( C, 4, 3, 8), + TEGRA194_MAIN_GPIO_PORT( D, 4, 4, 4), + TEGRA194_MAIN_GPIO_PORT( E, 4, 5, 8), + TEGRA194_MAIN_GPIO_PORT( F, 4, 6, 6), + TEGRA194_MAIN_GPIO_PORT( G, 4, 0, 8), + TEGRA194_MAIN_GPIO_PORT( H, 4, 1, 8), + TEGRA194_MAIN_GPIO_PORT( I, 4, 2, 5), + TEGRA194_MAIN_GPIO_PORT( J, 5, 1, 6), + TEGRA194_MAIN_GPIO_PORT( K, 3, 0, 8), + TEGRA194_MAIN_GPIO_PORT( L, 3, 1, 4), + TEGRA194_MAIN_GPIO_PORT( M, 2, 3, 8), + TEGRA194_MAIN_GPIO_PORT( N, 2, 4, 3), + TEGRA194_MAIN_GPIO_PORT( O, 5, 0, 6), + TEGRA194_MAIN_GPIO_PORT( P, 2, 5, 8), + TEGRA194_MAIN_GPIO_PORT( Q, 2, 6, 8), + TEGRA194_MAIN_GPIO_PORT( R, 2, 7, 6), + TEGRA194_MAIN_GPIO_PORT( S, 3, 3, 8), + TEGRA194_MAIN_GPIO_PORT( T, 3, 4, 8), + TEGRA194_MAIN_GPIO_PORT( U, 3, 5, 1), + TEGRA194_MAIN_GPIO_PORT( V, 1, 0, 8), + TEGRA194_MAIN_GPIO_PORT( W, 1, 1, 2), + TEGRA194_MAIN_GPIO_PORT( X, 2, 0, 8), + TEGRA194_MAIN_GPIO_PORT( Y, 2, 1, 8), + TEGRA194_MAIN_GPIO_PORT( Z, 2, 2, 8), + TEGRA194_MAIN_GPIO_PORT(FF, 3, 2, 2), + TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2) }; static const struct tegra_gpio_soc tegra194_main_soc = { .num_ports = ARRAY_SIZE(tegra194_main_ports), .ports = tegra194_main_ports, .name = "tegra194-gpio", + .instance = 0, }; -#define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA194_AON_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA194_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra194_aon_ports[] = { - TEGRA194_AON_GPIO_PORT(AA, 0x0600, 8, 0), - TEGRA194_AON_GPIO_PORT(BB, 0x0800, 4, 0), - TEGRA194_AON_GPIO_PORT(CC, 0x0200, 8, 0), - TEGRA194_AON_GPIO_PORT(DD, 0x0400, 3, 0), - TEGRA194_AON_GPIO_PORT(EE, 0x0000, 7, 0) + TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8), + TEGRA194_AON_GPIO_PORT(BB, 0, 4, 4), + TEGRA194_AON_GPIO_PORT(CC, 0, 1, 8), + TEGRA194_AON_GPIO_PORT(DD, 0, 2, 3), + TEGRA194_AON_GPIO_PORT(EE, 0, 0, 7) }; static const struct tegra_gpio_soc tegra194_aon_soc = { .num_ports = ARRAY_SIZE(tegra194_aon_ports), .ports = tegra194_aon_ports, .name = "tegra194-gpio-aon", + .instance = 1, }; static const struct of_device_id tegra186_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c index ddad5c7ea617..d08d86a22b1f 100644 --- a/drivers/gpio/gpio-thunderx.c +++ b/drivers/gpio/gpio-thunderx.c @@ -169,7 +169,10 @@ static int thunderx_gpio_get_direction(struct gpio_chip *chip, unsigned int line bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line)); - return !(bit_cfg & GPIO_BIT_CFG_TX_OE); + if (bit_cfg & GPIO_BIT_CFG_TX_OE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int thunderx_gpio_set_config(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index c8b34d787eed..99d5a84a9129 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -39,7 +39,7 @@ static int tpic2810_get_direction(struct gpio_chip *chip, unsigned offset) { /* This device always output */ - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int tpic2810_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tps65086.c b/drivers/gpio/gpio-tps65086.c index 2eea98ff4ea3..1e9d8262d0ff 100644 --- a/drivers/gpio/gpio-tps65086.c +++ b/drivers/gpio/gpio-tps65086.c @@ -21,7 +21,7 @@ static int tps65086_gpio_get_direction(struct gpio_chip *chip, unsigned offset) { /* This device is output only */ - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int tps65086_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-tps65912.c b/drivers/gpio/gpio-tps65912.c index 3ad68bd78282..510d9ed9fd2a 100644 --- a/drivers/gpio/gpio-tps65912.c +++ b/drivers/gpio/gpio-tps65912.c @@ -32,9 +32,9 @@ static int tps65912_gpio_get_direction(struct gpio_chip *gc, return ret; if (val & GPIO_CFG_MASK) - return 0; + return GPIO_LINE_DIRECTION_OUT; else - return 1; + return GPIO_LINE_DIRECTION_IN; } static int tps65912_gpio_direction_input(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-tps68470.c b/drivers/gpio/gpio-tps68470.c index aff6e504c666..f7f5f770e0fb 100644 --- a/drivers/gpio/gpio-tps68470.c +++ b/drivers/gpio/gpio-tps68470.c @@ -47,7 +47,6 @@ static int tps68470_gpio_get(struct gpio_chip *gc, unsigned int offset) return !!(val & BIT(offset)); } -/* Return 0 if output, 1 if input */ static int tps68470_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { @@ -57,7 +56,7 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc, /* rest are always outputs */ if (offset >= TPS68470_N_REGULAR_GPIO) - return 0; + return GPIO_LINE_DIRECTION_OUT; ret = regmap_read(regmap, TPS68470_GPIO_CTL_REG_A(offset), &val); if (ret) { @@ -67,7 +66,8 @@ static int tps68470_gpio_get_direction(struct gpio_chip *gc, } val &= TPS68470_GPIO_MODE_MASK; - return val >= TPS68470_GPIO_MODE_OUT_CMOS ? 0 : 1; + return val >= TPS68470_GPIO_MODE_OUT_CMOS ? GPIO_LINE_DIRECTION_OUT : + GPIO_LINE_DIRECTION_IN; } static void tps68470_gpio_set(struct gpio_chip *gc, unsigned int offset, diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c index a3109bcaa0ac..5022e0ad0fae 100644 --- a/drivers/gpio/gpio-tqmx86.c +++ b/drivers/gpio/gpio-tqmx86.c @@ -101,7 +101,10 @@ static int tqmx86_gpio_direction_output(struct gpio_chip *chip, static int tqmx86_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - return !!(TQMX86_DIR_INPUT_MASK & BIT(offset)); + if (TQMX86_DIR_INPUT_MASK & BIT(offset)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static void tqmx86_gpio_irq_mask(struct irq_data *data) diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c index 1da8d0586329..d885032cf814 100644 --- a/drivers/gpio/gpio-ts4900.c +++ b/drivers/gpio/gpio-ts4900.c @@ -44,7 +44,10 @@ static int ts4900_gpio_get_direction(struct gpio_chip *chip, regmap_read(priv->regmap, offset, ®); - return !(reg & TS4900_GPIO_OE); + if (reg & TS4900_GPIO_OE) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int ts4900_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index fbfb648d3502..de249726230e 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -165,10 +165,10 @@ static int twl4030_get_gpio_direction(int gpio) if (ret < 0) return ret; - /* 1 = output, but gpiolib semantics are inverse so invert */ - ret = !(ret & d_msk); + if (ret & d_msk) + return GPIO_LINE_DIRECTION_OUT; - return ret; + return GPIO_LINE_DIRECTION_IN; } static int twl4030_set_gpio_dataout(int gpio, int enable) @@ -380,10 +380,10 @@ static int twl_get_direction(struct gpio_chip *chip, unsigned offset) { struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); /* - * Default 0 = output + * Default GPIO_LINE_DIRECTION_OUT * LED GPIOs >= TWL4030_GPIO_MAX are always output */ - int ret = 0; + int ret = GPIO_LINE_DIRECTION_OUT; mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) { diff --git a/drivers/gpio/gpio-twl6040.c b/drivers/gpio/gpio-twl6040.c index c845b2ff1f43..648fb418d775 100644 --- a/drivers/gpio/gpio-twl6040.c +++ b/drivers/gpio/gpio-twl6040.c @@ -34,8 +34,7 @@ static int twl6040gpo_get(struct gpio_chip *chip, unsigned offset) static int twl6040gpo_get_direction(struct gpio_chip *chip, unsigned offset) { - /* This means "out" */ - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c index 93cdcc41e9fb..bd203e8fa58e 100644 --- a/drivers/gpio/gpio-uniphier.c +++ b/drivers/gpio/gpio-uniphier.c @@ -113,7 +113,10 @@ static int uniphier_gpio_offset_read(struct gpio_chip *chip, static int uniphier_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - return uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DIR); + if (uniphier_gpio_offset_read(chip, offset, UNIPHIER_GPIO_PORT_DIR)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int uniphier_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 444fe9e7f04a..8b481b3c1ebe 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -170,13 +170,16 @@ static int wcove_gpio_get_direction(struct gpio_chip *chip, unsigned int gpio) int ret, reg = to_reg(gpio, CTRL_OUT); if (reg < 0) - return 0; + return GPIO_LINE_DIRECTION_OUT; ret = regmap_read(wg->regmap, reg, &val); if (ret) return ret; - return !(val & CTLO_DIR_OUT); + if (val & CTLO_DIR_OUT) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } static int wcove_gpio_get(struct gpio_chip *chip, unsigned int gpio) diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index e0ef66b6a237..fe456bea81f6 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -56,7 +56,10 @@ static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) const unsigned port = offset / 8; const unsigned mask = BIT(offset % 8); - return !!(ws16c48gpio->io_state[port] & mask); + if (ws16c48gpio->io_state[port] & mask) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c index 2918363884de..532b0df8a1f2 100644 --- a/drivers/gpio/gpio-xgene.c +++ b/drivers/gpio/gpio-xgene.c @@ -80,7 +80,10 @@ static int xgene_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) bank_offset = GPIO_SET_DR_OFFSET + GPIO_BANK_OFFSET(offset); bit_offset = GPIO_BIT_OFFSET(offset); - return !!(ioread32(chip->base + bank_offset) & BIT(bit_offset)); + if (ioread32(chip->base + bank_offset) & BIT(bit_offset)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int xgene_gpio_dir_in(struct gpio_chip *gc, unsigned int offset) @@ -155,28 +158,16 @@ static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume); static int xgene_gpio_probe(struct platform_device *pdev) { - struct resource *res; struct xgene_gpio *gpio; int err = 0; gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); - if (!gpio) { - err = -ENOMEM; - goto err; - } + if (!gpio) + return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - err = -EINVAL; - goto err; - } - - gpio->base = devm_ioremap_nocache(&pdev->dev, res->start, - resource_size(res)); - if (!gpio->base) { - err = -ENOMEM; - goto err; - } + gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); gpio->chip.ngpio = XGENE_MAX_GPIOS; @@ -196,14 +187,11 @@ static int xgene_gpio_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "failed to register gpiochip.\n"); - goto err; + return err; } dev_info(&pdev->dev, "X-Gene GPIO driver registered.\n"); return 0; -err: - dev_err(&pdev->dev, "X-Gene GPIO driver registration failed.\n"); - return err; } static const struct of_device_id xgene_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c new file mode 100644 index 000000000000..773e5c24309e --- /dev/null +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017 Broadcom + */ + +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> + +#define IPROC_CCA_INT_F_GPIOINT BIT(0) +#define IPROC_CCA_INT_STS 0x20 +#define IPROC_CCA_INT_MASK 0x24 + +#define IPROC_GPIO_CCA_DIN 0x0 +#define IPROC_GPIO_CCA_DOUT 0x4 +#define IPROC_GPIO_CCA_OUT_EN 0x8 +#define IPROC_GPIO_CCA_INT_LEVEL 0x10 +#define IPROC_GPIO_CCA_INT_LEVEL_MASK 0x14 +#define IPROC_GPIO_CCA_INT_EVENT 0x18 +#define IPROC_GPIO_CCA_INT_EVENT_MASK 0x1C +#define IPROC_GPIO_CCA_INT_EDGE 0x24 + +struct iproc_gpio_chip { + struct irq_chip irqchip; + struct gpio_chip gc; + spinlock_t lock; + struct device *dev; + void __iomem *base; + void __iomem *intr; +}; + +static inline struct iproc_gpio_chip * +to_iproc_gpio(struct gpio_chip *gc) +{ + return container_of(gc, struct iproc_gpio_chip, gc); +} + +static void iproc_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 irq_type, event_status = 0; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_status |= BIT(pin); + writel_relaxed(event_status, + chip->base + IPROC_GPIO_CCA_INT_EVENT); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 int_mask, irq_type, event_mask; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_mask |= 1 << pin; + writel_relaxed(event_mask, + chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + } else { + int_mask |= 1 << pin; + writel_relaxed(int_mask, + chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static void iproc_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 irq_type, int_mask, event_mask; + + spin_lock_irqsave(&chip->lock, flags); + irq_type = irq_get_trigger_type(irq); + event_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + int_mask = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + + if (irq_type & IRQ_TYPE_EDGE_BOTH) { + event_mask &= ~BIT(pin); + writel_relaxed(event_mask, + chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + } else { + int_mask &= ~BIT(pin); + writel_relaxed(int_mask, + chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + } + spin_unlock_irqrestore(&chip->lock, flags); +} + +static int iproc_gpio_irq_set_type(struct irq_data *d, u32 type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int pin = d->hwirq; + unsigned long flags; + u32 irq = d->irq; + u32 event_pol, int_pol; + int ret = 0; + + spin_lock_irqsave(&chip->lock, flags); + switch (type & IRQ_TYPE_SENSE_MASK) { + case IRQ_TYPE_EDGE_RISING: + event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); + event_pol &= ~BIT(pin); + writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); + break; + case IRQ_TYPE_EDGE_FALLING: + event_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EDGE); + event_pol |= BIT(pin); + writel_relaxed(event_pol, chip->base + IPROC_GPIO_CCA_INT_EDGE); + break; + case IRQ_TYPE_LEVEL_HIGH: + int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + int_pol &= ~BIT(pin); + writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); + break; + case IRQ_TYPE_LEVEL_LOW: + int_pol = readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + int_pol |= BIT(pin); + writel_relaxed(int_pol, chip->base + IPROC_GPIO_CCA_INT_LEVEL); + break; + default: + /* should not come here */ + ret = -EINVAL; + goto out_unlock; + } + + if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(irq_get_irq_data(irq), handle_level_irq); + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(irq_get_irq_data(irq), handle_edge_irq); + +out_unlock: + spin_unlock_irqrestore(&chip->lock, flags); + + return ret; +} + +static irqreturn_t iproc_gpio_irq_handler(int irq, void *data) +{ + struct gpio_chip *gc = (struct gpio_chip *)data; + struct iproc_gpio_chip *chip = to_iproc_gpio(gc); + int bit; + unsigned long int_bits = 0; + u32 int_status; + + /* go through the entire GPIOs and handle all interrupts */ + int_status = readl_relaxed(chip->intr + IPROC_CCA_INT_STS); + if (int_status & IPROC_CCA_INT_F_GPIOINT) { + u32 event, level; + + /* Get level and edge interrupts */ + event = + readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT_MASK); + event &= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_EVENT); + level = readl_relaxed(chip->base + IPROC_GPIO_CCA_DIN); + level ^= readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL); + level &= + readl_relaxed(chip->base + IPROC_GPIO_CCA_INT_LEVEL_MASK); + int_bits = level | event; + + for_each_set_bit(bit, &int_bits, gc->ngpio) + generic_handle_irq(irq_linear_revmap(gc->irq.domain, bit)); + } + + return int_bits ? IRQ_HANDLED : IRQ_NONE; +} + +static int iproc_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = pdev->dev.of_node; + struct iproc_gpio_chip *chip; + u32 num_gpios; + int irq, ret; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = dev; + platform_set_drvdata(pdev, chip); + spin_lock_init(&chip->lock); + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + ret = bgpio_init(&chip->gc, dev, 4, + chip->base + IPROC_GPIO_CCA_DIN, + chip->base + IPROC_GPIO_CCA_DOUT, + NULL, + chip->base + IPROC_GPIO_CCA_OUT_EN, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init GPIO chip\n"); + return ret; + } + + chip->gc.label = dev_name(dev); + if (of_property_read_u32(dn, "ngpios", &num_gpios)) + chip->gc.ngpio = num_gpios; + + irq = platform_get_irq(pdev, 0); + if (irq > 0) { + struct gpio_irq_chip *girq; + struct irq_chip *irqc; + u32 val; + + irqc = &chip->irqchip; + irqc->name = dev_name(dev); + irqc->irq_ack = iproc_gpio_irq_ack; + irqc->irq_mask = iproc_gpio_irq_mask; + irqc->irq_unmask = iproc_gpio_irq_unmask; + irqc->irq_set_type = iproc_gpio_irq_set_type; + + chip->intr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(chip->intr)) + return PTR_ERR(chip->intr); + + /* Enable GPIO interrupts for CCA GPIO */ + val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); + val |= IPROC_CCA_INT_F_GPIOINT; + writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); + + /* + * Directly request the irq here instead of passing + * a flow-handler to gpiochip_set_chained_irqchip, + * because the irq is shared. + */ + ret = devm_request_irq(dev, irq, iproc_gpio_irq_handler, + IRQF_SHARED, chip->gc.label, &chip->gc); + if (ret) { + dev_err(dev, "Fail to request IRQ%d: %d\n", irq, ret); + return ret; + } + + girq = &chip->gc.irq; + girq->chip = irqc; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + } + + ret = devm_gpiochip_add_data(dev, &chip->gc, chip); + if (ret) { + dev_err(dev, "unable to add GPIO chip\n"); + return ret; + } + + return 0; +} + +static int __exit iproc_gpio_remove(struct platform_device *pdev) +{ + struct iproc_gpio_chip *chip; + + chip = platform_get_drvdata(pdev); + if (!chip) + return -ENODEV; + + if (chip->intr) { + u32 val; + + val = readl_relaxed(chip->intr + IPROC_CCA_INT_MASK); + val &= ~IPROC_CCA_INT_F_GPIOINT; + writel_relaxed(val, chip->intr + IPROC_CCA_INT_MASK); + } + + return 0; +} + +static const struct of_device_id bcm_iproc_gpio_of_match[] = { + { .compatible = "brcm,iproc-gpio-cca" }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_gpio_of_match); + +static struct platform_driver bcm_iproc_gpio_driver = { + .driver = { + .name = "iproc-xgs-gpio", + .of_match_table = bcm_iproc_gpio_of_match, + }, + .probe = iproc_gpio_probe, + .remove = iproc_gpio_remove, +}; + +module_platform_driver(bcm_iproc_gpio_driver); + +MODULE_DESCRIPTION("XGS IPROC GPIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index 05f1998c11a4..31b5072b2df0 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -83,7 +83,10 @@ static int xra1403_get_direction(struct gpio_chip *chip, unsigned int offset) if (ret) return ret; - return !!(val & BIT(offset % 8)); + if (val & BIT(offset % 8)) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int xra1403_get(struct gpio_chip *chip, unsigned int offset) diff --git a/drivers/gpio/gpio-xtensa.c b/drivers/gpio/gpio-xtensa.c index 43d3fa5f511a..08d7c3b32038 100644 --- a/drivers/gpio/gpio-xtensa.c +++ b/drivers/gpio/gpio-xtensa.c @@ -72,7 +72,7 @@ static inline void disable_cp(unsigned long flags, unsigned long cpenable) static int xtensa_impwire_get_direction(struct gpio_chip *gc, unsigned offset) { - return 1; /* input only */ + return GPIO_LINE_DIRECTION_IN; /* input only */ } static int xtensa_impwire_get_value(struct gpio_chip *gc, unsigned offset) @@ -95,7 +95,7 @@ static void xtensa_impwire_set_value(struct gpio_chip *gc, unsigned offset, static int xtensa_expstate_get_direction(struct gpio_chip *gc, unsigned offset) { - return 0; /* output only */ + return GPIO_LINE_DIRECTION_OUT; /* output only */ } static int xtensa_expstate_get_value(struct gpio_chip *gc, unsigned offset) diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index cd475ff4bcad..4c3f6370eab4 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -360,7 +360,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin, * * This function returns the direction of the specified GPIO. * - * Return: 0 for output, 1 for input + * Return: GPIO_LINE_DIRECTION_OUT or GPIO_LINE_DIRECTION_IN */ static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) { @@ -372,7 +372,10 @@ static int zynq_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) reg = readl_relaxed(gpio->base_addr + ZYNQ_GPIO_DIRM_OFFSET(bank_num)); - return !(reg & BIT(bank_pin_num)); + if (reg & BIT(bank_pin_num)) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; } /** diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 59ccfd24627d..d30e57dc755c 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -194,6 +194,7 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) acpi_gpiochip_request_irq(acpi_gpio, event); } +/* Always returns AE_OK so that we keep looping over the resources */ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, void *context) { @@ -230,19 +231,25 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", GPIO_ACTIVE_HIGH, GPIOD_IN); if (IS_ERR(desc)) { - dev_err(chip->parent, "Failed to request GPIO\n"); - return AE_ERROR; + dev_err(chip->parent, + "Failed to request GPIO for pin 0x%04X, err %ld\n", + pin, PTR_ERR(desc)); + return AE_OK; } ret = gpiochip_lock_as_irq(chip, pin); if (ret) { - dev_err(chip->parent, "Failed to lock GPIO as interrupt\n"); + dev_err(chip->parent, + "Failed to lock GPIO pin 0x%04X as interrupt, err %d\n", + pin, ret); goto fail_free_desc; } irq = gpiod_to_irq(desc); if (irq < 0) { - dev_err(chip->parent, "Failed to translate GPIO to IRQ\n"); + dev_err(chip->parent, + "Failed to translate GPIO pin 0x%04X to IRQ, err %d\n", + pin, irq); goto fail_unlock_irq; } @@ -287,7 +294,7 @@ fail_unlock_irq: fail_free_desc: gpiochip_free_own_desc(desc); - return AE_ERROR; + return AE_OK; } /** diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 80ea49f570f4..dc27b1a88e93 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -27,7 +27,7 @@ * This is used by external users of of_gpio_count() from <linux/of_gpio.h> * * FIXME: get rid of those external users by converting them to GPIO - * descriptors and let them all use gpiod_get_count() + * descriptors and let them all use gpiod_count() */ int of_gpio_get_count(struct device *dev, const char *con_id) { @@ -84,8 +84,9 @@ static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip, /** * of_gpio_need_valid_mask() - figure out if the OF GPIO driver needs * to set the .valid_mask - * @dev: the device for the GPIO provider - * @return: true if the valid mask needs to be set + * @gc: the target gpio_chip + * + * Return: true if the valid mask needs to be set */ bool of_gpio_need_valid_mask(const struct gpio_chip *gc) { @@ -134,18 +135,20 @@ static void of_gpio_flags_quirks(struct device_node *np, (!(strcmp(propname, "enable-gpio") && strcmp(propname, "enable-gpios")) && of_device_is_compatible(np, "regulator-gpio")))) { + bool active_low = !of_property_read_bool(np, + "enable-active-high"); /* * The regulator GPIO handles are specified such that the * presence or absence of "enable-active-high" solely controls * the polarity of the GPIO line. Any phandle flags must * be actively ignored. */ - if (*flags & OF_GPIO_ACTIVE_LOW) { + if ((*flags & OF_GPIO_ACTIVE_LOW) && !active_low) { pr_warn("%s GPIO handle specifies active low - ignored\n", of_node_full_name(np)); *flags &= ~OF_GPIO_ACTIVE_LOW; } - if (!of_property_read_bool(np, "enable-active-high")) + if (active_low) *flags |= OF_GPIO_ACTIVE_LOW; } /* @@ -882,16 +885,13 @@ int of_gpiochip_add(struct gpio_chip *chip) of_node_get(chip->of_node); ret = of_gpiochip_scan_gpios(chip); - if (ret) { + if (ret) of_node_put(chip->of_node); - gpiochip_remove_pin_ranges(chip); - } return ret; } void of_gpiochip_remove(struct gpio_chip *chip) { - gpiochip_remove_pin_ranges(chip); of_node_put(chip->of_node); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fb33ff6fc1a9..9913886ede90 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -390,6 +390,14 @@ static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip) gpiochip->valid_mask = NULL; } +static int gpiochip_add_pin_ranges(struct gpio_chip *gc) +{ + if (gc->add_pin_ranges) + return gc->add_pin_ranges(gc); + + return 0; +} + bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip, unsigned int offset) { @@ -422,9 +430,127 @@ struct linehandle_state { (GPIOHANDLE_REQUEST_INPUT | \ GPIOHANDLE_REQUEST_OUTPUT | \ GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ + GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ + GPIOHANDLE_REQUEST_BIAS_DISABLE | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) +static int linehandle_validate_flags(u32 flags) +{ + /* Return an error if an unknown flag is set */ + if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; + + /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((flags & GPIOHANDLE_REQUEST_INPUT) && + (flags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ + if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && + ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) + return -EINVAL; + + /* Bias flags only allowed for input or output mode. */ + if (!((flags & GPIOHANDLE_REQUEST_INPUT) || + (flags & GPIOHANDLE_REQUEST_OUTPUT)) && + ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + return 0; +} + +static void linehandle_configure_flag(unsigned long *flagsp, + u32 bit, bool active) +{ + if (active) + set_bit(bit, flagsp); + else + clear_bit(bit, flagsp); +} + +static long linehandle_set_config(struct linehandle_state *lh, + void __user *ip) +{ + struct gpiohandle_config gcnf; + struct gpio_desc *desc; + int i, ret; + u32 lflags; + unsigned long *flagsp; + + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + + lflags = gcnf.flags; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + for (i = 0; i < lh->numdescs; i++) { + desc = lh->descs[i]; + flagsp = &desc->flags; + + linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, + lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); + + linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, + lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); + + linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, + lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); + + linehandle_configure_flag(flagsp, FLAG_PULL_UP, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); + + linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); + + linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, + lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!gcnf.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + return ret; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + return ret; + } + } + return 0; +} + static long linehandle_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -475,6 +601,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, lh->descs, NULL, vals); + } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { + return linehandle_set_config(lh, ip); } return -EINVAL; } @@ -526,32 +654,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) lflags = handlereq.flags; - /* Return an error if an unknown flag is set */ - if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) - return -EINVAL; - - /* - * Do not allow both INPUT & OUTPUT flags to be set as they are - * contradictory. - */ - if ((lflags & GPIOHANDLE_REQUEST_INPUT) && - (lflags & GPIOHANDLE_REQUEST_OUTPUT)) - return -EINVAL; - - /* - * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If - * the hardware actually supports enabling both at the same time the - * electrical result would be disastrous. - */ - if ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ - if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) && - ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) - return -EINVAL; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; lh = kzalloc(sizeof(*lh), GFP_KERNEL); if (!lh) @@ -593,6 +698,12 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); ret = gpiod_set_transitory(desc, false); if (ret < 0) @@ -895,6 +1006,32 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (copy_from_user(&eventreq, ip, sizeof(eventreq))) return -EFAULT; + offset = eventreq.lineoffset; + lflags = eventreq.handleflags; + eflags = eventreq.eventflags; + + if (offset >= gdev->ngpio) + return -EINVAL; + + /* Return an error if a unknown flag is set */ + if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || + (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) + return -EINVAL; + + /* This is just wrong: we don't look for events on output lines */ + if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || + (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + le = kzalloc(sizeof(*le), GFP_KERNEL); if (!le) return -ENOMEM; @@ -912,30 +1049,6 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) } } - offset = eventreq.lineoffset; - lflags = eventreq.handleflags; - eflags = eventreq.eventflags; - - if (offset >= gdev->ngpio) { - ret = -EINVAL; - goto out_free_label; - } - - /* Return an error if a unknown flag is set */ - if ((lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) || - (eflags & ~GPIOEVENT_REQUEST_VALID_FLAGS)) { - ret = -EINVAL; - goto out_free_label; - } - - /* This is just wrong: we don't look for events on output lines */ - if ((lflags & GPIOHANDLE_REQUEST_OUTPUT) || - (lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) { - ret = -EINVAL; - goto out_free_label; - } - desc = &gdev->descs[offset]; ret = gpiod_request(desc, le->label); if (ret) @@ -945,6 +1058,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); ret = gpiod_direction_input(desc); if (ret) @@ -1098,6 +1217,12 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_DISABLE; + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_UP; if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; @@ -1403,15 +1528,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, } } + ret = gpiochip_add_pin_ranges(chip); + if (ret) + goto err_remove_of_chip; + acpi_gpiochip_add(chip); machine_gpiochip_add(chip); - ret = gpiochip_irqchip_init_hw(chip); + ret = gpiochip_irqchip_init_valid_mask(chip); if (ret) goto err_remove_acpi_chip; - ret = gpiochip_irqchip_init_valid_mask(chip); + ret = gpiochip_irqchip_init_hw(chip); if (ret) goto err_remove_acpi_chip; @@ -1444,6 +1573,7 @@ err_remove_of_chip: gpiochip_free_hogs(chip); of_gpiochip_remove(chip); err_free_gpiochip_mask: + gpiochip_remove_pin_ranges(chip); gpiochip_free_valid_mask(chip); err_remove_from_list: spin_lock_irqsave(&gpio_lock, flags); @@ -1499,8 +1629,8 @@ void gpiochip_remove(struct gpio_chip *chip) gdev->chip = NULL; gpiochip_irqchip_remove(chip); acpi_gpiochip_remove(chip); - gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); + gpiochip_remove_pin_ranges(chip); gpiochip_free_valid_mask(chip); /* * We accept no more calls into the driver from this point, so @@ -1539,7 +1669,7 @@ static void devm_gpio_chip_release(struct device *dev, void *res) } /** - * devm_gpiochip_add_data() - Resource manager gpiochip_add_data() + * devm_gpiochip_add_data() - Resource managed gpiochip_add_data() * @dev: pointer to the device that gpio_chip belongs to. * @chip: the chip to register, with chip->base initialized * @data: driver-private data associated with this chip @@ -2790,6 +2920,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_OPEN_DRAIN, &desc->flags); clear_bit(FLAG_OPEN_SOURCE, &desc->flags); + clear_bit(FLAG_PULL_UP, &desc->flags); + clear_bit(FLAG_PULL_DOWN, &desc->flags); + clear_bit(FLAG_BIAS_DISABLE, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = true; } @@ -2916,6 +3049,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, unsigned arg; switch (mode) { + case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: arg = 1; @@ -2929,6 +3063,26 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; } +static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) +{ + int bias = 0; + int ret = 0; + + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + bias = PIN_CONFIG_BIAS_DISABLE; + else if (test_bit(FLAG_PULL_UP, &desc->flags)) + bias = PIN_CONFIG_BIAS_PULL_UP; + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + bias = PIN_CONFIG_BIAS_PULL_DOWN; + + if (bias) { + ret = gpio_set_config(chip, gpio_chip_hwgpio(desc), bias); + if (ret != -ENOTSUPP) + return ret; + } + return 0; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2973,15 +3127,10 @@ int gpiod_direction_input(struct gpio_desc *desc) __func__); return -EIO; } - if (ret == 0) + if (ret == 0) { clear_bit(FLAG_IS_OUT, &desc->flags); - - if (test_bit(FLAG_PULL_UP, &desc->flags)) - gpio_set_config(chip, gpio_chip_hwgpio(desc), - PIN_CONFIG_BIAS_PULL_UP); - else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - gpio_set_config(chip, gpio_chip_hwgpio(desc), - PIN_CONFIG_BIAS_PULL_DOWN); + ret = gpio_set_bias(chip, desc); + } trace_gpio_direction(desc_to_gpio(desc), 1, ret); @@ -3111,6 +3260,9 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) } set_output_value: + ret = gpio_set_bias(gc, desc); + if (ret) + return ret; return gpiod_direction_output_raw_commit(desc, value); set_output_flag: @@ -4742,9 +4894,9 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, pr_info("GPIO line %d (%s) hogged as %s%s\n", desc_to_gpio(desc), name, - (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", - (dflags&GPIOD_FLAGS_BIT_DIR_OUT) ? - (dflags&GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low":""); + (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", + (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? + (dflags & GPIOD_FLAGS_BIT_DIR_VAL) ? "/high" : "/low" : ""); return 0; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b8b10a409c7b..ca9bc1e4803c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -110,6 +110,7 @@ struct gpio_desc { #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ #define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ #define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ +#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ /* Connection label */ const char *label; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index bcc5d40a8d5f..0c229a92a24b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -967,6 +967,8 @@ struct amdgpu_device { struct mutex lock_reset; struct amdgpu_doorbell_index doorbell_index; + struct mutex notifier_lock; + int asic_reset_res; struct work_struct xgmi_reset_work; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index ae6f5446262c..7d35b5b66229 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -505,8 +505,7 @@ static void remove_kgd_mem_from_kfd_bo_list(struct kgd_mem *mem, * * Returns 0 for success, negative errno for errors. */ -static int init_user_pages(struct kgd_mem *mem, struct mm_struct *mm, - uint64_t user_addr) +static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr) { struct amdkfd_process_info *process_info = mem->process_info; struct amdgpu_bo *bo = mem->bo; @@ -1199,7 +1198,7 @@ int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu( add_kgd_mem_to_kfd_bo_list(*mem, avm->process_info, user_addr); if (user_addr) { - ret = init_user_pages(*mem, current->mm, user_addr); + ret = init_user_pages(*mem, user_addr); if (ret) goto allocate_init_user_pages_failed; } @@ -1744,6 +1743,10 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info, return ret; } + /* + * FIXME: Cannot ignore the return code, must hold + * notifier_lock + */ amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm); /* Mark the BO as valid unless it was invalidated diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index a169ff16277f..5ca905b4a0fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -538,8 +538,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, e->tv.num_shared = 2; amdgpu_bo_list_get_list(p->bo_list, &p->validated); - if (p->bo_list->first_userptr != p->bo_list->num_entries) - p->mn = amdgpu_mn_get(p->adev, AMDGPU_MN_TYPE_GFX); INIT_LIST_HEAD(&duplicates); amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd); @@ -1219,11 +1217,11 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, if (r) goto error_unlock; - /* No memory allocation is allowed while holding the mn lock. - * p->mn is hold until amdgpu_cs_submit is finished and fence is added - * to BOs. + /* No memory allocation is allowed while holding the notifier lock. + * The lock is held until amdgpu_cs_submit is finished and fence is + * added to BOs. */ - amdgpu_mn_lock(p->mn); + mutex_lock(&p->adev->notifier_lock); /* If userptr are invalidated after amdgpu_cs_parser_bos(), return * -EAGAIN, drmIoctl in libdrm will restart the amdgpu_cs_ioctl. @@ -1266,13 +1264,13 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); ttm_eu_fence_buffer_objects(&p->ticket, &p->validated, p->fence); - amdgpu_mn_unlock(p->mn); + mutex_unlock(&p->adev->notifier_lock); return 0; error_abort: drm_sched_job_cleanup(&job->base); - amdgpu_mn_unlock(p->mn); + mutex_unlock(&p->adev->notifier_lock); error_unlock: amdgpu_job_free(job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 4f76beafb2fa..c17505fba988 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2794,6 +2794,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, mutex_init(&adev->virt.vf_errors.lock); hash_init(adev->mn_hash); mutex_init(&adev->lock_reset); + mutex_init(&adev->notifier_lock); mutex_init(&adev->virt.dpm_mutex); mutex_init(&adev->psp.mutex); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index 392300f77b13..828b5167ff12 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -51,439 +51,107 @@ #include "amdgpu_amdkfd.h" /** - * struct amdgpu_mn_node + * amdgpu_mn_invalidate_gfx - callback to notify about mm change * - * @it: interval node defining start-last of the affected address range - * @bos: list of all BOs in the affected address range - * - * Manages all BOs which are affected of a certain range of address space. - */ -struct amdgpu_mn_node { - struct interval_tree_node it; - struct list_head bos; -}; - -/** - * amdgpu_mn_destroy - destroy the HMM mirror - * - * @work: previously sheduled work item - * - * Lazy destroys the notifier from a work item - */ -static void amdgpu_mn_destroy(struct work_struct *work) -{ - struct amdgpu_mn *amn = container_of(work, struct amdgpu_mn, work); - struct amdgpu_device *adev = amn->adev; - struct amdgpu_mn_node *node, *next_node; - struct amdgpu_bo *bo, *next_bo; - - mutex_lock(&adev->mn_lock); - down_write(&amn->lock); - hash_del(&amn->node); - rbtree_postorder_for_each_entry_safe(node, next_node, - &amn->objects.rb_root, it.rb) { - list_for_each_entry_safe(bo, next_bo, &node->bos, mn_list) { - bo->mn = NULL; - list_del_init(&bo->mn_list); - } - kfree(node); - } - up_write(&amn->lock); - mutex_unlock(&adev->mn_lock); - - hmm_mirror_unregister(&amn->mirror); - kfree(amn); -} - -/** - * amdgpu_hmm_mirror_release - callback to notify about mm destruction - * - * @mirror: the HMM mirror (mm) this callback is about - * - * Shedule a work item to lazy destroy HMM mirror. - */ -static void amdgpu_hmm_mirror_release(struct hmm_mirror *mirror) -{ - struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); - - INIT_WORK(&amn->work, amdgpu_mn_destroy); - schedule_work(&amn->work); -} - -/** - * amdgpu_mn_lock - take the write side lock for this notifier - * - * @mn: our notifier - */ -void amdgpu_mn_lock(struct amdgpu_mn *mn) -{ - if (mn) - down_write(&mn->lock); -} - -/** - * amdgpu_mn_unlock - drop the write side lock for this notifier - * - * @mn: our notifier - */ -void amdgpu_mn_unlock(struct amdgpu_mn *mn) -{ - if (mn) - up_write(&mn->lock); -} - -/** - * amdgpu_mn_read_lock - take the read side lock for this notifier - * - * @amn: our notifier - * @blockable: is the notifier blockable - */ -static int amdgpu_mn_read_lock(struct amdgpu_mn *amn, bool blockable) -{ - if (blockable) - down_read(&amn->lock); - else if (!down_read_trylock(&amn->lock)) - return -EAGAIN; - - return 0; -} - -/** - * amdgpu_mn_read_unlock - drop the read side lock for this notifier - * - * @amn: our notifier - */ -static void amdgpu_mn_read_unlock(struct amdgpu_mn *amn) -{ - up_read(&amn->lock); -} - -/** - * amdgpu_mn_invalidate_node - unmap all BOs of a node - * - * @node: the node with the BOs to unmap - * @start: start of address range affected - * @end: end of address range affected + * @mni: the range (mm) is about to update + * @range: details on the invalidation + * @cur_seq: Value to pass to mmu_interval_set_seq() * * Block for operations on BOs to finish and mark pages as accessed and * potentially dirty. */ -static void amdgpu_mn_invalidate_node(struct amdgpu_mn_node *node, - unsigned long start, - unsigned long end) +static bool amdgpu_mn_invalidate_gfx(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { - struct amdgpu_bo *bo; + struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); long r; - list_for_each_entry(bo, &node->bos, mn_list) { - - if (!amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, start, end)) - continue; - - r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, - true, false, MAX_SCHEDULE_TIMEOUT); - if (r <= 0) - DRM_ERROR("(%ld) failed to wait for user bo\n", r); - } -} - -/** - * amdgpu_mn_sync_pagetables_gfx - callback to notify about mm change - * - * @mirror: the hmm_mirror (mm) is about to update - * @update: the update start, end address - * - * Block for operations on BOs to finish and mark pages as accessed and - * potentially dirty. - */ -static int -amdgpu_mn_sync_pagetables_gfx(struct hmm_mirror *mirror, - const struct mmu_notifier_range *update) -{ - struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); - unsigned long start = update->start; - unsigned long end = update->end; - bool blockable = mmu_notifier_range_blockable(update); - struct interval_tree_node *it; + if (!mmu_notifier_range_blockable(range)) + return false; - /* notification is exclusive, but interval is inclusive */ - end -= 1; + mutex_lock(&adev->notifier_lock); - /* TODO we should be able to split locking for interval tree and - * amdgpu_mn_invalidate_node - */ - if (amdgpu_mn_read_lock(amn, blockable)) - return -EAGAIN; + mmu_interval_set_seq(mni, cur_seq); - it = interval_tree_iter_first(&amn->objects, start, end); - while (it) { - struct amdgpu_mn_node *node; - - if (!blockable) { - amdgpu_mn_read_unlock(amn); - return -EAGAIN; - } - - node = container_of(it, struct amdgpu_mn_node, it); - it = interval_tree_iter_next(it, start, end); - - amdgpu_mn_invalidate_node(node, start, end); - } - - amdgpu_mn_read_unlock(amn); - - return 0; -} - -/** - * amdgpu_mn_sync_pagetables_hsa - callback to notify about mm change - * - * @mirror: the hmm_mirror (mm) is about to update - * @update: the update start, end address - * - * We temporarily evict all BOs between start and end. This - * necessitates evicting all user-mode queues of the process. The BOs - * are restorted in amdgpu_mn_invalidate_range_end_hsa. - */ -static int -amdgpu_mn_sync_pagetables_hsa(struct hmm_mirror *mirror, - const struct mmu_notifier_range *update) -{ - struct amdgpu_mn *amn = container_of(mirror, struct amdgpu_mn, mirror); - unsigned long start = update->start; - unsigned long end = update->end; - bool blockable = mmu_notifier_range_blockable(update); - struct interval_tree_node *it; - - /* notification is exclusive, but interval is inclusive */ - end -= 1; - - if (amdgpu_mn_read_lock(amn, blockable)) - return -EAGAIN; - - it = interval_tree_iter_first(&amn->objects, start, end); - while (it) { - struct amdgpu_mn_node *node; - struct amdgpu_bo *bo; - - if (!blockable) { - amdgpu_mn_read_unlock(amn); - return -EAGAIN; - } - - node = container_of(it, struct amdgpu_mn_node, it); - it = interval_tree_iter_next(it, start, end); - - list_for_each_entry(bo, &node->bos, mn_list) { - struct kgd_mem *mem = bo->kfd_bo; - - if (amdgpu_ttm_tt_affect_userptr(bo->tbo.ttm, - start, end)) - amdgpu_amdkfd_evict_userptr(mem, amn->mm); - } - } - - amdgpu_mn_read_unlock(amn); - - return 0; + r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, true, false, + MAX_SCHEDULE_TIMEOUT); + mutex_unlock(&adev->notifier_lock); + if (r <= 0) + DRM_ERROR("(%ld) failed to wait for user bo\n", r); + return true; } -/* Low bits of any reasonable mm pointer will be unused due to struct - * alignment. Use these bits to make a unique key from the mm pointer - * and notifier type. - */ -#define AMDGPU_MN_KEY(mm, type) ((unsigned long)(mm) + (type)) - -static struct hmm_mirror_ops amdgpu_hmm_mirror_ops[] = { - [AMDGPU_MN_TYPE_GFX] = { - .sync_cpu_device_pagetables = amdgpu_mn_sync_pagetables_gfx, - .release = amdgpu_hmm_mirror_release - }, - [AMDGPU_MN_TYPE_HSA] = { - .sync_cpu_device_pagetables = amdgpu_mn_sync_pagetables_hsa, - .release = amdgpu_hmm_mirror_release - }, +static const struct mmu_interval_notifier_ops amdgpu_mn_gfx_ops = { + .invalidate = amdgpu_mn_invalidate_gfx, }; /** - * amdgpu_mn_get - create HMM mirror context + * amdgpu_mn_invalidate_hsa - callback to notify about mm change * - * @adev: amdgpu device pointer - * @type: type of MMU notifier context + * @mni: the range (mm) is about to update + * @range: details on the invalidation + * @cur_seq: Value to pass to mmu_interval_set_seq() * - * Creates a HMM mirror context for current->mm. + * We temporarily evict the BO attached to this range. This necessitates + * evicting all user-mode queues of the process. */ -struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev, - enum amdgpu_mn_type type) +static bool amdgpu_mn_invalidate_hsa(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { - struct mm_struct *mm = current->mm; - struct amdgpu_mn *amn; - unsigned long key = AMDGPU_MN_KEY(mm, type); - int r; - - mutex_lock(&adev->mn_lock); - if (down_write_killable(&mm->mmap_sem)) { - mutex_unlock(&adev->mn_lock); - return ERR_PTR(-EINTR); - } - - hash_for_each_possible(adev->mn_hash, amn, node, key) - if (AMDGPU_MN_KEY(amn->mm, amn->type) == key) - goto release_locks; - - amn = kzalloc(sizeof(*amn), GFP_KERNEL); - if (!amn) { - amn = ERR_PTR(-ENOMEM); - goto release_locks; - } - - amn->adev = adev; - amn->mm = mm; - init_rwsem(&amn->lock); - amn->type = type; - amn->objects = RB_ROOT_CACHED; - - amn->mirror.ops = &amdgpu_hmm_mirror_ops[type]; - r = hmm_mirror_register(&amn->mirror, mm); - if (r) - goto free_amn; + struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); - hash_add(adev->mn_hash, &amn->node, AMDGPU_MN_KEY(mm, type)); + if (!mmu_notifier_range_blockable(range)) + return false; -release_locks: - up_write(&mm->mmap_sem); - mutex_unlock(&adev->mn_lock); + mutex_lock(&adev->notifier_lock); - return amn; + mmu_interval_set_seq(mni, cur_seq); -free_amn: - up_write(&mm->mmap_sem); - mutex_unlock(&adev->mn_lock); - kfree(amn); + amdgpu_amdkfd_evict_userptr(bo->kfd_bo, bo->notifier.mm); + mutex_unlock(&adev->notifier_lock); - return ERR_PTR(r); + return true; } +static const struct mmu_interval_notifier_ops amdgpu_mn_hsa_ops = { + .invalidate = amdgpu_mn_invalidate_hsa, +}; + /** * amdgpu_mn_register - register a BO for notifier updates * * @bo: amdgpu buffer object * @addr: userptr addr we should monitor * - * Registers an HMM mirror for the given BO at the specified address. + * Registers a mmu_notifier for the given BO at the specified address. * Returns 0 on success, -ERRNO if anything goes wrong. */ int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) { - unsigned long end = addr + amdgpu_bo_size(bo) - 1; - struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); - enum amdgpu_mn_type type = - bo->kfd_bo ? AMDGPU_MN_TYPE_HSA : AMDGPU_MN_TYPE_GFX; - struct amdgpu_mn *amn; - struct amdgpu_mn_node *node = NULL, *new_node; - struct list_head bos; - struct interval_tree_node *it; - - amn = amdgpu_mn_get(adev, type); - if (IS_ERR(amn)) - return PTR_ERR(amn); - - new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); - if (!new_node) - return -ENOMEM; - - INIT_LIST_HEAD(&bos); - - down_write(&amn->lock); - - while ((it = interval_tree_iter_first(&amn->objects, addr, end))) { - kfree(node); - node = container_of(it, struct amdgpu_mn_node, it); - interval_tree_remove(&node->it, &amn->objects); - addr = min(it->start, addr); - end = max(it->last, end); - list_splice(&node->bos, &bos); - } - - if (!node) - node = new_node; - else - kfree(new_node); - - bo->mn = amn; - - node->it.start = addr; - node->it.last = end; - INIT_LIST_HEAD(&node->bos); - list_splice(&bos, &node->bos); - list_add(&bo->mn_list, &node->bos); - - interval_tree_insert(&node->it, &amn->objects); - - up_write(&amn->lock); - - return 0; + if (bo->kfd_bo) + return mmu_interval_notifier_insert(&bo->notifier, current->mm, + addr, amdgpu_bo_size(bo), + &amdgpu_mn_hsa_ops); + return mmu_interval_notifier_insert(&bo->notifier, current->mm, addr, + amdgpu_bo_size(bo), + &amdgpu_mn_gfx_ops); } /** - * amdgpu_mn_unregister - unregister a BO for HMM mirror updates + * amdgpu_mn_unregister - unregister a BO for notifier updates * * @bo: amdgpu buffer object * - * Remove any registration of HMM mirror updates from the buffer object. + * Remove any registration of mmu notifier updates from the buffer object. */ void amdgpu_mn_unregister(struct amdgpu_bo *bo) { - struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); - struct amdgpu_mn *amn; - struct list_head *head; - - mutex_lock(&adev->mn_lock); - - amn = bo->mn; - if (amn == NULL) { - mutex_unlock(&adev->mn_lock); + if (!bo->notifier.mm) return; - } - - down_write(&amn->lock); - - /* save the next list entry for later */ - head = bo->mn_list.next; - - bo->mn = NULL; - list_del_init(&bo->mn_list); - - if (list_empty(head)) { - struct amdgpu_mn_node *node; - - node = container_of(head, struct amdgpu_mn_node, bos); - interval_tree_remove(&node->it, &amn->objects); - kfree(node); - } - - up_write(&amn->lock); - mutex_unlock(&adev->mn_lock); -} - -/* flags used by HMM internal, not related to CPU/GPU PTE flags */ -static const uint64_t hmm_range_flags[HMM_PFN_FLAG_MAX] = { - (1 << 0), /* HMM_PFN_VALID */ - (1 << 1), /* HMM_PFN_WRITE */ - 0 /* HMM_PFN_DEVICE_PRIVATE */ -}; - -static const uint64_t hmm_range_values[HMM_PFN_VALUE_MAX] = { - 0xfffffffffffffffeUL, /* HMM_PFN_ERROR */ - 0, /* HMM_PFN_NONE */ - 0xfffffffffffffffcUL /* HMM_PFN_SPECIAL */ -}; - -void amdgpu_hmm_init_range(struct hmm_range *range) -{ - if (range) { - range->flags = hmm_range_flags; - range->values = hmm_range_values; - range->pfn_shift = PAGE_SHIFT; - } + mmu_interval_notifier_remove(&bo->notifier); + bo->notifier.mm = NULL; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h index b8ed68943625..a292238f75eb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.h @@ -30,63 +30,10 @@ #include <linux/workqueue.h> #include <linux/interval_tree.h> -enum amdgpu_mn_type { - AMDGPU_MN_TYPE_GFX, - AMDGPU_MN_TYPE_HSA, -}; - -/** - * struct amdgpu_mn - * - * @adev: amdgpu device pointer - * @mm: process address space - * @type: type of MMU notifier - * @work: destruction work item - * @node: hash table node to find structure by adev and mn - * @lock: rw semaphore protecting the notifier nodes - * @objects: interval tree containing amdgpu_mn_nodes - * @mirror: HMM mirror function support - * - * Data for each amdgpu device and process address space. - */ -struct amdgpu_mn { - /* constant after initialisation */ - struct amdgpu_device *adev; - struct mm_struct *mm; - enum amdgpu_mn_type type; - - /* only used on destruction */ - struct work_struct work; - - /* protected by adev->mn_lock */ - struct hlist_node node; - - /* objects protected by lock */ - struct rw_semaphore lock; - struct rb_root_cached objects; - -#ifdef CONFIG_HMM_MIRROR - /* HMM mirror */ - struct hmm_mirror mirror; -#endif -}; - #if defined(CONFIG_HMM_MIRROR) -void amdgpu_mn_lock(struct amdgpu_mn *mn); -void amdgpu_mn_unlock(struct amdgpu_mn *mn); -struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev, - enum amdgpu_mn_type type); int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr); void amdgpu_mn_unregister(struct amdgpu_bo *bo); -void amdgpu_hmm_init_range(struct hmm_range *range); #else -static inline void amdgpu_mn_lock(struct amdgpu_mn *mn) {} -static inline void amdgpu_mn_unlock(struct amdgpu_mn *mn) {} -static inline struct amdgpu_mn *amdgpu_mn_get(struct amdgpu_device *adev, - enum amdgpu_mn_type type) -{ - return NULL; -} static inline int amdgpu_mn_register(struct amdgpu_bo *bo, unsigned long addr) { DRM_WARN_ONCE("HMM_MIRROR kernel config option is not enabled, " diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 7e99f6c58c48..36dec51d1ef1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -30,6 +30,9 @@ #include <drm/amdgpu_drm.h> #include "amdgpu.h" +#ifdef CONFIG_MMU_NOTIFIER +#include <linux/mmu_notifier.h> +#endif #define AMDGPU_BO_INVALID_OFFSET LONG_MAX #define AMDGPU_BO_MAX_PLACEMENTS 3 @@ -101,10 +104,12 @@ struct amdgpu_bo { struct ttm_bo_kmap_obj dma_buf_vmap; struct amdgpu_mn *mn; - union { - struct list_head mn_list; - struct list_head shadow_list; - }; + +#ifdef CONFIG_MMU_NOTIFIER + struct mmu_interval_notifier notifier; +#endif + + struct list_head shadow_list; struct kgd_mem *kfd_bo; }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 61d9b7774d42..2616e2eafdeb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -35,6 +35,7 @@ #include <linux/hmm.h> #include <linux/pagemap.h> #include <linux/sched/task.h> +#include <linux/sched/mm.h> #include <linux/seq_file.h> #include <linux/slab.h> #include <linux/swap.h> @@ -769,6 +770,20 @@ struct amdgpu_ttm_tt { #endif }; +#ifdef CONFIG_DRM_AMDGPU_USERPTR +/* flags used by HMM internal, not related to CPU/GPU PTE flags */ +static const uint64_t hmm_range_flags[HMM_PFN_FLAG_MAX] = { + (1 << 0), /* HMM_PFN_VALID */ + (1 << 1), /* HMM_PFN_WRITE */ + 0 /* HMM_PFN_DEVICE_PRIVATE */ +}; + +static const uint64_t hmm_range_values[HMM_PFN_VALUE_MAX] = { + 0xfffffffffffffffeUL, /* HMM_PFN_ERROR */ + 0, /* HMM_PFN_NONE */ + 0xfffffffffffffffcUL /* HMM_PFN_SPECIAL */ +}; + /** * amdgpu_ttm_tt_get_user_pages - get device accessible pages that back user * memory and start HMM tracking CPU page table update @@ -776,85 +791,89 @@ struct amdgpu_ttm_tt { * Calling function must call amdgpu_ttm_tt_userptr_range_done() once and only * once afterwards to stop HMM tracking */ -#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) - -#define MAX_RETRY_HMM_RANGE_FAULT 16 - int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo, struct page **pages) { - struct hmm_mirror *mirror = bo->mn ? &bo->mn->mirror : NULL; struct ttm_tt *ttm = bo->tbo.ttm; struct amdgpu_ttm_tt *gtt = (void *)ttm; - struct mm_struct *mm = gtt->usertask->mm; unsigned long start = gtt->userptr; struct vm_area_struct *vma; struct hmm_range *range; + unsigned long timeout; + struct mm_struct *mm; unsigned long i; - uint64_t *pfns; int r = 0; - if (!mm) /* Happens during process shutdown */ - return -ESRCH; - - if (unlikely(!mirror)) { - DRM_DEBUG_DRIVER("Failed to get hmm_mirror\n"); - r = -EFAULT; - goto out; + mm = bo->notifier.mm; + if (unlikely(!mm)) { + DRM_DEBUG_DRIVER("BO is not registered?\n"); + return -EFAULT; } - vma = find_vma(mm, start); - if (unlikely(!vma || start < vma->vm_start)) { - r = -EFAULT; - goto out; - } - if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) && - vma->vm_file)) { - r = -EPERM; - goto out; - } + /* Another get_user_pages is running at the same time?? */ + if (WARN_ON(gtt->range)) + return -EFAULT; + + if (!mmget_not_zero(mm)) /* Happens during process shutdown */ + return -ESRCH; range = kzalloc(sizeof(*range), GFP_KERNEL); if (unlikely(!range)) { r = -ENOMEM; goto out; } + range->notifier = &bo->notifier; + range->flags = hmm_range_flags; + range->values = hmm_range_values; + range->pfn_shift = PAGE_SHIFT; + range->start = bo->notifier.interval_tree.start; + range->end = bo->notifier.interval_tree.last + 1; + range->default_flags = hmm_range_flags[HMM_PFN_VALID]; + if (!amdgpu_ttm_tt_is_readonly(ttm)) + range->default_flags |= range->flags[HMM_PFN_WRITE]; - pfns = kvmalloc_array(ttm->num_pages, sizeof(*pfns), GFP_KERNEL); - if (unlikely(!pfns)) { + range->pfns = kvmalloc_array(ttm->num_pages, sizeof(*range->pfns), + GFP_KERNEL); + if (unlikely(!range->pfns)) { r = -ENOMEM; goto out_free_ranges; } - amdgpu_hmm_init_range(range); - range->default_flags = range->flags[HMM_PFN_VALID]; - range->default_flags |= amdgpu_ttm_tt_is_readonly(ttm) ? - 0 : range->flags[HMM_PFN_WRITE]; - range->pfn_flags_mask = 0; - range->pfns = pfns; - range->start = start; - range->end = start + ttm->num_pages * PAGE_SIZE; - - hmm_range_register(range, mirror); + down_read(&mm->mmap_sem); + vma = find_vma(mm, start); + if (unlikely(!vma || start < vma->vm_start)) { + r = -EFAULT; + goto out_unlock; + } + if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) && + vma->vm_file)) { + r = -EPERM; + goto out_unlock; + } + up_read(&mm->mmap_sem); + timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); - /* - * Just wait for range to be valid, safe to ignore return value as we - * will use the return value of hmm_range_fault() below under the - * mmap_sem to ascertain the validity of the range. - */ - hmm_range_wait_until_valid(range, HMM_RANGE_DEFAULT_TIMEOUT); +retry: + range->notifier_seq = mmu_interval_read_begin(&bo->notifier); down_read(&mm->mmap_sem); r = hmm_range_fault(range, 0); up_read(&mm->mmap_sem); - - if (unlikely(r < 0)) + if (unlikely(r <= 0)) { + /* + * FIXME: This timeout should encompass the retry from + * mmu_interval_read_retry() as well. + */ + if ((r == 0 || r == -EBUSY) && !time_after(jiffies, timeout)) + goto retry; goto out_free_pfns; + } for (i = 0; i < ttm->num_pages; i++) { - pages[i] = hmm_device_entry_to_page(range, pfns[i]); + /* FIXME: The pages cannot be touched outside the notifier_lock */ + pages[i] = hmm_device_entry_to_page(range, range->pfns[i]); if (unlikely(!pages[i])) { pr_err("Page fault failed for pfn[%lu] = 0x%llx\n", - i, pfns[i]); + i, range->pfns[i]); r = -ENOMEM; goto out_free_pfns; @@ -862,15 +881,18 @@ int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo, struct page **pages) } gtt->range = range; + mmput(mm); return 0; +out_unlock: + up_read(&mm->mmap_sem); out_free_pfns: - hmm_range_unregister(range); - kvfree(pfns); + kvfree(range->pfns); out_free_ranges: kfree(range); out: + mmput(mm); return r; } @@ -895,15 +917,18 @@ bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm) "No user pages to check\n"); if (gtt->range) { - r = hmm_range_valid(gtt->range); - hmm_range_unregister(gtt->range); - + /* + * FIXME: Must always hold notifier_lock for this, and must + * not ignore the return code. + */ + r = mmu_interval_read_retry(gtt->range->notifier, + gtt->range->notifier_seq); kvfree(gtt->range->pfns); kfree(gtt->range); gtt->range = NULL; } - return r; + return !r; } #endif @@ -984,10 +1009,18 @@ static void amdgpu_ttm_tt_unpin_userptr(struct ttm_tt *ttm) sg_free_table(ttm->sg); #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) - if (gtt->range && - ttm->pages[0] == hmm_device_entry_to_page(gtt->range, - gtt->range->pfns[0])) - WARN_ONCE(1, "Missing get_user_page_done\n"); + if (gtt->range) { + unsigned long i; + + for (i = 0; i < ttm->num_pages; i++) { + if (ttm->pages[i] != + hmm_device_entry_to_page(gtt->range, + gtt->range->pfns[i])) + break; + } + + WARN((i == ttm->num_pages), "Missing get_user_page_done\n"); + } #endif } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 9af45d07515b..1544007af34a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -49,7 +49,7 @@ static const char kfd_dev_name[] = "kfd"; static const struct file_operations kfd_fops = { .owner = THIS_MODULE, .unlocked_ioctl = kfd_ioctl, - .compat_ioctl = kfd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = kfd_open, .mmap = kfd_mmap, }; diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index aa3198dc9903..6f6d6d1e60ae 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -285,8 +285,8 @@ static int tfp410_get_connector_properties(struct tfp410 *dvi) else dvi->connector_type = DRM_MODE_CONNECTOR_DVID; - dvi->hpd = fwnode_get_named_gpiod(&connector_node->fwnode, - "hpd-gpios", 0, GPIOD_IN, "hpd"); + dvi->hpd = fwnode_gpiod_get_index(&connector_node->fwnode, + "hpd", 0, GPIOD_IN, "hpd"); if (IS_ERR(dvi->hpd)) { ret = PTR_ERR(dvi->hpd); dvi->hpd = NULL; diff --git a/drivers/gpu/drm/nouveau/nouveau_svm.c b/drivers/gpu/drm/nouveau/nouveau_svm.c index 668d4bd0c118..df9bf1fd1bc0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_svm.c +++ b/drivers/gpu/drm/nouveau/nouveau_svm.c @@ -88,6 +88,7 @@ nouveau_ivmm_find(struct nouveau_svm *svm, u64 inst) } struct nouveau_svmm { + struct mmu_notifier notifier; struct nouveau_vmm *vmm; struct { unsigned long start; @@ -95,9 +96,6 @@ struct nouveau_svmm { } unmanaged; struct mutex mutex; - - struct mm_struct *mm; - struct hmm_mirror mirror; }; #define SVMM_DBG(s,f,a...) \ @@ -251,10 +249,11 @@ nouveau_svmm_invalidate(struct nouveau_svmm *svmm, u64 start, u64 limit) } static int -nouveau_svmm_sync_cpu_device_pagetables(struct hmm_mirror *mirror, - const struct mmu_notifier_range *update) +nouveau_svmm_invalidate_range_start(struct mmu_notifier *mn, + const struct mmu_notifier_range *update) { - struct nouveau_svmm *svmm = container_of(mirror, typeof(*svmm), mirror); + struct nouveau_svmm *svmm = + container_of(mn, struct nouveau_svmm, notifier); unsigned long start = update->start; unsigned long limit = update->end; @@ -264,6 +263,9 @@ nouveau_svmm_sync_cpu_device_pagetables(struct hmm_mirror *mirror, SVMM_DBG(svmm, "invalidate %016lx-%016lx", start, limit); mutex_lock(&svmm->mutex); + if (unlikely(!svmm->vmm)) + goto out; + if (limit > svmm->unmanaged.start && start < svmm->unmanaged.limit) { if (start < svmm->unmanaged.start) { nouveau_svmm_invalidate(svmm, start, @@ -273,19 +275,20 @@ nouveau_svmm_sync_cpu_device_pagetables(struct hmm_mirror *mirror, } nouveau_svmm_invalidate(svmm, start, limit); + +out: mutex_unlock(&svmm->mutex); return 0; } -static void -nouveau_svmm_release(struct hmm_mirror *mirror) +static void nouveau_svmm_free_notifier(struct mmu_notifier *mn) { + kfree(container_of(mn, struct nouveau_svmm, notifier)); } -static const struct hmm_mirror_ops -nouveau_svmm = { - .sync_cpu_device_pagetables = nouveau_svmm_sync_cpu_device_pagetables, - .release = nouveau_svmm_release, +static const struct mmu_notifier_ops nouveau_mn_ops = { + .invalidate_range_start = nouveau_svmm_invalidate_range_start, + .free_notifier = nouveau_svmm_free_notifier, }; void @@ -293,8 +296,10 @@ nouveau_svmm_fini(struct nouveau_svmm **psvmm) { struct nouveau_svmm *svmm = *psvmm; if (svmm) { - hmm_mirror_unregister(&svmm->mirror); - kfree(*psvmm); + mutex_lock(&svmm->mutex); + svmm->vmm = NULL; + mutex_unlock(&svmm->mutex); + mmu_notifier_put(&svmm->notifier); *psvmm = NULL; } } @@ -320,7 +325,7 @@ nouveau_svmm_init(struct drm_device *dev, void *data, mutex_lock(&cli->mutex); if (cli->svm.cli) { ret = -EBUSY; - goto done; + goto out_free; } /* Allocate a new GPU VMM that can support SVM (managed by the @@ -335,24 +340,26 @@ nouveau_svmm_init(struct drm_device *dev, void *data, .fault_replay = true, }, sizeof(struct gp100_vmm_v0), &cli->svm.vmm); if (ret) - goto done; - - /* Enable HMM mirroring of CPU address-space to VMM. */ - svmm->mm = get_task_mm(current); - down_write(&svmm->mm->mmap_sem); - svmm->mirror.ops = &nouveau_svmm; - ret = hmm_mirror_register(&svmm->mirror, svmm->mm); - if (ret == 0) { - cli->svm.svmm = svmm; - cli->svm.cli = cli; - } - up_write(&svmm->mm->mmap_sem); - mmput(svmm->mm); + goto out_free; -done: + down_write(¤t->mm->mmap_sem); + svmm->notifier.ops = &nouveau_mn_ops; + ret = __mmu_notifier_register(&svmm->notifier, current->mm); if (ret) - nouveau_svmm_fini(&svmm); + goto out_mm_unlock; + /* Note, ownership of svmm transfers to mmu_notifier */ + + cli->svm.svmm = svmm; + cli->svm.cli = cli; + up_write(¤t->mm->mmap_sem); mutex_unlock(&cli->mutex); + return 0; + +out_mm_unlock: + up_write(¤t->mm->mmap_sem); +out_free: + mutex_unlock(&cli->mutex); + kfree(svmm); return ret; } @@ -475,43 +482,90 @@ nouveau_svm_fault_cache(struct nouveau_svm *svm, fault->inst, fault->addr, fault->access); } -static inline bool -nouveau_range_done(struct hmm_range *range) +struct svm_notifier { + struct mmu_interval_notifier notifier; + struct nouveau_svmm *svmm; +}; + +static bool nouveau_svm_range_invalidate(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { - bool ret = hmm_range_valid(range); + struct svm_notifier *sn = + container_of(mni, struct svm_notifier, notifier); - hmm_range_unregister(range); - return ret; + /* + * serializes the update to mni->invalidate_seq done by caller and + * prevents invalidation of the PTE from progressing while HW is being + * programmed. This is very hacky and only works because the normal + * notifier that does invalidation is always called after the range + * notifier. + */ + if (mmu_notifier_range_blockable(range)) + mutex_lock(&sn->svmm->mutex); + else if (!mutex_trylock(&sn->svmm->mutex)) + return false; + mmu_interval_set_seq(mni, cur_seq); + mutex_unlock(&sn->svmm->mutex); + return true; } -static int -nouveau_range_fault(struct nouveau_svmm *svmm, struct hmm_range *range) +static const struct mmu_interval_notifier_ops nouveau_svm_mni_ops = { + .invalidate = nouveau_svm_range_invalidate, +}; + +static int nouveau_range_fault(struct nouveau_svmm *svmm, + struct nouveau_drm *drm, void *data, u32 size, + u64 *pfns, struct svm_notifier *notifier) { + unsigned long timeout = + jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT); + /* Have HMM fault pages within the fault window to the GPU. */ + struct hmm_range range = { + .notifier = ¬ifier->notifier, + .start = notifier->notifier.interval_tree.start, + .end = notifier->notifier.interval_tree.last + 1, + .pfns = pfns, + .flags = nouveau_svm_pfn_flags, + .values = nouveau_svm_pfn_values, + .pfn_shift = NVIF_VMM_PFNMAP_V0_ADDR_SHIFT, + }; + struct mm_struct *mm = notifier->notifier.mm; long ret; - range->default_flags = 0; - range->pfn_flags_mask = -1UL; + while (true) { + if (time_after(jiffies, timeout)) + return -EBUSY; + + range.notifier_seq = mmu_interval_read_begin(range.notifier); + range.default_flags = 0; + range.pfn_flags_mask = -1UL; + down_read(&mm->mmap_sem); + ret = hmm_range_fault(&range, 0); + up_read(&mm->mmap_sem); + if (ret <= 0) { + if (ret == 0 || ret == -EBUSY) + continue; + return ret; + } - ret = hmm_range_register(range, &svmm->mirror); - if (ret) { - up_read(&svmm->mm->mmap_sem); - return (int)ret; + mutex_lock(&svmm->mutex); + if (mmu_interval_read_retry(range.notifier, + range.notifier_seq)) { + mutex_unlock(&svmm->mutex); + continue; + } + break; } - if (!hmm_range_wait_until_valid(range, HMM_RANGE_DEFAULT_TIMEOUT)) { - up_read(&svmm->mm->mmap_sem); - return -EBUSY; - } + nouveau_dmem_convert_pfn(drm, &range); - ret = hmm_range_fault(range, 0); - if (ret <= 0) { - if (ret == 0) - ret = -EBUSY; - up_read(&svmm->mm->mmap_sem); - hmm_range_unregister(range); - return ret; - } - return 0; + svmm->vmm->vmm.object.client->super = true; + ret = nvif_object_ioctl(&svmm->vmm->vmm.object, data, size, NULL); + svmm->vmm->vmm.object.client->super = false; + mutex_unlock(&svmm->mutex); + + return ret; } static int @@ -531,7 +585,6 @@ nouveau_svm_fault(struct nvif_notify *notify) } i; u64 phys[16]; } args; - struct hmm_range range; struct vm_area_struct *vma; u64 inst, start, limit; int fi, fn, pi, fill; @@ -587,6 +640,9 @@ nouveau_svm_fault(struct nvif_notify *notify) args.i.p.version = 0; for (fi = 0; fn = fi + 1, fi < buffer->fault_nr; fi = fn) { + struct svm_notifier notifier; + struct mm_struct *mm; + /* Cancel any faults from non-SVM channels. */ if (!(svmm = buffer->fault[fi]->svmm)) { nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]); @@ -606,24 +662,32 @@ nouveau_svm_fault(struct nvif_notify *notify) start = max_t(u64, start, svmm->unmanaged.limit); SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit); + mm = svmm->notifier.mm; + if (!mmget_not_zero(mm)) { + nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]); + continue; + } + /* Intersect fault window with the CPU VMA, cancelling * the fault if the address is invalid. */ - down_read(&svmm->mm->mmap_sem); - vma = find_vma_intersection(svmm->mm, start, limit); + down_read(&mm->mmap_sem); + vma = find_vma_intersection(mm, start, limit); if (!vma) { SVMM_ERR(svmm, "wndw %016llx-%016llx", start, limit); - up_read(&svmm->mm->mmap_sem); + up_read(&mm->mmap_sem); + mmput(mm); nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]); continue; } start = max_t(u64, start, vma->vm_start); limit = min_t(u64, limit, vma->vm_end); + up_read(&mm->mmap_sem); SVMM_DBG(svmm, "wndw %016llx-%016llx", start, limit); if (buffer->fault[fi]->addr != start) { SVMM_ERR(svmm, "addr %016llx", buffer->fault[fi]->addr); - up_read(&svmm->mm->mmap_sem); + mmput(mm); nouveau_svm_fault_cancel_fault(svm, buffer->fault[fi]); continue; } @@ -679,33 +743,19 @@ nouveau_svm_fault(struct nvif_notify *notify) args.i.p.addr, args.i.p.addr + args.i.p.size, fn - fi); - /* Have HMM fault pages within the fault window to the GPU. */ - range.start = args.i.p.addr; - range.end = args.i.p.addr + args.i.p.size; - range.pfns = args.phys; - range.flags = nouveau_svm_pfn_flags; - range.values = nouveau_svm_pfn_values; - range.pfn_shift = NVIF_VMM_PFNMAP_V0_ADDR_SHIFT; -again: - ret = nouveau_range_fault(svmm, &range); - if (ret == 0) { - mutex_lock(&svmm->mutex); - if (!nouveau_range_done(&range)) { - mutex_unlock(&svmm->mutex); - goto again; - } - - nouveau_dmem_convert_pfn(svm->drm, &range); - - svmm->vmm->vmm.object.client->super = true; - ret = nvif_object_ioctl(&svmm->vmm->vmm.object, - &args, sizeof(args.i) + - pi * sizeof(args.phys[0]), - NULL); - svmm->vmm->vmm.object.client->super = false; - mutex_unlock(&svmm->mutex); - up_read(&svmm->mm->mmap_sem); + notifier.svmm = svmm; + ret = mmu_interval_notifier_insert(¬ifier.notifier, + svmm->notifier.mm, + args.i.p.addr, args.i.p.size, + &nouveau_svm_mni_ops); + if (!ret) { + ret = nouveau_range_fault( + svmm, svm->drm, &args, + sizeof(args.i) + pi * sizeof(args.phys[0]), + args.phys, ¬ifier); + mmu_interval_notifier_remove(¬ifier.notifier); } + mmput(mm); /* Cancel any faults in the window whose pages didn't manage * to keep their valid bit, or stay writeable when required. @@ -714,10 +764,10 @@ again: */ while (fi < fn) { struct nouveau_svm_fault *fault = buffer->fault[fi++]; - pi = (fault->addr - range.start) >> PAGE_SHIFT; + pi = (fault->addr - args.i.p.addr) >> PAGE_SHIFT; if (ret || - !(range.pfns[pi] & NVIF_VMM_PFNMAP_V0_V) || - (!(range.pfns[pi] & NVIF_VMM_PFNMAP_V0_W) && + !(args.phys[pi] & NVIF_VMM_PFNMAP_V0_V) || + (!(args.phys[pi] & NVIF_VMM_PFNMAP_V0_W) && fault->access != 0 && fault->access != 3)) { nouveau_svm_fault_cancel_fault(svm, fault); continue; diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index d59b004f6695..30e32adc1fc6 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -68,6 +68,10 @@ #include <linux/hashtable.h> #include <linux/dma-fence.h> +#ifdef CONFIG_MMU_NOTIFIER +#include <linux/mmu_notifier.h> +#endif + #include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> @@ -509,8 +513,9 @@ struct radeon_bo { struct ttm_bo_kmap_obj dma_buf_vmap; pid_t pid; - struct radeon_mn *mn; - struct list_head mn_list; +#ifdef CONFIG_MMU_NOTIFIER + struct mmu_interval_notifier notifier; +#endif }; #define gem_to_radeon_bo(gobj) container_of((gobj), struct radeon_bo, tbo.base) diff --git a/drivers/gpu/drm/radeon/radeon_mn.c b/drivers/gpu/drm/radeon/radeon_mn.c index dbab9a3a969b..f93829f08a4d 100644 --- a/drivers/gpu/drm/radeon/radeon_mn.c +++ b/drivers/gpu/drm/radeon/radeon_mn.c @@ -36,131 +36,51 @@ #include "radeon.h" -struct radeon_mn { - struct mmu_notifier mn; - - /* objects protected by lock */ - struct mutex lock; - struct rb_root_cached objects; -}; - -struct radeon_mn_node { - struct interval_tree_node it; - struct list_head bos; -}; - /** - * radeon_mn_invalidate_range_start - callback to notify about mm change + * radeon_mn_invalidate - callback to notify about mm change * * @mn: our notifier - * @mn: the mm this callback is about - * @start: start of updated range - * @end: end of updated range + * @range: the VMA under invalidation * * We block for all BOs between start and end to be idle and * unmap them by move them into system domain again. */ -static int radeon_mn_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) +static bool radeon_mn_invalidate(struct mmu_interval_notifier *mn, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { - struct radeon_mn *rmn = container_of(mn, struct radeon_mn, mn); + struct radeon_bo *bo = container_of(mn, struct radeon_bo, notifier); struct ttm_operation_ctx ctx = { false, false }; - struct interval_tree_node *it; - unsigned long end; - int ret = 0; - - /* notification is exclusive, but interval is inclusive */ - end = range->end - 1; - - /* TODO we should be able to split locking for interval tree and - * the tear down. - */ - if (mmu_notifier_range_blockable(range)) - mutex_lock(&rmn->lock); - else if (!mutex_trylock(&rmn->lock)) - return -EAGAIN; - - it = interval_tree_iter_first(&rmn->objects, range->start, end); - while (it) { - struct radeon_mn_node *node; - struct radeon_bo *bo; - long r; - - if (!mmu_notifier_range_blockable(range)) { - ret = -EAGAIN; - goto out_unlock; - } - - node = container_of(it, struct radeon_mn_node, it); - it = interval_tree_iter_next(it, range->start, end); + long r; - list_for_each_entry(bo, &node->bos, mn_list) { + if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound) + return true; - if (!bo->tbo.ttm || bo->tbo.ttm->state != tt_bound) - continue; + if (!mmu_notifier_range_blockable(range)) + return false; - r = radeon_bo_reserve(bo, true); - if (r) { - DRM_ERROR("(%ld) failed to reserve user bo\n", r); - continue; - } - - r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, - true, false, MAX_SCHEDULE_TIMEOUT); - if (r <= 0) - DRM_ERROR("(%ld) failed to wait for user bo\n", r); - - radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); - r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); - if (r) - DRM_ERROR("(%ld) failed to validate user bo\n", r); - - radeon_bo_unreserve(bo); - } + r = radeon_bo_reserve(bo, true); + if (r) { + DRM_ERROR("(%ld) failed to reserve user bo\n", r); + return true; } - -out_unlock: - mutex_unlock(&rmn->lock); - - return ret; -} - -static void radeon_mn_release(struct mmu_notifier *mn, struct mm_struct *mm) -{ - struct mmu_notifier_range range = { - .mm = mm, - .start = 0, - .end = ULONG_MAX, - .flags = 0, - .event = MMU_NOTIFY_UNMAP, - }; - - radeon_mn_invalidate_range_start(mn, &range); -} - -static struct mmu_notifier *radeon_mn_alloc_notifier(struct mm_struct *mm) -{ - struct radeon_mn *rmn; - rmn = kzalloc(sizeof(*rmn), GFP_KERNEL); - if (!rmn) - return ERR_PTR(-ENOMEM); + r = dma_resv_wait_timeout_rcu(bo->tbo.base.resv, true, false, + MAX_SCHEDULE_TIMEOUT); + if (r <= 0) + DRM_ERROR("(%ld) failed to wait for user bo\n", r); - mutex_init(&rmn->lock); - rmn->objects = RB_ROOT_CACHED; - return &rmn->mn; -} + radeon_ttm_placement_from_domain(bo, RADEON_GEM_DOMAIN_CPU); + r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); + if (r) + DRM_ERROR("(%ld) failed to validate user bo\n", r); -static void radeon_mn_free_notifier(struct mmu_notifier *mn) -{ - kfree(container_of(mn, struct radeon_mn, mn)); + radeon_bo_unreserve(bo); + return true; } -static const struct mmu_notifier_ops radeon_mn_ops = { - .release = radeon_mn_release, - .invalidate_range_start = radeon_mn_invalidate_range_start, - .alloc_notifier = radeon_mn_alloc_notifier, - .free_notifier = radeon_mn_free_notifier, +static const struct mmu_interval_notifier_ops radeon_mn_ops = { + .invalidate = radeon_mn_invalidate, }; /** @@ -174,51 +94,20 @@ static const struct mmu_notifier_ops radeon_mn_ops = { */ int radeon_mn_register(struct radeon_bo *bo, unsigned long addr) { - unsigned long end = addr + radeon_bo_size(bo) - 1; - struct mmu_notifier *mn; - struct radeon_mn *rmn; - struct radeon_mn_node *node = NULL; - struct list_head bos; - struct interval_tree_node *it; - - mn = mmu_notifier_get(&radeon_mn_ops, current->mm); - if (IS_ERR(mn)) - return PTR_ERR(mn); - rmn = container_of(mn, struct radeon_mn, mn); - - INIT_LIST_HEAD(&bos); - - mutex_lock(&rmn->lock); - - while ((it = interval_tree_iter_first(&rmn->objects, addr, end))) { - kfree(node); - node = container_of(it, struct radeon_mn_node, it); - interval_tree_remove(&node->it, &rmn->objects); - addr = min(it->start, addr); - end = max(it->last, end); - list_splice(&node->bos, &bos); - } - - if (!node) { - node = kmalloc(sizeof(struct radeon_mn_node), GFP_KERNEL); - if (!node) { - mutex_unlock(&rmn->lock); - return -ENOMEM; - } - } - - bo->mn = rmn; - - node->it.start = addr; - node->it.last = end; - INIT_LIST_HEAD(&node->bos); - list_splice(&bos, &node->bos); - list_add(&bo->mn_list, &node->bos); - - interval_tree_insert(&node->it, &rmn->objects); - - mutex_unlock(&rmn->lock); - + int ret; + + ret = mmu_interval_notifier_insert(&bo->notifier, current->mm, addr, + radeon_bo_size(bo), &radeon_mn_ops); + if (ret) + return ret; + + /* + * FIXME: radeon appears to allow get_user_pages to run during + * invalidate_range_start/end, which is not a safe way to read the + * PTEs. It should use the mmu_interval_read_begin() scheme around the + * get_user_pages to ensure that the PTEs are read properly + */ + mmu_interval_read_begin(&bo->notifier); return 0; } @@ -231,27 +120,8 @@ int radeon_mn_register(struct radeon_bo *bo, unsigned long addr) */ void radeon_mn_unregister(struct radeon_bo *bo) { - struct radeon_mn *rmn = bo->mn; - struct list_head *head; - - if (!rmn) + if (!bo->notifier.mm) return; - - mutex_lock(&rmn->lock); - /* save the next list entry for later */ - head = bo->mn_list.next; - - list_del(&bo->mn_list); - - if (list_empty(head)) { - struct radeon_mn_node *node; - node = container_of(head, struct radeon_mn_node, bos); - interval_tree_remove(&node->it, &rmn->objects); - kfree(node); - } - - mutex_unlock(&rmn->lock); - - mmu_notifier_put(&rmn->mn); - bo->mn = NULL; + mmu_interval_notifier_remove(&bo->notifier); + bo->notifier.mm = NULL; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 4b34a278d65b..11863fbdd5d6 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -42,8 +42,6 @@ #include <linux/uaccess.h> #include <linux/mem_encrypt.h> -#define TTM_BO_VM_NUM_PREFAULT 16 - static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, struct vm_fault *vmf) { @@ -106,25 +104,30 @@ static unsigned long ttm_bo_io_mem_pfn(struct ttm_buffer_object *bo, + page_offset; } -static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) +/** + * ttm_bo_vm_reserve - Reserve a buffer object in a retryable vm callback + * @bo: The buffer object + * @vmf: The fault structure handed to the callback + * + * vm callbacks like fault() and *_mkwrite() allow for the mm_sem to be dropped + * during long waits, and after the wait the callback will be restarted. This + * is to allow other threads using the same virtual memory space concurrent + * access to map(), unmap() completely unrelated buffer objects. TTM buffer + * object reservations sometimes wait for GPU and should therefore be + * considered long waits. This function reserves the buffer object interruptibly + * taking this into account. Starvation is avoided by the vm system not + * allowing too many repeated restarts. + * This function is intended to be used in customized fault() and _mkwrite() + * handlers. + * + * Return: + * 0 on success and the bo was reserved. + * VM_FAULT_RETRY if blocking wait. + * VM_FAULT_NOPAGE if blocking wait and retrying was not allowed. + */ +vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, + struct vm_fault *vmf) { - struct vm_area_struct *vma = vmf->vma; - struct ttm_buffer_object *bo = (struct ttm_buffer_object *) - vma->vm_private_data; - struct ttm_bo_device *bdev = bo->bdev; - unsigned long page_offset; - unsigned long page_last; - unsigned long pfn; - struct ttm_tt *ttm = NULL; - struct page *page; - int err; - int i; - vm_fault_t ret = VM_FAULT_NOPAGE; - unsigned long address = vmf->address; - struct ttm_mem_type_manager *man = - &bdev->man[bo->mem.mem_type]; - struct vm_area_struct cvma; - /* * Work around locking order reversal in fault / nopfn * between mmap_sem and bo_reserve: Perform a trylock operation @@ -151,14 +154,54 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) return VM_FAULT_NOPAGE; } + return 0; +} +EXPORT_SYMBOL(ttm_bo_vm_reserve); + +/** + * ttm_bo_vm_fault_reserved - TTM fault helper + * @vmf: The struct vm_fault given as argument to the fault callback + * @prot: The page protection to be used for this memory area. + * @num_prefault: Maximum number of prefault pages. The caller may want to + * specify this based on madvice settings and the size of the GPU object + * backed by the memory. + * + * This function inserts one or more page table entries pointing to the + * memory backing the buffer object, and then returns a return code + * instructing the caller to retry the page access. + * + * Return: + * VM_FAULT_NOPAGE on success or pending signal + * VM_FAULT_SIGBUS on unspecified error + * VM_FAULT_OOM on out-of-memory + * VM_FAULT_RETRY if retryable wait + */ +vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, + pgprot_t prot, + pgoff_t num_prefault) +{ + struct vm_area_struct *vma = vmf->vma; + struct vm_area_struct cvma = *vma; + struct ttm_buffer_object *bo = vma->vm_private_data; + struct ttm_bo_device *bdev = bo->bdev; + unsigned long page_offset; + unsigned long page_last; + unsigned long pfn; + struct ttm_tt *ttm = NULL; + struct page *page; + int err; + pgoff_t i; + vm_fault_t ret = VM_FAULT_NOPAGE; + unsigned long address = vmf->address; + struct ttm_mem_type_manager *man = + &bdev->man[bo->mem.mem_type]; + /* * Refuse to fault imported pages. This should be handled * (if at all) by redirecting mmap to the exporter. */ - if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) { - ret = VM_FAULT_SIGBUS; - goto out_unlock; - } + if (bo->ttm && (bo->ttm->page_flags & TTM_PAGE_FLAG_SG)) + return VM_FAULT_SIGBUS; if (bdev->driver->fault_reserve_notify) { struct dma_fence *moving = dma_fence_get(bo->moving); @@ -169,11 +212,9 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) break; case -EBUSY: case -ERESTARTSYS: - ret = VM_FAULT_NOPAGE; - goto out_unlock; + return VM_FAULT_NOPAGE; default: - ret = VM_FAULT_SIGBUS; - goto out_unlock; + return VM_FAULT_SIGBUS; } if (bo->moving != moving) { @@ -189,21 +230,12 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) * move. */ ret = ttm_bo_vm_fault_idle(bo, vmf); - if (unlikely(ret != 0)) { - if (ret == VM_FAULT_RETRY && - !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) { - /* The BO has already been unreserved. */ - return ret; - } - - goto out_unlock; - } + if (unlikely(ret != 0)) + return ret; err = ttm_mem_io_lock(man, true); - if (unlikely(err != 0)) { - ret = VM_FAULT_NOPAGE; - goto out_unlock; - } + if (unlikely(err != 0)) + return VM_FAULT_NOPAGE; err = ttm_mem_io_reserve_vm(bo); if (unlikely(err != 0)) { ret = VM_FAULT_SIGBUS; @@ -220,18 +252,8 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) goto out_io_unlock; } - /* - * Make a local vma copy to modify the page_prot member - * and vm_flags if necessary. The vma parameter is protected - * by mmap_sem in write mode. - */ - cvma = *vma; - cvma.vm_page_prot = vm_get_page_prot(cvma.vm_flags); - - if (bo->mem.bus.is_iomem) { - cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, - cvma.vm_page_prot); - } else { + cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, prot); + if (!bo->mem.bus.is_iomem) { struct ttm_operation_ctx ctx = { .interruptible = false, .no_wait_gpu = false, @@ -240,24 +262,21 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) }; ttm = bo->ttm; - cvma.vm_page_prot = ttm_io_prot(bo->mem.placement, - cvma.vm_page_prot); - - /* Allocate all page at once, most common usage */ - if (ttm_tt_populate(ttm, &ctx)) { + if (ttm_tt_populate(bo->ttm, &ctx)) { ret = VM_FAULT_OOM; goto out_io_unlock; } + } else { + /* Iomem should not be marked encrypted */ + cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); } /* * Speculatively prefault a number of pages. Only error on * first page. */ - for (i = 0; i < TTM_BO_VM_NUM_PREFAULT; ++i) { + for (i = 0; i < num_prefault; ++i) { if (bo->mem.bus.is_iomem) { - /* Iomem should not be marked encrypted */ - cvma.vm_page_prot = pgprot_decrypted(cvma.vm_page_prot); pfn = ttm_bo_io_mem_pfn(bo, page_offset); } else { page = ttm->pages[page_offset]; @@ -293,28 +312,49 @@ static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) ret = VM_FAULT_NOPAGE; out_io_unlock: ttm_mem_io_unlock(man); -out_unlock: + return ret; +} +EXPORT_SYMBOL(ttm_bo_vm_fault_reserved); + +static vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + pgprot_t prot; + struct ttm_buffer_object *bo = vma->vm_private_data; + vm_fault_t ret; + + ret = ttm_bo_vm_reserve(bo, vmf); + if (ret) + return ret; + + prot = vm_get_page_prot(vma->vm_flags); + ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT); + if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) + return ret; + dma_resv_unlock(bo->base.resv); + return ret; } -static void ttm_bo_vm_open(struct vm_area_struct *vma) +void ttm_bo_vm_open(struct vm_area_struct *vma) { - struct ttm_buffer_object *bo = - (struct ttm_buffer_object *)vma->vm_private_data; + struct ttm_buffer_object *bo = vma->vm_private_data; WARN_ON(bo->bdev->dev_mapping != vma->vm_file->f_mapping); ttm_bo_get(bo); } +EXPORT_SYMBOL(ttm_bo_vm_open); -static void ttm_bo_vm_close(struct vm_area_struct *vma) +void ttm_bo_vm_close(struct vm_area_struct *vma) { - struct ttm_buffer_object *bo = (struct ttm_buffer_object *)vma->vm_private_data; + struct ttm_buffer_object *bo = vma->vm_private_data; ttm_bo_put(bo); vma->vm_private_data = NULL; } +EXPORT_SYMBOL(ttm_bo_vm_close); static int ttm_bo_vm_access_kmap(struct ttm_buffer_object *bo, unsigned long offset, diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig index 6b28a326f8bb..15acdf2a7c0f 100644 --- a/drivers/gpu/drm/vmwgfx/Kconfig +++ b/drivers/gpu/drm/vmwgfx/Kconfig @@ -8,6 +8,7 @@ config DRM_VMWGFX select FB_CFB_IMAGEBLIT select DRM_TTM select FB + select MAPPING_DIRTY_HELPERS # Only needed for the transitional use of drm_crtc_init - can be removed # again once vmwgfx sets up the primary plane itself. select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 8841bd30e1e5..c877a21a0739 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -8,7 +8,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_cmdbuf_res.o vmwgfx_cmdbuf.o vmwgfx_stdu.o \ vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \ vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \ - vmwgfx_validation.o \ + vmwgfx_validation.o vmwgfx_page_dirty.o \ ttm_object.o ttm_lock.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h index f2bfd3d80598..61414f105c67 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h @@ -1280,7 +1280,6 @@ svga3dsurface_get_pixel_offset(SVGA3dSurfaceFormat format, return offset; } - static inline u32 svga3dsurface_get_image_offset(SVGA3dSurfaceFormat format, surf_size_struct baseLevelSize, @@ -1375,4 +1374,236 @@ svga3dsurface_is_screen_target_format(SVGA3dSurfaceFormat format) return svga3dsurface_is_dx_screen_target_format(format); } +/** + * struct svga3dsurface_mip - Mimpmap level information + * @bytes: Bytes required in the backing store of this mipmap level. + * @img_stride: Byte stride per image. + * @row_stride: Byte stride per block row. + * @size: The size of the mipmap. + */ +struct svga3dsurface_mip { + size_t bytes; + size_t img_stride; + size_t row_stride; + struct drm_vmw_size size; + +}; + +/** + * struct svga3dsurface_cache - Cached surface information + * @desc: Pointer to the surface descriptor + * @mip: Array of mipmap level information. Valid size is @num_mip_levels. + * @mip_chain_bytes: Bytes required in the backing store for the whole chain + * of mip levels. + * @sheet_bytes: Bytes required in the backing store for a sheet + * representing a single sample. + * @num_mip_levels: Valid size of the @mip array. Number of mipmap levels in + * a chain. + * @num_layers: Number of slices in an array texture or number of faces in + * a cubemap texture. + */ +struct svga3dsurface_cache { + const struct svga3d_surface_desc *desc; + struct svga3dsurface_mip mip[DRM_VMW_MAX_MIP_LEVELS]; + size_t mip_chain_bytes; + size_t sheet_bytes; + u32 num_mip_levels; + u32 num_layers; +}; + +/** + * struct svga3dsurface_loc - Surface location + * @sub_resource: Surface subresource. Defined as layer * num_mip_levels + + * mip_level. + * @x: X coordinate. + * @y: Y coordinate. + * @z: Z coordinate. + */ +struct svga3dsurface_loc { + u32 sub_resource; + u32 x, y, z; +}; + +/** + * svga3dsurface_subres - Compute the subresource from layer and mipmap. + * @cache: Surface layout data. + * @mip_level: The mipmap level. + * @layer: The surface layer (face or array slice). + * + * Return: The subresource. + */ +static inline u32 svga3dsurface_subres(const struct svga3dsurface_cache *cache, + u32 mip_level, u32 layer) +{ + return cache->num_mip_levels * layer + mip_level; +} + +/** + * svga3dsurface_setup_cache - Build a surface cache entry + * @size: The surface base level dimensions. + * @format: The surface format. + * @num_mip_levels: Number of mipmap levels. + * @num_layers: Number of layers. + * @cache: Pointer to a struct svga3dsurface_cach object to be filled in. + * + * Return: Zero on success, -EINVAL on invalid surface layout. + */ +static inline int svga3dsurface_setup_cache(const struct drm_vmw_size *size, + SVGA3dSurfaceFormat format, + u32 num_mip_levels, + u32 num_layers, + u32 num_samples, + struct svga3dsurface_cache *cache) +{ + const struct svga3d_surface_desc *desc; + u32 i; + + memset(cache, 0, sizeof(*cache)); + cache->desc = desc = svga3dsurface_get_desc(format); + cache->num_mip_levels = num_mip_levels; + cache->num_layers = num_layers; + for (i = 0; i < cache->num_mip_levels; i++) { + struct svga3dsurface_mip *mip = &cache->mip[i]; + + mip->size = svga3dsurface_get_mip_size(*size, i); + mip->bytes = svga3dsurface_get_image_buffer_size + (desc, &mip->size, 0); + mip->row_stride = + __KERNEL_DIV_ROUND_UP(mip->size.width, + desc->block_size.width) * + desc->bytes_per_block * num_samples; + if (!mip->row_stride) + goto invalid_dim; + + mip->img_stride = + __KERNEL_DIV_ROUND_UP(mip->size.height, + desc->block_size.height) * + mip->row_stride; + if (!mip->img_stride) + goto invalid_dim; + + cache->mip_chain_bytes += mip->bytes; + } + cache->sheet_bytes = cache->mip_chain_bytes * num_layers; + if (!cache->sheet_bytes) + goto invalid_dim; + + return 0; + +invalid_dim: + VMW_DEBUG_USER("Invalid surface layout for dirty tracking.\n"); + return -EINVAL; +} + +/** + * svga3dsurface_get_loc - Get a surface location from an offset into the + * backing store + * @cache: Surface layout data. + * @loc: Pointer to a struct svga3dsurface_loc to be filled in. + * @offset: Offset into the surface backing store. + */ +static inline void +svga3dsurface_get_loc(const struct svga3dsurface_cache *cache, + struct svga3dsurface_loc *loc, + size_t offset) +{ + const struct svga3dsurface_mip *mip = &cache->mip[0]; + const struct svga3d_surface_desc *desc = cache->desc; + u32 layer; + int i; + + if (offset >= cache->sheet_bytes) + offset %= cache->sheet_bytes; + + layer = offset / cache->mip_chain_bytes; + offset -= layer * cache->mip_chain_bytes; + for (i = 0; i < cache->num_mip_levels; ++i, ++mip) { + if (mip->bytes > offset) + break; + offset -= mip->bytes; + } + + loc->sub_resource = svga3dsurface_subres(cache, i, layer); + loc->z = offset / mip->img_stride; + offset -= loc->z * mip->img_stride; + loc->z *= desc->block_size.depth; + loc->y = offset / mip->row_stride; + offset -= loc->y * mip->row_stride; + loc->y *= desc->block_size.height; + loc->x = offset / desc->bytes_per_block; + loc->x *= desc->block_size.width; +} + +/** + * svga3dsurface_inc_loc - Clamp increment a surface location with one block + * size + * in each dimension. + * @loc: Pointer to a struct svga3dsurface_loc to be incremented. + * + * When computing the size of a range as size = end - start, the range does not + * include the end element. However a location representing the last byte + * of a touched region in the backing store *is* included in the range. + * This function modifies such a location to match the end definition + * given as start + size which is the one used in a SVGA3dBox. + */ +static inline void +svga3dsurface_inc_loc(const struct svga3dsurface_cache *cache, + struct svga3dsurface_loc *loc) +{ + const struct svga3d_surface_desc *desc = cache->desc; + u32 mip = loc->sub_resource % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + + loc->sub_resource++; + loc->x += desc->block_size.width; + if (loc->x > size->width) + loc->x = size->width; + loc->y += desc->block_size.height; + if (loc->y > size->height) + loc->y = size->height; + loc->z += desc->block_size.depth; + if (loc->z > size->depth) + loc->z = size->depth; +} + +/** + * svga3dsurface_min_loc - The start location in a subresource + * @cache: Surface layout data. + * @sub_resource: The subresource. + * @loc: Pointer to a struct svga3dsurface_loc to be filled in. + */ +static inline void +svga3dsurface_min_loc(const struct svga3dsurface_cache *cache, + u32 sub_resource, + struct svga3dsurface_loc *loc) +{ + loc->sub_resource = sub_resource; + loc->x = loc->y = loc->z = 0; +} + +/** + * svga3dsurface_min_loc - The end location in a subresource + * @cache: Surface layout data. + * @sub_resource: The subresource. + * @loc: Pointer to a struct svga3dsurface_loc to be filled in. + * + * Following the end definition given in svga3dsurface_inc_loc(), + * Compute the end location of a surface subresource. + */ +static inline void +svga3dsurface_max_loc(const struct svga3dsurface_cache *cache, + u32 sub_resource, + struct svga3dsurface_loc *loc) +{ + const struct drm_vmw_size *size; + u32 mip; + + loc->sub_resource = sub_resource + 1; + mip = sub_resource % cache->num_mip_levels; + size = &cache->mip[mip].size; + loc->x = size->width; + loc->y = size->height; + loc->z = size->depth; +} + #endif /* _SVGA3D_SURFACEDEFS_H_ */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index 74016a08d118..8b71bf6b58ef 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -462,6 +462,8 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) { struct vmw_buffer_object *vmw_bo = vmw_buffer_object(bo); + WARN_ON(vmw_bo->dirty); + WARN_ON(!RB_EMPTY_ROOT(&vmw_bo->res_tree)); vmw_bo_unmap(vmw_bo); kfree(vmw_bo); } @@ -475,8 +477,11 @@ void vmw_bo_bo_free(struct ttm_buffer_object *bo) static void vmw_user_bo_destroy(struct ttm_buffer_object *bo) { struct vmw_user_buffer_object *vmw_user_bo = vmw_user_buffer_object(bo); + struct vmw_buffer_object *vbo = &vmw_user_bo->vbo; - vmw_bo_unmap(&vmw_user_bo->vbo); + WARN_ON(vbo->dirty); + WARN_ON(!RB_EMPTY_ROOT(&vbo->res_tree)); + vmw_bo_unmap(vbo); ttm_prime_object_kfree(vmw_user_bo, prime); } @@ -511,8 +516,7 @@ int vmw_bo_init(struct vmw_private *dev_priv, memset(vmw_bo, 0, sizeof(*vmw_bo)); BUILD_BUG_ON(TTM_MAX_BO_PRIORITY <= 3); vmw_bo->base.priority = 3; - - INIT_LIST_HEAD(&vmw_bo->res_list); + vmw_bo->res_tree = RB_ROOT; ret = ttm_bo_init(bdev, &vmw_bo->base, size, ttm_bo_type_device, placement, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index b18842f73081..a31e726d6d71 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -56,9 +56,9 @@ #define VMWGFX_DRIVER_NAME "vmwgfx" -#define VMWGFX_DRIVER_DATE "20180704" +#define VMWGFX_DRIVER_DATE "20190328" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 15 +#define VMWGFX_DRIVER_MINOR 16 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) #define VMWGFX_MAX_RELOCATIONS 2048 @@ -100,17 +100,18 @@ struct vmw_fpriv { /** * struct vmw_buffer_object - TTM buffer object with vmwgfx additions * @base: The TTM buffer object - * @res_list: List of resources using this buffer object as a backing MOB + * @res_tree: RB tree of resources using this buffer object as a backing MOB * @pin_count: pin depth * @cpu_writers: Number of synccpu write grabs. Protected by reservation when * increased. May be decreased without reservation. * @dx_query_ctx: DX context if this buffer object is used as a DX query MOB * @map: Kmap object for semi-persistent mappings * @res_prios: Eviction priority counts for attached resources + * @dirty: structure for user-space dirty-tracking */ struct vmw_buffer_object { struct ttm_buffer_object base; - struct list_head res_list; + struct rb_root res_tree; s32 pin_count; atomic_t cpu_writers; /* Not ref-counted. Protected by binding_mutex */ @@ -118,6 +119,7 @@ struct vmw_buffer_object { /* Protected by reservation */ struct ttm_bo_kmap_obj map; u32 res_prios[TTM_MAX_BO_PRIORITY]; + struct vmw_bo_dirty *dirty; }; /** @@ -148,7 +150,8 @@ struct vmw_res_func; * @res_dirty: Resource contains data not yet in the backup buffer. Protected * by resource reserved. * @backup_dirty: Backup buffer contains data not yet in the HW resource. - * Protecte by resource reserved. + * Protected by resource reserved. + * @coherent: Emulate coherency by tracking vm accesses. * @backup: The backup buffer if any. Protected by resource reserved. * @backup_offset: Offset into the backup buffer if any. Protected by resource * reserved. Note that only a few resource types can have a @backup_offset @@ -157,29 +160,32 @@ struct vmw_res_func; * pin-count greater than zero. It is not on the resource LRU lists and its * backup buffer is pinned. Hence it can't be evicted. * @func: Method vtable for this resource. Immutable. + * @mob_node; Node for the MOB backup rbtree. Protected by @backup reserved. * @lru_head: List head for the LRU list. Protected by @dev_priv::resource_lock. - * @mob_head: List head for the MOB backup list. Protected by @backup reserved. * @binding_head: List head for the context binding list. Protected by * the @dev_priv::binding_mutex * @res_free: The resource destructor. * @hw_destroy: Callback to destroy the resource on the device, as part of * resource destruction. */ +struct vmw_resource_dirty; struct vmw_resource { struct kref kref; struct vmw_private *dev_priv; int id; u32 used_prio; unsigned long backup_size; - bool res_dirty; - bool backup_dirty; + u32 res_dirty : 1; + u32 backup_dirty : 1; + u32 coherent : 1; struct vmw_buffer_object *backup; unsigned long backup_offset; unsigned long pin_count; const struct vmw_res_func *func; + struct rb_node mob_node; struct list_head lru_head; - struct list_head mob_head; struct list_head binding_head; + struct vmw_resource_dirty *dirty; void (*res_free) (struct vmw_resource *res); void (*hw_destroy) (struct vmw_resource *res); }; @@ -678,7 +684,8 @@ extern void vmw_resource_unreference(struct vmw_resource **p_res); extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res); extern struct vmw_resource * vmw_resource_reference_unless_doomed(struct vmw_resource *res); -extern int vmw_resource_validate(struct vmw_resource *res, bool intr); +extern int vmw_resource_validate(struct vmw_resource *res, bool intr, + bool dirtying); extern int vmw_resource_reserve(struct vmw_resource *res, bool interruptible, bool no_backup); extern bool vmw_resource_needs_backup(const struct vmw_resource *res); @@ -720,6 +727,10 @@ extern void vmw_resource_evict_all(struct vmw_private *dev_priv); extern void vmw_resource_unbind_list(struct vmw_buffer_object *vbo); void vmw_resource_mob_attach(struct vmw_resource *res); void vmw_resource_mob_detach(struct vmw_resource *res); +void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, + pgoff_t end); +int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, + pgoff_t end, pgoff_t *num_prefault); /** * vmw_resource_mob_attached - Whether a resource currently has a mob attached @@ -729,7 +740,7 @@ void vmw_resource_mob_detach(struct vmw_resource *res); */ static inline bool vmw_resource_mob_attached(const struct vmw_resource *res) { - return !list_empty(&res->mob_head); + return !RB_EMPTY_NODE(&res->mob_node); } /** @@ -1407,6 +1418,17 @@ int vmw_host_log(const char *log); #define VMW_DEBUG_USER(fmt, ...) \ DRM_DEBUG_DRIVER(fmt, ##__VA_ARGS__) +/* Resource dirtying - vmwgfx_page_dirty.c */ +void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo); +int vmw_bo_dirty_add(struct vmw_buffer_object *vbo); +void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res); +void vmw_bo_dirty_clear_res(struct vmw_resource *res); +void vmw_bo_dirty_release(struct vmw_buffer_object *vbo); +void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, + pgoff_t start, pgoff_t end); +vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf); +vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf); + /** * VMW_DEBUG_KMS - Debug output for kernel mode-setting * diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index ff86d49dc5e8..934ad7c0c342 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2560,7 +2560,6 @@ static int vmw_cmd_dx_check_subresource(struct vmw_private *dev_priv, offsetof(typeof(*cmd), sid)); cmd = container_of(header, typeof(*cmd), header); - return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, VMW_RES_DIRTY_NONE, user_surface_converter, &cmd->sid, NULL); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c new file mode 100644 index 000000000000..f07aa857587c --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/************************************************************************** + * + * Copyright 2019 VMware, Inc., Palo Alto, CA., USA + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +#include "vmwgfx_drv.h" + +/* + * Different methods for tracking dirty: + * VMW_BO_DIRTY_PAGETABLE - Scan the pagetable for hardware dirty bits + * VMW_BO_DIRTY_MKWRITE - Write-protect page table entries and record write- + * accesses in the VM mkwrite() callback + */ +enum vmw_bo_dirty_method { + VMW_BO_DIRTY_PAGETABLE, + VMW_BO_DIRTY_MKWRITE, +}; + +/* + * No dirtied pages at scan trigger a transition to the _MKWRITE method, + * similarly a certain percentage of dirty pages trigger a transition to + * the _PAGETABLE method. How many triggers should we wait for before + * changing method? + */ +#define VMW_DIRTY_NUM_CHANGE_TRIGGERS 2 + +/* Percentage to trigger a transition to the _PAGETABLE method */ +#define VMW_DIRTY_PERCENTAGE 10 + +/** + * struct vmw_bo_dirty - Dirty information for buffer objects + * @start: First currently dirty bit + * @end: Last currently dirty bit + 1 + * @method: The currently used dirty method + * @change_count: Number of consecutive method change triggers + * @ref_count: Reference count for this structure + * @bitmap_size: The size of the bitmap in bits. Typically equal to the + * nuber of pages in the bo. + * @size: The accounting size for this struct. + * @bitmap: A bitmap where each bit represents a page. A set bit means a + * dirty page. + */ +struct vmw_bo_dirty { + unsigned long start; + unsigned long end; + enum vmw_bo_dirty_method method; + unsigned int change_count; + unsigned int ref_count; + unsigned long bitmap_size; + size_t size; + unsigned long bitmap[0]; +}; + +/** + * vmw_bo_dirty_scan_pagetable - Perform a pagetable scan for dirty bits + * @vbo: The buffer object to scan + * + * Scans the pagetable for dirty bits. Clear those bits and modify the + * dirty structure with the results. This function may change the + * dirty-tracking method. + */ +static void vmw_bo_dirty_scan_pagetable(struct vmw_buffer_object *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t offset = drm_vma_node_start(&vbo->base.base.vma_node); + struct address_space *mapping = vbo->base.bdev->dev_mapping; + pgoff_t num_marked; + + num_marked = clean_record_shared_mapping_range + (mapping, + offset, dirty->bitmap_size, + offset, &dirty->bitmap[0], + &dirty->start, &dirty->end); + if (num_marked == 0) + dirty->change_count++; + else + dirty->change_count = 0; + + if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { + dirty->change_count = 0; + dirty->method = VMW_BO_DIRTY_MKWRITE; + wp_shared_mapping_range(mapping, + offset, dirty->bitmap_size); + clean_record_shared_mapping_range(mapping, + offset, dirty->bitmap_size, + offset, &dirty->bitmap[0], + &dirty->start, &dirty->end); + } +} + +/** + * vmw_bo_dirty_scan_mkwrite - Reset the mkwrite dirty-tracking method + * @vbo: The buffer object to scan + * + * Write-protect pages written to so that consecutive write accesses will + * trigger a call to mkwrite. + * + * This function may change the dirty-tracking method. + */ +static void vmw_bo_dirty_scan_mkwrite(struct vmw_buffer_object *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + unsigned long offset = drm_vma_node_start(&vbo->base.base.vma_node); + struct address_space *mapping = vbo->base.bdev->dev_mapping; + pgoff_t num_marked; + + if (dirty->end <= dirty->start) + return; + + num_marked = wp_shared_mapping_range(vbo->base.bdev->dev_mapping, + dirty->start + offset, + dirty->end - dirty->start); + + if (100UL * num_marked / dirty->bitmap_size > + VMW_DIRTY_PERCENTAGE) { + dirty->change_count++; + } else { + dirty->change_count = 0; + } + + if (dirty->change_count > VMW_DIRTY_NUM_CHANGE_TRIGGERS) { + pgoff_t start = 0; + pgoff_t end = dirty->bitmap_size; + + dirty->method = VMW_BO_DIRTY_PAGETABLE; + clean_record_shared_mapping_range(mapping, offset, end, offset, + &dirty->bitmap[0], + &start, &end); + bitmap_clear(&dirty->bitmap[0], 0, dirty->bitmap_size); + if (dirty->start < dirty->end) + bitmap_set(&dirty->bitmap[0], dirty->start, + dirty->end - dirty->start); + dirty->change_count = 0; + } +} + +/** + * vmw_bo_dirty_scan - Scan for dirty pages and add them to the dirty + * tracking structure + * @vbo: The buffer object to scan + * + * This function may change the dirty tracking method. + */ +void vmw_bo_dirty_scan(struct vmw_buffer_object *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + + if (dirty->method == VMW_BO_DIRTY_PAGETABLE) + vmw_bo_dirty_scan_pagetable(vbo); + else + vmw_bo_dirty_scan_mkwrite(vbo); +} + +/** + * vmw_bo_dirty_pre_unmap - write-protect and pick up dirty pages before + * an unmap_mapping_range operation. + * @vbo: The buffer object, + * @start: First page of the range within the buffer object. + * @end: Last page of the range within the buffer object + 1. + * + * If we're using the _PAGETABLE scan method, we may leak dirty pages + * when calling unmap_mapping_range(). This function makes sure we pick + * up all dirty pages. + */ +static void vmw_bo_dirty_pre_unmap(struct vmw_buffer_object *vbo, + pgoff_t start, pgoff_t end) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + unsigned long offset = drm_vma_node_start(&vbo->base.base.vma_node); + struct address_space *mapping = vbo->base.bdev->dev_mapping; + + if (dirty->method != VMW_BO_DIRTY_PAGETABLE || start >= end) + return; + + wp_shared_mapping_range(mapping, start + offset, end - start); + clean_record_shared_mapping_range(mapping, start + offset, + end - start, offset, + &dirty->bitmap[0], &dirty->start, + &dirty->end); +} + +/** + * vmw_bo_dirty_unmap - Clear all ptes pointing to a range within a bo + * @vbo: The buffer object, + * @start: First page of the range within the buffer object. + * @end: Last page of the range within the buffer object + 1. + * + * This is similar to ttm_bo_unmap_virtual_locked() except it takes a subrange. + */ +void vmw_bo_dirty_unmap(struct vmw_buffer_object *vbo, + pgoff_t start, pgoff_t end) +{ + unsigned long offset = drm_vma_node_start(&vbo->base.base.vma_node); + struct address_space *mapping = vbo->base.bdev->dev_mapping; + + vmw_bo_dirty_pre_unmap(vbo, start, end); + unmap_shared_mapping_range(mapping, (offset + start) << PAGE_SHIFT, + (loff_t) (end - start) << PAGE_SHIFT); +} + +/** + * vmw_bo_dirty_add - Add a dirty-tracking user to a buffer object + * @vbo: The buffer object + * + * This function registers a dirty-tracking user to a buffer object. + * A user can be for example a resource or a vma in a special user-space + * mapping. + * + * Return: Zero on success, -ENOMEM on memory allocation failure. + */ +int vmw_bo_dirty_add(struct vmw_buffer_object *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t num_pages = vbo->base.num_pages; + size_t size, acc_size; + int ret; + static struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false + }; + + if (dirty) { + dirty->ref_count++; + return 0; + } + + size = sizeof(*dirty) + BITS_TO_LONGS(num_pages) * sizeof(long); + acc_size = ttm_round_pot(size); + ret = ttm_mem_global_alloc(&ttm_mem_glob, acc_size, &ctx); + if (ret) { + VMW_DEBUG_USER("Out of graphics memory for buffer object " + "dirty tracker.\n"); + return ret; + } + dirty = kvzalloc(size, GFP_KERNEL); + if (!dirty) { + ret = -ENOMEM; + goto out_no_dirty; + } + + dirty->size = acc_size; + dirty->bitmap_size = num_pages; + dirty->start = dirty->bitmap_size; + dirty->end = 0; + dirty->ref_count = 1; + if (num_pages < PAGE_SIZE / sizeof(pte_t)) { + dirty->method = VMW_BO_DIRTY_PAGETABLE; + } else { + struct address_space *mapping = vbo->base.bdev->dev_mapping; + pgoff_t offset = drm_vma_node_start(&vbo->base.base.vma_node); + + dirty->method = VMW_BO_DIRTY_MKWRITE; + + /* Write-protect and then pick up already dirty bits */ + wp_shared_mapping_range(mapping, offset, num_pages); + clean_record_shared_mapping_range(mapping, offset, num_pages, + offset, + &dirty->bitmap[0], + &dirty->start, &dirty->end); + } + + vbo->dirty = dirty; + + return 0; + +out_no_dirty: + ttm_mem_global_free(&ttm_mem_glob, acc_size); + return ret; +} + +/** + * vmw_bo_dirty_release - Release a dirty-tracking user from a buffer object + * @vbo: The buffer object + * + * This function releases a dirty-tracking user from a buffer object. + * If the reference count reaches zero, then the dirty-tracking object is + * freed and the pointer to it cleared. + * + * Return: Zero on success, -ENOMEM on memory allocation failure. + */ +void vmw_bo_dirty_release(struct vmw_buffer_object *vbo) +{ + struct vmw_bo_dirty *dirty = vbo->dirty; + + if (dirty && --dirty->ref_count == 0) { + size_t acc_size = dirty->size; + + kvfree(dirty); + ttm_mem_global_free(&ttm_mem_glob, acc_size); + vbo->dirty = NULL; + } +} + +/** + * vmw_bo_dirty_transfer_to_res - Pick up a resource's dirty region from + * its backing mob. + * @res: The resource + * + * This function will pick up all dirty ranges affecting the resource from + * it's backup mob, and call vmw_resource_dirty_update() once for each + * range. The transferred ranges will be cleared from the backing mob's + * dirty tracking. + */ +void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) +{ + struct vmw_buffer_object *vbo = res->backup; + struct vmw_bo_dirty *dirty = vbo->dirty; + pgoff_t start, cur, end; + unsigned long res_start = res->backup_offset; + unsigned long res_end = res->backup_offset + res->backup_size; + + WARN_ON_ONCE(res_start & ~PAGE_MASK); + res_start >>= PAGE_SHIFT; + res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); + + if (res_start >= dirty->end || res_end <= dirty->start) + return; + + cur = max(res_start, dirty->start); + res_end = max(res_end, dirty->end); + while (cur < res_end) { + unsigned long num; + + start = find_next_bit(&dirty->bitmap[0], res_end, cur); + if (start >= res_end) + break; + + end = find_next_zero_bit(&dirty->bitmap[0], res_end, start + 1); + cur = end + 1; + num = end - start; + bitmap_clear(&dirty->bitmap[0], start, num); + vmw_resource_dirty_update(res, start, end); + } + + if (res_start <= dirty->start && res_end > dirty->start) + dirty->start = res_end; + if (res_start < dirty->end && res_end >= dirty->end) + dirty->end = res_start; +} + +/** + * vmw_bo_dirty_clear_res - Clear a resource's dirty region from + * its backing mob. + * @res: The resource + * + * This function will clear all dirty ranges affecting the resource from + * it's backup mob's dirty tracking. + */ +void vmw_bo_dirty_clear_res(struct vmw_resource *res) +{ + unsigned long res_start = res->backup_offset; + unsigned long res_end = res->backup_offset + res->backup_size; + struct vmw_buffer_object *vbo = res->backup; + struct vmw_bo_dirty *dirty = vbo->dirty; + + res_start >>= PAGE_SHIFT; + res_end = DIV_ROUND_UP(res_end, PAGE_SIZE); + + if (res_start >= dirty->end || res_end <= dirty->start) + return; + + res_start = max(res_start, dirty->start); + res_end = min(res_end, dirty->end); + bitmap_clear(&dirty->bitmap[0], res_start, res_end - res_start); + + if (res_start <= dirty->start && res_end > dirty->start) + dirty->start = res_end; + if (res_start < dirty->end && res_end >= dirty->end) + dirty->end = res_start; +} + +vm_fault_t vmw_bo_vm_mkwrite(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + vm_fault_t ret; + unsigned long page_offset; + unsigned int save_flags; + struct vmw_buffer_object *vbo = + container_of(bo, typeof(*vbo), base); + + /* + * mkwrite() doesn't handle the VM_FAULT_RETRY return value correctly. + * So make sure the TTM helpers are aware. + */ + save_flags = vmf->flags; + vmf->flags &= ~FAULT_FLAG_ALLOW_RETRY; + ret = ttm_bo_vm_reserve(bo, vmf); + vmf->flags = save_flags; + if (ret) + return ret; + + page_offset = vmf->pgoff - drm_vma_node_start(&bo->base.vma_node); + if (unlikely(page_offset >= bo->num_pages)) { + ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + + if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE && + !test_bit(page_offset, &vbo->dirty->bitmap[0])) { + struct vmw_bo_dirty *dirty = vbo->dirty; + + __set_bit(page_offset, &dirty->bitmap[0]); + dirty->start = min(dirty->start, page_offset); + dirty->end = max(dirty->end, page_offset + 1); + } + +out_unlock: + dma_resv_unlock(bo->base.resv); + return ret; +} + +vm_fault_t vmw_bo_vm_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct ttm_buffer_object *bo = (struct ttm_buffer_object *) + vma->vm_private_data; + struct vmw_buffer_object *vbo = + container_of(bo, struct vmw_buffer_object, base); + pgoff_t num_prefault; + pgprot_t prot; + vm_fault_t ret; + + ret = ttm_bo_vm_reserve(bo, vmf); + if (ret) + return ret; + + num_prefault = (vma->vm_flags & VM_RAND_READ) ? 1 : + TTM_BO_VM_NUM_PREFAULT; + + if (vbo->dirty) { + pgoff_t allowed_prefault; + unsigned long page_offset; + + page_offset = vmf->pgoff - + drm_vma_node_start(&bo->base.vma_node); + if (page_offset >= bo->num_pages || + vmw_resources_clean(vbo, page_offset, + page_offset + PAGE_SIZE, + &allowed_prefault)) { + ret = VM_FAULT_SIGBUS; + goto out_unlock; + } + + num_prefault = min(num_prefault, allowed_prefault); + } + + /* + * If we don't track dirty using the MKWRITE method, make sure + * sure the page protection is write-enabled so we don't get + * a lot of unnecessary write faults. + */ + if (vbo->dirty && vbo->dirty->method == VMW_BO_DIRTY_MKWRITE) + prot = vma->vm_page_prot; + else + prot = vm_get_page_prot(vma->vm_flags); + + ret = ttm_bo_vm_fault_reserved(vmf, prot, num_prefault); + if (ret == VM_FAULT_RETRY && !(vmf->flags & FAULT_FLAG_RETRY_NOWAIT)) + return ret; + +out_unlock: + dma_resv_unlock(bo->base.resv); + + return ret; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 6dfe36fb817c..c8441030637a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -40,11 +40,24 @@ void vmw_resource_mob_attach(struct vmw_resource *res) { struct vmw_buffer_object *backup = res->backup; + struct rb_node **new = &backup->res_tree.rb_node, *parent = NULL; dma_resv_assert_held(res->backup->base.base.resv); res->used_prio = (res->res_dirty) ? res->func->dirty_prio : res->func->prio; - list_add_tail(&res->mob_head, &backup->res_list); + + while (*new) { + struct vmw_resource *this = + container_of(*new, struct vmw_resource, mob_node); + + parent = *new; + new = (res->backup_offset < this->backup_offset) ? + &((*new)->rb_left) : &((*new)->rb_right); + } + + rb_link_node(&res->mob_node, parent, new); + rb_insert_color(&res->mob_node, &backup->res_tree); + vmw_bo_prio_add(backup, res->used_prio); } @@ -58,7 +71,8 @@ void vmw_resource_mob_detach(struct vmw_resource *res) dma_resv_assert_held(backup->base.base.resv); if (vmw_resource_mob_attached(res)) { - list_del_init(&res->mob_head); + rb_erase(&res->mob_node, &backup->res_tree); + RB_CLEAR_NODE(&res->mob_node); vmw_bo_prio_del(backup, res->used_prio); } } @@ -119,6 +133,10 @@ static void vmw_resource_release(struct kref *kref) } res->backup_dirty = false; vmw_resource_mob_detach(res); + if (res->dirty) + res->func->dirty_free(res); + if (res->coherent) + vmw_bo_dirty_release(res->backup); ttm_bo_unreserve(bo); vmw_bo_unreference(&res->backup); } @@ -200,15 +218,17 @@ int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, res->res_free = res_free; res->dev_priv = dev_priv; res->func = func; + RB_CLEAR_NODE(&res->mob_node); INIT_LIST_HEAD(&res->lru_head); - INIT_LIST_HEAD(&res->mob_head); INIT_LIST_HEAD(&res->binding_head); res->id = -1; res->backup = NULL; res->backup_offset = 0; res->backup_dirty = false; res->res_dirty = false; + res->coherent = false; res->used_prio = 3; + res->dirty = NULL; if (delay_id) return 0; else @@ -373,7 +393,8 @@ out_no_bo: * should be retried once resources have been freed up. */ static int vmw_resource_do_validate(struct vmw_resource *res, - struct ttm_validate_buffer *val_buf) + struct ttm_validate_buffer *val_buf, + bool dirtying) { int ret = 0; const struct vmw_res_func *func = res->func; @@ -395,6 +416,39 @@ static int vmw_resource_do_validate(struct vmw_resource *res, vmw_resource_mob_attach(res); } + /* + * Handle the case where the backup mob is marked coherent but + * the resource isn't. + */ + if (func->dirty_alloc && vmw_resource_mob_attached(res) && + !res->coherent) { + if (res->backup->dirty && !res->dirty) { + ret = func->dirty_alloc(res); + if (ret) + return ret; + } else if (!res->backup->dirty && res->dirty) { + func->dirty_free(res); + } + } + + /* + * Transfer the dirty regions to the resource and update + * the resource. + */ + if (res->dirty) { + if (dirtying && !res->res_dirty) { + pgoff_t start = res->backup_offset >> PAGE_SHIFT; + pgoff_t end = __KERNEL_DIV_ROUND_UP + (res->backup_offset + res->backup_size, + PAGE_SIZE); + + vmw_bo_dirty_unmap(res->backup, start, end); + } + + vmw_bo_dirty_transfer_to_res(res); + return func->dirty_sync(res); + } + return 0; out_bind_failed: @@ -433,16 +487,28 @@ void vmw_resource_unreserve(struct vmw_resource *res, if (switch_backup && new_backup != res->backup) { if (res->backup) { vmw_resource_mob_detach(res); + if (res->coherent) + vmw_bo_dirty_release(res->backup); vmw_bo_unreference(&res->backup); } if (new_backup) { res->backup = vmw_bo_reference(new_backup); + + /* + * The validation code should already have added a + * dirty tracker here. + */ + WARN_ON(res->coherent && !new_backup->dirty); + vmw_resource_mob_attach(res); } else { res->backup = NULL; } + } else if (switch_backup && res->coherent) { + vmw_bo_dirty_release(res->backup); } + if (switch_backup) res->backup_offset = new_backup_offset; @@ -622,6 +688,7 @@ out_no_unbind: * to the device. * @res: The resource to make visible to the device. * @intr: Perform waits interruptible if possible. + * @dirtying: Pending GPU operation will dirty the resource * * On succesful return, any backup DMA buffer pointed to by @res->backup will * be reserved and validated. @@ -631,7 +698,8 @@ out_no_unbind: * Return: Zero on success, -ERESTARTSYS if interrupted, negative error code * on failure. */ -int vmw_resource_validate(struct vmw_resource *res, bool intr) +int vmw_resource_validate(struct vmw_resource *res, bool intr, + bool dirtying) { int ret; struct vmw_resource *evict_res; @@ -648,7 +716,7 @@ int vmw_resource_validate(struct vmw_resource *res, bool intr) if (res->backup) val_buf.bo = &res->backup->base; do { - ret = vmw_resource_do_validate(res, &val_buf); + ret = vmw_resource_do_validate(res, &val_buf, dirtying); if (likely(ret != -EBUSY)) break; @@ -711,19 +779,20 @@ out_no_validate: */ void vmw_resource_unbind_list(struct vmw_buffer_object *vbo) { - - struct vmw_resource *res, *next; struct ttm_validate_buffer val_buf = { .bo = &vbo->base, .num_shared = 0 }; dma_resv_assert_held(vbo->base.base.resv); - list_for_each_entry_safe(res, next, &vbo->res_list, mob_head) { - if (!res->func->unbind) - continue; + while (!RB_EMPTY_ROOT(&vbo->res_tree)) { + struct rb_node *node = vbo->res_tree.rb_node; + struct vmw_resource *res = + container_of(node, struct vmw_resource, mob_node); + + if (!WARN_ON_ONCE(!res->func->unbind)) + (void) res->func->unbind(res, res->res_dirty, &val_buf); - (void) res->func->unbind(res, res->res_dirty, &val_buf); res->backup_dirty = true; res->res_dirty = false; vmw_resource_mob_detach(res); @@ -947,7 +1016,7 @@ int vmw_resource_pin(struct vmw_resource *res, bool interruptible) /* Do we really need to pin the MOB as well? */ vmw_bo_pin_reserved(vbo, true); } - ret = vmw_resource_validate(res, interruptible); + ret = vmw_resource_validate(res, interruptible, true); if (vbo) ttm_bo_unreserve(&vbo->base); if (ret) @@ -1007,3 +1076,101 @@ enum vmw_res_type vmw_res_type(const struct vmw_resource *res) { return res->func->res_type; } + +/** + * vmw_resource_update_dirty - Update a resource's dirty tracker with a + * sequential range of touched backing store memory. + * @res: The resource. + * @start: The first page touched. + * @end: The last page touched + 1. + */ +void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start, + pgoff_t end) +{ + if (res->dirty) + res->func->dirty_range_add(res, start << PAGE_SHIFT, + end << PAGE_SHIFT); +} + +/** + * vmw_resources_clean - Clean resources intersecting a mob range + * @vbo: The mob buffer object + * @start: The mob page offset starting the range + * @end: The mob page offset ending the range + * @num_prefault: Returns how many pages including the first have been + * cleaned and are ok to prefault + */ +int vmw_resources_clean(struct vmw_buffer_object *vbo, pgoff_t start, + pgoff_t end, pgoff_t *num_prefault) +{ + struct rb_node *cur = vbo->res_tree.rb_node; + struct vmw_resource *found = NULL; + unsigned long res_start = start << PAGE_SHIFT; + unsigned long res_end = end << PAGE_SHIFT; + unsigned long last_cleaned = 0; + + /* + * Find the resource with lowest backup_offset that intersects the + * range. + */ + while (cur) { + struct vmw_resource *cur_res = + container_of(cur, struct vmw_resource, mob_node); + + if (cur_res->backup_offset >= res_end) { + cur = cur->rb_left; + } else if (cur_res->backup_offset + cur_res->backup_size <= + res_start) { + cur = cur->rb_right; + } else { + found = cur_res; + cur = cur->rb_left; + /* Continue to look for resources with lower offsets */ + } + } + + /* + * In order of increasing backup_offset, clean dirty resorces + * intersecting the range. + */ + while (found) { + if (found->res_dirty) { + int ret; + + if (!found->func->clean) + return -EINVAL; + + ret = found->func->clean(found); + if (ret) + return ret; + + found->res_dirty = false; + } + last_cleaned = found->backup_offset + found->backup_size; + cur = rb_next(&found->mob_node); + if (!cur) + break; + + found = container_of(cur, struct vmw_resource, mob_node); + if (found->backup_offset >= res_end) + break; + } + + /* + * Set number of pages allowed prefaulting and fence the buffer object + */ + *num_prefault = 1; + if (last_cleaned > res_start) { + struct ttm_buffer_object *bo = &vbo->base; + + *num_prefault = __KERNEL_DIV_ROUND_UP(last_cleaned - res_start, + PAGE_SIZE); + vmw_bo_fence_single(bo, NULL); + if (bo->moving) + dma_fence_put(bo->moving); + bo->moving = dma_fence_get + (dma_resv_get_excl(bo->base.resv)); + } + + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h index 984e588c62ca..3b7438b2d289 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -71,6 +71,13 @@ struct vmw_user_resource_conv { * @commit_notify: If the resource is a command buffer managed resource, * callback to notify that a define or remove command * has been committed to the device. + * @dirty_alloc: Allocate a dirty tracker. NULL if dirty-tracking is not + * supported. + * @dirty_free: Free the dirty tracker. + * @dirty_sync: Upload the dirty mob contents to the resource. + * @dirty_add_range: Add a sequential dirty range to the resource + * dirty tracker. + * @clean: Clean the resource. */ struct vmw_res_func { enum vmw_res_type res_type; @@ -90,6 +97,12 @@ struct vmw_res_func { struct ttm_validate_buffer *val_buf); void (*commit_notify)(struct vmw_resource *res, enum vmw_cmdbuf_res_state state); + int (*dirty_alloc)(struct vmw_resource *res); + void (*dirty_free)(struct vmw_resource *res); + int (*dirty_sync)(struct vmw_resource *res); + void (*dirty_range_add)(struct vmw_resource *res, size_t start, + size_t end); + int (*clean)(struct vmw_resource *res); }; /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index de0530b4dc1b..32b9131b2bae 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -68,6 +68,20 @@ struct vmw_surface_offset { uint32_t bo_offset; }; +/** + * vmw_surface_dirty - Surface dirty-tracker + * @cache: Cached layout information of the surface. + * @size: Accounting size for the struct vmw_surface_dirty. + * @num_subres: Number of subresources. + * @boxes: Array of SVGA3dBoxes indicating dirty regions. One per subresource. + */ +struct vmw_surface_dirty { + struct svga3dsurface_cache cache; + size_t size; + u32 num_subres; + SVGA3dBox boxes[0]; +}; + static void vmw_user_surface_free(struct vmw_resource *res); static struct vmw_resource * vmw_user_surface_base_to_res(struct ttm_base_object *base); @@ -96,6 +110,13 @@ vmw_gb_surface_reference_internal(struct drm_device *dev, struct drm_vmw_gb_surface_ref_ext_rep *rep, struct drm_file *file_priv); +static void vmw_surface_dirty_free(struct vmw_resource *res); +static int vmw_surface_dirty_alloc(struct vmw_resource *res); +static int vmw_surface_dirty_sync(struct vmw_resource *res); +static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, + size_t end); +static int vmw_surface_clean(struct vmw_resource *res); + static const struct vmw_user_resource_conv user_surface_conv = { .object_type = VMW_RES_SURFACE, .base_obj_to_res = vmw_user_surface_base_to_res, @@ -133,7 +154,12 @@ static const struct vmw_res_func vmw_gb_surface_func = { .create = vmw_gb_surface_create, .destroy = vmw_gb_surface_destroy, .bind = vmw_gb_surface_bind, - .unbind = vmw_gb_surface_unbind + .unbind = vmw_gb_surface_unbind, + .dirty_alloc = vmw_surface_dirty_alloc, + .dirty_free = vmw_surface_dirty_free, + .dirty_sync = vmw_surface_dirty_sync, + .dirty_range_add = vmw_surface_dirty_range_add, + .clean = vmw_surface_clean, }; /** @@ -639,6 +665,7 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_private *dev_priv = srf->res.dev_priv; uint32_t size = user_srf->size; + WARN_ON_ONCE(res->dirty); if (user_srf->master) drm_master_put(&user_srf->master); kfree(srf->offsets); @@ -1166,10 +1193,16 @@ static int vmw_gb_surface_bind(struct vmw_resource *res, cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_SURFACE; cmd2->header.size = sizeof(cmd2->body); cmd2->body.sid = res->id; - res->backup_dirty = false; } vmw_fifo_commit(dev_priv, submit_size); + if (res->backup->dirty && res->backup_dirty) { + /* We've just made a full upload. Cear dirty regions. */ + vmw_bo_dirty_clear_res(res); + } + + res->backup_dirty = false; + return 0; } @@ -1634,7 +1667,8 @@ vmw_gb_surface_define_internal(struct drm_device *dev, } } } else if (req->base.drm_surface_flags & - drm_vmw_surface_flag_create_buffer) + (drm_vmw_surface_flag_create_buffer | + drm_vmw_surface_flag_coherent)) ret = vmw_user_bo_alloc(dev_priv, tfile, res->backup_size, req->base.drm_surface_flags & @@ -1648,6 +1682,26 @@ vmw_gb_surface_define_internal(struct drm_device *dev, goto out_unlock; } + if (req->base.drm_surface_flags & drm_vmw_surface_flag_coherent) { + struct vmw_buffer_object *backup = res->backup; + + ttm_bo_reserve(&backup->base, false, false, NULL); + if (!res->func->dirty_alloc) + ret = -EINVAL; + if (!ret) + ret = vmw_bo_dirty_add(backup); + if (!ret) { + res->coherent = true; + ret = res->func->dirty_alloc(res); + } + ttm_bo_unreserve(&backup->base); + if (ret) { + vmw_resource_unreference(&res); + goto out_unlock; + } + + } + tmp = vmw_resource_reference(res); ret = ttm_prime_object_init(tfile, res->backup_size, &user_srf->prime, req->base.drm_surface_flags & @@ -1756,3 +1810,338 @@ out_bad_resource: return ret; } + +/** + * vmw_subres_dirty_add - Add a dirty region to a subresource + * @dirty: The surfaces's dirty tracker. + * @loc_start: The location corresponding to the start of the region. + * @loc_end: The location corresponding to the end of the region. + * + * As we are assuming that @loc_start and @loc_end represent a sequential + * range of backing store memory, if the region spans multiple lines then + * regardless of the x coordinate, the full lines are dirtied. + * Correspondingly if the region spans multiple z slices, then full rather + * than partial z slices are dirtied. + */ +static void vmw_subres_dirty_add(struct vmw_surface_dirty *dirty, + const struct svga3dsurface_loc *loc_start, + const struct svga3dsurface_loc *loc_end) +{ + const struct svga3dsurface_cache *cache = &dirty->cache; + SVGA3dBox *box = &dirty->boxes[loc_start->sub_resource]; + u32 mip = loc_start->sub_resource % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + u32 box_c2 = box->z + box->d; + + if (WARN_ON(loc_start->sub_resource >= dirty->num_subres)) + return; + + if (box->d == 0 || box->z > loc_start->z) + box->z = loc_start->z; + if (box_c2 < loc_end->z) + box->d = loc_end->z - box->z; + + if (loc_start->z + 1 == loc_end->z) { + box_c2 = box->y + box->h; + if (box->h == 0 || box->y > loc_start->y) + box->y = loc_start->y; + if (box_c2 < loc_end->y) + box->h = loc_end->y - box->y; + + if (loc_start->y + 1 == loc_end->y) { + box_c2 = box->x + box->w; + if (box->w == 0 || box->x > loc_start->x) + box->x = loc_start->x; + if (box_c2 < loc_end->x) + box->w = loc_end->x - box->x; + } else { + box->x = 0; + box->w = size->width; + } + } else { + box->y = 0; + box->h = size->height; + box->x = 0; + box->w = size->width; + } +} + +/** + * vmw_subres_dirty_full - Mark a full subresource as dirty + * @dirty: The surface's dirty tracker. + * @subres: The subresource + */ +static void vmw_subres_dirty_full(struct vmw_surface_dirty *dirty, u32 subres) +{ + const struct svga3dsurface_cache *cache = &dirty->cache; + u32 mip = subres % cache->num_mip_levels; + const struct drm_vmw_size *size = &cache->mip[mip].size; + SVGA3dBox *box = &dirty->boxes[subres]; + + box->x = 0; + box->y = 0; + box->z = 0; + box->w = size->width; + box->h = size->height; + box->d = size->depth; +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for texture + * surfaces. + */ +static void vmw_surface_tex_dirty_range_add(struct vmw_resource *res, + size_t start, size_t end) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + size_t backup_end = res->backup_offset + res->backup_size; + struct svga3dsurface_loc loc1, loc2; + const struct svga3dsurface_cache *cache; + + start = max_t(size_t, start, res->backup_offset) - res->backup_offset; + end = min(end, backup_end) - res->backup_offset; + cache = &dirty->cache; + svga3dsurface_get_loc(cache, &loc1, start); + svga3dsurface_get_loc(cache, &loc2, end - 1); + svga3dsurface_inc_loc(cache, &loc2); + + if (loc1.sub_resource + 1 == loc2.sub_resource) { + /* Dirty range covers a single sub-resource */ + vmw_subres_dirty_add(dirty, &loc1, &loc2); + } else { + /* Dirty range covers multiple sub-resources */ + struct svga3dsurface_loc loc_min, loc_max; + u32 sub_res; + + svga3dsurface_max_loc(cache, loc1.sub_resource, &loc_max); + vmw_subres_dirty_add(dirty, &loc1, &loc_max); + svga3dsurface_min_loc(cache, loc2.sub_resource - 1, &loc_min); + vmw_subres_dirty_add(dirty, &loc_min, &loc2); + for (sub_res = loc1.sub_resource + 1; + sub_res < loc2.sub_resource - 1; ++sub_res) + vmw_subres_dirty_full(dirty, sub_res); + } +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for buffer + * surfaces. + */ +static void vmw_surface_buf_dirty_range_add(struct vmw_resource *res, + size_t start, size_t end) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + const struct svga3dsurface_cache *cache = &dirty->cache; + size_t backup_end = res->backup_offset + cache->mip_chain_bytes; + SVGA3dBox *box = &dirty->boxes[0]; + u32 box_c2; + + box->h = box->d = 1; + start = max_t(size_t, start, res->backup_offset) - res->backup_offset; + end = min(end, backup_end) - res->backup_offset; + box_c2 = box->x + box->w; + if (box->w == 0 || box->x > start) + box->x = start; + if (box_c2 < end) + box->w = end - box->x; +} + +/* + * vmw_surface_tex_dirty_add_range - The dirty_add_range callback for surfaces + */ +static void vmw_surface_dirty_range_add(struct vmw_resource *res, size_t start, + size_t end) +{ + struct vmw_surface *srf = vmw_res_to_srf(res); + + if (WARN_ON(end <= res->backup_offset || + start >= res->backup_offset + res->backup_size)) + return; + + if (srf->format == SVGA3D_BUFFER) + vmw_surface_buf_dirty_range_add(res, start, end); + else + vmw_surface_tex_dirty_range_add(res, start, end); +} + +/* + * vmw_surface_dirty_sync - The surface's dirty_sync callback. + */ +static int vmw_surface_dirty_sync(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + bool has_dx = 0; + u32 i, num_dirty; + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + size_t alloc_size; + const struct svga3dsurface_cache *cache = &dirty->cache; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXUpdateSubResource body; + } *cmd1; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdUpdateGBImage body; + } *cmd2; + void *cmd; + + num_dirty = 0; + for (i = 0; i < dirty->num_subres; ++i) { + const SVGA3dBox *box = &dirty->boxes[i]; + + if (box->d) + num_dirty++; + } + + if (!num_dirty) + goto out; + + alloc_size = num_dirty * ((has_dx) ? sizeof(*cmd1) : sizeof(*cmd2)); + cmd = VMW_FIFO_RESERVE(dev_priv, alloc_size); + if (!cmd) + return -ENOMEM; + + cmd1 = cmd; + cmd2 = cmd; + + for (i = 0; i < dirty->num_subres; ++i) { + const SVGA3dBox *box = &dirty->boxes[i]; + + if (!box->d) + continue; + + /* + * DX_UPDATE_SUBRESOURCE is aware of array surfaces. + * UPDATE_GB_IMAGE is not. + */ + if (has_dx) { + cmd1->header.id = SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE; + cmd1->header.size = sizeof(cmd1->body); + cmd1->body.sid = res->id; + cmd1->body.subResource = i; + cmd1->body.box = *box; + cmd1++; + } else { + cmd2->header.id = SVGA_3D_CMD_UPDATE_GB_IMAGE; + cmd2->header.size = sizeof(cmd2->body); + cmd2->body.image.sid = res->id; + cmd2->body.image.face = i / cache->num_mip_levels; + cmd2->body.image.mipmap = i - + (cache->num_mip_levels * cmd2->body.image.face); + cmd2->body.box = *box; + cmd2++; + } + + } + vmw_fifo_commit(dev_priv, alloc_size); + out: + memset(&dirty->boxes[0], 0, sizeof(dirty->boxes[0]) * + dirty->num_subres); + + return 0; +} + +/* + * vmw_surface_dirty_alloc - The surface's dirty_alloc callback. + */ +static int vmw_surface_dirty_alloc(struct vmw_resource *res) +{ + struct vmw_surface *srf = vmw_res_to_srf(res); + struct vmw_surface_dirty *dirty; + u32 num_layers = 1; + u32 num_mip; + u32 num_subres; + u32 num_samples; + size_t dirty_size, acc_size; + static struct ttm_operation_ctx ctx = { + .interruptible = false, + .no_wait_gpu = false + }; + int ret; + + if (srf->array_size) + num_layers = srf->array_size; + else if (srf->flags & SVGA3D_SURFACE_CUBEMAP) + num_layers *= SVGA3D_MAX_SURFACE_FACES; + + num_mip = srf->mip_levels[0]; + if (!num_mip) + num_mip = 1; + + num_subres = num_layers * num_mip; + dirty_size = sizeof(*dirty) + num_subres * sizeof(dirty->boxes[0]); + acc_size = ttm_round_pot(dirty_size); + ret = ttm_mem_global_alloc(vmw_mem_glob(res->dev_priv), + acc_size, &ctx); + if (ret) { + VMW_DEBUG_USER("Out of graphics memory for surface " + "dirty tracker.\n"); + return ret; + } + + dirty = kvzalloc(dirty_size, GFP_KERNEL); + if (!dirty) { + ret = -ENOMEM; + goto out_no_dirty; + } + + num_samples = max_t(u32, 1, srf->multisample_count); + ret = svga3dsurface_setup_cache(&srf->base_size, srf->format, num_mip, + num_layers, num_samples, &dirty->cache); + if (ret) + goto out_no_cache; + + dirty->num_subres = num_subres; + dirty->size = acc_size; + res->dirty = (struct vmw_resource_dirty *) dirty; + + return 0; + +out_no_cache: + kvfree(dirty); +out_no_dirty: + ttm_mem_global_free(vmw_mem_glob(res->dev_priv), acc_size); + return ret; +} + +/* + * vmw_surface_dirty_free - The surface's dirty_free callback + */ +static void vmw_surface_dirty_free(struct vmw_resource *res) +{ + struct vmw_surface_dirty *dirty = + (struct vmw_surface_dirty *) res->dirty; + size_t acc_size = dirty->size; + + kvfree(dirty); + ttm_mem_global_free(vmw_mem_glob(res->dev_priv), acc_size); + res->dirty = NULL; +} + +/* + * vmw_surface_clean - The surface's clean callback + */ +static int vmw_surface_clean(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + size_t alloc_size; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdReadbackGBSurface body; + } *cmd; + + alloc_size = sizeof(*cmd); + cmd = VMW_FIFO_RESERVE(dev_priv, alloc_size); + if (!cmd) + return -ENOMEM; + + cmd->header.id = SVGA_3D_CMD_READBACK_GB_SURFACE; + cmd->header.size = sizeof(cmd->body); + cmd->body.sid = res->id; + vmw_fifo_commit(dev_priv, alloc_size); + + return 0; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c index 5a7b8bb420de..ce288756531b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c @@ -29,10 +29,23 @@ int vmw_mmap(struct file *filp, struct vm_area_struct *vma) { + static const struct vm_operations_struct vmw_vm_ops = { + .pfn_mkwrite = vmw_bo_vm_mkwrite, + .page_mkwrite = vmw_bo_vm_mkwrite, + .fault = vmw_bo_vm_fault, + .open = ttm_bo_vm_open, + .close = ttm_bo_vm_close + }; struct drm_file *file_priv = filp->private_data; struct vmw_private *dev_priv = vmw_priv(file_priv->minor->dev); + int ret = ttm_bo_mmap(filp, vma, &dev_priv->bdev); - return ttm_bo_mmap(filp, vma, &dev_priv->bdev); + if (ret) + return ret; + + vma->vm_ops = &vmw_vm_ops; + + return 0; } /* struct vmw_validation_mem callback */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c index 7bff3628fc54..e69bc373ae2e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -33,6 +33,8 @@ * struct vmw_validation_bo_node - Buffer object validation metadata. * @base: Metadata used for TTM reservation- and validation. * @hash: A hash entry used for the duplicate detection hash table. + * @coherent_count: If switching backup buffers, number of new coherent + * resources that will have this buffer as a backup buffer. * @as_mob: Validate as mob. * @cpu_blit: Validate for cpu blit access. * @@ -42,6 +44,7 @@ struct vmw_validation_bo_node { struct ttm_validate_buffer base; struct drm_hash_item hash; + unsigned int coherent_count; u32 as_mob : 1; u32 cpu_blit : 1; }; @@ -459,6 +462,19 @@ int vmw_validation_res_reserve(struct vmw_validation_context *ctx, if (ret) goto out_unreserve; } + + if (val->switching_backup && val->new_backup && + res->coherent) { + struct vmw_validation_bo_node *bo_node = + vmw_validation_find_bo_dup(ctx, + val->new_backup); + + if (WARN_ON(!bo_node)) { + ret = -EINVAL; + goto out_unreserve; + } + bo_node->coherent_count++; + } } return 0; @@ -565,6 +581,9 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) int ret; list_for_each_entry(entry, &ctx->bo_list, base.head) { + struct vmw_buffer_object *vbo = + container_of(entry->base.bo, typeof(*vbo), base); + if (entry->cpu_blit) { struct ttm_operation_ctx ctx = { .interruptible = intr, @@ -579,6 +598,27 @@ int vmw_validation_bo_validate(struct vmw_validation_context *ctx, bool intr) } if (ret) return ret; + + /* + * Rather than having the resource code allocating the bo + * dirty tracker in resource_unreserve() where we can't fail, + * Do it here when validating the buffer object. + */ + if (entry->coherent_count) { + unsigned int coherent_count = entry->coherent_count; + + while (coherent_count) { + ret = vmw_bo_dirty_add(vbo); + if (ret) + return ret; + + coherent_count--; + } + entry->coherent_count -= coherent_count; + } + + if (vbo->dirty) + vmw_bo_dirty_scan(vbo); } return 0; } @@ -604,7 +644,8 @@ int vmw_validation_res_validate(struct vmw_validation_context *ctx, bool intr) struct vmw_resource *res = val->res; struct vmw_buffer_object *backup = res->backup; - ret = vmw_resource_validate(res, intr); + ret = vmw_resource_validate(res, intr, val->dirty_set && + val->dirty); if (ret) { if (ret != -ERESTARTSYS) DRM_ERROR("Failed to validate resource.\n"); @@ -831,3 +872,34 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, ctx->mem_size_left += size; return 0; } + +/** + * vmw_validation_bo_backoff - Unreserve buffer objects registered with a + * validation context + * @ctx: The validation context + * + * This function unreserves the buffer objects previously reserved using + * vmw_validation_bo_reserve. It's typically used as part of an error path + */ +void vmw_validation_bo_backoff(struct vmw_validation_context *ctx) +{ + struct vmw_validation_bo_node *entry; + + /* + * Switching coherent resource backup buffers failed. + * Release corresponding buffer object dirty trackers. + */ + list_for_each_entry(entry, &ctx->bo_list, base.head) { + if (entry->coherent_count) { + unsigned int coherent_count = entry->coherent_count; + struct vmw_buffer_object *vbo = + container_of(entry->base.bo, typeof(*vbo), + base); + + while (coherent_count--) + vmw_bo_dirty_release(vbo); + } + } + + ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h index 71ce4b318850..739906d1b3eb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.h @@ -174,20 +174,6 @@ vmw_validation_bo_reserve(struct vmw_validation_context *ctx, } /** - * vmw_validation_bo_backoff - Unreserve buffer objects registered with a - * validation context - * @ctx: The validation context - * - * This function unreserves the buffer objects previously reserved using - * vmw_validation_bo_reserve. It's typically used as part of an error path - */ -static inline void -vmw_validation_bo_backoff(struct vmw_validation_context *ctx) -{ - ttm_eu_backoff_reservation(&ctx->ticket, &ctx->bo_list); -} - -/** * vmw_validation_bo_fence - Unreserve and fence buffer objects registered * with a validation context * @ctx: The validation context @@ -269,4 +255,6 @@ int vmw_validation_preload_res(struct vmw_validation_context *ctx, unsigned int size); void vmw_validation_res_set_dirty(struct vmw_validation_context *ctx, void *val_private, u32 dirty); +void vmw_validation_bo_backoff(struct vmw_validation_context *ctx); + #endif diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 1ecb5124421c..494a39e74939 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -525,6 +525,7 @@ config HID_LENOVO config HID_LOGITECH tristate "Logitech devices" depends on HID + depends on LEDS_CLASS default !EXPERT ---help--- Support for Logitech devices that are not fully compliant with HID standard. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0c03308cfb08..bfefa365b1ce 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_LOGITECH) += hid-lg-g15.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o obj-$(CONFIG_HID_MACALLY) += hid-macally.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 63fdbf09b044..e0b241bd3070 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -212,6 +212,18 @@ static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) } /* + * Concatenate usage which defines 16 bits or less with the + * currently defined usage page to form a 32 bit usage + */ + +static void complete_usage(struct hid_parser *parser, unsigned int index) +{ + parser->local.usage[index] &= 0xFFFF; + parser->local.usage[index] |= + (parser->global.usage_page & 0xFFFF) << 16; +} + +/* * Add a usage to the temporary parser table. */ @@ -222,6 +234,14 @@ static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size) return -1; } parser->local.usage[parser->local.usage_index] = usage; + + /* + * If Usage item only includes usage id, concatenate it with + * currently defined usage page + */ + if (size <= 2) + complete_usage(parser, parser->local.usage_index); + parser->local.usage_size[parser->local.usage_index] = size; parser->local.collection_index[parser->local.usage_index] = parser->collection_stack_ptr ? @@ -543,13 +563,32 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) * usage value." */ -static void hid_concatenate_usage_page(struct hid_parser *parser) +static void hid_concatenate_last_usage_page(struct hid_parser *parser) { int i; + unsigned int usage_page; + unsigned int current_page; + + if (!parser->local.usage_index) + return; - for (i = 0; i < parser->local.usage_index; i++) - if (parser->local.usage_size[i] <= 2) - parser->local.usage[i] += parser->global.usage_page << 16; + usage_page = parser->global.usage_page; + + /* + * Concatenate usage page again only if last declared Usage Page + * has not been already used in previous usages concatenation + */ + for (i = parser->local.usage_index - 1; i >= 0; i--) { + if (parser->local.usage_size[i] > 2) + /* Ignore extended usages */ + continue; + + current_page = parser->local.usage[i] >> 16; + if (current_page == usage_page) + break; + + complete_usage(parser, i); + } } /* @@ -561,7 +600,7 @@ static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) __u32 data; int ret; - hid_concatenate_usage_page(parser); + hid_concatenate_last_usage_page(parser); data = item_udata(item); @@ -742,6 +781,10 @@ static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) if (usage == 0xff0000c5 && parser->global.report_count == 256 && parser->global.report_size == 8) parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; + + if (usage == 0xff0000c6 && parser->global.report_count == 1 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; } static void hid_scan_collection(struct hid_parser *parser, unsigned type) @@ -772,7 +815,7 @@ static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) __u32 data; int i; - hid_concatenate_usage_page(parser); + hid_concatenate_last_usage_page(parser); data = item_udata(item); diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index d86a9189e88f..2aa4ed157aec 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -35,6 +35,7 @@ struct cbas_ec { struct device *dev; /* The platform device (EC) */ struct input_dev *input; bool base_present; + bool base_folded; struct notifier_block notifier; }; @@ -208,7 +209,14 @@ static int __cbas_ec_probe(struct platform_device *pdev) return error; } - input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present); + if (!cbas_ec.base_present) + cbas_ec.base_folded = false; + + dev_dbg(&pdev->dev, "%s: base: %d, folded: %d\n", __func__, + cbas_ec.base_present, cbas_ec.base_folded); + + input_report_switch(input, SW_TABLET_MODE, + !cbas_ec.base_present || cbas_ec.base_folded); cbas_ec_set_input(input); @@ -322,10 +330,9 @@ static int hammer_kbd_brightness_set_blocking(struct led_classdev *cdev, static int hammer_register_leds(struct hid_device *hdev) { struct hammer_kbd_leds *kbd_backlight; + int error; - kbd_backlight = devm_kzalloc(&hdev->dev, - sizeof(*kbd_backlight), - GFP_KERNEL); + kbd_backlight = kzalloc(sizeof(*kbd_backlight), GFP_KERNEL); if (!kbd_backlight) return -ENOMEM; @@ -339,12 +346,31 @@ static int hammer_register_leds(struct hid_device *hdev) /* Set backlight to 0% initially. */ hammer_kbd_brightness_set_blocking(&kbd_backlight->cdev, 0); - return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev); + error = led_classdev_register(&hdev->dev, &kbd_backlight->cdev); + if (error) + goto err_free_mem; + + hid_set_drvdata(hdev, kbd_backlight); + return 0; + +err_free_mem: + kfree(kbd_backlight); + return error; +} + +static void hammer_unregister_leds(struct hid_device *hdev) +{ + struct hammer_kbd_leds *kbd_backlight = hid_get_drvdata(hdev); + + if (kbd_backlight) { + led_classdev_unregister(&kbd_backlight->cdev); + kfree(kbd_backlight); + } } #define HID_UP_GOOGLEVENDOR 0xffd10000 #define HID_VD_KBD_FOLDED 0x00000019 -#define WHISKERS_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) +#define HID_USAGE_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) /* HID usage for keyboard backlight (Alphanumeric display brightness) */ #define HID_AD_BRIGHTNESS 0x00140046 @@ -354,8 +380,7 @@ static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_usage *usage, unsigned long **bit, int *max) { - if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - usage->hid == WHISKERS_KBD_FOLDED) { + if (usage->hid == HID_USAGE_KBD_FOLDED) { /* * We do not want to have this usage mapped as it will get * mixed in with "base attached" signal and delivered over @@ -372,19 +397,19 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, { unsigned long flags; - if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - usage->hid == WHISKERS_KBD_FOLDED) { + if (usage->hid == HID_USAGE_KBD_FOLDED) { spin_lock_irqsave(&cbas_ec_lock, flags); - hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, - cbas_ec.base_present, value); - /* - * We should not get event if base is detached, but in case - * we happen to service HID and EC notifications out of order - * let's still check the "base present" flag. + * If we are getting events from Whiskers that means that it + * is attached to the lid. */ - if (cbas_ec.input && cbas_ec.base_present) { + cbas_ec.base_present = true; + cbas_ec.base_folded = value; + hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, + cbas_ec.base_present, cbas_ec.base_folded); + + if (cbas_ec.input) { input_report_switch(cbas_ec.input, SW_TABLET_MODE, value); input_sync(cbas_ec.input); @@ -397,33 +422,22 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, return 0; } -static bool hammer_is_keyboard_interface(struct hid_device *hdev) +static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type, + unsigned application, unsigned usage) { - struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT]; - struct hid_report *report; - - list_for_each_entry(report, &re->report_list, list) - if (report->application == HID_GD_KEYBOARD) - return true; - - return false; -} - -static bool hammer_has_backlight_control(struct hid_device *hdev) -{ - struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT]; + struct hid_report_enum *re = &hdev->report_enum[report_type]; struct hid_report *report; int i, j; list_for_each_entry(report, &re->report_list, list) { - if (report->application != HID_GD_KEYBOARD) + if (report->application != application) continue; for (i = 0; i < report->maxfield; i++) { struct hid_field *field = report->field[i]; for (j = 0; j < field->maxusage; j++) - if (field->usage[j].hid == HID_AD_BRIGHTNESS) + if (field->usage[j].hid == usage) return true; } } @@ -431,21 +445,23 @@ static bool hammer_has_backlight_control(struct hid_device *hdev) return false; } +static bool hammer_has_folded_event(struct hid_device *hdev) +{ + return hammer_has_usage(hdev, HID_INPUT_REPORT, + HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED); +} + +static bool hammer_has_backlight_control(struct hid_device *hdev) +{ + return hammer_has_usage(hdev, HID_OUTPUT_REPORT, + HID_GD_KEYBOARD, HID_AD_BRIGHTNESS); +} + static int hammer_probe(struct hid_device *hdev, const struct hid_device_id *id) { int error; - /* - * We always want to poll for, and handle tablet mode events from - * Whiskers, even when nobody has opened the input device. This also - * prevents the hid core from dropping early tablet mode events from - * the device. - */ - if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && - hammer_is_keyboard_interface(hdev)) - hdev->quirks |= HID_QUIRK_ALWAYS_POLL; - error = hid_parse(hdev); if (error) return error; @@ -454,6 +470,19 @@ static int hammer_probe(struct hid_device *hdev, if (error) return error; + /* + * We always want to poll for, and handle tablet mode events from + * devices that have folded usage, even when nobody has opened the input + * device. This also prevents the hid core from dropping early tablet + * mode events from the device. + */ + if (hammer_has_folded_event(hdev)) { + hdev->quirks |= HID_QUIRK_ALWAYS_POLL; + error = hid_hw_open(hdev); + if (error) + return error; + } + if (hammer_has_backlight_control(hdev)) { error = hammer_register_leds(hdev); if (error) @@ -465,6 +494,36 @@ static int hammer_probe(struct hid_device *hdev, return 0; } +static void hammer_remove(struct hid_device *hdev) +{ + unsigned long flags; + + if (hammer_has_folded_event(hdev)) { + hid_hw_close(hdev); + + /* + * If we are disconnecting then most likely Whiskers is + * being removed. Even if it is not removed, without proper + * keyboard we should not stay in clamshell mode. + * + * The reason for doing it here and not waiting for signal + * from EC, is that on some devices there are high leakage + * on Whiskers pins and we do not detect disconnect reliably, + * resulting in devices being stuck in clamshell mode. + */ + spin_lock_irqsave(&cbas_ec_lock, flags); + if (cbas_ec.input && cbas_ec.base_present) { + input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1); + input_sync(cbas_ec.input); + } + cbas_ec.base_present = false; + spin_unlock_irqrestore(&cbas_ec_lock, flags); + } + + hammer_unregister_leds(hdev); + + hid_hw_stop(hdev); +} static const struct hid_device_id hammer_devices[] = { { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, @@ -487,6 +546,7 @@ static struct hid_driver hammer_driver = { .name = "hammer", .id_table = hammer_devices, .probe = hammer_probe, + .remove = hammer_remove, .input_mapping = hammer_input_mapping, .event = hammer_event, }; diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 79a28fc91521..dddfca555df9 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -192,6 +192,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, if (desc->bLength == 0) goto cleanup; + /* The pointer is not NULL when we resume from hibernation */ + if (input_device->hid_desc != NULL) + kfree(input_device->hid_desc); input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); if (!input_device->hid_desc) @@ -203,6 +206,9 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, goto cleanup; } + /* The pointer is not NULL when we resume from hibernation */ + if (input_device->report_desc != NULL) + kfree(input_device->report_desc); input_device->report_desc = kzalloc(input_device->report_desc_size, GFP_ATOMIC); @@ -342,6 +348,8 @@ static int mousevsc_connect_to_vsp(struct hv_device *device) struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *response; + reinit_completion(&input_dev->wait_event); + request = &input_dev->protocol_req; memset(request, 0, sizeof(struct mousevsc_prt_msg)); @@ -541,6 +549,30 @@ static int mousevsc_remove(struct hv_device *dev) return 0; } +static int mousevsc_suspend(struct hv_device *dev) +{ + vmbus_close(dev->channel); + + return 0; +} + +static int mousevsc_resume(struct hv_device *dev) +{ + int ret; + + ret = vmbus_open(dev->channel, + INPUTVSC_SEND_RING_BUFFER_SIZE, + INPUTVSC_RECV_RING_BUFFER_SIZE, + NULL, 0, + mousevsc_on_channel_callback, + dev); + if (ret) + return ret; + + ret = mousevsc_connect_to_vsp(dev); + return ret; +} + static const struct hv_vmbus_device_id id_table[] = { /* Mouse guid */ { HV_MOUSE_GUID, }, @@ -554,6 +586,8 @@ static struct hv_driver mousevsc_drv = { .id_table = id_table, .probe = mousevsc_probe, .remove = mousevsc_remove, + .suspend = mousevsc_suspend, + .resume = mousevsc_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 447e8db21174..7e1689ef35f5 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -573,6 +573,7 @@ #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A 0x094a #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941 0x0941 #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641 0x0641 +#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a 0x1f4a #define USB_VENDOR_ID_HUION 0x256c #define USB_DEVICE_ID_HUION_TABLET 0x006e @@ -749,6 +750,10 @@ #define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 +#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222 +#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227 +#define USB_DEVICE_ID_LOGITECH_G510 0xc22d +#define USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO 0xc22e #define USB_DEVICE_ID_LOGITECH_G29_WHEEL 0xc24f #define USB_DEVICE_ID_LOGITECH_G920_WHEEL 0xc262 #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 @@ -959,6 +964,7 @@ #define I2C_VENDOR_ID_RAYDIUM 0x2386 #define I2C_PRODUCT_ID_RAYDIUM_4B33 0x4b33 +#define I2C_PRODUCT_ID_RAYDIUM_3118 0x3118 #define USB_VENDOR_ID_RAZER 0x1532 #define USB_DEVICE_ID_RAZER_BLADE_14 0x011D diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c new file mode 100644 index 000000000000..8a9268a5c66a --- /dev/null +++ b/drivers/hid/hid-lg-g15.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for gaming keys on Logitech gaming keyboards (such as the G15) + * + * Copyright (c) 2019 Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <linux/usb.h> +#include <linux/wait.h> + +#include "hid-ids.h" + +#define LG_G15_TRANSFER_BUF_SIZE 20 + +#define LG_G15_FEATURE_REPORT 0x02 + +#define LG_G510_FEATURE_M_KEYS_LEDS 0x04 +#define LG_G510_FEATURE_BACKLIGHT_RGB 0x05 +#define LG_G510_FEATURE_POWER_ON_RGB 0x06 + +enum lg_g15_model { + LG_G15, + LG_G15_V2, + LG_G510, + LG_G510_USB_AUDIO, +}; + +enum lg_g15_led_type { + LG_G15_KBD_BRIGHTNESS, + LG_G15_LCD_BRIGHTNESS, + LG_G15_BRIGHTNESS_MAX, + LG_G15_MACRO_PRESET1 = 2, + LG_G15_MACRO_PRESET2, + LG_G15_MACRO_PRESET3, + LG_G15_MACRO_RECORD, + LG_G15_LED_MAX +}; + +struct lg_g15_led { + struct led_classdev cdev; + enum led_brightness brightness; + enum lg_g15_led_type led; + u8 red, green, blue; +}; + +struct lg_g15_data { + /* Must be first for proper dma alignment */ + u8 transfer_buf[LG_G15_TRANSFER_BUF_SIZE]; + /* Protects the transfer_buf and led brightness */ + struct mutex mutex; + struct work_struct work; + struct input_dev *input; + struct hid_device *hdev; + enum lg_g15_model model; + struct lg_g15_led leds[LG_G15_LED_MAX]; + bool game_mode_enabled; +}; + +/******** G15 and G15 v2 LED functions ********/ + +static int lg_g15_update_led_brightness(struct lg_g15_data *g15) +{ + int ret; + + ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT, + g15->transfer_buf, 4, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != 4) { + hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = g15->transfer_buf[1]; + g15->leds[LG_G15_LCD_BRIGHTNESS].brightness = g15->transfer_buf[2]; + + g15->leds[LG_G15_MACRO_PRESET1].brightness = + !(g15->transfer_buf[3] & 0x01); + g15->leds[LG_G15_MACRO_PRESET2].brightness = + !(g15->transfer_buf[3] & 0x02); + g15->leds[LG_G15_MACRO_PRESET3].brightness = + !(g15->transfer_buf[3] & 0x04); + g15->leds[LG_G15_MACRO_RECORD].brightness = + !(g15->transfer_buf[3] & 0x08); + + return 0; +} + +static enum led_brightness lg_g15_led_get(struct led_classdev *led_cdev) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + enum led_brightness brightness; + + mutex_lock(&g15->mutex); + lg_g15_update_led_brightness(g15); + brightness = g15->leds[g15_led->led].brightness; + mutex_unlock(&g15->mutex); + + return brightness; +} + +static int lg_g15_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + u8 val, mask = 0; + int i, ret; + + /* Ignore LED off on unregister / keyboard unplug */ + if (led_cdev->flags & LED_UNREGISTERING) + return 0; + + mutex_lock(&g15->mutex); + + g15->transfer_buf[0] = LG_G15_FEATURE_REPORT; + g15->transfer_buf[3] = 0; + + if (g15_led->led < LG_G15_BRIGHTNESS_MAX) { + g15->transfer_buf[1] = g15_led->led + 1; + g15->transfer_buf[2] = brightness << (g15_led->led * 4); + } else { + for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) { + if (i == g15_led->led) + val = brightness; + else + val = g15->leds[i].brightness; + + if (val) + mask |= 1 << (i - LG_G15_MACRO_PRESET1); + } + + g15->transfer_buf[1] = 0x04; + g15->transfer_buf[2] = ~mask; + } + + ret = hid_hw_raw_request(g15->hdev, LG_G15_FEATURE_REPORT, + g15->transfer_buf, 4, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret == 4) { + /* Success */ + g15_led->brightness = brightness; + ret = 0; + } else { + hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret); + ret = (ret < 0) ? ret : -EIO; + } + + mutex_unlock(&g15->mutex); + + return ret; +} + +static void lg_g15_leds_changed_work(struct work_struct *work) +{ + struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work); + enum led_brightness old_brightness[LG_G15_BRIGHTNESS_MAX]; + enum led_brightness brightness[LG_G15_BRIGHTNESS_MAX]; + int i, ret; + + mutex_lock(&g15->mutex); + for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) + old_brightness[i] = g15->leds[i].brightness; + + ret = lg_g15_update_led_brightness(g15); + + for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) + brightness[i] = g15->leds[i].brightness; + mutex_unlock(&g15->mutex); + + if (ret) + return; + + for (i = 0; i < LG_G15_BRIGHTNESS_MAX; i++) { + if (brightness[i] == old_brightness[i]) + continue; + + led_classdev_notify_brightness_hw_changed(&g15->leds[i].cdev, + brightness[i]); + } +} + +/******** G510 LED functions ********/ + +static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i) +{ + int ret, high; + + ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_BACKLIGHT_RGB + i, + g15->transfer_buf, 4, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != 4) { + hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret); + return (ret < 0) ? ret : -EIO; + } + + high = max3(g15->transfer_buf[1], g15->transfer_buf[2], + g15->transfer_buf[3]); + + if (high) { + g15->leds[i].red = + DIV_ROUND_CLOSEST(g15->transfer_buf[1] * 255, high); + g15->leds[i].green = + DIV_ROUND_CLOSEST(g15->transfer_buf[2] * 255, high); + g15->leds[i].blue = + DIV_ROUND_CLOSEST(g15->transfer_buf[3] * 255, high); + g15->leds[i].brightness = high; + } else { + g15->leds[i].red = 255; + g15->leds[i].green = 255; + g15->leds[i].blue = 255; + g15->leds[i].brightness = 0; + } + + return 0; +} + +/* Must be called with g15->mutex locked */ +static int lg_g510_kbd_led_write(struct lg_g15_data *g15, + struct lg_g15_led *g15_led, + enum led_brightness brightness) +{ + int ret; + + g15->transfer_buf[0] = 5 + g15_led->led; + g15->transfer_buf[1] = + DIV_ROUND_CLOSEST(g15_led->red * brightness, 255); + g15->transfer_buf[2] = + DIV_ROUND_CLOSEST(g15_led->green * brightness, 255); + g15->transfer_buf[3] = + DIV_ROUND_CLOSEST(g15_led->blue * brightness, 255); + + ret = hid_hw_raw_request(g15->hdev, + LG_G510_FEATURE_BACKLIGHT_RGB + g15_led->led, + g15->transfer_buf, 4, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret == 4) { + /* Success */ + g15_led->brightness = brightness; + ret = 0; + } else { + hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret); + ret = (ret < 0) ? ret : -EIO; + } + + return ret; +} + +static int lg_g510_kbd_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + int ret; + + /* Ignore LED off on unregister / keyboard unplug */ + if (led_cdev->flags & LED_UNREGISTERING) + return 0; + + mutex_lock(&g15->mutex); + ret = lg_g510_kbd_led_write(g15, g15_led, brightness); + mutex_unlock(&g15->mutex); + + return ret; +} + +static enum led_brightness lg_g510_kbd_led_get(struct led_classdev *led_cdev) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + + return g15_led->brightness; +} + +static ssize_t color_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + unsigned long value; + int ret; + + if (count < 7 || (count == 8 && buf[7] != '\n') || count > 8) + return -EINVAL; + + if (buf[0] != '#') + return -EINVAL; + + ret = kstrtoul(buf + 1, 16, &value); + if (ret) + return ret; + + mutex_lock(&g15->mutex); + g15_led->red = (value & 0xff0000) >> 16; + g15_led->green = (value & 0x00ff00) >> 8; + g15_led->blue = (value & 0x0000ff); + ret = lg_g510_kbd_led_write(g15, g15_led, g15_led->brightness); + mutex_unlock(&g15->mutex); + + return (ret < 0) ? ret : count; +} + +static ssize_t color_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + ssize_t ret; + + mutex_lock(&g15->mutex); + ret = sprintf(buf, "#%02x%02x%02x\n", + g15_led->red, g15_led->green, g15_led->blue); + mutex_unlock(&g15->mutex); + + return ret; +} + +static DEVICE_ATTR_RW(color); + +static struct attribute *lg_g510_kbd_led_attrs[] = { + &dev_attr_color.attr, + NULL, +}; + +static const struct attribute_group lg_g510_kbd_led_group = { + .attrs = lg_g510_kbd_led_attrs, +}; + +static const struct attribute_group *lg_g510_kbd_led_groups[] = { + &lg_g510_kbd_led_group, + NULL, +}; + +static void lg_g510_leds_sync_work(struct work_struct *work) +{ + struct lg_g15_data *g15 = container_of(work, struct lg_g15_data, work); + + mutex_lock(&g15->mutex); + lg_g510_kbd_led_write(g15, &g15->leds[LG_G15_KBD_BRIGHTNESS], + g15->leds[LG_G15_KBD_BRIGHTNESS].brightness); + mutex_unlock(&g15->mutex); +} + +static int lg_g510_update_mkey_led_brightness(struct lg_g15_data *g15) +{ + int ret; + + ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS, + g15->transfer_buf, 2, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret != 2) { + hid_err(g15->hdev, "Error getting LED brightness: %d\n", ret); + ret = (ret < 0) ? ret : -EIO; + } + + g15->leds[LG_G15_MACRO_PRESET1].brightness = + !!(g15->transfer_buf[1] & 0x80); + g15->leds[LG_G15_MACRO_PRESET2].brightness = + !!(g15->transfer_buf[1] & 0x40); + g15->leds[LG_G15_MACRO_PRESET3].brightness = + !!(g15->transfer_buf[1] & 0x20); + g15->leds[LG_G15_MACRO_RECORD].brightness = + !!(g15->transfer_buf[1] & 0x10); + + return 0; +} + +static enum led_brightness lg_g510_mkey_led_get(struct led_classdev *led_cdev) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + enum led_brightness brightness; + + mutex_lock(&g15->mutex); + lg_g510_update_mkey_led_brightness(g15); + brightness = g15->leds[g15_led->led].brightness; + mutex_unlock(&g15->mutex); + + return brightness; +} + +static int lg_g510_mkey_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct lg_g15_led *g15_led = + container_of(led_cdev, struct lg_g15_led, cdev); + struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent); + u8 val, mask = 0; + int i, ret; + + /* Ignore LED off on unregister / keyboard unplug */ + if (led_cdev->flags & LED_UNREGISTERING) + return 0; + + mutex_lock(&g15->mutex); + + for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; i++) { + if (i == g15_led->led) + val = brightness; + else + val = g15->leds[i].brightness; + + if (val) + mask |= 0x80 >> (i - LG_G15_MACRO_PRESET1); + } + + g15->transfer_buf[0] = LG_G510_FEATURE_M_KEYS_LEDS; + g15->transfer_buf[1] = mask; + + ret = hid_hw_raw_request(g15->hdev, LG_G510_FEATURE_M_KEYS_LEDS, + g15->transfer_buf, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + if (ret == 2) { + /* Success */ + g15_led->brightness = brightness; + ret = 0; + } else { + hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret); + ret = (ret < 0) ? ret : -EIO; + } + + mutex_unlock(&g15->mutex); + + return ret; +} + +/******** Generic LED functions ********/ +static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15) +{ + int ret; + + switch (g15->model) { + case LG_G15: + case LG_G15_V2: + return lg_g15_update_led_brightness(g15); + case LG_G510: + case LG_G510_USB_AUDIO: + ret = lg_g510_get_initial_led_brightness(g15, 0); + if (ret) + return ret; + + ret = lg_g510_get_initial_led_brightness(g15, 1); + if (ret) + return ret; + + return lg_g510_update_mkey_led_brightness(g15); + } + return -EINVAL; /* Never reached */ +} + +/******** Input functions ********/ + +/* On the G15 Mark I Logitech has been quite creative with which bit is what */ +static int lg_g15_event(struct lg_g15_data *g15, u8 *data, int size) +{ + int i, val; + + /* G1 - G6 */ + for (i = 0; i < 6; i++) { + val = data[i + 1] & (1 << i); + input_report_key(g15->input, KEY_MACRO1 + i, val); + } + /* G7 - G12 */ + for (i = 0; i < 6; i++) { + val = data[i + 2] & (1 << i); + input_report_key(g15->input, KEY_MACRO7 + i, val); + } + /* G13 - G17 */ + for (i = 0; i < 5; i++) { + val = data[i + 1] & (4 << i); + input_report_key(g15->input, KEY_MACRO13 + i, val); + } + /* G18 */ + input_report_key(g15->input, KEY_MACRO18, data[8] & 0x40); + + /* M1 - M3 */ + for (i = 0; i < 3; i++) { + val = data[i + 6] & (1 << i); + input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val); + } + /* MR */ + input_report_key(g15->input, KEY_MACRO_RECORD_START, data[7] & 0x40); + + /* Most left (round) button below the LCD */ + input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[8] & 0x80); + /* 4 other buttons below the LCD */ + for (i = 0; i < 4; i++) { + val = data[i + 2] & 0x80; + input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val); + } + + /* Backlight cycle button pressed? */ + if (data[1] & 0x80) + schedule_work(&g15->work); + + input_sync(g15->input); + return 0; +} + +static int lg_g15_v2_event(struct lg_g15_data *g15, u8 *data, int size) +{ + int i, val; + + /* G1 - G6 */ + for (i = 0; i < 6; i++) { + val = data[1] & (1 << i); + input_report_key(g15->input, KEY_MACRO1 + i, val); + } + + /* M1 - M3 + MR */ + input_report_key(g15->input, KEY_MACRO_PRESET1, data[1] & 0x40); + input_report_key(g15->input, KEY_MACRO_PRESET2, data[1] & 0x80); + input_report_key(g15->input, KEY_MACRO_PRESET3, data[2] & 0x20); + input_report_key(g15->input, KEY_MACRO_RECORD_START, data[2] & 0x40); + + /* Round button to the left of the LCD */ + input_report_key(g15->input, KEY_KBD_LCD_MENU1, data[2] & 0x80); + /* 4 buttons below the LCD */ + for (i = 0; i < 4; i++) { + val = data[2] & (2 << i); + input_report_key(g15->input, KEY_KBD_LCD_MENU2 + i, val); + } + + /* Backlight cycle button pressed? */ + if (data[2] & 0x01) + schedule_work(&g15->work); + + input_sync(g15->input); + return 0; +} + +static int lg_g510_event(struct lg_g15_data *g15, u8 *data, int size) +{ + bool game_mode_enabled; + int i, val; + + /* G1 - G18 */ + for (i = 0; i < 18; i++) { + val = data[i / 8 + 1] & (1 << (i % 8)); + input_report_key(g15->input, KEY_MACRO1 + i, val); + } + + /* Game mode on/off slider */ + game_mode_enabled = data[3] & 0x04; + if (game_mode_enabled != g15->game_mode_enabled) { + if (game_mode_enabled) + hid_info(g15->hdev, "Game Mode enabled, Windows (super) key is disabled\n"); + else + hid_info(g15->hdev, "Game Mode disabled\n"); + g15->game_mode_enabled = game_mode_enabled; + } + + /* M1 - M3 */ + for (i = 0; i < 3; i++) { + val = data[3] & (0x10 << i); + input_report_key(g15->input, KEY_MACRO_PRESET1 + i, val); + } + /* MR */ + input_report_key(g15->input, KEY_MACRO_RECORD_START, data[3] & 0x80); + + /* LCD menu keys */ + for (i = 0; i < 5; i++) { + val = data[4] & (1 << i); + input_report_key(g15->input, KEY_KBD_LCD_MENU1 + i, val); + } + + /* Headphone Mute */ + input_report_key(g15->input, KEY_MUTE, data[4] & 0x20); + /* Microphone Mute */ + input_report_key(g15->input, KEY_F20, data[4] & 0x40); + + input_sync(g15->input); + return 0; +} + +static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data, int size) +{ + bool backlight_disabled; + + /* + * The G510 ignores backlight updates when the backlight is turned off + * through the light toggle button on the keyboard, to work around this + * we queue a workitem to sync values when the backlight is turned on. + */ + backlight_disabled = data[1] & 0x04; + if (!backlight_disabled) + schedule_work(&g15->work); + + return 0; +} + +static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct lg_g15_data *g15 = hid_get_drvdata(hdev); + + if (!g15) + return 0; + + switch (g15->model) { + case LG_G15: + if (data[0] == 0x02 && size == 9) + return lg_g15_event(g15, data, size); + break; + case LG_G15_V2: + if (data[0] == 0x02 && size == 5) + return lg_g15_v2_event(g15, data, size); + break; + case LG_G510: + case LG_G510_USB_AUDIO: + if (data[0] == 0x03 && size == 5) + return lg_g510_event(g15, data, size); + if (data[0] == 0x04 && size == 2) + return lg_g510_leds_event(g15, data, size); + break; + } + + return 0; +} + +static int lg_g15_input_open(struct input_dev *dev) +{ + struct hid_device *hdev = input_get_drvdata(dev); + + return hid_hw_open(hdev); +} + +static void lg_g15_input_close(struct input_dev *dev) +{ + struct hid_device *hdev = input_get_drvdata(dev); + + hid_hw_close(hdev); +} + +static int lg_g15_register_led(struct lg_g15_data *g15, int i) +{ + const char * const led_names[] = { + "g15::kbd_backlight", + "g15::lcd_backlight", + "g15::macro_preset1", + "g15::macro_preset2", + "g15::macro_preset3", + "g15::macro_record", + }; + + g15->leds[i].led = i; + g15->leds[i].cdev.name = led_names[i]; + + switch (g15->model) { + case LG_G15: + case LG_G15_V2: + g15->leds[i].cdev.brightness_set_blocking = lg_g15_led_set; + g15->leds[i].cdev.brightness_get = lg_g15_led_get; + if (i < LG_G15_BRIGHTNESS_MAX) { + g15->leds[i].cdev.flags = LED_BRIGHT_HW_CHANGED; + g15->leds[i].cdev.max_brightness = 2; + } else { + g15->leds[i].cdev.max_brightness = 1; + } + break; + case LG_G510: + case LG_G510_USB_AUDIO: + switch (i) { + case LG_G15_LCD_BRIGHTNESS: + /* + * The G510 does not have a separate LCD brightness, + * but it does have a separate power-on (reset) value. + */ + g15->leds[i].cdev.name = "g15::power_on_backlight_val"; + /* fall through */ + case LG_G15_KBD_BRIGHTNESS: + g15->leds[i].cdev.brightness_set_blocking = + lg_g510_kbd_led_set; + g15->leds[i].cdev.brightness_get = + lg_g510_kbd_led_get; + g15->leds[i].cdev.max_brightness = 255; + g15->leds[i].cdev.groups = lg_g510_kbd_led_groups; + break; + default: + g15->leds[i].cdev.brightness_set_blocking = + lg_g510_mkey_led_set; + g15->leds[i].cdev.brightness_get = + lg_g510_mkey_led_get; + g15->leds[i].cdev.max_brightness = 1; + } + break; + } + + return devm_led_classdev_register(&g15->hdev->dev, &g15->leds[i].cdev); +} + +static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + u8 gkeys_settings_output_report = 0; + u8 gkeys_settings_feature_report = 0; + struct hid_report_enum *rep_enum; + unsigned int connect_mask = 0; + bool has_ff000000 = false; + struct lg_g15_data *g15; + struct input_dev *input; + struct hid_report *rep; + int ret, i, gkeys = 0; + + hdev->quirks |= HID_QUIRK_INPUT_PER_APP; + + ret = hid_parse(hdev); + if (ret) + return ret; + + /* + * Some models have multiple interfaces, we want the interface with + * with the f000.0000 application input report. + */ + rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + if (rep->application == 0xff000000) + has_ff000000 = true; + } + if (!has_ff000000) + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + g15 = devm_kzalloc(&hdev->dev, sizeof(*g15), GFP_KERNEL); + if (!g15) + return -ENOMEM; + + mutex_init(&g15->mutex); + + input = devm_input_allocate_device(&hdev->dev); + if (!input) + return -ENOMEM; + + g15->hdev = hdev; + g15->model = id->driver_data; + hid_set_drvdata(hdev, (void *)g15); + + switch (g15->model) { + case LG_G15: + INIT_WORK(&g15->work, lg_g15_leds_changed_work); + /* + * The G15 and G15 v2 use a separate usb-device (on a builtin + * hub) which emulates a keyboard for the F1 - F12 emulation + * on the G-keys, which we disable, rendering the emulated kbd + * non-functional, so we do not let hid-input connect. + */ + connect_mask = HID_CONNECT_HIDRAW; + gkeys_settings_output_report = 0x02; + gkeys = 18; + break; + case LG_G15_V2: + INIT_WORK(&g15->work, lg_g15_leds_changed_work); + connect_mask = HID_CONNECT_HIDRAW; + gkeys_settings_output_report = 0x02; + gkeys = 6; + break; + case LG_G510: + case LG_G510_USB_AUDIO: + INIT_WORK(&g15->work, lg_g510_leds_sync_work); + connect_mask = HID_CONNECT_HIDINPUT | HID_CONNECT_HIDRAW; + gkeys_settings_feature_report = 0x01; + gkeys = 18; + break; + } + + ret = hid_hw_start(hdev, connect_mask); + if (ret) + return ret; + + /* Tell the keyboard to stop sending F1-F12 + 1-6 for G1 - G18 */ + if (gkeys_settings_output_report) { + g15->transfer_buf[0] = gkeys_settings_output_report; + memset(g15->transfer_buf + 1, 0, gkeys); + /* + * The kbd ignores our output report if we do not queue + * an URB on the USB input endpoint first... + */ + ret = hid_hw_open(hdev); + if (ret) + goto error_hw_stop; + ret = hid_hw_output_report(hdev, g15->transfer_buf, gkeys + 1); + hid_hw_close(hdev); + } + + if (gkeys_settings_feature_report) { + g15->transfer_buf[0] = gkeys_settings_feature_report; + memset(g15->transfer_buf + 1, 0, gkeys); + ret = hid_hw_raw_request(g15->hdev, + gkeys_settings_feature_report, + g15->transfer_buf, gkeys + 1, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + } + + if (ret < 0) { + hid_err(hdev, "Error disabling keyboard emulation for the G-keys\n"); + goto error_hw_stop; + } + + /* Get initial brightness levels */ + ret = lg_g15_get_initial_led_brightness(g15); + if (ret) + goto error_hw_stop; + + /* Setup and register input device */ + input->name = "Logitech Gaming Keyboard Gaming Keys"; + input->phys = hdev->phys; + input->uniq = hdev->uniq; + input->id.bustype = hdev->bus; + input->id.vendor = hdev->vendor; + input->id.product = hdev->product; + input->id.version = hdev->version; + input->dev.parent = &hdev->dev; + input->open = lg_g15_input_open; + input->close = lg_g15_input_close; + + /* G-keys */ + for (i = 0; i < gkeys; i++) + input_set_capability(input, EV_KEY, KEY_MACRO1 + i); + + /* M1 - M3 and MR keys */ + for (i = 0; i < 3; i++) + input_set_capability(input, EV_KEY, KEY_MACRO_PRESET1 + i); + input_set_capability(input, EV_KEY, KEY_MACRO_RECORD_START); + + /* Keys below the LCD, intended for controlling a menu on the LCD */ + for (i = 0; i < 5; i++) + input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i); + + /* + * On the G510 only report headphone and mic mute keys when *not* using + * the builtin USB audio device. When the builtin audio is used these + * keys directly toggle mute (and the LEDs) on/off. + */ + if (g15->model == LG_G510) { + input_set_capability(input, EV_KEY, KEY_MUTE); + /* Userspace expects F20 for micmute */ + input_set_capability(input, EV_KEY, KEY_F20); + } + + g15->input = input; + input_set_drvdata(input, hdev); + + ret = input_register_device(input); + if (ret) + goto error_hw_stop; + + /* Register LED devices */ + for (i = 0; i < LG_G15_LED_MAX; i++) { + ret = lg_g15_register_led(g15, i); + if (ret) + goto error_hw_stop; + } + + return 0; + +error_hw_stop: + hid_hw_stop(hdev); + return ret; +} + +static const struct hid_device_id lg_g15_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G15_LCD), + .driver_data = LG_G15 }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G15_V2_LCD), + .driver_data = LG_G15_V2 }, + /* G510 without a headset plugged in */ + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G510), + .driver_data = LG_G510 }, + /* G510 with headset plugged in / with extra USB audio interface */ + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_G510_USB_AUDIO), + .driver_data = LG_G510_USB_AUDIO }, + { } +}; +MODULE_DEVICE_TABLE(hid, lg_g15_devices); + +static struct hid_driver lg_g15_driver = { + .name = "lg-g15", + .id_table = lg_g15_devices, + .raw_event = lg_g15_raw_event, + .probe = lg_g15_probe, +}; +module_hid_driver(lg_g15_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 8e91e2f06cb4..cd9193078525 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -1102,6 +1102,9 @@ static int hidpp20_batterylevel_get_battery_capacity(struct hidpp_device *hidpp, ret = hidpp_send_fap_command_sync(hidpp, feature_index, CMD_BATTERY_LEVEL_STATUS_GET_BATTERY_LEVEL_STATUS, NULL, 0, &response); + /* Ignore these intermittent errors */ + if (ret == HIDPP_ERROR_RESOURCE_ERROR) + return -EIO; if (ret > 0) { hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", __func__, ret); diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index c50bcd967d99..d1b39c29e353 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -94,6 +94,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0941), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_0641), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_1f4a), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X), HID_QUIRK_MULTI_INPUT }, @@ -419,13 +420,6 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_LCPOWER) { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) }, #endif -#if IS_ENABLED(CONFIG_HID_LED) - { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_WN) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, USB_DEVICE_ID_DREAM_CHEEKY_FA) }, - { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_LUXAFOR) }, - { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) }, - { HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) }, -#endif #if IS_ENABLED(CONFIG_HID_LENOVO) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 7c6abd7e0979..9ce22acdfaca 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -744,7 +744,8 @@ static void rmi_remove(struct hid_device *hdev) { struct rmi_data *hdata = hid_get_drvdata(hdev); - if (hdata->device_flags & RMI_DEVICE) { + if ((hdata->device_flags & RMI_DEVICE) + && test_bit(RMI_STARTED, &hdata->flags)) { clear_bit(RMI_STARTED, &hdata->flags); cancel_work_sync(&hdata->reset_work); rmi_unregister_transport_device(&hdata->xport); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index bbc6ec1aa5cb..c3fc0ceb8096 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -197,15 +197,15 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t } if (count > HID_MAX_BUFFER_SIZE) { - printk(KERN_WARNING "hidraw: pid %d passed too large report\n", - task_pid_nr(current)); + hid_warn(dev, "pid %d passed too large report\n", + task_pid_nr(current)); ret = -EINVAL; goto out; } if (count < 2) { - printk(KERN_WARNING "hidraw: pid %d passed too short report\n", - task_pid_nr(current)); + hid_warn(dev, "pid %d passed too short report\n", + task_pid_nr(current)); ret = -EINVAL; goto out; } @@ -468,9 +468,7 @@ static const struct file_operations hidraw_ops = { .release = hidraw_release, .unlocked_ioctl = hidraw_ioctl, .fasync = hidraw_fasync, -#ifdef CONFIG_COMPAT - .compat_ioctl = hidraw_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; @@ -597,7 +595,7 @@ int __init hidraw_init(void) if (result < 0) goto error_class; - printk(KERN_INFO "hidraw: raw HID events driver (C) Jiri Kosina\n"); + pr_info("raw HID events driver (C) Jiri Kosina\n"); out: return result; diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 04c088131e04..a358e61fbc82 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -48,6 +48,7 @@ #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) #define I2C_HID_QUIRK_BOGUS_IRQ BIT(4) +#define I2C_HID_QUIRK_RESET_ON_RESUME BIT(5) /* flags */ #define I2C_HID_STARTED 0 @@ -157,8 +158,6 @@ struct i2c_hid { bool irq_wake_enabled; struct mutex reset_lock; - - unsigned long sleep_delay; }; static const struct i2c_hid_quirks { @@ -170,8 +169,12 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV }, { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, + { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_3118, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, { USB_VENDOR_ID_ELAN, HID_ANY_ID, I2C_HID_QUIRK_BOGUS_IRQ }, + { USB_VENDOR_ID_ALPS_JP, HID_ANY_ID, + I2C_HID_QUIRK_RESET_ON_RESUME }, { 0, 0 } }; @@ -1212,8 +1215,15 @@ static int i2c_hid_resume(struct device *dev) * solves "incomplete reports" on Raydium devices 2386:3118 and * 2386:4B33 and fixes various SIS touchscreens no longer sending * data after a suspend/resume. + * + * However some ALPS touchpads generate IRQ storm without reset, so + * let's still reset them here. */ - ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) + ret = i2c_hid_hwreset(client); + else + ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); + if (ret) return ret; diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index c6c9ac09dac3..30a91d068306 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -402,7 +402,7 @@ static void ishtp_hbm_cl_connect_res(struct ishtp_device *dev, * @dev: ISHTP device instance * @disconnect_req: disconnect request structure * - * Disconnect request bus message from the fw. Send diconnect response. + * Disconnect request bus message from the fw. Send disconnect response. */ static void ishtp_hbm_fw_disconnect_req(struct ishtp_device *dev, struct hbm_client_connect_request *disconnect_req) diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 1f9bc4483465..e421cdf2d1a4 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -854,13 +854,6 @@ ret_unlock: return r; } -#ifdef CONFIG_COMPAT -static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations hiddev_fops = { .owner = THIS_MODULE, .read = hiddev_read, @@ -870,9 +863,7 @@ static const struct file_operations hiddev_fops = { .release = hiddev_release, .unlocked_ioctl = hiddev_ioctl, .fasync = hiddev_fasync, -#ifdef CONFIG_COMPAT - .compat_ioctl = hiddev_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile index a1eec7177c2d..94daf8240c95 100644 --- a/drivers/hv/Makefile +++ b/drivers/hv/Makefile @@ -9,4 +9,5 @@ CFLAGS_hv_balloon.o = -I$(src) hv_vmbus-y := vmbus_drv.o \ hv.o connection.o channel.o \ channel_mgmt.o ring_buffer.o hv_trace.o +hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 6e4c015783ff..74e77de89b4f 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -14,6 +14,7 @@ #include <linux/wait.h> #include <linux/delay.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/hyperv.h> @@ -40,29 +41,30 @@ EXPORT_SYMBOL_GPL(vmbus_connection); __u32 vmbus_proto_version; EXPORT_SYMBOL_GPL(vmbus_proto_version); -static __u32 vmbus_get_next_version(__u32 current_version) -{ - switch (current_version) { - case (VERSION_WIN7): - return VERSION_WS2008; - - case (VERSION_WIN8): - return VERSION_WIN7; - - case (VERSION_WIN8_1): - return VERSION_WIN8; +/* + * Table of VMBus versions listed from newest to oldest. + */ +static __u32 vmbus_versions[] = { + VERSION_WIN10_V5_2, + VERSION_WIN10_V5_1, + VERSION_WIN10_V5, + VERSION_WIN10_V4_1, + VERSION_WIN10, + VERSION_WIN8_1, + VERSION_WIN8, + VERSION_WIN7, + VERSION_WS2008 +}; - case (VERSION_WIN10): - return VERSION_WIN8_1; +/* + * Maximal VMBus protocol version guests can negotiate. Useful to cap the + * VMBus version for testing and debugging purpose. + */ +static uint max_version = VERSION_WIN10_V5_2; - case (VERSION_WIN10_V5): - return VERSION_WIN10; - - case (VERSION_WS2008): - default: - return VERSION_INVAL; - } -} +module_param(max_version, uint, S_IRUGO); +MODULE_PARM_DESC(max_version, + "Maximal VMBus protocol version which can be negotiated"); int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) { @@ -80,12 +82,12 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) msg->vmbus_version_requested = version; /* - * VMBus protocol 5.0 (VERSION_WIN10_V5) requires that we must use - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, + * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must + * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, * and for subsequent messages, we must use the Message Connection ID * field in the host-returned Version Response Message. And, with - * VERSION_WIN10_V5, we don't use msg->interrupt_page, but we tell - * the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for + * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we + * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for * compatibility. * * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). @@ -169,8 +171,8 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) */ int vmbus_connect(void) { - int ret = 0; struct vmbus_channel_msginfo *msginfo = NULL; + int i, ret = 0; __u32 version; /* Initialize the vmbus connection */ @@ -206,7 +208,7 @@ int vmbus_connect(void) * abstraction stuff */ vmbus_connection.int_page = - (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO, 0); + (void *)hv_alloc_hyperv_zeroed_page(); if (vmbus_connection.int_page == NULL) { ret = -ENOMEM; goto cleanup; @@ -215,14 +217,14 @@ int vmbus_connect(void) vmbus_connection.recv_int_page = vmbus_connection.int_page; vmbus_connection.send_int_page = (void *)((unsigned long)vmbus_connection.int_page + - (PAGE_SIZE >> 1)); + (HV_HYP_PAGE_SIZE >> 1)); /* * Setup the monitor notification facility. The 1st page for * parent->child and the 2nd page for child->parent */ - vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); - vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0); + vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_zeroed_page(); + vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_zeroed_page(); if ((vmbus_connection.monitor_pages[0] == NULL) || (vmbus_connection.monitor_pages[1] == NULL)) { ret = -ENOMEM; @@ -244,21 +246,21 @@ int vmbus_connect(void) * version. */ - version = VERSION_CURRENT; + for (i = 0; ; i++) { + if (i == ARRAY_SIZE(vmbus_versions)) + goto cleanup; + + version = vmbus_versions[i]; + if (version > max_version) + continue; - do { ret = vmbus_negotiate_version(msginfo, version); if (ret == -ETIMEDOUT) goto cleanup; if (vmbus_connection.conn_state == CONNECTED) break; - - version = vmbus_get_next_version(version); - } while (version != VERSION_INVAL); - - if (version == VERSION_INVAL) - goto cleanup; + } vmbus_proto_version = version; pr_info("Vmbus version:%d.%d\n", @@ -295,12 +297,12 @@ void vmbus_disconnect(void) destroy_workqueue(vmbus_connection.work_queue); if (vmbus_connection.int_page) { - free_pages((unsigned long)vmbus_connection.int_page, 0); + hv_free_hyperv_page((unsigned long)vmbus_connection.int_page); vmbus_connection.int_page = NULL; } - free_pages((unsigned long)vmbus_connection.monitor_pages[0], 0); - free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0); + hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[0]); + hv_free_hyperv_page((unsigned long)vmbus_connection.monitor_pages[1]); vmbus_connection.monitor_pages[0] = NULL; vmbus_connection.monitor_pages[1] = NULL; } @@ -361,6 +363,7 @@ void vmbus_on_event(unsigned long data) trace_vmbus_on_event(channel); + hv_debug_delay_test(channel, INTERRUPT_DELAY); do { void (*callback_fn)(void *); @@ -413,7 +416,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) case HV_STATUS_INVALID_CONNECTION_ID: /* * See vmbus_negotiate_version(): VMBus protocol 5.0 - * requires that we must use + * and higher require that we must use * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate * Contact message, but on old hosts that only * support VMBus protocol 4.0 or lower, here we get diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 34bd73526afd..7f4cf4fc805e 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -23,6 +23,9 @@ #include <linux/percpu_counter.h> #include <linux/hyperv.h> +#include <asm/hyperv-tlfs.h> + +#include <asm/mshyperv.h> #define CREATE_TRACE_POINTS #include "hv_trace_balloon.h" @@ -341,8 +344,6 @@ struct dm_unballoon_response { * * mem_range: Memory range to hot add. * - * On Linux we currently don't support this since we cannot hot add - * arbitrary granularity of memory. */ struct dm_hot_add { @@ -457,6 +458,7 @@ struct hot_add_wrk { struct work_struct wrk; }; +static bool allow_hibernation; static bool hot_add = true; static bool do_hot_add; /* @@ -477,7 +479,7 @@ module_param(pressure_report_delay, uint, (S_IRUGO | S_IWUSR)); MODULE_PARM_DESC(pressure_report_delay, "Delay in secs in reporting pressure"); static atomic_t trans_id = ATOMIC_INIT(0); -static int dm_ring_size = (5 * PAGE_SIZE); +static int dm_ring_size = 20 * 1024; /* * Driver specific state. @@ -493,10 +495,10 @@ enum hv_dm_state { }; -static __u8 recv_buffer[PAGE_SIZE]; -static __u8 balloon_up_send_buffer[PAGE_SIZE]; -#define PAGES_IN_2M 512 -#define HA_CHUNK (32 * 1024) +static __u8 recv_buffer[HV_HYP_PAGE_SIZE]; +static __u8 balloon_up_send_buffer[HV_HYP_PAGE_SIZE]; +#define PAGES_IN_2M (2 * 1024 * 1024 / PAGE_SIZE) +#define HA_CHUNK (128 * 1024 * 1024 / PAGE_SIZE) struct hv_dynmem_device { struct hv_device *dev; @@ -1053,8 +1055,12 @@ static void hot_add_req(struct work_struct *dummy) else resp.result = 0; - if (!do_hot_add || (resp.page_count == 0)) - pr_err("Memory hot add failed\n"); + if (!do_hot_add || resp.page_count == 0) { + if (!allow_hibernation) + pr_err("Memory hot add failed\n"); + else + pr_info("Ignore hot-add request!\n"); + } dm->state = DM_INITIALIZED; resp.hdr.trans_id = atomic_inc_return(&trans_id); @@ -1076,7 +1082,7 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) __u64 *max_page_count = (__u64 *)&info_hdr[1]; pr_info("Max. dynamic memory size: %llu MB\n", - (*max_page_count) >> (20 - PAGE_SHIFT)); + (*max_page_count) >> (20 - HV_HYP_PAGE_SHIFT)); } break; @@ -1218,7 +1224,7 @@ static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm, for (i = 0; (i * alloc_unit) < num_pages; i++) { if (bl_resp->hdr.size + sizeof(union dm_mem_page_range) > - PAGE_SIZE) + HV_HYP_PAGE_SIZE) return i * alloc_unit; /* @@ -1274,9 +1280,9 @@ static void balloon_up(struct work_struct *dummy) /* * We will attempt 2M allocations. However, if we fail to - * allocate 2M chunks, we will go back to 4k allocations. + * allocate 2M chunks, we will go back to PAGE_SIZE allocations. */ - alloc_unit = 512; + alloc_unit = PAGES_IN_2M; avail_pages = si_mem_available(); floor = compute_balloon_floor(); @@ -1292,7 +1298,7 @@ static void balloon_up(struct work_struct *dummy) } while (!done) { - memset(balloon_up_send_buffer, 0, PAGE_SIZE); + memset(balloon_up_send_buffer, 0, HV_HYP_PAGE_SIZE); bl_resp = (struct dm_balloon_response *)balloon_up_send_buffer; bl_resp->hdr.type = DM_BALLOON_RESPONSE; bl_resp->hdr.size = sizeof(struct dm_balloon_response); @@ -1491,7 +1497,7 @@ static void balloon_onchannelcallback(void *context) memset(recv_buffer, 0, sizeof(recv_buffer)); vmbus_recvpacket(dev->channel, recv_buffer, - PAGE_SIZE, &recvlen, &requestid); + HV_HYP_PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { dm_msg = (struct dm_message *)recv_buffer; @@ -1509,6 +1515,11 @@ static void balloon_onchannelcallback(void *context) break; case DM_BALLOON_REQUEST: + if (allow_hibernation) { + pr_info("Ignore balloon-up request!\n"); + break; + } + if (dm->state == DM_BALLOON_UP) pr_warn("Currently ballooning\n"); bal_msg = (struct dm_balloon *)recv_buffer; @@ -1518,6 +1529,11 @@ static void balloon_onchannelcallback(void *context) break; case DM_UNBALLOON_REQUEST: + if (allow_hibernation) { + pr_info("Ignore balloon-down request!\n"); + break; + } + dm->state = DM_BALLOON_DOWN; balloon_down(dm, (struct dm_unballoon_request *)recv_buffer); @@ -1623,6 +1639,11 @@ static int balloon_connect_vsp(struct hv_device *dev) cap_msg.hdr.size = sizeof(struct dm_capabilities); cap_msg.hdr.trans_id = atomic_inc_return(&trans_id); + /* + * When hibernation (i.e. virtual ACPI S4 state) is enabled, the host + * currently still requires the bits to be set, so we have to add code + * to fail the host's hot-add and balloon up/down requests, if any. + */ cap_msg.caps.cap_bits.balloon = 1; cap_msg.caps.cap_bits.hot_add = 1; @@ -1672,6 +1693,10 @@ static int balloon_probe(struct hv_device *dev, { int ret; + allow_hibernation = hv_is_hibernation_supported(); + if (allow_hibernation) + hot_add = false; + #ifdef CONFIG_MEMORY_HOTPLUG do_hot_add = hot_add; #else @@ -1711,6 +1736,8 @@ static int balloon_probe(struct hv_device *dev, return 0; probe_error: + dm_device.state = DM_INIT_ERROR; + dm_device.thread = NULL; vmbus_close(dev->channel); #ifdef CONFIG_MEMORY_HOTPLUG unregister_memory_notifier(&hv_memory_nb); @@ -1752,6 +1779,59 @@ static int balloon_remove(struct hv_device *dev) return 0; } +static int balloon_suspend(struct hv_device *hv_dev) +{ + struct hv_dynmem_device *dm = hv_get_drvdata(hv_dev); + + tasklet_disable(&hv_dev->channel->callback_event); + + cancel_work_sync(&dm->balloon_wrk.wrk); + cancel_work_sync(&dm->ha_wrk.wrk); + + if (dm->thread) { + kthread_stop(dm->thread); + dm->thread = NULL; + vmbus_close(hv_dev->channel); + } + + tasklet_enable(&hv_dev->channel->callback_event); + + return 0; + +} + +static int balloon_resume(struct hv_device *dev) +{ + int ret; + + dm_device.state = DM_INITIALIZING; + + ret = balloon_connect_vsp(dev); + + if (ret != 0) + goto out; + + dm_device.thread = + kthread_run(dm_thread_func, &dm_device, "hv_balloon"); + if (IS_ERR(dm_device.thread)) { + ret = PTR_ERR(dm_device.thread); + dm_device.thread = NULL; + goto close_channel; + } + + dm_device.state = DM_INITIALIZED; + return 0; +close_channel: + vmbus_close(dev->channel); +out: + dm_device.state = DM_INIT_ERROR; +#ifdef CONFIG_MEMORY_HOTPLUG + unregister_memory_notifier(&hv_memory_nb); + restore_online_page_callback(&hv_online_page); +#endif + return ret; +} + static const struct hv_vmbus_device_id id_table[] = { /* Dynamic Memory Class ID */ /* 525074DC-8985-46e2-8057-A307DC18A502 */ @@ -1766,6 +1846,8 @@ static struct hv_driver balloon_drv = { .id_table = id_table, .probe = balloon_probe, .remove = balloon_remove, + .suspend = balloon_suspend, + .resume = balloon_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, diff --git a/drivers/hv/hv_debugfs.c b/drivers/hv/hv_debugfs.c new file mode 100644 index 000000000000..8a2878573582 --- /dev/null +++ b/drivers/hv/hv_debugfs.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Authors: + * Branden Bonaby <brandonbonaby94@gmail.com> + */ + +#include <linux/hyperv.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include "hyperv_vmbus.h" + +struct dentry *hv_debug_root; + +static int hv_debugfs_delay_get(void *data, u64 *val) +{ + *val = *(u32 *)data; + return 0; +} + +static int hv_debugfs_delay_set(void *data, u64 val) +{ + if (val > 1000) + return -EINVAL; + *(u32 *)data = val; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_delay_fops, hv_debugfs_delay_get, + hv_debugfs_delay_set, "%llu\n"); + +static int hv_debugfs_state_get(void *data, u64 *val) +{ + *val = *(bool *)data; + return 0; +} + +static int hv_debugfs_state_set(void *data, u64 val) +{ + if (val == 1) + *(bool *)data = true; + else if (val == 0) + *(bool *)data = false; + else + return -EINVAL; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(hv_debugfs_state_fops, hv_debugfs_state_get, + hv_debugfs_state_set, "%llu\n"); + +/* Setup delay files to store test values */ +static int hv_debug_delay_files(struct hv_device *dev, struct dentry *root) +{ + struct vmbus_channel *channel = dev->channel; + char *buffer = "fuzz_test_buffer_interrupt_delay"; + char *message = "fuzz_test_message_delay"; + int *buffer_val = &channel->fuzz_testing_interrupt_delay; + int *message_val = &channel->fuzz_testing_message_delay; + struct dentry *buffer_file, *message_file; + + buffer_file = debugfs_create_file(buffer, 0644, root, + buffer_val, + &hv_debugfs_delay_fops); + if (IS_ERR(buffer_file)) { + pr_debug("debugfs_hyperv: file %s not created\n", buffer); + return PTR_ERR(buffer_file); + } + + message_file = debugfs_create_file(message, 0644, root, + message_val, + &hv_debugfs_delay_fops); + if (IS_ERR(message_file)) { + pr_debug("debugfs_hyperv: file %s not created\n", message); + return PTR_ERR(message_file); + } + + return 0; +} + +/* Setup test state value for vmbus device */ +static int hv_debug_set_test_state(struct hv_device *dev, struct dentry *root) +{ + struct vmbus_channel *channel = dev->channel; + bool *state = &channel->fuzz_testing_state; + char *status = "fuzz_test_state"; + struct dentry *test_state; + + test_state = debugfs_create_file(status, 0644, root, + state, + &hv_debugfs_state_fops); + if (IS_ERR(test_state)) { + pr_debug("debugfs_hyperv: file %s not created\n", status); + return PTR_ERR(test_state); + } + + return 0; +} + +/* Bind hv device to a dentry for debugfs */ +static void hv_debug_set_dir_dentry(struct hv_device *dev, struct dentry *root) +{ + if (hv_debug_root) + dev->debug_dir = root; +} + +/* Create all test dentry's and names for fuzz testing */ +int hv_debug_add_dev_dir(struct hv_device *dev) +{ + const char *device = dev_name(&dev->device); + char *delay_name = "delay"; + struct dentry *delay, *dev_root; + int ret; + + if (!IS_ERR(hv_debug_root)) { + dev_root = debugfs_create_dir(device, hv_debug_root); + if (IS_ERR(dev_root)) { + pr_debug("debugfs_hyperv: hyperv/%s/ not created\n", + device); + return PTR_ERR(dev_root); + } + hv_debug_set_test_state(dev, dev_root); + hv_debug_set_dir_dentry(dev, dev_root); + delay = debugfs_create_dir(delay_name, dev_root); + + if (IS_ERR(delay)) { + pr_debug("debugfs_hyperv: hyperv/%s/%s/ not created\n", + device, delay_name); + return PTR_ERR(delay); + } + ret = hv_debug_delay_files(dev, delay); + + return ret; + } + pr_debug("debugfs_hyperv: hyperv/ not in root debugfs path\n"); + return PTR_ERR(hv_debug_root); +} + +/* Remove dentry associated with released hv device */ +void hv_debug_rm_dev_dir(struct hv_device *dev) +{ + if (!IS_ERR(hv_debug_root)) + debugfs_remove_recursive(dev->debug_dir); +} + +/* Remove all dentrys associated with vmbus testing */ +void hv_debug_rm_all_dir(void) +{ + debugfs_remove_recursive(hv_debug_root); +} + +/* Delay buffer/message reads on a vmbus channel */ +void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type) +{ + struct vmbus_channel *test_channel = channel->primary_channel ? + channel->primary_channel : + channel; + bool state = test_channel->fuzz_testing_state; + + if (state) { + if (delay_type == 0) + udelay(test_channel->fuzz_testing_interrupt_delay); + else + udelay(test_channel->fuzz_testing_message_delay); + } +} + +/* Initialize top dentry for vmbus testing */ +int hv_debug_init(void) +{ + hv_debug_root = debugfs_create_dir("hyperv", NULL); + if (IS_ERR(hv_debug_root)) { + pr_debug("debugfs_hyperv: hyperv/ not created\n"); + return PTR_ERR(hv_debug_root); + } + return 0; +} diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 7e30ae0635cc..08fa4a5de644 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -13,6 +13,7 @@ #include <linux/workqueue.h> #include <linux/hyperv.h> #include <linux/sched.h> +#include <asm/hyperv-tlfs.h> #include "hyperv_vmbus.h" #include "hv_utils_transport.h" @@ -234,7 +235,7 @@ void hv_fcopy_onchannelcallback(void *context) if (fcopy_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid); if (recvlen <= 0) return; diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 5054d1105236..ae7c028dc5a8 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -27,6 +27,7 @@ #include <linux/connector.h> #include <linux/workqueue.h> #include <linux/hyperv.h> +#include <asm/hyperv-tlfs.h> #include "hyperv_vmbus.h" #include "hv_utils_transport.h" @@ -661,7 +662,7 @@ void hv_kvp_onchannelcallback(void *context) if (kvp_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 4, &recvlen, + vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, &requestid); if (recvlen > 0) { diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 20ba95b75a94..03b6454268b3 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -12,6 +12,7 @@ #include <linux/connector.h> #include <linux/workqueue.h> #include <linux/hyperv.h> +#include <asm/hyperv-tlfs.h> #include "hyperv_vmbus.h" #include "hv_utils_transport.h" @@ -297,7 +298,7 @@ void hv_vss_onchannelcallback(void *context) if (vss_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen, + vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid); if (recvlen > 0) { diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index e32681ee7b9f..766bd8457346 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -136,7 +136,7 @@ static void shutdown_onchannelcallback(void *context) struct icmsg_hdr *icmsghdrp; vmbus_recvpacket(channel, shut_txf_buf, - PAGE_SIZE, &recvlen, &requestid); + HV_HYP_PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ @@ -284,7 +284,7 @@ static void timesync_onchannelcallback(void *context) u8 *time_txf_buf = util_timesynch.recv_buffer; vmbus_recvpacket(channel, time_txf_buf, - PAGE_SIZE, &recvlen, &requestid); + HV_HYP_PAGE_SIZE, &recvlen, &requestid); if (recvlen > 0) { icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ @@ -346,7 +346,7 @@ static void heartbeat_onchannelcallback(void *context) while (1) { vmbus_recvpacket(channel, hbeat_txf_buf, - PAGE_SIZE, &recvlen, &requestid); + HV_HYP_PAGE_SIZE, &recvlen, &requestid); if (!recvlen) break; @@ -390,7 +390,7 @@ static int util_probe(struct hv_device *dev, (struct hv_util_service *)dev_id->driver_data; int ret; - srv->recv_buffer = kmalloc(PAGE_SIZE * 4, GFP_KERNEL); + srv->recv_buffer = kmalloc(HV_HYP_PAGE_SIZE * 4, GFP_KERNEL); if (!srv->recv_buffer) return -ENOMEM; srv->channel = dev->channel; @@ -413,8 +413,9 @@ static int util_probe(struct hv_device *dev, hv_set_drvdata(dev, srv); - ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0, - srv->util_cb, dev->channel); + ret = vmbus_open(dev->channel, 4 * HV_HYP_PAGE_SIZE, + 4 * HV_HYP_PAGE_SIZE, NULL, 0, srv->util_cb, + dev->channel); if (ret) goto error; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index af9379a3bf89..20edcfd3b96c 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -385,4 +385,35 @@ enum hvutil_device_state { HVUTIL_DEVICE_DYING, /* driver unload is in progress */ }; +enum delay { + INTERRUPT_DELAY = 0, + MESSAGE_DELAY = 1, +}; + +#ifdef CONFIG_HYPERV_TESTING + +int hv_debug_add_dev_dir(struct hv_device *dev); +void hv_debug_rm_dev_dir(struct hv_device *dev); +void hv_debug_rm_all_dir(void); +int hv_debug_init(void); +void hv_debug_delay_test(struct vmbus_channel *channel, enum delay delay_type); + +#else /* CONFIG_HYPERV_TESTING */ + +static inline void hv_debug_rm_dev_dir(struct hv_device *dev) {}; +static inline void hv_debug_rm_all_dir(void) {}; +static inline void hv_debug_delay_test(struct vmbus_channel *channel, + enum delay delay_type) {}; +static inline int hv_debug_init(void) +{ + return -1; +} + +static inline int hv_debug_add_dev_dir(struct hv_device *dev) +{ + return -1; +} + +#endif /* CONFIG_HYPERV_TESTING */ + #endif /* _HYPERV_VMBUS_H */ diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 9a03b163cbbd..356e22159e83 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -396,6 +396,7 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel) struct hv_ring_buffer_info *rbi = &channel->inbound; struct vmpacket_descriptor *desc; + hv_debug_delay_test(channel, MESSAGE_DELAY); if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) return NULL; @@ -421,6 +422,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel, u32 packetlen = desc->len8 << 3; u32 dsize = rbi->ring_datasize; + hv_debug_delay_test(channel, MESSAGE_DELAY); /* bump offset to next potential packet */ rbi->priv_read_index += packetlen + VMBUS_PKT_TRAILER; if (rbi->priv_read_index >= dsize) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 8c06b3361c27..4ef5a66df680 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -79,7 +79,7 @@ static struct notifier_block hyperv_panic_block = { static const char *fb_mmio_name = "fb_range"; static struct resource *fb_mmio; static struct resource *hyperv_mmio; -static DEFINE_SEMAPHORE(hyperv_mmio_lock); +static DEFINE_MUTEX(hyperv_mmio_lock); static int vmbus_exists(void) { @@ -960,6 +960,8 @@ static void vmbus_device_release(struct device *device) struct hv_device *hv_dev = device_to_hv_device(device); struct vmbus_channel *channel = hv_dev->channel; + hv_debug_rm_dev_dir(hv_dev); + mutex_lock(&vmbus_connection.channel_mutex); hv_process_channel_removal(channel); mutex_unlock(&vmbus_connection.channel_mutex); @@ -1273,7 +1275,7 @@ static void hv_kmsg_dump(struct kmsg_dumper *dumper, * Write dump contents to the page. No need to synchronize; panic should * be single-threaded. */ - kmsg_dump_get_buffer(dumper, true, hv_panic_page, PAGE_SIZE, + kmsg_dump_get_buffer(dumper, true, hv_panic_page, HV_HYP_PAGE_SIZE, &bytes_written); if (bytes_written) hyperv_report_panic_msg(panic_pa, bytes_written); @@ -1373,7 +1375,7 @@ static int vmbus_bus_init(void) */ hv_get_crash_ctl(hyperv_crash_ctl); if (hyperv_crash_ctl & HV_CRASH_CTL_CRASH_NOTIFY_MSG) { - hv_panic_page = (void *)get_zeroed_page(GFP_KERNEL); + hv_panic_page = (void *)hv_alloc_hyperv_zeroed_page(); if (hv_panic_page) { ret = kmsg_dump_register(&hv_kmsg_dumper); if (ret) @@ -1401,7 +1403,7 @@ err_alloc: hv_remove_vmbus_irq(); bus_unregister(&hv_bus); - free_page((unsigned long)hv_panic_page); + hv_free_hyperv_page((unsigned long)hv_panic_page); unregister_sysctl_table(hv_ctl_table_hdr); hv_ctl_table_hdr = NULL; return ret; @@ -1809,6 +1811,7 @@ int vmbus_device_register(struct hv_device *child_device_obj) pr_err("Unable to register primary channeln"); goto err_kset_unregister; } + hv_debug_add_dev_dir(child_device_obj); return 0; @@ -2010,7 +2013,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, int retval; retval = -ENXIO; - down(&hyperv_mmio_lock); + mutex_lock(&hyperv_mmio_lock); /* * If overlaps with frame buffers are allowed, then first attempt to @@ -2057,7 +2060,7 @@ int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, } exit: - up(&hyperv_mmio_lock); + mutex_unlock(&hyperv_mmio_lock); return retval; } EXPORT_SYMBOL_GPL(vmbus_allocate_mmio); @@ -2074,7 +2077,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size) { struct resource *iter; - down(&hyperv_mmio_lock); + mutex_lock(&hyperv_mmio_lock); for (iter = hyperv_mmio; iter; iter = iter->sibling) { if ((iter->start >= start + size) || (iter->end <= start)) continue; @@ -2082,7 +2085,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size) __release_region(iter, start, size); } release_mem_region(start, size); - up(&hyperv_mmio_lock); + mutex_unlock(&hyperv_mmio_lock); } EXPORT_SYMBOL_GPL(vmbus_free_mmio); @@ -2215,8 +2218,7 @@ static int vmbus_bus_resume(struct device *dev) * We only use the 'vmbus_proto_version', which was in use before * hibernation, to re-negotiate with the host. */ - if (vmbus_proto_version == VERSION_INVAL || - vmbus_proto_version == 0) { + if (!vmbus_proto_version) { pr_err("Invalid proto version = 0x%x\n", vmbus_proto_version); return -EINVAL; } @@ -2303,7 +2305,7 @@ static void hv_crash_handler(struct pt_regs *regs) vmbus_connection.conn_state = DISCONNECTED; cpu = smp_processor_id(); hv_stimer_cleanup(cpu); - hv_synic_cleanup(cpu); + hv_synic_disable_regs(cpu); hyperv_cleanup(); }; @@ -2373,6 +2375,7 @@ static int __init hv_acpi_init(void) ret = -ETIMEDOUT; goto cleanup; } + hv_debug_init(); ret = vmbus_bus_init(); if (ret) @@ -2409,6 +2412,8 @@ static void __exit vmbus_exit(void) tasklet_kill(&hv_cpu->msg_dpc); } + hv_debug_rm_all_dir(); + vmbus_free_channels(); if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index fa0c2f1fb443..4136643d8e0c 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -954,6 +954,7 @@ static const struct file_operations watchdog_fops = { .release = watchdog_release, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index a0307e6761b8..3f59f2a1a5e3 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1458,6 +1458,7 @@ static const struct file_operations watchdog_fops = { .release = watchdog_close, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; /* diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index 8862445aa858..fd5f5c5a5244 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -92,8 +92,8 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) { int ret; - BUG_ON(!hwlock); - BUG_ON(!flags && mode == HWLOCK_IRQSTATE); + if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE))) + return -EINVAL; /* * This spin_lock{_irq, _irqsave} serves three purposes: @@ -264,8 +264,8 @@ EXPORT_SYMBOL_GPL(__hwspin_lock_timeout); */ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) { - BUG_ON(!hwlock); - BUG_ON(!flags && mode == HWLOCK_IRQSTATE); + if (WARN_ON(!hwlock || (!flags && mode == HWLOCK_IRQSTATE))) + return; /* * We must make sure that memory operations (both reads and writes), @@ -657,13 +657,15 @@ static int __hwspin_lock_request(struct hwspinlock *hwlock) /* notify PM core that power is now needed */ ret = pm_runtime_get_sync(dev); - if (ret < 0) { + if (ret < 0 && ret != -EACCES) { dev_err(dev, "%s: can't power on device\n", __func__); pm_runtime_put_noidle(dev); module_put(dev->driver->owner); return ret; } + ret = 0; + /* mark hwspinlock as used, should not fail */ tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock), HWSPINLOCK_UNUSED); @@ -820,9 +822,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock) } /* notify the underlying device that power is not needed */ - ret = pm_runtime_put(dev); - if (ret < 0) - goto out; + pm_runtime_put(dev); /* mark this hwspinlock as available */ tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock), diff --git a/drivers/hwspinlock/sprd_hwspinlock.c b/drivers/hwspinlock/sprd_hwspinlock.c index dc42bf51f3e6..36dc8038bbb4 100644 --- a/drivers/hwspinlock/sprd_hwspinlock.c +++ b/drivers/hwspinlock/sprd_hwspinlock.c @@ -15,7 +15,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include "hwspinlock_internal.h" @@ -79,11 +78,17 @@ static const struct hwspinlock_ops sprd_hwspinlock_ops = { .relax = sprd_hwspinlock_relax, }; +static void sprd_hwspinlock_disable(void *data) +{ + struct sprd_hwspinlock_dev *sprd_hwlock = data; + + clk_disable_unprepare(sprd_hwlock->clk); +} + static int sprd_hwspinlock_probe(struct platform_device *pdev) { struct sprd_hwspinlock_dev *sprd_hwlock; struct hwspinlock *lock; - struct resource *res; int i, ret; if (!pdev->dev.of_node) @@ -96,8 +101,7 @@ static int sprd_hwspinlock_probe(struct platform_device *pdev) if (!sprd_hwlock) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sprd_hwlock->base = devm_ioremap_resource(&pdev->dev, res); + sprd_hwlock->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sprd_hwlock->base)) return PTR_ERR(sprd_hwlock->base); @@ -107,7 +111,17 @@ static int sprd_hwspinlock_probe(struct platform_device *pdev) return PTR_ERR(sprd_hwlock->clk); } - clk_prepare_enable(sprd_hwlock->clk); + ret = clk_prepare_enable(sprd_hwlock->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, sprd_hwspinlock_disable, + sprd_hwlock); + if (ret) { + dev_err(&pdev->dev, + "Failed to add hwspinlock disable action\n"); + return ret; + } /* set the hwspinlock to record user id to identify subsystems */ writel(HWSPINLOCK_USER_BITS, sprd_hwlock->base + HWSPINLOCK_RECCTRL); @@ -118,27 +132,10 @@ static int sprd_hwspinlock_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, sprd_hwlock); - pm_runtime_enable(&pdev->dev); - ret = hwspin_lock_register(&sprd_hwlock->bank, &pdev->dev, - &sprd_hwspinlock_ops, 0, SPRD_HWLOCKS_NUM); - if (ret) { - pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(sprd_hwlock->clk); - return ret; - } - - return 0; -} - -static int sprd_hwspinlock_remove(struct platform_device *pdev) -{ - struct sprd_hwspinlock_dev *sprd_hwlock = platform_get_drvdata(pdev); - - hwspin_lock_unregister(&sprd_hwlock->bank); - pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(sprd_hwlock->clk); - return 0; + return devm_hwspin_lock_register(&pdev->dev, &sprd_hwlock->bank, + &sprd_hwspinlock_ops, 0, + SPRD_HWLOCKS_NUM); } static const struct of_device_id sprd_hwspinlock_of_match[] = { @@ -149,7 +146,6 @@ MODULE_DEVICE_TABLE(of, sprd_hwspinlock_of_match); static struct platform_driver sprd_hwspinlock_driver = { .probe = sprd_hwspinlock_probe, - .remove = sprd_hwspinlock_remove, .driver = { .name = "sprd_hwspinlock", .of_match_table = of_match_ptr(sprd_hwspinlock_of_match), diff --git a/drivers/hwspinlock/u8500_hsem.c b/drivers/hwspinlock/u8500_hsem.c index 572ca79d77e8..67845c0c9701 100644 --- a/drivers/hwspinlock/u8500_hsem.c +++ b/drivers/hwspinlock/u8500_hsem.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/io.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/hwspinlock.h> @@ -88,21 +87,16 @@ static int u8500_hsem_probe(struct platform_device *pdev) struct hwspinlock_pdata *pdata = pdev->dev.platform_data; struct hwspinlock_device *bank; struct hwspinlock *hwlock; - struct resource *res; void __iomem *io_base; - int i, ret, num_locks = U8500_MAX_SEMAPHORE; + int i, num_locks = U8500_MAX_SEMAPHORE; ulong val; if (!pdata) return -ENODEV; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; - - io_base = ioremap(res->start, resource_size(res)); - if (!io_base) - return -ENOMEM; + io_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); /* make sure protocol 1 is selected */ val = readl(io_base + HSEM_CTRL_REG); @@ -111,54 +105,29 @@ static int u8500_hsem_probe(struct platform_device *pdev) /* clear all interrupts */ writel(0xFFFF, io_base + HSEM_ICRALL); - bank = kzalloc(struct_size(bank, lock, num_locks), GFP_KERNEL); - if (!bank) { - ret = -ENOMEM; - goto iounmap_base; - } + bank = devm_kzalloc(&pdev->dev, struct_size(bank, lock, num_locks), + GFP_KERNEL); + if (!bank) + return -ENOMEM; platform_set_drvdata(pdev, bank); for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) hwlock->priv = io_base + HSEM_REGISTER_OFFSET + sizeof(u32) * i; - /* no pm needed for HSem but required to comply with hwspilock core */ - pm_runtime_enable(&pdev->dev); - - ret = hwspin_lock_register(bank, &pdev->dev, &u8500_hwspinlock_ops, - pdata->base_id, num_locks); - if (ret) - goto reg_fail; - - return 0; - -reg_fail: - pm_runtime_disable(&pdev->dev); - kfree(bank); -iounmap_base: - iounmap(io_base); - return ret; + return devm_hwspin_lock_register(&pdev->dev, bank, + &u8500_hwspinlock_ops, + pdata->base_id, num_locks); } static int u8500_hsem_remove(struct platform_device *pdev) { struct hwspinlock_device *bank = platform_get_drvdata(pdev); void __iomem *io_base = bank->lock[0].priv - HSEM_REGISTER_OFFSET; - int ret; /* clear all interrupts */ writel(0xFFFF, io_base + HSEM_ICRALL); - ret = hwspin_lock_unregister(bank); - if (ret) { - dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); - return ret; - } - - pm_runtime_disable(&pdev->dev); - iounmap(io_base); - kfree(bank); - return 0; } diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 603b83ac5085..2712e699ba08 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -832,23 +832,13 @@ stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return err; } -#ifdef CONFIG_COMPAT -static long -stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#else -#define stm_char_compat_ioctl NULL -#endif - static const struct file_operations stm_fops = { .open = stm_char_open, .release = stm_char_release, .write = stm_char_write, .mmap = stm_char_mmap, .unlocked_ioctl = stm_char_ioctl, - .compat_ioctl = stm_char_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = no_llseek, }; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 146ce40d8e0a..6a0aa76859f3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -145,6 +145,7 @@ config I2C_I801 Comet Lake (PCH) Elkhart Lake (PCH) Tiger Lake (PCH) + Jasper Lake (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. @@ -292,7 +293,7 @@ config I2C_VIA select I2C_ALGOBIT help If you say yes to this option, support will be included for the VIA - 82C586B I2C interface + 82C586B I2C interface This driver can also be built as a module. If so, the module will be called i2c-via. @@ -677,11 +678,11 @@ config I2C_IMX_LPI2C tristate "IMX Low Power I2C interface" depends on ARCH_MXC || COMPILE_TEST help - Say Y here if you want to use the Low Power IIC bus controller - on the Freescale i.MX processors. + Say Y here if you want to use the Low Power IIC bus controller + on the Freescale i.MX processors. - This driver can also be built as a module. If so, the module - will be called i2c-imx-lpi2c. + This driver can also be built as a module. If so, the module + will be called i2c-imx-lpi2c. config I2C_IOP3XX tristate "Intel IOPx3xx and IXP4xx on-chip I2C interface" @@ -874,6 +875,7 @@ config I2C_PXA_PCI config I2C_PXA_SLAVE bool "Intel PXA2XX I2C Slave comms support" depends on I2C_PXA && !X86_32 + select I2C_SLAVE help Support I2C slave mode communications on the PXA I2C bus. This is necessary for systems where the PXA may be a target on the @@ -1182,9 +1184,9 @@ config I2C_DIOLAN_U2C will be called i2c-diolan-u2c. config I2C_DLN2 - tristate "Diolan DLN-2 USB I2C adapter" - depends on MFD_DLN2 - help + tristate "Diolan DLN-2 USB I2C adapter" + depends on MFD_DLN2 + help If you say yes to this option, support will be included for Diolan DLN2, a USB to I2C interface. @@ -1283,9 +1285,9 @@ config I2C_VIPERBOARD help Say yes here to access the I2C part of the Nano River Technologies Viperboard as I2C master. - See viperboard API specification and Nano - River Tech's viperboard.h for detailed meaning - of the module parameters. + See viperboard API specification and Nano + River Tech's viperboard.h for detailed meaning + of the module parameters. comment "Other I2C/SMBus bus drivers" diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 7b098ff5f5dd..a7be6f24450b 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -952,6 +952,10 @@ static const struct of_device_id aspeed_i2c_bus_of_table[] = { .compatible = "aspeed,ast2500-i2c-bus", .data = aspeed_i2c_25xx_get_clk_reg_val, }, + { + .compatible = "aspeed,ast2600-i2c-bus", + .data = aspeed_i2c_25xx_get_clk_reg_val, + }, { }, }; MODULE_DEVICE_TABLE(of, aspeed_i2c_bus_of_table); diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c index 435c7d7377a3..e13af4874976 100644 --- a/drivers/i2c/busses/i2c-at91-core.c +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -68,6 +68,9 @@ static struct at91_twi_pdata at91rm9200_config = { .has_unre_flag = true, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata at91sam9261_config = { @@ -76,6 +79,9 @@ static struct at91_twi_pdata at91sam9261_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata at91sam9260_config = { @@ -84,6 +90,9 @@ static struct at91_twi_pdata at91sam9260_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata at91sam9g20_config = { @@ -92,6 +101,9 @@ static struct at91_twi_pdata at91sam9g20_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata at91sam9g10_config = { @@ -100,6 +112,9 @@ static struct at91_twi_pdata at91sam9g10_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static const struct platform_device_id at91_twi_devtypes[] = { @@ -130,6 +145,9 @@ static struct at91_twi_pdata at91sam9x5_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_dig_filtr = false, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata sama5d4_config = { @@ -138,6 +156,9 @@ static struct at91_twi_pdata sama5d4_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = true, + .has_dig_filtr = true, + .has_adv_dig_filtr = false, + .has_ana_filtr = false, }; static struct at91_twi_pdata sama5d2_config = { @@ -146,6 +167,20 @@ static struct at91_twi_pdata sama5d2_config = { .has_unre_flag = true, .has_alt_cmd = true, .has_hold_field = true, + .has_dig_filtr = true, + .has_adv_dig_filtr = true, + .has_ana_filtr = true, +}; + +static struct at91_twi_pdata sam9x60_config = { + .clk_max_div = 7, + .clk_offset = 4, + .has_unre_flag = true, + .has_alt_cmd = true, + .has_hold_field = true, + .has_dig_filtr = true, + .has_adv_dig_filtr = true, + .has_ana_filtr = true, }; static const struct of_device_id atmel_twi_dt_ids[] = { @@ -174,6 +209,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = { .compatible = "atmel,sama5d2-i2c", .data = &sama5d2_config, }, { + .compatible = "microchip,sam9x60-i2c", + .data = &sam9x60_config, + }, { /* sentinel */ } }; diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c index a3fcc35ffd3b..7a862e00b475 100644 --- a/drivers/i2c/busses/i2c-at91-master.c +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -31,12 +31,32 @@ void at91_init_twi_bus_master(struct at91_twi_dev *dev) { + struct at91_twi_pdata *pdata = dev->pdata; + u32 filtr = 0; + /* FIFO should be enabled immediately after the software reset */ if (dev->fifo_size) at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_FIFOEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_MSEN); at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_SVDIS); at91_twi_write(dev, AT91_TWI_CWGR, dev->twi_cwgr_reg); + + /* enable digital filter */ + if (pdata->has_dig_filtr && dev->enable_dig_filt) + filtr |= AT91_TWI_FILTR_FILT; + + /* enable advanced digital filter */ + if (pdata->has_adv_dig_filtr && dev->enable_dig_filt) + filtr |= AT91_TWI_FILTR_FILT | + (AT91_TWI_FILTR_THRES(dev->filter_width) & + AT91_TWI_FILTR_THRES_MASK); + + /* enable analog filter */ + if (pdata->has_ana_filtr && dev->enable_ana_filt) + filtr |= AT91_TWI_FILTR_PADFEN; + + if (filtr) + at91_twi_write(dev, AT91_TWI_FILTR, filtr); } /* @@ -45,7 +65,7 @@ void at91_init_twi_bus_master(struct at91_twi_dev *dev) */ static void at91_calc_twi_clock(struct at91_twi_dev *dev) { - int ckdiv, cdiv, div, hold = 0; + int ckdiv, cdiv, div, hold = 0, filter_width = 0; struct at91_twi_pdata *pdata = dev->pdata; int offset = pdata->clk_offset; int max_ckdiv = pdata->clk_max_div; @@ -84,11 +104,29 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev) } } + if (pdata->has_adv_dig_filtr) { + /* + * filter width = 0 to AT91_TWI_FILTR_THRES_MAX + * peripheral clocks + */ + filter_width = DIV_ROUND_UP(t->digital_filter_width_ns + * (clk_get_rate(dev->clk) / 1000), 1000000); + if (filter_width > AT91_TWI_FILTR_THRES_MAX) { + dev_warn(dev->dev, + "Filter threshold set to its maximum value (%d instead of %d)\n", + AT91_TWI_FILTR_THRES_MAX, filter_width); + filter_width = AT91_TWI_FILTR_THRES_MAX; + } + } + dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv | AT91_TWI_CWGR_HOLD(hold); - dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n", - cdiv, ckdiv, hold, t->sda_hold_ns); + dev->filter_width = filter_width; + + dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns), filter_width %d (%d ns)\n", + cdiv, ckdiv, hold, t->sda_hold_ns, filter_width, + t->digital_filter_width_ns); } static void at91_twi_dma_cleanup(struct at91_twi_dev *dev) @@ -720,14 +758,14 @@ static int at91_twi_configure_dma(struct at91_twi_dev *dev, u32 phy_addr) slave_config.dst_maxburst = 1; slave_config.device_fc = false; - dma->chan_tx = dma_request_slave_channel_reason(dev->dev, "tx"); + dma->chan_tx = dma_request_chan(dev->dev, "tx"); if (IS_ERR(dma->chan_tx)) { ret = PTR_ERR(dma->chan_tx); dma->chan_tx = NULL; goto error; } - dma->chan_rx = dma_request_slave_channel_reason(dev->dev, "rx"); + dma->chan_rx = dma_request_chan(dev->dev, "rx"); if (IS_ERR(dma->chan_rx)) { ret = PTR_ERR(dma->chan_rx); dma->chan_rx = NULL; @@ -793,6 +831,11 @@ int at91_twi_probe_master(struct platform_device *pdev, dev_info(dev->dev, "Using FIFO (%u data)\n", dev->fifo_size); } + dev->enable_dig_filt = of_property_read_bool(pdev->dev.of_node, + "i2c-digital-filter"); + + dev->enable_ana_filt = of_property_read_bool(pdev->dev.of_node, + "i2c-analog-filter"); at91_calc_twi_clock(dev); dev->adapter.algo = &at91_twi_algorithm; diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h index 499b506f6128..977a67bc0f88 100644 --- a/drivers/i2c/busses/i2c-at91.h +++ b/drivers/i2c/busses/i2c-at91.h @@ -84,6 +84,13 @@ #define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) #define AT91_TWI_ACR_DIR BIT(8) +#define AT91_TWI_FILTR 0x0044 +#define AT91_TWI_FILTR_FILT BIT(0) +#define AT91_TWI_FILTR_PADFEN BIT(1) +#define AT91_TWI_FILTR_THRES(v) ((v) << 8) +#define AT91_TWI_FILTR_THRES_MAX 7 +#define AT91_TWI_FILTR_THRES_MASK GENMASK(10, 8) + #define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */ #define AT91_TWI_FMR_TXRDYM(mode) (((mode) & 0x3) << 0) #define AT91_TWI_FMR_TXRDYM_MASK (0x3 << 0) @@ -108,6 +115,9 @@ struct at91_twi_pdata { bool has_unre_flag; bool has_alt_cmd; bool has_hold_field; + bool has_dig_filtr; + bool has_adv_dig_filtr; + bool has_ana_filtr; struct at_dma_slave dma_slave; }; @@ -145,6 +155,9 @@ struct at91_twi_dev { unsigned smr; struct i2c_client *slave; #endif + bool enable_dig_filt; + bool enable_ana_filt; + u32 filter_width; }; unsigned at91_twi_read(struct at91_twi_dev *dev, unsigned reg); diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 9ffdffaf6141..30efb7913b2e 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -81,6 +81,7 @@ #define M_CMD_PROTOCOL_MASK 0xf #define M_CMD_PROTOCOL_BLK_WR 0x7 #define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PROTOCOL_PROCESS 0xa #define M_CMD_PEC_SHIFT 8 #define M_CMD_RD_CNT_SHIFT 0 #define M_CMD_RD_CNT_MASK 0xff @@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, return 0; } -static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, - struct i2c_msg *msg) +/* + * If 'process_call' is true, then this is a multi-msg transfer that requires + * a repeated start between the messages. + * More specifically, it must be a write (reg) followed by a read (data). + * The i2c quirks are set to enforce this rule. + */ +static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msgs, bool process_call) { int i; u8 addr; u32 val, tmp, val_intr_en; unsigned int tx_bytes; + struct i2c_msg *msg = &msgs[0]; /* check if bus is busy */ if (!!(iproc_i2c_rd_reg(iproc_i2c, @@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, val = msg->buf[i]; /* mark the last byte */ - if (i == msg->len - 1) - val |= BIT(M_TX_WR_STATUS_SHIFT); + if (!process_call && (i == msg->len - 1)) + val |= 1 << M_TX_WR_STATUS_SHIFT; iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); } iproc_i2c->tx_bytes = tx_bytes; } + /* Process the read message if this is process call */ + if (process_call) { + msg++; + iproc_i2c->msg = msg; /* point to second msg */ + + /* + * The last byte to be sent out should be a slave + * address with read operation + */ + addr = i2c_8bit_addr_from_msg(msg); + /* mark it the last byte out */ + val = addr | (1 << M_TX_WR_STATUS_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val); + } + /* mark as incomplete before starting the transaction */ if (iproc_i2c->irq) reinit_completion(&iproc_i2c->done); @@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, * underrun interrupt, which will be triggerred when the TX FIFO is * empty. When that happens we can then pump more data into the FIFO */ - if (!(msg->flags & I2C_M_RD) && + if (!process_call && !(msg->flags & I2C_M_RD) && msg->len > iproc_i2c->tx_bytes) val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT); @@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, */ val = BIT(M_CMD_START_BUSY_SHIFT); if (msg->flags & I2C_M_RD) { + u32 protocol; + iproc_i2c->rx_bytes = 0; if (msg->len > M_RX_FIFO_MAX_THLD_VALUE) iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE; @@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, /* enable the RX threshold interrupt */ val_intr_en |= BIT(IE_M_RX_THLD_SHIFT); - val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + protocol = process_call ? + M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD; + + val |= (protocol << M_CMD_PROTOCOL_SHIFT) | (msg->len << M_CMD_RD_CNT_SHIFT); } else { val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); @@ -774,17 +802,24 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) { struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); - int ret, i; + bool process_call = false; + int ret; - /* go through all messages */ - for (i = 0; i < num; i++) { - ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); - if (ret) { - dev_dbg(iproc_i2c->device, "xfer failed\n"); - return ret; + if (num == 2) { + /* Repeated start, use process call */ + process_call = true; + if (msgs[1].flags & I2C_M_NOSTART) { + dev_err(iproc_i2c->device, "Invalid repeated start\n"); + return -EOPNOTSUPP; } } + ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call); + if (ret) { + dev_dbg(iproc_i2c->device, "xfer failed\n"); + return ret; + } + return num; } @@ -809,6 +844,8 @@ static struct i2c_algorithm bcm_iproc_algo = { }; static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = { + .flags = I2C_AQ_COMB_WRITE_THEN_READ, + .max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE, .max_read_len = M_RX_MAX_READ_LEN, }; diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index c551aa96a2e3..958161c71985 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -3,6 +3,7 @@ // // Copyright (C) 2013 Google, Inc. +#include <linux/acpi.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/platform_data/cros_ec_commands.h> @@ -240,7 +241,6 @@ static const struct i2c_algorithm ec_i2c_algorithm = { static int ec_i2c_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; struct ec_i2c_device *bus = NULL; @@ -256,7 +256,7 @@ static int ec_i2c_probe(struct platform_device *pdev) if (bus == NULL) return -ENOMEM; - err = of_property_read_u32(np, "google,remote-bus", &remote_bus); + err = device_property_read_u32(dev, "google,remote-bus", &remote_bus); if (err) { dev_err(dev, "Couldn't read remote-bus property\n"); return err; @@ -271,7 +271,7 @@ static int ec_i2c_probe(struct platform_device *pdev) bus->adap.algo = &ec_i2c_algorithm; bus->adap.algo_data = bus; bus->adap.dev.parent = &pdev->dev; - bus->adap.dev.of_node = np; + bus->adap.dev.of_node = pdev->dev.of_node; bus->adap.retries = I2C_MAX_RETRIES; err = i2c_add_adapter(&bus->adap); @@ -291,19 +291,24 @@ static int ec_i2c_remove(struct platform_device *dev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id cros_ec_i2c_of_match[] = { { .compatible = "google,cros-ec-i2c-tunnel" }, {}, }; MODULE_DEVICE_TABLE(of, cros_ec_i2c_of_match); -#endif + +static const struct acpi_device_id cros_ec_i2c_tunnel_acpi_id[] = { + { "GOOG001A", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_i2c_tunnel_acpi_id); static struct platform_driver ec_i2c_tunnel_driver = { .probe = ec_i2c_probe, .remove = ec_i2c_remove, .driver = { .name = "cros-ec-i2c-tunnel", + .acpi_match_table = ACPI_PTR(cros_ec_i2c_tunnel_acpi_id), .of_match_table = of_match_ptr(cros_ec_i2c_of_match), }, }; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index f1c714acc280..f5e69fe56532 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -64,8 +64,10 @@ * Cedar Fork (PCH) 0x18df 32 hard yes yes yes * Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes * Comet Lake (PCH) 0x02a3 32 hard yes yes yes + * Comet Lake-H (PCH) 0x06a3 32 hard yes yes yes * Elkhart Lake (PCH) 0x4b23 32 hard yes yes yes * Tiger Lake-LP (PCH) 0xa0a3 32 hard yes yes yes + * Jasper Lake (SOC) 0x4da3 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -205,6 +207,7 @@ /* Older devices have their ID defined in <linux/pci_ids.h> */ #define PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS 0x02a3 +#define PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS 0x06a3 #define PCI_DEVICE_ID_INTEL_BAYTRAIL_SMBUS 0x0f12 #define PCI_DEVICE_ID_INTEL_CDF_SMBUS 0x18df #define PCI_DEVICE_ID_INTEL_DNV_SMBUS 0x19df @@ -223,6 +226,7 @@ #define PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS 0x34a3 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23 +#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2 @@ -1069,8 +1073,10 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CANNONLAKE_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) }, { 0, } }; @@ -1750,8 +1756,10 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_CDF_SMBUS: case PCI_DEVICE_ID_INTEL_ICELAKE_LP_SMBUS: case PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS: + case PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS: case PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS: case PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS: + case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS: priv->features |= FEATURE_BLOCK_PROC; priv->features |= FEATURE_I2C_BLOCK_READ; priv->features |= FEATURE_IRQ; diff --git a/drivers/i2c/busses/i2c-icy.c b/drivers/i2c/busses/i2c-icy.c index 8382eb64b424..271470f4d8a9 100644 --- a/drivers/i2c/busses/i2c-icy.c +++ b/drivers/i2c/busses/i2c-icy.c @@ -122,7 +122,6 @@ static int icy_probe(struct zorro_dev *z, struct fwnode_handle *new_fwnode; struct i2c_board_info ltc2990_info = { .type = "ltc2990", - .addr = 0x4c, }; i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL); @@ -188,10 +187,10 @@ static int icy_probe(struct zorro_dev *z, ltc2990_info.fwnode = new_fwnode; i2c->ltc2990_client = - i2c_new_probed_device(&i2c->adapter, - <c2990_info, - icy_ltc2990_addresses, - NULL); + i2c_new_scanned_device(&i2c->adapter, + <c2990_info, + icy_ltc2990_addresses, + NULL); } return 0; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 2c3c3d6935c0..466e4f681d7a 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -25,7 +25,6 @@ #include <linux/delay.h> #include <linux/errno.h> #include <linux/interrupt.h> -#include <linux/i2c-pxa.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> @@ -180,7 +179,7 @@ struct pxa_i2c { struct i2c_adapter adap; struct clk *clk; #ifdef CONFIG_I2C_PXA_SLAVE - struct i2c_slave_client *slave; + struct i2c_client *slave; #endif unsigned int irqlogidx; @@ -544,22 +543,23 @@ static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) if (isr & ISR_BED) { /* what should we do here? */ } else { - int ret = 0; + u8 byte = 0; if (i2c->slave != NULL) - ret = i2c->slave->read(i2c->slave->data); + i2c_slave_event(i2c->slave, I2C_SLAVE_READ_PROCESSED, + &byte); - writel(ret, _IDBR(i2c)); + writel(byte, _IDBR(i2c)); writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); /* allow next byte */ } } static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) { - unsigned int byte = readl(_IDBR(i2c)); + u8 byte = readl(_IDBR(i2c)); if (i2c->slave != NULL) - i2c->slave->write(i2c->slave->data, byte); + i2c_slave_event(i2c->slave, I2C_SLAVE_WRITE_RECEIVED, &byte); writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); } @@ -572,9 +572,18 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) dev_dbg(&i2c->adap.dev, "SAD, mode is slave-%cx\n", (isr & ISR_RWM) ? 'r' : 't'); - if (i2c->slave != NULL) - i2c->slave->event(i2c->slave->data, - (isr & ISR_RWM) ? I2C_SLAVE_EVENT_START_READ : I2C_SLAVE_EVENT_START_WRITE); + if (i2c->slave != NULL) { + if (isr & ISR_RWM) { + u8 byte = 0; + + i2c_slave_event(i2c->slave, I2C_SLAVE_READ_REQUESTED, + &byte); + writel(byte, _IDBR(i2c)); + } else { + i2c_slave_event(i2c->slave, I2C_SLAVE_WRITE_REQUESTED, + NULL); + } + } /* * slave could interrupt in the middle of us generating a @@ -607,7 +616,7 @@ static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop)\n"); if (i2c->slave != NULL) - i2c->slave->event(i2c->slave->data, I2C_SLAVE_EVENT_STOP); + i2c_slave_event(i2c->slave, I2C_SLAVE_STOP, NULL); if (i2c_debug > 2) dev_dbg(&i2c->adap.dev, "ISR: SSD (Slave Stop) acked\n"); @@ -619,6 +628,38 @@ static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) if (i2c->msg) i2c_pxa_master_complete(i2c, I2C_RETRY); } + +static int i2c_pxa_slave_reg(struct i2c_client *slave) +{ + struct pxa_i2c *i2c = slave->adapter->algo_data; + + if (i2c->slave) + return -EBUSY; + + if (!i2c->reg_isar) + return -EAFNOSUPPORT; + + i2c->slave = slave; + i2c->slave_addr = slave->addr; + + writel(i2c->slave_addr, _ISAR(i2c)); + + return 0; +} + +static int i2c_pxa_slave_unreg(struct i2c_client *slave) +{ + struct pxa_i2c *i2c = slave->adapter->algo_data; + + WARN_ON(!i2c->slave); + + i2c->slave_addr = I2C_PXA_SLAVE_ADDR; + writel(i2c->slave_addr, _ISAR(i2c)); + + i2c->slave = NULL; + + return 0; +} #else static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) { @@ -1141,11 +1182,19 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap) static const struct i2c_algorithm i2c_pxa_algorithm = { .master_xfer = i2c_pxa_xfer, .functionality = i2c_pxa_functionality, +#ifdef CONFIG_I2C_PXA_SLAVE + .reg_slave = i2c_pxa_slave_reg, + .unreg_slave = i2c_pxa_slave_unreg, +#endif }; static const struct i2c_algorithm i2c_pxa_pio_algorithm = { .master_xfer = i2c_pxa_pio_xfer, .functionality = i2c_pxa_functionality, +#ifdef CONFIG_I2C_PXA_SLAVE + .reg_slave = i2c_pxa_slave_reg, + .unreg_slave = i2c_pxa_slave_unreg, +#endif }; static const struct of_device_id i2c_pxa_dt_ids[] = { @@ -1270,10 +1319,6 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->highmode_enter = false; if (plat) { -#ifdef CONFIG_I2C_PXA_SLAVE - i2c->slave_addr = plat->slave_addr; - i2c->slave = plat->slave; -#endif i2c->adap.class = plat->class; } diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index e09cd0775ae9..2d7dabe12723 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -628,7 +628,7 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup) int err; if (!qup->btx.dma) { - qup->btx.dma = dma_request_slave_channel_reason(qup->dev, "tx"); + qup->btx.dma = dma_request_chan(qup->dev, "tx"); if (IS_ERR(qup->btx.dma)) { err = PTR_ERR(qup->btx.dma); qup->btx.dma = NULL; @@ -638,7 +638,7 @@ static int qup_i2c_req_dma(struct qup_i2c_dev *qup) } if (!qup->brx.dma) { - qup->brx.dma = dma_request_slave_channel_reason(qup->dev, "rx"); + qup->brx.dma = dma_request_chan(qup->dev, "rx"); if (IS_ERR(qup->brx.dma)) { dev_err(qup->dev, "\n rx channel not available"); err = PTR_ERR(qup->brx.dma); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 531c01100b56..879f0e61a496 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -317,7 +317,7 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timin scgd_find: dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n", - scl, t->bus_freq_hz, clk_get_rate(priv->clk), round, cdf, scgd); + scl, t->bus_freq_hz, rate, round, cdf, scgd); /* keep icccr value */ priv->icccr = scgd << cdf_width | cdf; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 8777af4c695e..82b3b795e0bd 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -486,7 +486,7 @@ static struct dma_chan *sh_mobile_i2c_request_dma_chan(struct device *dev, char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx"; int ret; - chan = dma_request_slave_channel_reason(dev, chan_name); + chan = dma_request_chan(dev, chan_name); if (IS_ERR(chan)) { dev_dbg(dev, "request_channel failed for %s (%ld)\n", chan_name, PTR_ERR(chan)); diff --git a/drivers/i2c/busses/i2c-stm32.c b/drivers/i2c/busses/i2c-stm32.c index 07d5dfce68d4..1da347e6a358 100644 --- a/drivers/i2c/busses/i2c-stm32.c +++ b/drivers/i2c/busses/i2c-stm32.c @@ -20,13 +20,13 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL); if (!dma) - return NULL; + return ERR_PTR(-ENOMEM); /* Request and configure I2C TX dma channel */ - dma->chan_tx = dma_request_slave_channel(dev, "tx"); - if (!dma->chan_tx) { + dma->chan_tx = dma_request_chan(dev, "tx"); + if (IS_ERR(dma->chan_tx)) { dev_dbg(dev, "can't request DMA tx channel\n"); - ret = -EINVAL; + ret = PTR_ERR(dma->chan_tx); goto fail_al; } @@ -42,10 +42,10 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev, } /* Request and configure I2C RX dma channel */ - dma->chan_rx = dma_request_slave_channel(dev, "rx"); - if (!dma->chan_rx) { + dma->chan_rx = dma_request_chan(dev, "rx"); + if (IS_ERR(dma->chan_rx)) { dev_err(dev, "can't request DMA rx channel\n"); - ret = -EINVAL; + ret = PTR_ERR(dma->chan_rx); goto fail_tx; } @@ -75,7 +75,7 @@ fail_al: devm_kfree(dev, dma); dev_info(dev, "can't use DMA\n"); - return NULL; + return ERR_PTR(ret); } void stm32_i2c_dma_free(struct stm32_i2c_dma *dma) diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index b24e7b937f21..b2634afe066d 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -1267,8 +1267,8 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev, * slave[0] supports 7-bit and 10-bit slave address * slave[1] supports 7-bit slave address only */ - for (i = 0; i < STM32F7_I2C_MAX_SLAVE; i++) { - if (i == 1 && (slave->flags & I2C_CLIENT_PEC)) + for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) { + if (i == 1 && (slave->flags & I2C_CLIENT_TEN)) continue; if (!i2c_dev->slave[i]) { *id = i; @@ -1955,6 +1955,15 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr, STM32F7_I2C_TXDR, STM32F7_I2C_RXDR); + if (PTR_ERR(i2c_dev->dma) == -ENODEV) + i2c_dev->dma = NULL; + else if (IS_ERR(i2c_dev->dma)) { + ret = PTR_ERR(i2c_dev->dma); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "Failed to request dma error %i\n", ret); + goto clk_free; + } platform_set_drvdata(pdev, i2c_dev); @@ -1985,6 +1994,11 @@ pm_disable: pm_runtime_set_suspended(i2c_dev->dev); pm_runtime_dont_use_autosuspend(i2c_dev->dev); + if (i2c_dev->dma) { + stm32_i2c_dma_free(i2c_dev->dma); + i2c_dev->dma = NULL; + } + clk_free: clk_disable_unprepare(i2c_dev->clk); @@ -1995,21 +2009,21 @@ static int stm32f7_i2c_remove(struct platform_device *pdev) { struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev); - if (i2c_dev->dma) { - stm32_i2c_dma_free(i2c_dev->dma); - i2c_dev->dma = NULL; - } - i2c_del_adapter(&i2c_dev->adap); pm_runtime_get_sync(i2c_dev->dev); - clk_disable_unprepare(i2c_dev->clk); - pm_runtime_put_noidle(i2c_dev->dev); pm_runtime_disable(i2c_dev->dev); pm_runtime_set_suspended(i2c_dev->dev); pm_runtime_dont_use_autosuspend(i2c_dev->dev); + if (i2c_dev->dma) { + stm32_i2c_dma_free(i2c_dev->dma); + i2c_dev->dma = NULL; + } + + clk_disable_unprepare(i2c_dev->clk); + return 0; } diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index c1683f9338b4..a98bf31d0e5c 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -413,7 +413,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) return 0; } - chan = dma_request_slave_channel_reason(i2c_dev->dev, "rx"); + chan = dma_request_chan(i2c_dev->dev, "rx"); if (IS_ERR(chan)) { err = PTR_ERR(chan); goto err_out; @@ -421,7 +421,7 @@ static int tegra_i2c_init_dma(struct tegra_i2c_dev *i2c_dev) i2c_dev->rx_dma_chan = chan; - chan = dma_request_slave_channel_reason(i2c_dev->dev, "tx"); + chan = dma_request_chan(i2c_dev->dev, "tx"); if (IS_ERR(chan)) { err = PTR_ERR(chan); goto err_out; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 37b3b9307d07..d8d49f1814c7 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -46,6 +46,7 @@ enum xiic_endian { /** * struct xiic_i2c - Internal representation of the XIIC I2C bus + * @dev: Pointer to device structure * @base: Memory base of the HW registers * @wait: Wait queue for callers * @adap: Kernel adapter representation @@ -57,6 +58,7 @@ enum xiic_endian { * @rx_msg: Current RX message * @rx_pos: Position within current RX message * @endianness: big/little-endian byte order + * @clk: Pointer to AXI4-lite input clock */ struct xiic_i2c { struct device *dev; diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 5f6a4985f2bc..9333c865d4a9 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1656,6 +1656,12 @@ void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_de t->sda_fall_ns = t->scl_fall_ns; device_property_read_u32(dev, "i2c-sda-hold-time-ns", &t->sda_hold_ns); + + device_property_read_u32(dev, "i2c-digital-filter-width-ns", + &t->digital_filter_width_ns); + + device_property_read_u32(dev, "i2c-analog-filter-cutoff-frequency", + &t->analog_filter_cutoff_freq_hz); } EXPORT_SYMBOL_GPL(i2c_parse_fw_timings); @@ -1737,38 +1743,6 @@ EXPORT_SYMBOL(i2c_del_driver); /* ------------------------------------------------------------------------- */ -/** - * i2c_use_client - increments the reference count of the i2c client structure - * @client: the client being referenced - * - * Each live reference to a client should be refcounted. The driver model does - * that automatically as part of driver binding, so that most drivers don't - * need to do this explicitly: they hold a reference until they're unbound - * from the device. - * - * A pointer to the client with the incremented reference counter is returned. - */ -struct i2c_client *i2c_use_client(struct i2c_client *client) -{ - if (client && get_device(&client->dev)) - return client; - return NULL; -} -EXPORT_SYMBOL(i2c_use_client); - -/** - * i2c_release_client - release a use of the i2c client structure - * @client: the client being no longer referenced - * - * Must be called when a user of a client is finished with it. - */ -void i2c_release_client(struct i2c_client *client) -{ - if (client) - put_device(&client->dev); -} -EXPORT_SYMBOL(i2c_release_client); - struct i2c_cmd_arg { unsigned cmd; void *arg; @@ -2271,10 +2245,10 @@ int i2c_probe_func_quick_read(struct i2c_adapter *adap, unsigned short addr) EXPORT_SYMBOL_GPL(i2c_probe_func_quick_read); struct i2c_client * -i2c_new_probed_device(struct i2c_adapter *adap, - struct i2c_board_info *info, - unsigned short const *addr_list, - int (*probe)(struct i2c_adapter *adap, unsigned short addr)) +i2c_new_scanned_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *adap, unsigned short addr)) { int i; @@ -2304,11 +2278,24 @@ i2c_new_probed_device(struct i2c_adapter *adap, if (addr_list[i] == I2C_CLIENT_END) { dev_dbg(&adap->dev, "Probing failed, no device found\n"); - return NULL; + return ERR_PTR(-ENODEV); } info->addr = addr_list[i]; - return i2c_new_device(adap, info); + return i2c_new_client_device(adap, info); +} +EXPORT_SYMBOL_GPL(i2c_new_scanned_device); + +struct i2c_client * +i2c_new_probed_device(struct i2c_adapter *adap, + struct i2c_board_info *info, + unsigned short const *addr_list, + int (*probe)(struct i2c_adapter *adap, unsigned short addr)) +{ + struct i2c_client *client; + + client = i2c_new_scanned_device(adap, info, addr_list, probe); + return IS_ERR(client) ? NULL : client; } EXPORT_SYMBOL_GPL(i2c_new_probed_device); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 03096f47e6ab..7e2f5d0eacdb 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -66,7 +66,6 @@ static irqreturn_t smbus_alert(int irq, void *d) { struct i2c_smbus_alert *alert = d; struct i2c_client *ara; - unsigned short prev_addr = 0; /* Not a valid address */ ara = alert->ara; @@ -90,18 +89,12 @@ static irqreturn_t smbus_alert(int irq, void *d) data.addr = status >> 1; data.type = I2C_PROTOCOL_SMBUS_ALERT; - if (data.addr == prev_addr) { - dev_warn(&ara->dev, "Duplicate SMBALERT# from dev " - "0x%02x, skipping\n", data.addr); - break; - } dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n", data.addr, data.data); /* Notify driver for the device which issued the alert */ device_for_each_child(&ara->adapter->dev, &data, smbus_do_alert); - prev_addr = data.addr; } return IRQ_HANDLED; diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index c6040aa839ac..1708b1a82da2 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -109,14 +109,14 @@ config I2C_DEMUX_PINCTRL want to change the I2C master at run-time depending on features. config I2C_MUX_MLXCPLD - tristate "Mellanox CPLD based I2C multiplexer" - help - If you say yes to this option, support will be included for a - CPLD based I2C multiplexer. This driver provides access to - I2C busses connected through a MUX, which is controlled - by a CPLD register. - - This driver can also be built as a module. If so, the module - will be called i2c-mux-mlxcpld. + tristate "Mellanox CPLD based I2C multiplexer" + help + If you say yes to this option, support will be included for a + CPLD based I2C multiplexer. This driver provides access to + I2C busses connected through a MUX, which is controlled + by a CPLD register. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-mlxcpld. endmenu diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index db1a65f4b490..3e7482695f77 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -19,6 +19,7 @@ #define IDETAPE_VERSION "1.20" +#include <linux/compat.h> #include <linux/module.h> #include <linux/types.h> #include <linux/string.h> @@ -1407,14 +1408,10 @@ static long do_idetape_chrdev_ioctl(struct file *file, if (tape->drv_write_prot) mtget.mt_gstat |= GMT_WR_PROT(0xffffffff); - if (copy_to_user(argp, &mtget, sizeof(struct mtget))) - return -EFAULT; - return 0; + return put_user_mtget(argp, &mtget); case MTIOCPOS: mtpos.mt_blkno = position / tape->user_bs_factor - block_offset; - if (copy_to_user(argp, &mtpos, sizeof(struct mtpos))) - return -EFAULT; - return 0; + return put_user_mtpos(argp, &mtpos); default: if (tape->chrdev_dir == IDETAPE_DIR_READ) ide_tape_discard_merge_buffer(drive, 1); @@ -1432,6 +1429,22 @@ static long idetape_chrdev_ioctl(struct file *file, return ret; } +static long idetape_chrdev_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + + if (cmd == MTIOCPOS32) + cmd = MTIOCPOS; + else if (cmd == MTIOCGET32) + cmd = MTIOCGET; + + mutex_lock(&ide_tape_mutex); + ret = do_idetape_chrdev_ioctl(file, cmd, arg); + mutex_unlock(&ide_tape_mutex); + return ret; +} + /* * Do a mode sense page 0 with block descriptor and if it succeeds set the tape * block size with the reported value. @@ -1886,6 +1899,8 @@ static const struct file_operations idetape_fops = { .read = idetape_chrdev_read, .write = idetape_chrdev_write, .unlocked_ioctl = idetape_chrdev_ioctl, + .compat_ioctl = IS_ENABLED(CONFIG_COMPAT) ? + idetape_chrdev_compat_ioctl : NULL, .open = idetape_chrdev_open, .release = idetape_chrdev_release, .llseek = noop_llseek, diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f72c2dc5f703..a46cdf2d8833 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1624,7 +1624,7 @@ static const struct file_operations iio_buffer_fileops = { .owner = THIS_MODULE, .llseek = noop_llseek, .unlocked_ioctl = iio_ioctl, - .compat_ioctl = iio_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static int iio_check_unique_scan_index(struct iio_dev *indio_dev) diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index 9ebb09a75049..84dd74fe13b8 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -2634,7 +2634,6 @@ void ib_set_device_ops(struct ib_device *dev, const struct ib_device_ops *ops) SET_DEVICE_OP(dev_ops, get_vf_guid); SET_DEVICE_OP(dev_ops, get_vf_stats); SET_DEVICE_OP(dev_ops, init_port); - SET_DEVICE_OP(dev_ops, invalidate_range); SET_DEVICE_OP(dev_ops, iw_accept); SET_DEVICE_OP(dev_ops, iw_add_ref); SET_DEVICE_OP(dev_ops, iw_connect); diff --git a/drivers/infiniband/core/umem_odp.c b/drivers/infiniband/core/umem_odp.c index d7d5fadf0899..e42d44e501fd 100644 --- a/drivers/infiniband/core/umem_odp.c +++ b/drivers/infiniband/core/umem_odp.c @@ -48,197 +48,33 @@ #include "uverbs.h" -static void ib_umem_notifier_start_account(struct ib_umem_odp *umem_odp) +static inline int ib_init_umem_odp(struct ib_umem_odp *umem_odp, + const struct mmu_interval_notifier_ops *ops) { - mutex_lock(&umem_odp->umem_mutex); - if (umem_odp->notifiers_count++ == 0) - /* - * Initialize the completion object for waiting on - * notifiers. Since notifier_count is zero, no one should be - * waiting right now. - */ - reinit_completion(&umem_odp->notifier_completion); - mutex_unlock(&umem_odp->umem_mutex); -} - -static void ib_umem_notifier_end_account(struct ib_umem_odp *umem_odp) -{ - mutex_lock(&umem_odp->umem_mutex); - /* - * This sequence increase will notify the QP page fault that the page - * that is going to be mapped in the spte could have been freed. - */ - ++umem_odp->notifiers_seq; - if (--umem_odp->notifiers_count == 0) - complete_all(&umem_odp->notifier_completion); - mutex_unlock(&umem_odp->umem_mutex); -} - -static void ib_umem_notifier_release(struct mmu_notifier *mn, - struct mm_struct *mm) -{ - struct ib_ucontext_per_mm *per_mm = - container_of(mn, struct ib_ucontext_per_mm, mn); - struct rb_node *node; - - down_read(&per_mm->umem_rwsem); - if (!per_mm->mn.users) - goto out; - - for (node = rb_first_cached(&per_mm->umem_tree); node; - node = rb_next(node)) { - struct ib_umem_odp *umem_odp = - rb_entry(node, struct ib_umem_odp, interval_tree.rb); - - /* - * Increase the number of notifiers running, to prevent any - * further fault handling on this MR. - */ - ib_umem_notifier_start_account(umem_odp); - complete_all(&umem_odp->notifier_completion); - umem_odp->umem.ibdev->ops.invalidate_range( - umem_odp, ib_umem_start(umem_odp), - ib_umem_end(umem_odp)); - } - -out: - up_read(&per_mm->umem_rwsem); -} - -static int invalidate_range_start_trampoline(struct ib_umem_odp *item, - u64 start, u64 end, void *cookie) -{ - ib_umem_notifier_start_account(item); - item->umem.ibdev->ops.invalidate_range(item, start, end); - return 0; -} - -static int ib_umem_notifier_invalidate_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct ib_ucontext_per_mm *per_mm = - container_of(mn, struct ib_ucontext_per_mm, mn); - int rc; - - if (mmu_notifier_range_blockable(range)) - down_read(&per_mm->umem_rwsem); - else if (!down_read_trylock(&per_mm->umem_rwsem)) - return -EAGAIN; - - if (!per_mm->mn.users) { - up_read(&per_mm->umem_rwsem); - /* - * At this point users is permanently zero and visible to this - * CPU without a lock, that fact is relied on to skip the unlock - * in range_end. - */ - return 0; - } - - rc = rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, range->start, - range->end, - invalidate_range_start_trampoline, - mmu_notifier_range_blockable(range), - NULL); - if (rc) - up_read(&per_mm->umem_rwsem); - return rc; -} - -static int invalidate_range_end_trampoline(struct ib_umem_odp *item, u64 start, - u64 end, void *cookie) -{ - ib_umem_notifier_end_account(item); - return 0; -} - -static void ib_umem_notifier_invalidate_range_end(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct ib_ucontext_per_mm *per_mm = - container_of(mn, struct ib_ucontext_per_mm, mn); - - if (unlikely(!per_mm->mn.users)) - return; - - rbt_ib_umem_for_each_in_range(&per_mm->umem_tree, range->start, - range->end, - invalidate_range_end_trampoline, true, NULL); - up_read(&per_mm->umem_rwsem); -} - -static struct mmu_notifier *ib_umem_alloc_notifier(struct mm_struct *mm) -{ - struct ib_ucontext_per_mm *per_mm; - - per_mm = kzalloc(sizeof(*per_mm), GFP_KERNEL); - if (!per_mm) - return ERR_PTR(-ENOMEM); - - per_mm->umem_tree = RB_ROOT_CACHED; - init_rwsem(&per_mm->umem_rwsem); - - WARN_ON(mm != current->mm); - rcu_read_lock(); - per_mm->tgid = get_task_pid(current->group_leader, PIDTYPE_PID); - rcu_read_unlock(); - return &per_mm->mn; -} - -static void ib_umem_free_notifier(struct mmu_notifier *mn) -{ - struct ib_ucontext_per_mm *per_mm = - container_of(mn, struct ib_ucontext_per_mm, mn); - - WARN_ON(!RB_EMPTY_ROOT(&per_mm->umem_tree.rb_root)); - - put_pid(per_mm->tgid); - kfree(per_mm); -} - -static const struct mmu_notifier_ops ib_umem_notifiers = { - .release = ib_umem_notifier_release, - .invalidate_range_start = ib_umem_notifier_invalidate_range_start, - .invalidate_range_end = ib_umem_notifier_invalidate_range_end, - .alloc_notifier = ib_umem_alloc_notifier, - .free_notifier = ib_umem_free_notifier, -}; - -static inline int ib_init_umem_odp(struct ib_umem_odp *umem_odp) -{ - struct ib_ucontext_per_mm *per_mm; - struct mmu_notifier *mn; int ret; umem_odp->umem.is_odp = 1; + mutex_init(&umem_odp->umem_mutex); + if (!umem_odp->is_implicit_odp) { size_t page_size = 1UL << umem_odp->page_shift; + unsigned long start; + unsigned long end; size_t pages; - umem_odp->interval_tree.start = - ALIGN_DOWN(umem_odp->umem.address, page_size); + start = ALIGN_DOWN(umem_odp->umem.address, page_size); if (check_add_overflow(umem_odp->umem.address, (unsigned long)umem_odp->umem.length, - &umem_odp->interval_tree.last)) + &end)) return -EOVERFLOW; - umem_odp->interval_tree.last = - ALIGN(umem_odp->interval_tree.last, page_size); - if (unlikely(umem_odp->interval_tree.last < page_size)) + end = ALIGN(end, page_size); + if (unlikely(end < page_size)) return -EOVERFLOW; - pages = (umem_odp->interval_tree.last - - umem_odp->interval_tree.start) >> - umem_odp->page_shift; + pages = (end - start) >> umem_odp->page_shift; if (!pages) return -EINVAL; - /* - * Note that the representation of the intervals in the - * interval tree considers the ending point as contained in - * the interval. - */ - umem_odp->interval_tree.last--; - umem_odp->page_list = kvcalloc( pages, sizeof(*umem_odp->page_list), GFP_KERNEL); if (!umem_odp->page_list) @@ -250,26 +86,13 @@ static inline int ib_init_umem_odp(struct ib_umem_odp *umem_odp) ret = -ENOMEM; goto out_page_list; } - } - mn = mmu_notifier_get(&ib_umem_notifiers, umem_odp->umem.owning_mm); - if (IS_ERR(mn)) { - ret = PTR_ERR(mn); - goto out_dma_list; + ret = mmu_interval_notifier_insert(&umem_odp->notifier, + umem_odp->umem.owning_mm, + start, end - start, ops); + if (ret) + goto out_dma_list; } - umem_odp->per_mm = per_mm = - container_of(mn, struct ib_ucontext_per_mm, mn); - - mutex_init(&umem_odp->umem_mutex); - init_completion(&umem_odp->notifier_completion); - - if (!umem_odp->is_implicit_odp) { - down_write(&per_mm->umem_rwsem); - interval_tree_insert(&umem_odp->interval_tree, - &per_mm->umem_tree); - up_write(&per_mm->umem_rwsem); - } - mmgrab(umem_odp->umem.owning_mm); return 0; @@ -305,8 +128,6 @@ struct ib_umem_odp *ib_umem_odp_alloc_implicit(struct ib_udata *udata, if (!context) return ERR_PTR(-EIO); - if (WARN_ON_ONCE(!context->device->ops.invalidate_range)) - return ERR_PTR(-EINVAL); umem_odp = kzalloc(sizeof(*umem_odp), GFP_KERNEL); if (!umem_odp) @@ -318,8 +139,10 @@ struct ib_umem_odp *ib_umem_odp_alloc_implicit(struct ib_udata *udata, umem_odp->is_implicit_odp = 1; umem_odp->page_shift = PAGE_SHIFT; - ret = ib_init_umem_odp(umem_odp); + umem_odp->tgid = get_task_pid(current->group_leader, PIDTYPE_PID); + ret = ib_init_umem_odp(umem_odp, NULL); if (ret) { + put_pid(umem_odp->tgid); kfree(umem_odp); return ERR_PTR(ret); } @@ -336,8 +159,10 @@ EXPORT_SYMBOL(ib_umem_odp_alloc_implicit); * @addr: The starting userspace VA * @size: The length of the userspace VA */ -struct ib_umem_odp *ib_umem_odp_alloc_child(struct ib_umem_odp *root, - unsigned long addr, size_t size) +struct ib_umem_odp * +ib_umem_odp_alloc_child(struct ib_umem_odp *root, unsigned long addr, + size_t size, + const struct mmu_interval_notifier_ops *ops) { /* * Caller must ensure that root cannot be freed during the call to @@ -360,9 +185,12 @@ struct ib_umem_odp *ib_umem_odp_alloc_child(struct ib_umem_odp *root, umem->writable = root->umem.writable; umem->owning_mm = root->umem.owning_mm; odp_data->page_shift = PAGE_SHIFT; + odp_data->notifier.ops = ops; - ret = ib_init_umem_odp(odp_data); + odp_data->tgid = get_pid(root->tgid); + ret = ib_init_umem_odp(odp_data, ops); if (ret) { + put_pid(odp_data->tgid); kfree(odp_data); return ERR_PTR(ret); } @@ -383,7 +211,8 @@ EXPORT_SYMBOL(ib_umem_odp_alloc_child); * conjunction with MMU notifiers. */ struct ib_umem_odp *ib_umem_odp_get(struct ib_udata *udata, unsigned long addr, - size_t size, int access) + size_t size, int access, + const struct mmu_interval_notifier_ops *ops) { struct ib_umem_odp *umem_odp; struct ib_ucontext *context; @@ -398,8 +227,7 @@ struct ib_umem_odp *ib_umem_odp_get(struct ib_udata *udata, unsigned long addr, if (!context) return ERR_PTR(-EIO); - if (WARN_ON_ONCE(!(access & IB_ACCESS_ON_DEMAND)) || - WARN_ON_ONCE(!context->device->ops.invalidate_range)) + if (WARN_ON_ONCE(!(access & IB_ACCESS_ON_DEMAND))) return ERR_PTR(-EINVAL); umem_odp = kzalloc(sizeof(struct ib_umem_odp), GFP_KERNEL); @@ -411,6 +239,7 @@ struct ib_umem_odp *ib_umem_odp_get(struct ib_udata *udata, unsigned long addr, umem_odp->umem.address = addr; umem_odp->umem.writable = ib_access_writable(access); umem_odp->umem.owning_mm = mm = current->mm; + umem_odp->notifier.ops = ops; umem_odp->page_shift = PAGE_SHIFT; if (access & IB_ACCESS_HUGETLB) { @@ -429,11 +258,14 @@ struct ib_umem_odp *ib_umem_odp_get(struct ib_udata *udata, unsigned long addr, up_read(&mm->mmap_sem); } - ret = ib_init_umem_odp(umem_odp); + umem_odp->tgid = get_task_pid(current->group_leader, PIDTYPE_PID); + ret = ib_init_umem_odp(umem_odp, ops); if (ret) - goto err_free; + goto err_put_pid; return umem_odp; +err_put_pid: + put_pid(umem_odp->tgid); err_free: kfree(umem_odp); return ERR_PTR(ret); @@ -442,8 +274,6 @@ EXPORT_SYMBOL(ib_umem_odp_get); void ib_umem_odp_release(struct ib_umem_odp *umem_odp) { - struct ib_ucontext_per_mm *per_mm = umem_odp->per_mm; - /* * Ensure that no more pages are mapped in the umem. * @@ -455,28 +285,11 @@ void ib_umem_odp_release(struct ib_umem_odp *umem_odp) ib_umem_odp_unmap_dma_pages(umem_odp, ib_umem_start(umem_odp), ib_umem_end(umem_odp)); mutex_unlock(&umem_odp->umem_mutex); + mmu_interval_notifier_remove(&umem_odp->notifier); kvfree(umem_odp->dma_list); kvfree(umem_odp->page_list); + put_pid(umem_odp->tgid); } - - down_write(&per_mm->umem_rwsem); - if (!umem_odp->is_implicit_odp) { - interval_tree_remove(&umem_odp->interval_tree, - &per_mm->umem_tree); - complete_all(&umem_odp->notifier_completion); - } - /* - * NOTE! mmu_notifier_unregister() can happen between a start/end - * callback, resulting in a missing end, and thus an unbalanced - * lock. This doesn't really matter to us since we are about to kfree - * the memory that holds the lock, however LOCKDEP doesn't like this. - * Thus we call the mmu_notifier_put under the rwsem and test the - * internal users count to reliably see if we are past this point. - */ - mmu_notifier_put(&per_mm->mn); - up_write(&per_mm->umem_rwsem); - - mmdrop(umem_odp->umem.owning_mm); kfree(umem_odp); } EXPORT_SYMBOL(ib_umem_odp_release); @@ -501,7 +314,7 @@ EXPORT_SYMBOL(ib_umem_odp_release); */ static int ib_umem_odp_map_dma_single_page( struct ib_umem_odp *umem_odp, - int page_index, + unsigned int page_index, struct page *page, u64 access_mask, unsigned long current_seq) @@ -510,12 +323,7 @@ static int ib_umem_odp_map_dma_single_page( dma_addr_t dma_addr; int ret = 0; - /* - * Note: we avoid writing if seq is different from the initial seq, to - * handle case of a racing notifier. This check also allows us to bail - * early if we have a notifier running in parallel with us. - */ - if (ib_umem_mmu_notifier_retry(umem_odp, current_seq)) { + if (mmu_interval_check_retry(&umem_odp->notifier, current_seq)) { ret = -EAGAIN; goto out; } @@ -618,7 +426,7 @@ int ib_umem_odp_map_dma_pages(struct ib_umem_odp *umem_odp, u64 user_virt, * existing beyond the lifetime of the originating process.. Presumably * mmget_not_zero will fail in this case. */ - owning_process = get_pid_task(umem_odp->per_mm->tgid, PIDTYPE_PID); + owning_process = get_pid_task(umem_odp->tgid, PIDTYPE_PID); if (!owning_process || !mmget_not_zero(owning_mm)) { ret = -EINVAL; goto out_put_task; @@ -762,32 +570,3 @@ void ib_umem_odp_unmap_dma_pages(struct ib_umem_odp *umem_odp, u64 virt, } } EXPORT_SYMBOL(ib_umem_odp_unmap_dma_pages); - -/* @last is not a part of the interval. See comment for function - * node_last. - */ -int rbt_ib_umem_for_each_in_range(struct rb_root_cached *root, - u64 start, u64 last, - umem_call_back cb, - bool blockable, - void *cookie) -{ - int ret_val = 0; - struct interval_tree_node *node, *next; - struct ib_umem_odp *umem; - - if (unlikely(start == last)) - return ret_val; - - for (node = interval_tree_iter_first(root, start, last - 1); - node; node = next) { - /* TODO move the blockable decision up to the callback */ - if (!blockable) - return -EAGAIN; - next = interval_tree_iter_next(node, start, last - 1); - umem = container_of(node, struct ib_umem_odp, interval_tree); - ret_val = cb(umem, start, last, cookie) || ret_val; - } - - return ret_val; -} diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 9aa7ffc1d12a..970d8e31dd65 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -1079,7 +1079,7 @@ static const struct file_operations uverbs_fops = { .release = ib_uverbs_close, .llseek = no_llseek, .unlocked_ioctl = ib_uverbs_ioctl, - .compat_ioctl = ib_uverbs_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static const struct file_operations uverbs_mmap_fops = { @@ -1090,7 +1090,7 @@ static const struct file_operations uverbs_mmap_fops = { .release = ib_uverbs_close, .llseek = no_llseek, .unlocked_ioctl = ib_uverbs_ioctl, - .compat_ioctl = ib_uverbs_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static int ib_uverbs_get_nl_info(struct ib_device *ibdev, void *client_data, diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index f9a7e9d29c8b..7c5e3fb22413 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -1138,7 +1138,7 @@ static int get_ctxt_info(struct hfi1_filedata *fd, unsigned long arg, u32 len) HFI1_CAP_UGET_MASK(uctxt->flags, MASK) | HFI1_CAP_KGET_MASK(uctxt->flags, K2U); /* adjust flag if this fd is not able to cache */ - if (!fd->handler) + if (!fd->use_mn) cinfo.runtime_flags |= HFI1_CAP_TID_UNMAP; /* no caching */ cinfo.num_active = hfi1_count_active_units(); diff --git a/drivers/infiniband/hw/hfi1/hfi.h b/drivers/infiniband/hw/hfi1/hfi.h index fa45350a9a1d..fc10d65fc3e1 100644 --- a/drivers/infiniband/hw/hfi1/hfi.h +++ b/drivers/infiniband/hw/hfi1/hfi.h @@ -1444,7 +1444,7 @@ struct hfi1_filedata { /* for cpu affinity; -1 if none */ int rec_cpu_num; u32 tid_n_pinned; - struct mmu_rb_handler *handler; + bool use_mn; struct tid_rb_node **entry_to_rb; spinlock_t tid_lock; /* protect tid_[limit,used] counters */ u32 tid_limit; diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c index 3592a9ec155e..f05742ac0949 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c @@ -59,11 +59,11 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd, struct tid_user_buf *tbuf, u32 rcventry, struct tid_group *grp, u16 pageidx, unsigned int npages); -static int tid_rb_insert(void *arg, struct mmu_rb_node *node); static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, struct tid_rb_node *tnode); -static void tid_rb_remove(void *arg, struct mmu_rb_node *node); -static int tid_rb_invalidate(void *arg, struct mmu_rb_node *mnode); +static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq); static int program_rcvarray(struct hfi1_filedata *fd, struct tid_user_buf *, struct tid_group *grp, unsigned int start, u16 count, @@ -73,10 +73,8 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, struct tid_group **grp); static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node); -static struct mmu_rb_ops tid_rb_ops = { - .insert = tid_rb_insert, - .remove = tid_rb_remove, - .invalidate = tid_rb_invalidate +static const struct mmu_interval_notifier_ops tid_mn_ops = { + .invalidate = tid_rb_invalidate, }; /* @@ -87,7 +85,6 @@ static struct mmu_rb_ops tid_rb_ops = { int hfi1_user_exp_rcv_init(struct hfi1_filedata *fd, struct hfi1_ctxtdata *uctxt) { - struct hfi1_devdata *dd = uctxt->dd; int ret = 0; spin_lock_init(&fd->tid_lock); @@ -109,20 +106,7 @@ int hfi1_user_exp_rcv_init(struct hfi1_filedata *fd, fd->entry_to_rb = NULL; return -ENOMEM; } - - /* - * Register MMU notifier callbacks. If the registration - * fails, continue without TID caching for this context. - */ - ret = hfi1_mmu_rb_register(fd, fd->mm, &tid_rb_ops, - dd->pport->hfi1_wq, - &fd->handler); - if (ret) { - dd_dev_info(dd, - "Failed MMU notifier registration %d\n", - ret); - ret = 0; - } + fd->use_mn = true; } /* @@ -139,7 +123,7 @@ int hfi1_user_exp_rcv_init(struct hfi1_filedata *fd, * init. */ spin_lock(&fd->tid_lock); - if (uctxt->subctxt_cnt && fd->handler) { + if (uctxt->subctxt_cnt && fd->use_mn) { u16 remainder; fd->tid_limit = uctxt->expected_count / uctxt->subctxt_cnt; @@ -158,18 +142,10 @@ void hfi1_user_exp_rcv_free(struct hfi1_filedata *fd) { struct hfi1_ctxtdata *uctxt = fd->uctxt; - /* - * The notifier would have been removed when the process'es mm - * was freed. - */ - if (fd->handler) { - hfi1_mmu_rb_unregister(fd->handler); - } else { - if (!EXP_TID_SET_EMPTY(uctxt->tid_full_list)) - unlock_exp_tids(uctxt, &uctxt->tid_full_list, fd); - if (!EXP_TID_SET_EMPTY(uctxt->tid_used_list)) - unlock_exp_tids(uctxt, &uctxt->tid_used_list, fd); - } + if (!EXP_TID_SET_EMPTY(uctxt->tid_full_list)) + unlock_exp_tids(uctxt, &uctxt->tid_full_list, fd); + if (!EXP_TID_SET_EMPTY(uctxt->tid_used_list)) + unlock_exp_tids(uctxt, &uctxt->tid_used_list, fd); kfree(fd->invalid_tids); fd->invalid_tids = NULL; @@ -201,7 +177,7 @@ static void unpin_rcv_pages(struct hfi1_filedata *fd, if (mapped) { pci_unmap_single(dd->pcidev, node->dma_addr, - node->mmu.len, PCI_DMA_FROMDEVICE); + node->npages * PAGE_SIZE, PCI_DMA_FROMDEVICE); pages = &node->pages[idx]; } else { pages = &tidbuf->pages[idx]; @@ -777,8 +753,7 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd, return -EFAULT; } - node->mmu.addr = tbuf->vaddr + (pageidx * PAGE_SIZE); - node->mmu.len = npages * PAGE_SIZE; + node->fdata = fd; node->phys = page_to_phys(pages[0]); node->npages = npages; node->rcventry = rcventry; @@ -787,23 +762,35 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd, node->freed = false; memcpy(node->pages, pages, sizeof(struct page *) * npages); - if (!fd->handler) - ret = tid_rb_insert(fd, &node->mmu); - else - ret = hfi1_mmu_rb_insert(fd->handler, &node->mmu); - - if (ret) { - hfi1_cdbg(TID, "Failed to insert RB node %u 0x%lx, 0x%lx %d", - node->rcventry, node->mmu.addr, node->phys, ret); - pci_unmap_single(dd->pcidev, phys, npages * PAGE_SIZE, - PCI_DMA_FROMDEVICE); - kfree(node); - return -EFAULT; + if (fd->use_mn) { + ret = mmu_interval_notifier_insert( + &node->notifier, fd->mm, + tbuf->vaddr + (pageidx * PAGE_SIZE), npages * PAGE_SIZE, + &tid_mn_ops); + if (ret) + goto out_unmap; + /* + * FIXME: This is in the wrong order, the notifier should be + * established before the pages are pinned by pin_rcv_pages. + */ + mmu_interval_read_begin(&node->notifier); } + fd->entry_to_rb[node->rcventry - uctxt->expected_base] = node; + hfi1_put_tid(dd, rcventry, PT_EXPECTED, phys, ilog2(npages) + 1); trace_hfi1_exp_tid_reg(uctxt->ctxt, fd->subctxt, rcventry, npages, - node->mmu.addr, node->phys, phys); + node->notifier.interval_tree.start, node->phys, + phys); return 0; + +out_unmap: + hfi1_cdbg(TID, "Failed to insert RB node %u 0x%lx, 0x%lx %d", + node->rcventry, node->notifier.interval_tree.start, + node->phys, ret); + pci_unmap_single(dd->pcidev, phys, npages * PAGE_SIZE, + PCI_DMA_FROMDEVICE); + kfree(node); + return -EFAULT; } static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, @@ -833,10 +820,9 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo, if (grp) *grp = node->grp; - if (!fd->handler) - cacheless_tid_rb_remove(fd, node); - else - hfi1_mmu_rb_remove(fd->handler, &node->mmu); + if (fd->use_mn) + mmu_interval_notifier_remove(&node->notifier); + cacheless_tid_rb_remove(fd, node); return 0; } @@ -847,7 +833,8 @@ static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node) struct hfi1_devdata *dd = uctxt->dd; trace_hfi1_exp_tid_unreg(uctxt->ctxt, fd->subctxt, node->rcventry, - node->npages, node->mmu.addr, node->phys, + node->npages, + node->notifier.interval_tree.start, node->phys, node->dma_addr); /* @@ -894,30 +881,29 @@ static void unlock_exp_tids(struct hfi1_ctxtdata *uctxt, if (!node || node->rcventry != rcventry) continue; + if (fd->use_mn) + mmu_interval_notifier_remove( + &node->notifier); cacheless_tid_rb_remove(fd, node); } } } } -/* - * Always return 0 from this function. A non-zero return indicates that the - * remove operation will be called and that memory should be unpinned. - * However, the driver cannot unpin out from under PSM. Instead, retain the - * memory (by returning 0) and inform PSM that the memory is going away. PSM - * will call back later when it has removed the memory from its list. - */ -static int tid_rb_invalidate(void *arg, struct mmu_rb_node *mnode) +static bool tid_rb_invalidate(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { - struct hfi1_filedata *fdata = arg; - struct hfi1_ctxtdata *uctxt = fdata->uctxt; struct tid_rb_node *node = - container_of(mnode, struct tid_rb_node, mmu); + container_of(mni, struct tid_rb_node, notifier); + struct hfi1_filedata *fdata = node->fdata; + struct hfi1_ctxtdata *uctxt = fdata->uctxt; if (node->freed) - return 0; + return true; - trace_hfi1_exp_tid_inval(uctxt->ctxt, fdata->subctxt, node->mmu.addr, + trace_hfi1_exp_tid_inval(uctxt->ctxt, fdata->subctxt, + node->notifier.interval_tree.start, node->rcventry, node->npages, node->dma_addr); node->freed = true; @@ -946,18 +932,7 @@ static int tid_rb_invalidate(void *arg, struct mmu_rb_node *mnode) fdata->invalid_tid_idx++; } spin_unlock(&fdata->invalid_lock); - return 0; -} - -static int tid_rb_insert(void *arg, struct mmu_rb_node *node) -{ - struct hfi1_filedata *fdata = arg; - struct tid_rb_node *tnode = - container_of(node, struct tid_rb_node, mmu); - u32 base = fdata->uctxt->expected_base; - - fdata->entry_to_rb[tnode->rcventry - base] = tnode; - return 0; + return true; } static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, @@ -968,12 +943,3 @@ static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata, fdata->entry_to_rb[tnode->rcventry - base] = NULL; clear_tid_node(fdata, tnode); } - -static void tid_rb_remove(void *arg, struct mmu_rb_node *node) -{ - struct hfi1_filedata *fdata = arg; - struct tid_rb_node *tnode = - container_of(node, struct tid_rb_node, mmu); - - cacheless_tid_rb_remove(fdata, tnode); -} diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h index 43b105de1d54..6257eee083a1 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h @@ -65,7 +65,8 @@ struct tid_user_buf { }; struct tid_rb_node { - struct mmu_rb_node mmu; + struct mmu_interval_notifier notifier; + struct hfi1_filedata *fdata; unsigned long phys; struct tid_group *grp; u32 rcventry; diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 03af54b53bf7..5986953ec2fa 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -1258,8 +1258,6 @@ int mlx5_ib_odp_init_one(struct mlx5_ib_dev *ibdev); void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *ibdev); int __init mlx5_ib_odp_init(void); void mlx5_ib_odp_cleanup(void); -void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, - unsigned long end); void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent); void mlx5_odp_populate_klm(struct mlx5_klm *pklm, size_t offset, size_t nentries, struct mlx5_ib_mr *mr, int flags); @@ -1289,11 +1287,10 @@ mlx5_ib_advise_mr_prefetch(struct ib_pd *pd, { return -EOPNOTSUPP; } -static inline void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, - unsigned long start, - unsigned long end){}; #endif /* CONFIG_INFINIBAND_ON_DEMAND_PAGING */ +extern const struct mmu_interval_notifier_ops mlx5_mn_ops; + /* Needed for rep profile */ void __mlx5_ib_remove(struct mlx5_ib_dev *dev, const struct mlx5_ib_profile *profile, diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 60d39b9ec41c..ea8bfc3e2d8d 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -749,7 +749,8 @@ static int mr_umem_get(struct mlx5_ib_dev *dev, struct ib_udata *udata, if (access_flags & IB_ACCESS_ON_DEMAND) { struct ib_umem_odp *odp; - odp = ib_umem_odp_get(udata, start, length, access_flags); + odp = ib_umem_odp_get(udata, start, length, access_flags, + &mlx5_mn_ops); if (IS_ERR(odp)) { mlx5_ib_dbg(dev, "umem get failed (%ld)\n", PTR_ERR(odp)); diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 45ee40c2f36e..f924250f80c2 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -241,18 +241,27 @@ out_unlock: xa_unlock(&imr->implicit_children); } -void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, - unsigned long end) +static bool mlx5_ib_invalidate_range(struct mmu_interval_notifier *mni, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { + struct ib_umem_odp *umem_odp = + container_of(mni, struct ib_umem_odp, notifier); struct mlx5_ib_mr *mr; const u64 umr_block_mask = (MLX5_UMR_MTT_ALIGNMENT / sizeof(struct mlx5_mtt)) - 1; u64 idx = 0, blk_start_idx = 0; u64 invalidations = 0; + unsigned long start; + unsigned long end; int in_block = 0; u64 addr; + if (!mmu_notifier_range_blockable(range)) + return false; + mutex_lock(&umem_odp->umem_mutex); + mmu_interval_set_seq(mni, cur_seq); /* * If npages is zero then umem_odp->private may not be setup yet. This * does not complete until after the first page is mapped for DMA. @@ -261,8 +270,8 @@ void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, goto out; mr = umem_odp->private; - start = max_t(u64, ib_umem_start(umem_odp), start); - end = min_t(u64, ib_umem_end(umem_odp), end); + start = max_t(u64, ib_umem_start(umem_odp), range->start); + end = min_t(u64, ib_umem_end(umem_odp), range->end); /* * Iteration one - zap the HW's MTTs. The notifiers_count ensures that @@ -319,8 +328,13 @@ void mlx5_ib_invalidate_range(struct ib_umem_odp *umem_odp, unsigned long start, destroy_unused_implicit_child_mr(mr); out: mutex_unlock(&umem_odp->umem_mutex); + return true; } +const struct mmu_interval_notifier_ops mlx5_mn_ops = { + .invalidate = mlx5_ib_invalidate_range, +}; + void mlx5_ib_internal_fill_odp_caps(struct mlx5_ib_dev *dev) { struct ib_odp_caps *caps = &dev->odp_caps; @@ -419,7 +433,7 @@ static struct mlx5_ib_mr *implicit_get_child_mr(struct mlx5_ib_mr *imr, odp = ib_umem_odp_alloc_child(to_ib_umem_odp(imr->umem), idx * MLX5_IMR_MTT_SIZE, - MLX5_IMR_MTT_SIZE); + MLX5_IMR_MTT_SIZE, &mlx5_mn_ops); if (IS_ERR(odp)) return ERR_CAST(odp); @@ -606,8 +620,9 @@ static int pagefault_real_mr(struct mlx5_ib_mr *mr, struct ib_umem_odp *odp, u64 user_va, size_t bcnt, u32 *bytes_mapped, u32 flags) { - int current_seq, page_shift, ret, np; + int page_shift, ret, np; bool downgrade = flags & MLX5_PF_FLAGS_DOWNGRADE; + unsigned long current_seq; u64 access_mask; u64 start_idx, page_mask; @@ -619,12 +634,7 @@ static int pagefault_real_mr(struct mlx5_ib_mr *mr, struct ib_umem_odp *odp, if (odp->umem.writable && !downgrade) access_mask |= ODP_WRITE_ALLOWED_BIT; - current_seq = READ_ONCE(odp->notifiers_seq); - /* - * Ensure the sequence number is valid for some time before we call - * gup. - */ - smp_rmb(); + current_seq = mmu_interval_read_begin(&odp->notifier); np = ib_umem_odp_map_dma_pages(odp, user_va, bcnt, access_mask, current_seq); @@ -632,7 +642,7 @@ static int pagefault_real_mr(struct mlx5_ib_mr *mr, struct ib_umem_odp *odp, return np; mutex_lock(&odp->umem_mutex); - if (!ib_umem_mmu_notifier_retry(odp, current_seq)) { + if (!mmu_interval_read_retry(&odp->notifier, current_seq)) { /* * No need to check whether the MTTs really belong to * this MR, since ib_umem_odp_map_dma_pages already @@ -662,19 +672,6 @@ static int pagefault_real_mr(struct mlx5_ib_mr *mr, struct ib_umem_odp *odp, return np << (page_shift - PAGE_SHIFT); out: - if (ret == -EAGAIN) { - unsigned long timeout = msecs_to_jiffies(MMU_NOTIFIER_TIMEOUT); - - if (!wait_for_completion_timeout(&odp->notifier_completion, - timeout)) { - mlx5_ib_warn( - mr->dev, - "timeout waiting for mmu notifier. seq %d against %d. notifiers_count=%d\n", - current_seq, odp->notifiers_seq, - odp->notifiers_count); - } - } - return ret; } @@ -1622,7 +1619,6 @@ void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) static const struct ib_device_ops mlx5_ib_dev_odp_ops = { .advise_mr = mlx5_ib_advise_mr, - .invalidate_range = mlx5_ib_invalidate_range, }; int mlx5_ib_odp_init_one(struct mlx5_ib_dev *dev) diff --git a/drivers/input/input-poller.c b/drivers/input/input-poller.c index 1b3d28964bb2..7d6b4e8879f1 100644 --- a/drivers/input/input-poller.c +++ b/drivers/input/input-poller.c @@ -123,6 +123,15 @@ void input_set_max_poll_interval(struct input_dev *dev, unsigned int interval) } EXPORT_SYMBOL(input_set_max_poll_interval); +int input_get_poll_interval(struct input_dev *dev) +{ + if (!dev->poller) + return -EINVAL; + + return dev->poller->poll_interval; +} +EXPORT_SYMBOL(input_get_poll_interval); + /* SYSFS interface */ static ssize_t input_dev_get_poll_interval(struct device *dev, diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 312b854b5506..940b744639c7 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -334,7 +334,6 @@ config JOYSTICK_MAPLE config JOYSTICK_PSXPAD_SPI tristate "PlayStation 1/2 joypads via SPI interface" depends on SPI - select INPUT_POLLDEV help Say Y here if you wish to connect PlayStation 1/2 joypads via SPI interface. diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c index 7eee1b0e360f..a32656064f39 100644 --- a/drivers/input/joystick/psxpad-spi.c +++ b/drivers/input/joystick/psxpad-spi.c @@ -22,7 +22,6 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/module.h> #include <linux/spi/spi.h> #include <linux/types.h> @@ -60,7 +59,7 @@ static const u8 PSX_CMD_ENABLE_MOTOR[] = { struct psxpad { struct spi_device *spi; - struct input_polled_dev *pdev; + struct input_dev *idev; char phys[0x20]; bool motor1enable; bool motor2enable; @@ -140,8 +139,7 @@ static void psxpad_set_motor_level(struct psxpad *pad, static int psxpad_spi_play_effect(struct input_dev *idev, void *data, struct ff_effect *effect) { - struct input_polled_dev *pdev = input_get_drvdata(idev); - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(idev); switch (effect->type) { case FF_RUMBLE: @@ -158,10 +156,9 @@ static int psxpad_spi_init_ff(struct psxpad *pad) { int err; - input_set_capability(pad->pdev->input, EV_FF, FF_RUMBLE); + input_set_capability(pad->idev, EV_FF, FF_RUMBLE); - err = input_ff_create_memless(pad->pdev->input, NULL, - psxpad_spi_play_effect); + err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_play_effect); if (err) { dev_err(&pad->spi->dev, "input_ff_create_memless() failed: %d\n", err); @@ -189,24 +186,25 @@ static inline int psxpad_spi_init_ff(struct psxpad *pad) } #endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */ -static void psxpad_spi_poll_open(struct input_polled_dev *pdev) +static int psxpad_spi_poll_open(struct input_dev *input) { - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(input); pm_runtime_get_sync(&pad->spi->dev); + + return 0; } -static void psxpad_spi_poll_close(struct input_polled_dev *pdev) +static void psxpad_spi_poll_close(struct input_dev *input) { - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(input); pm_runtime_put_sync(&pad->spi->dev); } -static void psxpad_spi_poll(struct input_polled_dev *pdev) +static void psxpad_spi_poll(struct input_dev *input) { - struct psxpad *pad = pdev->private; - struct input_dev *input = pdev->input; + struct psxpad *pad = input_get_drvdata(input); u8 b_rsp3, b_rsp4; int err; @@ -284,7 +282,6 @@ static void psxpad_spi_poll(struct input_polled_dev *pdev) static int psxpad_spi_probe(struct spi_device *spi) { struct psxpad *pad; - struct input_polled_dev *pdev; struct input_dev *idev; int err; @@ -292,31 +289,26 @@ static int psxpad_spi_probe(struct spi_device *spi) if (!pad) return -ENOMEM; - pdev = input_allocate_polled_device(); - if (!pdev) { + idev = devm_input_allocate_device(&spi->dev); + if (!idev) { dev_err(&spi->dev, "failed to allocate input device\n"); return -ENOMEM; } /* input poll device settings */ - pad->pdev = pdev; + pad->idev = idev; pad->spi = spi; - pdev->private = pad; - pdev->open = psxpad_spi_poll_open; - pdev->close = psxpad_spi_poll_close; - pdev->poll = psxpad_spi_poll; - /* poll interval is about 60fps */ - pdev->poll_interval = 16; - pdev->poll_interval_min = 8; - pdev->poll_interval_max = 32; - /* input device settings */ - idev = pdev->input; + input_set_drvdata(idev, pad); + idev->name = "PlayStation 1/2 joypad"; snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev)); idev->id.bustype = BUS_SPI; + idev->open = psxpad_spi_poll_open; + idev->close = psxpad_spi_poll_close; + /* key/value map settings */ input_set_abs_params(idev, ABS_X, 0, 255, 0, 0); input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0); @@ -354,11 +346,23 @@ static int psxpad_spi_probe(struct spi_device *spi) /* pad settings */ psxpad_set_motor_level(pad, 0, 0); + + err = input_setup_polling(idev, psxpad_spi_poll); + if (err) { + dev_err(&spi->dev, "failed to set up polling: %d\n", err); + return err; + } + + /* poll interval is about 60fps */ + input_set_poll_interval(idev, 16); + input_set_min_poll_interval(idev, 8); + input_set_max_poll_interval(idev, 32); + /* register input poll device */ - err = input_register_polled_device(pdev); + err = input_register_device(idev); if (err) { dev_err(&spi->dev, - "failed to register input poll device: %d\n", err); + "failed to register input device: %d\n", err); return err; } diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 8911bc2ec42a..61f4eb63eec1 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -16,7 +16,6 @@ if INPUT_KEYBOARD config KEYBOARD_ADC tristate "ADC Ladder Buttons" depends on IIO - select INPUT_POLLDEV help This driver implements support for buttons connected to an ADC using a resistor ladder. @@ -168,14 +167,14 @@ config KEYBOARD_QT1050 the module will be called qt1050 config KEYBOARD_QT1070 - tristate "Atmel AT42QT1070 Touch Sensor Chip" - depends on I2C - help - Say Y here if you want to use Atmel AT42QT1070 QTouch - Sensor chip as input device. + tristate "Atmel AT42QT1070 Touch Sensor Chip" + depends on I2C + help + Say Y here if you want to use Atmel AT42QT1070 QTouch + Sensor chip as input device. - To compile this driver as a module, choose M here: - the module will be called qt1070 + To compile this driver as a module, choose M here: + the module will be called qt1070 config KEYBOARD_QT2160 tristate "Atmel AT42QT2160 Touch Sensor Chip" @@ -191,7 +190,6 @@ config KEYBOARD_CLPS711X tristate "CLPS711X Keypad support" depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST) select INPUT_MATRIXKMAP - select INPUT_POLLDEV help Say Y here to enable the matrix keypad on the Cirrus Logic CLPS711X CPUs. @@ -250,7 +248,6 @@ config KEYBOARD_GPIO config KEYBOARD_GPIO_POLLED tristate "Polled GPIO buttons" depends on GPIOLIB - select INPUT_POLLDEV help This driver implements support for buttons connected to GPIO pins that are not capable of generating interrupts. @@ -342,7 +339,6 @@ config KEYBOARD_HIL config KEYBOARD_HP6XX tristate "HP Jornada 6xx keyboard" depends on SH_HP6XX - select INPUT_POLLDEV help Say Y here if you have a HP Jornada 620/660/680/690 and want to support the built-in keyboard. @@ -469,6 +465,16 @@ config KEYBOARD_IMX To compile this driver as a module, choose M here: the module will be called imx_keypad. +config KEYBOARD_IMX_SC_KEY + tristate "IMX SCU Key Driver" + depends on IMX_SCU + help + This is the system controller key driver for NXP i.MX SoCs with + system controller inside. + + To compile this driver as a module, choose M here: the + module will be called imx_sc_key. + config KEYBOARD_NEWTON tristate "Newton keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 9510325c0c5d..f5b17524adf2 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o +obj-$(CONFIG_KEYBOARD_IMX_SC_KEY) += imx_sc_key.o obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o diff --git a/drivers/input/keyboard/adc-keys.c b/drivers/input/keyboard/adc-keys.c index 9885fd56f5f9..6d5be48d1b3d 100644 --- a/drivers/input/keyboard/adc-keys.c +++ b/drivers/input/keyboard/adc-keys.c @@ -9,7 +9,6 @@ #include <linux/iio/consumer.h> #include <linux/iio/types.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -30,9 +29,9 @@ struct adc_keys_state { const struct adc_keys_button *map; }; -static void adc_keys_poll(struct input_polled_dev *dev) +static void adc_keys_poll(struct input_dev *input) { - struct adc_keys_state *st = dev->private; + struct adc_keys_state *st = input_get_drvdata(input); int i, value, ret; u32 diff, closest = 0xffffffff; int keycode = 0; @@ -55,12 +54,12 @@ static void adc_keys_poll(struct input_polled_dev *dev) keycode = 0; if (st->last_key && st->last_key != keycode) - input_report_key(dev->input, st->last_key, 0); + input_report_key(input, st->last_key, 0); if (keycode) - input_report_key(dev->input, keycode, 1); + input_report_key(input, keycode, 1); - input_sync(dev->input); + input_sync(input); st->last_key = keycode; } @@ -108,7 +107,6 @@ static int adc_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct adc_keys_state *st; - struct input_polled_dev *poll_dev; struct input_dev *input; enum iio_chan_type type; int i, value; @@ -145,19 +143,13 @@ static int adc_keys_probe(struct platform_device *pdev) if (error) return error; - poll_dev = devm_input_allocate_polled_device(dev); - if (!poll_dev) { + input = devm_input_allocate_device(dev); + if (!input) { dev_err(dev, "failed to allocate input device\n"); return -ENOMEM; } - if (!device_property_read_u32(dev, "poll-interval", &value)) - poll_dev->poll_interval = value; - - poll_dev->poll = adc_keys_poll; - poll_dev->private = st; - - input = poll_dev->input; + input_set_drvdata(input, st); input->name = pdev->name; input->phys = "adc-keys/input0"; @@ -174,7 +166,17 @@ static int adc_keys_probe(struct platform_device *pdev) if (device_property_read_bool(dev, "autorepeat")) __set_bit(EV_REP, input->evbit); - error = input_register_polled_device(poll_dev); + + error = input_setup_polling(input, adc_keys_poll); + if (error) { + dev_err(dev, "Unable to set up polling: %d\n", error); + return error; + } + + if (!device_property_read_u32(dev, "poll-interval", &value)) + input_set_poll_interval(input, value); + + error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device: %d\n", error); return error; diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c index 4f96a4a99e5b..e7d58e7f0257 100644 --- a/drivers/input/keyboard/adp5589-keys.c +++ b/drivers/input/keyboard/adp5589-keys.c @@ -857,70 +857,35 @@ static void adp5589_report_switch_state(struct adp5589_kpad *kpad) input_sync(kpad->input); } -static int adp5589_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adp5589_keypad_add(struct adp5589_kpad *kpad, unsigned int revid) { - struct adp5589_kpad *kpad; + struct i2c_client *client = kpad->client; const struct adp5589_kpad_platform_data *pdata = dev_get_platdata(&client->dev); struct input_dev *input; - unsigned int revid; - int ret, i; + unsigned int i; int error; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); - return -EIO; - } - - if (!pdata) { - dev_err(&client->dev, "no platform data?\n"); - return -EINVAL; - } - - kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); - if (!kpad) - return -ENOMEM; - - switch (id->driver_data) { - case ADP5585_02: - kpad->support_row5 = true; - /* fall through */ - case ADP5585_01: - kpad->is_adp5585 = true; - kpad->var = &const_adp5585; - break; - case ADP5589: - kpad->support_row5 = true; - kpad->var = &const_adp5589; - break; - } - if (!((pdata->keypad_en_mask & kpad->var->row_mask) && (pdata->keypad_en_mask >> kpad->var->col_shift)) || !pdata->keymap) { dev_err(&client->dev, "no rows, cols or keymap from pdata\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } if (pdata->keymapsize != kpad->var->keymapsize) { dev_err(&client->dev, "invalid keymapsize\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } if (!pdata->gpimap && pdata->gpimapsize) { dev_err(&client->dev, "invalid gpimap from pdata\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } if (pdata->gpimapsize > kpad->var->gpimapsize_max) { dev_err(&client->dev, "invalid gpimapsize\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } for (i = 0; i < pdata->gpimapsize; i++) { @@ -929,41 +894,27 @@ static int adp5589_probe(struct i2c_client *client, if (pin < kpad->var->gpi_pin_base || pin > kpad->var->gpi_pin_end) { dev_err(&client->dev, "invalid gpi pin data\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } if ((1 << (pin - kpad->var->gpi_pin_row_base)) & pdata->keypad_en_mask) { dev_err(&client->dev, "invalid gpi row/col data\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } } if (!client->irq) { dev_err(&client->dev, "no IRQ?\n"); - error = -EINVAL; - goto err_free_mem; + return -EINVAL; } input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_mem; - } + if (!input) + return -ENOMEM; - kpad->client = client; kpad->input = input; - ret = adp5589_read(client, ADP5589_5_ID); - if (ret < 0) { - error = ret; - goto err_free_input; - } - - revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK; - input->name = client->name; input->phys = "adp5589-keys/input0"; input->dev.parent = &client->dev; @@ -1015,30 +966,99 @@ static int adp5589_probe(struct i2c_client *client, goto err_unreg_dev; } + device_init_wakeup(&client->dev, 1); + + return 0; + +err_unreg_dev: + input_unregister_device(input); + input = NULL; +err_free_input: + input_free_device(input); + + return error; +} + +static void adp5589_keypad_remove(struct adp5589_kpad *kpad) +{ + if (kpad->input) { + free_irq(kpad->client->irq, kpad); + input_unregister_device(kpad->input); + } +} + +static int adp5589_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adp5589_kpad *kpad; + const struct adp5589_kpad_platform_data *pdata = + dev_get_platdata(&client->dev); + unsigned int revid; + int error, ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + if (!pdata) { + dev_err(&client->dev, "no platform data?\n"); + return -EINVAL; + } + + kpad = kzalloc(sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + kpad->client = client; + + switch (id->driver_data) { + case ADP5585_02: + kpad->support_row5 = true; + /* fall through */ + case ADP5585_01: + kpad->is_adp5585 = true; + kpad->var = &const_adp5585; + break; + case ADP5589: + kpad->support_row5 = true; + kpad->var = &const_adp5589; + break; + } + + ret = adp5589_read(client, ADP5589_5_ID); + if (ret < 0) { + error = ret; + goto err_free_mem; + } + + revid = (u8) ret & ADP5589_5_DEVICE_ID_MASK; + + if (pdata->keymapsize) { + error = adp5589_keypad_add(kpad, revid); + if (error) + goto err_free_mem; + } + error = adp5589_setup(kpad); if (error) - goto err_free_irq; + goto err_keypad_remove; if (kpad->gpimapsize) adp5589_report_switch_state(kpad); error = adp5589_gpio_add(kpad); if (error) - goto err_free_irq; + goto err_keypad_remove; - device_init_wakeup(&client->dev, 1); i2c_set_clientdata(client, kpad); dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq); return 0; -err_free_irq: - free_irq(client->irq, kpad); -err_unreg_dev: - input_unregister_device(input); - input = NULL; -err_free_input: - input_free_device(input); +err_keypad_remove: + adp5589_keypad_remove(kpad); err_free_mem: kfree(kpad); @@ -1050,8 +1070,7 @@ static int adp5589_remove(struct i2c_client *client) struct adp5589_kpad *kpad = i2c_get_clientdata(client); adp5589_write(client, kpad->var->reg(ADP5589_GENERAL_CFG), 0); - free_irq(client->irq, kpad); - input_unregister_device(kpad->input); + adp5589_keypad_remove(kpad); adp5589_gpio_remove(kpad); kfree(kpad); @@ -1064,6 +1083,9 @@ static int adp5589_suspend(struct device *dev) struct adp5589_kpad *kpad = dev_get_drvdata(dev); struct i2c_client *client = kpad->client; + if (!kpad->input) + return 0; + disable_irq(client->irq); if (device_may_wakeup(&client->dev)) @@ -1077,6 +1099,9 @@ static int adp5589_resume(struct device *dev) struct adp5589_kpad *kpad = dev_get_drvdata(dev); struct i2c_client *client = kpad->client; + if (!kpad->input) + return 0; + if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c index c4a5c07a4b98..019dd6ed2c29 100644 --- a/drivers/input/keyboard/clps711x-keypad.c +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -6,7 +6,6 @@ */ #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> @@ -30,10 +29,10 @@ struct clps711x_keypad_data { struct clps711x_gpio_data *gpio_data; }; -static void clps711x_keypad_poll(struct input_polled_dev *dev) +static void clps711x_keypad_poll(struct input_dev *input) { - const unsigned short *keycodes = dev->input->keycode; - struct clps711x_keypad_data *priv = dev->private; + const unsigned short *keycodes = input->keycode; + struct clps711x_keypad_data *priv = input_get_drvdata(input); bool sync = false; int col, row; @@ -61,14 +60,14 @@ static void clps711x_keypad_poll(struct input_polled_dev *dev) if (state) { set_bit(col, data->last_state); - input_event(dev->input, EV_MSC, - MSC_SCAN, code); + input_event(input, + EV_MSC, MSC_SCAN, code); } else { clear_bit(col, data->last_state); } if (keycodes[code]) - input_report_key(dev->input, + input_report_key(input, keycodes[code], state); sync = true; } @@ -80,7 +79,7 @@ static void clps711x_keypad_poll(struct input_polled_dev *dev) } if (sync) - input_sync(dev->input); + input_sync(input); } static int clps711x_keypad_probe(struct platform_device *pdev) @@ -88,7 +87,7 @@ static int clps711x_keypad_probe(struct platform_device *pdev) struct clps711x_keypad_data *priv; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct input_polled_dev *poll_dev; + struct input_dev *input; u32 poll_interval; int i, err; @@ -125,53 +124,43 @@ static int clps711x_keypad_probe(struct platform_device *pdev) if (err) return err; - poll_dev = input_allocate_polled_device(); - if (!poll_dev) + input = devm_input_allocate_device(dev); + if (!input) return -ENOMEM; - poll_dev->private = priv; - poll_dev->poll = clps711x_keypad_poll; - poll_dev->poll_interval = poll_interval; - poll_dev->input->name = pdev->name; - poll_dev->input->dev.parent = dev; - poll_dev->input->id.bustype = BUS_HOST; - poll_dev->input->id.vendor = 0x0001; - poll_dev->input->id.product = 0x0001; - poll_dev->input->id.version = 0x0100; + input_set_drvdata(input, priv); + + input->name = pdev->name; + input->dev.parent = dev; + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count, CLPS711X_KEYPAD_COL_COUNT, - NULL, poll_dev->input); + NULL, input); if (err) - goto out_err; + return err; - input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN); + input_set_capability(input, EV_MSC, MSC_SCAN); if (of_property_read_bool(np, "autorepeat")) - __set_bit(EV_REP, poll_dev->input->evbit); - - platform_set_drvdata(pdev, poll_dev); + __set_bit(EV_REP, input->evbit); /* Set all columns to low */ regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1)); - err = input_register_polled_device(poll_dev); - if (err) - goto out_err; - - return 0; -out_err: - input_free_polled_device(poll_dev); - return err; -} + err = input_setup_polling(input, clps711x_keypad_poll); + if (err) + return err; -static int clps711x_keypad_remove(struct platform_device *pdev) -{ - struct input_polled_dev *poll_dev = platform_get_drvdata(pdev); + input_set_poll_interval(input, poll_interval); - input_unregister_polled_device(poll_dev); - input_free_polled_device(poll_dev); + err = input_register_device(input); + if (err) + return err; return 0; } @@ -188,7 +177,6 @@ static struct platform_driver clps711x_keypad_driver = { .of_match_table = clps711x_keypad_of_match, }, .probe = clps711x_keypad_probe, - .remove = clps711x_keypad_remove, }; module_platform_driver(clps711x_keypad_driver); diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 1373dc5b0765..1f56d53454b2 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -494,10 +494,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev, spin_lock_init(&bdata->lock); if (child) { - bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, - child, - GPIOD_IN, - desc); + bdata->gpiod = devm_fwnode_gpiod_get(dev, child, + NULL, GPIOD_IN, desc); if (IS_ERR(bdata->gpiod)) { error = PTR_ERR(bdata->gpiod); if (error == -ENOENT) { diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 465eecfa6b3f..6eb0a2f3f9de 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/gpio.h> @@ -34,7 +33,7 @@ struct gpio_keys_button_data { }; struct gpio_keys_polled_dev { - struct input_polled_dev *poll_dev; + struct input_dev *input; struct device *dev; const struct gpio_keys_platform_data *pdata; unsigned long rel_axis_seen[BITS_TO_LONGS(REL_CNT)]; @@ -42,12 +41,11 @@ struct gpio_keys_polled_dev { struct gpio_keys_button_data data[0]; }; -static void gpio_keys_button_event(struct input_polled_dev *dev, +static void gpio_keys_button_event(struct input_dev *input, const struct gpio_keys_button *button, int state) { - struct gpio_keys_polled_dev *bdev = dev->private; - struct input_dev *input = dev->input; + struct gpio_keys_polled_dev *bdev = input_get_drvdata(input); unsigned int type = button->type ?: EV_KEY; if (type == EV_REL) { @@ -66,7 +64,7 @@ static void gpio_keys_button_event(struct input_polled_dev *dev, } } -static void gpio_keys_polled_check_state(struct input_polled_dev *dev, +static void gpio_keys_polled_check_state(struct input_dev *input, const struct gpio_keys_button *button, struct gpio_keys_button_data *bdata) { @@ -74,10 +72,10 @@ static void gpio_keys_polled_check_state(struct input_polled_dev *dev, state = gpiod_get_value_cansleep(bdata->gpiod); if (state < 0) { - dev_err(dev->input->dev.parent, + dev_err(input->dev.parent, "failed to get gpio state: %d\n", state); } else { - gpio_keys_button_event(dev, button, state); + gpio_keys_button_event(input, button, state); if (state != bdata->last_state) { bdata->count = 0; @@ -86,11 +84,10 @@ static void gpio_keys_polled_check_state(struct input_polled_dev *dev, } } -static void gpio_keys_polled_poll(struct input_polled_dev *dev) +static void gpio_keys_polled_poll(struct input_dev *input) { - struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_polled_dev *bdev = input_get_drvdata(input); const struct gpio_keys_platform_data *pdata = bdev->pdata; - struct input_dev *input = dev->input; int i; memset(bdev->rel_axis_seen, 0, sizeof(bdev->rel_axis_seen)); @@ -101,10 +98,10 @@ static void gpio_keys_polled_poll(struct input_polled_dev *dev) if (bdata->count < bdata->threshold) { bdata->count++; - gpio_keys_button_event(dev, &pdata->buttons[i], + gpio_keys_button_event(input, &pdata->buttons[i], bdata->last_state); } else { - gpio_keys_polled_check_state(dev, &pdata->buttons[i], + gpio_keys_polled_check_state(input, &pdata->buttons[i], bdata); } } @@ -122,18 +119,20 @@ static void gpio_keys_polled_poll(struct input_polled_dev *dev) input_sync(input); } -static void gpio_keys_polled_open(struct input_polled_dev *dev) +static int gpio_keys_polled_open(struct input_dev *input) { - struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_polled_dev *bdev = input_get_drvdata(input); const struct gpio_keys_platform_data *pdata = bdev->pdata; if (pdata->enable) pdata->enable(bdev->dev); + + return 0; } -static void gpio_keys_polled_close(struct input_polled_dev *dev) +static void gpio_keys_polled_close(struct input_dev *input) { - struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_polled_dev *bdev = input_get_drvdata(input); const struct gpio_keys_platform_data *pdata = bdev->pdata; if (pdata->disable) @@ -232,7 +231,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) struct fwnode_handle *child = NULL; const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev); struct gpio_keys_polled_dev *bdev; - struct input_polled_dev *poll_dev; struct input_dev *input; int error; int i; @@ -255,19 +253,13 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) return -ENOMEM; } - poll_dev = devm_input_allocate_polled_device(dev); - if (!poll_dev) { - dev_err(dev, "no memory for polled device\n"); + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "no memory for input device\n"); return -ENOMEM; } - poll_dev->private = bdev; - poll_dev->poll = gpio_keys_polled_poll; - poll_dev->poll_interval = pdata->poll_interval; - poll_dev->open = gpio_keys_polled_open; - poll_dev->close = gpio_keys_polled_close; - - input = poll_dev->input; + input_set_drvdata(input, bdev); input->name = pdata->name ?: pdev->name; input->phys = DRV_NAME"/input0"; @@ -277,6 +269,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + input->open = gpio_keys_polled_open; + input->close = gpio_keys_polled_close; + __set_bit(EV_KEY, input->evbit); if (pdata->rep) __set_bit(EV_REP, input->evbit); @@ -300,10 +295,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) return -EINVAL; } - bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev, - NULL, child, - GPIOD_IN, - button->desc); + bdata->gpiod = devm_fwnode_gpiod_get(dev, child, + NULL, GPIOD_IN, + button->desc); if (IS_ERR(bdata->gpiod)) { error = PTR_ERR(bdata->gpiod); if (error != -EPROBE_DEFER) @@ -353,11 +347,19 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) fwnode_handle_put(child); - bdev->poll_dev = poll_dev; + bdev->input = input; bdev->dev = dev; bdev->pdata = pdata; - error = input_register_polled_device(poll_dev); + error = input_setup_polling(input, gpio_keys_polled_poll); + if (error) { + dev_err(dev, "unable to set up polling, err=%d\n", error); + return error; + } + + input_set_poll_interval(input, pdata->poll_interval); + + error = input_register_device(input); if (error) { dev_err(dev, "unable to register polled device, err=%d\n", error); @@ -366,7 +368,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) /* report initial state of the buttons */ for (i = 0; i < pdata->nbuttons; i++) - gpio_keys_polled_check_state(poll_dev, &pdata->buttons[i], + gpio_keys_polled_check_state(input, &pdata->buttons[i], &bdev->data[i]); input_sync(input); diff --git a/drivers/input/keyboard/imx_sc_key.c b/drivers/input/keyboard/imx_sc_key.c new file mode 100644 index 000000000000..53799527dc75 --- /dev/null +++ b/drivers/input/keyboard/imx_sc_key.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/err.h> +#include <linux/device.h> +#include <linux/firmware/imx/sci.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +#define DEBOUNCE_TIME 30 +#define REPEAT_INTERVAL 60 + +#define SC_IRQ_BUTTON 1 +#define SC_IRQ_GROUP_WAKE 3 + +#define IMX_SC_MISC_FUNC_GET_BUTTON_STATUS 18 + +struct imx_key_drv_data { + u32 keycode; + bool keystate; /* true: pressed, false: released */ + struct delayed_work check_work; + struct input_dev *input; + struct imx_sc_ipc *key_ipc_handle; + struct notifier_block key_notifier; +}; + +struct imx_sc_msg_key { + struct imx_sc_rpc_msg hdr; + u32 state; +}; + +static int imx_sc_key_notify(struct notifier_block *nb, + unsigned long event, void *group) +{ + struct imx_key_drv_data *priv = + container_of(nb, + struct imx_key_drv_data, + key_notifier); + + if ((event & SC_IRQ_BUTTON) && (*(u8 *)group == SC_IRQ_GROUP_WAKE)) { + schedule_delayed_work(&priv->check_work, + msecs_to_jiffies(DEBOUNCE_TIME)); + pm_wakeup_event(priv->input->dev.parent, 0); + } + + return 0; +} + +static void imx_sc_check_for_events(struct work_struct *work) +{ + struct imx_key_drv_data *priv = + container_of(work, + struct imx_key_drv_data, + check_work.work); + struct input_dev *input = priv->input; + struct imx_sc_msg_key msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + bool state; + int error; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_MISC; + hdr->func = IMX_SC_MISC_FUNC_GET_BUTTON_STATUS; + hdr->size = 1; + + error = imx_scu_call_rpc(priv->key_ipc_handle, &msg, true); + if (error) { + dev_err(&input->dev, "read imx sc key failed, error %d\n", error); + return; + } + + state = (bool)msg.state; + + if (state ^ priv->keystate) { + priv->keystate = state; + input_event(input, EV_KEY, priv->keycode, state); + input_sync(input); + if (!priv->keystate) + pm_relax(priv->input->dev.parent); + } + + if (state) + schedule_delayed_work(&priv->check_work, + msecs_to_jiffies(REPEAT_INTERVAL)); +} + +static int imx_sc_key_probe(struct platform_device *pdev) +{ + struct imx_key_drv_data *priv; + struct input_dev *input; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + error = imx_scu_get_handle(&priv->key_ipc_handle); + if (error) + return error; + + if (device_property_read_u32(&pdev->dev, "linux,keycodes", + &priv->keycode)) { + dev_err(&pdev->dev, "missing linux,keycodes property\n"); + return -EINVAL; + } + + INIT_DELAYED_WORK(&priv->check_work, imx_sc_check_for_events); + + input = devm_input_allocate_device(&pdev->dev); + if (!input) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + input->name = pdev->name; + input->phys = "imx-sc-key/input0"; + input->id.bustype = BUS_HOST; + + input_set_capability(input, EV_KEY, priv->keycode); + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + priv->input = input; + platform_set_drvdata(pdev, priv); + + error = imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, + true); + if (error) { + dev_err(&pdev->dev, "failed to enable scu group irq\n"); + return error; + } + + priv->key_notifier.notifier_call = imx_sc_key_notify; + error = imx_scu_irq_register_notifier(&priv->key_notifier); + if (error) { + imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, + false); + dev_err(&pdev->dev, "failed to register scu notifier\n"); + return error; + } + + return 0; +} + +static int imx_sc_key_remove(struct platform_device *pdev) +{ + struct imx_key_drv_data *priv = platform_get_drvdata(pdev); + + imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, false); + imx_scu_irq_unregister_notifier(&priv->key_notifier); + cancel_delayed_work_sync(&priv->check_work); + + return 0; +} + +static const struct of_device_id imx_sc_key_ids[] = { + { .compatible = "fsl,imx-sc-key" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_sc_key_ids); + +static struct platform_driver imx_sc_key_driver = { + .driver = { + .name = "imx-sc-key", + .of_match_table = imx_sc_key_ids, + }, + .probe = imx_sc_key_probe, + .remove = imx_sc_key_remove, +}; +module_platform_driver(imx_sc_key_driver); + +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>"); +MODULE_DESCRIPTION("i.MX System Controller Key Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c index 4232aa876d2e..7e35081393be 100644 --- a/drivers/input/keyboard/jornada680_kbd.c +++ b/drivers/input/keyboard/jornada680_kbd.c @@ -15,7 +15,6 @@ #include <linux/device.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/kernel.h> @@ -64,7 +63,7 @@ static const unsigned short jornada_scancodes[] = { #define JORNADA_SCAN_SIZE 18 struct jornadakbd { - struct input_polled_dev *poll_dev; + struct input_dev *input; unsigned short keymap[ARRAY_SIZE(jornada_scancodes)]; unsigned char length; unsigned char old_scan[JORNADA_SCAN_SIZE]; @@ -73,7 +72,7 @@ struct jornadakbd { static void jornada_parse_kbd(struct jornadakbd *jornadakbd) { - struct input_dev *input_dev = jornadakbd->poll_dev->input; + struct input_dev *input_dev = jornadakbd->input; unsigned short *keymap = jornadakbd->keymap; unsigned int sync_me = 0; unsigned int i, j; @@ -167,9 +166,9 @@ static void jornada_scan_keyb(unsigned char *s) *s++ = __raw_readb(PHDR); } -static void jornadakbd680_poll(struct input_polled_dev *dev) +static void jornadakbd680_poll(struct input_dev *input) { - struct jornadakbd *jornadakbd = dev->private; + struct jornadakbd *jornadakbd = input_get_drvdata(input); jornada_scan_keyb(jornadakbd->new_scan); jornada_parse_kbd(jornadakbd); @@ -179,7 +178,6 @@ static void jornadakbd680_poll(struct input_polled_dev *dev) static int jornada680kbd_probe(struct platform_device *pdev) { struct jornadakbd *jornadakbd; - struct input_polled_dev *poll_dev; struct input_dev *input_dev; int i, error; @@ -188,29 +186,24 @@ static int jornada680kbd_probe(struct platform_device *pdev) if (!jornadakbd) return -ENOMEM; - poll_dev = devm_input_allocate_polled_device(&pdev->dev); - if (!poll_dev) { - dev_err(&pdev->dev, "failed to allocate polled input device\n"); + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); return -ENOMEM; } - jornadakbd->poll_dev = poll_dev; + jornadakbd->input = input_dev; memcpy(jornadakbd->keymap, jornada_scancodes, sizeof(jornadakbd->keymap)); - poll_dev->private = jornadakbd; - poll_dev->poll = jornadakbd680_poll; - poll_dev->poll_interval = 50; /* msec */ - - input_dev = poll_dev->input; + input_set_drvdata(input_dev, jornadakbd); input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); input_dev->name = "HP Jornada 680 keyboard"; input_dev->phys = "jornadakbd/input0"; input_dev->keycode = jornadakbd->keymap; input_dev->keycodesize = sizeof(unsigned short); input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes); - input_dev->dev.parent = &pdev->dev; input_dev->id.bustype = BUS_HOST; for (i = 0; i < 128; i++) @@ -220,9 +213,17 @@ static int jornada680kbd_probe(struct platform_device *pdev) input_set_capability(input_dev, EV_MSC, MSC_SCAN); - error = input_register_polled_device(jornadakbd->poll_dev); + error = input_setup_polling(input_dev, jornadakbd680_poll); + if (error) { + dev_err(&pdev->dev, "failed to set up polling\n"); + return error; + } + + input_set_poll_interval(input_dev, 50 /* msec */); + + error = input_register_device(input_dev); if (error) { - dev_err(&pdev->dev, "failed to register polled input device\n"); + dev_err(&pdev->dev, "failed to register input device\n"); return error; } diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c index ee80de44ce3f..40d6e5087cde 100644 --- a/drivers/input/keyboard/mpr121_touchkey.c +++ b/drivers/input/keyboard/mpr121_touchkey.c @@ -54,6 +54,9 @@ /* MPR121 has 12 keys */ #define MPR121_MAX_KEY_COUNT 12 +#define MPR121_MIN_POLL_INTERVAL 10 +#define MPR121_MAX_POLL_INTERVAL 200 + struct mpr121_touchkey { struct i2c_client *client; struct input_dev *input_dev; @@ -115,11 +118,11 @@ static struct regulator *mpr121_vdd_supply_init(struct device *dev) return vdd_supply; } -static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +static void mpr_touchkey_report(struct input_dev *dev) { - struct mpr121_touchkey *mpr121 = dev_id; - struct i2c_client *client = mpr121->client; + struct mpr121_touchkey *mpr121 = input_get_drvdata(dev); struct input_dev *input = mpr121->input_dev; + struct i2c_client *client = mpr121->client; unsigned long bit_changed; unsigned int key_num; int reg; @@ -127,14 +130,14 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); if (reg < 0) { dev_err(&client->dev, "i2c read error [%d]\n", reg); - goto out; + return; } reg <<= 8; reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); if (reg < 0) { dev_err(&client->dev, "i2c read error [%d]\n", reg); - goto out; + return; } reg &= TOUCH_STATUS_MASK; @@ -155,8 +158,14 @@ static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) } input_sync(input); +} + +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +{ + struct mpr121_touchkey *mpr121 = dev_id; + + mpr_touchkey_report(mpr121->input_dev); -out: return IRQ_HANDLED; } @@ -229,14 +238,10 @@ static int mpr_touchkey_probe(struct i2c_client *client, int vdd_uv; struct mpr121_touchkey *mpr121; struct input_dev *input_dev; + u32 poll_interval = 0; int error; int i; - if (!client->irq) { - dev_err(dev, "irq number should not be zero\n"); - return -EINVAL; - } - vdd_supply = mpr121_vdd_supply_init(dev); if (IS_ERR(vdd_supply)) return PTR_ERR(vdd_supply); @@ -274,6 +279,7 @@ static int mpr_touchkey_probe(struct i2c_client *client, if (device_property_read_bool(dev, "autorepeat")) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, mpr121); input_dev->keycode = mpr121->keycodes; input_dev->keycodesize = sizeof(mpr121->keycodes[0]); @@ -288,13 +294,40 @@ static int mpr_touchkey_probe(struct i2c_client *client, return error; } - error = devm_request_threaded_irq(dev, client->irq, NULL, - mpr_touchkey_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev->driver->name, mpr121); - if (error) { - dev_err(dev, "Failed to register interrupt\n"); - return error; + device_property_read_u32(dev, "poll-interval", &poll_interval); + + if (client->irq) { + error = devm_request_threaded_irq(dev, client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev->driver->name, mpr121); + if (error) { + dev_err(dev, "Failed to register interrupt\n"); + return error; + } + } else if (poll_interval) { + if (poll_interval < MPR121_MIN_POLL_INTERVAL) + return -EINVAL; + + if (poll_interval > MPR121_MAX_POLL_INTERVAL) + return -EINVAL; + + error = input_setup_polling(input_dev, mpr_touchkey_report); + if (error) { + dev_err(dev, "Failed to setup polling\n"); + return error; + } + + input_set_poll_interval(input_dev, poll_interval); + input_set_min_poll_interval(input_dev, + MPR121_MIN_POLL_INTERVAL); + input_set_max_poll_interval(input_dev, + MPR121_MAX_POLL_INTERVAL); + } else { + dev_err(dev, + "invalid IRQ number and polling not configured\n"); + return -EINVAL; } error = input_register_device(input_dev); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7d9ae394e597..7e2e658d551c 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -100,7 +100,6 @@ config INPUT_ATMEL_CAPTOUCH config INPUT_BMA150 tristate "BMA150/SMB380 acceleration sensor support" depends on I2C - select INPUT_POLLDEV help Say Y here if you have Bosch Sensortec's BMA150 or SMB380 acceleration sensor hooked to an I2C bus. @@ -246,7 +245,6 @@ config INPUT_MC13783_PWRBUTTON config INPUT_MMA8450 tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer" depends on I2C - select INPUT_POLLDEV help Say Y here if you want to support Freescale's MMA8450 Accelerometer through I2C interface. @@ -257,7 +255,6 @@ config INPUT_MMA8450 config INPUT_APANEL tristate "Fujitsu Lifebook Application Panel buttons" depends on X86 && I2C && LEDS_CLASS - select INPUT_POLLDEV select CHECK_SIGNATURE help Say Y here for support of the Application Panel buttons, used on @@ -291,7 +288,6 @@ config INPUT_GPIO_BEEPER config INPUT_GPIO_DECODER tristate "Polled GPIO Decoder Input driver" depends on GPIOLIB || COMPILE_TEST - select INPUT_POLLDEV help Say Y here if you want driver to read status of multiple GPIO lines and report the encoded value as an absolute integer to @@ -327,7 +323,6 @@ config INPUT_IXP4XX_BEEPER config INPUT_COBALT_BTNS tristate "Cobalt button interface" depends on MIPS_COBALT - select INPUT_POLLDEV help Say Y here if you want to support MIPS Cobalt button interface. @@ -347,7 +342,6 @@ config INPUT_CPCAP_PWRBUTTON config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" depends on X86_32 - select INPUT_POLLDEV select INPUT_SPARSEKMAP select NEW_LEDS select LEDS_CLASS @@ -410,13 +404,6 @@ config INPUT_KXTJ9 To compile this driver as a module, choose M here: the module will be called kxtj9. -config INPUT_KXTJ9_POLLED_MODE - bool "Enable polling mode support" - depends on INPUT_KXTJ9 - select INPUT_POLLDEV - help - Say Y here if you need accelerometer to work in polling mode. - config INPUT_POWERMATE tristate "Griffin PowerMate and Contour Jog support" depends on USB_ARCH_HAS_HCD @@ -546,7 +533,6 @@ config INPUT_UINPUT config INPUT_SGI_BTNS tristate "SGI Indy/O2 volume button interface" depends on SGI_IP22 || SGI_IP32 - select INPUT_POLLDEV help Say Y here if you want to support SGI Indy/O2 volume button interface. @@ -637,7 +623,6 @@ config INPUT_RB532_BUTTON tristate "Mikrotik Routerboard 532 button interface" depends on MIKROTIK_RB532 depends on GPIOLIB - select INPUT_POLLDEV help Say Y here if you want support for the S1 button built into Mikrotik's Routerboard 532. diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c index 53ec40d1b90d..7276657ad7ca 100644 --- a/drivers/input/misc/apanel.c +++ b/drivers/input/misc/apanel.c @@ -17,7 +17,7 @@ #include <linux/module.h> #include <linux/ioport.h> #include <linux/io.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/i2c.h> #include <linux/leds.h> @@ -51,19 +51,28 @@ static enum apanel_chip device_chip[APANEL_DEV_MAX]; #define MAX_PANEL_KEYS 12 struct apanel { - struct input_polled_dev *ipdev; + struct input_dev *idev; struct i2c_client *client; unsigned short keymap[MAX_PANEL_KEYS]; - u16 nkeys; + u16 nkeys; struct led_classdev mail_led; }; +static const unsigned short apanel_keymap[MAX_PANEL_KEYS] = { + [0] = KEY_MAIL, + [1] = KEY_WWW, + [2] = KEY_PROG2, + [3] = KEY_PROG1, -static int apanel_probe(struct i2c_client *, const struct i2c_device_id *); + [8] = KEY_FORWARD, + [9] = KEY_REWIND, + [10] = KEY_STOPCD, + [11] = KEY_PLAYPAUSE, +}; static void report_key(struct input_dev *input, unsigned keycode) { - pr_debug(APANEL ": report key %#x\n", keycode); + dev_dbg(input->dev.parent, "report key %#x\n", keycode); input_report_key(input, keycode, 1); input_sync(input); @@ -79,10 +88,9 @@ static void report_key(struct input_dev *input, unsigned keycode) * CD keys: * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800) */ -static void apanel_poll(struct input_polled_dev *ipdev) +static void apanel_poll(struct input_dev *idev) { - struct apanel *ap = ipdev->private; - struct input_dev *idev = ipdev->input; + struct apanel *ap = input_get_drvdata(idev); u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; s32 data; int i; @@ -112,126 +120,93 @@ static int mail_led_set(struct led_classdev *led, return i2c_smbus_write_word_data(ap->client, 0x10, led_bits); } -static int apanel_remove(struct i2c_client *client) -{ - struct apanel *ap = i2c_get_clientdata(client); - - if (device_chip[APANEL_DEV_LED] != CHIP_NONE) - led_classdev_unregister(&ap->mail_led); - - input_unregister_polled_device(ap->ipdev); - input_free_polled_device(ap->ipdev); - - return 0; -} - -static void apanel_shutdown(struct i2c_client *client) -{ - apanel_remove(client); -} - -static const struct i2c_device_id apanel_id[] = { - { "fujitsu_apanel", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, apanel_id); - -static struct i2c_driver apanel_driver = { - .driver = { - .name = APANEL, - }, - .probe = &apanel_probe, - .remove = &apanel_remove, - .shutdown = &apanel_shutdown, - .id_table = apanel_id, -}; - -static struct apanel apanel = { - .keymap = { - [0] = KEY_MAIL, - [1] = KEY_WWW, - [2] = KEY_PROG2, - [3] = KEY_PROG1, - - [8] = KEY_FORWARD, - [9] = KEY_REWIND, - [10] = KEY_STOPCD, - [11] = KEY_PLAYPAUSE, - - }, - .mail_led = { - .name = "mail:blue", - .brightness_set_blocking = mail_led_set, - }, -}; - -/* NB: Only one panel on the i2c. */ static int apanel_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct apanel *ap; - struct input_polled_dev *ipdev; struct input_dev *idev; u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; - int i, err = -ENOMEM; + int i, err; - ap = &apanel; + ap = devm_kzalloc(&client->dev, sizeof(*ap), GFP_KERNEL); + if (!ap) + return -ENOMEM; - ipdev = input_allocate_polled_device(); - if (!ipdev) - goto out1; + idev = devm_input_allocate_device(&client->dev); + if (!idev) + return -ENOMEM; - ap->ipdev = ipdev; + ap->idev = idev; ap->client = client; i2c_set_clientdata(client, ap); err = i2c_smbus_write_word_data(client, cmd, 0); if (err) { - dev_warn(&client->dev, APANEL ": smbus write error %d\n", - err); - goto out3; + dev_warn(&client->dev, "smbus write error %d\n", err); + return err; } - ipdev->poll = apanel_poll; - ipdev->poll_interval = POLL_INTERVAL_DEFAULT; - ipdev->private = ap; + input_set_drvdata(idev, ap); - idev = ipdev->input; idev->name = APANEL_NAME " buttons"; idev->phys = "apanel/input0"; idev->id.bustype = BUS_HOST; - idev->dev.parent = &client->dev; - - set_bit(EV_KEY, idev->evbit); + memcpy(ap->keymap, apanel_keymap, sizeof(apanel_keymap)); idev->keycode = ap->keymap; idev->keycodesize = sizeof(ap->keymap[0]); idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4; + set_bit(EV_KEY, idev->evbit); for (i = 0; i < idev->keycodemax; i++) if (ap->keymap[i]) set_bit(ap->keymap[i], idev->keybit); - err = input_register_polled_device(ipdev); + err = input_setup_polling(idev, apanel_poll); if (err) - goto out3; + return err; + + input_set_poll_interval(idev, POLL_INTERVAL_DEFAULT); + + err = input_register_device(idev); + if (err) + return err; if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { - err = led_classdev_register(&client->dev, &ap->mail_led); + ap->mail_led.name = "mail:blue"; + ap->mail_led.brightness_set_blocking = mail_led_set; + err = devm_led_classdev_register(&client->dev, &ap->mail_led); if (err) - goto out4; + return err; } return 0; -out4: - input_unregister_polled_device(ipdev); -out3: - input_free_polled_device(ipdev); -out1: - return err; } +static void apanel_shutdown(struct i2c_client *client) +{ + struct apanel *ap = i2c_get_clientdata(client); + + if (device_chip[APANEL_DEV_LED] != CHIP_NONE) + led_set_brightness(&ap->mail_led, LED_OFF); +} + +static const struct i2c_device_id apanel_id[] = { + { "fujitsu_apanel", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, apanel_id); + +static struct i2c_driver apanel_driver = { + .driver = { + .name = APANEL, + }, + .probe = apanel_probe, + .shutdown = apanel_shutdown, + .id_table = apanel_id, +}; + /* Scan the system ROM for the signature "FJKEYINF" */ static __init const void __iomem *bios_signature(const void __iomem *bios) { diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 735d3a46f44b..a9d984da95f3 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/slab.h> @@ -123,7 +122,6 @@ struct bma150_data { struct i2c_client *client; - struct input_polled_dev *input_polled; struct input_dev *input; u8 mode; }; @@ -336,13 +334,16 @@ static irqreturn_t bma150_irq_thread(int irq, void *dev) return IRQ_HANDLED; } -static void bma150_poll(struct input_polled_dev *dev) +static void bma150_poll(struct input_dev *input) { - bma150_report_xyz(dev->private); + struct bma150_data *bma150 = input_get_drvdata(input); + + bma150_report_xyz(bma150); } -static int bma150_open(struct bma150_data *bma150) +static int bma150_open(struct input_dev *input) { + struct bma150_data *bma150 = input_get_drvdata(input); int error; error = pm_runtime_get_sync(&bma150->client->dev); @@ -362,44 +363,18 @@ static int bma150_open(struct bma150_data *bma150) return 0; } -static void bma150_close(struct bma150_data *bma150) +static void bma150_close(struct input_dev *input) { + struct bma150_data *bma150 = input_get_drvdata(input); + pm_runtime_put_sync(&bma150->client->dev); if (bma150->mode != BMA150_MODE_SLEEP) bma150_set_mode(bma150, BMA150_MODE_SLEEP); } -static int bma150_irq_open(struct input_dev *input) -{ - struct bma150_data *bma150 = input_get_drvdata(input); - - return bma150_open(bma150); -} - -static void bma150_irq_close(struct input_dev *input) -{ - struct bma150_data *bma150 = input_get_drvdata(input); - - bma150_close(bma150); -} - -static void bma150_poll_open(struct input_polled_dev *ipoll_dev) -{ - struct bma150_data *bma150 = ipoll_dev->private; - - bma150_open(bma150); -} - -static void bma150_poll_close(struct input_polled_dev *ipoll_dev) -{ - struct bma150_data *bma150 = ipoll_dev->private; - - bma150_close(bma150); -} - static int bma150_initialize(struct bma150_data *bma150, - const struct bma150_cfg *cfg) + const struct bma150_cfg *cfg) { int error; @@ -439,84 +414,14 @@ static int bma150_initialize(struct bma150_data *bma150, return bma150_set_mode(bma150, BMA150_MODE_SLEEP); } -static void bma150_init_input_device(struct bma150_data *bma150, - struct input_dev *idev) -{ - idev->name = BMA150_DRIVER; - idev->phys = BMA150_DRIVER "/input0"; - idev->id.bustype = BUS_I2C; - idev->dev.parent = &bma150->client->dev; - - idev->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); - input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); - input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); -} - -static int bma150_register_input_device(struct bma150_data *bma150) -{ - struct input_dev *idev; - int error; - - idev = input_allocate_device(); - if (!idev) - return -ENOMEM; - - bma150_init_input_device(bma150, idev); - - idev->open = bma150_irq_open; - idev->close = bma150_irq_close; - input_set_drvdata(idev, bma150); - - bma150->input = idev; - - error = input_register_device(idev); - if (error) { - input_free_device(idev); - return error; - } - - return 0; -} - -static int bma150_register_polled_device(struct bma150_data *bma150) -{ - struct input_polled_dev *ipoll_dev; - int error; - - ipoll_dev = input_allocate_polled_device(); - if (!ipoll_dev) - return -ENOMEM; - - ipoll_dev->private = bma150; - ipoll_dev->open = bma150_poll_open; - ipoll_dev->close = bma150_poll_close; - ipoll_dev->poll = bma150_poll; - ipoll_dev->poll_interval = BMA150_POLL_INTERVAL; - ipoll_dev->poll_interval_min = BMA150_POLL_MIN; - ipoll_dev->poll_interval_max = BMA150_POLL_MAX; - - bma150_init_input_device(bma150, ipoll_dev->input); - - bma150->input_polled = ipoll_dev; - bma150->input = ipoll_dev->input; - - error = input_register_polled_device(ipoll_dev); - if (error) { - input_free_polled_device(ipoll_dev); - return error; - } - - return 0; -} - static int bma150_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { const struct bma150_platform_data *pdata = dev_get_platdata(&client->dev); const struct bma150_cfg *cfg; struct bma150_data *bma150; + struct input_dev *idev; int chip_id; int error; @@ -531,7 +436,7 @@ static int bma150_probe(struct i2c_client *client, return -EINVAL; } - bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL); + bma150 = devm_kzalloc(&client->dev, sizeof(*bma150), GFP_KERNEL); if (!bma150) return -ENOMEM; @@ -544,7 +449,7 @@ static int bma150_probe(struct i2c_client *client, dev_err(&client->dev, "IRQ GPIO conf. error %d, error %d\n", client->irq, error); - goto err_free_mem; + return error; } } cfg = &pdata->cfg; @@ -554,14 +459,42 @@ static int bma150_probe(struct i2c_client *client, error = bma150_initialize(bma150, cfg); if (error) - goto err_free_mem; + return error; - if (client->irq > 0) { - error = bma150_register_input_device(bma150); + idev = devm_input_allocate_device(&bma150->client->dev); + if (!idev) + return -ENOMEM; + + input_set_drvdata(idev, bma150); + bma150->input = idev; + + idev->name = BMA150_DRIVER; + idev->phys = BMA150_DRIVER "/input0"; + idev->id.bustype = BUS_I2C; + + idev->open = bma150_open; + idev->close = bma150_close; + + input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); + input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); + input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0); + + if (client->irq <= 0) { + error = input_setup_polling(idev, bma150_poll); if (error) - goto err_free_mem; + return error; + + input_set_poll_interval(idev, BMA150_POLL_INTERVAL); + input_set_min_poll_interval(idev, BMA150_POLL_MIN); + input_set_max_poll_interval(idev, BMA150_POLL_MAX); + } + + error = input_register_device(idev); + if (error) + return error; - error = request_threaded_irq(client->irq, + if (client->irq > 0) { + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, bma150_irq_thread, IRQF_TRIGGER_RISING | IRQF_ONESHOT, BMA150_DRIVER, bma150); @@ -569,13 +502,8 @@ static int bma150_probe(struct i2c_client *client, dev_err(&client->dev, "irq request failed %d, error %d\n", client->irq, error); - input_unregister_device(bma150->input); - goto err_free_mem; + return error; } - } else { - error = bma150_register_polled_device(bma150); - if (error) - goto err_free_mem; } i2c_set_clientdata(client, bma150); @@ -583,33 +511,16 @@ static int bma150_probe(struct i2c_client *client, pm_runtime_enable(&client->dev); return 0; - -err_free_mem: - kfree(bma150); - return error; } static int bma150_remove(struct i2c_client *client) { - struct bma150_data *bma150 = i2c_get_clientdata(client); - pm_runtime_disable(&client->dev); - if (client->irq > 0) { - free_irq(client->irq, bma150); - input_unregister_device(bma150->input); - } else { - input_unregister_polled_device(bma150->input_polled); - input_free_polled_device(bma150->input_polled); - } - - kfree(bma150); - return 0; } -#ifdef CONFIG_PM -static int bma150_suspend(struct device *dev) +static int __maybe_unused bma150_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct bma150_data *bma150 = i2c_get_clientdata(client); @@ -617,14 +528,13 @@ static int bma150_suspend(struct device *dev) return bma150_set_mode(bma150, BMA150_MODE_SLEEP); } -static int bma150_resume(struct device *dev) +static int __maybe_unused bma150_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct bma150_data *bma150 = i2c_get_clientdata(client); return bma150_set_mode(bma150, BMA150_MODE_NORMAL); } -#endif static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL); diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c index bcf6174bbd5d..b1624f5414ee 100644 --- a/drivers/input/misc/cobalt_btns.c +++ b/drivers/input/misc/cobalt_btns.c @@ -4,7 +4,8 @@ * * Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org> */ -#include <linux/input-polldev.h> +#include <linux/input.h> +#include <linux/io.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -26,16 +27,14 @@ static const unsigned short cobalt_map[] = { }; struct buttons_dev { - struct input_polled_dev *poll_dev; unsigned short keymap[ARRAY_SIZE(cobalt_map)]; int count[ARRAY_SIZE(cobalt_map)]; void __iomem *reg; }; -static void handle_buttons(struct input_polled_dev *dev) +static void handle_buttons(struct input_dev *input) { - struct buttons_dev *bdev = dev->private; - struct input_dev *input = dev->input; + struct buttons_dev *bdev = input_get_drvdata(input); uint32_t status; int i; @@ -62,29 +61,33 @@ static void handle_buttons(struct input_polled_dev *dev) static int cobalt_buttons_probe(struct platform_device *pdev) { struct buttons_dev *bdev; - struct input_polled_dev *poll_dev; struct input_dev *input; struct resource *res; int error, i; - bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); - poll_dev = input_allocate_polled_device(); - if (!bdev || !poll_dev) { - error = -ENOMEM; - goto err_free_mem; - } + bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); + if (!bdev) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EBUSY; + + bdev->reg = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + if (!bdev->reg) + return -ENOMEM; memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap)); - poll_dev->private = bdev; - poll_dev->poll = handle_buttons; - poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + input_set_drvdata(input, bdev); - input = poll_dev->input; input->name = "Cobalt buttons"; input->phys = "cobalt/input0"; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; input->keycode = bdev->keymap; input->keycodemax = ARRAY_SIZE(bdev->keymap); @@ -96,39 +99,16 @@ static int cobalt_buttons_probe(struct platform_device *pdev) __set_bit(bdev->keymap[i], input->keybit); __clear_bit(KEY_RESERVED, input->keybit); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - error = -EBUSY; - goto err_free_mem; - } - - bdev->poll_dev = poll_dev; - bdev->reg = ioremap(res->start, resource_size(res)); - dev_set_drvdata(&pdev->dev, bdev); - error = input_register_polled_device(poll_dev); + error = input_setup_polling(input, handle_buttons); if (error) - goto err_iounmap; - - return 0; + return error; - err_iounmap: - iounmap(bdev->reg); - err_free_mem: - input_free_polled_device(poll_dev); - kfree(bdev); - return error; -} + input_set_poll_interval(input, BUTTONS_POLL_INTERVAL); -static int cobalt_buttons_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct buttons_dev *bdev = dev_get_drvdata(dev); - - input_unregister_polled_device(bdev->poll_dev); - input_free_polled_device(bdev->poll_dev); - iounmap(bdev->reg); - kfree(bdev); + error = input_register_device(input); + if (error) + return error; return 0; } @@ -141,7 +121,6 @@ MODULE_ALIAS("platform:Cobalt buttons"); static struct platform_driver cobalt_buttons_driver = { .probe = cobalt_buttons_probe, - .remove = cobalt_buttons_remove, .driver = { .name = "Cobalt buttons", }, diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c index 1dca526e6f1a..145826a1a9a1 100644 --- a/drivers/input/misc/gpio_decoder.c +++ b/drivers/input/misc/gpio_decoder.c @@ -17,14 +17,12 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> struct gpio_decoder { - struct input_polled_dev *poll_dev; struct gpio_descs *input_gpios; struct device *dev; u32 axis; @@ -53,15 +51,15 @@ static int gpio_decoder_get_gpios_state(struct gpio_decoder *decoder) return ret; } -static void gpio_decoder_poll_gpios(struct input_polled_dev *poll_dev) +static void gpio_decoder_poll_gpios(struct input_dev *input) { - struct gpio_decoder *decoder = poll_dev->private; + struct gpio_decoder *decoder = input_get_drvdata(input); int state; state = gpio_decoder_get_gpios_state(decoder); if (state >= 0 && state != decoder->last_stable) { - input_report_abs(poll_dev->input, decoder->axis, state); - input_sync(poll_dev->input); + input_report_abs(input, decoder->axis, state); + input_sync(input); decoder->last_stable = state; } } @@ -70,20 +68,23 @@ static int gpio_decoder_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_decoder *decoder; - struct input_polled_dev *poll_dev; + struct input_dev *input; u32 max; int err; - decoder = devm_kzalloc(dev, sizeof(struct gpio_decoder), GFP_KERNEL); + decoder = devm_kzalloc(dev, sizeof(*decoder), GFP_KERNEL); if (!decoder) return -ENOMEM; + decoder->dev = dev; device_property_read_u32(dev, "linux,axis", &decoder->axis); + decoder->input_gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); if (IS_ERR(decoder->input_gpios)) { dev_err(dev, "unable to acquire input gpios\n"); return PTR_ERR(decoder->input_gpios); } + if (decoder->input_gpios->ndescs < 2) { dev_err(dev, "not enough gpios found\n"); return -EINVAL; @@ -92,22 +93,25 @@ static int gpio_decoder_probe(struct platform_device *pdev) if (device_property_read_u32(dev, "decoder-max-value", &max)) max = (1U << decoder->input_gpios->ndescs) - 1; - decoder->dev = dev; - poll_dev = devm_input_allocate_polled_device(decoder->dev); - if (!poll_dev) + input = devm_input_allocate_device(dev); + if (!input) return -ENOMEM; - poll_dev->private = decoder; - poll_dev->poll = gpio_decoder_poll_gpios; - decoder->poll_dev = poll_dev; + input_set_drvdata(input, decoder); - poll_dev->input->name = pdev->name; - poll_dev->input->id.bustype = BUS_HOST; - input_set_abs_params(poll_dev->input, decoder->axis, 0, max, 0, 0); + input->name = pdev->name; + input->id.bustype = BUS_HOST; + input_set_abs_params(input, decoder->axis, 0, max, 0, 0); + + err = input_setup_polling(input, gpio_decoder_poll_gpios); + if (err) { + dev_err(dev, "failed to set up polling\n"); + return err; + } - err = input_register_polled_device(poll_dev); + err = input_register_device(input); if (err) { - dev_err(dev, "failed to register polled device\n"); + dev_err(dev, "failed to register input device\n"); return err; } diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c index abca895a6156..199bc17ddb1d 100644 --- a/drivers/input/misc/hp_sdc_rtc.c +++ b/drivers/input/misc/hp_sdc_rtc.c @@ -53,28 +53,10 @@ MODULE_LICENSE("Dual BSD/GPL"); #define RTC_VERSION "1.10d" -static DEFINE_MUTEX(hp_sdc_rtc_mutex); static unsigned long epoch = 2000; static struct semaphore i8042tregs; -static hp_sdc_irqhook hp_sdc_rtc_isr; - -static struct fasync_struct *hp_sdc_rtc_async_queue; - -static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait); - -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); - -static long hp_sdc_rtc_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); - -static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait); - -static int hp_sdc_rtc_open(struct inode *inode, struct file *file); -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on); - static void hp_sdc_rtc_isr (int irq, void *dev_id, uint8_t status, uint8_t data) { @@ -283,151 +265,6 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) { return 0; } - -#if 0 /* not used yet */ -/* Set the i8042 real-time clock */ -static int hp_sdc_rtc_set_rt (struct timeval *setto) -{ - uint32_t tenms; - unsigned int days; - hp_sdc_transaction t; - uint8_t tseq[11] = { - HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, - HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0, - HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, - HP_SDC_CMD_SET_RTD, 2, 0, 0 - }; - - t.endidx = 10; - - if (0xffff < setto->tv_sec / 86400) return -1; - days = setto->tv_sec / 86400; - if (0xffff < setto->tv_usec / 1000000 / 86400) return -1; - days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400; - if (days > 0xffff) return -1; - - if (0xffffff < setto->tv_sec) return -1; - tenms = setto->tv_sec * 100; - if (0xffffff < setto->tv_usec / 10000) return -1; - tenms += setto->tv_usec / 10000; - if (tenms > 0xffffff) return -1; - - tseq[3] = (uint8_t)(tenms & 0xff); - tseq[4] = (uint8_t)((tenms >> 8) & 0xff); - tseq[5] = (uint8_t)((tenms >> 16) & 0xff); - - tseq[9] = (uint8_t)(days & 0xff); - tseq[10] = (uint8_t)((days >> 8) & 0xff); - - t.seq = tseq; - - if (hp_sdc_enqueue_transaction(&t)) return -1; - return 0; -} - -/* Set the i8042 fast handshake timer */ -static int hp_sdc_rtc_set_fhs (struct timeval *setto) -{ - uint32_t tenms; - hp_sdc_transaction t; - uint8_t tseq[5] = { - HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, - HP_SDC_CMD_SET_FHS, 2, 0, 0 - }; - - t.endidx = 4; - - if (0xffff < setto->tv_sec) return -1; - tenms = setto->tv_sec * 100; - if (0xffff < setto->tv_usec / 10000) return -1; - tenms += setto->tv_usec / 10000; - if (tenms > 0xffff) return -1; - - tseq[3] = (uint8_t)(tenms & 0xff); - tseq[4] = (uint8_t)((tenms >> 8) & 0xff); - - t.seq = tseq; - - if (hp_sdc_enqueue_transaction(&t)) return -1; - return 0; -} - - -/* Set the i8042 match timer (a.k.a. alarm) */ -#define hp_sdc_rtc_set_mt (setto) \ - hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT) - -/* Set the i8042 delay timer */ -#define hp_sdc_rtc_set_dt (setto) \ - hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT) - -/* Set the i8042 cycle timer (a.k.a. periodic) */ -#define hp_sdc_rtc_set_ct (setto) \ - hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT) - -/* Set one of the i8042 3-byte wide timers */ -static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd) -{ - uint32_t tenms; - hp_sdc_transaction t; - uint8_t tseq[6] = { - HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, - 0, 3, 0, 0, 0 - }; - - t.endidx = 6; - - if (0xffffff < setto->tv_sec) return -1; - tenms = setto->tv_sec * 100; - if (0xffffff < setto->tv_usec / 10000) return -1; - tenms += setto->tv_usec / 10000; - if (tenms > 0xffffff) return -1; - - tseq[1] = setcmd; - tseq[3] = (uint8_t)(tenms & 0xff); - tseq[4] = (uint8_t)((tenms >> 8) & 0xff); - tseq[5] = (uint8_t)((tenms >> 16) & 0xff); - - t.seq = tseq; - - if (hp_sdc_enqueue_transaction(&t)) { - return -1; - } - return 0; -} -#endif - -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) { - ssize_t retval; - - if (count < sizeof(unsigned long)) - return -EINVAL; - - retval = put_user(68, (unsigned long __user *)buf); - return retval; -} - -static __poll_t hp_sdc_rtc_poll(struct file *file, poll_table *wait) -{ - unsigned long l; - - l = 0; - if (l != 0) - return EPOLLIN | EPOLLRDNORM; - return 0; -} - -static int hp_sdc_rtc_open(struct inode *inode, struct file *file) -{ - return 0; -} - -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on) -{ - return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue); -} - static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v) { #define YN(bit) ("no") @@ -507,182 +344,6 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v) #undef NY } -static int hp_sdc_rtc_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ -#if 1 - return -EINVAL; -#else - - struct rtc_time wtime; - struct timeval ttime; - int use_wtime = 0; - - /* This needs major work. */ - - switch (cmd) { - - case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ - case RTC_AIE_ON: /* Allow alarm interrupts. */ - case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ - case RTC_PIE_ON: /* Allow periodic ints */ - case RTC_UIE_ON: /* Allow ints for RTC updates. */ - case RTC_UIE_OFF: /* Allow ints for RTC updates. */ - { - /* We cannot mask individual user timers and we - cannot tell them apart when they occur, so it - would be disingenuous to succeed these IOCTLs */ - return -EINVAL; - } - case RTC_ALM_READ: /* Read the present alarm time */ - { - if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT; - if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; - - wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600; - wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60; - wtime.tm_sec = ttime.tv_sec; - - break; - } - case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ - { - return put_user(hp_sdc_rtc_freq, (unsigned long *)arg); - } - case RTC_IRQP_SET: /* Set periodic IRQ rate. */ - { - /* - * The max we can do is 100Hz. - */ - - if ((arg < 1) || (arg > 100)) return -EINVAL; - ttime.tv_sec = 0; - ttime.tv_usec = 1000000 / arg; - if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT; - hp_sdc_rtc_freq = arg; - return 0; - } - case RTC_ALM_SET: /* Store a time into the alarm */ - { - /* - * This expects a struct hp_sdc_rtc_time. Writing 0xff means - * "don't care" or "match all" for PC timers. The HP SDC - * does not support that perk, but it could be emulated fairly - * easily. Only the tm_hour, tm_min and tm_sec are used. - * We could do it with 10ms accuracy with the HP SDC, if the - * rtc interface left us a way to do that. - */ - struct hp_sdc_rtc_time alm_tm; - - if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg, - sizeof(struct hp_sdc_rtc_time))) - return -EFAULT; - - if (alm_tm.tm_hour > 23) return -EINVAL; - if (alm_tm.tm_min > 59) return -EINVAL; - if (alm_tm.tm_sec > 59) return -EINVAL; - - ttime.sec = alm_tm.tm_hour * 3600 + - alm_tm.tm_min * 60 + alm_tm.tm_sec; - ttime.usec = 0; - if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT; - return 0; - } - case RTC_RD_TIME: /* Read the time/date from RTC */ - { - if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; - break; - } - case RTC_SET_TIME: /* Set the RTC */ - { - struct rtc_time hp_sdc_rtc_tm; - unsigned char mon, day, hrs, min, sec, leap_yr; - unsigned int yrs; - - if (!capable(CAP_SYS_TIME)) - return -EACCES; - if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg, - sizeof(struct rtc_time))) - return -EFAULT; - - yrs = hp_sdc_rtc_tm.tm_year + 1900; - mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ - day = hp_sdc_rtc_tm.tm_mday; - hrs = hp_sdc_rtc_tm.tm_hour; - min = hp_sdc_rtc_tm.tm_min; - sec = hp_sdc_rtc_tm.tm_sec; - - if (yrs < 1970) - return -EINVAL; - - leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); - - if ((mon > 12) || (day == 0)) - return -EINVAL; - if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) - return -EINVAL; - if ((hrs >= 24) || (min >= 60) || (sec >= 60)) - return -EINVAL; - - if ((yrs -= eH) > 255) /* They are unsigned */ - return -EINVAL; - - - return 0; - } - case RTC_EPOCH_READ: /* Read the epoch. */ - { - return put_user (epoch, (unsigned long *)arg); - } - case RTC_EPOCH_SET: /* Set the epoch. */ - { - /* - * There were no RTC clocks before 1900. - */ - if (arg < 1900) - return -EINVAL; - if (!capable(CAP_SYS_TIME)) - return -EACCES; - - epoch = arg; - return 0; - } - default: - return -EINVAL; - } - return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; -#endif -} - -static long hp_sdc_rtc_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - int ret; - - mutex_lock(&hp_sdc_rtc_mutex); - ret = hp_sdc_rtc_ioctl(file, cmd, arg); - mutex_unlock(&hp_sdc_rtc_mutex); - - return ret; -} - - -static const struct file_operations hp_sdc_rtc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = hp_sdc_rtc_read, - .poll = hp_sdc_rtc_poll, - .unlocked_ioctl = hp_sdc_rtc_unlocked_ioctl, - .open = hp_sdc_rtc_open, - .fasync = hp_sdc_rtc_fasync, -}; - -static struct miscdevice hp_sdc_rtc_dev = { - .minor = RTC_MINOR, - .name = "rtc_HIL", - .fops = &hp_sdc_rtc_fops -}; - static int __init hp_sdc_rtc_init(void) { int ret; @@ -696,8 +357,6 @@ static int __init hp_sdc_rtc_init(void) if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr))) return ret; - if (misc_register(&hp_sdc_rtc_dev) != 0) - printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n"); proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show); @@ -710,7 +369,6 @@ static int __init hp_sdc_rtc_init(void) static void __exit hp_sdc_rtc_exit(void) { remove_proc_entry ("driver/rtc", NULL); - misc_deregister(&hp_sdc_rtc_dev); hp_sdc_release_timer_irq(hp_sdc_rtc_isr); printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n"); } diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index db01c4a33914..52313c6e3fb3 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/input/kxtj9.h> -#include <linux/input-polldev.h> #define NAME "kxtj9" #define G_MAX 8000 @@ -71,9 +70,6 @@ struct kxtj9_data { struct i2c_client *client; struct kxtj9_platform_data pdata; struct input_dev *input_dev; -#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE - struct input_polled_dev *poll_dev; -#endif unsigned int last_poll_interval; u8 shift; u8 ctrl_reg1; @@ -282,50 +278,6 @@ static void kxtj9_input_close(struct input_dev *dev) kxtj9_disable(tj9); } -static void kxtj9_init_input_device(struct kxtj9_data *tj9, - struct input_dev *input_dev) -{ - __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); - input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); - input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); - - input_dev->name = "kxtj9_accel"; - input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &tj9->client->dev; -} - -static int kxtj9_setup_input_device(struct kxtj9_data *tj9) -{ - struct input_dev *input_dev; - int err; - - input_dev = input_allocate_device(); - if (!input_dev) { - dev_err(&tj9->client->dev, "input device allocate failed\n"); - return -ENOMEM; - } - - tj9->input_dev = input_dev; - - input_dev->open = kxtj9_input_open; - input_dev->close = kxtj9_input_close; - input_set_drvdata(input_dev, tj9); - - kxtj9_init_input_device(tj9, input_dev); - - err = input_register_device(tj9->input_dev); - if (err) { - dev_err(&tj9->client->dev, - "unable to register input polled device %s: %d\n", - tj9->input_dev->name, err); - input_free_device(tj9->input_dev); - return err; - } - - return 0; -} - /* * When IRQ mode is selected, we need to provide an interface to allow the user * to change the output data rate of the part. For consistency, we are using @@ -391,12 +343,10 @@ static struct attribute_group kxtj9_attribute_group = { .attrs = kxtj9_attributes }; - -#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE -static void kxtj9_poll(struct input_polled_dev *dev) +static void kxtj9_poll(struct input_dev *input) { - struct kxtj9_data *tj9 = dev->private; - unsigned int poll_interval = dev->poll_interval; + struct kxtj9_data *tj9 = input_get_drvdata(input); + unsigned int poll_interval = input_get_poll_interval(input); kxtj9_report_acceleration_data(tj9); @@ -406,72 +356,14 @@ static void kxtj9_poll(struct input_polled_dev *dev) } } -static void kxtj9_polled_input_open(struct input_polled_dev *dev) -{ - struct kxtj9_data *tj9 = dev->private; - - kxtj9_enable(tj9); -} - -static void kxtj9_polled_input_close(struct input_polled_dev *dev) -{ - struct kxtj9_data *tj9 = dev->private; - - kxtj9_disable(tj9); -} - -static int kxtj9_setup_polled_device(struct kxtj9_data *tj9) -{ - int err; - struct input_polled_dev *poll_dev; - poll_dev = input_allocate_polled_device(); - - if (!poll_dev) { - dev_err(&tj9->client->dev, - "Failed to allocate polled device\n"); - return -ENOMEM; - } - - tj9->poll_dev = poll_dev; - tj9->input_dev = poll_dev->input; - - poll_dev->private = tj9; - poll_dev->poll = kxtj9_poll; - poll_dev->open = kxtj9_polled_input_open; - poll_dev->close = kxtj9_polled_input_close; - - kxtj9_init_input_device(tj9, poll_dev->input); - - err = input_register_polled_device(poll_dev); - if (err) { - dev_err(&tj9->client->dev, - "Unable to register polled device, err=%d\n", err); - input_free_polled_device(poll_dev); - return err; - } - - return 0; -} - -static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9) -{ - input_unregister_polled_device(tj9->poll_dev); - input_free_polled_device(tj9->poll_dev); -} - -#else - -static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9) +static void kxtj9_platform_exit(void *data) { - return -ENOSYS; -} + struct kxtj9_data *tj9 = data; -static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9) -{ + if (tj9->pdata.exit) + tj9->pdata.exit(); } -#endif - static int kxtj9_verify(struct kxtj9_data *tj9) { int retval; @@ -494,11 +386,12 @@ out: } static int kxtj9_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { const struct kxtj9_platform_data *pdata = dev_get_platdata(&client->dev); struct kxtj9_data *tj9; + struct input_dev *input_dev; int err; if (!i2c_check_functionality(client->adapter, @@ -512,7 +405,7 @@ static int kxtj9_probe(struct i2c_client *client, return -EINVAL; } - tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL); + tj9 = devm_kzalloc(&client->dev, sizeof(*tj9), GFP_KERNEL); if (!tj9) { dev_err(&client->dev, "failed to allocate memory for module data\n"); @@ -525,13 +418,17 @@ static int kxtj9_probe(struct i2c_client *client, if (pdata->init) { err = pdata->init(); if (err < 0) - goto err_free_mem; + return err; } + err = devm_add_action_or_reset(&client->dev, kxtj9_platform_exit, tj9); + if (err) + return err; + err = kxtj9_verify(tj9); if (err < 0) { dev_err(&client->dev, "device not recognized\n"); - goto err_pdata_exit; + return err; } i2c_set_clientdata(client, tj9); @@ -539,67 +436,63 @@ static int kxtj9_probe(struct i2c_client *client, tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; tj9->last_poll_interval = tj9->pdata.init_interval; + input_dev = devm_input_allocate_device(&client->dev); + if (!input_dev) { + dev_err(&client->dev, "input device allocate failed\n"); + return -ENOMEM; + } + + input_set_drvdata(input_dev, tj9); + tj9->input_dev = input_dev; + + input_dev->name = "kxtj9_accel"; + input_dev->id.bustype = BUS_I2C; + + input_dev->open = kxtj9_input_open; + input_dev->close = kxtj9_input_close; + + input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT); + input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT); + + if (client->irq <= 0) { + err = input_setup_polling(input_dev, kxtj9_poll); + if (err) + return err; + } + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, + "unable to register input polled device %s: %d\n", + input_dev->name, err); + return err; + } + if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL; tj9->ctrl_reg1 |= DRDYE; - err = kxtj9_setup_input_device(tj9); - if (err) - goto err_pdata_exit; - - err = request_threaded_irq(client->irq, NULL, kxtj9_isr, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, - "kxtj9-irq", tj9); + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, kxtj9_isr, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "kxtj9-irq", tj9); if (err) { dev_err(&client->dev, "request irq failed: %d\n", err); - goto err_destroy_input; + return err; } - err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group); + err = devm_device_add_group(&client->dev, + &kxtj9_attribute_group); if (err) { dev_err(&client->dev, "sysfs create failed: %d\n", err); - goto err_free_irq; + return err; } - - } else { - err = kxtj9_setup_polled_device(tj9); - if (err) - goto err_pdata_exit; } return 0; - -err_free_irq: - free_irq(client->irq, tj9); -err_destroy_input: - input_unregister_device(tj9->input_dev); -err_pdata_exit: - if (tj9->pdata.exit) - tj9->pdata.exit(); -err_free_mem: - kfree(tj9); - return err; -} - -static int kxtj9_remove(struct i2c_client *client) -{ - struct kxtj9_data *tj9 = i2c_get_clientdata(client); - - if (client->irq) { - sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group); - free_irq(client->irq, tj9); - input_unregister_device(tj9->input_dev); - } else { - kxtj9_teardown_polled_device(tj9); - } - - if (tj9->pdata.exit) - tj9->pdata.exit(); - - kfree(tj9); - - return 0; } static int __maybe_unused kxtj9_suspend(struct device *dev) @@ -647,7 +540,6 @@ static struct i2c_driver kxtj9_driver = { .pm = &kxtj9_pm_ops, }, .probe = kxtj9_probe, - .remove = kxtj9_remove, .id_table = kxtj9_id, }; diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c index 49f5242bc54c..1b5a5e19230a 100644 --- a/drivers/input/misc/mma8450.c +++ b/drivers/input/misc/mma8450.c @@ -10,7 +10,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/of_device.h> #define MMA8450_DRV_NAME "mma8450" @@ -39,15 +39,8 @@ #define MMA8450_CTRL_REG1 0x38 #define MMA8450_CTRL_REG2 0x39 -/* mma8450 status */ -struct mma8450 { - struct i2c_client *client; - struct input_polled_dev *idev; -}; - -static int mma8450_read(struct mma8450 *m, unsigned off) +static int mma8450_read(struct i2c_client *c, unsigned int off) { - struct i2c_client *c = m->client; int ret; ret = i2c_smbus_read_byte_data(c, off); @@ -59,9 +52,8 @@ static int mma8450_read(struct mma8450 *m, unsigned off) return ret; } -static int mma8450_write(struct mma8450 *m, unsigned off, u8 v) +static int mma8450_write(struct i2c_client *c, unsigned int off, u8 v) { - struct i2c_client *c = m->client; int error; error = i2c_smbus_write_byte_data(c, off, v); @@ -75,10 +67,9 @@ static int mma8450_write(struct mma8450 *m, unsigned off, u8 v) return 0; } -static int mma8450_read_block(struct mma8450 *m, unsigned off, +static int mma8450_read_block(struct i2c_client *c, unsigned int off, u8 *buf, size_t size) { - struct i2c_client *c = m->client; int err; err = i2c_smbus_read_i2c_block_data(c, off, size, buf); @@ -92,21 +83,21 @@ static int mma8450_read_block(struct mma8450 *m, unsigned off, return 0; } -static void mma8450_poll(struct input_polled_dev *dev) +static void mma8450_poll(struct input_dev *input) { - struct mma8450 *m = dev->private; + struct i2c_client *c = input_get_drvdata(input); int x, y, z; int ret; u8 buf[6]; - ret = mma8450_read(m, MMA8450_STATUS); + ret = mma8450_read(c, MMA8450_STATUS); if (ret < 0) return; if (!(ret & MMA8450_STATUS_ZXYDR)) return; - ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf)); + ret = mma8450_read_block(c, MMA8450_OUT_X_LSB, buf, sizeof(buf)); if (ret < 0) return; @@ -114,41 +105,42 @@ static void mma8450_poll(struct input_polled_dev *dev) y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf); z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf); - input_report_abs(dev->input, ABS_X, x); - input_report_abs(dev->input, ABS_Y, y); - input_report_abs(dev->input, ABS_Z, z); - input_sync(dev->input); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_Z, z); + input_sync(input); } /* Initialize the MMA8450 chip */ -static void mma8450_open(struct input_polled_dev *dev) +static int mma8450_open(struct input_dev *input) { - struct mma8450 *m = dev->private; + struct i2c_client *c = input_get_drvdata(input); int err; /* enable all events from X/Y/Z, no FIFO */ - err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07); + err = mma8450_write(c, MMA8450_XYZ_DATA_CFG, 0x07); if (err) - return; + return err; /* * Sleep mode poll rate - 50Hz * System output data rate - 400Hz * Full scale selection - Active, +/- 2G */ - err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01); - if (err < 0) - return; + err = mma8450_write(c, MMA8450_CTRL_REG1, 0x01); + if (err) + return err; msleep(MODE_CHANGE_DELAY_MS); + return 0; } -static void mma8450_close(struct input_polled_dev *dev) +static void mma8450_close(struct input_dev *input) { - struct mma8450 *m = dev->private; + struct i2c_client *c = input_get_drvdata(input); - mma8450_write(m, MMA8450_CTRL_REG1, 0x00); - mma8450_write(m, MMA8450_CTRL_REG2, 0x01); + mma8450_write(c, MMA8450_CTRL_REG1, 0x00); + mma8450_write(c, MMA8450_CTRL_REG2, 0x01); } /* @@ -157,38 +149,37 @@ static void mma8450_close(struct input_polled_dev *dev) static int mma8450_probe(struct i2c_client *c, const struct i2c_device_id *id) { - struct input_polled_dev *idev; - struct mma8450 *m; + struct input_dev *input; int err; - m = devm_kzalloc(&c->dev, sizeof(*m), GFP_KERNEL); - if (!m) + input = devm_input_allocate_device(&c->dev); + if (!input) return -ENOMEM; - idev = devm_input_allocate_polled_device(&c->dev); - if (!idev) - return -ENOMEM; + input_set_drvdata(input, c); + + input->name = MMA8450_DRV_NAME; + input->id.bustype = BUS_I2C; + + input->open = mma8450_open; + input->close = mma8450_close; - m->client = c; - m->idev = idev; + input_set_abs_params(input, ABS_X, -2048, 2047, 32, 32); + input_set_abs_params(input, ABS_Y, -2048, 2047, 32, 32); + input_set_abs_params(input, ABS_Z, -2048, 2047, 32, 32); - idev->private = m; - idev->input->name = MMA8450_DRV_NAME; - idev->input->id.bustype = BUS_I2C; - idev->poll = mma8450_poll; - idev->poll_interval = POLL_INTERVAL; - idev->poll_interval_max = POLL_INTERVAL_MAX; - idev->open = mma8450_open; - idev->close = mma8450_close; + err = input_setup_polling(input, mma8450_poll); + if (err) { + dev_err(&c->dev, "failed to set up polling\n"); + return err; + } - __set_bit(EV_ABS, idev->input->evbit); - input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32); - input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32); - input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32); + input_set_poll_interval(input, POLL_INTERVAL); + input_set_max_poll_interval(input, POLL_INTERVAL_MAX); - err = input_register_polled_device(idev); + err = input_register_device(input); if (err) { - dev_err(&c->dev, "failed to register polled input device\n"); + dev_err(&c->dev, "failed to register input device\n"); return err; } diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c index 4412055f8761..190a80e1e2c1 100644 --- a/drivers/input/misc/rb532_button.c +++ b/drivers/input/misc/rb532_button.c @@ -5,7 +5,7 @@ * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org> */ -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/gpio.h> @@ -46,56 +46,42 @@ static bool rb532_button_pressed(void) return !val; } -static void rb532_button_poll(struct input_polled_dev *poll_dev) +static void rb532_button_poll(struct input_dev *input) { - input_report_key(poll_dev->input, RB532_BTN_KSYM, - rb532_button_pressed()); - input_sync(poll_dev->input); + input_report_key(input, RB532_BTN_KSYM, rb532_button_pressed()); + input_sync(input); } static int rb532_button_probe(struct platform_device *pdev) { - struct input_polled_dev *poll_dev; + struct input_dev *input; int error; - poll_dev = input_allocate_polled_device(); - if (!poll_dev) + input = devm_input_allocate_device(&pdev->dev); + if (!input) return -ENOMEM; - poll_dev->poll = rb532_button_poll; - poll_dev->poll_interval = RB532_BTN_RATE; + input->name = "rb532 button"; + input->phys = "rb532/button0"; + input->id.bustype = BUS_HOST; - poll_dev->input->name = "rb532 button"; - poll_dev->input->phys = "rb532/button0"; - poll_dev->input->id.bustype = BUS_HOST; - poll_dev->input->dev.parent = &pdev->dev; + input_set_capability(input, EV_KEY, RB532_BTN_KSYM); - dev_set_drvdata(&pdev->dev, poll_dev); - - input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM); - - error = input_register_polled_device(poll_dev); - if (error) { - input_free_polled_device(poll_dev); + error = input_setup_polling(input, rb532_button_poll); + if (error) return error; - } - return 0; -} + input_set_poll_interval(input, RB532_BTN_RATE); -static int rb532_button_remove(struct platform_device *pdev) -{ - struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev); - - input_unregister_polled_device(poll_dev); - input_free_polled_device(poll_dev); + error = input_register_device(input); + if (error) + return error; return 0; } static struct platform_driver rb532_button_driver = { .probe = rb532_button_probe, - .remove = rb532_button_remove, .driver = { .name = DRV_NAME, }, diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c index 0fee6ddf3602..0657d785b3cc 100644 --- a/drivers/input/misc/sgi_btns.c +++ b/drivers/input/misc/sgi_btns.c @@ -4,7 +4,7 @@ * * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.de> */ -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -45,15 +45,13 @@ static const unsigned short sgi_map[] = { }; struct buttons_dev { - struct input_polled_dev *poll_dev; unsigned short keymap[ARRAY_SIZE(sgi_map)]; int count[ARRAY_SIZE(sgi_map)]; }; -static void handle_buttons(struct input_polled_dev *dev) +static void handle_buttons(struct input_dev *input) { - struct buttons_dev *bdev = dev->private; - struct input_dev *input = dev->input; + struct buttons_dev *bdev = input_get_drvdata(input); u8 status; int i; @@ -80,28 +78,24 @@ static void handle_buttons(struct input_polled_dev *dev) static int sgi_buttons_probe(struct platform_device *pdev) { struct buttons_dev *bdev; - struct input_polled_dev *poll_dev; struct input_dev *input; int error, i; - bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL); - poll_dev = input_allocate_polled_device(); - if (!bdev || !poll_dev) { - error = -ENOMEM; - goto err_free_mem; - } + bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); + if (!bdev) + return -ENOMEM; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap)); - poll_dev->private = bdev; - poll_dev->poll = handle_buttons; - poll_dev->poll_interval = BUTTONS_POLL_INTERVAL; + input_set_drvdata(input, bdev); - input = poll_dev->input; input->name = "SGI buttons"; input->phys = "sgi/input0"; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; input->keycode = bdev->keymap; input->keycodemax = ARRAY_SIZE(bdev->keymap); @@ -113,35 +107,21 @@ static int sgi_buttons_probe(struct platform_device *pdev) __set_bit(bdev->keymap[i], input->keybit); __clear_bit(KEY_RESERVED, input->keybit); - bdev->poll_dev = poll_dev; - platform_set_drvdata(pdev, bdev); - - error = input_register_polled_device(poll_dev); + error = input_setup_polling(input, handle_buttons); if (error) - goto err_free_mem; + return error; - return 0; + input_set_poll_interval(input, BUTTONS_POLL_INTERVAL); - err_free_mem: - input_free_polled_device(poll_dev); - kfree(bdev); - return error; -} - -static int sgi_buttons_remove(struct platform_device *pdev) -{ - struct buttons_dev *bdev = platform_get_drvdata(pdev); - - input_unregister_polled_device(bdev->poll_dev); - input_free_polled_device(bdev->poll_dev); - kfree(bdev); + error = input_register_device(input); + if (error) + return error; return 0; } static struct platform_driver sgi_buttons_driver = { .probe = sgi_buttons_probe, - .remove = sgi_buttons_remove, .driver = { .name = "sgibtns", }, diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 7ce6cc60d4d2..80dfd72a02d3 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -8,7 +8,7 @@ #include <linux/io.h> #include <linux/dmi.h> #include <linux/init.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/input/sparse-keymap.h> #include <linux/interrupt.h> #include <linux/jiffies.h> @@ -1030,7 +1030,7 @@ static int __init select_keymap(void) /* Input layer interface */ -static struct input_polled_dev *wistron_idev; +static struct input_dev *wistron_idev; static unsigned long jiffies_last_press; static bool wifi_enabled; static bool bluetooth_enabled; @@ -1114,7 +1114,7 @@ static inline void wistron_led_resume(void) static void handle_key(u8 code) { const struct key_entry *key = - sparse_keymap_entry_from_scancode(wistron_idev->input, code); + sparse_keymap_entry_from_scancode(wistron_idev, code); if (key) { switch (key->type) { @@ -1133,14 +1133,14 @@ static void handle_key(u8 code) break; default: - sparse_keymap_report_entry(wistron_idev->input, - key, 1, true); + sparse_keymap_report_entry(wistron_idev, key, 1, true); break; } jiffies_last_press = jiffies; - } else + } else { printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); + } } static void poll_bios(bool discard) @@ -1158,21 +1158,23 @@ static void poll_bios(bool discard) } } -static void wistron_flush(struct input_polled_dev *dev) +static int wistron_flush(struct input_dev *dev) { /* Flush stale event queue */ poll_bios(true); + + return 0; } -static void wistron_poll(struct input_polled_dev *dev) +static void wistron_poll(struct input_dev *dev) { poll_bios(false); /* Increase poll frequency if user is currently pressing keys (< 2s ago) */ if (time_before(jiffies, jiffies_last_press + 2 * HZ)) - dev->poll_interval = POLL_INTERVAL_BURST; + input_set_poll_interval(dev, POLL_INTERVAL_BURST); else - dev->poll_interval = POLL_INTERVAL_DEFAULT; + input_set_poll_interval(dev, POLL_INTERVAL_DEFAULT); } static int wistron_setup_keymap(struct input_dev *dev, @@ -1208,35 +1210,37 @@ static int wistron_setup_keymap(struct input_dev *dev, static int setup_input_dev(void) { - struct input_dev *input_dev; int error; - wistron_idev = input_allocate_polled_device(); + wistron_idev = input_allocate_device(); if (!wistron_idev) return -ENOMEM; + wistron_idev->name = "Wistron laptop buttons"; + wistron_idev->phys = "wistron/input0"; + wistron_idev->id.bustype = BUS_HOST; + wistron_idev->dev.parent = &wistron_device->dev; + wistron_idev->open = wistron_flush; - wistron_idev->poll = wistron_poll; - wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT; - input_dev = wistron_idev->input; - input_dev->name = "Wistron laptop buttons"; - input_dev->phys = "wistron/input0"; - input_dev->id.bustype = BUS_HOST; - input_dev->dev.parent = &wistron_device->dev; + error = sparse_keymap_setup(wistron_idev, keymap, wistron_setup_keymap); + if (error) + goto err_free_dev; - error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap); + error = input_setup_polling(wistron_idev, wistron_poll); if (error) goto err_free_dev; - error = input_register_polled_device(wistron_idev); + input_set_poll_interval(wistron_idev, POLL_INTERVAL_DEFAULT); + + error = input_register_device(wistron_idev); if (error) goto err_free_dev; return 0; err_free_dev: - input_free_polled_device(wistron_idev); + input_free_device(wistron_idev); return error; } @@ -1285,8 +1289,7 @@ static int wistron_probe(struct platform_device *dev) static int wistron_remove(struct platform_device *dev) { wistron_led_remove(); - input_unregister_polled_device(wistron_idev); - input_free_polled_device(wistron_idev); + input_unregister_device(wistron_idev); bios_detach(); return 0; diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 652c38e3c0b5..d8b6a5dab190 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -92,14 +92,14 @@ config MOUSE_PS2_SYNAPTICS_SMBUS If unsure, say Y. config MOUSE_PS2_CYPRESS - bool "Cypress PS/2 mouse protocol extension" if EXPERT - default y - depends on MOUSE_PS2 - help - Say Y here if you have a Cypress PS/2 Trackpad connected to - your system. + bool "Cypress PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Cypress PS/2 Trackpad connected to + your system. - If unsure, say Y. + If unsure, say Y. config MOUSE_PS2_LIFEBOOK bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT @@ -381,7 +381,6 @@ config MOUSE_VSXXXAA config MOUSE_GPIO tristate "GPIO mouse" depends on GPIOLIB || COMPILE_TEST - select INPUT_POLLDEV help This driver simulates a mouse on GPIO lines of various CPUs (and some other chips). diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 461436f6f087..23507fce3a2b 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -8,7 +8,7 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/gpio/consumer.h> #include <linux/property.h> #include <linux/of.h> @@ -43,10 +43,9 @@ struct gpio_mouse { * Timer function which is run every scan_ms ms when the device is opened. * The dev input variable is set to the the input_dev pointer. */ -static void gpio_mouse_scan(struct input_polled_dev *dev) +static void gpio_mouse_scan(struct input_dev *input) { - struct gpio_mouse *gpio = dev->private; - struct input_dev *input = dev->input; + struct gpio_mouse *gpio = input_get_drvdata(input); int x, y; if (gpio->bleft) @@ -71,18 +70,17 @@ static int gpio_mouse_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct gpio_mouse *gmouse; - struct input_polled_dev *input_poll; struct input_dev *input; - int ret; + int error; gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); if (!gmouse) return -ENOMEM; /* Assign some default scanning time */ - ret = device_property_read_u32(dev, "scan-interval-ms", - &gmouse->scan_ms); - if (ret || gmouse->scan_ms == 0) { + error = device_property_read_u32(dev, "scan-interval-ms", + &gmouse->scan_ms); + if (error || gmouse->scan_ms == 0) { dev_warn(dev, "invalid scan time, set to 50 ms\n"); gmouse->scan_ms = 50; } @@ -112,23 +110,14 @@ static int gpio_mouse_probe(struct platform_device *pdev) if (IS_ERR(gmouse->bright)) return PTR_ERR(gmouse->bright); - input_poll = devm_input_allocate_polled_device(dev); - if (!input_poll) { - dev_err(dev, "not enough memory for input device\n"); + input = devm_input_allocate_device(dev); + if (!input) return -ENOMEM; - } - - platform_set_drvdata(pdev, input_poll); - - /* set input-polldev handlers */ - input_poll->private = gmouse; - input_poll->poll = gpio_mouse_scan; - input_poll->poll_interval = gmouse->scan_ms; - input = input_poll->input; input->name = pdev->name; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; + + input_set_drvdata(input, gmouse); input_set_capability(input, EV_REL, REL_X); input_set_capability(input, EV_REL, REL_Y); @@ -139,10 +128,16 @@ static int gpio_mouse_probe(struct platform_device *pdev) if (gmouse->bright) input_set_capability(input, EV_KEY, BTN_RIGHT); - ret = input_register_polled_device(input_poll); - if (ret) { + error = input_setup_polling(input, gpio_mouse_scan); + if (error) + return error; + + input_set_poll_interval(input, gmouse->scan_ms); + + error = input_register_device(input); + if (error) { dev_err(dev, "could not register input device\n"); - return ret; + return error; } dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 56fae3472114..1ae6f8bba9ae 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -172,6 +172,7 @@ static const char * const smbus_pnp_ids[] = { "LEN0071", /* T480 */ "LEN0072", /* X1 Carbon Gen 5 (2017) - Elan/ALPS trackpoint */ "LEN0073", /* X1 Carbon G5 (Elantech) */ + "LEN0091", /* X1 Carbon 6 */ "LEN0092", /* X1 Carbon 6 */ "LEN0093", /* T480 */ "LEN0096", /* X280 */ diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c index 897105b9a98b..0bc01cfc2b51 100644 --- a/drivers/input/rmi4/rmi_f54.c +++ b/drivers/input/rmi4/rmi_f54.c @@ -81,11 +81,6 @@ static const char * const rmi_f54_report_type_names[] = { = "Full Raw Capacitance RX Offset Removed", }; -struct rmi_f54_reports { - int start; - int size; -}; - struct f54_data { struct rmi_function *fn; @@ -98,7 +93,6 @@ struct f54_data { enum rmi_f54_report_type report_type; u8 *report_data; int report_size; - struct rmi_f54_reports standard_report[2]; bool is_busy; struct mutex status_mutex; @@ -116,6 +110,7 @@ struct f54_data { struct video_device vdev; struct vb2_queue queue; struct mutex lock; + u32 sequence; int input; enum rmi_f54_report_type inputs[F54_MAX_REPORT_TYPE]; }; @@ -290,6 +285,7 @@ static int rmi_f54_queue_setup(struct vb2_queue *q, unsigned int *nbuffers, static void rmi_f54_buffer_queue(struct vb2_buffer *vb) { + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct f54_data *f54 = vb2_get_drv_priv(vb->vb2_queue); u16 *ptr; enum vb2_buffer_state state; @@ -298,6 +294,7 @@ static void rmi_f54_buffer_queue(struct vb2_buffer *vb) mutex_lock(&f54->status_mutex); + vb2_set_plane_payload(vb, 0, 0); reptype = rmi_f54_get_reptype(f54, f54->input); if (reptype == F54_REPORT_NONE) { state = VB2_BUF_STATE_ERROR; @@ -344,14 +341,25 @@ static void rmi_f54_buffer_queue(struct vb2_buffer *vb) data_done: mutex_unlock(&f54->data_mutex); done: + vb->timestamp = ktime_get_ns(); + vbuf->field = V4L2_FIELD_NONE; + vbuf->sequence = f54->sequence++; vb2_buffer_done(vb, state); mutex_unlock(&f54->status_mutex); } +static void rmi_f54_stop_streaming(struct vb2_queue *q) +{ + struct f54_data *f54 = vb2_get_drv_priv(q); + + f54->sequence = 0; +} + /* V4L2 structures */ static const struct vb2_ops rmi_f54_queue_ops = { .queue_setup = rmi_f54_queue_setup, .buf_queue = rmi_f54_buffer_queue, + .stop_streaming = rmi_f54_stop_streaming, .wait_prepare = vb2_ops_wait_prepare, .wait_finish = vb2_ops_wait_finish, }; @@ -363,7 +371,6 @@ static const struct vb2_queue rmi_f54_queue = { .ops = &rmi_f54_queue_ops, .mem_ops = &vb2_vmalloc_memops, .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, - .min_buffers_needed = 1, }; static int rmi_f54_vidioc_querycap(struct file *file, void *priv, @@ -516,13 +523,10 @@ static void rmi_f54_work(struct work_struct *work) struct f54_data *f54 = container_of(work, struct f54_data, work.work); struct rmi_function *fn = f54->fn; u8 fifo[2]; - struct rmi_f54_reports *report; int report_size; u8 command; - u8 *data; int error; - data = f54->report_data; report_size = rmi_f54_get_report_size(f54); if (report_size == 0) { dev_err(&fn->dev, "Bad report size, report type=%d\n", @@ -530,8 +534,6 @@ static void rmi_f54_work(struct work_struct *work) error = -EINVAL; goto error; /* retry won't help */ } - f54->standard_report[0].size = report_size; - report = f54->standard_report; mutex_lock(&f54->data_mutex); @@ -556,28 +558,23 @@ static void rmi_f54_work(struct work_struct *work) rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n"); - report_size = 0; - for (; report->size; report++) { - fifo[0] = report->start & 0xff; - fifo[1] = (report->start >> 8) & 0xff; - error = rmi_write_block(fn->rmi_dev, - fn->fd.data_base_addr + F54_FIFO_OFFSET, - fifo, sizeof(fifo)); - if (error) { - dev_err(&fn->dev, "Failed to set fifo start offset\n"); - goto abort; - } + fifo[0] = 0; + fifo[1] = 0; + error = rmi_write_block(fn->rmi_dev, + fn->fd.data_base_addr + F54_FIFO_OFFSET, + fifo, sizeof(fifo)); + if (error) { + dev_err(&fn->dev, "Failed to set fifo start offset\n"); + goto abort; + } - error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + - F54_REPORT_DATA_OFFSET, data, - report->size); - if (error) { - dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", - __func__, report->size, error); - goto abort; - } - data += report->size; - report_size += report->size; + error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr + + F54_REPORT_DATA_OFFSET, f54->report_data, + report_size); + if (error) { + dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n", + __func__, report_size, error); + goto abort; } abort: diff --git a/drivers/input/tablet/Kconfig b/drivers/input/tablet/Kconfig index e4c0d9a055b9..51c339182017 100644 --- a/drivers/input/tablet/Kconfig +++ b/drivers/input/tablet/Kconfig @@ -39,16 +39,16 @@ config TABLET_USB_AIPTEK module will be called aiptek. config TABLET_USB_GTCO - tristate "GTCO CalComp/InterWrite USB Support" - depends on USB && INPUT - help - Say Y here if you want to use the USB version of the GTCO - CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" - (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" - (CONFIG_INPUT_EVDEV) as well. - - To compile this driver as a module, choose M here: the - module will be called gtco. + tristate "GTCO CalComp/InterWrite USB Support" + depends on USB && INPUT + help + Say Y here if you want to use the USB version of the GTCO + CalComp/InterWrite Tablet. Make sure to say Y to "Mouse support" + (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support" + (CONFIG_INPUT_EVDEV) as well. + + To compile this driver as a module, choose M here: the + module will be called gtco. config TABLET_USB_HANWANG tristate "Hanwang Art Master III tablet support (USB)" diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 46ad9090493b..c071f7c407b6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -633,7 +633,7 @@ config TOUCHSCREEN_HP600 depends on SH_HP6XX && SH_ADC help Say Y here if you have a HP Jornada 620/660/680/690 and want to - support the built-in touchscreen. + support the built-in touchscreen. To compile this driver as a module, choose M here: the module will be called hp680_ts_input. @@ -700,7 +700,6 @@ config TOUCHSCREEN_EDT_FT5X06 config TOUCHSCREEN_RASPBERRYPI_FW tristate "Raspberry Pi's firmware base touch screen support" depends on RASPBERRYPI_FIRMWARE || (RASPBERRYPI_FIRMWARE=n && COMPILE_TEST) - select INPUT_POLLDEV help Say Y here if you have the official Raspberry Pi 7 inch screen on your system. @@ -1038,7 +1037,6 @@ config TOUCHSCREEN_TS4800 depends on HAS_IOMEM && OF depends on SOC_IMX51 || COMPILE_TEST select MFD_SYSCON - select INPUT_POLLDEV help Say Y here if you have a touchscreen on a TS-4800 board. @@ -1210,7 +1208,6 @@ config TOUCHSCREEN_SUR40 tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen" depends on USB && MEDIA_USB_SUPPORT && HAS_DMA depends on VIDEO_V4L2 - select INPUT_POLLDEV select VIDEOBUF2_DMA_SG help Say Y here if you want support for the Samsung SUR40 touchscreen @@ -1246,7 +1243,6 @@ config TOUCHSCREEN_SX8654 config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C - select INPUT_POLLDEV help Say Y here if you have a TPS6507x based touchscreen controller. diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index 28644f372bd8..c0d5c2413356 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -13,7 +13,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> -#define AR1021_TOCUH_PKG_SIZE 5 +#define AR1021_TOUCH_PKG_SIZE 5 #define AR1021_MAX_X 4095 #define AR1021_MAX_Y 4095 @@ -25,7 +25,7 @@ struct ar1021_i2c { struct i2c_client *client; struct input_dev *input; - u8 data[AR1021_TOCUH_PKG_SIZE]; + u8 data[AR1021_TOUCH_PKG_SIZE]; }; static irqreturn_t ar1021_i2c_irq(int irq, void *dev_id) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 24c4b691b1c9..ae60442efda0 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3156,6 +3156,8 @@ static int __maybe_unused mxt_suspend(struct device *dev) mutex_unlock(&input_dev->mutex); + disable_irq(data->irq); + return 0; } @@ -3168,6 +3170,8 @@ static int __maybe_unused mxt_resume(struct device *dev) if (!input_dev) return 0; + enable_irq(data->irq); + mutex_lock(&input_dev->mutex); if (input_dev->users) diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c index 0e40897949bb..aa829725ded7 100644 --- a/drivers/input/touchscreen/colibri-vf50-ts.c +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -9,7 +9,6 @@ #include <linux/delay.h> #include <linux/err.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/iio/consumer.h> #include <linux/iio/types.h> diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 5525f1fb1526..d61731c0037d 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -28,6 +28,7 @@ #include <linux/input/mt.h> #include <linux/input/touchscreen.h> #include <asm/unaligned.h> +#include <linux/regulator/consumer.h> #define WORK_REGISTER_THRESHOLD 0x00 #define WORK_REGISTER_REPORT_RATE 0x08 @@ -88,6 +89,7 @@ struct edt_ft5x06_ts_data { struct touchscreen_properties prop; u16 num_x; u16 num_y; + struct regulator *vcc; struct gpio_desc *reset_gpio; struct gpio_desc *wake_gpio; @@ -1036,6 +1038,13 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) } } +static void edt_ft5x06_disable_regulator(void *arg) +{ + struct edt_ft5x06_ts_data *data = arg; + + regulator_disable(data->vcc); +} + static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1064,6 +1073,27 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->max_support_points = chip_data->max_support_points; + tsdata->vcc = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(tsdata->vcc)) { + error = PTR_ERR(tsdata->vcc); + if (error != -EPROBE_DEFER) + dev_err(&client->dev, + "failed to request regulator: %d\n", error); + return error; + } + + error = regulator_enable(tsdata->vcc); + if (error < 0) { + dev_err(&client->dev, "failed to enable vcc: %d\n", error); + return error; + } + + error = devm_add_action_or_reset(&client->dev, + edt_ft5x06_disable_regulator, + tsdata); + if (error) + return error; + tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(tsdata->reset_gpio)) { diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c index e9006407c9bc..4a17096e83e1 100644 --- a/drivers/input/touchscreen/ili210x.c +++ b/drivers/input/touchscreen/ili210x.c @@ -1,54 +1,54 @@ // SPDX-License-Identifier: GPL-2.0-only -#include <linux/module.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> -#include <linux/interrupt.h> -#include <linux/slab.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -#include <linux/delay.h> -#include <linux/workqueue.h> -#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> #include <linux/of_device.h> +#include <linux/sizes.h> +#include <linux/slab.h> #include <asm/unaligned.h> -#define ILI210X_TOUCHES 2 -#define ILI251X_TOUCHES 10 -#define DEFAULT_POLL_PERIOD 20 +#define ILI2XXX_POLL_PERIOD 20 + +#define ILI210X_DATA_SIZE 64 +#define ILI211X_DATA_SIZE 43 +#define ILI251X_DATA_SIZE1 31 +#define ILI251X_DATA_SIZE2 20 /* Touchscreen commands */ #define REG_TOUCHDATA 0x10 #define REG_PANEL_INFO 0x20 -#define REG_FIRMWARE_VERSION 0x40 #define REG_CALIBRATE 0xcc -struct firmware_version { - u8 id; - u8 major; - u8 minor; -} __packed; - -enum ili2xxx_model { - MODEL_ILI210X, - MODEL_ILI251X, +struct ili2xxx_chip { + int (*read_reg)(struct i2c_client *client, u8 reg, + void *buf, size_t len); + int (*get_touch_data)(struct i2c_client *client, u8 *data); + bool (*parse_touch_data)(const u8 *data, unsigned int finger, + unsigned int *x, unsigned int *y); + bool (*continue_polling)(const u8 *data, bool touch); + unsigned int max_touches; + unsigned int resolution; + bool has_calibrate_reg; }; struct ili210x { struct i2c_client *client; struct input_dev *input; - unsigned int poll_period; - struct delayed_work dwork; struct gpio_desc *reset_gpio; struct touchscreen_properties prop; - enum ili2xxx_model model; - unsigned int max_touches; + const struct ili2xxx_chip *chip; + bool stop; }; -static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, - size_t len) +static int ili210x_read_reg(struct i2c_client *client, + u8 reg, void *buf, size_t len) { - struct ili210x *priv = i2c_get_clientdata(client); - struct i2c_msg msg[2] = { + struct i2c_msg msg[] = { { .addr = client->addr, .flags = 0, @@ -62,151 +62,223 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, .buf = buf, } }; + int error, ret; - if (priv->model == MODEL_ILI251X) { - if (i2c_transfer(client->adapter, msg, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } - - usleep_range(5000, 5500); - - if (i2c_transfer(client->adapter, msg + 1, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } - } else { - if (i2c_transfer(client->adapter, msg, 2) != 2) { - dev_err(&client->dev, "i2c transfer failed\n"); - return -EIO; - } + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return error; } return 0; } -static int ili210x_read(struct i2c_client *client, void *buf, size_t len) +static int ili210x_read_touch_data(struct i2c_client *client, u8 *data) { - struct i2c_msg msg = { - .addr = client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = buf, - }; + return ili210x_read_reg(client, REG_TOUCHDATA, + data, ILI210X_DATA_SIZE); +} + +static bool ili210x_touchdata_to_coords(const u8 *touchdata, + unsigned int finger, + unsigned int *x, unsigned int *y) +{ + if (touchdata[0] & BIT(finger)) + return false; + + *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); + *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); - if (i2c_transfer(client->adapter, &msg, 1) != 1) { - dev_err(&client->dev, "i2c transfer failed\n"); + return true; +} + +static bool ili210x_check_continue_polling(const u8 *data, bool touch) +{ + return data[0] & 0xf3; +} + +static const struct ili2xxx_chip ili210x_chip = { + .read_reg = ili210x_read_reg, + .get_touch_data = ili210x_read_touch_data, + .parse_touch_data = ili210x_touchdata_to_coords, + .continue_polling = ili210x_check_continue_polling, + .max_touches = 2, + .has_calibrate_reg = true, +}; + +static int ili211x_read_touch_data(struct i2c_client *client, u8 *data) +{ + s16 sum = 0; + int error; + int ret; + int i; + + ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE); + if (ret != ILI211X_DATA_SIZE) { + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return error; + } + + /* This chip uses custom checksum at the end of data */ + for (i = 0; i < ILI211X_DATA_SIZE - 1; i++) + sum = (sum + data[i]) & 0xff; + + if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) { + dev_err(&client->dev, + "CRC error (crc=0x%02x expected=0x%02x)\n", + sum, data[ILI211X_DATA_SIZE - 1]); return -EIO; } return 0; } -static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, +static bool ili211x_touchdata_to_coords(const u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= ILI210X_TOUCHES) - return false; + u32 data; - if (touchdata[0] & BIT(finger)) + data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); + if (data == 0xffffffff) /* Finger up */ return false; - *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); - *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); + *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | + touchdata[1 + (finger * 4) + 1]; + *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | + touchdata[1 + (finger * 4) + 2]; return true; } -static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata, +static bool ili211x_decline_polling(const u8 *data, bool touch) +{ + return false; +} + +static const struct ili2xxx_chip ili211x_chip = { + .read_reg = ili210x_read_reg, + .get_touch_data = ili211x_read_touch_data, + .parse_touch_data = ili211x_touchdata_to_coords, + .continue_polling = ili211x_decline_polling, + .max_touches = 10, + .resolution = 2048, +}; + +static int ili251x_read_reg(struct i2c_client *client, + u8 reg, void *buf, size_t len) +{ + int error; + int ret; + + ret = i2c_master_send(client, ®, 1); + if (ret == 1) { + usleep_range(5000, 5500); + + ret = i2c_master_recv(client, buf, len); + if (ret == len) + return 0; + } + + error = ret < 0 ? ret : -EIO; + dev_err(&client->dev, "%s failed: %d\n", __func__, error); + return ret; +} + +static int ili251x_read_touch_data(struct i2c_client *client, u8 *data) +{ + int error; + + error = ili251x_read_reg(client, REG_TOUCHDATA, + data, ILI251X_DATA_SIZE1); + if (!error && data[0] == 2) { + error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, + ILI251X_DATA_SIZE2); + if (error >= 0 && error != ILI251X_DATA_SIZE2) + error = -EIO; + } + + return error; +} + +static bool ili251x_touchdata_to_coords(const u8 *touchdata, unsigned int finger, unsigned int *x, unsigned int *y) { - if (finger >= ILI251X_TOUCHES) - return false; + u16 val; - *x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); - if (!(*x & BIT(15))) /* Touch indication */ + val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); + if (!(val & BIT(15))) /* Touch indication */ return false; - *x &= 0x3fff; + *x = val & 0x3fff; *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); return true; } +static bool ili251x_check_continue_polling(const u8 *data, bool touch) +{ + return touch; +} + +static const struct ili2xxx_chip ili251x_chip = { + .read_reg = ili251x_read_reg, + .get_touch_data = ili251x_read_touch_data, + .parse_touch_data = ili251x_touchdata_to_coords, + .continue_polling = ili251x_check_continue_polling, + .max_touches = 10, + .has_calibrate_reg = true, +}; + static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) { struct input_dev *input = priv->input; int i; - bool contact = false, touch = false; + bool contact = false, touch; unsigned int x = 0, y = 0; - for (i = 0; i < priv->max_touches; i++) { - if (priv->model == MODEL_ILI210X) { - touch = ili210x_touchdata_to_coords(priv, touchdata, - i, &x, &y); - } else if (priv->model == MODEL_ILI251X) { - touch = ili251x_touchdata_to_coords(priv, touchdata, - i, &x, &y); - if (touch) - contact = true; - } + for (i = 0; i < priv->chip->max_touches; i++) { + touch = priv->chip->parse_touch_data(touchdata, i, &x, &y); input_mt_slot(input, i); - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); - if (!touch) - continue; - touchscreen_report_pos(input, &priv->prop, x, y, - true); + if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { + touchscreen_report_pos(input, &priv->prop, x, y, true); + contact = true; + } } input_mt_report_pointer_emulation(input, false); input_sync(input); - if (priv->model == MODEL_ILI210X) - contact = touchdata[0] & 0xf3; - return contact; } -static void ili210x_work(struct work_struct *work) +static irqreturn_t ili210x_irq(int irq, void *irq_data) { - struct ili210x *priv = container_of(work, struct ili210x, - dwork.work); + struct ili210x *priv = irq_data; struct i2c_client *client = priv->client; - u8 touchdata[64] = { 0 }; + const struct ili2xxx_chip *chip = priv->chip; + u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; + bool keep_polling; bool touch; - int error = -EINVAL; - - if (priv->model == MODEL_ILI210X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, sizeof(touchdata)); - } else if (priv->model == MODEL_ILI251X) { - error = ili210x_read_reg(client, REG_TOUCHDATA, - touchdata, 31); - if (!error && touchdata[0] == 2) - error = ili210x_read(client, &touchdata[31], 20); - } - - if (error) { - dev_err(&client->dev, - "Unable to get touchdata, err = %d\n", error); - return; - } - - touch = ili210x_report_events(priv, touchdata); - - if (touch) - schedule_delayed_work(&priv->dwork, - msecs_to_jiffies(priv->poll_period)); -} + int error; -static irqreturn_t ili210x_irq(int irq, void *irq_data) -{ - struct ili210x *priv = irq_data; + do { + error = chip->get_touch_data(client, touchdata); + if (error) { + dev_err(&client->dev, + "Unable to get touch data: %d\n", error); + break; + } - schedule_delayed_work(&priv->dwork, 0); + touch = ili210x_report_events(priv, touchdata); + keep_polling = chip->continue_polling(touchdata, touch); + if (keep_polling) + msleep(ILI2XXX_POLL_PERIOD); + } while (!priv->stop && keep_polling); return IRQ_HANDLED; } @@ -242,8 +314,19 @@ static struct attribute *ili210x_attributes[] = { NULL, }; +static umode_t ili210x_calibrate_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = kobj_to_dev(kobj); + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + + return priv->chip->has_calibrate_reg; +} + static const struct attribute_group ili210x_attr_group = { .attrs = ili210x_attributes, + .is_visible = ili210x_calibrate_visible, }; static void ili210x_power_down(void *data) @@ -253,28 +336,35 @@ static void ili210x_power_down(void *data) gpiod_set_value_cansleep(reset_gpio, 1); } -static void ili210x_cancel_work(void *data) +static void ili210x_stop(void *data) { struct ili210x *priv = data; - cancel_delayed_work_sync(&priv->dwork); + /* Tell ISR to quit even if there is a contact. */ + priv->stop = true; } static int ili210x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct device *dev = &client->dev; + const struct ili2xxx_chip *chip; struct ili210x *priv; struct gpio_desc *reset_gpio; struct input_dev *input; - struct firmware_version firmware; - enum ili2xxx_model model; int error; - - model = (enum ili2xxx_model)id->driver_data; + unsigned int max_xy; dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + chip = device_get_match_data(dev); + if (!chip && id) + chip = (const struct ili2xxx_chip *)id->driver_data; + if (!chip) { + dev_err(&client->dev, "unknown device model\n"); + return -ENODEV; + } + if (client->irq <= 0) { dev_err(dev, "No IRQ!\n"); return -EINVAL; @@ -305,49 +395,39 @@ static int ili210x_i2c_probe(struct i2c_client *client, priv->client = client; priv->input = input; - priv->poll_period = DEFAULT_POLL_PERIOD; - INIT_DELAYED_WORK(&priv->dwork, ili210x_work); priv->reset_gpio = reset_gpio; - priv->model = model; - if (model == MODEL_ILI210X) - priv->max_touches = ILI210X_TOUCHES; - if (model == MODEL_ILI251X) - priv->max_touches = ILI251X_TOUCHES; - + priv->chip = chip; i2c_set_clientdata(client, priv); - /* Get firmware version */ - error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, - &firmware, sizeof(firmware)); - if (error) { - dev_err(dev, "Failed to get firmware version, err: %d\n", - error); - return error; - } - /* Setup input device */ input->name = "ILI210x Touchscreen"; input->id.bustype = BUS_I2C; - input->dev.parent = dev; /* Multi touch */ - input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0); + max_xy = (chip->resolution ?: SZ_64K) - 1; + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); touchscreen_parse_properties(input, true, &priv->prop); - input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT); - error = devm_add_action(dev, ili210x_cancel_work, priv); - if (error) + error = input_mt_init_slots(input, priv->chip->max_touches, + INPUT_MT_DIRECT); + if (error) { + dev_err(dev, "Unable to set up slots, err: %d\n", error); return error; + } - error = devm_request_irq(dev, client->irq, ili210x_irq, 0, - client->name, priv); + error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, + IRQF_ONESHOT, client->name, priv); if (error) { dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", error); return error; } + error = devm_add_action_or_reset(dev, ili210x_stop, priv); + if (error) + return error; + error = devm_device_add_group(dev, &ili210x_attr_group); if (error) { dev_err(dev, "Unable to create sysfs attributes, err: %d\n", @@ -361,56 +441,28 @@ static int ili210x_i2c_probe(struct i2c_client *client, return error; } - device_init_wakeup(dev, 1); - - dev_dbg(dev, - "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", - client->irq, firmware.id, firmware.major, firmware.minor); - return 0; } -static int __maybe_unused ili210x_i2c_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); - - return 0; -} - -static int __maybe_unused ili210x_i2c_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, - ili210x_i2c_suspend, ili210x_i2c_resume); - static const struct i2c_device_id ili210x_i2c_id[] = { - { "ili210x", MODEL_ILI210X }, - { "ili251x", MODEL_ILI251X }, + { "ili210x", (long)&ili210x_chip }, + { "ili2117", (long)&ili211x_chip }, + { "ili251x", (long)&ili251x_chip }, { } }; MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); static const struct of_device_id ili210x_dt_ids[] = { - { .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X }, - { .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X }, - { }, + { .compatible = "ilitek,ili210x", .data = &ili210x_chip }, + { .compatible = "ilitek,ili2117", .data = &ili211x_chip }, + { .compatible = "ilitek,ili251x", .data = &ili251x_chip }, + { } }; MODULE_DEVICE_TABLE(of, ili210x_dt_ids); static struct i2c_driver ili210x_ts_driver = { .driver = { .name = "ili210x_i2c", - .pm = &ili210x_i2c_pm, .of_match_table = ili210x_dt_ids, }, .id_table = ili210x_i2c_id, diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index a5ab774da4cc..69c6d559eeb0 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -446,8 +446,7 @@ static int mms114_probe(struct i2c_client *client, data->client = client; data->input_dev = input_dev; - /* FIXME: switch to device_get_match_data() when available */ - match_data = of_device_get_match_data(&client->dev); + match_data = device_get_match_data(&client->dev); if (!match_data) return -EINVAL; diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index e146dfa257b1..9aa098577350 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -5,22 +5,73 @@ * Copyright (C) 2010-2011 Pixcir, Inc. */ +#include <asm/unaligned.h> #include <linux/delay.h> -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/slab.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> #include <linux/of_device.h> -#include <linux/platform_data/pixcir_i2c_ts.h> -#include <asm/unaligned.h> +#include <linux/module.h> +#include <linux/slab.h> #define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */ +/* + * Register map + */ +#define PIXCIR_REG_POWER_MODE 51 +#define PIXCIR_REG_INT_MODE 52 + +/* + * Power modes: + * active: max scan speed + * idle: lower scan speed with automatic transition to active on touch + * halt: datasheet says sleep but this is more like halt as the chip + * clocks are cut and it can only be brought out of this mode + * using the RESET pin. + */ +enum pixcir_power_mode { + PIXCIR_POWER_ACTIVE, + PIXCIR_POWER_IDLE, + PIXCIR_POWER_HALT, +}; + +#define PIXCIR_POWER_MODE_MASK 0x03 +#define PIXCIR_POWER_ALLOW_IDLE (1UL << 2) + +/* + * Interrupt modes: + * periodical: interrupt is asserted periodicaly + * diff coordinates: interrupt is asserted when coordinates change + * level on touch: interrupt level asserted during touch + * pulse on touch: interrupt pulse asserted during touch + * + */ +enum pixcir_int_mode { + PIXCIR_INT_PERIODICAL, + PIXCIR_INT_DIFF_COORD, + PIXCIR_INT_LEVEL_TOUCH, + PIXCIR_INT_PULSE_TOUCH, +}; + +#define PIXCIR_INT_MODE_MASK 0x03 +#define PIXCIR_INT_ENABLE (1UL << 3) +#define PIXCIR_INT_POL_HIGH (1UL << 2) + +/** + * struct pixcir_i2c_chip_data - chip related data + * @max_fingers: Max number of fingers reported simultaneously by h/w + * @has_hw_ids: Hardware supports finger tracking IDs + * + */ +struct pixcir_i2c_chip_data { + u8 max_fingers; + bool has_hw_ids; +}; + struct pixcir_i2c_ts_data { struct i2c_client *client; struct input_dev *input; @@ -30,7 +81,6 @@ struct pixcir_i2c_ts_data { struct gpio_desc *gpio_wake; const struct pixcir_i2c_chip_data *chip; struct touchscreen_properties prop; - int max_fingers; /* Max fingers supported in this instance */ bool running; }; @@ -54,7 +104,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, memset(report, 0, sizeof(struct pixcir_report_data)); i = chip->has_hw_ids ? 1 : 0; - readsize = 2 + tsdata->max_fingers * (4 + i); + readsize = 2 + tsdata->chip->max_fingers * (4 + i); if (readsize > sizeof(rdbuf)) readsize = sizeof(rdbuf); @@ -75,8 +125,8 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata, } touch = rdbuf[0] & 0x7; - if (touch > tsdata->max_fingers) - touch = tsdata->max_fingers; + if (touch > tsdata->chip->max_fingers) + touch = tsdata->chip->max_fingers; report->num_touches = touch; bufptr = &rdbuf[2]; @@ -192,7 +242,7 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE); if (ret < 0) { - dev_err(dev, "%s: can't read reg 0x%x : %d\n", + dev_err(dev, "%s: can't read reg %d : %d\n", __func__, PIXCIR_REG_POWER_MODE, ret); return ret; } @@ -205,7 +255,7 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts, ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret); if (ret < 0) { - dev_err(dev, "%s: can't write reg 0x%x : %d\n", + dev_err(dev, "%s: can't write reg %d : %d\n", __func__, PIXCIR_REG_POWER_MODE, ret); return ret; } @@ -231,7 +281,7 @@ static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts, ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); if (ret < 0) { - dev_err(dev, "%s: can't read reg 0x%x : %d\n", + dev_err(dev, "%s: can't read reg %d : %d\n", __func__, PIXCIR_REG_INT_MODE, ret); return ret; } @@ -246,7 +296,7 @@ static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts, ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); if (ret < 0) { - dev_err(dev, "%s: can't write reg 0x%x : %d\n", + dev_err(dev, "%s: can't write reg %d : %d\n", __func__, PIXCIR_REG_INT_MODE, ret); return ret; } @@ -264,7 +314,7 @@ static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable) ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE); if (ret < 0) { - dev_err(dev, "%s: can't read reg 0x%x : %d\n", + dev_err(dev, "%s: can't read reg %d : %d\n", __func__, PIXCIR_REG_INT_MODE, ret); return ret; } @@ -276,7 +326,7 @@ static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable) ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret); if (ret < 0) { - dev_err(dev, "%s: can't write reg 0x%x : %d\n", + dev_err(dev, "%s: can't write reg %d : %d\n", __func__, PIXCIR_REG_INT_MODE, ret); return ret; } @@ -412,31 +462,9 @@ unlock: static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops, pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume); -#ifdef CONFIG_OF -static const struct of_device_id pixcir_of_match[]; - -static int pixcir_parse_dt(struct device *dev, - struct pixcir_i2c_ts_data *tsdata) -{ - tsdata->chip = of_device_get_match_data(dev); - if (!tsdata->chip) - return -EINVAL; - - return 0; -} -#else -static int pixcir_parse_dt(struct device *dev, - struct pixcir_i2c_ts_data *tsdata) -{ - return -EINVAL; -} -#endif - static int pixcir_i2c_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct pixcir_ts_platform_data *pdata = - dev_get_platdata(&client->dev); struct device *dev = &client->dev; struct pixcir_i2c_ts_data *tsdata; struct input_dev *input; @@ -446,19 +474,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, if (!tsdata) return -ENOMEM; - if (pdata) { - tsdata->chip = &pdata->chip; - } else if (dev->of_node) { - error = pixcir_parse_dt(dev, tsdata); - if (error) - return error; - } else { - dev_err(dev, "platform data not defined\n"); - return -EINVAL; - } - - if (!tsdata->chip->max_fingers) { - dev_err(dev, "Invalid max_fingers in chip data\n"); + tsdata->chip = device_get_match_data(dev); + if (!tsdata->chip && id) + tsdata->chip = (const void *)id->driver_data; + if (!tsdata->chip) { + dev_err(dev, "can't locate chip data\n"); return -EINVAL; } @@ -475,30 +495,17 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, input->id.bustype = BUS_I2C; input->open = pixcir_input_open; input->close = pixcir_input_close; - input->dev.parent = dev; - - if (pdata) { - input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0); - } else { - input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); - input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); - touchscreen_parse_properties(input, true, &tsdata->prop); - if (!input_abs_get_max(input, ABS_MT_POSITION_X) || - !input_abs_get_max(input, ABS_MT_POSITION_Y)) { - dev_err(dev, "Touchscreen size is not specified\n"); - return -EINVAL; - } - } - tsdata->max_fingers = tsdata->chip->max_fingers; - if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) { - tsdata->max_fingers = PIXCIR_MAX_SLOTS; - dev_info(dev, "Limiting maximum fingers to %d\n", - tsdata->max_fingers); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); + touchscreen_parse_properties(input, true, &tsdata->prop); + if (!input_abs_get_max(input, ABS_MT_POSITION_X) || + !input_abs_get_max(input, ABS_MT_POSITION_Y)) { + dev_err(dev, "Touchscreen size is not specified\n"); + return -EINVAL; } - error = input_mt_init_slots(input, tsdata->max_fingers, + error = input_mt_init_slots(input, tsdata->chip->max_fingers, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); if (error) { dev_err(dev, "Error initializing Multi-Touch slots\n"); @@ -510,7 +517,9 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN); if (IS_ERR(tsdata->gpio_attb)) { error = PTR_ERR(tsdata->gpio_attb); - dev_err(dev, "Failed to request ATTB gpio: %d\n", error); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to request ATTB gpio: %d\n", + error); return error; } @@ -518,7 +527,9 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, GPIOD_OUT_LOW); if (IS_ERR(tsdata->gpio_reset)) { error = PTR_ERR(tsdata->gpio_reset); - dev_err(dev, "Failed to request RESET gpio: %d\n", error); + if (error != -EPROBE_DEFER) + dev_err(dev, "Failed to request RESET gpio: %d\n", + error); return error; } @@ -574,14 +585,6 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client, return 0; } -static const struct i2c_device_id pixcir_i2c_ts_id[] = { - { "pixcir_ts", 0 }, - { "pixcir_tangoc", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); - -#ifdef CONFIG_OF static const struct pixcir_i2c_chip_data pixcir_ts_data = { .max_fingers = 2, /* no hw id support */ @@ -592,6 +595,14 @@ static const struct pixcir_i2c_chip_data pixcir_tangoc_data = { .has_hw_ids = true, }; +static const struct i2c_device_id pixcir_i2c_ts_id[] = { + { "pixcir_ts", (unsigned long) &pixcir_ts_data }, + { "pixcir_tangoc", (unsigned long) &pixcir_tangoc_data }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id); + +#ifdef CONFIG_OF static const struct of_device_id pixcir_of_match[] = { { .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data }, { .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data }, diff --git a/drivers/input/touchscreen/raspberrypi-ts.c b/drivers/input/touchscreen/raspberrypi-ts.c index 69881265d121..0e2e08f3f433 100644 --- a/drivers/input/touchscreen/raspberrypi-ts.c +++ b/drivers/input/touchscreen/raspberrypi-ts.c @@ -16,7 +16,6 @@ #include <linux/platform_device.h> #include <linux/input.h> #include <linux/input/mt.h> -#include <linux/input-polldev.h> #include <linux/input/touchscreen.h> #include <soc/bcm2835/raspberrypi-firmware.h> @@ -34,7 +33,7 @@ struct rpi_ts { struct platform_device *pdev; - struct input_polled_dev *poll_dev; + struct input_dev *input; struct touchscreen_properties prop; void __iomem *fw_regs_va; @@ -57,10 +56,9 @@ struct rpi_ts_regs { } point[RPI_TS_MAX_SUPPORTED_POINTS]; }; -static void rpi_ts_poll(struct input_polled_dev *dev) +static void rpi_ts_poll(struct input_dev *input) { - struct input_dev *input = dev->input; - struct rpi_ts *ts = dev->private; + struct rpi_ts *ts = input_get_drvdata(input); struct rpi_ts_regs regs; int modified_ids = 0; long released_ids; @@ -123,10 +121,9 @@ static int rpi_ts_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct input_polled_dev *poll_dev; + struct input_dev *input; struct device_node *fw_node; struct rpi_firmware *fw; - struct input_dev *input; struct rpi_ts *ts; u32 touchbuf; int error; @@ -160,7 +157,6 @@ static int rpi_ts_probe(struct platform_device *pdev) return error; } - touchbuf = (u32)ts->fw_regs_phys; error = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF, &touchbuf, sizeof(touchbuf)); @@ -170,19 +166,17 @@ static int rpi_ts_probe(struct platform_device *pdev) return error; } - poll_dev = devm_input_allocate_polled_device(dev); - if (!poll_dev) { + input = devm_input_allocate_device(dev); + if (!input) { dev_err(dev, "Failed to allocate input device\n"); return -ENOMEM; } - ts->poll_dev = poll_dev; - input = poll_dev->input; + + ts->input = input; + input_set_drvdata(input, ts); input->name = "raspberrypi-ts"; input->id.bustype = BUS_HOST; - poll_dev->poll_interval = RPI_TS_POLL_INTERVAL; - poll_dev->poll = rpi_ts_poll; - poll_dev->private = ts; input_set_abs_params(input, ABS_MT_POSITION_X, 0, RPI_TS_DEFAULT_WIDTH, 0, 0); @@ -197,7 +191,15 @@ static int rpi_ts_probe(struct platform_device *pdev) return error; } - error = input_register_polled_device(poll_dev); + error = input_setup_polling(input, rpi_ts_poll); + if (error) { + dev_err(dev, "could not set up polling mode, %d\n", error); + return error; + } + + input_set_poll_interval(input, RPI_TS_POLL_INTERVAL); + + error = input_register_device(input); if (error) { dev_err(dev, "could not register input device, %d\n", error); return error; @@ -214,10 +216,10 @@ MODULE_DEVICE_TABLE(of, rpi_ts_match); static struct platform_driver rpi_ts_driver = { .driver = { - .name = "raspberrypi-ts", + .name = "raspberrypi-ts", .of_match_table = rpi_ts_match, }, - .probe = rpi_ts_probe, + .probe = rpi_ts_probe, }; module_platform_driver(rpi_ts_driver); diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index b346e7cafd62..82920ff46f72 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -13,7 +13,6 @@ #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/gpio.h> #include <linux/input.h> #include <linux/delay.h> #include <linux/interrupt.h> diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index 1139714e72e2..63b29c7279e2 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -14,23 +14,19 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pm_qos.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/input/touchscreen.h> #define ST1232_TS_NAME "st1232-ts" #define ST1633_TS_NAME "st1633-ts" -struct st1232_ts_finger { - u16 x; - u16 y; - u8 t; - bool is_valid; -}; +#define ST_TS_MAX_FINGERS 10 struct st_chip_info { bool have_z; @@ -50,81 +46,89 @@ struct st1232_ts_data { const struct st_chip_info *chip_info; int read_buf_len; u8 *read_buf; - struct st1232_ts_finger *finger; }; static int st1232_ts_read_data(struct st1232_ts_data *ts) { - struct st1232_ts_finger *finger = ts->finger; struct i2c_client *client = ts->client; - struct i2c_msg msg[2]; - int error; - int i, y; u8 start_reg = ts->chip_info->start_reg; - u8 *buf = ts->read_buf; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .len = sizeof(start_reg), + .buf = &start_reg, + }, + { + .addr = client->addr, + .flags = I2C_M_RD | I2C_M_DMA_SAFE, + .len = ts->read_buf_len, + .buf = ts->read_buf, + } + }; + int ret; - /* read touchscreen data */ - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = &start_reg; + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) + return ret < 0 ? ret : -EIO; - msg[1].addr = ts->client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = ts->read_buf_len; - msg[1].buf = buf; + return 0; +} - error = i2c_transfer(client->adapter, msg, 2); - if (error < 0) - return error; +static int st1232_ts_parse_and_report(struct st1232_ts_data *ts) +{ + struct input_dev *input = ts->input_dev; + struct input_mt_pos pos[ST_TS_MAX_FINGERS]; + u8 z[ST_TS_MAX_FINGERS]; + int slots[ST_TS_MAX_FINGERS]; + int n_contacts = 0; + int i; + + for (i = 0; i < ts->chip_info->max_fingers; i++) { + u8 *buf = &ts->read_buf[i * 4]; + + if (buf[0] & BIT(7)) { + unsigned int x = ((buf[0] & 0x70) << 4) | buf[1]; + unsigned int y = ((buf[0] & 0x07) << 8) | buf[2]; - for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) { - finger[i].is_valid = buf[i + y] >> 7; - if (finger[i].is_valid) { - finger[i].x = ((buf[i + y] & 0x0070) << 4) | - buf[i + y + 1]; - finger[i].y = ((buf[i + y] & 0x0007) << 8) | - buf[i + y + 2]; + touchscreen_set_mt_pos(&pos[n_contacts], + &ts->prop, x, y); /* st1232 includes a z-axis / touch strength */ if (ts->chip_info->have_z) - finger[i].t = buf[i + 6]; + z[n_contacts] = ts->read_buf[i + 6]; + + n_contacts++; } } - return 0; + input_mt_assign_slots(input, slots, pos, n_contacts, 0); + for (i = 0; i < n_contacts; i++) { + input_mt_slot(input, slots[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, pos[i].x); + input_report_abs(input, ABS_MT_POSITION_Y, pos[i].y); + if (ts->chip_info->have_z) + input_report_abs(input, ABS_MT_TOUCH_MAJOR, z[i]); + } + + input_mt_sync_frame(input); + input_sync(input); + + return n_contacts; } static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) { struct st1232_ts_data *ts = dev_id; - struct st1232_ts_finger *finger = ts->finger; - struct input_dev *input_dev = ts->input_dev; - int count = 0; - int i, ret; - - ret = st1232_ts_read_data(ts); - if (ret < 0) - goto end; - - /* multi touch protocol */ - for (i = 0; i < ts->chip_info->max_fingers; i++) { - if (!finger[i].is_valid) - continue; - - if (ts->chip_info->have_z) - input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, - finger[i].t); + int count; + int error; - touchscreen_report_pos(input_dev, &ts->prop, - finger[i].x, finger[i].y, true); - input_mt_sync(input_dev); - count++; - } + error = st1232_ts_read_data(ts); + if (error) + goto out; - /* SYN_MT_REPORT only if no contact */ + count = st1232_ts_parse_and_report(ts); if (!count) { - input_mt_sync(input_dev); if (ts->low_latency_req.dev) { dev_pm_qos_remove_request(&ts->low_latency_req); ts->low_latency_req.dev = NULL; @@ -136,10 +140,7 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id) DEV_PM_QOS_RESUME_LATENCY, 100); } - /* SYN_REPORT */ - input_sync(input_dev); - -end: +out: return IRQ_HANDLED; } @@ -149,6 +150,11 @@ static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) gpiod_set_value_cansleep(ts->reset_gpio, !poweron); } +static void st1232_ts_power_off(void *data) +{ + st1232_ts_power(data, false); +} + static const struct st_chip_info st1232_chip_info = { .have_z = true, .max_x = 0x31f, /* 800 - 1 */ @@ -172,7 +178,6 @@ static int st1232_ts_probe(struct i2c_client *client, { const struct st_chip_info *match; struct st1232_ts_data *ts; - struct st1232_ts_finger *finger; struct input_dev *input_dev; int error; @@ -199,11 +204,6 @@ static int st1232_ts_probe(struct i2c_client *client, return -ENOMEM; ts->chip_info = match; - ts->finger = devm_kcalloc(&client->dev, - ts->chip_info->max_fingers, sizeof(*finger), - GFP_KERNEL); - if (!ts->finger) - return -ENOMEM; /* allocate a buffer according to the number of registers to read */ ts->read_buf_len = ts->chip_info->max_fingers * 4; @@ -229,14 +229,15 @@ static int st1232_ts_probe(struct i2c_client *client, st1232_ts_power(ts, true); + error = devm_add_action_or_reset(&client->dev, st1232_ts_power_off, ts); + if (error) { + dev_err(&client->dev, + "Failed to install power off action: %d\n", error); + return error; + } + input_dev->name = "st1232-touchscreen"; input_dev->id.bustype = BUS_I2C; - input_dev->dev.parent = &client->dev; - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - __set_bit(EV_SYN, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_ABS, input_dev->evbit); if (ts->chip_info->have_z) input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, @@ -249,6 +250,14 @@ static int st1232_ts_probe(struct i2c_client *client, touchscreen_parse_properties(input_dev, true, &ts->prop); + error = input_mt_init_slots(input_dev, ts->chip_info->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_TRACK | + INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(&client->dev, "failed to initialize MT slots\n"); + return error; + } + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, st1232_ts_irq_handler, IRQF_ONESHOT, @@ -266,16 +275,6 @@ static int st1232_ts_probe(struct i2c_client *client, } i2c_set_clientdata(client, ts); - device_init_wakeup(&client->dev, 1); - - return 0; -} - -static int st1232_ts_remove(struct i2c_client *client) -{ - struct st1232_ts_data *ts = i2c_get_clientdata(client); - - st1232_ts_power(ts, false); return 0; } @@ -285,12 +284,10 @@ static int __maybe_unused st1232_ts_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct st1232_ts_data *ts = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) { - enable_irq_wake(client->irq); - } else { - disable_irq(client->irq); + disable_irq(client->irq); + + if (!device_may_wakeup(&client->dev)) st1232_ts_power(ts, false); - } return 0; } @@ -300,12 +297,10 @@ static int __maybe_unused st1232_ts_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct st1232_ts_data *ts = i2c_get_clientdata(client); - if (device_may_wakeup(&client->dev)) { - disable_irq_wake(client->irq); - } else { + if (!device_may_wakeup(&client->dev)) st1232_ts_power(ts, true); - enable_irq(client->irq); - } + + enable_irq(client->irq); return 0; } @@ -329,7 +324,6 @@ MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); static struct i2c_driver st1232_ts_driver = { .probe = st1232_ts_probe, - .remove = st1232_ts_remove, .id_table = st1232_ts_id, .driver = { .name = ST1232_TS_NAME, diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index 3fd3e862269b..1dd47dda71cd 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -27,7 +27,7 @@ #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/printk.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/input/mt.h> #include <linux/usb/input.h> #include <linux/videodev2.h> @@ -206,7 +206,7 @@ struct sur40_state { struct usb_device *usbdev; struct device *dev; - struct input_polled_dev *input; + struct input_dev *input; struct v4l2_device v4l2; struct video_device vdev; @@ -370,6 +370,10 @@ static int sur40_init(struct sur40_state *dev) goto error; result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12); + if (result < 0) + goto error; + + result = 0; /* * Discard the result buffer - no known data inside except @@ -381,22 +385,22 @@ error: } /* - * Callback routines from input_polled_dev + * Callback routines from input_dev */ /* Enable the device, polling will now start. */ -static void sur40_open(struct input_polled_dev *polldev) +static int sur40_open(struct input_dev *input) { - struct sur40_state *sur40 = polldev->private; + struct sur40_state *sur40 = input_get_drvdata(input); dev_dbg(sur40->dev, "open\n"); - sur40_init(sur40); + return sur40_init(sur40); } /* Disable device, polling has stopped. */ -static void sur40_close(struct input_polled_dev *polldev) +static void sur40_close(struct input_dev *input) { - struct sur40_state *sur40 = polldev->private; + struct sur40_state *sur40 = input_get_drvdata(input); dev_dbg(sur40->dev, "close\n"); /* @@ -448,10 +452,9 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input) } /* core function: poll for new input data */ -static void sur40_poll(struct input_polled_dev *polldev) +static void sur40_poll(struct input_dev *input) { - struct sur40_state *sur40 = polldev->private; - struct input_dev *input = polldev->input; + struct sur40_state *sur40 = input_get_drvdata(input); int result, bulk_read, need_blobs, packet_blobs, i; u32 uninitialized_var(packet_id); @@ -613,10 +616,9 @@ err_poll: } /* Initialize input device parameters. */ -static void sur40_input_setup(struct input_dev *input_dev) +static int sur40_input_setup_events(struct input_dev *input_dev) { - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_ABS, input_dev->evbit); + int error; input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, SENSOR_RES_X, 0, 0); @@ -637,8 +639,14 @@ static void sur40_input_setup(struct input_dev *input_dev) input_set_abs_params(input_dev, ABS_MT_ORIENTATION, 0, 1, 0, 0); - input_mt_init_slots(input_dev, MAX_CONTACTS, - INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + error = input_mt_init_slots(input_dev, MAX_CONTACTS, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(input_dev->dev.parent, "failed to set up slots\n"); + return error; + } + + return 0; } /* Check candidate USB interface. */ @@ -649,7 +657,7 @@ static int sur40_probe(struct usb_interface *interface, struct sur40_state *sur40; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - struct input_polled_dev *poll_dev; + struct input_dev *input; int error; /* Check if we really have the right interface. */ @@ -670,8 +678,8 @@ static int sur40_probe(struct usb_interface *interface, if (!sur40) return -ENOMEM; - poll_dev = input_allocate_polled_device(); - if (!poll_dev) { + input = input_allocate_device(); + if (!input) { error = -ENOMEM; goto err_free_dev; } @@ -681,26 +689,33 @@ static int sur40_probe(struct usb_interface *interface, spin_lock_init(&sur40->qlock); mutex_init(&sur40->lock); - /* Set up polled input device control structure */ - poll_dev->private = sur40; - poll_dev->poll_interval = POLL_INTERVAL; - poll_dev->open = sur40_open; - poll_dev->poll = sur40_poll; - poll_dev->close = sur40_close; - /* Set up regular input device structure */ - sur40_input_setup(poll_dev->input); - - poll_dev->input->name = DRIVER_LONG; - usb_to_input_id(usbdev, &poll_dev->input->id); + input->name = DRIVER_LONG; + usb_to_input_id(usbdev, &input->id); usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys)); strlcat(sur40->phys, "/input0", sizeof(sur40->phys)); - poll_dev->input->phys = sur40->phys; - poll_dev->input->dev.parent = &interface->dev; + input->phys = sur40->phys; + input->dev.parent = &interface->dev; + + input->open = sur40_open; + input->close = sur40_close; + + error = sur40_input_setup_events(input); + if (error) + goto err_free_input; + + input_set_drvdata(input, sur40); + error = input_setup_polling(input, sur40_poll); + if (error) { + dev_err(&interface->dev, "failed to set up polling"); + goto err_free_input; + } + + input_set_poll_interval(input, POLL_INTERVAL); sur40->usbdev = usbdev; sur40->dev = &interface->dev; - sur40->input = poll_dev; + sur40->input = input; /* use the bulk-in endpoint tested above */ sur40->bulk_in_size = usb_endpoint_maxp(endpoint); @@ -709,11 +724,11 @@ static int sur40_probe(struct usb_interface *interface, if (!sur40->bulk_in_buffer) { dev_err(&interface->dev, "Unable to allocate input buffer."); error = -ENOMEM; - goto err_free_polldev; + goto err_free_input; } /* register the polled input device */ - error = input_register_polled_device(poll_dev); + error = input_register_device(input); if (error) { dev_err(&interface->dev, "Unable to register polled input device."); @@ -796,8 +811,8 @@ err_unreg_v4l2: v4l2_device_unregister(&sur40->v4l2); err_free_buffer: kfree(sur40->bulk_in_buffer); -err_free_polldev: - input_free_polled_device(sur40->input); +err_free_input: + input_free_device(input); err_free_dev: kfree(sur40); @@ -813,8 +828,7 @@ static void sur40_disconnect(struct usb_interface *interface) video_unregister_device(&sur40->vdev); v4l2_device_unregister(&sur40->v4l2); - input_unregister_polled_device(sur40->input); - input_free_polled_device(sur40->input); + input_unregister_device(sur40->input); kfree(sur40->bulk_in_buffer); kfree(sur40); diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 75170a7439b1..357a3108f2e5 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -17,7 +17,6 @@ #include <linux/workqueue.h> #include <linux/slab.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/platform_device.h> #include <linux/mfd/tps6507x.h> #include <linux/input/tps6507x-ts.h> @@ -40,7 +39,7 @@ struct ts_event { struct tps6507x_ts { struct device *dev; - struct input_polled_dev *poll_dev; + struct input_dev *input; struct tps6507x_dev *mfd; char phys[32]; struct ts_event tc; @@ -148,10 +147,9 @@ static s32 tps6507x_adc_standby(struct tps6507x_ts *tsc) return ret; } -static void tps6507x_ts_poll(struct input_polled_dev *poll_dev) +static void tps6507x_ts_poll(struct input_dev *input_dev) { - struct tps6507x_ts *tsc = poll_dev->private; - struct input_dev *input_dev = poll_dev->input; + struct tps6507x_ts *tsc = input_get_drvdata(input_dev); bool pendown; s32 ret; @@ -205,7 +203,6 @@ static int tps6507x_ts_probe(struct platform_device *pdev) const struct tps6507x_board *tps_board; const struct touchscreen_init_data *init_data; struct tps6507x_ts *tsc; - struct input_polled_dev *poll_dev; struct input_dev *input_dev; int error; @@ -240,23 +237,16 @@ static int tps6507x_ts_probe(struct platform_device *pdev) snprintf(tsc->phys, sizeof(tsc->phys), "%s/input0", dev_name(tsc->dev)); - poll_dev = devm_input_allocate_polled_device(&pdev->dev); - if (!poll_dev) { + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { dev_err(tsc->dev, "Failed to allocate polled input device.\n"); return -ENOMEM; } - tsc->poll_dev = poll_dev; - - poll_dev->private = tsc; - poll_dev->poll = tps6507x_ts_poll; - poll_dev->poll_interval = init_data ? - init_data->poll_period : TSC_DEFAULT_POLL_PERIOD; - - input_dev = poll_dev->input; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + tsc->input = input_dev; + input_set_drvdata(input_dev, tsc); + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, 0, MAX_10BIT, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX_10BIT, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_10BIT, 0, 0); @@ -275,7 +265,15 @@ static int tps6507x_ts_probe(struct platform_device *pdev) if (error) return error; - error = input_register_polled_device(poll_dev); + error = input_setup_polling(input_dev, tps6507x_ts_poll); + if (error) + return error; + + input_set_poll_interval(input_dev, + init_data ? init_data->poll_period : + TSC_DEFAULT_POLL_PERIOD); + + error = input_register_device(input_dev); if (error) return error; diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c index 5b4f5362c67b..6cf66aadc10e 100644 --- a/drivers/input/touchscreen/ts4800-ts.c +++ b/drivers/input/touchscreen/ts4800-ts.c @@ -10,7 +10,6 @@ #include <linux/bitops.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> @@ -33,7 +32,7 @@ #define Y_OFFSET 0x2 struct ts4800_ts { - struct input_polled_dev *poll_dev; + struct input_dev *input; struct device *dev; char phys[32]; @@ -46,22 +45,26 @@ struct ts4800_ts { int debounce; }; -static void ts4800_ts_open(struct input_polled_dev *dev) +static int ts4800_ts_open(struct input_dev *input_dev) { - struct ts4800_ts *ts = dev->private; - int ret; + struct ts4800_ts *ts = input_get_drvdata(input_dev); + int error; ts->pendown = false; ts->debounce = DEBOUNCE_COUNT; - ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); - if (ret) - dev_warn(ts->dev, "Failed to enable touchscreen\n"); + error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); + if (error) { + dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error); + return error; + } + + return 0; } -static void ts4800_ts_close(struct input_polled_dev *dev) +static void ts4800_ts_close(struct input_dev *input_dev) { - struct ts4800_ts *ts = dev->private; + struct ts4800_ts *ts = input_get_drvdata(input_dev); int ret; ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); @@ -70,10 +73,9 @@ static void ts4800_ts_close(struct input_polled_dev *dev) } -static void ts4800_ts_poll(struct input_polled_dev *dev) +static void ts4800_ts_poll(struct input_dev *input_dev) { - struct input_dev *input_dev = dev->input; - struct ts4800_ts *ts = dev->private; + struct ts4800_ts *ts = input_get_drvdata(input_dev); u16 last_x = readw(ts->base + X_OFFSET); u16 last_y = readw(ts->base + Y_OFFSET); bool pendown = last_x & PENDOWN_MASK; @@ -146,7 +148,7 @@ static int ts4800_parse_dt(struct platform_device *pdev, static int ts4800_ts_probe(struct platform_device *pdev) { - struct input_polled_dev *poll_dev; + struct input_dev *input_dev; struct ts4800_ts *ts; int error; @@ -162,32 +164,38 @@ static int ts4800_ts_probe(struct platform_device *pdev) if (IS_ERR(ts->base)) return PTR_ERR(ts->base); - poll_dev = devm_input_allocate_polled_device(&pdev->dev); - if (!poll_dev) + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) return -ENOMEM; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); - ts->poll_dev = poll_dev; + ts->input = input_dev; ts->dev = &pdev->dev; - poll_dev->private = ts; - poll_dev->poll_interval = POLL_INTERVAL; - poll_dev->open = ts4800_ts_open; - poll_dev->close = ts4800_ts_close; - poll_dev->poll = ts4800_ts_poll; + input_set_drvdata(input_dev, ts); + + input_dev->name = "TS-4800 Touchscreen"; + input_dev->phys = ts->phys; + + input_dev->open = ts4800_ts_open; + input_dev->close = ts4800_ts_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - poll_dev->input->name = "TS-4800 Touchscreen"; - poll_dev->input->phys = ts->phys; + error = input_setup_polling(input_dev, ts4800_ts_poll); + if (error) { + dev_err(&pdev->dev, "Unable to set up polling: %d\n", error); + return error; + } - input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH); - input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0); - input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_poll_interval(input_dev, POLL_INTERVAL); - error = input_register_polled_device(poll_dev); + error = input_register_device(input_dev); if (error) { dev_err(&pdev->dev, - "Unabled to register polled input device (%d)\n", - error); + "Unable to register input device: %d\n", error); return error; } diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index f017af8c2aa3..1afc6bde2891 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -12,7 +12,6 @@ #include <linux/slab.h> #include <linux/irq.h> #include <linux/interrupt.h> -#include <linux/gpio.h> #include <asm/unaligned.h> #define WACOM_CMD_QUERY0 0x04 diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index e3842eabcfdd..f1086eaed41c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -467,7 +467,7 @@ config QCOM_IOMMU config HYPERV_IOMMU bool "Hyper-V x2APIC IRQ Handling" - depends on HYPERV + depends on HYPERV && X86 select IOMMU_API default HYPERV help diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index ba8619524231..1675da34239b 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -950,6 +950,34 @@ capi_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return ret; } +#ifdef CONFIG_COMPAT +static long +capi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + + if (cmd == CAPI_MANUFACTURER_CMD) { + struct { + compat_ulong_t cmd; + compat_uptr_t data; + } mcmd32; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&mcmd32, compat_ptr(arg), sizeof(mcmd32))) + return -EFAULT; + + mutex_lock(&capi_mutex); + ret = capi20_manufacturer(mcmd32.cmd, compat_ptr(mcmd32.data)); + mutex_unlock(&capi_mutex); + + return ret; + } + + return capi_unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + static int capi_open(struct inode *inode, struct file *file) { struct capidev *cdev; @@ -996,6 +1024,9 @@ static const struct file_operations capi_fops = .write = capi_write, .poll = capi_poll, .unlocked_ioctl = capi_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = capi_compat_ioctl, +#endif .open = capi_open, .release = capi_release, }; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1988de1d64c0..4b68520ac251 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -17,7 +17,7 @@ if NEW_LEDS config LEDS_CLASS tristate "LED Class Support" help - This option enables the led sysfs class in /sys/class/leds. You'll + This option enables the LED sysfs class in /sys/class/leds. You'll need this to do anything useful with LEDs. If unsure, say N. config LEDS_CLASS_FLASH @@ -35,7 +35,7 @@ config LEDS_BRIGHTNESS_HW_CHANGED depends on LEDS_CLASS help This option enables support for the brightness_hw_changed attribute - for led sysfs class devices under /sys/class/leds. + for LED sysfs class devices under /sys/class/leds. See Documentation/ABI/testing/sysfs-class-led for details. @@ -132,6 +132,19 @@ config LEDS_CR0014114 To compile this driver as a module, choose M here: the module will be called leds-cr0014114. +config LEDS_EL15203000 + tristate "LED Support for Crane EL15203000" + depends on LEDS_CLASS + depends on SPI + depends on OF + help + This option enables support for EL15203000 LED Board + (aka RED LED board) which is widely used in coffee vending + machines produced by Crane Merchandising Systems. + + To compile this driver as a module, choose M here: the module + will be called leds-el15203000. + config LEDS_LM3530 tristate "LCD Backlight driver for LM3530" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 41fb073a39c1..2da39e896ce8 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_LEDS_LM36274) += leds-lm36274.o # LED SPI Drivers obj-$(CONFIG_LEDS_CR0014114) += leds-cr0014114.o obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o +obj-$(CONFIG_LEDS_EL15203000) += leds-el15203000.o # LED Userspace Drivers obj-$(CONFIG_LEDS_USER) += uleds.o diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 60c3de5c6b9f..6eeb9effcf65 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -327,6 +327,56 @@ void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev) } EXPORT_SYMBOL_GPL(led_classdev_flash_unregister); +static void devm_led_classdev_flash_release(struct device *dev, void *res) +{ + led_classdev_flash_unregister(*(struct led_classdev_flash **)res); +} + +int devm_led_classdev_flash_register_ext(struct device *parent, + struct led_classdev_flash *fled_cdev, + struct led_init_data *init_data) +{ + struct led_classdev_flash **dr; + int ret; + + dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr), + GFP_KERNEL); + if (!dr) + return -ENOMEM; + + ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data); + if (ret) { + devres_free(dr); + return ret; + } + + *dr = fled_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext); + +static int devm_led_classdev_flash_match(struct device *dev, + void *res, void *data) +{ + struct led_classdev_flash **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +void devm_led_classdev_flash_unregister(struct device *dev, + struct led_classdev_flash *fled_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_flash_release, + devm_led_classdev_flash_match, fled_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister); + static void led_clamp_align(struct led_flash_setting *s) { u32 v, offset; diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 647b1263c579..438774315e6c 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -74,13 +74,13 @@ static ssize_t max_brightness_show(struct device *dev, static DEVICE_ATTR_RO(max_brightness); #ifdef CONFIG_LEDS_TRIGGERS -static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); -static struct attribute *led_trigger_attrs[] = { - &dev_attr_trigger.attr, +static BIN_ATTR(trigger, 0644, led_trigger_read, led_trigger_write, 0); +static struct bin_attribute *led_trigger_bin_attrs[] = { + &bin_attr_trigger, NULL, }; static const struct attribute_group led_trigger_group = { - .attrs = led_trigger_attrs, + .bin_attrs = led_trigger_bin_attrs, }; #endif @@ -403,7 +403,7 @@ EXPORT_SYMBOL_GPL(devm_led_classdev_register_ext); static int devm_led_classdev_match(struct device *dev, void *res, void *data) { - struct led_cdev **p = res; + struct led_classdev **p = res; if (WARN_ON(!p || !*p)) return 0; diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index 23963e5cb5d6..79e30d2cb7a5 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -16,6 +16,7 @@ #include <linux/rwsem.h> #include <linux/leds.h> #include <linux/slab.h> +#include <linux/mm.h> #include "leds.h" /* @@ -26,9 +27,11 @@ LIST_HEAD(trigger_list); /* Used by LED Class */ -ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count) { + struct device *dev = kobj_to_dev(kobj); struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; int ret = count; @@ -64,39 +67,82 @@ unlock: mutex_unlock(&led_cdev->led_access); return ret; } -EXPORT_SYMBOL_GPL(led_trigger_store); +EXPORT_SYMBOL_GPL(led_trigger_write); -ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr, - char *buf) +__printf(3, 4) +static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + if (size <= 0) + i = vsnprintf(NULL, 0, fmt, args); + else + i = vscnprintf(buf, size, fmt, args); + va_end(args); + + return i; +} + +static int led_trigger_format(char *buf, size_t size, + struct led_classdev *led_cdev) { - struct led_classdev *led_cdev = dev_get_drvdata(dev); struct led_trigger *trig; - int len = 0; + int len = led_trigger_snprintf(buf, size, "%s", + led_cdev->trigger ? "none" : "[none]"); + + list_for_each_entry(trig, &trigger_list, next_trig) { + bool hit = led_cdev->trigger && + !strcmp(led_cdev->trigger->name, trig->name); + + len += led_trigger_snprintf(buf + len, size - len, + " %s%s%s", hit ? "[" : "", + trig->name, hit ? "]" : ""); + } + + len += led_trigger_snprintf(buf + len, size - len, "\n"); + + return len; +} + +/* + * It was stupid to create 10000 cpu triggers, but we are stuck with it now. + * Don't make that mistake again. We work around it here by creating binary + * attribute, which is not limited by length. This is _not_ good design, do not + * copy it. + */ +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_classdev *led_cdev = dev_get_drvdata(dev); + void *data; + int len; down_read(&triggers_list_lock); down_read(&led_cdev->trigger_lock); - if (!led_cdev->trigger) - len += scnprintf(buf+len, PAGE_SIZE - len, "[none] "); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "none "); - - list_for_each_entry(trig, &trigger_list, next_trig) { - if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, - trig->name)) - len += scnprintf(buf+len, PAGE_SIZE - len, "[%s] ", - trig->name); - else - len += scnprintf(buf+len, PAGE_SIZE - len, "%s ", - trig->name); + len = led_trigger_format(NULL, 0, led_cdev); + data = kvmalloc(len + 1, GFP_KERNEL); + if (!data) { + up_read(&led_cdev->trigger_lock); + up_read(&triggers_list_lock); + return -ENOMEM; } + len = led_trigger_format(data, len + 1, led_cdev); + up_read(&led_cdev->trigger_lock); up_read(&triggers_list_lock); - len += scnprintf(len+buf, PAGE_SIZE - len, "\n"); + len = memory_read_from_buffer(buf, count, &pos, data, len); + + kvfree(data); + return len; } -EXPORT_SYMBOL_GPL(led_trigger_show); +EXPORT_SYMBOL_GPL(led_trigger_read); /* Caller must ensure led_cdev->trigger_lock held */ int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig) diff --git a/drivers/leds/leds-an30259a.c b/drivers/leds/leds-an30259a.c index 250dc9d6f635..82350a28a564 100644 --- a/drivers/leds/leds-an30259a.c +++ b/drivers/leds/leds-an30259a.c @@ -305,6 +305,13 @@ static int an30259a_probe(struct i2c_client *client) chip->regmap = devm_regmap_init_i2c(client, &an30259a_regmap_config); + if (IS_ERR(chip->regmap)) { + err = PTR_ERR(chip->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + err); + goto exit; + } + for (i = 0; i < chip->num_leds; i++) { struct led_init_data init_data = {}; diff --git a/drivers/leds/leds-bcm6328.c b/drivers/leds/leds-bcm6328.c index c50d34e2b098..42e1b7598c3a 100644 --- a/drivers/leds/leds-bcm6328.c +++ b/drivers/leds/leds-bcm6328.c @@ -346,16 +346,11 @@ static int bcm6328_leds_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *child; - struct resource *mem_r; void __iomem *mem; spinlock_t *lock; /* memory lock */ unsigned long val, *blink_leds, *blink_delay; - mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_r) - return -EINVAL; - - mem = devm_ioremap_resource(dev, mem_r); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c index aec285fd21c0..94fefd456ba0 100644 --- a/drivers/leds/leds-bcm6358.c +++ b/drivers/leds/leds-bcm6358.c @@ -151,17 +151,12 @@ static int bcm6358_leds_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct device_node *child; - struct resource *mem_r; void __iomem *mem; spinlock_t *lock; /* memory lock */ unsigned long val; u32 clk_div; - mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem_r) - return -EINVAL; - - mem = devm_ioremap_resource(dev, mem_r); + mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mem)) return PTR_ERR(mem); diff --git a/drivers/leds/leds-el15203000.c b/drivers/leds/leds-el15203000.c new file mode 100644 index 000000000000..298b13e4807a --- /dev/null +++ b/drivers/leds/leds-el15203000.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Crane Merchandising Systems. All rights reserved. +// Copyright (C) 2019 Oleh Kravchenko <oleg@kaa.org.ua> + +#include <linux/delay.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/spi/spi.h> + +/* + * EL15203000 SPI protocol description: + * +-----+---------+ + * | LED | COMMAND | + * +-----+---------+ + * | 1 | 1 | + * +-----+---------+ + * (*) LEDs MCU board expects 20 msec delay per byte. + * + * LEDs: + * +----------+--------------+-------------------------------------------+ + * | ID | NAME | DESCRIPTION | + * +----------+--------------+-------------------------------------------+ + * | 'P' 0x50 | Pipe | Consists from 5 LEDs, controlled by board | + * +----------+--------------+-------------------------------------------+ + * | 'S' 0x53 | Screen frame | Light tube around the screen | + * +----------+--------------+-------------------------------------------+ + * | 'V' 0x56 | Vending area | Highlights a cup of coffee | + * +----------+--------------+-------------------------------------------+ + * + * COMMAND: + * +----------+-----------------+--------------+--------------+ + * | VALUES | PIPE | SCREEN FRAME | VENDING AREA | + * +----------+-----------------+--------------+--------------+ + * | '0' 0x30 | Off | + * +----------+-----------------------------------------------+ + * | '1' 0x31 | On | + * +----------+-----------------+--------------+--------------+ + * | '2' 0x32 | Cascade | Breathing | + * +----------+-----------------+--------------+ + * | '3' 0x33 | Inverse cascade | + * +----------+-----------------+ + * | '4' 0x34 | Bounce | + * +----------+-----------------+ + * | '5' 0x35 | Inverse bounce | + * +----------+-----------------+ + */ + +/* EL15203000 default settings */ +#define EL_FW_DELAY_USEC 20000ul +#define EL_PATTERN_DELAY_MSEC 800u +#define EL_PATTERN_LEN 10u +#define EL_PATTERN_HALF_LEN (EL_PATTERN_LEN / 2) + +enum el15203000_command { + /* for all LEDs */ + EL_OFF = '0', + EL_ON = '1', + + /* for Screen LED */ + EL_SCREEN_BREATHING = '2', + + /* for Pipe LED */ + EL_PIPE_CASCADE = '2', + EL_PIPE_INV_CASCADE = '3', + EL_PIPE_BOUNCE = '4', + EL_PIPE_INV_BOUNCE = '5', +}; + +struct el15203000_led { + struct el15203000 *priv; + struct led_classdev ldev; + u32 reg; +}; + +struct el15203000 { + struct device *dev; + struct mutex lock; + struct spi_device *spi; + unsigned long delay; + size_t count; + struct el15203000_led leds[]; +}; + +static int el15203000_cmd(struct el15203000_led *led, u8 brightness) +{ + int ret; + u8 cmd[2]; + size_t i; + + mutex_lock(&led->priv->lock); + + dev_dbg(led->priv->dev, "Set brightness of 0x%02x(%c) to 0x%02x(%c)", + led->reg, led->reg, brightness, brightness); + + /* to avoid SPI mistiming with firmware we should wait some time */ + if (time_after(led->priv->delay, jiffies)) { + dev_dbg(led->priv->dev, "Wait %luus to sync", + EL_FW_DELAY_USEC); + + usleep_range(EL_FW_DELAY_USEC, + EL_FW_DELAY_USEC + 1); + } + + cmd[0] = led->reg; + cmd[1] = brightness; + + for (i = 0; i < ARRAY_SIZE(cmd); i++) { + if (i) + usleep_range(EL_FW_DELAY_USEC, + EL_FW_DELAY_USEC + 1); + + ret = spi_write(led->priv->spi, &cmd[i], sizeof(cmd[i])); + if (ret) { + dev_err(led->priv->dev, + "spi_write() error %d", ret); + break; + } + } + + led->priv->delay = jiffies + usecs_to_jiffies(EL_FW_DELAY_USEC); + + mutex_unlock(&led->priv->lock); + + return ret; +} + +static int el15203000_set_blocking(struct led_classdev *ldev, + enum led_brightness brightness) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + return el15203000_cmd(led, brightness == LED_OFF ? EL_OFF : EL_ON); +} + +static int el15203000_pattern_set_S(struct led_classdev *ldev, + struct led_pattern *pattern, + u32 len, int repeat) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + if (repeat > 0 || len != 2 || + pattern[0].delta_t != 4000 || pattern[0].brightness != 0 || + pattern[1].delta_t != 4000 || pattern[1].brightness != 1) + return -EINVAL; + + dev_dbg(led->priv->dev, "Breathing mode for 0x%02x(%c)", + led->reg, led->reg); + + return el15203000_cmd(led, EL_SCREEN_BREATHING); +} + +static bool is_cascade(const struct led_pattern *pattern, u32 len, + bool inv, bool right) +{ + int val, t; + u32 i; + + if (len != EL_PATTERN_HALF_LEN) + return false; + + val = right ? BIT(4) : BIT(0); + + for (i = 0; i < len; i++) { + t = inv ? ~val & GENMASK(4, 0) : val; + + if (pattern[i].delta_t != EL_PATTERN_DELAY_MSEC || + pattern[i].brightness != t) + return false; + + val = right ? val >> 1 : val << 1; + } + + return true; +} + +static bool is_bounce(const struct led_pattern *pattern, u32 len, bool inv) +{ + if (len != EL_PATTERN_LEN) + return false; + + return is_cascade(pattern, EL_PATTERN_HALF_LEN, inv, false) && + is_cascade(pattern + EL_PATTERN_HALF_LEN, + EL_PATTERN_HALF_LEN, inv, true); +} + +static int el15203000_pattern_set_P(struct led_classdev *ldev, + struct led_pattern *pattern, + u32 len, int repeat) +{ + u8 cmd; + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + if (repeat > 0) + return -EINVAL; + + if (is_cascade(pattern, len, false, false)) { + dev_dbg(led->priv->dev, "Cascade mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_CASCADE; + } else if (is_cascade(pattern, len, true, false)) { + dev_dbg(led->priv->dev, "Inverse cascade mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_INV_CASCADE; + } else if (is_bounce(pattern, len, false)) { + dev_dbg(led->priv->dev, "Bounce mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_BOUNCE; + } else if (is_bounce(pattern, len, true)) { + dev_dbg(led->priv->dev, "Inverse bounce mode for 0x%02x(%c)", + led->reg, led->reg); + + cmd = EL_PIPE_INV_BOUNCE; + } else { + dev_err(led->priv->dev, "Invalid hw_pattern for 0x%02x(%c)!", + led->reg, led->reg); + + return -EINVAL; + } + + return el15203000_cmd(led, cmd); +} + +static int el15203000_pattern_clear(struct led_classdev *ldev) +{ + struct el15203000_led *led = container_of(ldev, + struct el15203000_led, + ldev); + + return el15203000_cmd(led, EL_OFF); +} + +static int el15203000_probe_dt(struct el15203000 *priv) +{ + struct el15203000_led *led = priv->leds; + struct fwnode_handle *child; + int ret; + + device_for_each_child_node(priv->dev, child) { + struct led_init_data init_data = {}; + + ret = fwnode_property_read_u32(child, "reg", &led->reg); + if (ret) { + dev_err(priv->dev, "LED without ID number"); + fwnode_handle_put(child); + + break; + } + + if (led->reg > U8_MAX) { + dev_err(priv->dev, "LED value %d is invalid", led->reg); + fwnode_handle_put(child); + + return -EINVAL; + } + + fwnode_property_read_string(child, "linux,default-trigger", + &led->ldev.default_trigger); + + led->priv = priv; + led->ldev.max_brightness = LED_ON; + led->ldev.brightness_set_blocking = el15203000_set_blocking; + + if (led->reg == 'S') { + led->ldev.pattern_set = el15203000_pattern_set_S; + led->ldev.pattern_clear = el15203000_pattern_clear; + } else if (led->reg == 'P') { + led->ldev.pattern_set = el15203000_pattern_set_P; + led->ldev.pattern_clear = el15203000_pattern_clear; + } + + init_data.fwnode = child; + ret = devm_led_classdev_register_ext(priv->dev, &led->ldev, + &init_data); + if (ret) { + dev_err(priv->dev, + "failed to register LED device %s, err %d", + led->ldev.name, ret); + fwnode_handle_put(child); + + break; + } + + led++; + } + + return ret; +} + +static int el15203000_probe(struct spi_device *spi) +{ + struct el15203000 *priv; + size_t count; + + count = device_get_child_node_count(&spi->dev); + if (!count) { + dev_err(&spi->dev, "LEDs are not defined in device tree!"); + return -ENODEV; + } + + priv = devm_kzalloc(&spi->dev, struct_size(priv, leds, count), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + priv->count = count; + priv->dev = &spi->dev; + priv->spi = spi; + priv->delay = jiffies - + usecs_to_jiffies(EL_FW_DELAY_USEC); + + spi_set_drvdata(spi, priv); + + return el15203000_probe_dt(priv); +} + +static int el15203000_remove(struct spi_device *spi) +{ + struct el15203000 *priv = spi_get_drvdata(spi); + + mutex_destroy(&priv->lock); + + return 0; +} + +static const struct of_device_id el15203000_dt_ids[] = { + { .compatible = "crane,el15203000", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, el15203000_dt_ids); + +static struct spi_driver el15203000_driver = { + .probe = el15203000_probe, + .remove = el15203000_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = el15203000_dt_ids, + }, +}; + +module_spi_driver(el15203000_driver); + +MODULE_AUTHOR("Oleh Kravchenko <oleg@kaa.org.ua>"); +MODULE_DESCRIPTION("el15203000 LED driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:el15203000"); diff --git a/drivers/leds/leds-lm3601x.c b/drivers/leds/leds-lm3601x.c index b02972f1a341..fce89f2a2d92 100644 --- a/drivers/leds/leds-lm3601x.c +++ b/drivers/leds/leds-lm3601x.c @@ -350,8 +350,7 @@ static int lm3601x_register_leds(struct lm3601x_led *led, init_data.devicename = led->client->name; init_data.default_label = (led->led_mode == LM3601X_LED_TORCH) ? "torch" : "infrared"; - - return led_classdev_flash_register_ext(&led->client->dev, + return devm_led_classdev_flash_register_ext(&led->client->dev, &led->fled_cdev, &init_data); } @@ -445,7 +444,6 @@ static int lm3601x_remove(struct i2c_client *client) { struct lm3601x_led *led = i2c_get_clientdata(client); - led_classdev_flash_unregister(&led->fled_cdev); mutex_destroy(&led->lock); return regmap_update_bits(led->regmap, LM3601X_ENABLE_REG, diff --git a/drivers/leds/leds-lm3692x.c b/drivers/leds/leds-lm3692x.c index 3d381f2f73d0..8b408102e138 100644 --- a/drivers/leds/leds-lm3692x.c +++ b/drivers/leds/leds-lm3692x.c @@ -174,19 +174,20 @@ static int lm3692x_brightness_set(struct led_classdev *led_cdev, ret = lm3692x_fault_check(led); if (ret) { - dev_err(&led->client->dev, "Cannot read/clear faults\n"); + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", + ret); goto out; } ret = regmap_write(led->regmap, LM3692X_BRT_MSB, brt_val); if (ret) { - dev_err(&led->client->dev, "Cannot write MSB\n"); + dev_err(&led->client->dev, "Cannot write MSB: %d\n", ret); goto out; } ret = regmap_write(led->regmap, LM3692X_BRT_LSB, led_brightness_lsb); if (ret) { - dev_err(&led->client->dev, "Cannot write LSB\n"); + dev_err(&led->client->dev, "Cannot write LSB: %d\n", ret); goto out; } out: @@ -197,13 +198,13 @@ out: static int lm3692x_init(struct lm3692x_led *led) { int enable_state; - int ret; + int ret, reg_ret; if (led->regulator) { ret = regulator_enable(led->regulator); if (ret) { dev_err(&led->client->dev, - "Failed to enable regulator\n"); + "Failed to enable regulator: %d\n", ret); return ret; } } @@ -213,7 +214,8 @@ static int lm3692x_init(struct lm3692x_led *led) ret = lm3692x_fault_check(led); if (ret) { - dev_err(&led->client->dev, "Cannot read/clear faults\n"); + dev_err(&led->client->dev, "Cannot read/clear faults: %d\n", + ret); goto out; } @@ -248,9 +250,9 @@ static int lm3692x_init(struct lm3692x_led *led) goto out; ret = regmap_write(led->regmap, LM3692X_BOOST_CTRL, - LM3692X_BRHT_MODE_RAMP_MULTI | - LM3692X_BL_ADJ_POL | - LM3692X_RAMP_RATE_250us); + LM3692X_BOOST_SW_1MHZ | + LM3692X_BOOST_SW_NO_SHIFT | + LM3692X_OCP_PROT_1_5A); if (ret) goto out; @@ -267,7 +269,7 @@ static int lm3692x_init(struct lm3692x_led *led) goto out; ret = regmap_write(led->regmap, LM3692X_BRT_CTRL, - LM3692X_BL_ADJ_POL | LM3692X_PWM_HYSTER_4LSB); + LM3692X_BL_ADJ_POL | LM3692X_RAMP_EN); if (ret) goto out; @@ -311,14 +313,15 @@ out: gpiod_direction_output(led->enable_gpio, 0); if (led->regulator) { - ret = regulator_disable(led->regulator); - if (ret) + reg_ret = regulator_disable(led->regulator); + if (reg_ret) dev_err(&led->client->dev, - "Failed to disable regulator\n"); + "Failed to disable regulator: %d\n", reg_ret); } return ret; } + static int lm3692x_probe_dt(struct lm3692x_led *led) { struct fwnode_handle *child = NULL; @@ -334,9 +337,18 @@ static int lm3692x_probe_dt(struct lm3692x_led *led) return ret; } - led->regulator = devm_regulator_get(&led->client->dev, "vled"); - if (IS_ERR(led->regulator)) + led->regulator = devm_regulator_get_optional(&led->client->dev, "vled"); + if (IS_ERR(led->regulator)) { + ret = PTR_ERR(led->regulator); + if (ret != -ENODEV) { + if (ret != -EPROBE_DEFER) + dev_err(&led->client->dev, + "Failed to get vled regulator: %d\n", + ret); + return ret; + } led->regulator = NULL; + } child = device_get_next_child_node(&led->client->dev, child); if (!child) { @@ -409,7 +421,8 @@ static int lm3692x_remove(struct i2c_client *client) ret = regmap_update_bits(led->regmap, LM3692X_EN, LM3692X_DEVICE_EN, 0); if (ret) { - dev_err(&led->client->dev, "Failed to disable regulator\n"); + dev_err(&led->client->dev, "Failed to disable regulator: %d\n", + ret); return ret; } @@ -420,7 +433,7 @@ static int lm3692x_remove(struct i2c_client *client) ret = regulator_disable(led->regulator); if (ret) dev_err(&led->client->dev, - "Failed to disable regulator\n"); + "Failed to disable regulator: %d\n", ret); } mutex_destroy(&led->lock); diff --git a/drivers/leds/leds-mlxreg.c b/drivers/leds/leds-mlxreg.c index cabe379071a7..82aea1cd0c12 100644 --- a/drivers/leds/leds-mlxreg.c +++ b/drivers/leds/leds-mlxreg.c @@ -228,8 +228,8 @@ static int mlxreg_led_config(struct mlxreg_led_priv_data *priv) brightness = LED_OFF; led_data->base_color = MLXREG_LED_GREEN_SOLID; } - sprintf(led_data->led_cdev_name, "%s:%s", "mlxreg", - data->label); + snprintf(led_data->led_cdev_name, sizeof(led_data->led_cdev_name), + "mlxreg:%s", data->label); led_cdev->name = led_data->led_cdev_name; led_cdev->brightness = brightness; led_cdev->max_brightness = LED_ON; diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index c7c7199e8ebd..7d515d5e57bd 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -467,16 +467,11 @@ pca9532_of_populate_pdata(struct device *dev, struct device_node *np) { struct pca9532_platform_data *pdata; struct device_node *child; - const struct of_device_id *match; int devid, maxleds; int i = 0; const char *state; - match = of_match_device(of_pca9532_leds_match, dev); - if (!match) - return ERR_PTR(-ENODEV); - - devid = (int)(uintptr_t)match->data; + devid = (int)(uintptr_t)of_device_get_match_data(dev); maxleds = pca9532_chip_info_tbl[devid].num_leds; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -509,7 +504,6 @@ static int pca9532_probe(struct i2c_client *client, const struct i2c_device_id *id) { int devid; - const struct of_device_id *of_id; struct pca9532_data *data = i2c_get_clientdata(client); struct pca9532_platform_data *pca9532_pdata = dev_get_platdata(&client->dev); @@ -525,11 +519,7 @@ static int pca9532_probe(struct i2c_client *client, dev_err(&client->dev, "no platform data\n"); return -EINVAL; } - of_id = of_match_device(of_pca9532_leds_match, - &client->dev); - if (unlikely(!of_id)) - return -EINVAL; - devid = (int)(uintptr_t) of_id->data; + devid = (int)(uintptr_t)of_device_get_match_data(&client->dev); } else { devid = id->driver_data; } diff --git a/drivers/leds/leds-tlc591xx.c b/drivers/leds/leds-tlc591xx.c index 59ff088c7d75..a8911ebd30e5 100644 --- a/drivers/leds/leds-tlc591xx.c +++ b/drivers/leds/leds-tlc591xx.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #define TLC591XX_MAX_LEDS 16 +#define TLC591XX_MAX_BRIGHTNESS 256 #define TLC591XX_REG_MODE1 0x00 #define MODE1_RESPON_ADDR_MASK 0xF0 @@ -112,11 +113,11 @@ tlc591xx_brightness_set(struct led_classdev *led_cdev, struct tlc591xx_priv *priv = led->priv; int err; - switch (brightness) { + switch ((int)brightness) { case 0: err = tlc591xx_set_ledout(priv, led, LEDOUT_OFF); break; - case LED_FULL: + case TLC591XX_MAX_BRIGHTNESS: err = tlc591xx_set_ledout(priv, led, LEDOUT_ON); break; default: @@ -128,51 +129,6 @@ tlc591xx_brightness_set(struct led_classdev *led_cdev, return err; } -static void -tlc591xx_destroy_devices(struct tlc591xx_priv *priv, unsigned int j) -{ - int i = j; - - while (--i >= 0) { - if (priv->leds[i].active) - led_classdev_unregister(&priv->leds[i].ldev); - } -} - -static int -tlc591xx_configure(struct device *dev, - struct tlc591xx_priv *priv, - const struct tlc591xx *tlc591xx) -{ - unsigned int i; - int err = 0; - - tlc591xx_set_mode(priv->regmap, MODE2_DIM); - for (i = 0; i < TLC591XX_MAX_LEDS; i++) { - struct tlc591xx_led *led = &priv->leds[i]; - - if (!led->active) - continue; - - led->priv = priv; - led->led_no = i; - led->ldev.brightness_set_blocking = tlc591xx_brightness_set; - led->ldev.max_brightness = LED_FULL; - err = led_classdev_register(dev, &led->ldev); - if (err < 0) { - dev_err(dev, "couldn't register LED %s\n", - led->ldev.name); - goto exit; - } - } - - return 0; - -exit: - tlc591xx_destroy_devices(priv, i); - return err; -} - static const struct regmap_config tlc591xx_regmap = { .reg_bits = 8, .val_bits = 8, @@ -225,7 +181,16 @@ tlc591xx_probe(struct i2c_client *client, i2c_set_clientdata(client, priv); + err = tlc591xx_set_mode(priv->regmap, MODE2_DIM); + if (err < 0) + return err; + for_each_child_of_node(np, child) { + struct tlc591xx_led *led; + struct led_init_data init_data = {}; + + init_data.fwnode = of_fwnode_handle(child); + err = of_property_read_u32(child, "reg", ®); if (err) { of_node_put(child); @@ -236,22 +201,24 @@ tlc591xx_probe(struct i2c_client *client, of_node_put(child); return -EINVAL; } - priv->leds[reg].active = true; - priv->leds[reg].ldev.name = - of_get_property(child, "label", NULL) ? : child->name; - priv->leds[reg].ldev.default_trigger = - of_get_property(child, "linux,default-trigger", NULL); - } - return tlc591xx_configure(dev, priv, tlc591xx); -} + led = &priv->leds[reg]; -static int -tlc591xx_remove(struct i2c_client *client) -{ - struct tlc591xx_priv *priv = i2c_get_clientdata(client); - - tlc591xx_destroy_devices(priv, TLC591XX_MAX_LEDS); + led->active = true; + led->ldev.default_trigger = + of_get_property(child, "linux,default-trigger", NULL); + led->priv = priv; + led->led_no = reg; + led->ldev.brightness_set_blocking = tlc591xx_brightness_set; + led->ldev.max_brightness = TLC591XX_MAX_BRIGHTNESS; + err = devm_led_classdev_register_ext(dev, &led->ldev, + &init_data); + if (err < 0) { + dev_err(dev, "couldn't register LED %s\n", + led->ldev.name); + return err; + } + } return 0; } @@ -268,7 +235,6 @@ static struct i2c_driver tlc591xx_driver = { .of_match_table = of_match_ptr(of_tlc591xx_leds_match), }, .probe = tlc591xx_probe, - .remove = tlc591xx_remove, .id_table = tlc591xx_id, }; diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h index 0b577cece8f7..2d9eb48bbed9 100644 --- a/drivers/leds/leds.h +++ b/drivers/leds/leds.h @@ -23,6 +23,12 @@ void led_set_brightness_nopm(struct led_classdev *led_cdev, enum led_brightness value); void led_set_brightness_nosleep(struct led_classdev *led_cdev, enum led_brightness value); +ssize_t led_trigger_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t pos, size_t count); +ssize_t led_trigger_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t pos, size_t count); extern struct rw_semaphore leds_list_lock; extern struct list_head leds_list; diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 136f86a1627d..d5e774d83021 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -302,10 +302,12 @@ static int netdev_trig_notify(struct notifier_block *nb, container_of(nb, struct led_netdev_data, notifier); if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE - && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER) + && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER + && evt != NETDEV_CHANGENAME) return NOTIFY_DONE; if (!(dev == trigger_data->net_dev || + (evt == NETDEV_CHANGENAME && !strcmp(dev->name, trigger_data->device_name)) || (evt == NETDEV_REGISTER && !strcmp(dev->name, trigger_data->device_name)))) return NOTIFY_DONE; @@ -315,6 +317,7 @@ static int netdev_trig_notify(struct notifier_block *nb, clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); switch (evt) { + case NETDEV_CHANGENAME: case NETDEV_REGISTER: if (trigger_data->net_dev) dev_put(trigger_data->net_dev); diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index 400960cf04d5..b1314d104b06 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -147,7 +147,8 @@ static struct miscdevice anslcd_dev = { &anslcd_fops }; -const char anslcd_logo[] = "********************" /* Line #1 */ +static const char anslcd_logo[] __initconst = + "********************" /* Line #1 */ "* LINUX! *" /* Line #3 */ "* Welcome to *" /* Line #2 */ "********************"; /* Line #4 */ diff --git a/drivers/mailbox/hi6220-mailbox.c b/drivers/mailbox/hi6220-mailbox.c index 8b9eb56e4311..cc236ac7a0b5 100644 --- a/drivers/mailbox/hi6220-mailbox.c +++ b/drivers/mailbox/hi6220-mailbox.c @@ -354,7 +354,6 @@ static int hi6220_mbox_probe(struct platform_device *pdev) static struct platform_driver hi6220_mbox_driver = { .driver = { .name = "hi6220-mbox", - .owner = THIS_MODULE, .of_match_table = hi6220_mbox_of_match, }, .probe = hi6220_mbox_probe, diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c index 9f74dee1a58c..2cdcdc5f1119 100644 --- a/drivers/mailbox/imx-mailbox.c +++ b/drivers/mailbox/imx-mailbox.c @@ -12,19 +12,11 @@ #include <linux/of_device.h> #include <linux/slab.h> -/* Transmit Register */ -#define IMX_MU_xTRn(x) (0x00 + 4 * (x)) -/* Receive Register */ -#define IMX_MU_xRRn(x) (0x10 + 4 * (x)) -/* Status Register */ -#define IMX_MU_xSR 0x20 #define IMX_MU_xSR_GIPn(x) BIT(28 + (3 - (x))) #define IMX_MU_xSR_RFn(x) BIT(24 + (3 - (x))) #define IMX_MU_xSR_TEn(x) BIT(20 + (3 - (x))) #define IMX_MU_xSR_BRDIP BIT(9) -/* Control Register */ -#define IMX_MU_xCR 0x24 /* General Purpose Interrupt Enable */ #define IMX_MU_xCR_GIEn(x) BIT(28 + (3 - (x))) /* Receive Interrupt Enable */ @@ -44,6 +36,13 @@ enum imx_mu_chan_type { IMX_MU_TYPE_RXDB, /* Rx doorbell */ }; +struct imx_mu_dcfg { + u32 xTR[4]; /* Transmit Registers */ + u32 xRR[4]; /* Receive Registers */ + u32 xSR; /* Status Register */ + u32 xCR; /* Control Register */ +}; + struct imx_mu_con_priv { unsigned int idx; char irq_desc[IMX_MU_CHAN_NAME_SIZE]; @@ -61,12 +60,27 @@ struct imx_mu_priv { struct mbox_chan mbox_chans[IMX_MU_CHANS]; struct imx_mu_con_priv con_priv[IMX_MU_CHANS]; + const struct imx_mu_dcfg *dcfg; struct clk *clk; int irq; bool side_b; }; +static const struct imx_mu_dcfg imx_mu_cfg_imx6sx = { + .xTR = {0x0, 0x4, 0x8, 0xc}, + .xRR = {0x10, 0x14, 0x18, 0x1c}, + .xSR = 0x20, + .xCR = 0x24, +}; + +static const struct imx_mu_dcfg imx_mu_cfg_imx7ulp = { + .xTR = {0x20, 0x24, 0x28, 0x2c}, + .xRR = {0x40, 0x44, 0x48, 0x4c}, + .xSR = 0x60, + .xCR = 0x64, +}; + static struct imx_mu_priv *to_imx_mu_priv(struct mbox_controller *mbox) { return container_of(mbox, struct imx_mu_priv, mbox); @@ -88,10 +102,10 @@ static u32 imx_mu_xcr_rmw(struct imx_mu_priv *priv, u32 set, u32 clr) u32 val; spin_lock_irqsave(&priv->xcr_lock, flags); - val = imx_mu_read(priv, IMX_MU_xCR); + val = imx_mu_read(priv, priv->dcfg->xCR); val &= ~clr; val |= set; - imx_mu_write(priv, val, IMX_MU_xCR); + imx_mu_write(priv, val, priv->dcfg->xCR); spin_unlock_irqrestore(&priv->xcr_lock, flags); return val; @@ -111,8 +125,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p) struct imx_mu_con_priv *cp = chan->con_priv; u32 val, ctrl, dat; - ctrl = imx_mu_read(priv, IMX_MU_xCR); - val = imx_mu_read(priv, IMX_MU_xSR); + ctrl = imx_mu_read(priv, priv->dcfg->xCR); + val = imx_mu_read(priv, priv->dcfg->xSR); switch (cp->type) { case IMX_MU_TYPE_TX: @@ -138,10 +152,10 @@ static irqreturn_t imx_mu_isr(int irq, void *p) imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx)); mbox_chan_txdone(chan, 0); } else if (val == IMX_MU_xSR_RFn(cp->idx)) { - dat = imx_mu_read(priv, IMX_MU_xRRn(cp->idx)); + dat = imx_mu_read(priv, priv->dcfg->xRR[cp->idx]); mbox_chan_received_data(chan, (void *)&dat); } else if (val == IMX_MU_xSR_GIPn(cp->idx)) { - imx_mu_write(priv, IMX_MU_xSR_GIPn(cp->idx), IMX_MU_xSR); + imx_mu_write(priv, IMX_MU_xSR_GIPn(cp->idx), priv->dcfg->xSR); mbox_chan_received_data(chan, NULL); } else { dev_warn_ratelimited(priv->dev, "Not handled interrupt\n"); @@ -159,7 +173,7 @@ static int imx_mu_send_data(struct mbox_chan *chan, void *data) switch (cp->type) { case IMX_MU_TYPE_TX: - imx_mu_write(priv, *arg, IMX_MU_xTRn(cp->idx)); + imx_mu_write(priv, *arg, priv->dcfg->xTR[cp->idx]); imx_mu_xcr_rmw(priv, IMX_MU_xCR_TIEn(cp->idx), 0); break; case IMX_MU_TYPE_TXDB: @@ -214,11 +228,24 @@ static void imx_mu_shutdown(struct mbox_chan *chan) struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox); struct imx_mu_con_priv *cp = chan->con_priv; - if (cp->type == IMX_MU_TYPE_TXDB) + if (cp->type == IMX_MU_TYPE_TXDB) { tasklet_kill(&cp->txdb_tasklet); + return; + } - imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx) | - IMX_MU_xCR_RIEn(cp->idx) | IMX_MU_xCR_GIEn(cp->idx)); + switch (cp->type) { + case IMX_MU_TYPE_TX: + imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_TIEn(cp->idx)); + break; + case IMX_MU_TYPE_RX: + imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_RIEn(cp->idx)); + break; + case IMX_MU_TYPE_RXDB: + imx_mu_xcr_rmw(priv, 0, IMX_MU_xCR_GIEn(cp->idx)); + break; + default: + break; + } free_irq(priv->irq, chan); } @@ -257,7 +284,7 @@ static void imx_mu_init_generic(struct imx_mu_priv *priv) return; /* Set default MU configuration */ - imx_mu_write(priv, 0, IMX_MU_xCR); + imx_mu_write(priv, 0, priv->dcfg->xCR); } static int imx_mu_probe(struct platform_device *pdev) @@ -265,6 +292,7 @@ static int imx_mu_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct imx_mu_priv *priv; + const struct imx_mu_dcfg *dcfg; unsigned int i; int ret; @@ -282,6 +310,11 @@ static int imx_mu_probe(struct platform_device *pdev) if (priv->irq < 0) return priv->irq; + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + priv->dcfg = dcfg; + priv->clk = devm_clk_get(dev, NULL); if (IS_ERR(priv->clk)) { if (PTR_ERR(priv->clk) != -ENOENT) @@ -335,7 +368,8 @@ static int imx_mu_remove(struct platform_device *pdev) } static const struct of_device_id imx_mu_dt_ids[] = { - { .compatible = "fsl,imx6sx-mu" }, + { .compatible = "fsl,imx7ulp-mu", .data = &imx_mu_cfg_imx7ulp }, + { .compatible = "fsl,imx6sx-mu", .data = &imx_mu_cfg_imx6sx }, { }, }; MODULE_DEVICE_TABLE(of, imx_mu_dt_ids); diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index a3cd63583cf7..5978a35aac6d 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -868,7 +868,7 @@ static int omap_mbox_probe(struct platform_device *pdev) dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l); ret = pm_runtime_put_sync(mdev->dev); - if (ret < 0) + if (ret < 0 && ret != -ENOSYS) goto unregister; devm_kfree(&pdev->dev, finfoblk); diff --git a/drivers/mailbox/stm32-ipcc.c b/drivers/mailbox/stm32-ipcc.c index 5c2d1e1f988b..ef966887aa15 100644 --- a/drivers/mailbox/stm32-ipcc.c +++ b/drivers/mailbox/stm32-ipcc.c @@ -52,7 +52,6 @@ struct stm32_ipcc { struct clk *clk; spinlock_t lock; /* protect access to IPCC registers */ int irqs[IPCC_IRQ_NUM]; - int wkp; u32 proc_id; u32 n_chans; u32 xcr; @@ -282,16 +281,9 @@ static int stm32_ipcc_probe(struct platform_device *pdev) /* wakeup */ if (of_property_read_bool(np, "wakeup-source")) { - ipcc->wkp = platform_get_irq_byname(pdev, "wakeup"); - if (ipcc->wkp < 0) { - if (ipcc->wkp != -EPROBE_DEFER) - dev_err(dev, "could not get wakeup IRQ\n"); - ret = ipcc->wkp; - goto err_clk; - } - device_set_wakeup_capable(dev, true); - ret = dev_pm_set_dedicated_wake_irq(dev, ipcc->wkp); + + ret = dev_pm_set_wake_irq(dev, ipcc->irqs[IPCC_IRQ_RX]); if (ret) { dev_err(dev, "Failed to set wake up irq\n"); goto err_init_wkp; @@ -334,10 +326,10 @@ static int stm32_ipcc_probe(struct platform_device *pdev) return 0; err_irq_wkp: - if (ipcc->wkp) + if (of_property_read_bool(np, "wakeup-source")) dev_pm_clear_wake_irq(dev); err_init_wkp: - device_init_wakeup(dev, false); + device_set_wakeup_capable(dev, false); err_clk: clk_disable_unprepare(ipcc->clk); return ret; @@ -345,27 +337,17 @@ err_clk: static int stm32_ipcc_remove(struct platform_device *pdev) { - struct stm32_ipcc *ipcc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; - if (ipcc->wkp) + if (of_property_read_bool(dev->of_node, "wakeup-source")) dev_pm_clear_wake_irq(&pdev->dev); - device_init_wakeup(&pdev->dev, false); + device_set_wakeup_capable(dev, false); return 0; } #ifdef CONFIG_PM_SLEEP -static void stm32_ipcc_set_irq_wake(struct device *dev, bool enable) -{ - struct stm32_ipcc *ipcc = dev_get_drvdata(dev); - unsigned int i; - - if (device_may_wakeup(dev)) - for (i = 0; i < IPCC_IRQ_NUM; i++) - irq_set_irq_wake(ipcc->irqs[i], enable); -} - static int stm32_ipcc_suspend(struct device *dev) { struct stm32_ipcc *ipcc = dev_get_drvdata(dev); @@ -373,8 +355,6 @@ static int stm32_ipcc_suspend(struct device *dev) ipcc->xmr = readl_relaxed(ipcc->reg_proc + IPCC_XMR); ipcc->xcr = readl_relaxed(ipcc->reg_proc + IPCC_XCR); - stm32_ipcc_set_irq_wake(dev, true); - return 0; } @@ -382,8 +362,6 @@ static int stm32_ipcc_resume(struct device *dev) { struct stm32_ipcc *ipcc = dev_get_drvdata(dev); - stm32_ipcc_set_irq_wake(dev, false); - writel_relaxed(ipcc->xmr, ipcc->reg_proc + IPCC_XMR); writel_relaxed(ipcc->xcr, ipcc->reg_proc + IPCC_XCR); diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index f078f8a3aec8..9a8c1cf54ac4 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -720,9 +720,7 @@ static const struct file_operations lirc_fops = { .owner = THIS_MODULE, .write = ir_lirc_transmit_ir, .unlocked_ioctl = ir_lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ir_lirc_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .read = ir_lirc_read, .poll = ir_lirc_poll, .open = ir_lirc_open, diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 3e9dc92cb467..bafc729fc434 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -610,107 +610,53 @@ int ab8500_suspend(struct ab8500 *ab8500) } static const struct mfd_cell ab8500_bm_devs[] = { - { - .name = "ab8500-charger", - .of_compatible = "stericsson,ab8500-charger", - .platform_data = &ab8500_bm_data, - .pdata_size = sizeof(ab8500_bm_data), - }, - { - .name = "ab8500-btemp", - .of_compatible = "stericsson,ab8500-btemp", - .platform_data = &ab8500_bm_data, - .pdata_size = sizeof(ab8500_bm_data), - }, - { - .name = "ab8500-fg", - .of_compatible = "stericsson,ab8500-fg", - .platform_data = &ab8500_bm_data, - .pdata_size = sizeof(ab8500_bm_data), - }, - { - .name = "ab8500-chargalg", - .of_compatible = "stericsson,ab8500-chargalg", - .platform_data = &ab8500_bm_data, - .pdata_size = sizeof(ab8500_bm_data), - }, + OF_MFD_CELL("ab8500-charger", NULL, &ab8500_bm_data, + sizeof(ab8500_bm_data), 0, "stericsson,ab8500-charger"), + OF_MFD_CELL("ab8500-btemp", NULL, &ab8500_bm_data, + sizeof(ab8500_bm_data), 0, "stericsson,ab8500-btemp"), + OF_MFD_CELL("ab8500-fg", NULL, &ab8500_bm_data, + sizeof(ab8500_bm_data), 0, "stericsson,ab8500-fg"), + OF_MFD_CELL("ab8500-chargalg", NULL, &ab8500_bm_data, + sizeof(ab8500_bm_data), 0, "stericsson,ab8500-chargalg"), }; static const struct mfd_cell ab8500_devs[] = { #ifdef CONFIG_DEBUG_FS - { - .name = "ab8500-debug", - .of_compatible = "stericsson,ab8500-debug", - }, + OF_MFD_CELL("ab8500-debug", + NULL, NULL, 0, 0, "stericsson,ab8500-debug"), #endif - { - .name = "ab8500-sysctrl", - .of_compatible = "stericsson,ab8500-sysctrl", - }, - { - .name = "ab8500-ext-regulator", - .of_compatible = "stericsson,ab8500-ext-regulator", - }, - { - .name = "ab8500-regulator", - .of_compatible = "stericsson,ab8500-regulator", - }, - { - .name = "ab8500-clk", - .of_compatible = "stericsson,ab8500-clk", - }, - { - .name = "ab8500-gpadc", - .of_compatible = "stericsson,ab8500-gpadc", - }, - { - .name = "ab8500-rtc", - .of_compatible = "stericsson,ab8500-rtc", - }, - { - .name = "ab8500-acc-det", - .of_compatible = "stericsson,ab8500-acc-det", - }, - { - - .name = "ab8500-poweron-key", - .of_compatible = "stericsson,ab8500-poweron-key", - }, - { - .name = "ab8500-pwm", - .of_compatible = "stericsson,ab8500-pwm", - .id = 1, - }, - { - .name = "ab8500-pwm", - .of_compatible = "stericsson,ab8500-pwm", - .id = 2, - }, - { - .name = "ab8500-pwm", - .of_compatible = "stericsson,ab8500-pwm", - .id = 3, - }, - { - .name = "ab8500-denc", - .of_compatible = "stericsson,ab8500-denc", - }, - { - .name = "pinctrl-ab8500", - .of_compatible = "stericsson,ab8500-gpio", - }, - { - .name = "abx500-temp", - .of_compatible = "stericsson,abx500-temp", - }, - { - .name = "ab8500-usb", - .of_compatible = "stericsson,ab8500-usb", - }, - { - .name = "ab8500-codec", - .of_compatible = "stericsson,ab8500-codec", - }, + OF_MFD_CELL("ab8500-sysctrl", + NULL, NULL, 0, 0, "stericsson,ab8500-sysctrl"), + OF_MFD_CELL("ab8500-ext-regulator", + NULL, NULL, 0, 0, "stericsson,ab8500-ext-regulator"), + OF_MFD_CELL("ab8500-regulator", + NULL, NULL, 0, 0, "stericsson,ab8500-regulator"), + OF_MFD_CELL("abx500-clk", + NULL, NULL, 0, 0, "stericsson,abx500-clk"), + OF_MFD_CELL("ab8500-gpadc", + NULL, NULL, 0, 0, "stericsson,ab8500-gpadc"), + OF_MFD_CELL("ab8500-rtc", + NULL, NULL, 0, 0, "stericsson,ab8500-rtc"), + OF_MFD_CELL("ab8500-acc-det", + NULL, NULL, 0, 0, "stericsson,ab8500-acc-det"), + OF_MFD_CELL("ab8500-poweron-key", + NULL, NULL, 0, 0, "stericsson,ab8500-poweron-key"), + OF_MFD_CELL("ab8500-pwm", + NULL, NULL, 0, 1, "stericsson,ab8500-pwm"), + OF_MFD_CELL("ab8500-pwm", + NULL, NULL, 0, 2, "stericsson,ab8500-pwm"), + OF_MFD_CELL("ab8500-pwm", + NULL, NULL, 0, 3, "stericsson,ab8500-pwm"), + OF_MFD_CELL("ab8500-denc", + NULL, NULL, 0, 0, "stericsson,ab8500-denc"), + OF_MFD_CELL("pinctrl-ab8500", + NULL, NULL, 0, 0, "stericsson,ab8500-gpio"), + OF_MFD_CELL("abx500-temp", + NULL, NULL, 0, 0, "stericsson,abx500-temp"), + OF_MFD_CELL("ab8500-usb", + NULL, NULL, 0, 0, "stericsson,ab8500-usb"), + OF_MFD_CELL("ab8500-codec", + NULL, NULL, 0, 0, "stericsson,ab8500-codec"), }; static const struct mfd_cell ab9540_devs[] = { diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 4a31907a4525..f73cf76d1373 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -814,11 +814,7 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) int ret, i; /* Handle old non-standard DT binding */ - pdata->reset = devm_gpiod_get_from_of_node(arizona->dev, - arizona->dev->of_node, - "wlf,reset", 0, - GPIOD_OUT_LOW, - "arizona /RESET"); + pdata->reset = devm_gpiod_get(arizona->dev, "wlf,reset", GPIOD_OUT_LOW); if (IS_ERR(pdata->reset)) { ret = PTR_ERR(pdata->reset); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index f1825c0ccbd0..d0fb2e52ee76 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -27,121 +27,106 @@ enum cs5535_mfd_bars { NR_BARS, }; -static int cs5535_mfd_res_enable(struct platform_device *pdev) -{ - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(&pdev->dev, "can't fetch device resource info\n"); - return -EIO; - } - - if (!request_region(res->start, resource_size(res), DRV_NAME)) { - dev_err(&pdev->dev, "can't request region\n"); - return -EIO; - } - - return 0; -} - -static int cs5535_mfd_res_disable(struct platform_device *pdev) -{ - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(&pdev->dev, "can't fetch device resource info\n"); - return -EIO; - } - - release_region(res->start, resource_size(res)); - return 0; -} - static struct resource cs5535_mfd_resources[NR_BARS]; static struct mfd_cell cs5535_mfd_cells[] = { { - .id = SMB_BAR, .name = "cs5535-smb", .num_resources = 1, .resources = &cs5535_mfd_resources[SMB_BAR], }, { - .id = GPIO_BAR, .name = "cs5535-gpio", .num_resources = 1, .resources = &cs5535_mfd_resources[GPIO_BAR], }, { - .id = MFGPT_BAR, .name = "cs5535-mfgpt", .num_resources = 1, .resources = &cs5535_mfd_resources[MFGPT_BAR], }, { - .id = PMS_BAR, .name = "cs5535-pms", .num_resources = 1, .resources = &cs5535_mfd_resources[PMS_BAR], + }, +}; - .enable = cs5535_mfd_res_enable, - .disable = cs5535_mfd_res_disable, +static struct mfd_cell cs5535_olpc_mfd_cells[] = { + { + .name = "olpc-xo1-pm-acpi", + .num_resources = 1, + .resources = &cs5535_mfd_resources[ACPI_BAR], }, { - .id = ACPI_BAR, - .name = "cs5535-acpi", + .name = "olpc-xo1-sci-acpi", .num_resources = 1, .resources = &cs5535_mfd_resources[ACPI_BAR], - - .enable = cs5535_mfd_res_enable, - .disable = cs5535_mfd_res_disable, }, }; -static const char *olpc_acpi_clones[] = { - "olpc-xo1-pm-acpi", - "olpc-xo1-sci-acpi" -}; - static int cs5535_mfd_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int err, i; + int err, bar; err = pci_enable_device(pdev); if (err) return err; - /* fill in IO range for each cell; subdrivers handle the region */ - for (i = 0; i < ARRAY_SIZE(cs5535_mfd_cells); i++) { - int bar = cs5535_mfd_cells[i].id; + for (bar = 0; bar < NR_BARS; bar++) { struct resource *r = &cs5535_mfd_resources[bar]; r->flags = IORESOURCE_IO; r->start = pci_resource_start(pdev, bar); r->end = pci_resource_end(pdev, bar); + } - /* id is used for temporarily storing BAR; unset it now */ - cs5535_mfd_cells[i].id = 0; + err = pci_request_region(pdev, PMS_BAR, DRV_NAME); + if (err) { + dev_err(&pdev->dev, "Failed to request PMS_BAR's IO region\n"); + goto err_disable; } - err = mfd_add_devices(&pdev->dev, -1, cs5535_mfd_cells, + err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, cs5535_mfd_cells, ARRAY_SIZE(cs5535_mfd_cells), NULL, 0, NULL); if (err) { - dev_err(&pdev->dev, "MFD add devices failed: %d\n", err); - goto err_disable; + dev_err(&pdev->dev, + "Failed to add CS5535 sub-devices: %d\n", err); + goto err_release_pms; } - if (machine_is_olpc()) - mfd_clone_cell("cs5535-acpi", olpc_acpi_clones, ARRAY_SIZE(olpc_acpi_clones)); + if (machine_is_olpc()) { + err = pci_request_region(pdev, ACPI_BAR, DRV_NAME); + if (err) { + dev_err(&pdev->dev, + "Failed to request ACPI_BAR's IO region\n"); + goto err_remove_devices; + } + + err = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + cs5535_olpc_mfd_cells, + ARRAY_SIZE(cs5535_olpc_mfd_cells), + NULL, 0, NULL); + if (err) { + dev_err(&pdev->dev, + "Failed to add CS5535 OLPC sub-devices: %d\n", + err); + goto err_release_acpi; + } + } dev_info(&pdev->dev, "%zu devices registered.\n", ARRAY_SIZE(cs5535_mfd_cells)); return 0; +err_release_acpi: + pci_release_region(pdev, ACPI_BAR); +err_remove_devices: + mfd_remove_devices(&pdev->dev); +err_release_pms: + pci_release_region(pdev, PMS_BAR); err_disable: pci_disable_device(pdev); return err; @@ -150,6 +135,11 @@ err_disable: static void cs5535_mfd_remove(struct pci_dev *pdev) { mfd_remove_devices(&pdev->dev); + + if (machine_is_olpc()) + pci_release_region(pdev, ACPI_BAR); + + pci_release_region(pdev, PMS_BAR); pci_disable_device(pdev); } diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index dfac6afa82ca..57ac58b4b5f3 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -27,6 +27,7 @@ #include <linux/bitops.h> #include <linux/fs.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/uaccess.h> @@ -668,6 +669,14 @@ struct prcmu_fw_version *prcmu_get_fw_version(void) return fw_info.valid ? &fw_info.version : NULL; } +static bool prcmu_is_ulppll_disabled(void) +{ + struct prcmu_fw_version *ver; + + ver = prcmu_get_fw_version(); + return ver && ver->project == PRCMU_FW_PROJECT_U8420_SYSCLK; +} + bool prcmu_has_arm_maxopp(void) { return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) & @@ -1308,10 +1317,23 @@ static int request_sysclk(bool enable) static int request_timclk(bool enable) { - u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK); + u32 val; + + /* + * On the U8420_CLKSEL firmware, the ULP (Ultra Low Power) + * PLL is disabled so we cannot use doze mode, this will + * stop the clock on this firmware. + */ + if (prcmu_is_ulppll_disabled()) + val = 0; + else + val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK); if (!enable) - val |= PRCM_TCR_STOP_TIMERS; + val |= PRCM_TCR_STOP_TIMERS | + PRCM_TCR_DOZE_MODE | + PRCM_TCR_TENSEL_MASK; + writel(val, PRCM_TCR); return 0; @@ -1615,7 +1637,8 @@ unsigned long prcmu_clock_rate(u8 clock) if (clock < PRCMU_NUM_REG_CLOCKS) return clock_rate(clock); else if (clock == PRCMU_TIMCLK) - return ROOT_CLOCK_RATE / 16; + return prcmu_is_ulppll_disabled() ? + 32768 : ROOT_CLOCK_RATE / 16; else if (clock == PRCMU_SYSCLK) return ROOT_CLOCK_RATE; else if (clock == PRCMU_PLLSOC0) @@ -2646,6 +2669,8 @@ static char *fw_project_name(u32 project) return "U8520 MBL"; case PRCMU_FW_PROJECT_U8420: return "U8420"; + case PRCMU_FW_PROJECT_U8420_SYSCLK: + return "U8420-sysclk"; case PRCMU_FW_PROJECT_U9540: return "U9540"; case PRCMU_FW_PROJECT_A9420: @@ -2693,27 +2718,18 @@ static int db8500_irq_init(struct device_node *np) return 0; } -static void dbx500_fw_version_init(struct platform_device *pdev, - u32 version_offset) +static void dbx500_fw_version_init(struct device_node *np) { - struct resource *res; void __iomem *tcpm_base; u32 version; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "prcmu-tcpm"); - if (!res) { - dev_err(&pdev->dev, - "Error: no prcmu tcpm memory region provided\n"); - return; - } - tcpm_base = ioremap(res->start, resource_size(res)); + tcpm_base = of_iomap(np, 1); if (!tcpm_base) { - dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n"); + pr_err("no prcmu tcpm mem region provided\n"); return; } - version = readl(tcpm_base + version_offset); + version = readl(tcpm_base + DB8500_PRCMU_FW_VERSION_OFFSET); fw_info.version.project = (version & 0xFF); fw_info.version.api_version = (version >> 8) & 0xFF; fw_info.version.func_version = (version >> 16) & 0xFF; @@ -2731,7 +2747,7 @@ static void dbx500_fw_version_init(struct platform_device *pdev, iounmap(tcpm_base); } -void __init db8500_prcmu_early_init(u32 phy_base, u32 size) +void __init db8500_prcmu_early_init(void) { /* * This is a temporary remap to bring up the clocks. It is @@ -2740,9 +2756,17 @@ void __init db8500_prcmu_early_init(u32 phy_base, u32 size) * clock driver can probe independently. An early initcall will * still be needed, but it can be diverted into drivers/clk/ux500. */ - prcmu_base = ioremap(phy_base, size); - if (!prcmu_base) + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "stericsson,db8500-prcmu"); + prcmu_base = of_iomap(np, 0); + if (!prcmu_base) { + of_node_put(np); pr_err("%s: ioremap() of prcmu registers failed!\n", __func__); + return; + } + dbx500_fw_version_init(np); + of_node_put(np); spin_lock_init(&mb0_transfer.lock); spin_lock_init(&mb0_transfer.dbb_irqs_lock); @@ -3024,20 +3048,13 @@ static const struct mfd_cell common_prcmu_devs[] = { }; static const struct mfd_cell db8500_prcmu_devs[] = { - { - .name = "db8500-prcmu-regulators", - .of_compatible = "stericsson,db8500-prcmu-regulator", - .platform_data = &db8500_regulators, - .pdata_size = sizeof(db8500_regulators), - }, - { - .name = "cpuidle-dbx500", - .of_compatible = "stericsson,cpuidle-dbx500", - }, - { - .name = "db8500-thermal", - .of_compatible = "stericsson,db8500-thermal", - }, + OF_MFD_CELL("db8500-prcmu-regulators", NULL, + &db8500_regulators, sizeof(db8500_regulators), 0, + "stericsson,db8500-prcmu-regulator"), + OF_MFD_CELL("cpuidle-dbx500", + NULL, NULL, 0, 0, "stericsson,cpuidle-dbx500"), + OF_MFD_CELL("db8500-thermal", + NULL, NULL, 0, 0, "stericsson,db8500-thermal"), }; static int db8500_prcmu_register_ab8500(struct device *parent) @@ -3091,7 +3108,6 @@ static int db8500_prcmu_probe(struct platform_device *pdev) return -ENOMEM; } init_prcm_registers(); - dbx500_fw_version_init(pdev, DB8500_PRCMU_FW_VERSION_OFFSET); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcdm"); if (!res) { dev_err(&pdev->dev, "no prcmu tcdm region provided\n"); diff --git a/drivers/mfd/intel-lpss-pci.c b/drivers/mfd/intel-lpss-pci.c index 9355db29d2f9..b33030e3385c 100644 --- a/drivers/mfd/intel-lpss-pci.c +++ b/drivers/mfd/intel-lpss-pci.c @@ -122,13 +122,25 @@ static const struct intel_lpss_platform_info apl_i2c_info = { .properties = apl_i2c_properties, }; +static struct property_entry glk_i2c_properties[] = { + PROPERTY_ENTRY_U32("i2c-sda-hold-time-ns", 313), + PROPERTY_ENTRY_U32("i2c-sda-falling-time-ns", 171), + PROPERTY_ENTRY_U32("i2c-scl-falling-time-ns", 290), + { }, +}; + +static const struct intel_lpss_platform_info glk_i2c_info = { + .clk_rate = 133000000, + .properties = glk_i2c_properties, +}; + static const struct intel_lpss_platform_info cnl_i2c_info = { .clk_rate = 216000000, .properties = spt_i2c_properties, }; static const struct pci_device_id intel_lpss_pci_ids[] = { - /* CML */ + /* CML-LP */ { PCI_VDEVICE(INTEL, 0x02a8), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0x02a9), (kernel_ulong_t)&spt_uart_info }, { PCI_VDEVICE(INTEL, 0x02aa), (kernel_ulong_t)&spt_info }, @@ -141,6 +153,17 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x02ea), (kernel_ulong_t)&cnl_i2c_info }, { PCI_VDEVICE(INTEL, 0x02eb), (kernel_ulong_t)&cnl_i2c_info }, { PCI_VDEVICE(INTEL, 0x02fb), (kernel_ulong_t)&spt_info }, + /* CML-H */ + { PCI_VDEVICE(INTEL, 0x06a8), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06a9), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06aa), (kernel_ulong_t)&spt_info }, + { PCI_VDEVICE(INTEL, 0x06ab), (kernel_ulong_t)&spt_info }, + { PCI_VDEVICE(INTEL, 0x06c7), (kernel_ulong_t)&spt_uart_info }, + { PCI_VDEVICE(INTEL, 0x06e8), (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06e9), (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06ea), (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06eb), (kernel_ulong_t)&cnl_i2c_info }, + { PCI_VDEVICE(INTEL, 0x06fb), (kernel_ulong_t)&spt_info }, /* BXT A-Step */ { PCI_VDEVICE(INTEL, 0x0aac), (kernel_ulong_t)&bxt_i2c_info }, { PCI_VDEVICE(INTEL, 0x0aae), (kernel_ulong_t)&bxt_i2c_info }, @@ -174,14 +197,14 @@ static const struct pci_device_id intel_lpss_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x1ac6), (kernel_ulong_t)&bxt_info }, { PCI_VDEVICE(INTEL, 0x1aee), (kernel_ulong_t)&bxt_uart_info }, /* GLK */ - { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&bxt_i2c_info }, - { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&bxt_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ac), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ae), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b0), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b2), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b4), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b6), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31b8), (kernel_ulong_t)&glk_i2c_info }, + { PCI_VDEVICE(INTEL, 0x31ba), (kernel_ulong_t)&glk_i2c_info }, { PCI_VDEVICE(INTEL, 0x31bc), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x31be), (kernel_ulong_t)&bxt_uart_info }, { PCI_VDEVICE(INTEL, 0x31c0), (kernel_ulong_t)&bxt_uart_info }, diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index bfe4ff337581..b0f0781a6b9c 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -384,7 +384,7 @@ int intel_lpss_probe(struct device *dev, if (!lpss) return -ENOMEM; - lpss->priv = devm_ioremap(dev, info->mem->start + LPSS_PRIV_OFFSET, + lpss->priv = devm_ioremap_uc(dev, info->mem->start + LPSS_PRIV_OFFSET, LPSS_PRIV_SIZE); if (!lpss->priv) return -ENOMEM; diff --git a/drivers/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c index ab09b8225b76..429efa1f8e55 100644 --- a/drivers/mfd/intel_soc_pmic_crc.c +++ b/drivers/mfd/intel_soc_pmic_crc.c @@ -89,6 +89,9 @@ static struct mfd_cell crystal_cove_cht_dev[] = { .resources = gpio_resources, }, { + .name = "cht_crystal_cove_pmic", + }, + { .name = "crystal_cove_pwm", }, }; diff --git a/drivers/mfd/ipaq-micro.c b/drivers/mfd/ipaq-micro.c index a1d9be82734d..e92eeeb67a98 100644 --- a/drivers/mfd/ipaq-micro.c +++ b/drivers/mfd/ipaq-micro.c @@ -396,11 +396,7 @@ static int __init micro_probe(struct platform_device *pdev) if (IS_ERR(micro->base)) return PTR_ERR(micro->base); - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!res) - return -EINVAL; - - micro->sdlc = devm_ioremap_resource(&pdev->dev, res); + micro->sdlc = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(micro->sdlc)) return PTR_ERR(micro->sdlc); diff --git a/drivers/mfd/madera-core.c b/drivers/mfd/madera-core.c index 29540cbf7593..a8cfadc1fc01 100644 --- a/drivers/mfd/madera-core.c +++ b/drivers/mfd/madera-core.c @@ -450,6 +450,21 @@ int madera_dev_init(struct madera *madera) sizeof(madera->pdata)); } + madera->mclk[MADERA_MCLK1].id = "mclk1"; + madera->mclk[MADERA_MCLK2].id = "mclk2"; + madera->mclk[MADERA_MCLK3].id = "mclk3"; + + ret = devm_clk_bulk_get_optional(madera->dev, ARRAY_SIZE(madera->mclk), + madera->mclk); + if (ret) { + dev_err(madera->dev, "Failed to get clocks: %d\n", ret); + return ret; + } + + /* Not using devm_clk_get to prevent breakage of existing DTs */ + if (!madera->mclk[MADERA_MCLK2].clk) + dev_warn(madera->dev, "Missing MCLK2, requires 32kHz clock\n"); + ret = madera_get_reset_gpio(madera); if (ret) return ret; @@ -660,13 +675,19 @@ int madera_dev_init(struct madera *madera) } /* Init 32k clock sourced from MCLK2 */ + ret = clk_prepare_enable(madera->mclk[MADERA_MCLK2].clk); + if (ret) { + dev_err(madera->dev, "Failed to enable 32k clock: %d\n", ret); + goto err_reset; + } + ret = regmap_update_bits(madera->regmap, MADERA_CLOCK_32K_1, MADERA_CLK_32K_ENA_MASK | MADERA_CLK_32K_SRC_MASK, MADERA_CLK_32K_ENA | MADERA_32KZ_MCLK2); if (ret) { dev_err(madera->dev, "Failed to init 32k clock: %d\n", ret); - goto err_reset; + goto err_clock; } pm_runtime_set_active(madera->dev); @@ -687,6 +708,8 @@ int madera_dev_init(struct madera *madera) err_pm_runtime: pm_runtime_disable(madera->dev); +err_clock: + clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk); err_reset: madera_enable_hard_reset(madera); regulator_disable(madera->dcvdd); @@ -713,6 +736,8 @@ int madera_dev_exit(struct madera *madera) */ pm_runtime_disable(madera->dev); + clk_disable_unprepare(madera->mclk[MADERA_MCLK2].clk); + regulator_disable(madera->dcvdd); regulator_put(madera->dcvdd); diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c index a851ff473a44..c7ed5c353553 100644 --- a/drivers/mfd/max77620.c +++ b/drivers/mfd/max77620.c @@ -507,7 +507,6 @@ static int max77620_probe(struct i2c_client *client, i2c_set_clientdata(client, chip); chip->dev = &client->dev; - chip->irq_base = -1; chip->chip_irq = client->irq; chip->chip_id = (enum max77620_chip_id)id->driver_data; @@ -545,8 +544,8 @@ static int max77620_probe(struct i2c_client *client, max77620_top_irq_chip.irq_drv_data = chip; ret = devm_regmap_add_irq_chip(chip->dev, chip->rmap, client->irq, - IRQF_ONESHOT | IRQF_SHARED, - chip->irq_base, &max77620_top_irq_chip, + IRQF_ONESHOT | IRQF_SHARED, 0, + &max77620_top_irq_chip, &chip->top_irq_data); if (ret < 0) { dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret); diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 23276a80e3b4..f5a73af60dd4 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -26,54 +26,28 @@ static struct device_type mfd_dev_type = { int mfd_cell_enable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); - int err = 0; - /* only call enable hook if the cell wasn't previously enabled */ - if (atomic_inc_return(cell->usage_count) == 1) - err = cell->enable(pdev); - - /* if the enable hook failed, decrement counter to allow retries */ - if (err) - atomic_dec(cell->usage_count); + if (!cell->enable) { + dev_dbg(&pdev->dev, "No .enable() call-back registered\n"); + return 0; + } - return err; + return cell->enable(pdev); } EXPORT_SYMBOL(mfd_cell_enable); int mfd_cell_disable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); - int err = 0; - - /* only disable if no other clients are using it */ - if (atomic_dec_return(cell->usage_count) == 0) - err = cell->disable(pdev); - - /* if the disable hook failed, increment to allow retries */ - if (err) - atomic_inc(cell->usage_count); - - /* sanity check; did someone call disable too many times? */ - WARN_ON(atomic_read(cell->usage_count) < 0); - return err; -} -EXPORT_SYMBOL(mfd_cell_disable); - -static int mfd_platform_add_cell(struct platform_device *pdev, - const struct mfd_cell *cell, - atomic_t *usage_count) -{ - if (!cell) + if (!cell->disable) { + dev_dbg(&pdev->dev, "No .disable() call-back registered\n"); return 0; + } - pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL); - if (!pdev->mfd_cell) - return -ENOMEM; - - pdev->mfd_cell->usage_count = usage_count; - return 0; + return cell->disable(pdev); } +EXPORT_SYMBOL(mfd_cell_disable); #if IS_ENABLED(CONFIG_ACPI) static void mfd_acpi_add_device(const struct mfd_cell *cell, @@ -134,7 +108,7 @@ static inline void mfd_acpi_add_device(const struct mfd_cell *cell, #endif static int mfd_add_device(struct device *parent, int id, - const struct mfd_cell *cell, atomic_t *usage_count, + const struct mfd_cell *cell, struct resource *mem_base, int irq_base, struct irq_domain *domain) { @@ -154,6 +128,10 @@ static int mfd_add_device(struct device *parent, int id, if (!pdev) goto fail_alloc; + pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL); + if (!pdev->mfd_cell) + goto fail_device; + res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL); if (!res) goto fail_device; @@ -174,6 +152,11 @@ static int mfd_add_device(struct device *parent, int id, if (parent->of_node && cell->of_compatible) { for_each_child_of_node(parent->of_node, np) { if (of_device_is_compatible(np, cell->of_compatible)) { + if (!of_device_is_available(np)) { + /* Ignore disabled devices error free */ + ret = 0; + goto fail_alias; + } pdev->dev.of_node = np; pdev->dev.fwnode = &np->fwnode; break; @@ -196,10 +179,6 @@ static int mfd_add_device(struct device *parent, int id, goto fail_alias; } - ret = mfd_platform_add_cell(pdev, cell, usage_count); - if (ret) - goto fail_alias; - for (r = 0; r < cell->num_resources; r++) { res[r].name = cell->resources[r].name; res[r].flags = cell->resources[r].flags; @@ -286,16 +265,9 @@ int mfd_add_devices(struct device *parent, int id, { int i; int ret; - atomic_t *cnts; - - /* initialize reference counting for all cells */ - cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL); - if (!cnts) - return -ENOMEM; for (i = 0; i < n_devs; i++) { - atomic_set(&cnts[i], 0); - ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base, + ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base, domain); if (ret) goto fail; @@ -306,17 +278,15 @@ int mfd_add_devices(struct device *parent, int id, fail: if (i) mfd_remove_devices(parent); - else - kfree(cnts); + return ret; } EXPORT_SYMBOL(mfd_add_devices); -static int mfd_remove_devices_fn(struct device *dev, void *c) +static int mfd_remove_devices_fn(struct device *dev, void *data) { struct platform_device *pdev; const struct mfd_cell *cell; - atomic_t **usage_count = c; if (dev->type != &mfd_dev_type) return 0; @@ -327,20 +297,13 @@ static int mfd_remove_devices_fn(struct device *dev, void *c) regulator_bulk_unregister_supply_alias(dev, cell->parent_supplies, cell->num_parent_supplies); - /* find the base address of usage_count pointers (for freeing) */ - if (!*usage_count || (cell->usage_count < *usage_count)) - *usage_count = cell->usage_count; - platform_device_unregister(pdev); return 0; } void mfd_remove_devices(struct device *parent) { - atomic_t *cnts = NULL; - - device_for_each_child_reverse(parent, &cnts, mfd_remove_devices_fn); - kfree(cnts); + device_for_each_child_reverse(parent, NULL, mfd_remove_devices_fn); } EXPORT_SYMBOL(mfd_remove_devices); @@ -382,38 +345,5 @@ int devm_mfd_add_devices(struct device *dev, int id, } EXPORT_SYMBOL(devm_mfd_add_devices); -int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones) -{ - struct mfd_cell cell_entry; - struct device *dev; - struct platform_device *pdev; - int i; - - /* fetch the parent cell's device (should already be registered!) */ - dev = bus_find_device_by_name(&platform_bus_type, NULL, cell); - if (!dev) { - printk(KERN_ERR "failed to find device for cell %s\n", cell); - return -ENODEV; - } - pdev = to_platform_device(dev); - memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry)); - - WARN_ON(!cell_entry.enable); - - for (i = 0; i < n_clones; i++) { - cell_entry.name = clones[i]; - /* don't give up if a single call fails; just report error */ - if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, - cell_entry.usage_count, NULL, 0, NULL)) - dev_err(dev, "failed to create platform device '%s'\n", - clones[i]); - } - - put_device(dev); - - return 0; -} -EXPORT_SYMBOL(mfd_clone_cell); - MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c index b2c325ead1c8..0437c858d115 100644 --- a/drivers/mfd/mt6397-core.c +++ b/drivers/mfd/mt6397-core.c @@ -189,16 +189,16 @@ static int mt6397_probe(struct platform_device *pdev) switch (pmic->chip_id) { case MT6323_CHIP_ID: - ret = devm_mfd_add_devices(&pdev->dev, -1, mt6323_devs, - ARRAY_SIZE(mt6323_devs), NULL, - 0, pmic->irq_domain); + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + mt6323_devs, ARRAY_SIZE(mt6323_devs), + NULL, 0, pmic->irq_domain); break; case MT6391_CHIP_ID: case MT6397_CHIP_ID: - ret = devm_mfd_add_devices(&pdev->dev, -1, mt6397_devs, - ARRAY_SIZE(mt6397_devs), NULL, - 0, pmic->irq_domain); + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, + mt6397_devs, ARRAY_SIZE(mt6397_devs), + NULL, 0, pmic->irq_domain); break; default: diff --git a/drivers/mfd/qcom-spmi-pmic.c b/drivers/mfd/qcom-spmi-pmic.c index e8fe705073fa..1df1a2711328 100644 --- a/drivers/mfd/qcom-spmi-pmic.c +++ b/drivers/mfd/qcom-spmi-pmic.c @@ -31,6 +31,8 @@ #define PM8916_SUBTYPE 0x0b #define PM8004_SUBTYPE 0x0c #define PM8909_SUBTYPE 0x0d +#define PM8950_SUBTYPE 0x10 +#define PMI8950_SUBTYPE 0x11 #define PM8998_SUBTYPE 0x14 #define PMI8998_SUBTYPE 0x15 #define PM8005_SUBTYPE 0x18 @@ -50,6 +52,8 @@ static const struct of_device_id pmic_spmi_id_table[] = { { .compatible = "qcom,pm8916", .data = (void *)PM8916_SUBTYPE }, { .compatible = "qcom,pm8004", .data = (void *)PM8004_SUBTYPE }, { .compatible = "qcom,pm8909", .data = (void *)PM8909_SUBTYPE }, + { .compatible = "qcom,pm8950", .data = (void *)PM8950_SUBTYPE }, + { .compatible = "qcom,pmi8950", .data = (void *)PMI8950_SUBTYPE }, { .compatible = "qcom,pm8998", .data = (void *)PM8998_SUBTYPE }, { .compatible = "qcom,pmi8998", .data = (void *)PMI8998_SUBTYPE }, { .compatible = "qcom,pm8005", .data = (void *)PM8005_SUBTYPE }, diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index 050478cabc95..a69a6742ecdc 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -109,11 +109,7 @@ static const struct regmap_config rk817_regmap_config = { }; static struct resource rtc_resources[] = { - { - .start = RK808_IRQ_RTC_ALARM, - .end = RK808_IRQ_RTC_ALARM, - .flags = IORESOURCE_IRQ, - } + DEFINE_RES_IRQ(RK808_IRQ_RTC_ALARM), }; static struct resource rk817_rtc_resources[] = { @@ -121,16 +117,8 @@ static struct resource rk817_rtc_resources[] = { }; static struct resource rk805_key_resources[] = { - { - .start = RK805_IRQ_PWRON_FALL, - .end = RK805_IRQ_PWRON_FALL, - .flags = IORESOURCE_IRQ, - }, - { - .start = RK805_IRQ_PWRON_RISE, - .end = RK805_IRQ_PWRON_RISE, - .flags = IORESOURCE_IRQ, - } + DEFINE_RES_IRQ(RK805_IRQ_PWRON_RISE), + DEFINE_RES_IRQ(RK805_IRQ_PWRON_FALL), }; static struct resource rk817_pwrkey_resources[] = { @@ -167,7 +155,7 @@ static const struct mfd_cell rk817s[] = { { .name = "rk808-clkout",}, { .name = "rk808-regulator",}, { - .name = "rk8xx-pwrkey", + .name = "rk805-pwrkey", .num_resources = ARRAY_SIZE(rk817_pwrkey_resources), .resources = &rk817_pwrkey_resources[0], }, @@ -215,7 +203,7 @@ static const struct rk808_reg_data rk808_pre_init_reg[] = { static const struct rk808_reg_data rk817_pre_init_reg[] = { {RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP}, - {RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_H}, + {RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_L}, {RK817_SYS_CFG(1), RK817_HOTDIE_TEMP_MSK | RK817_TSD_TEMP_MSK, RK817_HOTDIE_105 | RK817_TSD_140}, }; diff --git a/drivers/mfd/rohm-bd70528.c b/drivers/mfd/rohm-bd70528.c index 55599d5c5c86..ef6786fd3b00 100644 --- a/drivers/mfd/rohm-bd70528.c +++ b/drivers/mfd/rohm-bd70528.c @@ -105,15 +105,14 @@ static struct regmap_config bd70528_regmap = { * register. */ -/* bit [0] - Shutdown register */ -unsigned int bit0_offsets[] = {0}; /* Shutdown register */ -unsigned int bit1_offsets[] = {1}; /* Power failure register */ -unsigned int bit2_offsets[] = {2}; /* VR FAULT register */ -unsigned int bit3_offsets[] = {3}; /* PMU register interrupts */ -unsigned int bit4_offsets[] = {4, 5}; /* Charger 1 and Charger 2 registers */ -unsigned int bit5_offsets[] = {6}; /* RTC register */ -unsigned int bit6_offsets[] = {7}; /* GPIO register */ -unsigned int bit7_offsets[] = {8}; /* Invalid operation register */ +static unsigned int bit0_offsets[] = {0}; /* Shutdown */ +static unsigned int bit1_offsets[] = {1}; /* Power failure */ +static unsigned int bit2_offsets[] = {2}; /* VR FAULT */ +static unsigned int bit3_offsets[] = {3}; /* PMU interrupts */ +static unsigned int bit4_offsets[] = {4, 5}; /* Charger 1 and Charger 2 */ +static unsigned int bit5_offsets[] = {6}; /* RTC */ +static unsigned int bit6_offsets[] = {7}; /* GPIO */ +static unsigned int bit7_offsets[] = {8}; /* Invalid operation */ static struct regmap_irq_sub_irq_map bd70528_sub_irq_offsets[] = { REGMAP_IRQ_MAIN_REG_OFFSET(bit0_offsets), diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 660723276481..e22197c832e8 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -105,7 +105,6 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk) syscon_config.reg_stride = reg_io_width; syscon_config.val_bits = reg_io_width * 8; syscon_config.max_register = resource_size(&res) - reg_io_width; - syscon_config.name = of_node_full_name(np); regmap = regmap_init_mmio(NULL, base, &syscon_config); if (IS_ERR(regmap)) { diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c index fd111296b959..926c289cb040 100644 --- a/drivers/mfd/ti_am335x_tscadc.c +++ b/drivers/mfd/ti_am335x_tscadc.c @@ -182,11 +182,11 @@ static int ti_tscadc_probe(struct platform_device *pdev) tscadc->irq = err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tscadc->tscadc_phys_base = res->start; tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(tscadc->tscadc_base)) return PTR_ERR(tscadc->tscadc_base); + tscadc->tscadc_phys_base = res->start; tscadc->regmap = devm_regmap_init_mmio(&pdev->dev, tscadc->tscadc_base, &tscadc_regmap_config); if (IS_ERR(tscadc->regmap)) { diff --git a/drivers/mfd/wm8998-tables.c b/drivers/mfd/wm8998-tables.c index ebf0eadd2075..9b34a6d76094 100644 --- a/drivers/mfd/wm8998-tables.c +++ b/drivers/mfd/wm8998-tables.c @@ -806,12 +806,6 @@ static const struct reg_default wm8998_reg_default[] = { { 0x00000EF3, 0x0000 }, /* R3827 - ISRC 2 CTRL 1 */ { 0x00000EF4, 0x0001 }, /* R3828 - ISRC 2 CTRL 2 */ { 0x00000EF5, 0x0000 }, /* R3829 - ISRC 2 CTRL 3 */ - { 0x00001700, 0x0000 }, /* R5888 - FRF_COEFF_1 */ - { 0x00001701, 0x0000 }, /* R5889 - FRF_COEFF_2 */ - { 0x00001702, 0x0000 }, /* R5890 - FRF_COEFF_3 */ - { 0x00001703, 0x0000 }, /* R5891 - FRF_COEFF_4 */ - { 0x00001704, 0x0000 }, /* R5892 - DAC_COMP_1 */ - { 0x00001705, 0x0000 }, /* R5893 - DAC_COMP_2 */ }; static bool wm8998_readable_register(struct device *dev, unsigned int reg) @@ -1492,12 +1486,6 @@ static bool wm8998_readable_register(struct device *dev, unsigned int reg) case ARIZONA_ISRC_2_CTRL_1: case ARIZONA_ISRC_2_CTRL_2: case ARIZONA_ISRC_2_CTRL_3: - case ARIZONA_FRF_COEFF_1: - case ARIZONA_FRF_COEFF_2: - case ARIZONA_FRF_COEFF_3: - case ARIZONA_FRF_COEFF_4: - case ARIZONA_V2_DAC_COMP_1: - case ARIZONA_V2_DAC_COMP_2: return true; default: return false; diff --git a/drivers/misc/cxl/flash.c b/drivers/misc/cxl/flash.c index 4d6836f19489..cb9cca35a226 100644 --- a/drivers/misc/cxl/flash.c +++ b/drivers/misc/cxl/flash.c @@ -473,12 +473,6 @@ static long device_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return -EINVAL; } -static long device_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return device_ioctl(file, cmd, arg); -} - static int device_close(struct inode *inode, struct file *file) { struct cxl *adapter = file->private_data; @@ -514,7 +508,7 @@ static const struct file_operations fops = { .owner = THIS_MODULE, .open = device_open, .unlocked_ioctl = device_ioctl, - .compat_ioctl = device_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, .release = device_close, }; diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2cccd82a3106..0681d5fdd538 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -716,9 +716,12 @@ static int at24_probe(struct i2c_client *client) return -ENODEV; } - dev_info(dev, "%u byte %s EEPROM, %s, %u bytes/write\n", - byte_len, client->name, - writable ? "writable" : "read-only", at24->write_max); + if (writable) + dev_info(dev, "%u byte %s EEPROM, writable, %u bytes/write\n", + byte_len, client->name, at24->write_max); + else + dev_info(dev, "%u byte %s EEPROM, read-only\n", + byte_len, client->name); return 0; } diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c index 0e34c0568fed..040a0bda3125 100644 --- a/drivers/misc/genwqe/card_dev.c +++ b/drivers/misc/genwqe/card_dev.c @@ -1215,34 +1215,13 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd, return rc; } -#if defined(CONFIG_COMPAT) -/** - * genwqe_compat_ioctl() - Compatibility ioctl - * - * Called whenever a 32-bit process running under a 64-bit kernel - * performs an ioctl on /dev/genwqe<n>_card. - * - * @filp: file pointer. - * @cmd: command. - * @arg: user argument. - * Return: zero on success or negative number on failure. - */ -static long genwqe_compat_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - return genwqe_ioctl(filp, cmd, arg); -} -#endif /* defined(CONFIG_COMPAT) */ - static const struct file_operations genwqe_fops = { .owner = THIS_MODULE, .open = genwqe_open, .fasync = genwqe_fasync, .mmap = genwqe_mmap, .unlocked_ioctl = genwqe_ioctl, -#if defined(CONFIG_COMPAT) - .compat_ioctl = genwqe_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .release = genwqe_release, }; diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index 7284a22b1a09..a4fdad04809a 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -12,6 +12,10 @@ #include <linux/sched/task_stack.h> #include <linux/uaccess.h> +#ifdef CONFIG_X86_32 +#include <asm/desc.h> +#endif + struct lkdtm_list { struct list_head node; }; @@ -337,3 +341,38 @@ void lkdtm_UNSET_SMEP(void) pr_err("FAIL: this test is x86_64-only\n"); #endif } + +#ifdef CONFIG_X86_32 +void lkdtm_DOUBLE_FAULT(void) +{ + /* + * Trigger #DF by setting the stack limit to zero. This clobbers + * a GDT TLS slot, which is okay because the current task will die + * anyway due to the double fault. + */ + struct desc_struct d = { + .type = 3, /* expand-up, writable, accessed data */ + .p = 1, /* present */ + .d = 1, /* 32-bit */ + .g = 0, /* limit in bytes */ + .s = 1, /* not system */ + }; + + local_irq_disable(); + write_gdt_entry(get_cpu_gdt_rw(smp_processor_id()), + GDT_ENTRY_TLS_MIN, &d, DESCTYPE_S); + + /* + * Put our zero-limit segment in SS and then trigger a fault. The + * 4-byte access to (%esp) will fault with #SS, and the attempt to + * deliver the fault will recursively cause #SS and result in #DF. + * This whole process happens while NMIs and MCEs are blocked by the + * MOV SS window. This is nice because an NMI with an invalid SS + * would also double-fault, resulting in the NMI or MCE being lost. + */ + asm volatile ("movw %0, %%ss; addl $0, (%%esp)" :: + "r" ((unsigned short)(GDT_ENTRY_TLS_MIN << 3))); + + panic("tried to double fault but didn't die\n"); +} +#endif diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index cbc4c9045a99..ee0d6e721441 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -171,6 +171,9 @@ static const struct crashtype crashtypes[] = { CRASHTYPE(USERCOPY_KERNEL_DS), CRASHTYPE(STACKLEAK_ERASING), CRASHTYPE(CFI_FORWARD_PROTO), +#ifdef CONFIG_X86_32 + CRASHTYPE(DOUBLE_FAULT), +#endif }; diff --git a/drivers/misc/lkdtm/lkdtm.h b/drivers/misc/lkdtm/lkdtm.h index ab446e0bde97..c56d23e37643 100644 --- a/drivers/misc/lkdtm/lkdtm.h +++ b/drivers/misc/lkdtm/lkdtm.h @@ -28,6 +28,9 @@ void lkdtm_CORRUPT_USER_DS(void); void lkdtm_STACK_GUARD_PAGE_LEADING(void); void lkdtm_STACK_GUARD_PAGE_TRAILING(void); void lkdtm_UNSET_SMEP(void); +#ifdef CONFIG_X86_32 +void lkdtm_DOUBLE_FAULT(void); +#endif /* lkdtm_heap.c */ void __init lkdtm_heap_init(void); diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 4ef6e37caafc..f17297f2943d 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -533,24 +533,6 @@ out: } /** - * mei_compat_ioctl - the compat IOCTL function - * - * @file: pointer to file structure - * @cmd: ioctl command - * @data: pointer to mei message structure - * - * Return: 0 on success , <0 on error - */ -#ifdef CONFIG_COMPAT -static long mei_compat_ioctl(struct file *file, - unsigned int cmd, unsigned long data) -{ - return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data)); -} -#endif - - -/** * mei_poll - the poll function * * @file: pointer to file structure @@ -922,9 +904,7 @@ static const struct file_operations mei_fops = { .owner = THIS_MODULE, .read = mei_read, .unlocked_ioctl = mei_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = mei_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = mei_open, .release = mei_release, .write = mei_write, diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index ff3c396146ff..ce16d6b99295 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -968,7 +968,7 @@ static const struct file_operations vmuser_fops = { .release = vmci_host_close, .poll = vmci_host_poll, .unlocked_ioctl = vmci_host_unlocked_ioctl, - .compat_ioctl = vmci_host_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice vmci_host_miscdev = { diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 1b77fff9f892..cc9a28cf9d82 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -1078,36 +1078,6 @@ static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd, return err; } -#ifdef CONFIG_COMPAT -static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - unsigned long translated_arg = (unsigned long)compat_ptr(arg); - - return vol_cdev_ioctl(file, cmd, translated_arg); -} - -static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - unsigned long translated_arg = (unsigned long)compat_ptr(arg); - - return ubi_cdev_ioctl(file, cmd, translated_arg); -} - -static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - unsigned long translated_arg = (unsigned long)compat_ptr(arg); - - return ctrl_cdev_ioctl(file, cmd, translated_arg); -} -#else -#define vol_cdev_compat_ioctl NULL -#define ubi_cdev_compat_ioctl NULL -#define ctrl_cdev_compat_ioctl NULL -#endif - /* UBI volume character device operations */ const struct file_operations ubi_vol_cdev_operations = { .owner = THIS_MODULE, @@ -1118,7 +1088,7 @@ const struct file_operations ubi_vol_cdev_operations = { .write = vol_cdev_write, .fsync = vol_cdev_fsync, .unlocked_ioctl = vol_cdev_ioctl, - .compat_ioctl = vol_cdev_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; /* UBI character device operations */ @@ -1126,13 +1096,13 @@ const struct file_operations ubi_cdev_operations = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = ubi_cdev_ioctl, - .compat_ioctl = ubi_cdev_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; /* UBI control character device operations */ const struct file_operations ubi_ctrl_cdev_operations = { .owner = THIS_MODULE, .unlocked_ioctl = ctrl_cdev_ioctl, - .compat_ioctl = ctrl_cdev_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = no_llseek, }; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 250bd90627a5..9caa876ce6e8 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -955,6 +955,9 @@ struct net_device_context { u32 vf_alloc; /* Serial number of the VF to team with */ u32 vf_serial; + + /* Used to temporarily save the config info across hibernation */ + struct netvsc_device_info *saved_netvsc_dev_info; }; /* Per channel data */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 868e22e286ca..eff8fef4f775 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -2424,6 +2424,61 @@ static int netvsc_remove(struct hv_device *dev) return 0; } +static int netvsc_suspend(struct hv_device *dev) +{ + struct net_device_context *ndev_ctx; + struct net_device *vf_netdev, *net; + struct netvsc_device *nvdev; + int ret; + + net = hv_get_drvdata(dev); + + ndev_ctx = netdev_priv(net); + cancel_delayed_work_sync(&ndev_ctx->dwork); + + rtnl_lock(); + + nvdev = rtnl_dereference(ndev_ctx->nvdev); + if (nvdev == NULL) { + ret = -ENODEV; + goto out; + } + + vf_netdev = rtnl_dereference(ndev_ctx->vf_netdev); + if (vf_netdev) + netvsc_unregister_vf(vf_netdev); + + /* Save the current config info */ + ndev_ctx->saved_netvsc_dev_info = netvsc_devinfo_get(nvdev); + + ret = netvsc_detach(net, nvdev); +out: + rtnl_unlock(); + + return ret; +} + +static int netvsc_resume(struct hv_device *dev) +{ + struct net_device *net = hv_get_drvdata(dev); + struct net_device_context *net_device_ctx; + struct netvsc_device_info *device_info; + int ret; + + rtnl_lock(); + + net_device_ctx = netdev_priv(net); + device_info = net_device_ctx->saved_netvsc_dev_info; + + ret = netvsc_attach(net, device_info); + + rtnl_unlock(); + + kfree(device_info); + net_device_ctx->saved_netvsc_dev_info = NULL; + + return ret; +} static const struct hv_vmbus_device_id id_table[] = { /* Network guid */ { HV_NIC_GUID, }, @@ -2438,6 +2493,8 @@ static struct hv_driver netvsc_drv = { .id_table = id_table, .probe = netvsc_probe, .remove = netvsc_remove, + .suspend = netvsc_suspend, + .resume = netvsc_resume, .driver = { .probe_type = PROBE_FORCE_SYNCHRONOUS, }, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 61824bbb5588..0cb1c2d0a8bc 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -270,7 +270,7 @@ static void ppp_mp_insert(struct ppp *ppp, struct sk_buff *skb); static struct sk_buff *ppp_mp_reconstruct(struct ppp *ppp); static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb); #endif /* CONFIG_PPP_MULTILINK */ -static int ppp_set_compress(struct ppp *ppp, unsigned long arg); +static int ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data); static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound); static void ppp_ccp_closed(struct ppp *ppp); static struct compressor *find_compressor(int type); @@ -554,36 +554,66 @@ static __poll_t ppp_poll(struct file *file, poll_table *wait) } #ifdef CONFIG_PPP_FILTER -static int get_filter(void __user *arg, struct sock_filter **p) +static struct bpf_prog *get_filter(struct sock_fprog *uprog) +{ + struct sock_fprog_kern fprog; + struct bpf_prog *res = NULL; + int err; + + if (!uprog->len) + return NULL; + + /* uprog->len is unsigned short, so no overflow here */ + fprog.len = uprog->len * sizeof(struct sock_filter); + fprog.filter = memdup_user(uprog->filter, fprog.len); + if (IS_ERR(fprog.filter)) + return ERR_CAST(fprog.filter); + + err = bpf_prog_create(&res, &fprog); + kfree(fprog.filter); + + return err ? ERR_PTR(err) : res; +} + +static struct bpf_prog *ppp_get_filter(struct sock_fprog __user *p) { struct sock_fprog uprog; - struct sock_filter *code = NULL; - int len; - if (copy_from_user(&uprog, arg, sizeof(uprog))) - return -EFAULT; + if (copy_from_user(&uprog, p, sizeof(struct sock_fprog))) + return ERR_PTR(-EFAULT); + return get_filter(&uprog); +} - if (!uprog.len) { - *p = NULL; - return 0; - } +#ifdef CONFIG_COMPAT +struct sock_fprog32 { + unsigned short len; + compat_caddr_t filter; +}; - len = uprog.len * sizeof(struct sock_filter); - code = memdup_user(uprog.filter, len); - if (IS_ERR(code)) - return PTR_ERR(code); +#define PPPIOCSPASS32 _IOW('t', 71, struct sock_fprog32) +#define PPPIOCSACTIVE32 _IOW('t', 70, struct sock_fprog32) + +static struct bpf_prog *compat_ppp_get_filter(struct sock_fprog32 __user *p) +{ + struct sock_fprog32 uprog32; + struct sock_fprog uprog; - *p = code; - return uprog.len; + if (copy_from_user(&uprog32, p, sizeof(struct sock_fprog32))) + return ERR_PTR(-EFAULT); + uprog.len = uprog32.len; + uprog.filter = compat_ptr(uprog32.filter); + return get_filter(&uprog); } -#endif /* CONFIG_PPP_FILTER */ +#endif +#endif static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ppp_file *pf; struct ppp *ppp; int err = -EFAULT, val, val2, i; - struct ppp_idle idle; + struct ppp_idle32 idle32; + struct ppp_idle64 idle64; struct npioctl npi; int unit, cflags; struct slcompress *vj; @@ -679,9 +709,14 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; case PPPIOCSCOMPRESS: - err = ppp_set_compress(ppp, arg); + { + struct ppp_option_data data; + if (copy_from_user(&data, argp, sizeof(data))) + err = -EFAULT; + else + err = ppp_set_compress(ppp, &data); break; - + } case PPPIOCGUNIT: if (put_user(ppp->file.index, p)) break; @@ -701,10 +736,18 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) err = 0; break; - case PPPIOCGIDLE: - idle.xmit_idle = (jiffies - ppp->last_xmit) / HZ; - idle.recv_idle = (jiffies - ppp->last_recv) / HZ; - if (copy_to_user(argp, &idle, sizeof(idle))) + case PPPIOCGIDLE32: + idle32.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + idle32.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (copy_to_user(argp, &idle32, sizeof(idle32))) + break; + err = 0; + break; + + case PPPIOCGIDLE64: + idle64.xmit_idle = (jiffies - ppp->last_xmit) / HZ; + idle64.recv_idle = (jiffies - ppp->last_recv) / HZ; + if (copy_to_user(argp, &idle64, sizeof(idle64))) break; err = 0; break; @@ -753,55 +796,25 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) #ifdef CONFIG_PPP_FILTER case PPPIOCSPASS: - { - struct sock_filter *code; - - err = get_filter(argp, &code); - if (err >= 0) { - struct bpf_prog *pass_filter = NULL; - struct sock_fprog_kern fprog = { - .len = err, - .filter = code, - }; - - err = 0; - if (fprog.filter) - err = bpf_prog_create(&pass_filter, &fprog); - if (!err) { - ppp_lock(ppp); - if (ppp->pass_filter) - bpf_prog_destroy(ppp->pass_filter); - ppp->pass_filter = pass_filter; - ppp_unlock(ppp); - } - kfree(code); - } - break; - } case PPPIOCSACTIVE: { - struct sock_filter *code; - - err = get_filter(argp, &code); - if (err >= 0) { - struct bpf_prog *active_filter = NULL; - struct sock_fprog_kern fprog = { - .len = err, - .filter = code, - }; + struct bpf_prog *filter = ppp_get_filter(argp); + struct bpf_prog **which; - err = 0; - if (fprog.filter) - err = bpf_prog_create(&active_filter, &fprog); - if (!err) { - ppp_lock(ppp); - if (ppp->active_filter) - bpf_prog_destroy(ppp->active_filter); - ppp->active_filter = active_filter; - ppp_unlock(ppp); - } - kfree(code); + if (IS_ERR(filter)) { + err = PTR_ERR(filter); + break; } + if (cmd == PPPIOCSPASS) + which = &ppp->pass_filter; + else + which = &ppp->active_filter; + ppp_lock(ppp); + if (*which) + bpf_prog_destroy(*which); + *which = filter; + ppp_unlock(ppp); + err = 0; break; } #endif /* CONFIG_PPP_FILTER */ @@ -827,6 +840,77 @@ out: return err; } +#ifdef CONFIG_COMPAT +struct ppp_option_data32 { + compat_uptr_t ptr; + u32 length; + compat_int_t transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +static long ppp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ppp_file *pf; + int err = -ENOIOCTLCMD; + void __user *argp = (void __user *)arg; + + mutex_lock(&ppp_mutex); + + pf = file->private_data; + if (pf && pf->kind == INTERFACE) { + struct ppp *ppp = PF_TO_PPP(pf); + switch (cmd) { +#ifdef CONFIG_PPP_FILTER + case PPPIOCSPASS32: + case PPPIOCSACTIVE32: + { + struct bpf_prog *filter = compat_ppp_get_filter(argp); + struct bpf_prog **which; + + if (IS_ERR(filter)) { + err = PTR_ERR(filter); + break; + } + if (cmd == PPPIOCSPASS32) + which = &ppp->pass_filter; + else + which = &ppp->active_filter; + ppp_lock(ppp); + if (*which) + bpf_prog_destroy(*which); + *which = filter; + ppp_unlock(ppp); + err = 0; + break; + } +#endif /* CONFIG_PPP_FILTER */ + case PPPIOCSCOMPRESS32: + { + struct ppp_option_data32 data32; + if (copy_from_user(&data32, argp, sizeof(data32))) { + err = -EFAULT; + } else { + struct ppp_option_data data = { + .ptr = compat_ptr(data32.ptr), + .length = data32.length, + .transmit = data32.transmit + }; + err = ppp_set_compress(ppp, &data); + } + break; + } + } + } + mutex_unlock(&ppp_mutex); + + /* all other commands have compatible arguments */ + if (err == -ENOIOCTLCMD) + err = ppp_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); + + return err; +} +#endif + static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, struct file *file, unsigned int cmd, unsigned long arg) { @@ -895,6 +979,9 @@ static const struct file_operations ppp_device_fops = { .write = ppp_write, .poll = ppp_poll, .unlocked_ioctl = ppp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ppp_compat_ioctl, +#endif .open = ppp_open, .release = ppp_release, .llseek = noop_llseek, @@ -2734,24 +2821,20 @@ ppp_output_wakeup(struct ppp_channel *chan) /* Process the PPPIOCSCOMPRESS ioctl. */ static int -ppp_set_compress(struct ppp *ppp, unsigned long arg) +ppp_set_compress(struct ppp *ppp, struct ppp_option_data *data) { - int err; + int err = -EFAULT; struct compressor *cp, *ocomp; - struct ppp_option_data data; void *state, *ostate; unsigned char ccp_option[CCP_MAX_OPTION_LENGTH]; - err = -EFAULT; - if (copy_from_user(&data, (void __user *) arg, sizeof(data))) - goto out; - if (data.length > CCP_MAX_OPTION_LENGTH) + if (data->length > CCP_MAX_OPTION_LENGTH) goto out; - if (copy_from_user(ccp_option, (void __user *) data.ptr, data.length)) + if (copy_from_user(ccp_option, data->ptr, data->length)) goto out; err = -EINVAL; - if (data.length < 2 || ccp_option[1] < 2 || ccp_option[1] > data.length) + if (data->length < 2 || ccp_option[1] < 2 || ccp_option[1] > data->length) goto out; cp = try_then_request_module( @@ -2761,8 +2844,8 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) goto out; err = -ENOBUFS; - if (data.transmit) { - state = cp->comp_alloc(ccp_option, data.length); + if (data->transmit) { + state = cp->comp_alloc(ccp_option, data->length); if (state) { ppp_xmit_lock(ppp); ppp->xstate &= ~SC_COMP_RUN; @@ -2780,7 +2863,7 @@ ppp_set_compress(struct ppp *ppp, unsigned long arg) module_put(cp->owner); } else { - state = cp->decomp_alloc(ccp_option, data.length); + state = cp->decomp_alloc(ccp_option, data->length); if (state) { ppp_recv_lock(ppp); ppp->rstate &= ~SC_DECOMP_RUN; diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 3ae70c7e6860..a6d63665ad03 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1123,14 +1123,6 @@ static long tap_ioctl(struct file *file, unsigned int cmd, } } -#ifdef CONFIG_COMPAT -static long tap_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return tap_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations tap_fops = { .owner = THIS_MODULE, .open = tap_open, @@ -1140,9 +1132,7 @@ static const struct file_operations tap_fops = { .poll = tap_poll, .llseek = no_llseek, .unlocked_ioctl = tap_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = tap_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) diff --git a/drivers/nvdimm/btt.c b/drivers/nvdimm/btt.c index 3e9f45aec8d1..0d04ea3d9fd7 100644 --- a/drivers/nvdimm/btt.c +++ b/drivers/nvdimm/btt.c @@ -1261,11 +1261,11 @@ static int btt_read_pg(struct btt *btt, struct bio_integrity_payload *bip, ret = btt_data_read(arena, page, off, postmap, cur_len); if (ret) { - int rc; - /* Media error - set the e_flag */ - rc = btt_map_write(arena, premap, postmap, 0, 1, - NVDIMM_IO_ATOMIC); + if (btt_map_write(arena, premap, postmap, 0, 1, NVDIMM_IO_ATOMIC)) + dev_warn_ratelimited(to_dev(arena), + "Error persistently tracking bad blocks at %#x\n", + premap); goto out_rtt; } @@ -1674,7 +1674,8 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) struct nd_region *nd_region; struct btt_sb *btt_sb; struct btt *btt; - size_t rawsize; + size_t size, rawsize; + int rc; if (!nd_btt->uuid || !nd_btt->ndns || !nd_btt->lbasize) { dev_dbg(&nd_btt->dev, "incomplete btt configuration\n"); @@ -1685,6 +1686,11 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) if (!btt_sb) return -ENOMEM; + size = nvdimm_namespace_capacity(ndns); + rc = devm_namespace_enable(&nd_btt->dev, ndns, size); + if (rc) + return rc; + /* * If this returns < 0, that is ok as it just means there wasn't * an existing BTT, and we're creating a new one. We still need to @@ -1693,7 +1699,7 @@ int nvdimm_namespace_attach_btt(struct nd_namespace_common *ndns) */ nd_btt_version(nd_btt, ndns, btt_sb); - rawsize = nvdimm_namespace_capacity(ndns) - nd_btt->initial_offset; + rawsize = size - nd_btt->initial_offset; if (rawsize < ARENA_MIN_SIZE) { dev_dbg(&nd_btt->dev, "%s must be at least %ld bytes\n", dev_name(&ndns->dev), diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 3508a79110c7..05feb97e11ce 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -25,17 +25,6 @@ static void nd_btt_release(struct device *dev) kfree(nd_btt); } -static struct device_type nd_btt_device_type = { - .name = "nd_btt", - .release = nd_btt_release, -}; - -bool is_nd_btt(struct device *dev) -{ - return dev->type == &nd_btt_device_type; -} -EXPORT_SYMBOL(is_nd_btt); - struct nd_btt *to_nd_btt(struct device *dev) { struct nd_btt *nd_btt = container_of(dev, struct nd_btt, dev); @@ -178,6 +167,18 @@ static const struct attribute_group *nd_btt_attribute_groups[] = { NULL, }; +static const struct device_type nd_btt_device_type = { + .name = "nd_btt", + .release = nd_btt_release, + .groups = nd_btt_attribute_groups, +}; + +bool is_nd_btt(struct device *dev) +{ + return dev->type == &nd_btt_device_type; +} +EXPORT_SYMBOL(is_nd_btt); + static struct device *__nd_btt_create(struct nd_region *nd_region, unsigned long lbasize, u8 *uuid, struct nd_namespace_common *ndns) @@ -204,7 +205,6 @@ static struct device *__nd_btt_create(struct nd_region *nd_region, dev_set_name(dev, "btt%d.%d", nd_region->id, nd_btt->id); dev->parent = &nd_region->dev; dev->type = &nd_btt_device_type; - dev->groups = nd_btt_attribute_groups; device_initialize(&nd_btt->dev); if (ndns && !__nd_attach_ndns(&nd_btt->dev, ndns, &nd_btt->ndns)) { dev_dbg(&ndns->dev, "failed, already claimed by %s\n", diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index d47412dcdf38..a8b515968569 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -300,9 +300,14 @@ static void nvdimm_bus_release(struct device *dev) kfree(nvdimm_bus); } +static const struct device_type nvdimm_bus_dev_type = { + .release = nvdimm_bus_release, + .groups = nvdimm_bus_attribute_groups, +}; + bool is_nvdimm_bus(struct device *dev) { - return dev->release == nvdimm_bus_release; + return dev->type == &nvdimm_bus_dev_type; } struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) @@ -355,7 +360,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, badrange_init(&nvdimm_bus->badrange); nvdimm_bus->nd_desc = nd_desc; nvdimm_bus->dev.parent = parent; - nvdimm_bus->dev.release = nvdimm_bus_release; + nvdimm_bus->dev.type = &nvdimm_bus_dev_type; nvdimm_bus->dev.groups = nd_desc->attr_groups; nvdimm_bus->dev.bus = &nvdimm_bus_type; nvdimm_bus->dev.of_node = nd_desc->of_node; @@ -669,10 +674,9 @@ static struct attribute *nd_device_attributes[] = { /* * nd_device_attribute_group - generic attributes for all devices on an nd bus */ -struct attribute_group nd_device_attribute_group = { +const struct attribute_group nd_device_attribute_group = { .attrs = nd_device_attributes, }; -EXPORT_SYMBOL_GPL(nd_device_attribute_group); static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -681,28 +685,56 @@ static ssize_t numa_node_show(struct device *dev, } static DEVICE_ATTR_RO(numa_node); +static int nvdimm_dev_to_target_node(struct device *dev) +{ + struct device *parent = dev->parent; + struct nd_region *nd_region = NULL; + + if (is_nd_region(dev)) + nd_region = to_nd_region(dev); + else if (parent && is_nd_region(parent)) + nd_region = to_nd_region(parent); + + if (!nd_region) + return NUMA_NO_NODE; + return nd_region->target_node; +} + +static ssize_t target_node_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", nvdimm_dev_to_target_node(dev)); +} +static DEVICE_ATTR_RO(target_node); + static struct attribute *nd_numa_attributes[] = { &dev_attr_numa_node.attr, + &dev_attr_target_node.attr, NULL, }; static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a, int n) { + struct device *dev = container_of(kobj, typeof(*dev), kobj); + if (!IS_ENABLED(CONFIG_NUMA)) return 0; + if (a == &dev_attr_target_node.attr && + nvdimm_dev_to_target_node(dev) == NUMA_NO_NODE) + return 0; + return a->mode; } /* * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus */ -struct attribute_group nd_numa_attribute_group = { +const struct attribute_group nd_numa_attribute_group = { .attrs = nd_numa_attributes, .is_visible = nd_numa_attr_visible, }; -EXPORT_SYMBOL_GPL(nd_numa_attribute_group); int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) { @@ -1227,7 +1259,7 @@ static const struct file_operations nvdimm_bus_fops = { .owner = THIS_MODULE, .open = nd_open, .unlocked_ioctl = bus_ioctl, - .compat_ioctl = bus_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; @@ -1235,7 +1267,7 @@ static const struct file_operations nvdimm_fops = { .owner = THIS_MODULE, .open = nd_open, .unlocked_ioctl = dimm_ioctl, - .compat_ioctl = dimm_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 2985ca949912..45964acba944 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -300,13 +300,14 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns, return rc; } -int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio) +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio, + resource_size_t size) { struct resource *res = &nsio->res; struct nd_namespace_common *ndns = &nsio->common; - nsio->size = resource_size(res); - if (!devm_request_mem_region(dev, res->start, resource_size(res), + nsio->size = size; + if (!devm_request_mem_region(dev, res->start, size, dev_name(&ndns->dev))) { dev_warn(dev, "could not reserve region %pR\n", res); return -EBUSY; @@ -318,12 +319,10 @@ int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio) nvdimm_badblocks_populate(to_nd_region(ndns->dev.parent), &nsio->bb, &nsio->res); - nsio->addr = devm_memremap(dev, res->start, resource_size(res), - ARCH_MEMREMAP_PMEM); + nsio->addr = devm_memremap(dev, res->start, size, ARCH_MEMREMAP_PMEM); return PTR_ERR_OR_ZERO(nsio->addr); } -EXPORT_SYMBOL_GPL(devm_nsio_enable); void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio) { @@ -331,6 +330,5 @@ void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio) devm_memunmap(dev, nsio->addr); devm_exit_badblocks(dev, &nsio->bb); - devm_release_mem_region(dev, res->start, resource_size(res)); + devm_release_mem_region(dev, res->start, nsio->size); } -EXPORT_SYMBOL_GPL(devm_nsio_disable); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index e592c4964674..fe9bd6febdd2 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -385,10 +385,14 @@ static struct attribute *nvdimm_bus_attributes[] = { NULL, }; -struct attribute_group nvdimm_bus_attribute_group = { +static const struct attribute_group nvdimm_bus_attribute_group = { .attrs = nvdimm_bus_attributes, }; -EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); + +const struct attribute_group *nvdimm_bus_attribute_groups[] = { + &nvdimm_bus_attribute_group, + NULL, +}; int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) { diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c index 6d22b0f83b3b..99965077bac4 100644 --- a/drivers/nvdimm/dax_devs.c +++ b/drivers/nvdimm/dax_devs.c @@ -23,17 +23,6 @@ static void nd_dax_release(struct device *dev) kfree(nd_dax); } -static struct device_type nd_dax_device_type = { - .name = "nd_dax", - .release = nd_dax_release, -}; - -bool is_nd_dax(struct device *dev) -{ - return dev ? dev->type == &nd_dax_device_type : false; -} -EXPORT_SYMBOL(is_nd_dax); - struct nd_dax *to_nd_dax(struct device *dev) { struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev); @@ -43,13 +32,18 @@ struct nd_dax *to_nd_dax(struct device *dev) } EXPORT_SYMBOL(to_nd_dax); -static const struct attribute_group *nd_dax_attribute_groups[] = { - &nd_pfn_attribute_group, - &nd_device_attribute_group, - &nd_numa_attribute_group, - NULL, +static const struct device_type nd_dax_device_type = { + .name = "nd_dax", + .release = nd_dax_release, + .groups = nd_pfn_attribute_groups, }; +bool is_nd_dax(struct device *dev) +{ + return dev ? dev->type == &nd_dax_device_type : false; +} +EXPORT_SYMBOL(is_nd_dax); + static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) { struct nd_pfn *nd_pfn; @@ -69,7 +63,6 @@ static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) dev = &nd_pfn->dev; dev_set_name(dev, "dax%d.%d", nd_region->id, nd_pfn->id); - dev->groups = nd_dax_attribute_groups; dev->type = &nd_dax_device_type; dev->parent = &nd_region->dev; diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 196aa44c4936..94ea6dba6b4f 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -202,16 +202,6 @@ static void nvdimm_release(struct device *dev) kfree(nvdimm); } -static struct device_type nvdimm_device_type = { - .name = "nvdimm", - .release = nvdimm_release, -}; - -bool is_nvdimm(struct device *dev) -{ - return dev->type == &nvdimm_device_type; -} - struct nvdimm *to_nvdimm(struct device *dev) { struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); @@ -450,11 +440,27 @@ static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n) return 0; } -struct attribute_group nvdimm_attribute_group = { +static const struct attribute_group nvdimm_attribute_group = { .attrs = nvdimm_attributes, .is_visible = nvdimm_visible, }; -EXPORT_SYMBOL_GPL(nvdimm_attribute_group); + +static const struct attribute_group *nvdimm_attribute_groups[] = { + &nd_device_attribute_group, + &nvdimm_attribute_group, + NULL, +}; + +static const struct device_type nvdimm_device_type = { + .name = "nvdimm", + .release = nvdimm_release, + .groups = nvdimm_attribute_groups, +}; + +bool is_nvdimm(struct device *dev) +{ + return dev->type == &nvdimm_device_type; +} struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, const struct attribute_group **groups, diff --git a/drivers/nvdimm/e820.c b/drivers/nvdimm/e820.c index 87f72f725e4f..e02f60ad6c99 100644 --- a/drivers/nvdimm/e820.c +++ b/drivers/nvdimm/e820.c @@ -8,17 +8,6 @@ #include <linux/libnvdimm.h> #include <linux/module.h> -static const struct attribute_group *e820_pmem_attribute_groups[] = { - &nvdimm_bus_attribute_group, - NULL, -}; - -static const struct attribute_group *e820_pmem_region_attribute_groups[] = { - &nd_region_attribute_group, - &nd_device_attribute_group, - NULL, -}; - static int e820_pmem_remove(struct platform_device *pdev) { struct nvdimm_bus *nvdimm_bus = platform_get_drvdata(pdev); @@ -46,7 +35,6 @@ static int e820_register_one(struct resource *res, void *data) memset(&ndr_desc, 0, sizeof(ndr_desc)); ndr_desc.res = res; - ndr_desc.attr_groups = e820_pmem_region_attribute_groups; ndr_desc.numa_node = e820_range_to_nid(res->start); ndr_desc.target_node = ndr_desc.numa_node; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); @@ -62,7 +50,6 @@ static int e820_pmem_probe(struct platform_device *pdev) struct nvdimm_bus *nvdimm_bus; int rc = -ENXIO; - nd_desc.attr_groups = e820_pmem_attribute_groups; nd_desc.provider_name = "e820"; nd_desc.module = THIS_MODULE; nvdimm_bus = nvdimm_bus_register(dev, &nd_desc); diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index cca0a3ba1d2c..032dc61725ff 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -44,35 +44,9 @@ static void namespace_blk_release(struct device *dev) kfree(nsblk); } -static const struct device_type namespace_io_device_type = { - .name = "nd_namespace_io", - .release = namespace_io_release, -}; - -static const struct device_type namespace_pmem_device_type = { - .name = "nd_namespace_pmem", - .release = namespace_pmem_release, -}; - -static const struct device_type namespace_blk_device_type = { - .name = "nd_namespace_blk", - .release = namespace_blk_release, -}; - -static bool is_namespace_pmem(const struct device *dev) -{ - return dev ? dev->type == &namespace_pmem_device_type : false; -} - -static bool is_namespace_blk(const struct device *dev) -{ - return dev ? dev->type == &namespace_blk_device_type : false; -} - -static bool is_namespace_io(const struct device *dev) -{ - return dev ? dev->type == &namespace_io_device_type : false; -} +static bool is_namespace_pmem(const struct device *dev); +static bool is_namespace_blk(const struct device *dev); +static bool is_namespace_io(const struct device *dev); static int is_uuid_busy(struct device *dev, void *data) { @@ -1329,7 +1303,7 @@ static ssize_t resource_show(struct device *dev, return -ENXIO; return sprintf(buf, "%#llx\n", (unsigned long long) res->start); } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static const unsigned long blk_lbasize_supported[] = { 512, 520, 528, 4096, 4104, 4160, 4224, 0 }; @@ -1510,16 +1484,20 @@ static ssize_t holder_show(struct device *dev, } static DEVICE_ATTR_RO(holder); -static ssize_t __holder_class_store(struct device *dev, const char *buf) +static int __holder_class_store(struct device *dev, const char *buf) { struct nd_namespace_common *ndns = to_ndns(dev); if (dev->driver || ndns->claim) return -EBUSY; - if (sysfs_streq(buf, "btt")) - ndns->claim_class = btt_claim_class(dev); - else if (sysfs_streq(buf, "pfn")) + if (sysfs_streq(buf, "btt")) { + int rc = btt_claim_class(dev); + + if (rc < NVDIMM_CCLASS_NONE) + return rc; + ndns->claim_class = rc; + } else if (sysfs_streq(buf, "pfn")) ndns->claim_class = NVDIMM_CCLASS_PFN; else if (sysfs_streq(buf, "dax")) ndns->claim_class = NVDIMM_CCLASS_DAX; @@ -1528,10 +1506,6 @@ static ssize_t __holder_class_store(struct device *dev, const char *buf) else return -EINVAL; - /* btt_claim_class() could've returned an error */ - if (ndns->claim_class < 0) - return ndns->claim_class; - return 0; } @@ -1539,7 +1513,7 @@ static ssize_t holder_class_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct nd_region *nd_region = to_nd_region(dev->parent); - ssize_t rc; + int rc; nd_device_lock(dev); nvdimm_bus_lock(dev); @@ -1547,7 +1521,7 @@ static ssize_t holder_class_store(struct device *dev, rc = __holder_class_store(dev, buf); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); - dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc); + dev_dbg(dev, "%s(%d)\n", rc < 0 ? "fail " : "", rc); nvdimm_bus_unlock(dev); nd_device_unlock(dev); @@ -1645,11 +1619,8 @@ static umode_t namespace_visible(struct kobject *kobj, { struct device *dev = container_of(kobj, struct device, kobj); - if (a == &dev_attr_resource.attr) { - if (is_namespace_blk(dev)) - return 0; - return 0400; - } + if (a == &dev_attr_resource.attr && is_namespace_blk(dev)) + return 0; if (is_namespace_pmem(dev) || is_namespace_blk(dev)) { if (a == &dev_attr_size.attr) @@ -1680,6 +1651,39 @@ static const struct attribute_group *nd_namespace_attribute_groups[] = { NULL, }; +static const struct device_type namespace_io_device_type = { + .name = "nd_namespace_io", + .release = namespace_io_release, + .groups = nd_namespace_attribute_groups, +}; + +static const struct device_type namespace_pmem_device_type = { + .name = "nd_namespace_pmem", + .release = namespace_pmem_release, + .groups = nd_namespace_attribute_groups, +}; + +static const struct device_type namespace_blk_device_type = { + .name = "nd_namespace_blk", + .release = namespace_blk_release, + .groups = nd_namespace_attribute_groups, +}; + +static bool is_namespace_pmem(const struct device *dev) +{ + return dev ? dev->type == &namespace_pmem_device_type : false; +} + +static bool is_namespace_blk(const struct device *dev) +{ + return dev ? dev->type == &namespace_blk_device_type : false; +} + +static bool is_namespace_io(const struct device *dev) +{ + return dev ? dev->type == &namespace_io_device_type : false; +} + struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) { struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL; @@ -1759,6 +1763,23 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev) } EXPORT_SYMBOL(nvdimm_namespace_common_probe); +int devm_namespace_enable(struct device *dev, struct nd_namespace_common *ndns, + resource_size_t size) +{ + if (is_namespace_blk(&ndns->dev)) + return 0; + return devm_nsio_enable(dev, to_nd_namespace_io(&ndns->dev), size); +} +EXPORT_SYMBOL_GPL(devm_namespace_enable); + +void devm_namespace_disable(struct device *dev, struct nd_namespace_common *ndns) +{ + if (is_namespace_blk(&ndns->dev)) + return; + devm_nsio_disable(dev, to_nd_namespace_io(&ndns->dev)); +} +EXPORT_SYMBOL_GPL(devm_namespace_disable); + static struct device **create_namespace_io(struct nd_region *nd_region) { struct nd_namespace_io *nsio; @@ -2078,7 +2099,6 @@ static struct device *nd_namespace_blk_create(struct nd_region *nd_region) } dev_set_name(dev, "namespace%d.%d", nd_region->id, nsblk->id); dev->parent = &nd_region->dev; - dev->groups = nd_namespace_attribute_groups; return &nsblk->common.dev; } @@ -2109,7 +2129,6 @@ static struct device *nd_namespace_pmem_create(struct nd_region *nd_region) return NULL; } dev_set_name(dev, "namespace%d.%d", nd_region->id, nspm->id); - dev->groups = nd_namespace_attribute_groups; nd_namespace_pmem_set_resource(nd_region, nspm, 0); return dev; @@ -2608,7 +2627,6 @@ int nd_region_register_namespaces(struct nd_region *nd_region, int *err) if (id < 0) break; dev_set_name(dev, "namespace%d.%d", nd_region->id, id); - dev->groups = nd_namespace_attribute_groups; nd_device_register(dev); } if (i) diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index aa059439fca0..ddb9d97d9129 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -123,11 +123,7 @@ void nd_region_create_dax_seed(struct nd_region *nd_region); int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus); void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus); void nd_synchronize(void); -int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus); -int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus); -int nvdimm_bus_init_interleave_sets(struct nvdimm_bus *nvdimm_bus); void __nd_device_register(struct device *dev); -int nd_match_dimm(struct device *dev, void *data); struct nd_label_id; char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags); bool nd_is_uuid_unique(struct device *dev, u8 *uuid); @@ -170,6 +166,23 @@ ssize_t nd_namespace_store(struct device *dev, struct nd_pfn *to_nd_pfn_safe(struct device *dev); bool is_nvdimm_bus(struct device *dev); +#if IS_ENABLED(CONFIG_ND_CLAIM) +int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio, + resource_size_t size); +void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio); +#else +static inline int devm_nsio_enable(struct device *dev, + struct nd_namespace_io *nsio, resource_size_t size) +{ + return -ENXIO; +} + +static inline void devm_nsio_disable(struct device *dev, + struct nd_namespace_io *nsio) +{ +} +#endif + #ifdef CONFIG_PROVE_LOCKING extern struct class *nd_class; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index ee5c04070ef9..c9f6a5b5253a 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -212,6 +212,11 @@ struct nd_dax { struct nd_pfn nd_pfn; }; +static inline u32 nd_info_block_reserve(void) +{ + return ALIGN(SZ_8K, PAGE_SIZE); +} + enum nd_async_mode { ND_SYNC, ND_ASYNC, @@ -234,6 +239,9 @@ int __init nd_label_init(void); void nvdimm_exit(void); void nd_region_exit(void); struct nvdimm; +extern const struct attribute_group nd_device_attribute_group; +extern const struct attribute_group nd_numa_attribute_group; +extern const struct attribute_group *nvdimm_bus_attribute_groups[]; struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping); int nvdimm_check_config_data(struct device *dev); int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd); @@ -297,7 +305,7 @@ struct device *nd_pfn_create(struct nd_region *nd_region); struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, struct nd_namespace_common *ndns); int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig); -extern struct attribute_group nd_pfn_attribute_group; +extern const struct attribute_group *nd_pfn_attribute_groups[]; #else static inline int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) @@ -370,29 +378,20 @@ const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns, unsigned int pmem_sector_size(struct nd_namespace_common *ndns); void nvdimm_badblocks_populate(struct nd_region *nd_region, struct badblocks *bb, const struct resource *res); +int devm_namespace_enable(struct device *dev, struct nd_namespace_common *ndns, + resource_size_t size); +void devm_namespace_disable(struct device *dev, + struct nd_namespace_common *ndns); #if IS_ENABLED(CONFIG_ND_CLAIM) - /* max struct page size independent of kernel config */ #define MAX_STRUCT_PAGE_SIZE 64 - int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap); -int devm_nsio_enable(struct device *dev, struct nd_namespace_io *nsio); -void devm_nsio_disable(struct device *dev, struct nd_namespace_io *nsio); #else static inline int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) { return -ENXIO; } -static inline int devm_nsio_enable(struct device *dev, - struct nd_namespace_io *nsio) -{ - return -ENXIO; -} -static inline void devm_nsio_disable(struct device *dev, - struct nd_namespace_io *nsio) -{ -} #endif int nd_blk_region_init(struct nd_region *nd_region); int nd_region_activate(struct nd_region *nd_region); diff --git a/drivers/nvdimm/of_pmem.c b/drivers/nvdimm/of_pmem.c index 97187d6c0bdb..8224d1431ea9 100644 --- a/drivers/nvdimm/of_pmem.c +++ b/drivers/nvdimm/of_pmem.c @@ -9,17 +9,6 @@ #include <linux/ioport.h> #include <linux/slab.h> -static const struct attribute_group *region_attr_groups[] = { - &nd_region_attribute_group, - &nd_device_attribute_group, - NULL, -}; - -static const struct attribute_group *bus_attr_groups[] = { - &nvdimm_bus_attribute_group, - NULL, -}; - struct of_pmem_private { struct nvdimm_bus_descriptor bus_desc; struct nvdimm_bus *bus; @@ -41,7 +30,6 @@ static int of_pmem_region_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->bus_desc.attr_groups = bus_attr_groups; priv->bus_desc.provider_name = kstrdup(pdev->name, GFP_KERNEL); priv->bus_desc.module = THIS_MODULE; priv->bus_desc.of_node = np; @@ -66,7 +54,6 @@ static int of_pmem_region_probe(struct platform_device *pdev) * structures so passing a stack pointer is fine. */ memset(&ndr_desc, 0, sizeof(ndr_desc)); - ndr_desc.attr_groups = region_attr_groups; ndr_desc.numa_node = dev_to_node(&pdev->dev); ndr_desc.target_node = ndr_desc.numa_node; ndr_desc.res = &pdev->resource[i]; diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 60d81fae06ee..b94f7a7e94b8 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -26,17 +26,6 @@ static void nd_pfn_release(struct device *dev) kfree(nd_pfn); } -static struct device_type nd_pfn_device_type = { - .name = "nd_pfn", - .release = nd_pfn_release, -}; - -bool is_nd_pfn(struct device *dev) -{ - return dev ? dev->type == &nd_pfn_device_type : false; -} -EXPORT_SYMBOL(is_nd_pfn); - struct nd_pfn *to_nd_pfn(struct device *dev) { struct nd_pfn *nd_pfn = container_of(dev, struct nd_pfn, dev); @@ -229,7 +218,7 @@ static ssize_t resource_show(struct device *dev, return rc; } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static ssize_t size_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -280,25 +269,29 @@ static struct attribute *nd_pfn_attributes[] = { NULL, }; -static umode_t pfn_visible(struct kobject *kobj, struct attribute *a, int n) -{ - if (a == &dev_attr_resource.attr) - return 0400; - return a->mode; -} - -struct attribute_group nd_pfn_attribute_group = { +static struct attribute_group nd_pfn_attribute_group = { .attrs = nd_pfn_attributes, - .is_visible = pfn_visible, }; -static const struct attribute_group *nd_pfn_attribute_groups[] = { +const struct attribute_group *nd_pfn_attribute_groups[] = { &nd_pfn_attribute_group, &nd_device_attribute_group, &nd_numa_attribute_group, NULL, }; +static const struct device_type nd_pfn_device_type = { + .name = "nd_pfn", + .release = nd_pfn_release, + .groups = nd_pfn_attribute_groups, +}; + +bool is_nd_pfn(struct device *dev) +{ + return dev ? dev->type == &nd_pfn_device_type : false; +} +EXPORT_SYMBOL(is_nd_pfn); + struct device *nd_pfn_devinit(struct nd_pfn *nd_pfn, struct nd_namespace_common *ndns) { @@ -337,7 +330,6 @@ static struct nd_pfn *nd_pfn_alloc(struct nd_region *nd_region) dev = &nd_pfn->dev; dev_set_name(dev, "pfn%d.%d", nd_region->id, nd_pfn->id); - dev->groups = nd_pfn_attribute_groups; dev->type = &nd_pfn_device_type; dev->parent = &nd_region->dev; @@ -382,6 +374,15 @@ static int nd_pfn_clear_memmap_errors(struct nd_pfn *nd_pfn) meta_start = (SZ_4K + sizeof(*pfn_sb)) >> 9; meta_num = (le64_to_cpu(pfn_sb->dataoff) >> 9) - meta_start; + /* + * re-enable the namespace with correct size so that we can access + * the device memmap area. + */ + devm_namespace_disable(&nd_pfn->dev, ndns); + rc = devm_namespace_enable(&nd_pfn->dev, ndns, le64_to_cpu(pfn_sb->dataoff)); + if (rc) + return rc; + do { unsigned long zero_len; u64 nsoff; @@ -591,7 +592,7 @@ int nd_pfn_validate(struct nd_pfn *nd_pfn, const char *sig) return -ENXIO; } - return nd_pfn_clear_memmap_errors(nd_pfn); + return 0; } EXPORT_SYMBOL(nd_pfn_validate); @@ -635,11 +636,6 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) } EXPORT_SYMBOL(nd_pfn_probe); -static u32 info_block_reserve(void) -{ - return ALIGN(SZ_8K, PAGE_SIZE); -} - /* * We hotplug memory at sub-section granularity, pad the reserved area * from the previous section base to the namespace base address. @@ -653,7 +649,7 @@ static unsigned long init_altmap_base(resource_size_t base) static unsigned long init_altmap_reserve(resource_size_t base) { - unsigned long reserve = info_block_reserve() >> PAGE_SHIFT; + unsigned long reserve = nd_info_block_reserve() >> PAGE_SHIFT; unsigned long base_pfn = PHYS_PFN(base); reserve += base_pfn - SUBSECTION_ALIGN_DOWN(base_pfn); @@ -668,7 +664,7 @@ static int __nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap) u64 offset = le64_to_cpu(pfn_sb->dataoff); u32 start_pad = __le32_to_cpu(pfn_sb->start_pad); u32 end_trunc = __le32_to_cpu(pfn_sb->end_trunc); - u32 reserve = info_block_reserve(); + u32 reserve = nd_info_block_reserve(); struct nd_namespace_common *ndns = nd_pfn->ndns; struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev); resource_size_t base = nsio->res.start + start_pad; @@ -729,6 +725,8 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) sig = PFN_SIG; rc = nd_pfn_validate(nd_pfn, sig); + if (rc == 0) + return nd_pfn_clear_memmap_errors(nd_pfn); if (rc != -ENODEV) return rc; @@ -796,6 +794,10 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb); pfn_sb->checksum = cpu_to_le64(checksum); + rc = nd_pfn_clear_memmap_errors(nd_pfn); + if (rc) + return rc; + return nvdimm_write_bytes(ndns, SZ_4K, pfn_sb, sizeof(*pfn_sb), 0); } diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index f9f76f6ba07b..ad8e4df1282b 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -28,7 +28,6 @@ #include "pmem.h" #include "pfn.h" #include "nd.h" -#include "nd-core.h" static struct device *to_dev(struct pmem_device *pmem) { @@ -372,6 +371,10 @@ static int pmem_attach_disk(struct device *dev, if (!pmem) return -ENOMEM; + rc = devm_namespace_enable(dev, ndns, nd_info_block_reserve()); + if (rc) + return rc; + /* while nsio_rw_bytes is active, parse a pfn info block if present */ if (is_nd_pfn(dev)) { nd_pfn = to_nd_pfn(dev); @@ -381,7 +384,7 @@ static int pmem_attach_disk(struct device *dev, } /* we're attaching a block device, disable raw namespace access */ - devm_nsio_disable(dev, nsio); + devm_namespace_disable(dev, ndns); dev_set_drvdata(dev, pmem); pmem->phys_addr = res->start; @@ -497,15 +500,16 @@ static int nd_pmem_probe(struct device *dev) if (IS_ERR(ndns)) return PTR_ERR(ndns); - if (devm_nsio_enable(dev, to_nd_namespace_io(&ndns->dev))) - return -ENXIO; - if (is_nd_btt(dev)) return nvdimm_namespace_attach_btt(ndns); if (is_nd_pfn(dev)) return pmem_attach_disk(dev, ndns); + ret = devm_namespace_enable(dev, ndns, nd_info_block_reserve()); + if (ret) + return ret; + ret = nd_btt_probe(dev, ndns); if (ret == 0) return -ENXIO; @@ -532,6 +536,10 @@ static int nd_pmem_probe(struct device *dev) return -ENXIO; else if (ret == -EOPNOTSUPP) return ret; + + /* probe complete, attach handles namespace enabling */ + devm_namespace_disable(dev, ndns); + return pmem_attach_disk(dev, ndns); } diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index fbf34cf688f4..a19e535830d9 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -140,36 +140,6 @@ static void nd_region_release(struct device *dev) kfree(nd_region); } -static struct device_type nd_blk_device_type = { - .name = "nd_blk", - .release = nd_region_release, -}; - -static struct device_type nd_pmem_device_type = { - .name = "nd_pmem", - .release = nd_region_release, -}; - -static struct device_type nd_volatile_device_type = { - .name = "nd_volatile", - .release = nd_region_release, -}; - -bool is_nd_pmem(struct device *dev) -{ - return dev ? dev->type == &nd_pmem_device_type : false; -} - -bool is_nd_blk(struct device *dev) -{ - return dev ? dev->type == &nd_blk_device_type : false; -} - -bool is_nd_volatile(struct device *dev) -{ - return dev ? dev->type == &nd_volatile_device_type : false; -} - struct nd_region *to_nd_region(struct device *dev) { struct nd_region *nd_region = container_of(dev, struct nd_region, dev); @@ -583,7 +553,7 @@ static ssize_t resource_show(struct device *dev, return sprintf(buf, "%#llx\n", nd_region->ndr_start); } -static DEVICE_ATTR_RO(resource); +static DEVICE_ATTR(resource, 0400, resource_show, NULL); static ssize_t persistence_domain_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -635,12 +605,8 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) if (!is_memory(dev) && a == &dev_attr_badblocks.attr) return 0; - if (a == &dev_attr_resource.attr) { - if (is_memory(dev)) - return 0400; - else - return 0; - } + if (a == &dev_attr_resource.attr && !is_memory(dev)) + return 0; if (a == &dev_attr_deep_flush.attr) { int has_flush = nvdimm_has_flush(nd_region); @@ -674,80 +640,6 @@ static umode_t region_visible(struct kobject *kobj, struct attribute *a, int n) return 0; } -struct attribute_group nd_region_attribute_group = { - .attrs = nd_region_attributes, - .is_visible = region_visible, -}; -EXPORT_SYMBOL_GPL(nd_region_attribute_group); - -u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, - struct nd_namespace_index *nsindex) -{ - struct nd_interleave_set *nd_set = nd_region->nd_set; - - if (!nd_set) - return 0; - - if (nsindex && __le16_to_cpu(nsindex->major) == 1 - && __le16_to_cpu(nsindex->minor) == 1) - return nd_set->cookie1; - return nd_set->cookie2; -} - -u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) -{ - struct nd_interleave_set *nd_set = nd_region->nd_set; - - if (nd_set) - return nd_set->altcookie; - return 0; -} - -void nd_mapping_free_labels(struct nd_mapping *nd_mapping) -{ - struct nd_label_ent *label_ent, *e; - - lockdep_assert_held(&nd_mapping->lock); - list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { - list_del(&label_ent->list); - kfree(label_ent); - } -} - -/* - * When a namespace is activated create new seeds for the next - * namespace, or namespace-personality to be configured. - */ -void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) -{ - nvdimm_bus_lock(dev); - if (nd_region->ns_seed == dev) { - nd_region_create_ns_seed(nd_region); - } else if (is_nd_btt(dev)) { - struct nd_btt *nd_btt = to_nd_btt(dev); - - if (nd_region->btt_seed == dev) - nd_region_create_btt_seed(nd_region); - if (nd_region->ns_seed == &nd_btt->ndns->dev) - nd_region_create_ns_seed(nd_region); - } else if (is_nd_pfn(dev)) { - struct nd_pfn *nd_pfn = to_nd_pfn(dev); - - if (nd_region->pfn_seed == dev) - nd_region_create_pfn_seed(nd_region); - if (nd_region->ns_seed == &nd_pfn->ndns->dev) - nd_region_create_ns_seed(nd_region); - } else if (is_nd_dax(dev)) { - struct nd_dax *nd_dax = to_nd_dax(dev); - - if (nd_region->dax_seed == dev) - nd_region_create_dax_seed(nd_region); - if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) - nd_region_create_ns_seed(nd_region); - } - nvdimm_bus_unlock(dev); -} - static ssize_t mappingN(struct device *dev, char *buf, int n) { struct nd_region *nd_region = to_nd_region(dev); @@ -855,11 +747,124 @@ static struct attribute *mapping_attributes[] = { NULL, }; -struct attribute_group nd_mapping_attribute_group = { +static const struct attribute_group nd_mapping_attribute_group = { .is_visible = mapping_visible, .attrs = mapping_attributes, }; -EXPORT_SYMBOL_GPL(nd_mapping_attribute_group); + +static const struct attribute_group nd_region_attribute_group = { + .attrs = nd_region_attributes, + .is_visible = region_visible, +}; + +static const struct attribute_group *nd_region_attribute_groups[] = { + &nd_device_attribute_group, + &nd_region_attribute_group, + &nd_numa_attribute_group, + &nd_mapping_attribute_group, + NULL, +}; + +static const struct device_type nd_blk_device_type = { + .name = "nd_blk", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +static const struct device_type nd_pmem_device_type = { + .name = "nd_pmem", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +static const struct device_type nd_volatile_device_type = { + .name = "nd_volatile", + .release = nd_region_release, + .groups = nd_region_attribute_groups, +}; + +bool is_nd_pmem(struct device *dev) +{ + return dev ? dev->type == &nd_pmem_device_type : false; +} + +bool is_nd_blk(struct device *dev) +{ + return dev ? dev->type == &nd_blk_device_type : false; +} + +bool is_nd_volatile(struct device *dev) +{ + return dev ? dev->type == &nd_volatile_device_type : false; +} + +u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, + struct nd_namespace_index *nsindex) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (!nd_set) + return 0; + + if (nsindex && __le16_to_cpu(nsindex->major) == 1 + && __le16_to_cpu(nsindex->minor) == 1) + return nd_set->cookie1; + return nd_set->cookie2; +} + +u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region) +{ + struct nd_interleave_set *nd_set = nd_region->nd_set; + + if (nd_set) + return nd_set->altcookie; + return 0; +} + +void nd_mapping_free_labels(struct nd_mapping *nd_mapping) +{ + struct nd_label_ent *label_ent, *e; + + lockdep_assert_held(&nd_mapping->lock); + list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) { + list_del(&label_ent->list); + kfree(label_ent); + } +} + +/* + * When a namespace is activated create new seeds for the next + * namespace, or namespace-personality to be configured. + */ +void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) +{ + nvdimm_bus_lock(dev); + if (nd_region->ns_seed == dev) { + nd_region_create_ns_seed(nd_region); + } else if (is_nd_btt(dev)) { + struct nd_btt *nd_btt = to_nd_btt(dev); + + if (nd_region->btt_seed == dev) + nd_region_create_btt_seed(nd_region); + if (nd_region->ns_seed == &nd_btt->ndns->dev) + nd_region_create_ns_seed(nd_region); + } else if (is_nd_pfn(dev)) { + struct nd_pfn *nd_pfn = to_nd_pfn(dev); + + if (nd_region->pfn_seed == dev) + nd_region_create_pfn_seed(nd_region); + if (nd_region->ns_seed == &nd_pfn->ndns->dev) + nd_region_create_ns_seed(nd_region); + } else if (is_nd_dax(dev)) { + struct nd_dax *nd_dax = to_nd_dax(dev); + + if (nd_region->dax_seed == dev) + nd_region_create_dax_seed(nd_region); + if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) + nd_region_create_ns_seed(nd_region); + } + nvdimm_bus_unlock(dev); +} int nd_blk_region_init(struct nd_region *nd_region) { @@ -931,8 +936,8 @@ void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane) EXPORT_SYMBOL(nd_region_release_lane); static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus, - struct nd_region_desc *ndr_desc, struct device_type *dev_type, - const char *caller) + struct nd_region_desc *ndr_desc, + const struct device_type *dev_type, const char *caller) { struct nd_region *nd_region; struct device *dev; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 8e8527408db3..6ec589268b9d 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2998,7 +2998,7 @@ static const struct file_operations nvme_dev_fops = { .owner = THIS_MODULE, .open = nvme_dev_open, .unlocked_ioctl = nvme_dev_ioctl, - .compat_ioctl = nvme_dev_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static ssize_t nvme_sysfs_reset(struct device *dev, diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 18627bb21e9e..951f7f216fb3 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -154,11 +154,11 @@ static enum pci_bus_speed get_max_bus_speed(struct slot *slot) return speed; } -static int get_children_props(struct device_node *dn, const int **drc_indexes, - const int **drc_names, const int **drc_types, - const int **drc_power_domains) +static int get_children_props(struct device_node *dn, const __be32 **drc_indexes, + const __be32 **drc_names, const __be32 **drc_types, + const __be32 **drc_power_domains) { - const int *indexes, *names, *types, *domains; + const __be32 *indexes, *names, *types, *domains; indexes = of_get_property(dn, "ibm,drc-indexes", NULL); names = of_get_property(dn, "ibm,drc-names", NULL); @@ -194,8 +194,8 @@ static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name, char *drc_type, unsigned int my_index) { char *name_tmp, *type_tmp; - const int *indexes, *names; - const int *types, *domains; + const __be32 *indexes, *names; + const __be32 *types, *domains; int i, rc; rc = get_children_props(dn->parent, &indexes, &names, &types, &domains); @@ -208,7 +208,7 @@ static int rpaphp_check_drc_props_v1(struct device_node *dn, char *drc_name, /* Iterate through parent properties, looking for my-drc-index */ for (i = 0; i < be32_to_cpu(indexes[0]); i++) { - if ((unsigned int) indexes[i + 1] == my_index) + if (be32_to_cpu(indexes[i + 1]) == my_index) break; name_tmp += (strlen(name_tmp) + 1); @@ -239,6 +239,8 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name, value = of_prop_next_u32(info, NULL, &entries); if (!value) return -EINVAL; + else + value++; for (j = 0; j < entries; j++) { of_read_drc_info_cell(&info, &value, &drc); @@ -246,9 +248,10 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name, /* Should now know end of current entry */ /* Found it */ - if (my_index <= drc.last_drc_index) { + if (my_index >= drc.drc_index_start && my_index <= drc.last_drc_index) { + int index = my_index - drc.drc_index_start; sprintf(cell_drc_name, "%s%d", drc.drc_name_prefix, - my_index); + drc.drc_name_suffix_start + index); break; } } @@ -265,7 +268,7 @@ static int rpaphp_check_drc_props_v2(struct device_node *dn, char *drc_name, int rpaphp_check_drc_props(struct device_node *dn, char *drc_name, char *drc_type) { - const unsigned int *my_index; + const __be32 *my_index; my_index = of_get_property(dn, "ibm,my-drc-index", NULL); if (!my_index) { @@ -273,12 +276,12 @@ int rpaphp_check_drc_props(struct device_node *dn, char *drc_name, return -EINVAL; } - if (firmware_has_feature(FW_FEATURE_DRC_INFO)) + if (of_find_property(dn->parent, "ibm,drc-info", NULL)) return rpaphp_check_drc_props_v2(dn, drc_name, drc_type, - *my_index); + be32_to_cpu(*my_index)); else return rpaphp_check_drc_props_v1(dn, drc_name, drc_type, - *my_index); + be32_to_cpu(*my_index)); } EXPORT_SYMBOL_GPL(rpaphp_check_drc_props); @@ -309,10 +312,11 @@ static int is_php_type(char *drc_type) * for built-in pci slots (even when the built-in slots are * dlparable.) */ -static int is_php_dn(struct device_node *dn, const int **indexes, - const int **names, const int **types, const int **power_domains) +static int is_php_dn(struct device_node *dn, const __be32 **indexes, + const __be32 **names, const __be32 **types, + const __be32 **power_domains) { - const int *drc_types; + const __be32 *drc_types; int rc; rc = get_children_props(dn, indexes, names, &drc_types, power_domains); @@ -326,33 +330,55 @@ static int is_php_dn(struct device_node *dn, const int **indexes, return 1; } -/** - * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem. - * @dn: device node of slot - * - * This subroutine will register a hotpluggable slot with the - * PCI hotplug infrastructure. This routine is typically called - * during boot time, if the hotplug slots are present at boot time, - * or is called later, by the dlpar add code, if the slot is - * being dynamically added during runtime. - * - * If the device node points at an embedded (built-in) slot, this - * routine will just return without doing anything, since embedded - * slots cannot be hotplugged. - * - * To remove a slot, it suffices to call rpaphp_deregister_slot(). - */ -int rpaphp_add_slot(struct device_node *dn) +static int rpaphp_drc_info_add_slot(struct device_node *dn) { struct slot *slot; + struct property *info; + struct of_drc_info drc; + char drc_name[MAX_DRC_NAME_LEN]; + const __be32 *cur; + u32 count; int retval = 0; - int i; - const int *indexes, *names, *types, *power_domains; - char *name, *type; - if (!dn->name || strcmp(dn->name, "pci")) + info = of_find_property(dn, "ibm,drc-info", NULL); + if (!info) + return 0; + + cur = of_prop_next_u32(info, NULL, &count); + if (cur) + cur++; + else return 0; + of_read_drc_info_cell(&info, &cur, &drc); + if (!is_php_type(drc.drc_type)) + return 0; + + sprintf(drc_name, "%s%d", drc.drc_name_prefix, drc.drc_name_suffix_start); + + slot = alloc_slot_struct(dn, drc.drc_index_start, drc_name, drc.drc_power_domain); + if (!slot) + return -ENOMEM; + + slot->type = simple_strtoul(drc.drc_type, NULL, 10); + retval = rpaphp_enable_slot(slot); + if (!retval) + retval = rpaphp_register_slot(slot); + + if (retval) + dealloc_slot_struct(slot); + + return retval; +} + +static int rpaphp_drc_add_slot(struct device_node *dn) +{ + struct slot *slot; + int retval = 0; + int i; + const __be32 *indexes, *names, *types, *power_domains; + char *name, *type; + /* If this is not a hotplug slot, return without doing anything. */ if (!is_php_dn(dn, &indexes, &names, &types, &power_domains)) return 0; @@ -391,6 +417,33 @@ int rpaphp_add_slot(struct device_node *dn) /* XXX FIXME: reports a failure only if last entry in loop failed */ return retval; } + +/** + * rpaphp_add_slot -- declare a hotplug slot to the hotplug subsystem. + * @dn: device node of slot + * + * This subroutine will register a hotpluggable slot with the + * PCI hotplug infrastructure. This routine is typically called + * during boot time, if the hotplug slots are present at boot time, + * or is called later, by the dlpar add code, if the slot is + * being dynamically added during runtime. + * + * If the device node points at an embedded (built-in) slot, this + * routine will just return without doing anything, since embedded + * slots cannot be hotplugged. + * + * To remove a slot, it suffices to call rpaphp_deregister_slot(). + */ +int rpaphp_add_slot(struct device_node *dn) +{ + if (!dn->name || strcmp(dn->name, "pci")) + return 0; + + if (of_find_property(dn, "ibm,drc-info", NULL)) + return rpaphp_drc_info_add_slot(dn); + else + return rpaphp_drc_add_slot(dn); +} EXPORT_SYMBOL_GPL(rpaphp_add_slot); static void __exit cleanup_slots(void) diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index 8c94cd3fd1f2..66610f04d76d 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -1025,7 +1025,7 @@ static const struct file_operations switchtec_fops = { .read = switchtec_dev_read, .poll = switchtec_dev_poll, .unlocked_ioctl = switchtec_dev_ioctl, - .compat_ioctl = switchtec_dev_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static void link_event_work(struct work_struct *work) diff --git a/drivers/pinctrl/pinctrl-equilibrium.c b/drivers/pinctrl/pinctrl-equilibrium.c index 36c9072c5ece..067271b7d35a 100644 --- a/drivers/pinctrl/pinctrl-equilibrium.c +++ b/drivers/pinctrl/pinctrl-equilibrium.c @@ -942,3 +942,4 @@ module_platform_driver(eqbr_pinctrl_driver); MODULE_AUTHOR("Zhu Yixin <yixin.zhu@intel.com>, Rahul Tanwar <rahul.tanwar@intel.com>"); MODULE_DESCRIPTION("Pinctrl Driver for LGM SoC (Equilibrium)"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index 530fe7e31397..56e037623b29 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -41,7 +41,19 @@ config MLXBF_TMFIFO depends on VIRTIO_CONSOLE && VIRTIO_NET help Say y here to enable TmFifo support. The TmFifo driver provides - platform driver support for the TmFifo which supports console - and networking based on the virtio framework. + platform driver support for the TmFifo which supports console + and networking based on the virtio framework. + +config MLXBF_BOOTCTL + tristate "Mellanox BlueField Firmware Boot Control driver" + depends on ARM64 + depends on ACPI + help + The Mellanox BlueField firmware implements functionality to + request swapping the primary and alternate eMMC boot partition, + and to set up a watchdog that can undo that swap if the system + does not boot up correctly. This driver provides sysfs access + to the userspace tools, to be used in conjunction with the eMMC + device driver to do necessary initial swap of the boot partition. endif # MELLANOX_PLATFORM diff --git a/drivers/platform/mellanox/Makefile b/drivers/platform/mellanox/Makefile index a229bda18fd9..499623ccf2fe 100644 --- a/drivers/platform/mellanox/Makefile +++ b/drivers/platform/mellanox/Makefile @@ -3,6 +3,7 @@ # Makefile for linux/drivers/platform/mellanox # Mellanox Platform-Specific Drivers # +obj-$(CONFIG_MLXBF_BOOTCTL) += mlxbf-bootctl.o obj-$(CONFIG_MLXBF_TMFIFO) += mlxbf-tmfifo.o obj-$(CONFIG_MLXREG_HOTPLUG) += mlxreg-hotplug.o obj-$(CONFIG_MLXREG_IO) += mlxreg-io.o diff --git a/drivers/platform/mellanox/mlxbf-bootctl.c b/drivers/platform/mellanox/mlxbf-bootctl.c new file mode 100644 index 000000000000..61753b648506 --- /dev/null +++ b/drivers/platform/mellanox/mlxbf-bootctl.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Mellanox boot control driver + * + * This driver provides a sysfs interface for systems management + * software to manage reset-time actions. + * + * Copyright (C) 2019 Mellanox Technologies + */ + +#include <linux/acpi.h> +#include <linux/arm-smccc.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "mlxbf-bootctl.h" + +#define MLXBF_BOOTCTL_SB_SECURE_MASK 0x03 +#define MLXBF_BOOTCTL_SB_TEST_MASK 0x0c + +#define MLXBF_SB_KEY_NUM 4 + +/* UUID used to probe ATF service. */ +static const char *mlxbf_bootctl_svc_uuid_str = + "89c036b4-e7d7-11e6-8797-001aca00bfc4"; + +struct mlxbf_bootctl_name { + u32 value; + const char *name; +}; + +static struct mlxbf_bootctl_name boot_names[] = { + { MLXBF_BOOTCTL_EXTERNAL, "external" }, + { MLXBF_BOOTCTL_EMMC, "emmc" }, + { MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" }, + { MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" }, + { MLXBF_BOOTCTL_NONE, "none" }, +}; + +static const char * const mlxbf_bootctl_lifecycle_states[] = { + [0] = "Production", + [1] = "GA Secured", + [2] = "GA Non-Secured", + [3] = "RMA", +}; + +/* ARM SMC call which is atomic and no need for lock. */ +static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) +{ + struct arm_smccc_res res; + + arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res); + + return res.a0; +} + +/* Return the action in integer or an error code. */ +static int mlxbf_bootctl_reset_action_to_val(const char *action) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(boot_names); i++) + if (sysfs_streq(boot_names[i].name, action)) + return boot_names[i].value; + + return -EINVAL; +} + +/* Return the action in string. */ +static const char *mlxbf_bootctl_action_to_string(int action) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(boot_names); i++) + if (boot_names[i].value == action) + return boot_names[i].name; + + return "invalid action"; +} + +static ssize_t post_reset_wdog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t post_reset_wdog_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long value; + int ret; + + ret = kstrtoul(buf, 10, &value); + if (ret) + return ret; + + ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t mlxbf_bootctl_show(int smc_op, char *buf) +{ + int action; + + action = mlxbf_bootctl_smc(smc_op, 0); + if (action < 0) + return action; + + return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action)); +} + +static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count) +{ + int ret, action; + + action = mlxbf_bootctl_reset_action_to_val(buf); + if (action < 0) + return action; + + ret = mlxbf_bootctl_smc(smc_op, action); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t reset_action_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf); +} + +static ssize_t reset_action_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count); +} + +static ssize_t second_reset_action_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf); +} + +static ssize_t second_reset_action_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf, + count); +} + +static ssize_t lifecycle_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int lc_state; + + lc_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, + MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE); + if (lc_state < 0) + return lc_state; + + lc_state &= + MLXBF_BOOTCTL_SB_TEST_MASK | MLXBF_BOOTCTL_SB_SECURE_MASK; + + /* + * If the test bits are set, we specify that the current state may be + * due to using the test bits. + */ + if (lc_state & MLXBF_BOOTCTL_SB_TEST_MASK) { + lc_state &= MLXBF_BOOTCTL_SB_SECURE_MASK; + + return sprintf(buf, "%s(test)\n", + mlxbf_bootctl_lifecycle_states[lc_state]); + } + + return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]); +} + +static ssize_t secure_boot_fuse_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0; + const char *status; + + key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, + MLXBF_BOOTCTL_FUSE_STATUS_KEYS); + if (key_state < 0) + return key_state; + + /* + * key_state contains the bits for 4 Key versions, loaded from eFuses + * after a hard reset. Lower 4 bits are a thermometer code indicating + * key programming has started for key n (0000 = none, 0001 = version 0, + * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits + * are a thermometer code indicating key programming has completed for + * key n (same encodings as the start bits). This allows for detection + * of an interruption in the progamming process which has left the key + * partially programmed (and thus invalid). The process is to burn the + * eFuse for the new key start bit, burn the key eFuses, then burn the + * eFuse for the new key complete bit. + * + * For example 0000_0000: no key valid, 0001_0001: key version 0 valid, + * 0011_0011: key 1 version valid, 0011_0111: key version 2 started + * programming but did not complete, etc. The most recent key for which + * both start and complete bit is set is loaded. On soft reset, this + * register is not modified. + */ + for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) { + burnt = key_state & BIT(key); + valid = key_state & BIT(key + MLXBF_SB_KEY_NUM); + + if (burnt && valid) + upper_key_used = 1; + + if (upper_key_used) { + if (burnt) + status = valid ? "Used" : "Wasted"; + else + status = valid ? "Invalid" : "Skipped"; + } else { + if (burnt) + status = valid ? "InUse" : "Incomplete"; + else + status = valid ? "Invalid" : "Free"; + } + buf_len += sprintf(buf + buf_len, "%d:%s ", key, status); + } + buf_len += sprintf(buf + buf_len, "\n"); + + return buf_len; +} + +static DEVICE_ATTR_RW(post_reset_wdog); +static DEVICE_ATTR_RW(reset_action); +static DEVICE_ATTR_RW(second_reset_action); +static DEVICE_ATTR_RO(lifecycle_state); +static DEVICE_ATTR_RO(secure_boot_fuse_state); + +static struct attribute *mlxbf_bootctl_attrs[] = { + &dev_attr_post_reset_wdog.attr, + &dev_attr_reset_action.attr, + &dev_attr_second_reset_action.attr, + &dev_attr_lifecycle_state.attr, + &dev_attr_secure_boot_fuse_state.attr, + NULL +}; + +ATTRIBUTE_GROUPS(mlxbf_bootctl); + +static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = { + {"MLNXBF04", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); + +static bool mlxbf_bootctl_guid_match(const guid_t *guid, + const struct arm_smccc_res *res) +{ + guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16, + res->a2, res->a2 >> 8, res->a2 >> 16, + res->a2 >> 24, res->a3, res->a3 >> 8, + res->a3 >> 16, res->a3 >> 24); + + return guid_equal(guid, &id); +} + +static int mlxbf_bootctl_probe(struct platform_device *pdev) +{ + struct arm_smccc_res res = { 0 }; + guid_t guid; + int ret; + + /* Ensure we have the UUID we expect for this service. */ + arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); + guid_parse(mlxbf_bootctl_svc_uuid_str, &guid); + if (!mlxbf_bootctl_guid_match(&guid, &res)) + return -ENODEV; + + /* + * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC + * in case of boot failures. However it doesn't clear the state if there + * is no failure. Restore the default boot mode here to avoid any + * unnecessary boot partition swapping. + */ + ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION, + MLXBF_BOOTCTL_EMMC); + if (ret < 0) + dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n"); + + return 0; +} + +static struct platform_driver mlxbf_bootctl_driver = { + .probe = mlxbf_bootctl_probe, + .driver = { + .name = "mlxbf-bootctl", + .groups = mlxbf_bootctl_groups, + .acpi_match_table = mlxbf_bootctl_acpi_ids, + } +}; + +module_platform_driver(mlxbf_bootctl_driver); + +MODULE_DESCRIPTION("Mellanox boot control driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mellanox Technologies"); diff --git a/drivers/platform/mellanox/mlxbf-bootctl.h b/drivers/platform/mellanox/mlxbf-bootctl.h new file mode 100644 index 000000000000..148fdb43b435 --- /dev/null +++ b/drivers/platform/mellanox/mlxbf-bootctl.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019, Mellanox Technologies. All rights reserved. + */ + +#ifndef __MLXBF_BOOTCTL_H__ +#define __MLXBF_BOOTCTL_H__ + +/* + * Request that the on-chip watchdog be enabled, or disabled, after + * the next chip soft reset. This call does not affect the current + * status of the on-chip watchdog. If non-zero, the argument + * specifies the watchdog interval in seconds. If zero, the watchdog + * will not be enabled after the next soft reset. Non-zero errors are + * returned as documented below. + */ +#define MLXBF_BOOTCTL_SET_POST_RESET_WDOG 0x82000000 + +/* + * Query the status which has been requested for the on-chip watchdog + * after the next chip soft reset. Returns the interval as set by + * MLXBF_BOOTCTL_SET_POST_RESET_WDOG. + */ +#define MLXBF_BOOTCTL_GET_POST_RESET_WDOG 0x82000001 + +/* + * Request that a specific boot action be taken at the next soft + * reset. By default, the boot action is set by external chip pins, + * which are sampled on hard reset. Note that the boot action + * requested by this call will persist on subsequent resets unless + * this service, or the MLNX_SET_SECOND_RESET_ACTION service, is + * invoked. See below for the available MLNX_BOOT_xxx parameter + * values. Non-zero errors are returned as documented below. + */ +#define MLXBF_BOOTCTL_SET_RESET_ACTION 0x82000002 + +/* + * Return the specific boot action which will be taken at the next + * soft reset. Returns the reset action (see below for the parameter + * values for MLXBF_BOOTCTL_SET_RESET_ACTION). + */ +#define MLXBF_BOOTCTL_GET_RESET_ACTION 0x82000003 + +/* + * Request that a specific boot action be taken at the soft reset + * after the next soft reset. For a specified valid boot mode, the + * effect of this call is identical to that of invoking + * MLXBF_BOOTCTL_SET_RESET_ACTION after the next chip soft reset; in + * particular, after that reset, the action for the now next reset can + * be queried with MLXBF_BOOTCTL_GET_RESET_ACTION and modified with + * MLXBF_BOOTCTL_SET_RESET_ACTION. You may also specify the parameter as + * MLNX_BOOT_NONE, which is equivalent to specifying that no call to + * MLXBF_BOOTCTL_SET_RESET_ACTION be taken after the next chip soft reset. + * This call does not affect the action to be taken at the next soft + * reset. Non-zero errors are returned as documented below. + */ +#define MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION 0x82000004 + +/* + * Return the specific boot action which will be taken at the soft + * reset after the next soft reset; this will be one of the valid + * actions for MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. + */ +#define MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION 0x82000005 + +/* + * Return the fuse status of the current chip. The caller should specify + * with the second argument if the state of the lifecycle fuses or the + * version of secure boot fuse keys left should be returned. + */ +#define MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS 0x82000006 + +/* Reset eMMC by programming the RST_N register. */ +#define MLXBF_BOOTCTL_SET_EMMC_RST_N 0x82000007 + +#define MLXBF_BOOTCTL_GET_DIMM_INFO 0x82000008 + +/* SMC function IDs for SiP Service queries */ +#define MLXBF_BOOTCTL_SIP_SVC_CALL_COUNT 0x8200ff00 +#define MLXBF_BOOTCTL_SIP_SVC_UID 0x8200ff01 +#define MLXBF_BOOTCTL_SIP_SVC_VERSION 0x8200ff03 + +/* ARM Standard Service Calls version numbers */ +#define MLXBF_BOOTCTL_SVC_VERSION_MAJOR 0x0 +#define MLXBF_BOOTCTL_SVC_VERSION_MINOR 0x2 + +/* Number of svc calls defined. */ +#define MLXBF_BOOTCTL_NUM_SVC_CALLS 12 + +/* Valid reset actions for MLXBF_BOOTCTL_SET_RESET_ACTION. */ +#define MLXBF_BOOTCTL_EXTERNAL 0 /* Not boot from eMMC */ +#define MLXBF_BOOTCTL_EMMC 1 /* From primary eMMC boot partition */ +#define MLNX_BOOTCTL_SWAP_EMMC 2 /* Swap eMMC boot partitions and reboot */ +#define MLXBF_BOOTCTL_EMMC_LEGACY 3 /* From primary eMMC in legacy mode */ + +/* Valid arguments for requesting the fuse status. */ +#define MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE 0 /* Return lifecycle status. */ +#define MLXBF_BOOTCTL_FUSE_STATUS_KEYS 1 /* Return secure boot key status */ + +/* Additional value to disable the MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION. */ +#define MLXBF_BOOTCTL_NONE 0x7fffffff /* Don't change next boot action */ + +#endif /* __MLXBF_BOOTCTL_H__ */ diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ae21d08c65e8..1041d80dde46 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -94,7 +94,6 @@ config ASUS_LAPTOP depends on RFKILL || RFKILL = n depends on ACPI_VIDEO || ACPI_VIDEO = n select INPUT_SPARSEKMAP - select INPUT_POLLDEV ---help--- This is a driver for Asus laptops, Lenovo SL and the Pegatron Lucid tablet. It may also support some MEDION, JVC or VICTOR @@ -623,7 +622,6 @@ config THINKPAD_ACPI_HOTKEY_POLL config SENSORS_HDAPS tristate "Thinkpad Hard Drive Active Protection System (hdaps)" depends on INPUT - select INPUT_POLLDEV help This driver provides support for the IBM Hard Drive Active Protection System (hdaps), which provides an accelerometer and other misc. data. @@ -806,7 +804,6 @@ config PEAQ_WMI tristate "PEAQ 2-in-1 WMI hotkey driver" depends on ACPI_WMI depends on INPUT - select INPUT_POLLDEV help Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. @@ -834,7 +831,6 @@ config ACPI_TOSHIBA depends on ACPI_VIDEO || ACPI_VIDEO = n depends on RFKILL || RFKILL = n depends on IIO - select INPUT_POLLDEV select INPUT_SPARSEKMAP ---help--- This driver adds support for access to certain system settings @@ -931,14 +927,20 @@ config INTEL_CHT_INT33FE This driver add support for the INT33FE ACPI device found on some Intel Cherry Trail devices. + There are two kinds of INT33FE ACPI device possible: for hardware + with USB Type-C and Micro-B connectors. This driver supports both. + The INT33FE ACPI device has a CRS table with I2cSerialBusV2 - resources for 3 devices: Maxim MAX17047 Fuel Gauge Controller, + resources for Fuel Gauge Controller and (in the Type-C variant) FUSB302 USB Type-C Controller and PI3USB30532 USB switch. This driver instantiates i2c-clients for these, so that standard i2c drivers for these chips can bind to the them. If you enable this driver it is advised to also select - CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m. + CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B + device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m + for Type-C device. + config INTEL_INT0002_VGPIO tristate "Intel ACPI INT0002 Virtual GPIO driver" @@ -1305,7 +1307,8 @@ config INTEL_ATOMISP2_PM will be called intel_atomisp2_pm. config HUAWEI_WMI - tristate "Huawei WMI hotkeys driver" + tristate "Huawei WMI laptop extras driver" + depends on ACPI_BATTERY depends on ACPI_WMI depends on INPUT select INPUT_SPARSEKMAP @@ -1314,9 +1317,8 @@ config HUAWEI_WMI select LEDS_TRIGGER_AUDIO select NEW_LEDS help - This driver provides support for Huawei WMI hotkeys. - It enables the missing keys and adds support to the micmute - LED found on some of these laptops. + This driver provides support for Huawei WMI hotkeys, battery charge + control, fn-lock, mic-mute LED, and other extra features. To compile this driver as a module, choose M here: the module will be called huawei-wmi. @@ -1337,6 +1339,19 @@ config PCENGINES_APU2 source "drivers/platform/x86/intel_speed_select_if/Kconfig" +config SYSTEM76_ACPI + tristate "System76 ACPI Driver" + depends on ACPI + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + help + This is a driver for System76 laptops running open firmware. It adds + support for Fn-Fx key combinations, keyboard backlight, and airplane mode + LEDs. + + If you have a System76 laptop running open firmware, say Y or M here. + endif # X86_PLATFORM_DEVICES config PMC_ATOM diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 415104033060..42d85a00be4e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -61,6 +61,10 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o +intel_cht_int33fe-objs := intel_cht_int33fe_common.o \ + intel_cht_int33fe_typec.o \ + intel_cht_int33fe_microb.o + obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o @@ -100,3 +104,4 @@ obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/ +obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 5ea8da5f0f70..8cc86f4e3ac1 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -4,7 +4,7 @@ * of the aspire one netbook, turns on/off the fan * as soon as the upper/lower threshold is reached. * - * (C) 2009 - Peter Feuerer peter (a) piie.net + * (C) 2009 - Peter Kaestle peter (a) piie.net * http://piie.net * 2009 Borislav Petkov bp (a) alien8.de * @@ -224,6 +224,8 @@ static const struct bios_settings bios_tbl[] __initconst = { {"Acer", "Aspire 5739G", "V1.3311", 0x55, 0x58, {0x20, 0x00}, 0}, /* Acer TravelMate 7730 */ {"Acer", "TravelMate 7730G", "v0.3509", 0x55, 0x58, {0xaf, 0x00}, 0}, + /* Acer Aspire 7551 */ + {"Acer", "Aspire 7551", "V1.18", 0x93, 0xa8, {0x14, 0x04}, 1}, /* Acer TravelMate TM8573T */ {"Acer", "TM8573T", "V1.13", 0x93, 0xa8, {0x14, 0x04}, 1}, /* Gateway */ @@ -801,7 +803,7 @@ static void __exit acerhdf_exit(void) } MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Peter Feuerer"); +MODULE_AUTHOR("Peter Kaestle"); MODULE_DESCRIPTION("Aspire One temperature and fan driver"); MODULE_ALIAS("dmi:*:*Acer*:pnAOA*:"); MODULE_ALIAS("dmi:*:*Acer*:pnAO751h*:"); @@ -815,6 +817,7 @@ MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5739G:"); MODULE_ALIAS("dmi:*:*Acer*:pnAspire*One*753:"); MODULE_ALIAS("dmi:*:*Acer*:pnAspire*5315:"); MODULE_ALIAS("dmi:*:*Acer*:TravelMate*7730G:"); +MODULE_ALIAS("dmi:*:*Acer*:pnAspire*7551:"); MODULE_ALIAS("dmi:*:*Acer*:TM8573T:"); MODULE_ALIAS("dmi:*:*Gateway*:pnAOA*:"); MODULE_ALIAS("dmi:*:*Gateway*:pnLT31*:"); diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index ca65e1039f92..a666fbc2e73b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -34,7 +34,6 @@ #include <linux/uaccess.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> -#include <linux/input-polldev.h> #include <linux/rfkill.h> #include <linux/slab.h> #include <linux/dmi.h> @@ -244,7 +243,7 @@ struct asus_laptop { struct input_dev *inputdev; struct key_entry *keymap; - struct input_polled_dev *pega_accel_poll; + struct input_dev *pega_accel_poll; struct asus_led wled; struct asus_led bled; @@ -446,9 +445,9 @@ static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP); } -static void pega_accel_poll(struct input_polled_dev *ipd) +static void pega_accel_poll(struct input_dev *input) { - struct device *parent = ipd->input->dev.parent; + struct device *parent = input->dev.parent; struct asus_laptop *asus = dev_get_drvdata(parent); /* In some cases, the very first call to poll causes a @@ -457,10 +456,10 @@ static void pega_accel_poll(struct input_polled_dev *ipd) * device, and perhaps a firmware bug. Fake the first report. */ if (!asus->pega_acc_live) { asus->pega_acc_live = true; - input_report_abs(ipd->input, ABS_X, 0); - input_report_abs(ipd->input, ABS_Y, 0); - input_report_abs(ipd->input, ABS_Z, 0); - input_sync(ipd->input); + input_report_abs(input, ABS_X, 0); + input_report_abs(input, ABS_Y, 0); + input_report_abs(input, ABS_Z, 0); + input_sync(input); return; } @@ -471,25 +470,24 @@ static void pega_accel_poll(struct input_polled_dev *ipd) /* Note transform, convert to "right/up/out" in the native * landscape orientation (i.e. the vector is the direction of * "real up" in the device's cartiesian coordinates). */ - input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x); - input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y); - input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z); - input_sync(ipd->input); + input_report_abs(input, ABS_X, -asus->pega_acc_x); + input_report_abs(input, ABS_Y, -asus->pega_acc_y); + input_report_abs(input, ABS_Z, asus->pega_acc_z); + input_sync(input); } static void pega_accel_exit(struct asus_laptop *asus) { if (asus->pega_accel_poll) { - input_unregister_polled_device(asus->pega_accel_poll); - input_free_polled_device(asus->pega_accel_poll); + input_unregister_device(asus->pega_accel_poll); + asus->pega_accel_poll = NULL; } - asus->pega_accel_poll = NULL; } static int pega_accel_init(struct asus_laptop *asus) { int err; - struct input_polled_dev *ipd; + struct input_dev *input; if (!asus->is_pega_lucid) return -ENODEV; @@ -499,37 +497,39 @@ static int pega_accel_init(struct asus_laptop *asus) acpi_check_handle(asus->handle, METHOD_XLRZ, NULL)) return -ENODEV; - ipd = input_allocate_polled_device(); - if (!ipd) + input = input_allocate_device(); + if (!input) return -ENOMEM; - ipd->poll = pega_accel_poll; - ipd->poll_interval = 125; - ipd->poll_interval_min = 50; - ipd->poll_interval_max = 2000; - - ipd->input->name = PEGA_ACCEL_DESC; - ipd->input->phys = PEGA_ACCEL_NAME "/input0"; - ipd->input->dev.parent = &asus->platform_device->dev; - ipd->input->id.bustype = BUS_HOST; + input->name = PEGA_ACCEL_DESC; + input->phys = PEGA_ACCEL_NAME "/input0"; + input->dev.parent = &asus->platform_device->dev; + input->id.bustype = BUS_HOST; - set_bit(EV_ABS, ipd->input->evbit); - input_set_abs_params(ipd->input, ABS_X, + input_set_abs_params(input, ABS_X, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); - input_set_abs_params(ipd->input, ABS_Y, + input_set_abs_params(input, ABS_Y, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); - input_set_abs_params(ipd->input, ABS_Z, + input_set_abs_params(input, ABS_Z, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); - err = input_register_polled_device(ipd); + err = input_setup_polling(input, pega_accel_poll); if (err) goto exit; - asus->pega_accel_poll = ipd; + input_set_poll_interval(input, 125); + input_set_min_poll_interval(input, 50); + input_set_max_poll_interval(input, 2000); + + err = input_register_device(input); + if (err) + goto exit; + + asus->pega_accel_poll = input; return 0; exit: - input_free_polled_device(ipd); + input_free_device(input); return err; } @@ -1550,8 +1550,7 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) /* Accelerometer "coarse orientation change" event */ if (asus->pega_accel_poll && event == 0xEA) { - kobject_uevent(&asus->pega_accel_poll->input->dev.kobj, - KOBJ_CHANGE); + kobject_uevent(&asus->pega_accel_poll->dev.kobj, KOBJ_CHANGE); return ; } diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d27be2836bc2..74e988f839e8 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -33,6 +33,7 @@ struct quirk_entry { bool touchpad_led; + bool kbd_led_not_present; bool kbd_led_levels_off_1; bool kbd_missing_ac_tag; @@ -73,6 +74,10 @@ static struct quirk_entry quirk_dell_latitude_e6410 = { .kbd_led_levels_off_1 = true, }; +static struct quirk_entry quirk_dell_inspiron_1012 = { + .kbd_led_not_present = true, +}; + static struct platform_driver platform_driver = { .driver = { .name = "dell-laptop", @@ -310,6 +315,24 @@ static const struct dmi_system_id dell_quirks[] __initconst = { }, .driver_data = &quirk_dell_latitude_e6410, }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 1012", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"), + }, + .driver_data = &quirk_dell_inspiron_1012, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron 1018", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1018"), + }, + .driver_data = &quirk_dell_inspiron_1012, + }, { } }; @@ -1493,6 +1516,9 @@ static void kbd_init(void) { int ret; + if (quirks && quirks->kbd_led_not_present) + return; + ret = kbd_init_info(); kbd_init_tokens(); diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 3adcb0de0193..04c4da6692d7 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -18,7 +18,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/kernel.h> #include <linux/mutex.h> #include <linux/module.h> @@ -59,7 +59,7 @@ #define HDAPS_BOTH_AXES (HDAPS_X_AXIS | HDAPS_Y_AXIS) static struct platform_device *pdev; -static struct input_polled_dev *hdaps_idev; +static struct input_dev *hdaps_idev; static unsigned int hdaps_invert; static u8 km_activity; static int rest_x; @@ -318,9 +318,8 @@ static void hdaps_calibrate(void) __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y); } -static void hdaps_mousedev_poll(struct input_polled_dev *dev) +static void hdaps_mousedev_poll(struct input_dev *input_dev) { - struct input_dev *input_dev = dev->input; int x, y; mutex_lock(&hdaps_mtx); @@ -531,7 +530,6 @@ static const struct dmi_system_id hdaps_whitelist[] __initconst = { static int __init hdaps_init(void) { - struct input_dev *idev; int ret; if (!dmi_check_system(hdaps_whitelist)) { @@ -559,31 +557,32 @@ static int __init hdaps_init(void) if (ret) goto out_device; - hdaps_idev = input_allocate_polled_device(); + hdaps_idev = input_allocate_device(); if (!hdaps_idev) { ret = -ENOMEM; goto out_group; } - hdaps_idev->poll = hdaps_mousedev_poll; - hdaps_idev->poll_interval = HDAPS_POLL_INTERVAL; - /* initial calibrate for the input device */ hdaps_calibrate(); /* initialize the input class */ - idev = hdaps_idev->input; - idev->name = "hdaps"; - idev->phys = "isa1600/input0"; - idev->id.bustype = BUS_ISA; - idev->dev.parent = &pdev->dev; - idev->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(idev, ABS_X, + hdaps_idev->name = "hdaps"; + hdaps_idev->phys = "isa1600/input0"; + hdaps_idev->id.bustype = BUS_ISA; + hdaps_idev->dev.parent = &pdev->dev; + input_set_abs_params(hdaps_idev, ABS_X, -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - input_set_abs_params(idev, ABS_Y, + input_set_abs_params(hdaps_idev, ABS_Y, -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT); - ret = input_register_polled_device(hdaps_idev); + ret = input_setup_polling(hdaps_idev, hdaps_mousedev_poll); + if (ret) + goto out_idev; + + input_set_poll_interval(hdaps_idev, HDAPS_POLL_INTERVAL); + + ret = input_register_device(hdaps_idev); if (ret) goto out_idev; @@ -591,7 +590,7 @@ static int __init hdaps_init(void) return 0; out_idev: - input_free_polled_device(hdaps_idev); + input_free_device(hdaps_idev); out_group: sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); out_device: @@ -607,8 +606,7 @@ out: static void __exit hdaps_exit(void) { - input_unregister_polled_device(hdaps_idev); - input_free_polled_device(hdaps_idev); + input_unregister_device(hdaps_idev); sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group); platform_device_unregister(pdev); platform_driver_unregister(&hdaps_driver); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 6bcbbb375401..9579a706fc08 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -65,7 +65,7 @@ struct bios_args { u32 command; u32 commandtype; u32 datasize; - u32 data; + u8 data[128]; }; enum hp_wmi_commandtype { @@ -216,7 +216,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command, .command = command, .commandtype = query, .datasize = insize, - .data = 0, + .data = { 0 }, }; struct acpi_buffer input = { sizeof(struct bios_args), &args }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -228,7 +228,7 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command, if (WARN_ON(insize > sizeof(args.data))) return -EINVAL; - memcpy(&args.data, buffer, insize); + memcpy(&args.data[0], buffer, insize); wmi_evaluate_method(HPWMI_BIOS_GUID, 0, mid, &input, &output); @@ -380,7 +380,7 @@ static int hp_wmi_rfkill2_refresh(void) int err, i; err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - 0, sizeof(state)); + sizeof(state), sizeof(state)); if (err) return err; @@ -778,7 +778,7 @@ static int __init hp_wmi_rfkill2_setup(struct platform_device *device) int err, i; err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, HPWMI_READ, &state, - 0, sizeof(state)); + sizeof(state), sizeof(state)); if (err) return err < 0 ? err : -EINVAL; diff --git a/drivers/platform/x86/huawei-wmi.c b/drivers/platform/x86/huawei-wmi.c index 195a7f3638cb..a2d846c4a7ee 100644 --- a/drivers/platform/x86/huawei-wmi.c +++ b/drivers/platform/x86/huawei-wmi.c @@ -1,32 +1,77 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Huawei WMI hotkeys + * Huawei WMI laptop extras driver * * Copyright (C) 2018 Ayman Bagabas <ayman.bagabas@gmail.com> */ #include <linux/acpi.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/dmi.h> #include <linux/input.h> #include <linux/input/sparse-keymap.h> #include <linux/leds.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/sysfs.h> #include <linux/wmi.h> +#include <acpi/battery.h> /* * Huawei WMI GUIDs */ -#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100" -#define AMW0_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000" +#define HWMI_METHOD_GUID "ABBC0F5B-8EA1-11D1-A000-C90629100000" +#define HWMI_EVENT_GUID "ABBC0F5C-8EA1-11D1-A000-C90629100000" +/* Legacy GUIDs */ #define WMI0_EXPENSIVE_GUID "39142400-C6A3-40fa-BADB-8A2652834100" +#define WMI0_EVENT_GUID "59142400-C6A3-40fa-BADB-8A2652834100" + +/* HWMI commands */ + +enum { + BATTERY_THRESH_GET = 0x00001103, /* \GBTT */ + BATTERY_THRESH_SET = 0x00001003, /* \SBTT */ + FN_LOCK_GET = 0x00000604, /* \GFRS */ + FN_LOCK_SET = 0x00000704, /* \SFRS */ + MICMUTE_LED_SET = 0x00000b04, /* \SMLS */ +}; + +union hwmi_arg { + u64 cmd; + u8 args[8]; +}; + +struct quirk_entry { + bool battery_reset; + bool ec_micmute; + bool report_brightness; +}; + +static struct quirk_entry *quirks; -struct huawei_wmi_priv { - struct input_dev *idev; +struct huawei_wmi_debug { + struct dentry *root; + u64 arg; +}; + +struct huawei_wmi { + bool battery_available; + bool fn_lock_available; + + struct huawei_wmi_debug debug; + struct input_dev *idev[2]; struct led_classdev cdev; - acpi_handle handle; - char *acpi_method; + struct device *dev; + + struct mutex wmi_lock; }; +static struct huawei_wmi *huawei_wmi; + static const struct key_entry huawei_wmi_keymap[] = { { KE_KEY, 0x281, { KEY_BRIGHTNESSDOWN } }, { KE_KEY, 0x282, { KEY_BRIGHTNESSUP } }, @@ -37,73 +82,614 @@ static const struct key_entry huawei_wmi_keymap[] = { { KE_KEY, 0x289, { KEY_WLAN } }, // Huawei |M| key { KE_KEY, 0x28a, { KEY_CONFIG } }, - // Keyboard backlight + // Keyboard backlit { KE_IGNORE, 0x293, { KEY_KBDILLUMTOGGLE } }, { KE_IGNORE, 0x294, { KEY_KBDILLUMUP } }, { KE_IGNORE, 0x295, { KEY_KBDILLUMUP } }, { KE_END, 0 } }; +static int battery_reset = -1; +static int report_brightness = -1; + +module_param(battery_reset, bint, 0444); +MODULE_PARM_DESC(battery_reset, + "Reset battery charge values to (0-0) before disabling it using (0-100)"); +module_param(report_brightness, bint, 0444); +MODULE_PARM_DESC(report_brightness, + "Report brightness keys."); + +/* Quirks */ + +static int __init dmi_matched(const struct dmi_system_id *dmi) +{ + quirks = dmi->driver_data; + return 1; +} + +static struct quirk_entry quirk_unknown = { +}; + +static struct quirk_entry quirk_battery_reset = { + .battery_reset = true, +}; + +static struct quirk_entry quirk_matebook_x = { + .ec_micmute = true, + .report_brightness = true, +}; + +static const struct dmi_system_id huawei_quirks[] = { + { + .callback = dmi_matched, + .ident = "Huawei MACH-WX9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), + DMI_MATCH(DMI_PRODUCT_NAME, "MACH-WX9"), + }, + .driver_data = &quirk_battery_reset + }, + { + .callback = dmi_matched, + .ident = "Huawei MateBook X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HUAWEI"), + DMI_MATCH(DMI_PRODUCT_NAME, "HUAWEI MateBook X") + }, + .driver_data = &quirk_matebook_x + }, + { } +}; + +/* Utils */ + +static int huawei_wmi_call(struct huawei_wmi *huawei, + struct acpi_buffer *in, struct acpi_buffer *out) +{ + acpi_status status; + + mutex_lock(&huawei->wmi_lock); + status = wmi_evaluate_method(HWMI_METHOD_GUID, 0, 1, in, out); + mutex_unlock(&huawei->wmi_lock); + if (ACPI_FAILURE(status)) { + dev_err(huawei->dev, "Failed to evaluate wmi method\n"); + return -ENODEV; + } + + return 0; +} + +/* HWMI takes a 64 bit input and returns either a package with 2 buffers, one of + * 4 bytes and the other of 256 bytes, or one buffer of size 0x104 (260) bytes. + * The first 4 bytes are ignored, we ignore the first 4 bytes buffer if we got a + * package, or skip the first 4 if a buffer of 0x104 is used. The first byte of + * the remaining 0x100 sized buffer has the return status of every call. In case + * the return status is non-zero, we return -ENODEV but still copy the returned + * buffer to the given buffer parameter (buf). + */ +static int huawei_wmi_cmd(u64 arg, u8 *buf, size_t buflen) +{ + struct huawei_wmi *huawei = huawei_wmi; + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer in; + union acpi_object *obj; + size_t len; + int err, i; + + in.length = sizeof(arg); + in.pointer = &arg; + + /* Some models require calling HWMI twice to execute a command. We evaluate + * HWMI and if we get a non-zero return status we evaluate it again. + */ + for (i = 0; i < 2; i++) { + err = huawei_wmi_call(huawei, &in, &out); + if (err) + goto fail_cmd; + + obj = out.pointer; + if (!obj) { + err = -EIO; + goto fail_cmd; + } + + switch (obj->type) { + /* Models that implement both "legacy" and HWMI tend to return a 0x104 + * sized buffer instead of a package of 0x4 and 0x100 buffers. + */ + case ACPI_TYPE_BUFFER: + if (obj->buffer.length == 0x104) { + // Skip the first 4 bytes. + obj->buffer.pointer += 4; + len = 0x100; + } else { + dev_err(huawei->dev, "Bad buffer length, got %d\n", obj->buffer.length); + err = -EIO; + goto fail_cmd; + } + + break; + /* HWMI returns a package with 2 buffer elements, one of 4 bytes and the + * other is 256 bytes. + */ + case ACPI_TYPE_PACKAGE: + if (obj->package.count != 2) { + dev_err(huawei->dev, "Bad package count, got %d\n", obj->package.count); + err = -EIO; + goto fail_cmd; + } + + obj = &obj->package.elements[1]; + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(huawei->dev, "Bad package element type, got %d\n", obj->type); + err = -EIO; + goto fail_cmd; + } + len = obj->buffer.length; + + break; + /* Shouldn't get here! */ + default: + dev_err(huawei->dev, "Unexpected obj type, got: %d\n", obj->type); + err = -EIO; + goto fail_cmd; + } + + if (!*obj->buffer.pointer) + break; + } + + err = (*obj->buffer.pointer) ? -ENODEV : 0; + + if (buf) { + len = min(buflen, len); + memcpy(buf, obj->buffer.pointer, len); + } + +fail_cmd: + kfree(out.pointer); + return err; +} + +/* LEDs */ + static int huawei_wmi_micmute_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct huawei_wmi_priv *priv = dev_get_drvdata(led_cdev->dev->parent); - acpi_status status; - union acpi_object args[3]; - struct acpi_object_list arg_list = { - .pointer = args, - .count = ARRAY_SIZE(args), - }; - - args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER; - args[1].integer.value = 0x04; - - if (strcmp(priv->acpi_method, "SPIN") == 0) { - args[0].integer.value = 0; - args[2].integer.value = brightness ? 1 : 0; - } else if (strcmp(priv->acpi_method, "WPIN") == 0) { - args[0].integer.value = 1; - args[2].integer.value = brightness ? 0 : 1; + /* This is a workaround until the "legacy" interface is implemented. */ + if (quirks && quirks->ec_micmute) { + char *acpi_method; + acpi_handle handle; + acpi_status status; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + + handle = ec_get_handle(); + if (!handle) + return -ENODEV; + + args[0].type = args[1].type = args[2].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0x04; + + if (acpi_has_method(handle, "SPIN")) { + acpi_method = "SPIN"; + args[0].integer.value = 0; + args[2].integer.value = brightness ? 1 : 0; + } else if (acpi_has_method(handle, "WPIN")) { + acpi_method = "WPIN"; + args[0].integer.value = 1; + args[2].integer.value = brightness ? 0 : 1; + } else { + return -ENODEV; + } + + status = acpi_evaluate_object(handle, acpi_method, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return -ENODEV; + + return 0; } else { + union hwmi_arg arg; + + arg.cmd = MICMUTE_LED_SET; + arg.args[2] = brightness; + + return huawei_wmi_cmd(arg.cmd, NULL, 0); + } +} + +static void huawei_wmi_leds_setup(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + huawei->cdev.name = "platform::micmute"; + huawei->cdev.max_brightness = 1; + huawei->cdev.brightness_set_blocking = &huawei_wmi_micmute_led_set; + huawei->cdev.default_trigger = "audio-micmute"; + huawei->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + huawei->cdev.dev = dev; + huawei->cdev.flags = LED_CORE_SUSPENDRESUME; + + devm_led_classdev_register(dev, &huawei->cdev); +} + +/* Battery protection */ + +static int huawei_wmi_battery_get(int *start, int *end) +{ + u8 ret[0x100]; + int err, i; + + err = huawei_wmi_cmd(BATTERY_THRESH_GET, ret, 0x100); + if (err) + return err; + + /* Find the last two non-zero values. Return status is ignored. */ + i = 0xff; + do { + if (start) + *start = ret[i-1]; + if (end) + *end = ret[i]; + } while (i > 2 && !ret[i--]); + + return 0; +} + +static int huawei_wmi_battery_set(int start, int end) +{ + union hwmi_arg arg; + int err; + + if (start < 0 || end < 0 || start > 100 || end > 100) return -EINVAL; + + arg.cmd = BATTERY_THRESH_SET; + arg.args[2] = start; + arg.args[3] = end; + + /* This is an edge case were some models turn battery protection + * off without changing their thresholds values. We clear the + * values before turning off protection. Sometimes we need a sleep delay to + * make sure these values make their way to EC memory. + */ + if (quirks && quirks->battery_reset && start == 0 && end == 100) { + err = huawei_wmi_battery_set(0, 0); + if (err) + return err; + + msleep(1000); } - status = acpi_evaluate_object(priv->handle, priv->acpi_method, &arg_list, NULL); - if (ACPI_FAILURE(status)) - return -ENXIO; + err = huawei_wmi_cmd(arg.cmd, NULL, 0); + + return err; +} + +static ssize_t charge_control_start_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err, start; + + err = huawei_wmi_battery_get(&start, NULL); + if (err) + return err; + + return sprintf(buf, "%d\n", start); +} + +static ssize_t charge_control_end_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err, end; + + err = huawei_wmi_battery_get(NULL, &end); + if (err) + return err; + + return sprintf(buf, "%d\n", end); +} + +static ssize_t charge_control_thresholds_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err, start, end; + + err = huawei_wmi_battery_get(&start, &end); + if (err) + return err; + + return sprintf(buf, "%d %d\n", start, end); +} + +static ssize_t charge_control_start_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err, start, end; + + err = huawei_wmi_battery_get(NULL, &end); + if (err) + return err; + + if (sscanf(buf, "%d", &start) != 1) + return -EINVAL; + + err = huawei_wmi_battery_set(start, end); + if (err) + return err; + + return size; +} + +static ssize_t charge_control_end_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err, start, end; + + err = huawei_wmi_battery_get(&start, NULL); + if (err) + return err; + + if (sscanf(buf, "%d", &end) != 1) + return -EINVAL; + + err = huawei_wmi_battery_set(start, end); + if (err) + return err; + + return size; +} + +static ssize_t charge_control_thresholds_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int err, start, end; + + if (sscanf(buf, "%d %d", &start, &end) != 2) + return -EINVAL; + + err = huawei_wmi_battery_set(start, end); + if (err) + return err; + + return size; +} + +static DEVICE_ATTR_RW(charge_control_start_threshold); +static DEVICE_ATTR_RW(charge_control_end_threshold); +static DEVICE_ATTR_RW(charge_control_thresholds); + +static int huawei_wmi_battery_add(struct power_supply *battery) +{ + device_create_file(&battery->dev, &dev_attr_charge_control_start_threshold); + device_create_file(&battery->dev, &dev_attr_charge_control_end_threshold); return 0; } -static int huawei_wmi_leds_setup(struct wmi_device *wdev) +static int huawei_wmi_battery_remove(struct power_supply *battery) { - struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); + device_remove_file(&battery->dev, &dev_attr_charge_control_start_threshold); + device_remove_file(&battery->dev, &dev_attr_charge_control_end_threshold); - priv->handle = ec_get_handle(); - if (!priv->handle) - return 0; + return 0; +} - if (acpi_has_method(priv->handle, "SPIN")) - priv->acpi_method = "SPIN"; - else if (acpi_has_method(priv->handle, "WPIN")) - priv->acpi_method = "WPIN"; - else - return 0; +static struct acpi_battery_hook huawei_wmi_battery_hook = { + .add_battery = huawei_wmi_battery_add, + .remove_battery = huawei_wmi_battery_remove, + .name = "Huawei Battery Extension" +}; + +static void huawei_wmi_battery_setup(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); - priv->cdev.name = "platform::micmute"; - priv->cdev.max_brightness = 1; - priv->cdev.brightness_set_blocking = huawei_wmi_micmute_led_set; - priv->cdev.default_trigger = "audio-micmute"; - priv->cdev.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); - priv->cdev.dev = &wdev->dev; - priv->cdev.flags = LED_CORE_SUSPENDRESUME; + huawei->battery_available = true; + if (huawei_wmi_battery_get(NULL, NULL)) { + huawei->battery_available = false; + return; + } - return devm_led_classdev_register(&wdev->dev, &priv->cdev); + battery_hook_register(&huawei_wmi_battery_hook); + device_create_file(dev, &dev_attr_charge_control_thresholds); } -static void huawei_wmi_process_key(struct wmi_device *wdev, int code) +static void huawei_wmi_battery_exit(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + if (huawei->battery_available) { + battery_hook_unregister(&huawei_wmi_battery_hook); + device_remove_file(dev, &dev_attr_charge_control_thresholds); + } +} + +/* Fn lock */ + +static int huawei_wmi_fn_lock_get(int *on) +{ + u8 ret[0x100] = { 0 }; + int err, i; + + err = huawei_wmi_cmd(FN_LOCK_GET, ret, 0x100); + if (err) + return err; + + /* Find the first non-zero value. Return status is ignored. */ + i = 1; + do { + if (on) + *on = ret[i] - 1; // -1 undefined, 0 off, 1 on. + } while (i < 0xff && !ret[i++]); + + return 0; +} + +static int huawei_wmi_fn_lock_set(int on) +{ + union hwmi_arg arg; + + arg.cmd = FN_LOCK_SET; + arg.args[2] = on + 1; // 0 undefined, 1 off, 2 on. + + return huawei_wmi_cmd(arg.cmd, NULL, 0); +} + +static ssize_t fn_lock_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int err, on; + + err = huawei_wmi_fn_lock_get(&on); + if (err) + return err; + + return sprintf(buf, "%d\n", on); +} + +static ssize_t fn_lock_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int on, err; + + if (kstrtoint(buf, 10, &on) || + on < 0 || on > 1) + return -EINVAL; + + err = huawei_wmi_fn_lock_set(on); + if (err) + return err; + + return size; +} + +static DEVICE_ATTR_RW(fn_lock_state); + +static void huawei_wmi_fn_lock_setup(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + huawei->fn_lock_available = true; + if (huawei_wmi_fn_lock_get(NULL)) { + huawei->fn_lock_available = false; + return; + } + + device_create_file(dev, &dev_attr_fn_lock_state); +} + +static void huawei_wmi_fn_lock_exit(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + if (huawei->fn_lock_available) + device_remove_file(dev, &dev_attr_fn_lock_state); +} + +/* debugfs */ + +static void huawei_wmi_debugfs_call_dump(struct seq_file *m, void *data, + union acpi_object *obj) +{ + struct huawei_wmi *huawei = m->private; + int i; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + seq_printf(m, "0x%llx", obj->integer.value); + break; + case ACPI_TYPE_STRING: + seq_printf(m, "\"%.*s\"", obj->string.length, obj->string.pointer); + break; + case ACPI_TYPE_BUFFER: + seq_puts(m, "{"); + for (i = 0; i < obj->buffer.length; i++) { + seq_printf(m, "0x%02x", obj->buffer.pointer[i]); + if (i < obj->buffer.length - 1) + seq_puts(m, ","); + } + seq_puts(m, "}"); + break; + case ACPI_TYPE_PACKAGE: + seq_puts(m, "["); + for (i = 0; i < obj->package.count; i++) { + huawei_wmi_debugfs_call_dump(m, huawei, &obj->package.elements[i]); + if (i < obj->package.count - 1) + seq_puts(m, ","); + } + seq_puts(m, "]"); + break; + default: + dev_err(huawei->dev, "Unexpected obj type, got %d\n", obj->type); + return; + } +} + +static int huawei_wmi_debugfs_call_show(struct seq_file *m, void *data) +{ + struct huawei_wmi *huawei = m->private; + struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer in; + union acpi_object *obj; + int err; + + in.length = sizeof(u64); + in.pointer = &huawei->debug.arg; + + err = huawei_wmi_call(huawei, &in, &out); + if (err) + return err; + + obj = out.pointer; + if (!obj) { + err = -EIO; + goto fail_debugfs_call; + } + + huawei_wmi_debugfs_call_dump(m, huawei, obj); + +fail_debugfs_call: + kfree(out.pointer); + return err; +} + +DEFINE_SHOW_ATTRIBUTE(huawei_wmi_debugfs_call); + +static void huawei_wmi_debugfs_setup(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + huawei->debug.root = debugfs_create_dir("huawei-wmi", NULL); + + debugfs_create_x64("arg", 0644, huawei->debug.root, + &huawei->debug.arg); + debugfs_create_file("call", 0400, + huawei->debug.root, huawei, &huawei_wmi_debugfs_call_fops); +} + +static void huawei_wmi_debugfs_exit(struct device *dev) +{ + struct huawei_wmi *huawei = dev_get_drvdata(dev); + + debugfs_remove_recursive(huawei->debug.root); +} + +/* Input */ + +static void huawei_wmi_process_key(struct input_dev *idev, int code) { - struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); const struct key_entry *key; /* @@ -127,81 +713,187 @@ static void huawei_wmi_process_key(struct wmi_device *wdev, int code) kfree(response.pointer); } - key = sparse_keymap_entry_from_scancode(priv->idev, code); + key = sparse_keymap_entry_from_scancode(idev, code); if (!key) { - dev_info(&wdev->dev, "Unknown key pressed, code: 0x%04x\n", code); + dev_info(&idev->dev, "Unknown key pressed, code: 0x%04x\n", code); return; } - sparse_keymap_report_entry(priv->idev, key, 1, true); + if (quirks && !quirks->report_brightness && + (key->sw.code == KEY_BRIGHTNESSDOWN || + key->sw.code == KEY_BRIGHTNESSUP)) + return; + + sparse_keymap_report_entry(idev, key, 1, true); } -static void huawei_wmi_notify(struct wmi_device *wdev, - union acpi_object *obj) +static void huawei_wmi_input_notify(u32 value, void *context) { - if (obj->type == ACPI_TYPE_INTEGER) - huawei_wmi_process_key(wdev, obj->integer.value); + struct input_dev *idev = (struct input_dev *)context; + struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + status = wmi_get_event_data(value, &response); + if (ACPI_FAILURE(status)) { + dev_err(&idev->dev, "Unable to get event data\n"); + return; + } + + obj = (union acpi_object *)response.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + huawei_wmi_process_key(idev, obj->integer.value); else - dev_info(&wdev->dev, "Bad response type %d\n", obj->type); + dev_err(&idev->dev, "Bad response type\n"); + + kfree(response.pointer); } -static int huawei_wmi_input_setup(struct wmi_device *wdev) +static int huawei_wmi_input_setup(struct device *dev, + const char *guid, + struct input_dev **idev) { - struct huawei_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - int err; - - priv->idev = devm_input_allocate_device(&wdev->dev); - if (!priv->idev) + *idev = devm_input_allocate_device(dev); + if (!*idev) return -ENOMEM; - priv->idev->name = "Huawei WMI hotkeys"; - priv->idev->phys = "wmi/input0"; - priv->idev->id.bustype = BUS_HOST; - priv->idev->dev.parent = &wdev->dev; + (*idev)->name = "Huawei WMI hotkeys"; + (*idev)->phys = "wmi/input0"; + (*idev)->id.bustype = BUS_HOST; + (*idev)->dev.parent = dev; - err = sparse_keymap_setup(priv->idev, huawei_wmi_keymap, NULL); - if (err) - return err; + return sparse_keymap_setup(*idev, huawei_wmi_keymap, NULL) || + input_register_device(*idev) || + wmi_install_notify_handler(guid, huawei_wmi_input_notify, + *idev); +} - return input_register_device(priv->idev); +static void huawei_wmi_input_exit(struct device *dev, const char *guid) +{ + wmi_remove_notify_handler(guid); } -static int huawei_wmi_probe(struct wmi_device *wdev, const void *context) +/* Huawei driver */ + +static const struct wmi_device_id huawei_wmi_events_id_table[] = { + { .guid_string = WMI0_EVENT_GUID }, + { .guid_string = HWMI_EVENT_GUID }, + { } +}; + +static int huawei_wmi_probe(struct platform_device *pdev) { - struct huawei_wmi_priv *priv; + const struct wmi_device_id *guid = huawei_wmi_events_id_table; int err; - priv = devm_kzalloc(&wdev->dev, sizeof(struct huawei_wmi_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + platform_set_drvdata(pdev, huawei_wmi); + huawei_wmi->dev = &pdev->dev; - dev_set_drvdata(&wdev->dev, priv); + while (*guid->guid_string) { + struct input_dev *idev = *huawei_wmi->idev; - err = huawei_wmi_input_setup(wdev); - if (err) - return err; + if (wmi_has_guid(guid->guid_string)) { + err = huawei_wmi_input_setup(&pdev->dev, guid->guid_string, &idev); + if (err) { + dev_err(&pdev->dev, "Failed to setup input on %s\n", guid->guid_string); + return err; + } + } + + idev++; + guid++; + } + + if (wmi_has_guid(HWMI_METHOD_GUID)) { + mutex_init(&huawei_wmi->wmi_lock); - return huawei_wmi_leds_setup(wdev); + huawei_wmi_leds_setup(&pdev->dev); + huawei_wmi_fn_lock_setup(&pdev->dev); + huawei_wmi_battery_setup(&pdev->dev); + huawei_wmi_debugfs_setup(&pdev->dev); + } + + return 0; } -static const struct wmi_device_id huawei_wmi_id_table[] = { - { .guid_string = WMI0_EVENT_GUID }, - { .guid_string = AMW0_EVENT_GUID }, - { } -}; +static int huawei_wmi_remove(struct platform_device *pdev) +{ + const struct wmi_device_id *guid = huawei_wmi_events_id_table; -static struct wmi_driver huawei_wmi_driver = { + while (*guid->guid_string) { + if (wmi_has_guid(guid->guid_string)) + huawei_wmi_input_exit(&pdev->dev, guid->guid_string); + + guid++; + } + + if (wmi_has_guid(HWMI_METHOD_GUID)) { + huawei_wmi_debugfs_exit(&pdev->dev); + huawei_wmi_battery_exit(&pdev->dev); + huawei_wmi_fn_lock_exit(&pdev->dev); + } + + return 0; +} + +static struct platform_driver huawei_wmi_driver = { .driver = { .name = "huawei-wmi", }, - .id_table = huawei_wmi_id_table, .probe = huawei_wmi_probe, - .notify = huawei_wmi_notify, + .remove = huawei_wmi_remove, }; -module_wmi_driver(huawei_wmi_driver); +static __init int huawei_wmi_init(void) +{ + struct platform_device *pdev; + int err; + + huawei_wmi = kzalloc(sizeof(struct huawei_wmi), GFP_KERNEL); + if (!huawei_wmi) + return -ENOMEM; + + quirks = &quirk_unknown; + dmi_check_system(huawei_quirks); + if (battery_reset != -1) + quirks->battery_reset = battery_reset; + if (report_brightness != -1) + quirks->report_brightness = report_brightness; + + err = platform_driver_register(&huawei_wmi_driver); + if (err) + goto pdrv_err; + + pdev = platform_device_register_simple("huawei-wmi", -1, NULL, 0); + if (IS_ERR(pdev)) { + err = PTR_ERR(pdev); + goto pdev_err; + } + + return 0; + +pdev_err: + platform_driver_unregister(&huawei_wmi_driver); +pdrv_err: + kfree(huawei_wmi); + return err; +} + +static __exit void huawei_wmi_exit(void) +{ + struct platform_device *pdev = to_platform_device(huawei_wmi->dev); + + platform_device_unregister(pdev); + platform_driver_unregister(&huawei_wmi_driver); + + kfree(huawei_wmi); +} + +module_init(huawei_wmi_init); +module_exit(huawei_wmi_exit); -MODULE_DEVICE_TABLE(wmi, huawei_wmi_id_table); +MODULE_ALIAS("wmi:"HWMI_METHOD_GUID); +MODULE_DEVICE_TABLE(wmi, huawei_wmi_events_id_table); MODULE_AUTHOR("Ayman Bagabas <ayman.bagabas@gmail.com>"); -MODULE_DESCRIPTION("Huawei WMI hotkeys"); +MODULE_DESCRIPTION("Huawei WMI laptop extras driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_cht_int33fe_common.c b/drivers/platform/x86/intel_cht_int33fe_common.c new file mode 100644 index 000000000000..42dd11623f56 --- /dev/null +++ b/drivers/platform/x86/intel_cht_int33fe_common.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers + * (USB Micro-B and Type-C connector variants). + * + * Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com> + */ + +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "intel_cht_int33fe_common.h" + +#define EXPECTED_PTYPE 4 + +static int cht_int33fe_i2c_res_filter(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_i2c_serialbus *sb; + int *count = data; + + if (i2c_acpi_get_i2c_resource(ares, &sb)) + (*count)++; + + return 1; +} + +static int cht_int33fe_count_i2c_clients(struct device *dev) +{ + struct acpi_device *adev; + LIST_HEAD(resource_list); + int count = 0; + + adev = ACPI_COMPANION(dev); + if (!adev) + return -EINVAL; + + acpi_dev_get_resources(adev, &resource_list, + cht_int33fe_i2c_res_filter, &count); + + acpi_dev_free_resource_list(&resource_list); + + return count; +} + +static int cht_int33fe_check_hw_type(struct device *dev) +{ + unsigned long long ptyp; + acpi_status status; + int ret; + + status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Error getting PTYPE\n"); + return -ENODEV; + } + + /* + * The same ACPI HID is used for different configurations check PTYP + * to ensure that we are dealing with the expected config. + */ + if (ptyp != EXPECTED_PTYPE) + return -ENODEV; + + /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ + if (!acpi_dev_present("INT34D3", "1", 3)) { + dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", + EXPECTED_PTYPE); + return -ENODEV; + } + + ret = cht_int33fe_count_i2c_clients(dev); + if (ret < 0) + return ret; + + switch (ret) { + case 2: + return INT33FE_HW_MICROB; + case 4: + return INT33FE_HW_TYPEC; + default: + return -ENODEV; + } +} + +static int cht_int33fe_probe(struct platform_device *pdev) +{ + struct cht_int33fe_data *data; + struct device *dev = &pdev->dev; + int ret; + + ret = cht_int33fe_check_hw_type(dev); + if (ret < 0) + return ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + switch (ret) { + case INT33FE_HW_MICROB: + data->probe = cht_int33fe_microb_probe; + data->remove = cht_int33fe_microb_remove; + break; + + case INT33FE_HW_TYPEC: + data->probe = cht_int33fe_typec_probe; + data->remove = cht_int33fe_typec_remove; + break; + } + + platform_set_drvdata(pdev, data); + + return data->probe(data); +} + +static int cht_int33fe_remove(struct platform_device *pdev) +{ + struct cht_int33fe_data *data = platform_get_drvdata(pdev); + + return data->remove(data); +} + +static const struct acpi_device_id cht_int33fe_acpi_ids[] = { + { "INT33FE", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids); + +static struct platform_driver cht_int33fe_driver = { + .driver = { + .name = "Intel Cherry Trail ACPI INT33FE driver", + .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), + }, + .probe = cht_int33fe_probe, + .remove = cht_int33fe_remove, +}; + +module_platform_driver(cht_int33fe_driver); + +MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver"); +MODULE_AUTHOR("Yauhen Kharuzhy <jekhor@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_cht_int33fe_common.h b/drivers/platform/x86/intel_cht_int33fe_common.h new file mode 100644 index 000000000000..03cd45f4e8cb --- /dev/null +++ b/drivers/platform/x86/intel_cht_int33fe_common.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers + * (USB Micro-B and Type-C connector variants), header file + * + * Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com> + */ + +#ifndef _INTEL_CHT_INT33FE_COMMON_H +#define _INTEL_CHT_INT33FE_COMMON_H + +#include <linux/device.h> +#include <linux/fwnode.h> +#include <linux/i2c.h> + +enum int33fe_hw_type { + INT33FE_HW_MICROB, + INT33FE_HW_TYPEC, +}; + +struct cht_int33fe_data { + struct device *dev; + + int (*probe)(struct cht_int33fe_data *data); + int (*remove)(struct cht_int33fe_data *data); + + struct i2c_client *battery_fg; + + /* Type-C only */ + struct i2c_client *fusb302; + struct i2c_client *pi3usb30532; + + struct fwnode_handle *dp; +}; + +int cht_int33fe_microb_probe(struct cht_int33fe_data *data); +int cht_int33fe_microb_remove(struct cht_int33fe_data *data); +int cht_int33fe_typec_probe(struct cht_int33fe_data *data); +int cht_int33fe_typec_remove(struct cht_int33fe_data *data); + +#endif /* _INTEL_CHT_INT33FE_COMMON_H */ diff --git a/drivers/platform/x86/intel_cht_int33fe_microb.c b/drivers/platform/x86/intel_cht_int33fe_microb.c new file mode 100644 index 000000000000..20b11e0d9a75 --- /dev/null +++ b/drivers/platform/x86/intel_cht_int33fe_microb.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Cherry Trail ACPI INT33FE pseudo device driver for devices with + * USB Micro-B connector (e.g. without of FUSB302 USB Type-C controller) + * + * Copyright (C) 2019 Yauhen Kharuzhy <jekhor@gmail.com> + * + * At least one Intel Cherry Trail based device which ship with Windows 10 + * (Lenovo YogaBook YB1-X91L/F tablet), have this weird INT33FE ACPI device + * with a CRS table with 2 I2cSerialBusV2 resources, for 2 different chips + * attached to various i2c busses: + * 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device + * 2. TI BQ27542 Fuel Gauge Controller + * + * So this driver is a stub / pseudo driver whose only purpose is to + * instantiate i2c-client for battery fuel gauge, so that standard i2c driver + * for these chip can bind to the it. + */ + +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/usb/pd.h> + +#include "intel_cht_int33fe_common.h" + +static const char * const bq27xxx_suppliers[] = { "bq25890-charger" }; + +static const struct property_entry bq27xxx_props[] = { + PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq27xxx_suppliers), + { } +}; + +int cht_int33fe_microb_probe(struct cht_int33fe_data *data) +{ + struct device *dev = data->dev; + struct i2c_board_info board_info; + + memset(&board_info, 0, sizeof(board_info)); + strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type)); + board_info.dev_name = "bq27542"; + board_info.properties = bq27xxx_props; + data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info); + + return PTR_ERR_OR_ZERO(data->battery_fg); +} + +int cht_int33fe_microb_remove(struct cht_int33fe_data *data) +{ + i2c_unregister_device(data->battery_fg); + + return 0; +} diff --git a/drivers/platform/x86/intel_cht_int33fe.c b/drivers/platform/x86/intel_cht_int33fe_typec.c index 1d5d877b9582..2d097fc2dd46 100644 --- a/drivers/platform/x86/intel_cht_int33fe.c +++ b/drivers/platform/x86/intel_cht_int33fe_typec.c @@ -17,17 +17,15 @@ * for these chips can bind to the them. */ -#include <linux/acpi.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#include <linux/module.h> #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/usb/pd.h> -#define EXPECTED_PTYPE 4 +#include "intel_cht_int33fe_common.h" enum { INT33FE_NODE_FUSB302, @@ -38,14 +36,6 @@ enum { INT33FE_NODE_MAX, }; -struct cht_int33fe_data { - struct i2c_client *max17047; - struct i2c_client *fusb302; - struct i2c_client *pi3usb30532; - - struct fwnode_handle *dp; -}; - static const struct software_node nodes[]; static const struct software_node_ref_args pi3usb30532_ref = { @@ -251,43 +241,20 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data) strlcpy(board_info.type, "max17047", I2C_NAME_SIZE); board_info.dev_name = "max17047"; board_info.fwnode = fwnode; - data->max17047 = i2c_acpi_new_device(dev, 1, &board_info); + data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info); - return PTR_ERR_OR_ZERO(data->max17047); + return PTR_ERR_OR_ZERO(data->battery_fg); } -static int cht_int33fe_probe(struct platform_device *pdev) +int cht_int33fe_typec_probe(struct cht_int33fe_data *data) { - struct device *dev = &pdev->dev; + struct device *dev = data->dev; struct i2c_board_info board_info; - struct cht_int33fe_data *data; struct fwnode_handle *fwnode; struct regulator *regulator; - unsigned long long ptyp; - acpi_status status; int fusb302_irq; int ret; - status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp); - if (ACPI_FAILURE(status)) { - dev_err(dev, "Error getting PTYPE\n"); - return -ENODEV; - } - - /* - * The same ACPI HID is used for different configurations check PTYP - * to ensure that we are dealing with the expected config. - */ - if (ptyp != EXPECTED_PTYPE) - return -ENODEV; - - /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */ - if (!acpi_dev_present("INT34D3", "1", 3)) { - dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n", - EXPECTED_PTYPE); - return -ENODEV; - } - /* * We expect the WC PMIC to be paired with a TI bq24292i charger-IC. * We check for the bq24292i vbus regulator here, this has 2 purposes: @@ -317,10 +284,6 @@ static int cht_int33fe_probe(struct platform_device *pdev) return fusb302_irq; } - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - ret = cht_int33fe_add_nodes(data); if (ret) return ret; @@ -365,15 +328,13 @@ static int cht_int33fe_probe(struct platform_device *pdev) goto out_unregister_fusb302; } - platform_set_drvdata(pdev, data); - return 0; out_unregister_fusb302: i2c_unregister_device(data->fusb302); out_unregister_max17047: - i2c_unregister_device(data->max17047); + i2c_unregister_device(data->battery_fg); out_remove_nodes: cht_int33fe_remove_nodes(data); @@ -381,36 +342,13 @@ out_remove_nodes: return ret; } -static int cht_int33fe_remove(struct platform_device *pdev) +int cht_int33fe_typec_remove(struct cht_int33fe_data *data) { - struct cht_int33fe_data *data = platform_get_drvdata(pdev); - i2c_unregister_device(data->pi3usb30532); i2c_unregister_device(data->fusb302); - i2c_unregister_device(data->max17047); + i2c_unregister_device(data->battery_fg); cht_int33fe_remove_nodes(data); return 0; } - -static const struct acpi_device_id cht_int33fe_acpi_ids[] = { - { "INT33FE", }, - { } -}; -MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids); - -static struct platform_driver cht_int33fe_driver = { - .driver = { - .name = "Intel Cherry Trail ACPI INT33FE driver", - .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids), - }, - .probe = cht_int33fe_probe, - .remove = cht_int33fe_remove, -}; - -module_platform_driver(cht_int33fe_driver); - -MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver"); -MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index af233b7b77f2..f14e2c5f9da5 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -164,8 +164,8 @@ static int int0002_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct x86_cpu_id *cpu_id; - struct irq_chip *irq_chip; struct gpio_chip *chip; + struct gpio_irq_chip *girq; int irq, ret; /* Menlow has a different INT0002 device? <sigh> */ @@ -192,15 +192,11 @@ static int int0002_probe(struct platform_device *pdev) chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; chip->irq.init_valid_mask = int0002_init_irq_valid_mask; - ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); - if (ret) { - dev_err(dev, "Error adding gpio chip: %d\n", ret); - return ret; - } - /* - * We manually request the irq here instead of passing a flow-handler + * We directly request the irq here instead of passing a flow-handler * to gpiochip_set_chained_irqchip, because the irq is shared. + * FIXME: augment this if we managed to pull handling of shared + * IRQs into gpiolib. */ ret = devm_request_irq(dev, irq, int0002_irq, IRQF_SHARED, "INT0002", chip); @@ -209,17 +205,21 @@ static int int0002_probe(struct platform_device *pdev) return ret; } - irq_chip = (struct irq_chip *)cpu_id->driver_data; + girq = &chip->irq; + girq->chip = (struct irq_chip *)cpu_id->driver_data; + /* This let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_edge_irq; - ret = gpiochip_irqchip_add(chip, irq_chip, 0, handle_edge_irq, - IRQ_TYPE_NONE); + ret = devm_gpiochip_add_data(dev, chip, NULL); if (ret) { - dev_err(dev, "Error adding irqchip: %d\n", ret); + dev_err(dev, "Error adding gpio chip: %d\n", ret); return ret; } - gpiochip_set_chained_irqchip(chip, irq_chip, irq, NULL); - device_init_wakeup(dev, true); return 0; } diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 94a008efb09b..571b4754477c 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -158,8 +158,9 @@ static const struct pmc_reg_map spt_reg_map = { .pm_vric1_offset = SPT_PMC_VRIC1_OFFSET, }; -/* Cannonlake: PGD PFET Enable Ack Status Register(s) bitmap */ +/* Cannon Lake: PGD PFET Enable Ack Status Register(s) bitmap */ static const struct pmc_bit_map cnp_pfear_map[] = { + /* Reserved for Cannon Lake but valid for Comet Lake */ {"PMC", BIT(0)}, {"OPI-DMI", BIT(1)}, {"SPI/eSPI", BIT(2)}, @@ -185,7 +186,7 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {"SDX", BIT(4)}, {"SPE", BIT(5)}, {"Fuse", BIT(6)}, - /* Reserved for Cannonlake but valid for Icelake */ + /* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */ {"SBR8", BIT(7)}, {"CSME_FSC", BIT(0)}, @@ -229,12 +230,12 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {"HDA_PGD4", BIT(2)}, {"HDA_PGD5", BIT(3)}, {"HDA_PGD6", BIT(4)}, - /* Reserved for Cannonlake but valid for Icelake */ + /* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */ {"PSF6", BIT(5)}, {"PSF7", BIT(6)}, {"PSF8", BIT(7)}, - /* Icelake generation onwards only */ + /* Ice Lake generation onwards only */ {"RES_65", BIT(0)}, {"RES_66", BIT(1)}, {"RES_67", BIT(2)}, @@ -324,7 +325,7 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = { {"ISH", CNP_PMC_LTR_ISH}, {"UFSX2", CNP_PMC_LTR_UFSX2}, {"EMMC", CNP_PMC_LTR_EMMC}, - /* Reserved for Cannonlake but valid for Icelake */ + /* Reserved for Cannon Lake but valid for Ice Lake */ {"WIGIG", ICL_PMC_LTR_WIGIG}, /* Below two cannot be used for LTR_IGNORE */ {"CURRENT_PLATFORM", CNP_PMC_LTR_CUR_PLT}, @@ -813,6 +814,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { INTEL_CPU_FAM6(CANNONLAKE_L, cnp_reg_map), INTEL_CPU_FAM6(ICELAKE_L, icl_reg_map), INTEL_CPU_FAM6(ICELAKE_NNPI, icl_reg_map), + INTEL_CPU_FAM6(COMETLAKE, cnp_reg_map), + INTEL_CPU_FAM6(COMETLAKE_L, cnp_reg_map), {} }; @@ -871,8 +874,8 @@ static int pmc_core_probe(struct platform_device *pdev) pmcdev->map = (struct pmc_reg_map *)cpu_id->driver_data; /* - * Coffeelake has CPU ID of Kabylake and Cannonlake PCH. So here - * Sunrisepoint PCH regmap can't be used. Use Cannonlake PCH regmap + * Coffee Lake has CPU ID of Kaby Lake and Cannon Lake PCH. So here + * Sunrisepoint PCH regmap can't be used. Use Cannon Lake PCH regmap * in this case. */ if (pmcdev->map == &spt_reg_map && !pci_dev_present(pmc_pci_ids)) diff --git a/drivers/platform/x86/intel_punit_ipc.c b/drivers/platform/x86/intel_punit_ipc.c index fa97834fdb78..05cced59e251 100644 --- a/drivers/platform/x86/intel_punit_ipc.c +++ b/drivers/platform/x86/intel_punit_ipc.c @@ -224,7 +224,6 @@ static irqreturn_t intel_punit_ioc(int irq, void *dev_id) static int intel_punit_get_bars(struct platform_device *pdev) { - struct resource *res; void __iomem *addr; /* @@ -232,14 +231,12 @@ static int intel_punit_get_bars(struct platform_device *pdev) * - BIOS_IPC BASE_DATA * - BIOS_IPC BASE_IFACE */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - addr = devm_ioremap_resource(&pdev->dev, res); + addr = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(addr)) return PTR_ERR(addr); punit_ipcdev->base[BIOS_IPC][BASE_DATA] = addr; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - addr = devm_ioremap_resource(&pdev->dev, res); + addr = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(addr)) return PTR_ERR(addr); punit_ipcdev->base[BIOS_IPC][BASE_IFACE] = addr; @@ -251,33 +248,21 @@ static int intel_punit_get_bars(struct platform_device *pdev) * - GTDRIVER_IPC BASE_DATA * - GTDRIVER_IPC BASE_IFACE */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 2); - if (res) { - addr = devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(addr)) - punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; - } + addr = devm_platform_ioremap_resource(pdev, 2); + if (!IS_ERR(addr)) + punit_ipcdev->base[ISPDRIVER_IPC][BASE_DATA] = addr; - res = platform_get_resource(pdev, IORESOURCE_MEM, 3); - if (res) { - addr = devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(addr)) - punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; - } + addr = devm_platform_ioremap_resource(pdev, 3); + if (!IS_ERR(addr)) + punit_ipcdev->base[ISPDRIVER_IPC][BASE_IFACE] = addr; - res = platform_get_resource(pdev, IORESOURCE_MEM, 4); - if (res) { - addr = devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(addr)) - punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; - } + addr = devm_platform_ioremap_resource(pdev, 4); + if (!IS_ERR(addr)) + punit_ipcdev->base[GTDRIVER_IPC][BASE_DATA] = addr; - res = platform_get_resource(pdev, IORESOURCE_MEM, 5); - if (res) { - addr = devm_ioremap_resource(&pdev->dev, res); - if (!IS_ERR(addr)) - punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr; - } + addr = devm_platform_ioremap_resource(pdev, 5); + if (!IS_ERR(addr)) + punit_ipcdev->base[GTDRIVER_IPC][BASE_IFACE] = addr; return 0; } @@ -309,14 +294,13 @@ static int intel_punit_ipc_probe(struct platform_device *pdev) ret = intel_punit_get_bars(pdev); if (ret) - goto out; + return ret; punit_ipcdev->dev = &pdev->dev; mutex_init(&punit_ipcdev->lock); init_completion(&punit_ipcdev->cmd_complete); -out: - return ret; + return 0; } static int intel_punit_ipc_remove(struct platform_device *pdev) diff --git a/drivers/platform/x86/peaq-wmi.c b/drivers/platform/x86/peaq-wmi.c index fdeb3624c529..cf9c44c20a82 100644 --- a/drivers/platform/x86/peaq-wmi.c +++ b/drivers/platform/x86/peaq-wmi.c @@ -6,7 +6,7 @@ #include <linux/acpi.h> #include <linux/dmi.h> -#include <linux/input-polldev.h> +#include <linux/input.h> #include <linux/kernel.h> #include <linux/module.h> @@ -18,8 +18,7 @@ MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); -static unsigned int peaq_ignore_events_counter; -static struct input_polled_dev *peaq_poll_dev; +static struct input_dev *peaq_poll_dev; /* * The Dolby button (yes really a Dolby button) causes an ACPI variable to get @@ -28,8 +27,10 @@ static struct input_polled_dev *peaq_poll_dev; * (if polling after the release) or twice (polling between press and release). * We ignore events for 0.5s after the first event to avoid reporting 2 presses. */ -static void peaq_wmi_poll(struct input_polled_dev *dev) +static void peaq_wmi_poll(struct input_dev *input_dev) { + static unsigned long last_event_time; + static bool had_events; union acpi_object obj; acpi_status status; u32 dummy = 0; @@ -44,22 +45,25 @@ static void peaq_wmi_poll(struct input_polled_dev *dev) return; if (obj.type != ACPI_TYPE_INTEGER) { - dev_err(&peaq_poll_dev->input->dev, + dev_err(&input_dev->dev, "Error WMBC did not return an integer\n"); return; } - if (peaq_ignore_events_counter && peaq_ignore_events_counter--) + if (!obj.integer.value) return; - if (obj.integer.value) { - input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1); - input_sync(peaq_poll_dev->input); - input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0); - input_sync(peaq_poll_dev->input); - peaq_ignore_events_counter = max(1u, - PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval); - } + if (had_events && time_before(jiffies, last_event_time + + msecs_to_jiffies(PEAQ_POLL_IGNORE_MS))) + return; + + input_event(input_dev, EV_KEY, KEY_SOUND, 1); + input_sync(input_dev); + input_event(input_dev, EV_KEY, KEY_SOUND, 0); + input_sync(input_dev); + + last_event_time = jiffies; + had_events = true; } /* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ @@ -75,6 +79,8 @@ static const struct dmi_system_id peaq_dmi_table[] __initconst = { static int __init peaq_wmi_init(void) { + int err; + /* WMI GUID is not unique, also check for a DMI match */ if (!dmi_check_system(peaq_dmi_table)) return -ENODEV; @@ -82,24 +88,36 @@ static int __init peaq_wmi_init(void) if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) return -ENODEV; - peaq_poll_dev = input_allocate_polled_device(); + peaq_poll_dev = input_allocate_device(); if (!peaq_poll_dev) return -ENOMEM; - peaq_poll_dev->poll = peaq_wmi_poll; - peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS; - peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS; - peaq_poll_dev->input->name = "PEAQ WMI hotkeys"; - peaq_poll_dev->input->phys = "wmi/input0"; - peaq_poll_dev->input->id.bustype = BUS_HOST; - input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND); + peaq_poll_dev->name = "PEAQ WMI hotkeys"; + peaq_poll_dev->phys = "wmi/input0"; + peaq_poll_dev->id.bustype = BUS_HOST; + input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND); + + err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll); + if (err) + goto err_out; + + input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS); + input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS); + + err = input_register_device(peaq_poll_dev); + if (err) + goto err_out; + + return 0; - return input_register_polled_device(peaq_poll_dev); +err_out: + input_free_device(peaq_poll_dev); + return err; } static void __exit peaq_wmi_exit(void) { - input_unregister_polled_device(peaq_poll_dev); + input_unregister_device(peaq_poll_dev); } module_init(peaq_wmi_init); diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c new file mode 100644 index 000000000000..4f6e4c342382 --- /dev/null +++ b/drivers/platform/x86/system76_acpi.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * System76 ACPI Driver + * + * Copyright (C) 2019 System76 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/acpi.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/pci_ids.h> +#include <linux/types.h> + +struct system76_data { + struct acpi_device *acpi_dev; + struct led_classdev ap_led; + struct led_classdev kb_led; + enum led_brightness kb_brightness; + enum led_brightness kb_toggle_brightness; + int kb_color; +}; + +static const struct acpi_device_id device_ids[] = { + {"17761776", 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, device_ids); + +// Array of keyboard LED brightness levels +static const enum led_brightness kb_levels[] = { + 48, + 72, + 96, + 144, + 192, + 255 +}; + +// Array of keyboard LED colors in 24-bit RGB format +static const int kb_colors[] = { + 0xFFFFFF, + 0x0000FF, + 0xFF0000, + 0xFF00FF, + 0x00FF00, + 0x00FFFF, + 0xFFFF00 +}; + +// Get a System76 ACPI device value by name +static int system76_get(struct system76_data *data, char *method) +{ + acpi_handle handle; + acpi_status status; + unsigned long long ret = 0; + + handle = acpi_device_handle(data->acpi_dev); + status = acpi_evaluate_integer(handle, method, NULL, &ret); + if (ACPI_SUCCESS(status)) + return (int)ret; + else + return -1; +} + +// Set a System76 ACPI device value by name +static int system76_set(struct system76_data *data, char *method, int value) +{ + union acpi_object obj; + struct acpi_object_list obj_list; + acpi_handle handle; + acpi_status status; + + obj.type = ACPI_TYPE_INTEGER; + obj.integer.value = value; + obj_list.count = 1; + obj_list.pointer = &obj; + handle = acpi_device_handle(data->acpi_dev); + status = acpi_evaluate_object(handle, method, &obj_list, NULL); + if (ACPI_SUCCESS(status)) + return 0; + else + return -1; +} + +// Get the airplane mode LED brightness +static enum led_brightness ap_led_get(struct led_classdev *led) +{ + struct system76_data *data; + int value; + + data = container_of(led, struct system76_data, ap_led); + value = system76_get(data, "GAPL"); + if (value > 0) + return (enum led_brightness)value; + else + return LED_OFF; +} + +// Set the airplane mode LED brightness +static void ap_led_set(struct led_classdev *led, enum led_brightness value) +{ + struct system76_data *data; + + data = container_of(led, struct system76_data, ap_led); + system76_set(data, "SAPL", value == LED_OFF ? 0 : 1); +} + +// Get the last set keyboard LED brightness +static enum led_brightness kb_led_get(struct led_classdev *led) +{ + struct system76_data *data; + + data = container_of(led, struct system76_data, kb_led); + return data->kb_brightness; +} + +// Set the keyboard LED brightness +static void kb_led_set(struct led_classdev *led, enum led_brightness value) +{ + struct system76_data *data; + + data = container_of(led, struct system76_data, kb_led); + data->kb_brightness = value; + system76_set(data, "SKBL", (int)data->kb_brightness); +} + +// Get the last set keyboard LED color +static ssize_t kb_led_color_show( + struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct led_classdev *led; + struct system76_data *data; + + led = (struct led_classdev *)dev->driver_data; + data = container_of(led, struct system76_data, kb_led); + return sprintf(buf, "%06X\n", data->kb_color); +} + +// Set the keyboard LED color +static ssize_t kb_led_color_store( + struct device *dev, + struct device_attribute *dev_attr, + const char *buf, + size_t size) +{ + struct led_classdev *led; + struct system76_data *data; + unsigned int val; + int ret; + + led = (struct led_classdev *)dev->driver_data; + data = container_of(led, struct system76_data, kb_led); + ret = kstrtouint(buf, 16, &val); + if (ret) + return ret; + if (val > 0xFFFFFF) + return -EINVAL; + data->kb_color = (int)val; + system76_set(data, "SKBC", data->kb_color); + + return size; +} + +static const struct device_attribute kb_led_color_dev_attr = { + .attr = { + .name = "color", + .mode = 0644, + }, + .show = kb_led_color_show, + .store = kb_led_color_store, +}; + +// Notify that the keyboard LED was changed by hardware +static void kb_led_notify(struct system76_data *data) +{ + led_classdev_notify_brightness_hw_changed( + &data->kb_led, + data->kb_brightness + ); +} + +// Read keyboard LED brightness as set by hardware +static void kb_led_hotkey_hardware(struct system76_data *data) +{ + int value; + + value = system76_get(data, "GKBL"); + if (value < 0) + return; + data->kb_brightness = value; + kb_led_notify(data); +} + +// Toggle the keyboard LED +static void kb_led_hotkey_toggle(struct system76_data *data) +{ + if (data->kb_brightness > 0) { + data->kb_toggle_brightness = data->kb_brightness; + kb_led_set(&data->kb_led, 0); + } else { + kb_led_set(&data->kb_led, data->kb_toggle_brightness); + } + kb_led_notify(data); +} + +// Decrease the keyboard LED brightness +static void kb_led_hotkey_down(struct system76_data *data) +{ + int i; + + if (data->kb_brightness > 0) { + for (i = ARRAY_SIZE(kb_levels); i > 0; i--) { + if (kb_levels[i - 1] < data->kb_brightness) { + kb_led_set(&data->kb_led, kb_levels[i - 1]); + break; + } + } + } else { + kb_led_set(&data->kb_led, data->kb_toggle_brightness); + } + kb_led_notify(data); +} + +// Increase the keyboard LED brightness +static void kb_led_hotkey_up(struct system76_data *data) +{ + int i; + + if (data->kb_brightness > 0) { + for (i = 0; i < ARRAY_SIZE(kb_levels); i++) { + if (kb_levels[i] > data->kb_brightness) { + kb_led_set(&data->kb_led, kb_levels[i]); + break; + } + } + } else { + kb_led_set(&data->kb_led, data->kb_toggle_brightness); + } + kb_led_notify(data); +} + +// Cycle the keyboard LED color +static void kb_led_hotkey_color(struct system76_data *data) +{ + int i; + + if (data->kb_color < 0) + return; + if (data->kb_brightness > 0) { + for (i = 0; i < ARRAY_SIZE(kb_colors); i++) { + if (kb_colors[i] == data->kb_color) + break; + } + i += 1; + if (i >= ARRAY_SIZE(kb_colors)) + i = 0; + data->kb_color = kb_colors[i]; + system76_set(data, "SKBC", data->kb_color); + } else { + kb_led_set(&data->kb_led, data->kb_toggle_brightness); + } + kb_led_notify(data); +} + +// Handle ACPI notification +static void system76_notify(struct acpi_device *acpi_dev, u32 event) +{ + struct system76_data *data; + + data = acpi_driver_data(acpi_dev); + switch (event) { + case 0x80: + kb_led_hotkey_hardware(data); + break; + case 0x81: + kb_led_hotkey_toggle(data); + break; + case 0x82: + kb_led_hotkey_down(data); + break; + case 0x83: + kb_led_hotkey_up(data); + break; + case 0x84: + kb_led_hotkey_color(data); + break; + } +} + +// Add a System76 ACPI device +static int system76_add(struct acpi_device *acpi_dev) +{ + struct system76_data *data; + int err; + + data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + acpi_dev->driver_data = data; + data->acpi_dev = acpi_dev; + + err = system76_get(data, "INIT"); + if (err) + return err; + data->ap_led.name = "system76_acpi::airplane"; + data->ap_led.flags = LED_CORE_SUSPENDRESUME; + data->ap_led.brightness_get = ap_led_get; + data->ap_led.brightness_set = ap_led_set; + data->ap_led.max_brightness = 1; + data->ap_led.default_trigger = "rfkill-none"; + err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led); + if (err) + return err; + + data->kb_led.name = "system76_acpi::kbd_backlight"; + data->kb_led.flags = LED_BRIGHT_HW_CHANGED | LED_CORE_SUSPENDRESUME; + data->kb_led.brightness_get = kb_led_get; + data->kb_led.brightness_set = kb_led_set; + if (acpi_has_method(acpi_device_handle(data->acpi_dev), "SKBC")) { + data->kb_led.max_brightness = 255; + data->kb_toggle_brightness = 72; + data->kb_color = 0xffffff; + system76_set(data, "SKBC", data->kb_color); + } else { + data->kb_led.max_brightness = 5; + data->kb_color = -1; + } + err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led); + if (err) + return err; + + if (data->kb_color >= 0) { + err = device_create_file( + data->kb_led.dev, + &kb_led_color_dev_attr + ); + if (err) + return err; + } + + return 0; +} + +// Remove a System76 ACPI device +static int system76_remove(struct acpi_device *acpi_dev) +{ + struct system76_data *data; + + data = acpi_driver_data(acpi_dev); + if (data->kb_color >= 0) + device_remove_file(data->kb_led.dev, &kb_led_color_dev_attr); + + devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led); + + devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); + + system76_get(data, "FINI"); + + return 0; +} + +static struct acpi_driver system76_driver = { + .name = "System76 ACPI Driver", + .class = "hotkey", + .ids = device_ids, + .ops = { + .add = system76_add, + .remove = system76_remove, + .notify = system76_notify, + }, +}; +module_acpi_driver(system76_driver); + +MODULE_DESCRIPTION("System76 ACPI Driver"); +MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 1c7d8324ff5c..72205771d03d 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -310,6 +310,22 @@ static const struct ts_dmi_data jumper_ezpad_6_pro_b_data = { .properties = jumper_ezpad_6_pro_b_props, }; +static const struct property_entry jumper_ezpad_6_m4_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-x", 35), + PROPERTY_ENTRY_U32("touchscreen-min-y", 15), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1950), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1525), + PROPERTY_ENTRY_STRING("firmware-name", "gsl3692-jumper-ezpad-6-m4.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data jumper_ezpad_6_m4_data = { + .acpi_name = "MSSL1680:00", + .properties = jumper_ezpad_6_m4_props, +}; + static const struct property_entry jumper_ezpad_mini3_props[] = { PROPERTY_ENTRY_U32("touchscreen-min-x", 23), PROPERTY_ENTRY_U32("touchscreen-min-y", 16), @@ -498,6 +514,24 @@ static const struct ts_dmi_data pov_mobii_wintab_p1006w_v10_data = { .properties = pov_mobii_wintab_p1006w_v10_props, }; +static const struct property_entry schneider_sct101ctm_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1715), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_STRING("firmware-name", + "gsl1680-schneider-sct101ctm.fw"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_BOOL("silead,home-button"), + { } +}; + +static const struct ts_dmi_data schneider_sct101ctm_data = { + .acpi_name = "MSSL1680:00", + .properties = schneider_sct101ctm_props, +}; + static const struct property_entry teclast_x3_plus_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1980), PROPERTY_ENTRY_U32("touchscreen-size-y", 1500), @@ -789,6 +823,16 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Jumper EZpad 6 m4 */ + .driver_data = (void *)&jumper_ezpad_6_m4_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "jumper"), + DMI_MATCH(DMI_PRODUCT_NAME, "EZpad"), + /* Jumper8.S106x.A00C.1066 with the version dropped */ + DMI_MATCH(DMI_BIOS_VERSION, "Jumper8.S106x"), + }, + }, + { /* Jumper EZpad mini3 */ .driver_data = (void *)&jumper_ezpad_mini3_data, .matches = { @@ -909,6 +953,14 @@ static const struct dmi_system_id touchscreen_dmi_table[] = { }, }, { + /* Schneider SCT101CTM */ + .driver_data = (void *)&schneider_sct101ctm_data, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Default string"), + DMI_MATCH(DMI_PRODUCT_NAME, "SCT101CTM"), + }, + }, + { /* Teclast X3 Plus */ .driver_data = (void *)&teclast_x3_plus_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 59e9aa0f9643..dc2e966a5c25 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -911,7 +911,7 @@ static const struct file_operations wmi_fops = { .read = wmi_char_read, .open = wmi_char_open, .unlocked_ioctl = wmi_ioctl, - .compat_ioctl = wmi_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static int wmi_dev_probe(struct device *dev) diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a564237278ff..c721939767eb 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -140,6 +140,16 @@ config POWER_RESET_LTC2952 This driver supports an external powerdown trigger and board power down via the LTC2952. Bindings are made in the device tree. +config POWER_RESET_MT6323 + bool "MediaTek MT6323 power-off driver" + depends on MFD_MT6397 + help + The power-off driver is responsible for externally shutdown down + the power of a remote MediaTek SoC MT6323 is connected to through + controlling a tiny circuit BBPU inside MT6323 RTC. + + Say Y if you have a board where MT6323 could be found. + config POWER_RESET_QNAP bool "QNAP power-off driver" depends on OF_GPIO && PLAT_ORION diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 85da3198e4e0..da37f8b851dc 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o +obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o diff --git a/drivers/power/reset/mt6323-poweroff.c b/drivers/power/reset/mt6323-poweroff.c new file mode 100644 index 000000000000..1caf43d9e46d --- /dev/null +++ b/drivers/power/reset/mt6323-poweroff.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Power off through MediaTek PMIC + * + * Copyright (C) 2018 MediaTek Inc. + * + * Author: Sean Wang <sean.wang@mediatek.com> + * + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/mfd/mt6397/rtc.h> + +struct mt6323_pwrc { + struct device *dev; + struct regmap *regmap; + u32 base; +}; + +static struct mt6323_pwrc *mt_pwrc; + +static void mt6323_do_pwroff(void) +{ + struct mt6323_pwrc *pwrc = mt_pwrc; + unsigned int val; + int ret; + + regmap_write(pwrc->regmap, pwrc->base + RTC_BBPU, RTC_BBPU_KEY); + regmap_write(pwrc->regmap, pwrc->base + RTC_WRTGR, 1); + + ret = regmap_read_poll_timeout(pwrc->regmap, + pwrc->base + RTC_BBPU, val, + !(val & RTC_BBPU_CBUSY), + MTK_RTC_POLL_DELAY_US, + MTK_RTC_POLL_TIMEOUT); + if (ret) + dev_err(pwrc->dev, "failed to write BBPU: %d\n", ret); + + /* Wait some time until system down, otherwise, notice with a warn */ + mdelay(1000); + + WARN_ONCE(1, "Unable to power off system\n"); +} + +static int mt6323_pwrc_probe(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); + struct mt6323_pwrc *pwrc; + struct resource *res; + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pwrc->base = res->start; + pwrc->regmap = mt6397_chip->regmap; + pwrc->dev = &pdev->dev; + mt_pwrc = pwrc; + + pm_power_off = &mt6323_do_pwroff; + + return 0; +} + +static int mt6323_pwrc_remove(struct platform_device *pdev) +{ + if (pm_power_off == &mt6323_do_pwroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id mt6323_pwrc_dt_match[] = { + { .compatible = "mediatek,mt6323-pwrc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match); + +static struct platform_driver mt6323_pwrc_driver = { + .probe = mt6323_pwrc_probe, + .remove = mt6323_pwrc_remove, + .driver = { + .name = "mt6323-pwrc", + .of_match_table = mt6323_pwrc_dt_match, + }, +}; + +module_platform_driver(mt6323_pwrc_driver); + +MODULE_DESCRIPTION("Poweroff driver for MT6323 PMIC"); +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index de919f2e8b94..471128a2e723 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -61,6 +61,7 @@ #define QDSP6SS_GFMUX_CTL_REG 0x020 #define QDSP6SS_PWR_CTL_REG 0x030 #define QDSP6SS_MEM_PWR_CTL 0x0B0 +#define QDSP6V6SS_MEM_PWR_CTL 0x034 #define QDSP6SS_STRAP_ACC 0x110 /* AXI Halt Register Offsets */ @@ -196,6 +197,7 @@ enum { MSS_MSM8916, MSS_MSM8974, MSS_MSM8996, + MSS_MSM8998, MSS_SDM845, }; @@ -498,7 +500,10 @@ static int q6v5proc_reset(struct q6v5 *qproc) } goto pbl_wait; - } else if (qproc->version == MSS_MSM8996) { + } else if (qproc->version == MSS_MSM8996 || + qproc->version == MSS_MSM8998) { + int mem_pwr_ctl; + /* Override the ACC value if required */ writel(QDSP6SS_ACC_OVERRIDE_VAL, qproc->reg_base + QDSP6SS_STRAP_ACC); @@ -543,17 +548,24 @@ static int q6v5proc_reset(struct q6v5 *qproc) writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); /* Turn on L1, L2, ETB and JU memories 1 at a time */ - val = readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); - for (i = 19; i >= 0; i--) { + if (qproc->version == MSS_MSM8996) { + mem_pwr_ctl = QDSP6SS_MEM_PWR_CTL; + i = 19; + } else { + /* MSS_MSM8998 */ + mem_pwr_ctl = QDSP6V6SS_MEM_PWR_CTL; + i = 28; + } + val = readl(qproc->reg_base + mem_pwr_ctl); + for (; i >= 0; i--) { val |= BIT(i); - writel(val, qproc->reg_base + - QDSP6SS_MEM_PWR_CTL); + writel(val, qproc->reg_base + mem_pwr_ctl); /* * Read back value to ensure the write is done then * wait for 1us for both memory peripheral and data * array to turn on. */ - val |= readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); + val |= readl(qproc->reg_base + mem_pwr_ctl); udelay(1); } /* Remove word line clamp */ @@ -1571,6 +1583,33 @@ static const struct rproc_hexagon_res sdm845_mss = { .version = MSS_SDM845, }; +static const struct rproc_hexagon_res msm8998_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_clk_names = (char*[]){ + "xo", + "qdss", + "mem", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + "gpll0_mss", + "mnoc_axi", + "snoc_axi", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + NULL + }, + .need_mem_protection = true, + .has_alt_reset = false, + .version = MSS_MSM8998, +}; + static const struct rproc_hexagon_res msm8996_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { @@ -1677,6 +1716,7 @@ static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, + { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss}, { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss}, { }, }; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 3c5fbbbfb0f1..307df98347ba 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -44,8 +44,6 @@ static DEFINE_MUTEX(rproc_list_mutex); static LIST_HEAD(rproc_list); -typedef int (*rproc_handle_resources_t)(struct rproc *rproc, - struct resource_table *table, int len); typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int offset, int avail); @@ -336,7 +334,8 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) return -ENOMEM; } else { /* Register carveout in in list */ - mem = rproc_mem_entry_init(dev, 0, 0, size, rsc->vring[i].da, + mem = rproc_mem_entry_init(dev, NULL, 0, + size, rsc->vring[i].da, rproc_alloc_carveout, rproc_release_carveout, "vdev%dvring%d", @@ -400,7 +399,7 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) void rproc_free_vring(struct rproc_vring *rvring) { struct rproc *rproc = rvring->rvdev->rproc; - int idx = rvring->rvdev->vring - rvring; + int idx = rvring - rvring->rvdev->vring; struct fw_rsc_vdev *rsc; idr_remove(&rproc->notifyids, rvring->notifyid); @@ -913,7 +912,7 @@ static int rproc_handle_carveout(struct rproc *rproc, } /* Register carveout in in list */ - carveout = rproc_mem_entry_init(dev, 0, 0, rsc->len, rsc->da, + carveout = rproc_mem_entry_init(dev, NULL, 0, rsc->len, rsc->da, rproc_alloc_carveout, rproc_release_carveout, rsc->name); if (!carveout) { diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 8cd4a0a3892b..dd93cf04e17f 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -333,9 +333,6 @@ struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, void rproc_delete_debug_dir(struct rproc *rproc) { - if (!rproc->dbg_dir) - return; - debugfs_remove_recursive(rproc->dbg_dir); } diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 2cf4b2992bfc..a18f88044111 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -15,9 +15,11 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_reserved_mem.h> +#include <linux/pm_wakeirq.h> #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/reset.h> +#include <linux/workqueue.h> #include "remoteproc_internal.h" @@ -31,7 +33,9 @@ #define STM32_SMC_REG_WRITE 0x1 #define STM32_MBX_VQ0 "vq0" +#define STM32_MBX_VQ0_ID 0 #define STM32_MBX_VQ1 "vq1" +#define STM32_MBX_VQ1_ID 1 #define STM32_MBX_SHUTDOWN "shutdown" struct stm32_syscon { @@ -58,6 +62,7 @@ struct stm32_mbox { const unsigned char name[10]; struct mbox_chan *chan; struct mbox_client client; + struct work_struct vq_work; int vq_id; }; @@ -65,9 +70,11 @@ struct stm32_rproc { struct reset_control *rst; struct stm32_syscon hold_boot; struct stm32_syscon pdds; + int wdg_irq; u32 nb_rmems; struct stm32_rproc_mem *rmems; struct stm32_mbox mb[MBOX_NB_MBX]; + struct workqueue_struct *workqueue; bool secured_soc; }; @@ -261,13 +268,22 @@ static irqreturn_t stm32_rproc_wdg(int irq, void *data) return IRQ_HANDLED; } +static void stm32_rproc_mb_vq_work(struct work_struct *work) +{ + struct stm32_mbox *mb = container_of(work, struct stm32_mbox, vq_work); + struct rproc *rproc = dev_get_drvdata(mb->client.dev); + + if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) + dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); +} + static void stm32_rproc_mb_callback(struct mbox_client *cl, void *data) { struct rproc *rproc = dev_get_drvdata(cl->dev); struct stm32_mbox *mb = container_of(cl, struct stm32_mbox, client); + struct stm32_rproc *ddata = rproc->priv; - if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) - dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); + queue_work(ddata->workqueue, &mb->vq_work); } static void stm32_rproc_free_mbox(struct rproc *rproc) @@ -285,7 +301,7 @@ static void stm32_rproc_free_mbox(struct rproc *rproc) static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { { .name = STM32_MBX_VQ0, - .vq_id = 0, + .vq_id = STM32_MBX_VQ0_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, @@ -293,7 +309,7 @@ static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { }, { .name = STM32_MBX_VQ1, - .vq_id = 1, + .vq_id = STM32_MBX_VQ1_ID, .client = { .rx_callback = stm32_rproc_mb_callback, .tx_block = false, @@ -310,11 +326,12 @@ static const struct stm32_mbox stm32_rproc_mbox[MBOX_NB_MBX] = { } }; -static void stm32_rproc_request_mbox(struct rproc *rproc) +static int stm32_rproc_request_mbox(struct rproc *rproc) { struct stm32_rproc *ddata = rproc->priv; struct device *dev = &rproc->dev; unsigned int i; + int j; const unsigned char *name; struct mbox_client *cl; @@ -329,10 +346,24 @@ static void stm32_rproc_request_mbox(struct rproc *rproc) ddata->mb[i].chan = mbox_request_channel_byname(cl, name); if (IS_ERR(ddata->mb[i].chan)) { + if (PTR_ERR(ddata->mb[i].chan) == -EPROBE_DEFER) + goto err_probe; dev_warn(dev, "cannot get %s mbox\n", name); ddata->mb[i].chan = NULL; } + if (ddata->mb[i].vq_id >= 0) { + INIT_WORK(&ddata->mb[i].vq_work, + stm32_rproc_mb_vq_work); + } } + + return 0; + +err_probe: + for (j = i - 1; j >= 0; j--) + if (ddata->mb[j].chan) + mbox_free_channel(ddata->mb[j].chan); + return -EPROBE_DEFER; } static int stm32_rproc_set_hold_boot(struct rproc *rproc, bool hold) @@ -528,6 +559,13 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) return err; } + ddata->wdg_irq = irq; + + if (of_property_read_bool(np, "wakeup-source")) { + device_init_wakeup(dev, true); + dev_pm_set_wake_irq(dev, irq); + } + dev_info(dev, "wdg irq registered\n"); } @@ -589,14 +627,22 @@ static int stm32_rproc_probe(struct platform_device *pdev) rproc->has_iommu = false; ddata = rproc->priv; + ddata->workqueue = create_workqueue(dev_name(dev)); + if (!ddata->workqueue) { + dev_err(dev, "cannot create workqueue\n"); + ret = -ENOMEM; + goto free_rproc; + } platform_set_drvdata(pdev, rproc); ret = stm32_rproc_parse_dt(pdev); if (ret) - goto free_rproc; + goto free_wkq; - stm32_rproc_request_mbox(rproc); + ret = stm32_rproc_request_mbox(rproc); + if (ret) + goto free_rproc; ret = rproc_add(rproc); if (ret) @@ -606,7 +652,13 @@ static int stm32_rproc_probe(struct platform_device *pdev) free_mb: stm32_rproc_free_mbox(rproc); +free_wkq: + destroy_workqueue(ddata->workqueue); free_rproc: + if (device_may_wakeup(dev)) { + dev_pm_clear_wake_irq(dev); + device_init_wakeup(dev, false); + } rproc_free(rproc); return ret; } @@ -614,22 +666,56 @@ free_rproc: static int stm32_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); + struct stm32_rproc *ddata = rproc->priv; + struct device *dev = &pdev->dev; if (atomic_read(&rproc->power) > 0) rproc_shutdown(rproc); rproc_del(rproc); stm32_rproc_free_mbox(rproc); + destroy_workqueue(ddata->workqueue); + + if (device_may_wakeup(dev)) { + dev_pm_clear_wake_irq(dev); + device_init_wakeup(dev, false); + } rproc_free(rproc); return 0; } +static int __maybe_unused stm32_rproc_suspend(struct device *dev) +{ + struct rproc *rproc = dev_get_drvdata(dev); + struct stm32_rproc *ddata = rproc->priv; + + if (device_may_wakeup(dev)) + return enable_irq_wake(ddata->wdg_irq); + + return 0; +} + +static int __maybe_unused stm32_rproc_resume(struct device *dev) +{ + struct rproc *rproc = dev_get_drvdata(dev); + struct stm32_rproc *ddata = rproc->priv; + + if (device_may_wakeup(dev)) + return disable_irq_wake(ddata->wdg_irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(stm32_rproc_pm_ops, + stm32_rproc_suspend, stm32_rproc_resume); + static struct platform_driver stm32_rproc_driver = { .probe = stm32_rproc_probe, .remove = stm32_rproc_remove, .driver = { .name = "stm32-rproc", + .pm = &stm32_rproc_pm_ops, .of_match_table = of_match_ptr(stm32_rproc_match), }, }; diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index d0322b41eca5..709276540ef1 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -21,7 +21,7 @@ config RPMSG_QCOM_GLINK_NATIVE config RPMSG_QCOM_GLINK_RPM tristate "Qualcomm RPM Glink driver" - select RPMSG_QCOM_GLINK_NATIVE + select RPMSG_QCOM_GLINK_NATIVE depends on HAS_IOMEM depends on MAILBOX help diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c index 621f1afd4d6b..1995f5b3ea67 100644 --- a/drivers/rpmsg/qcom_glink_native.c +++ b/drivers/rpmsg/qcom_glink_native.c @@ -241,10 +241,31 @@ static void qcom_glink_channel_release(struct kref *ref) { struct glink_channel *channel = container_of(ref, struct glink_channel, refcount); + struct glink_core_rx_intent *intent; + struct glink_core_rx_intent *tmp; unsigned long flags; + int iid; + + /* cancel pending rx_done work */ + cancel_work_sync(&channel->intent_work); spin_lock_irqsave(&channel->intent_lock, flags); + /* Free all non-reuse intents pending rx_done work */ + list_for_each_entry_safe(intent, tmp, &channel->done_intents, node) { + if (!intent->reuse) { + kfree(intent->data); + kfree(intent); + } + } + + idr_for_each_entry(&channel->liids, tmp, iid) { + kfree(tmp->data); + kfree(tmp); + } idr_destroy(&channel->liids); + + idr_for_each_entry(&channel->riids, tmp, iid) + kfree(tmp); idr_destroy(&channel->riids); spin_unlock_irqrestore(&channel->intent_lock, flags); @@ -1094,13 +1115,12 @@ static int qcom_glink_create_remote(struct qcom_glink *glink, close_link: /* * Send a close request to "undo" our open-ack. The close-ack will - * release the last reference. + * release qcom_glink_send_open_req() reference and the last reference + * will be relesed after receiving remote_close or transport unregister + * by calling qcom_glink_native_remove(). */ qcom_glink_send_close_req(glink, channel); - /* Release qcom_glink_send_open_req() reference */ - kref_put(&channel->refcount, qcom_glink_channel_release); - return ret; } @@ -1415,15 +1435,13 @@ static int qcom_glink_rx_open(struct qcom_glink *glink, unsigned int rcid, ret = rpmsg_register_device(rpdev); if (ret) - goto free_rpdev; + goto rcid_remove; channel->rpdev = rpdev; } return 0; -free_rpdev: - kfree(rpdev); rcid_remove: spin_lock_irqsave(&glink->idr_lock, flags); idr_remove(&glink->rcids, channel->rcid); @@ -1544,6 +1562,18 @@ static void qcom_glink_work(struct work_struct *work) } } +static void qcom_glink_cancel_rx_work(struct qcom_glink *glink) +{ + struct glink_defer_cmd *dcmd; + struct glink_defer_cmd *tmp; + + /* cancel any pending deferred rx_work */ + cancel_work_sync(&glink->rx_work); + + list_for_each_entry_safe(dcmd, tmp, &glink->rx_queue, node) + kfree(dcmd); +} + struct qcom_glink *qcom_glink_native_probe(struct device *dev, unsigned long features, struct qcom_glink_pipe *rx, @@ -1619,23 +1649,24 @@ void qcom_glink_native_remove(struct qcom_glink *glink) struct glink_channel *channel; int cid; int ret; - unsigned long flags; disable_irq(glink->irq); - cancel_work_sync(&glink->rx_work); + qcom_glink_cancel_rx_work(glink); ret = device_for_each_child(glink->dev, NULL, qcom_glink_remove_device); if (ret) dev_warn(glink->dev, "Can't remove GLINK devices: %d\n", ret); - spin_lock_irqsave(&glink->idr_lock, flags); /* Release any defunct local channels, waiting for close-ack */ idr_for_each_entry(&glink->lcids, channel, cid) kref_put(&channel->refcount, qcom_glink_channel_release); + /* Release any defunct local channels, waiting for close-req */ + idr_for_each_entry(&glink->rcids, channel, cid) + kref_put(&channel->refcount, qcom_glink_channel_release); + idr_destroy(&glink->lcids); idr_destroy(&glink->rcids); - spin_unlock_irqrestore(&glink->idr_lock, flags); mbox_free_channel(glink->mbox_chan); } EXPORT_SYMBOL_GPL(qcom_glink_native_remove); diff --git a/drivers/rpmsg/qcom_glink_smem.c b/drivers/rpmsg/qcom_glink_smem.c index 4238383d8685..579bc4443f6d 100644 --- a/drivers/rpmsg/qcom_glink_smem.c +++ b/drivers/rpmsg/qcom_glink_smem.c @@ -105,7 +105,7 @@ static void glink_smem_rx_advance(struct qcom_glink_pipe *np, tail = le32_to_cpu(*pipe->tail); tail += count; - if (tail > pipe->native.length) + if (tail >= pipe->native.length) tail -= pipe->native.length; *pipe->tail = cpu_to_le32(tail); diff --git a/drivers/rpmsg/rpmsg_char.c b/drivers/rpmsg/rpmsg_char.c index eea5ebbb5119..4bbbacdbf3bb 100644 --- a/drivers/rpmsg/rpmsg_char.c +++ b/drivers/rpmsg/rpmsg_char.c @@ -146,7 +146,6 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) { struct rpmsg_eptdev *eptdev = cdev_to_eptdev(inode->i_cdev); struct device *dev = &eptdev->dev; - struct sk_buff *skb; /* Close the endpoint, if it's not already destroyed by the parent */ mutex_lock(&eptdev->ept_lock); @@ -157,10 +156,7 @@ static int rpmsg_eptdev_release(struct inode *inode, struct file *filp) mutex_unlock(&eptdev->ept_lock); /* Discard all SKBs */ - while (!skb_queue_empty(&eptdev->queue)) { - skb = skb_dequeue(&eptdev->queue); - kfree_skb(skb); - } + skb_queue_purge(&eptdev->queue); put_device(dev); @@ -227,8 +223,10 @@ static ssize_t rpmsg_eptdev_write_iter(struct kiocb *iocb, if (!kbuf) return -ENOMEM; - if (!copy_from_iter_full(kbuf, len, from)) - return -EFAULT; + if (!copy_from_iter_full(kbuf, len, from)) { + ret = -EFAULT; + goto free_kbuf; + } if (mutex_lock_interruptible(&eptdev->ept_lock)) { ret = -ERESTARTSYS; @@ -290,7 +288,7 @@ static const struct file_operations rpmsg_eptdev_fops = { .write_iter = rpmsg_eptdev_write_iter, .poll = rpmsg_eptdev_poll, .unlocked_ioctl = rpmsg_eptdev_ioctl, - .compat_ioctl = rpmsg_eptdev_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static ssize_t name_show(struct device *dev, struct device_attribute *attr, @@ -451,7 +449,7 @@ static const struct file_operations rpmsg_ctrldev_fops = { .open = rpmsg_ctrldev_open, .release = rpmsg_ctrldev_release, .unlocked_ioctl = rpmsg_ctrldev_ioctl, - .compat_ioctl = rpmsg_ctrldev_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static void rpmsg_ctrldev_release_device(struct device *dev) diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c index 84feb2565abd..5b8ebe86124a 100644 --- a/drivers/rtc/dev.c +++ b/drivers/rtc/dev.c @@ -10,6 +10,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/compat.h> #include <linux/module.h> #include <linux/rtc.h> #include <linux/sched/signal.h> @@ -360,7 +361,6 @@ static long rtc_dev_ioctl(struct file *file, case RTC_IRQP_SET: err = rtc_irq_set_freq(rtc, arg); break; - case RTC_IRQP_READ: err = put_user(rtc->irq_freq, (unsigned long __user *)uarg); break; @@ -399,6 +399,34 @@ done: return err; } +#ifdef CONFIG_COMPAT +#define RTC_IRQP_SET32 _IOW('p', 0x0c, __u32) +#define RTC_IRQP_READ32 _IOR('p', 0x0b, __u32) +#define RTC_EPOCH_SET32 _IOW('p', 0x0e, __u32) + +static long rtc_dev_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_device *rtc = file->private_data; + void __user *uarg = compat_ptr(arg); + + switch (cmd) { + case RTC_IRQP_READ32: + return put_user(rtc->irq_freq, (__u32 __user *)uarg); + + case RTC_IRQP_SET32: + /* arg is a plain integer, not pointer */ + return rtc_dev_ioctl(file, RTC_IRQP_SET, arg); + + case RTC_EPOCH_SET32: + /* arg is a plain integer, not pointer */ + return rtc_dev_ioctl(file, RTC_EPOCH_SET, arg); + } + + return rtc_dev_ioctl(file, cmd, (unsigned long)uarg); +} +#endif + static int rtc_dev_fasync(int fd, struct file *file, int on) { struct rtc_device *rtc = file->private_data; @@ -434,6 +462,9 @@ static const struct file_operations rtc_dev_fops = { .read = rtc_dev_read, .poll = rtc_dev_poll, .unlocked_ioctl = rtc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = rtc_dev_compat_ioctl, +#endif .open = rtc_dev_open, .release = rtc_dev_release, .fasync = rtc_dev_fasync, diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 367497914c10..6e96916c41ff 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -586,6 +586,7 @@ static const struct file_operations ds1374_wdt_fops = { .owner = THIS_MODULE, .read = ds1374_wdt_read, .unlocked_ioctl = ds1374_wdt_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, .write = ds1374_wdt_write, .open = ds1374_wdt_open, .release = ds1374_wdt_release, diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 5f46f85f814b..f9fa4f068de3 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -840,6 +840,7 @@ static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .read = wdt_read, .unlocked_ioctl = wdt_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, .write = wdt_write, .open = wdt_open, .release = wdt_release, diff --git a/drivers/rtc/rtc-mt6397.c b/drivers/rtc/rtc-mt6397.c index 704229eb0cac..5249fc99fd5f 100644 --- a/drivers/rtc/rtc-mt6397.c +++ b/drivers/rtc/rtc-mt6397.c @@ -4,69 +4,19 @@ * Author: Tianping.Fang <tianping.fang@mediatek.com> */ -#include <linux/delay.h> -#include <linux/init.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/mfd/mt6397/core.h> #include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/rtc.h> -#include <linux/irqdomain.h> -#include <linux/platform_device.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> -#include <linux/io.h> -#include <linux/mfd/mt6397/core.h> - -#define RTC_BBPU 0x0000 -#define RTC_BBPU_CBUSY BIT(6) - -#define RTC_WRTGR 0x003c - -#define RTC_IRQ_STA 0x0002 -#define RTC_IRQ_STA_AL BIT(0) -#define RTC_IRQ_STA_LP BIT(3) - -#define RTC_IRQ_EN 0x0004 -#define RTC_IRQ_EN_AL BIT(0) -#define RTC_IRQ_EN_ONESHOT BIT(2) -#define RTC_IRQ_EN_LP BIT(3) -#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL) - -#define RTC_AL_MASK 0x0008 -#define RTC_AL_MASK_DOW BIT(4) - -#define RTC_TC_SEC 0x000a -/* Min, Hour, Dom... register offset to RTC_TC_SEC */ -#define RTC_OFFSET_SEC 0 -#define RTC_OFFSET_MIN 1 -#define RTC_OFFSET_HOUR 2 -#define RTC_OFFSET_DOM 3 -#define RTC_OFFSET_DOW 4 -#define RTC_OFFSET_MTH 5 -#define RTC_OFFSET_YEAR 6 -#define RTC_OFFSET_COUNT 7 - -#define RTC_AL_SEC 0x0018 - -#define RTC_PDN2 0x002e -#define RTC_PDN2_PWRON_ALARM BIT(4) - -#define RTC_MIN_YEAR 1968 -#define RTC_BASE_YEAR 1900 -#define RTC_NUM_YEARS 128 -#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR) - -struct mt6397_rtc { - struct device *dev; - struct rtc_device *rtc_dev; - struct mutex lock; - struct regmap *regmap; - int irq; - u32 addr_base; -}; +#include <linux/mfd/mt6397/rtc.h> +#include <linux/mod_devicetable.h> static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) { - unsigned long timeout = jiffies + HZ; int ret; u32 data; @@ -74,19 +24,13 @@ static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) if (ret < 0) return ret; - while (1) { - ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_BBPU, - &data); - if (ret < 0) - break; - if (!(data & RTC_BBPU_CBUSY)) - break; - if (time_after(jiffies, timeout)) { - ret = -ETIMEDOUT; - break; - } - cpu_relax(); - } + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->addr_base + RTC_BBPU, data, + !(data & RTC_BBPU_CBUSY), + MTK_RTC_POLL_DELAY_US, + MTK_RTC_POLL_TIMEOUT); + if (ret < 0) + dev_err(rtc->dev, "failed to write WRTGE: %d\n", ret); return ret; } @@ -319,19 +263,19 @@ static int mtk_rtc_probe(struct platform_device *pdev) return rtc->irq; rtc->regmap = mt6397_chip->regmap; - rtc->dev = &pdev->dev; mutex_init(&rtc->lock); platform_set_drvdata(pdev, rtc); - rtc->rtc_dev = devm_rtc_allocate_device(rtc->dev); + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(rtc->rtc_dev)) return PTR_ERR(rtc->rtc_dev); - ret = request_threaded_irq(rtc->irq, NULL, - mtk_rtc_irq_handler_thread, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, - "mt6397-rtc", rtc); + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + mtk_rtc_irq_handler_thread, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + "mt6397-rtc", rtc); + if (ret) { dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", rtc->irq, ret); @@ -353,15 +297,6 @@ out_free_irq: return ret; } -static int mtk_rtc_remove(struct platform_device *pdev) -{ - struct mt6397_rtc *rtc = platform_get_drvdata(pdev); - - free_irq(rtc->irq, rtc); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int mt6397_rtc_suspend(struct device *dev) { @@ -388,6 +323,7 @@ static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_rtc_suspend, mt6397_rtc_resume); static const struct of_device_id mt6397_rtc_of_match[] = { + { .compatible = "mediatek,mt6323-rtc", }, { .compatible = "mediatek,mt6397-rtc", }, { } }; @@ -400,7 +336,6 @@ static struct platform_driver mtk_rtc_driver = { .pm = &mt6397_pm_ops, }, .probe = mtk_rtc_probe, - .remove = mtk_rtc_remove, }; module_platform_driver(mtk_rtc_driver); diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index c75230562c0d..c3671043ace7 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -4,6 +4,7 @@ * * Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org> */ +#include <linux/compat.h> #include <linux/err.h> #include <linux/fs.h> #include <linux/init.h> @@ -66,6 +67,9 @@ static void __iomem *rtc2_base; #define rtc2_read(offset) readw(rtc2_base + (offset)) #define rtc2_write(offset, value) writew((value), rtc2_base + (offset)) +/* 32-bit compat for ioctls that nobody else uses */ +#define RTC_EPOCH_READ32 _IOR('p', 0x0d, __u32) + static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ static DEFINE_SPINLOCK(rtc_lock); @@ -179,6 +183,10 @@ static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long switch (cmd) { case RTC_EPOCH_READ: return put_user(epoch, (unsigned long __user *)arg); +#ifdef CONFIG_64BIT + case RTC_EPOCH_READ32: + return put_user(epoch, (unsigned int __user *)arg); +#endif case RTC_EPOCH_SET: /* Doesn't support before 1900 */ if (arg < 1900) diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index ea4253939555..8abb42923307 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c @@ -341,14 +341,14 @@ tapechar_release(struct inode *inode, struct file *filp) */ static int __tapechar_ioctl(struct tape_device *device, - unsigned int no, unsigned long data) + unsigned int no, void __user *data) { int rc; if (no == MTIOCTOP) { struct mtop op; - if (copy_from_user(&op, (char __user *) data, sizeof(op)) != 0) + if (copy_from_user(&op, data, sizeof(op)) != 0) return -EFAULT; if (op.mt_count < 0) return -EINVAL; @@ -392,9 +392,7 @@ __tapechar_ioctl(struct tape_device *device, if (rc < 0) return rc; pos.mt_blkno = rc; - if (copy_to_user((char __user *) data, &pos, sizeof(pos)) != 0) - return -EFAULT; - return 0; + return put_user_mtpos(data, &pos); } if (no == MTIOCGET) { /* MTIOCGET: query the tape drive status. */ @@ -424,15 +422,12 @@ __tapechar_ioctl(struct tape_device *device, get.mt_blkno = rc; } - if (copy_to_user((char __user *) data, &get, sizeof(get)) != 0) - return -EFAULT; - - return 0; + return put_user_mtget(data, &get); } /* Try the discipline ioctl function. */ if (device->discipline->ioctl_fn == NULL) return -EINVAL; - return device->discipline->ioctl_fn(device, no, data); + return device->discipline->ioctl_fn(device, no, (unsigned long)data); } static long @@ -445,7 +440,7 @@ tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data) device = (struct tape_device *) filp->private_data; mutex_lock(&device->mutex); - rc = __tapechar_ioctl(device, no, data); + rc = __tapechar_ioctl(device, no, (void __user *)data); mutex_unlock(&device->mutex); return rc; } @@ -455,23 +450,17 @@ static long tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) { struct tape_device *device = filp->private_data; - int rval = -ENOIOCTLCMD; - unsigned long argp; + long rc; - /* The 'arg' argument of any ioctl function may only be used for - * pointers because of the compat pointer conversion. - * Consider this when adding new ioctls. - */ - argp = (unsigned long) compat_ptr(data); - if (device->discipline->ioctl_fn) { - mutex_lock(&device->mutex); - rval = device->discipline->ioctl_fn(device, no, argp); - mutex_unlock(&device->mutex); - if (rval == -EINVAL) - rval = -ENOIOCTLCMD; - } + if (no == MTIOCPOS32) + no = MTIOCPOS; + else if (no == MTIOCGET32) + no = MTIOCGET; - return rval; + mutex_lock(&device->mutex); + rc = __tapechar_ioctl(device, no, compat_ptr(data)); + mutex_unlock(&device->mutex); + return rc; } #endif /* CONFIG_COMPAT */ diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index 971fe074d7c9..fad936eb845f 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -156,7 +156,7 @@ static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static const struct file_operations d7s_fops = { .owner = THIS_MODULE, .unlocked_ioctl = d7s_ioctl, - .compat_ioctl = d7s_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = d7s_open, .release = d7s_release, .llseek = noop_llseek, diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index a63d5e402ff2..12d66aa61ede 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -715,9 +715,7 @@ static const struct file_operations envctrl_fops = { .owner = THIS_MODULE, .read = envctrl_read, .unlocked_ioctl = envctrl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = envctrl_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = envctrl_open, .release = envctrl_release, .llseek = noop_llseek, diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 2b1e0d503020..fb6444d0409c 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1049,9 +1049,7 @@ static int tw_chrdev_open(struct inode *inode, struct file *file) static const struct file_operations tw_fops = { .owner = THIS_MODULE, .unlocked_ioctl = tw_chrdev_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = tw_chrdev_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = tw_chrdev_open, .release = NULL, .llseek = noop_llseek, diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 93ef97af22df..2dbf35f82787 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3593,7 +3593,7 @@ static const struct file_operations cxlflash_chr_fops = { .owner = THIS_MODULE, .open = cxlflash_chr_open, .unlocked_ioctl = cxlflash_chr_ioctl, - .compat_ioctl = cxlflash_chr_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; /** diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index fdbda5c05aa0..80c5a235d193 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -613,7 +613,7 @@ static int __init esas2r_init(void) /* Handle ioctl calls to "/proc/scsi/esas2r/ATTOnode" */ static const struct file_operations esas2r_proc_fops = { - .compat_ioctl = esas2r_proc_ioctl, + .compat_ioctl = compat_ptr_ioctl, .unlocked_ioctl = esas2r_proc_ioctl, }; diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index 59cca898f088..e83163c66884 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -41,10 +41,6 @@ static int mraid_mm_setup_dma_pools(mraid_mmadp_t *); static void mraid_mm_free_adp_resources(mraid_mmadp_t *); static void mraid_mm_teardown_dma_pools(mraid_mmadp_t *); -#ifdef CONFIG_COMPAT -static long mraid_mm_compat_ioctl(struct file *, unsigned int, unsigned long); -#endif - MODULE_AUTHOR("LSI Logic Corporation"); MODULE_DESCRIPTION("LSI Logic Management Module"); MODULE_LICENSE("GPL"); @@ -68,9 +64,7 @@ static wait_queue_head_t wait_q; static const struct file_operations lsi_fops = { .open = mraid_mm_open, .unlocked_ioctl = mraid_mm_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = mraid_mm_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .owner = THIS_MODULE, .llseek = noop_llseek, }; @@ -224,7 +218,6 @@ mraid_mm_unlocked_ioctl(struct file *filep, unsigned int cmd, { int err; - /* inconsistent: mraid_mm_compat_ioctl doesn't take the BKL */ mutex_lock(&mraid_mm_mutex); err = mraid_mm_ioctl(filep, cmd, arg); mutex_unlock(&mraid_mm_mutex); @@ -1228,25 +1221,6 @@ mraid_mm_init(void) } -#ifdef CONFIG_COMPAT -/** - * mraid_mm_compat_ioctl - 32bit to 64bit ioctl conversion routine - * @filep : file operations pointer (ignored) - * @cmd : ioctl command - * @arg : user ioctl packet - */ -static long -mraid_mm_compat_ioctl(struct file *filep, unsigned int cmd, - unsigned long arg) -{ - int err; - - err = mraid_mm_ioctl(filep, cmd, arg); - - return err; -} -#endif - /** * mraid_mm_exit - Module exit point */ diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 398d2af60832..7eb88fe1eb0b 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3973,9 +3973,7 @@ static const struct file_operations pmcraid_fops = { .open = pmcraid_chr_open, .fasync = pmcraid_chr_fasync, .unlocked_ioctl = pmcraid_chr_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = pmcraid_chr_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 470ee6dc3f7e..13925021473d 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1702,20 +1702,30 @@ static void sd_rescan(struct device *dev) static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device; + struct gendisk *disk = bdev->bd_disk; + struct scsi_disk *sdkp = scsi_disk(disk); + struct scsi_device *sdev = sdkp->device; + void __user *p = compat_ptr(arg); int error; + error = scsi_verify_blk_ioctl(bdev, cmd); + if (error < 0) + return error; + error = scsi_ioctl_block_when_processing_errors(sdev, cmd, (mode & FMODE_NDELAY) != 0); if (error) return error; + + if (is_sed_ioctl(cmd)) + return sed_ioctl(sdkp->opal_dev, cmd, p); /* * Let the static ioctl translation table take care of it. */ if (!sdev->host->hostt->compat_ioctl) return -ENOIOCTLCMD; - return sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); + return sdev->host->hostt->compat_ioctl(sdev, cmd, p); } #endif diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index cce757506383..9e4ef22b3579 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -447,8 +447,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) retval = -ENOMEM; goto free_old_hdr; } - retval =__copy_from_user - (new_hdr, buf, SZ_SG_IO_HDR); + retval = get_sg_io_hdr(new_hdr, buf); req_pack_id = new_hdr->pack_id; kfree(new_hdr); if (retval) { @@ -589,10 +588,7 @@ sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) } if (hp->masked_status || hp->host_status || hp->driver_status) hp->info |= SG_INFO_CHECK; - if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) { - err = -EFAULT; - goto err_out; - } + err = put_sg_io_hdr(hp, buf); err_out: err2 = sg_finish_rem_req(srp); sg_remove_request(sfp, srp); @@ -735,7 +731,7 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, } srp->sg_io_owned = sg_io_owned; hp = &srp->header; - if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { + if (get_sg_io_hdr(hp, buf)) { sg_remove_request(sfp, srp); return -EFAULT; } @@ -893,6 +889,33 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) } } +#ifdef CONFIG_COMPAT +struct compat_sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ + char req_state; + char orphan; + char sg_io_owned; + char problem; + int pack_id; + compat_uptr_t usr_ptr; + unsigned int duration; + int unused; +}; + +static int put_compat_request_table(struct compat_sg_req_info __user *o, + struct sg_req_info *rinfo) +{ + int i; + for (i = 0; i < SG_MAX_QUEUE; i++) { + if (copy_to_user(o + i, rinfo + i, offsetof(sg_req_info_t, usr_ptr)) || + put_user((uintptr_t)rinfo[i].usr_ptr, &o[i].usr_ptr) || + put_user(rinfo[i].duration, &o[i].duration) || + put_user(rinfo[i].unused, &o[i].unused)) + return -EFAULT; + } + return 0; +} +#endif + static long sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) { @@ -1073,9 +1096,7 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) val = (sdp->device ? 1 : 0); return put_user(val, ip); case SG_GET_REQUEST_TABLE: - if (!access_ok(p, SZ_SG_REQ_INFO * SG_MAX_QUEUE)) - return -EFAULT; - else { + { sg_req_info_t *rinfo; rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, @@ -1085,8 +1106,13 @@ sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) read_lock_irqsave(&sfp->rq_list_lock, iflags); sg_fill_request_table(sfp, rinfo); read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - result = __copy_to_user(p, rinfo, - SZ_SG_REQ_INFO * SG_MAX_QUEUE); + #ifdef CONFIG_COMPAT + if (in_compat_syscall()) + result = put_compat_request_table(p, rinfo); + else + #endif + result = copy_to_user(p, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); result = result ? -EFAULT : 0; kfree(rinfo); return result; @@ -1797,7 +1823,14 @@ sg_start_req(Sg_request *srp, unsigned char *cmd) struct iovec *iov = NULL; struct iov_iter i; - res = import_iovec(rw, hp->dxferp, iov_count, 0, &iov, &i); +#ifdef CONFIG_COMPAT + if (in_compat_syscall()) + res = compat_import_iovec(rw, hp->dxferp, iov_count, + 0, &iov, &i); + else +#endif + res = import_iovec(rw, hp->dxferp, iov_count, + 0, &iov, &i); if (res < 0) return res; diff --git a/drivers/scsi/sr_vendor.c b/drivers/scsi/sr_vendor.c index e3b0ce25162b..17a56c87d383 100644 --- a/drivers/scsi/sr_vendor.c +++ b/drivers/scsi/sr_vendor.c @@ -61,6 +61,7 @@ #define VENDOR_NEC 2 #define VENDOR_TOSHIBA 3 #define VENDOR_WRITER 4 /* pre-scsi3 writers */ +#define VENDOR_CYGNAL_85ED 5 /* CD-on-a-chip */ #define VENDOR_TIMEOUT 30*HZ @@ -99,6 +100,23 @@ void sr_vendor_init(Scsi_CD *cd) } else if (!strncmp(vendor, "TOSHIBA", 7)) { cd->vendor = VENDOR_TOSHIBA; + } else if (!strncmp(vendor, "Beurer", 6) && + !strncmp(model, "Gluco Memory", 12)) { + /* The Beurer GL50 evo uses a Cygnal-manufactured CD-on-a-chip + that only accepts a subset of SCSI commands. Most of the + not-implemented commands are fine to fail, but a few, + particularly around the MMC or Audio commands, will put the + device into an unrecoverable state, so they need to be + avoided at all costs. + */ + cd->vendor = VENDOR_CYGNAL_85ED; + cd->cdi.mask |= ( + CDC_MULTI_SESSION | + CDC_CLOSE_TRAY | CDC_OPEN_TRAY | + CDC_LOCK | + CDC_GENERIC_PACKET | + CDC_PLAY_AUDIO + ); } #endif } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index e3266a64a477..9e3fff2de83e 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -22,6 +22,7 @@ static const char *verstr = "20160209"; #include <linux/module.h> +#include <linux/compat.h> #include <linux/fs.h> #include <linux/kernel.h> #include <linux/sched/signal.h> @@ -3800,14 +3801,11 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) if (STp->cleaning_req) mt_status.mt_gstat |= GMT_CLN(0xffffffff); - i = copy_to_user(p, &mt_status, sizeof(struct mtget)); - if (i) { - retval = (-EFAULT); + retval = put_user_mtget(p, &mt_status); + if (retval) goto out; - } STp->recover_reg = 0; /* Clear after read */ - retval = 0; goto out; } /* End of MTIOCGET */ if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) { @@ -3821,9 +3819,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) goto out; } mt_pos.mt_blkno = blk; - i = copy_to_user(p, &mt_pos, sizeof(struct mtpos)); - if (i) - retval = (-EFAULT); + retval = put_user_mtpos(p, &mt_pos); goto out; } mutex_unlock(&STp->lock); @@ -3857,14 +3853,26 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) } #ifdef CONFIG_COMPAT -static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long st_compat_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg) { + void __user *p = compat_ptr(arg); struct scsi_tape *STp = file->private_data; struct scsi_device *sdev = STp->device; int ret = -ENOIOCTLCMD; + + /* argument conversion is handled using put_user_mtpos/put_user_mtget */ + switch (cmd_in) { + case MTIOCTOP: + return st_ioctl(file, MTIOCTOP, (unsigned long)p); + case MTIOCPOS32: + return st_ioctl(file, MTIOCPOS, (unsigned long)p); + case MTIOCGET32: + return st_ioctl(file, MTIOCGET, (unsigned long)p); + } + if (sdev->host->hostt->compat_ioctl) { - ret = sdev->host->hostt->compat_ioctl(sdev, cmd, (void __user *)arg); + ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg); } return ret; diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 542d2bac2922..f8faf8b3d965 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1727,6 +1727,13 @@ static const struct hv_vmbus_device_id id_table[] = { MODULE_DEVICE_TABLE(vmbus, id_table); +static const struct { guid_t guid; } fc_guid = { HV_SYNTHFC_GUID }; + +static bool hv_dev_is_fc(struct hv_device *hv_dev) +{ + return guid_equal(&fc_guid.guid, &hv_dev->dev_type); +} + static int storvsc_probe(struct hv_device *device, const struct hv_vmbus_device_id *dev_id) { @@ -1934,11 +1941,45 @@ static int storvsc_remove(struct hv_device *dev) return 0; } +static int storvsc_suspend(struct hv_device *hv_dev) +{ + struct storvsc_device *stor_device = hv_get_drvdata(hv_dev); + struct Scsi_Host *host = stor_device->host; + struct hv_host_device *host_dev = shost_priv(host); + + storvsc_wait_to_drain(stor_device); + + drain_workqueue(host_dev->handle_error_wq); + + vmbus_close(hv_dev->channel); + + memset(stor_device->stor_chns, 0, + num_possible_cpus() * sizeof(void *)); + + kfree(stor_device->stor_chns); + stor_device->stor_chns = NULL; + + cpumask_clear(&stor_device->alloced_cpus); + + return 0; +} + +static int storvsc_resume(struct hv_device *hv_dev) +{ + int ret; + + ret = storvsc_connect_to_vsp(hv_dev, storvsc_ringbuffer_size, + hv_dev_is_fc(hv_dev)); + return ret; +} + static struct hv_driver storvsc_drv = { .name = KBUILD_MODNAME, .id_table = id_table, .probe = storvsc_probe, .remove = storvsc_remove, + .suspend = storvsc_suspend, + .resume = storvsc_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c index e6b1ca141b93..c394686a8e7d 100644 --- a/drivers/staging/android/ion/ion.c +++ b/drivers/staging/android/ion/ion.c @@ -533,9 +533,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static const struct file_operations ion_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ion_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ion_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; static int debug_shrink_set(void *data, u64 val) diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c index 40c6f4e7632f..313d22f6210f 100644 --- a/drivers/staging/pi433/pi433_if.c +++ b/drivers/staging/pi433/pi433_if.c @@ -928,16 +928,6 @@ pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return 0; } -#ifdef CONFIG_COMPAT -static long -pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#else -#define pi433_compat_ioctl NULL -#endif /* CONFIG_COMPAT */ - /*-------------------------------------------------------------------------*/ static int pi433_open(struct inode *inode, struct file *filp) @@ -1094,7 +1084,7 @@ static const struct file_operations pi433_fops = { .write = pi433_write, .read = pi433_read, .unlocked_ioctl = pi433_ioctl, - .compat_ioctl = pi433_compat_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pi433_open, .release = pi433_release, .llseek = no_llseek, diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c index 6a33aaa1a49f..fd0ea4dbcb91 100644 --- a/drivers/staging/vme/devices/vme_user.c +++ b/drivers/staging/vme/devices/vme_user.c @@ -494,7 +494,7 @@ static const struct file_operations vme_user_fops = { .write = vme_user_write, .llseek = vme_user_llseek, .unlocked_ioctl = vme_user_unlocked_ioctl, - .compat_ioctl = vme_user_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, .mmap = vme_user_mmap, }; diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 0f16d9ffd8d1..37d22e39fd8d 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -675,7 +675,7 @@ static const struct file_operations tee_fops = { .open = tee_open, .release = tee_release, .unlocked_ioctl = tee_ioctl, - .compat_ioctl = tee_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static void tee_release_device(struct device *dev) diff --git a/drivers/thermal/gov_bang_bang.c b/drivers/thermal/gov_bang_bang.c index e0575d29023a..b831fc77cf64 100644 --- a/drivers/thermal/gov_bang_bang.c +++ b/drivers/thermal/gov_bang_bang.c @@ -2,7 +2,7 @@ /* * gov_bang_bang.c - A simple thermal throttling governor using hysteresis * - * Copyright (C) 2014 Peter Feuerer <peter@piie.net> + * Copyright (C) 2014 Peter Kaestle <peter@piie.net> * * Based on step_wise.c with following Copyrights: * Copyright (C) 2012 Intel Corp diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 802c1210558f..a81807b394d1 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -87,6 +87,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/poll.h> +#include <linux/ppp-ioctl.h> #include <linux/proc_fs.h> #include <linux/init.h> #include <linux/module.h> @@ -2755,6 +2756,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, int retval = -ENOIOCTLCMD; switch (cmd) { + case TIOCOUTQ: case TIOCSTI: case TIOCGWINSZ: case TIOCSWINSZ: @@ -2810,6 +2812,9 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, #endif case TIOCGSOFTCAR: case TIOCSSOFTCAR: + + case PPPIOCGCHAN: + case PPPIOCGUNIT: return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); case TIOCCONS: case TIOCEXCL: diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 70afb2ca1eab..e3db6fbeadef 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -734,7 +734,7 @@ static const struct file_operations wdm_fops = { .release = wdm_release, .poll = wdm_poll, .unlocked_ioctl = wdm_ioctl, - .compat_ioctl = wdm_ioctl, + .compat_ioctl = compat_ptr_ioctl, .llseek = noop_llseek, }; diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index dcd7066ffba2..ffc9c6fdd7e1 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2217,9 +2217,7 @@ static const struct file_operations fops = { .release = usbtmc_release, .flush = usbtmc_flush, .unlocked_ioctl = usbtmc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = usbtmc_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .fasync = usbtmc_fasync, .poll = usbtmc_poll, .llseek = default_llseek, diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 879d03f5127c..12bb5722b420 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -2698,18 +2698,6 @@ static long usbdev_ioctl(struct file *file, unsigned int cmd, return ret; } -#ifdef CONFIG_COMPAT -static long usbdev_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret; - - ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg)); - - return ret; -} -#endif - /* No kernel lock - fine */ static __poll_t usbdev_poll(struct file *file, struct poll_table_struct *wait) @@ -2733,9 +2721,7 @@ const struct file_operations usbdev_file_operations = { .read = usbdev_read, .poll = usbdev_poll, .unlocked_ioctl = usbdev_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = usbdev_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .mmap = usbdev_mmap, .open = usbdev_open, .release = usbdev_release, diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 59d9d512dcda..ce1d0235969c 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1352,14 +1352,6 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code, return ret; } -#ifdef CONFIG_COMPAT -static long ffs_epfile_compat_ioctl(struct file *file, unsigned code, - unsigned long value) -{ - return ffs_epfile_ioctl(file, code, value); -} -#endif - static const struct file_operations ffs_epfile_operations = { .llseek = no_llseek, @@ -1368,9 +1360,7 @@ static const struct file_operations ffs_epfile_operations = { .read_iter = ffs_epfile_read_iter, .release = ffs_epfile_release, .unlocked_ioctl = ffs_epfile_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ffs_epfile_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index 388597930b64..c8482624ca34 100644 --- a/drivers/vfio/vfio.c +++ b/drivers/vfio/vfio.c @@ -1184,15 +1184,6 @@ static long vfio_fops_unl_ioctl(struct file *filep, return ret; } -#ifdef CONFIG_COMPAT -static long vfio_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - static int vfio_fops_open(struct inode *inode, struct file *filep) { struct vfio_container *container; @@ -1275,9 +1266,7 @@ static const struct file_operations vfio_fops = { .read = vfio_fops_read, .write = vfio_fops_write, .unlocked_ioctl = vfio_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_fops_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .mmap = vfio_fops_mmap, }; @@ -1556,15 +1545,6 @@ static long vfio_group_fops_unl_ioctl(struct file *filep, return ret; } -#ifdef CONFIG_COMPAT -static long vfio_group_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_group_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - static int vfio_group_fops_open(struct inode *inode, struct file *filep) { struct vfio_group *group; @@ -1620,9 +1600,7 @@ static int vfio_group_fops_release(struct inode *inode, struct file *filep) static const struct file_operations vfio_group_fops = { .owner = THIS_MODULE, .unlocked_ioctl = vfio_group_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_group_fops_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = vfio_group_fops_open, .release = vfio_group_fops_release, }; @@ -1687,24 +1665,13 @@ static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma) return device->ops->mmap(device->device_data, vma); } -#ifdef CONFIG_COMPAT -static long vfio_device_fops_compat_ioctl(struct file *filep, - unsigned int cmd, unsigned long arg) -{ - arg = (unsigned long)compat_ptr(arg); - return vfio_device_fops_unl_ioctl(filep, cmd, arg); -} -#endif /* CONFIG_COMPAT */ - static const struct file_operations vfio_device_fops = { .owner = THIS_MODULE, .release = vfio_device_fops_release, .read = vfio_device_fops_read, .write = vfio_device_fops_write, .unlocked_ioctl = vfio_device_fops_unl_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vfio_device_fops_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .mmap = vfio_device_fops_mmap, }; diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 1a2dd53caade..e158159671fa 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -1751,14 +1751,6 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl, } } -#ifdef CONFIG_COMPAT -static long vhost_net_compat_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - return vhost_net_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); -} -#endif - static ssize_t vhost_net_chr_read_iter(struct kiocb *iocb, struct iov_iter *to) { struct file *file = iocb->ki_filp; @@ -1794,9 +1786,7 @@ static const struct file_operations vhost_net_fops = { .write_iter = vhost_net_chr_write_iter, .poll = vhost_net_chr_poll, .unlocked_ioctl = vhost_net_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vhost_net_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = vhost_net_open, .llseek = noop_llseek, }; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index a9caf1bc3c3e..0b949a14bce3 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1727,21 +1727,11 @@ vhost_scsi_ioctl(struct file *f, } } -#ifdef CONFIG_COMPAT -static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations vhost_scsi_fops = { .owner = THIS_MODULE, .release = vhost_scsi_release, .unlocked_ioctl = vhost_scsi_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vhost_scsi_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = vhost_scsi_open, .llseek = noop_llseek, }; diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index 056308008288..e37c92d4d7ad 100644 --- a/drivers/vhost/test.c +++ b/drivers/vhost/test.c @@ -304,21 +304,11 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl, } } -#ifdef CONFIG_COMPAT -static long vhost_test_compat_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - return vhost_test_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations vhost_test_fops = { .owner = THIS_MODULE, .release = vhost_test_release, .unlocked_ioctl = vhost_test_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vhost_test_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, .open = vhost_test_open, .llseek = noop_llseek, }; diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index dde392b91bb3..50de0642dea6 100644 --- a/drivers/vhost/vsock.c +++ b/drivers/vhost/vsock.c @@ -810,23 +810,13 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl, } } -#ifdef CONFIG_COMPAT -static long vhost_vsock_dev_compat_ioctl(struct file *f, unsigned int ioctl, - unsigned long arg) -{ - return vhost_vsock_dev_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); -} -#endif - static const struct file_operations vhost_vsock_fops = { .owner = THIS_MODULE, .open = vhost_vsock_dev_open, .release = vhost_vsock_dev_release, .llseek = noop_llseek, .unlocked_ioctl = vhost_vsock_dev_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = vhost_vsock_dev_compat_ioctl, -#endif + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice vhost_vsock_misc = { diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 40676be2e46a..403707a3e503 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -99,7 +99,7 @@ config LCD_TOSA config LCD_HP700 tristate "HP Jornada 700 series LCD Driver" - depends on SA1100_JORNADA720_SSP && !PREEMPT + depends on SA1100_JORNADA720_SSP && !PREEMPTION default y help If you have an HP Jornada 700 series handheld (710/720/728) @@ -228,7 +228,7 @@ config BACKLIGHT_HP680 config BACKLIGHT_HP700 tristate "HP Jornada 700 series Backlight Driver" - depends on SA1100_JORNADA720_SSP && !PREEMPT + depends on SA1100_JORNADA720_SSP && !PREEMPTION default y help If you have an HP Jornada 700 series, @@ -282,12 +282,12 @@ config BACKLIGHT_TOSA If you have an Sharp SL-6000 Zaurus say Y to enable a driver for its backlight -config BACKLIGHT_PM8941_WLED - tristate "Qualcomm PM8941 WLED Driver" +config BACKLIGHT_QCOM_WLED + tristate "Qualcomm PMIC WLED Driver" select REGMAP help - If you have the Qualcomm PM8941, say Y to enable a driver for the - WLED block. + If you have the Qualcomm PMIC, say Y to enable a driver for the + WLED block. Currently it supports PM8941 and PMI8998. config BACKLIGHT_SAHARA tristate "Tabletkiosk Sahara Touch-iT Backlight Driver" diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 63c507c07437..6f8777037c37 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -48,8 +48,8 @@ obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o -obj-$(CONFIG_BACKLIGHT_PM8941_WLED) += pm8941-wled.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o +obj-$(CONFIG_BACKLIGHT_QCOM_WLED) += qcom-wled.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o obj-$(CONFIG_BACKLIGHT_SKY81452) += sky81452-backlight.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 18e053e4716c..75409ddfba3e 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -6,29 +6,23 @@ #include <linux/backlight.h> #include <linux/err.h> #include <linux/fb.h> -#include <linux/gpio.h> /* Only for legacy support */ #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/platform_data/gpio_backlight.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> struct gpio_backlight { - struct device *dev; struct device *fbdev; - struct gpio_desc *gpiod; - int def_value; }; -static int gpio_backlight_update_status(struct backlight_device *bl) +static int gpio_backlight_get_next_brightness(struct backlight_device *bl) { - struct gpio_backlight *gbl = bl_get_data(bl); int brightness = bl->props.brightness; if (bl->props.power != FB_BLANK_UNBLANK || @@ -36,6 +30,14 @@ static int gpio_backlight_update_status(struct backlight_device *bl) bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) brightness = 0; + return brightness; +} + +static int gpio_backlight_update_status(struct backlight_device *bl) +{ + struct gpio_backlight *gbl = bl_get_data(bl); + int brightness = gpio_backlight_get_next_brightness(bl); + gpiod_set_value_cansleep(gbl->gpiod, brightness); return 0; @@ -55,105 +57,63 @@ static const struct backlight_ops gpio_backlight_ops = { .check_fb = gpio_backlight_check_fb, }; -static int gpio_backlight_probe_dt(struct platform_device *pdev, - struct gpio_backlight *gbl) -{ - struct device *dev = &pdev->dev; - int ret; - - gbl->def_value = device_property_read_bool(dev, "default-on"); - - gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); - if (IS_ERR(gbl->gpiod)) { - ret = PTR_ERR(gbl->gpiod); - - if (ret != -EPROBE_DEFER) { - dev_err(dev, - "Error: The gpios parameter is missing or invalid.\n"); - } - return ret; - } - - return 0; -} - -static int gpio_backlight_initial_power_state(struct gpio_backlight *gbl) -{ - struct device_node *node = gbl->dev->of_node; - - /* Not booted with device tree or no phandle link to the node */ - if (!node || !node->phandle) - return gbl->def_value ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; - - /* if the enable GPIO is disabled, do not enable the backlight */ - if (gpiod_get_value_cansleep(gbl->gpiod) == 0) - return FB_BLANK_POWERDOWN; - - return FB_BLANK_UNBLANK; -} - - static int gpio_backlight_probe(struct platform_device *pdev) { - struct gpio_backlight_platform_data *pdata = - dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct gpio_backlight_platform_data *pdata = dev_get_platdata(dev); + struct device_node *of_node = dev->of_node; struct backlight_properties props; struct backlight_device *bl; struct gpio_backlight *gbl; - int ret; + int ret, init_brightness, def_value; - gbl = devm_kzalloc(&pdev->dev, sizeof(*gbl), GFP_KERNEL); + gbl = devm_kzalloc(dev, sizeof(*gbl), GFP_KERNEL); if (gbl == NULL) return -ENOMEM; - gbl->dev = &pdev->dev; + if (pdata) + gbl->fbdev = pdata->fbdev; - if (pdev->dev.fwnode) { - ret = gpio_backlight_probe_dt(pdev, gbl); - if (ret) - return ret; - } else if (pdata) { - /* - * Legacy platform data GPIO retrieveal. Do not expand - * the use of this code path, currently only used by one - * SH board. - */ - unsigned long flags = GPIOF_DIR_OUT; + def_value = device_property_read_bool(dev, "default-on"); - gbl->fbdev = pdata->fbdev; - gbl->def_value = pdata->def_value; - flags |= gbl->def_value ? GPIOF_INIT_HIGH : GPIOF_INIT_LOW; - - ret = devm_gpio_request_one(gbl->dev, pdata->gpio, flags, - pdata ? pdata->name : "backlight"); - if (ret < 0) { - dev_err(&pdev->dev, "unable to request GPIO\n"); - return ret; - } - gbl->gpiod = gpio_to_desc(pdata->gpio); - if (!gbl->gpiod) - return -EINVAL; - } else { - dev_err(&pdev->dev, - "failed to find platform data or device tree node.\n"); - return -ENODEV; + gbl->gpiod = devm_gpiod_get(dev, NULL, GPIOD_ASIS); + if (IS_ERR(gbl->gpiod)) { + ret = PTR_ERR(gbl->gpiod); + if (ret != -EPROBE_DEFER) + dev_err(dev, + "Error: The gpios parameter is missing or invalid.\n"); + return ret; } memset(&props, 0, sizeof(props)); props.type = BACKLIGHT_RAW; props.max_brightness = 1; - bl = devm_backlight_device_register(&pdev->dev, dev_name(&pdev->dev), - &pdev->dev, gbl, &gpio_backlight_ops, - &props); + bl = devm_backlight_device_register(dev, dev_name(dev), dev, gbl, + &gpio_backlight_ops, &props); if (IS_ERR(bl)) { - dev_err(&pdev->dev, "failed to register backlight\n"); + dev_err(dev, "failed to register backlight\n"); return PTR_ERR(bl); } - bl->props.power = gpio_backlight_initial_power_state(gbl); + /* Set the initial power state */ + if (!of_node || !of_node->phandle) + /* Not booted with device tree or no phandle link to the node */ + bl->props.power = def_value ? FB_BLANK_UNBLANK + : FB_BLANK_POWERDOWN; + else if (gpiod_get_direction(gbl->gpiod) == 0 && + gpiod_get_value_cansleep(gbl->gpiod) == 0) + bl->props.power = FB_BLANK_POWERDOWN; + else + bl->props.power = FB_BLANK_UNBLANK; + bl->props.brightness = 1; - backlight_update_status(bl); + init_brightness = gpio_backlight_get_next_brightness(bl); + ret = gpiod_direction_output(gbl->gpiod, init_brightness); + if (ret) { + dev_err(dev, "failed to set initial brightness\n"); + return ret; + } platform_set_drvdata(pdev, bl); return 0; diff --git a/drivers/video/backlight/ipaq_micro_bl.c b/drivers/video/backlight/ipaq_micro_bl.c index 1123f67c12b3..85b16cc82878 100644 --- a/drivers/video/backlight/ipaq_micro_bl.c +++ b/drivers/video/backlight/ipaq_micro_bl.c @@ -44,7 +44,7 @@ static const struct backlight_ops micro_bl_ops = { .update_status = micro_bl_update_status, }; -static struct backlight_properties micro_bl_props = { +static const struct backlight_properties micro_bl_props = { .type = BACKLIGHT_RAW, .max_brightness = 255, .power = FB_BLANK_UNBLANK, diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c index 2d8e8192e4e2..ee320883b710 100644 --- a/drivers/video/backlight/lm3630a_bl.c +++ b/drivers/video/backlight/lm3630a_bl.c @@ -12,6 +12,7 @@ #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/regmap.h> +#include <linux/gpio/consumer.h> #include <linux/pwm.h> #include <linux/platform_data/lm3630a_bl.h> @@ -48,6 +49,7 @@ struct lm3630a_chip { struct lm3630a_platform_data *pdata; struct backlight_device *bleda; struct backlight_device *bledb; + struct gpio_desc *enable_gpio; struct regmap *regmap; struct pwm_device *pwmd; }; @@ -534,6 +536,13 @@ static int lm3630a_probe(struct i2c_client *client, } pchip->pdata = pdata; + pchip->enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", + GPIOD_OUT_HIGH); + if (IS_ERR(pchip->enable_gpio)) { + rval = PTR_ERR(pchip->enable_gpio); + return rval; + } + /* chip initialize */ rval = lm3630a_chip_init(pchip); if (rval < 0) { @@ -598,12 +607,14 @@ static const struct i2c_device_id lm3630a_id[] = { {} }; +MODULE_DEVICE_TABLE(i2c, lm3630a_id); + static const struct of_device_id lm3630a_match_table[] = { { .compatible = "ti,lm3630a", }, { }, }; -MODULE_DEVICE_TABLE(i2c, lm3630a_id); +MODULE_DEVICE_TABLE(of, lm3630a_match_table); static struct i2c_driver lm3630a_i2c_driver = { .driver = { diff --git a/drivers/video/backlight/pm8941-wled.c b/drivers/video/backlight/pm8941-wled.c deleted file mode 100644 index 82b85725a22a..000000000000 --- a/drivers/video/backlight/pm8941-wled.c +++ /dev/null @@ -1,424 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015, Sony Mobile Communications, AB. - */ - -#include <linux/kernel.h> -#include <linux/backlight.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/regmap.h> - -/* From DT binding */ -#define PM8941_WLED_DEFAULT_BRIGHTNESS 2048 - -#define PM8941_WLED_REG_VAL_BASE 0x40 -#define PM8941_WLED_REG_VAL_MAX 0xFFF - -#define PM8941_WLED_REG_MOD_EN 0x46 -#define PM8941_WLED_REG_MOD_EN_BIT BIT(7) -#define PM8941_WLED_REG_MOD_EN_MASK BIT(7) - -#define PM8941_WLED_REG_SYNC 0x47 -#define PM8941_WLED_REG_SYNC_MASK 0x07 -#define PM8941_WLED_REG_SYNC_LED1 BIT(0) -#define PM8941_WLED_REG_SYNC_LED2 BIT(1) -#define PM8941_WLED_REG_SYNC_LED3 BIT(2) -#define PM8941_WLED_REG_SYNC_ALL 0x07 -#define PM8941_WLED_REG_SYNC_CLEAR 0x00 - -#define PM8941_WLED_REG_FREQ 0x4c -#define PM8941_WLED_REG_FREQ_MASK 0x0f - -#define PM8941_WLED_REG_OVP 0x4d -#define PM8941_WLED_REG_OVP_MASK 0x03 - -#define PM8941_WLED_REG_BOOST 0x4e -#define PM8941_WLED_REG_BOOST_MASK 0x07 - -#define PM8941_WLED_REG_SINK 0x4f -#define PM8941_WLED_REG_SINK_MASK 0xe0 -#define PM8941_WLED_REG_SINK_SHFT 0x05 - -/* Per-'string' registers below */ -#define PM8941_WLED_REG_STR_OFFSET 0x10 - -#define PM8941_WLED_REG_STR_MOD_EN_BASE 0x60 -#define PM8941_WLED_REG_STR_MOD_MASK BIT(7) -#define PM8941_WLED_REG_STR_MOD_EN BIT(7) - -#define PM8941_WLED_REG_STR_SCALE_BASE 0x62 -#define PM8941_WLED_REG_STR_SCALE_MASK 0x1f - -#define PM8941_WLED_REG_STR_MOD_SRC_BASE 0x63 -#define PM8941_WLED_REG_STR_MOD_SRC_MASK 0x01 -#define PM8941_WLED_REG_STR_MOD_SRC_INT 0x00 -#define PM8941_WLED_REG_STR_MOD_SRC_EXT 0x01 - -#define PM8941_WLED_REG_STR_CABC_BASE 0x66 -#define PM8941_WLED_REG_STR_CABC_MASK BIT(7) -#define PM8941_WLED_REG_STR_CABC_EN BIT(7) - -struct pm8941_wled_config { - u32 i_boost_limit; - u32 ovp; - u32 switch_freq; - u32 num_strings; - u32 i_limit; - bool cs_out_en; - bool ext_gen; - bool cabc_en; -}; - -struct pm8941_wled { - const char *name; - struct regmap *regmap; - u16 addr; - - struct pm8941_wled_config cfg; -}; - -static int pm8941_wled_update_status(struct backlight_device *bl) -{ - struct pm8941_wled *wled = bl_get_data(bl); - u16 val = bl->props.brightness; - u8 ctrl = 0; - int rc; - int i; - - if (bl->props.power != FB_BLANK_UNBLANK || - bl->props.fb_blank != FB_BLANK_UNBLANK || - bl->props.state & BL_CORE_FBBLANK) - val = 0; - - if (val != 0) - ctrl = PM8941_WLED_REG_MOD_EN_BIT; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_MOD_EN, - PM8941_WLED_REG_MOD_EN_MASK, ctrl); - if (rc) - return rc; - - for (i = 0; i < wled->cfg.num_strings; ++i) { - u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; - - rc = regmap_bulk_write(wled->regmap, - wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i, - v, 2); - if (rc) - return rc; - } - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SYNC, - PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR); - return rc; -} - -static int pm8941_wled_setup(struct pm8941_wled *wled) -{ - int rc; - int i; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_OVP, - PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_BOOST, - PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_FREQ, - PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq); - if (rc) - return rc; - - if (wled->cfg.cs_out_en) { - u8 all = (BIT(wled->cfg.num_strings) - 1) - << PM8941_WLED_REG_SINK_SHFT; - - rc = regmap_update_bits(wled->regmap, - wled->addr + PM8941_WLED_REG_SINK, - PM8941_WLED_REG_SINK_MASK, all); - if (rc) - return rc; - } - - for (i = 0; i < wled->cfg.num_strings; ++i) { - u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i; - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_EN_BASE, - PM8941_WLED_REG_STR_MOD_MASK, - PM8941_WLED_REG_STR_MOD_EN); - if (rc) - return rc; - - if (wled->cfg.ext_gen) { - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_MOD_SRC_BASE, - PM8941_WLED_REG_STR_MOD_SRC_MASK, - PM8941_WLED_REG_STR_MOD_SRC_EXT); - if (rc) - return rc; - } - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_SCALE_BASE, - PM8941_WLED_REG_STR_SCALE_MASK, - wled->cfg.i_limit); - if (rc) - return rc; - - rc = regmap_update_bits(wled->regmap, - addr + PM8941_WLED_REG_STR_CABC_BASE, - PM8941_WLED_REG_STR_CABC_MASK, - wled->cfg.cabc_en ? - PM8941_WLED_REG_STR_CABC_EN : 0); - if (rc) - return rc; - } - - return 0; -} - -static const struct pm8941_wled_config pm8941_wled_config_defaults = { - .i_boost_limit = 3, - .i_limit = 20, - .ovp = 2, - .switch_freq = 5, - .num_strings = 0, - .cs_out_en = false, - .ext_gen = false, - .cabc_en = false, -}; - -struct pm8941_wled_var_cfg { - const u32 *values; - u32 (*fn)(u32); - int size; -}; - -static const u32 pm8941_wled_i_boost_limit_values[] = { - 105, 385, 525, 805, 980, 1260, 1400, 1680, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = { - .values = pm8941_wled_i_boost_limit_values, - .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values), -}; - -static const u32 pm8941_wled_ovp_values[] = { - 35, 32, 29, 27, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = { - .values = pm8941_wled_ovp_values, - .size = ARRAY_SIZE(pm8941_wled_ovp_values), -}; - -static u32 pm8941_wled_num_strings_values_fn(u32 idx) -{ - return idx + 1; -} - -static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = { - .fn = pm8941_wled_num_strings_values_fn, - .size = 3, -}; - -static u32 pm8941_wled_switch_freq_values_fn(u32 idx) -{ - return 19200 / (2 * (1 + idx)); -} - -static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = { - .fn = pm8941_wled_switch_freq_values_fn, - .size = 16, -}; - -static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = { - .size = 26, -}; - -static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) -{ - if (idx >= cfg->size) - return UINT_MAX; - if (cfg->fn) - return cfg->fn(idx); - if (cfg->values) - return cfg->values[idx]; - return idx; -} - -static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) -{ - struct pm8941_wled_config *cfg = &wled->cfg; - u32 val; - int rc; - u32 c; - int i; - int j; - - const struct { - const char *name; - u32 *val_ptr; - const struct pm8941_wled_var_cfg *cfg; - } u32_opts[] = { - { - "qcom,current-boost-limit", - &cfg->i_boost_limit, - .cfg = &pm8941_wled_i_boost_limit_cfg, - }, - { - "qcom,current-limit", - &cfg->i_limit, - .cfg = &pm8941_wled_i_limit_cfg, - }, - { - "qcom,ovp", - &cfg->ovp, - .cfg = &pm8941_wled_ovp_cfg, - }, - { - "qcom,switching-freq", - &cfg->switch_freq, - .cfg = &pm8941_wled_switch_freq_cfg, - }, - { - "qcom,num-strings", - &cfg->num_strings, - .cfg = &pm8941_wled_num_strings_cfg, - }, - }; - const struct { - const char *name; - bool *val_ptr; - } bool_opts[] = { - { "qcom,cs-out", &cfg->cs_out_en, }, - { "qcom,ext-gen", &cfg->ext_gen, }, - { "qcom,cabc", &cfg->cabc_en, }, - }; - - rc = of_property_read_u32(dev->of_node, "reg", &val); - if (rc || val > 0xffff) { - dev_err(dev, "invalid IO resources\n"); - return rc ? rc : -EINVAL; - } - wled->addr = val; - - rc = of_property_read_string(dev->of_node, "label", &wled->name); - if (rc) - wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - - *cfg = pm8941_wled_config_defaults; - for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { - rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); - if (rc == -EINVAL) { - continue; - } else if (rc) { - dev_err(dev, "error reading '%s'\n", u32_opts[i].name); - return rc; - } - - c = UINT_MAX; - for (j = 0; c != val; j++) { - c = pm8941_wled_values(u32_opts[i].cfg, j); - if (c == UINT_MAX) { - dev_err(dev, "invalid value for '%s'\n", - u32_opts[i].name); - return -EINVAL; - } - } - - dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); - *u32_opts[i].val_ptr = j; - } - - for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { - if (of_property_read_bool(dev->of_node, bool_opts[i].name)) - *bool_opts[i].val_ptr = true; - } - - cfg->num_strings = cfg->num_strings + 1; - - return 0; -} - -static const struct backlight_ops pm8941_wled_ops = { - .update_status = pm8941_wled_update_status, -}; - -static int pm8941_wled_probe(struct platform_device *pdev) -{ - struct backlight_properties props; - struct backlight_device *bl; - struct pm8941_wled *wled; - struct regmap *regmap; - u32 val; - int rc; - - regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!regmap) { - dev_err(&pdev->dev, "Unable to get regmap\n"); - return -EINVAL; - } - - wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); - if (!wled) - return -ENOMEM; - - wled->regmap = regmap; - - rc = pm8941_wled_configure(wled, &pdev->dev); - if (rc) - return rc; - - rc = pm8941_wled_setup(wled); - if (rc) - return rc; - - val = PM8941_WLED_DEFAULT_BRIGHTNESS; - of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_RAW; - props.brightness = val; - props.max_brightness = PM8941_WLED_REG_VAL_MAX; - bl = devm_backlight_device_register(&pdev->dev, wled->name, - &pdev->dev, wled, - &pm8941_wled_ops, &props); - return PTR_ERR_OR_ZERO(bl); -}; - -static const struct of_device_id pm8941_wled_match_table[] = { - { .compatible = "qcom,pm8941-wled" }, - {} -}; -MODULE_DEVICE_TABLE(of, pm8941_wled_match_table); - -static struct platform_driver pm8941_wled_driver = { - .probe = pm8941_wled_probe, - .driver = { - .name = "pm8941-wled", - .of_match_table = pm8941_wled_match_table, - }, -}; - -module_platform_driver(pm8941_wled_driver); - -MODULE_DESCRIPTION("pm8941 wled driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 746eebc411df..efb4efc2a13d 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -125,8 +125,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl) state.duty_cycle = compute_duty_cycle(pb, brightness); pwm_apply_state(pb->pwm, &state); pwm_backlight_power_on(pb); - } else + } else { pwm_backlight_power_off(pb); + } if (pb->notify_after) pb->notify_after(pb->dev, brightness); @@ -148,15 +149,16 @@ static const struct backlight_ops pwm_backlight_ops = { }; #ifdef CONFIG_OF -#define PWM_LUMINANCE_SCALE 10000 /* luminance scale */ +#define PWM_LUMINANCE_SHIFT 16 +#define PWM_LUMINANCE_SCALE (1 << PWM_LUMINANCE_SHIFT) /* luminance scale */ /* * CIE lightness to PWM conversion. * * The CIE 1931 lightness formula is what actually describes how we perceive * light: - * Y = (L* / 902.3) if L* ≤ 0.08856 - * Y = ((L* + 16) / 116)^3 if L* > 0.08856 + * Y = (L* / 903.3) if L* ≤ 8 + * Y = ((L* + 16) / 116)^3 if L* > 8 * * Where Y is the luminance, the amount of light coming out of the screen, and * is a number between 0.0 and 1.0; and L* is the lightness, how bright a human @@ -165,16 +167,25 @@ static const struct backlight_ops pwm_backlight_ops = { * The following function does the fixed point maths needed to implement the * above formula. */ -static u64 cie1931(unsigned int lightness, unsigned int scale) +static u64 cie1931(unsigned int lightness) { u64 retval; + /* + * @lightness is given as a number between 0 and 1, expressed + * as a fixed-point number in scale + * PWM_LUMINANCE_SCALE. Convert to a percentage, still + * expressed as a fixed-point number, so the above formulas + * can be applied. + */ lightness *= 100; - if (lightness <= (8 * scale)) { - retval = DIV_ROUND_CLOSEST_ULL(lightness * 10, 9023); + if (lightness <= (8 * PWM_LUMINANCE_SCALE)) { + retval = DIV_ROUND_CLOSEST(lightness * 10, 9033); } else { - retval = int_pow((lightness + (16 * scale)) / 116, 3); - retval = DIV_ROUND_CLOSEST_ULL(retval, (scale * scale)); + retval = (lightness + (16 * PWM_LUMINANCE_SCALE)) / 116; + retval *= retval * retval; + retval += 1ULL << (2*PWM_LUMINANCE_SHIFT - 1); + retval >>= 2*PWM_LUMINANCE_SHIFT; } return retval; @@ -208,8 +219,7 @@ int pwm_backlight_brightness_default(struct device *dev, /* Fill the table using the cie1931 algorithm */ for (i = 0; i < data->max_brightness; i++) { retval = cie1931((i * PWM_LUMINANCE_SCALE) / - data->max_brightness, PWM_LUMINANCE_SCALE) * - period; + data->max_brightness) * period; retval = DIV_ROUND_CLOSEST_ULL(retval, PWM_LUMINANCE_SCALE); if (retval > UINT_MAX) return -EINVAL; @@ -564,18 +574,17 @@ static int pwm_backlight_probe(struct platform_device *pdev) memset(&props, 0, sizeof(struct backlight_properties)); if (data->levels) { + pb->levels = data->levels; + /* * For the DT case, only when brightness levels is defined * data->levels is filled. For the non-DT case, data->levels * can come from platform data, however is not usual. */ - for (i = 0; i <= data->max_brightness; i++) { + for (i = 0; i <= data->max_brightness; i++) if (data->levels[i] > pb->scale) pb->scale = data->levels[i]; - pb->levels = data->levels; - } - if (pwm_backlight_is_linear(data)) props.scale = BACKLIGHT_SCALE_LINEAR; else diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c new file mode 100644 index 000000000000..d46052d8ff41 --- /dev/null +++ b/drivers/video/backlight/qcom-wled.c @@ -0,0 +1,1296 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015, Sony Mobile Communications, AB. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/ktime.h> +#include <linux/kernel.h> +#include <linux/backlight.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/regmap.h> + +/* From DT binding */ +#define WLED_MAX_STRINGS 4 + +#define WLED_DEFAULT_BRIGHTNESS 2048 +#define WLED_SOFT_START_DLY_US 10000 +#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF + +/* WLED3/WLED4 control registers */ +#define WLED3_CTRL_REG_FAULT_STATUS 0x08 +#define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0) +#define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1) +#define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2) + +#define WLED3_CTRL_REG_INT_RT_STS 0x10 +#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1) + +#define WLED3_CTRL_REG_MOD_EN 0x46 +#define WLED3_CTRL_REG_MOD_EN_MASK BIT(7) +#define WLED3_CTRL_REG_MOD_EN_SHIFT 7 + +#define WLED3_CTRL_REG_FEEDBACK_CONTROL 0x48 + +#define WLED3_CTRL_REG_FREQ 0x4c +#define WLED3_CTRL_REG_FREQ_MASK GENMASK(3, 0) + +#define WLED3_CTRL_REG_OVP 0x4d +#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0) + +#define WLED3_CTRL_REG_ILIMIT 0x4e +#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0) + +/* WLED3/WLED4 sink registers */ +#define WLED3_SINK_REG_SYNC 0x47 +#define WLED3_SINK_REG_SYNC_CLEAR 0x00 + +#define WLED3_SINK_REG_CURR_SINK 0x4f +#define WLED3_SINK_REG_CURR_SINK_MASK GENMASK(7, 5) +#define WLED3_SINK_REG_CURR_SINK_SHFT 5 + +/* WLED3 specific per-'string' registers below */ +#define WLED3_SINK_REG_BRIGHT(n) (0x40 + n) + +#define WLED3_SINK_REG_STR_MOD_EN(n) (0x60 + (n * 0x10)) +#define WLED3_SINK_REG_STR_MOD_MASK BIT(7) + +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR(n) (0x62 + (n * 0x10)) +#define WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(4, 0) + +#define WLED3_SINK_REG_STR_MOD_SRC(n) (0x63 + (n * 0x10)) +#define WLED3_SINK_REG_STR_MOD_SRC_MASK BIT(0) +#define WLED3_SINK_REG_STR_MOD_SRC_INT 0x00 +#define WLED3_SINK_REG_STR_MOD_SRC_EXT 0x01 + +#define WLED3_SINK_REG_STR_CABC(n) (0x66 + (n * 0x10)) +#define WLED3_SINK_REG_STR_CABC_MASK BIT(7) + +/* WLED4 specific control registers */ +#define WLED4_CTRL_REG_SHORT_PROTECT 0x5e +#define WLED4_CTRL_REG_SHORT_EN_MASK BIT(7) + +#define WLED4_CTRL_REG_SEC_ACCESS 0xd0 +#define WLED4_CTRL_REG_SEC_UNLOCK 0xa5 + +#define WLED4_CTRL_REG_TEST1 0xe2 +#define WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2 0x09 + +/* WLED4 specific sink registers */ +#define WLED4_SINK_REG_CURR_SINK 0x46 +#define WLED4_SINK_REG_CURR_SINK_MASK GENMASK(7, 4) +#define WLED4_SINK_REG_CURR_SINK_SHFT 4 + +/* WLED4 specific per-'string' registers below */ +#define WLED4_SINK_REG_STR_MOD_EN(n) (0x50 + (n * 0x10)) +#define WLED4_SINK_REG_STR_MOD_MASK BIT(7) + +#define WLED4_SINK_REG_STR_FULL_SCALE_CURR(n) (0x52 + (n * 0x10)) +#define WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK GENMASK(3, 0) + +#define WLED4_SINK_REG_STR_MOD_SRC(n) (0x53 + (n * 0x10)) +#define WLED4_SINK_REG_STR_MOD_SRC_MASK BIT(0) +#define WLED4_SINK_REG_STR_MOD_SRC_INT 0x00 +#define WLED4_SINK_REG_STR_MOD_SRC_EXT 0x01 + +#define WLED4_SINK_REG_STR_CABC(n) (0x56 + (n * 0x10)) +#define WLED4_SINK_REG_STR_CABC_MASK BIT(7) + +#define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10)) + +struct wled_var_cfg { + const u32 *values; + u32 (*fn)(u32); + int size; +}; + +struct wled_u32_opts { + const char *name; + u32 *val_ptr; + const struct wled_var_cfg *cfg; +}; + +struct wled_bool_opts { + const char *name; + bool *val_ptr; +}; + +struct wled_config { + u32 boost_i_limit; + u32 ovp; + u32 switch_freq; + u32 num_strings; + u32 string_i_limit; + u32 enabled_strings[WLED_MAX_STRINGS]; + bool cs_out_en; + bool ext_gen; + bool cabc; + bool external_pfet; + bool auto_detection_enabled; +}; + +struct wled { + const char *name; + struct device *dev; + struct regmap *regmap; + struct mutex lock; /* Lock to avoid race from thread irq handler */ + ktime_t last_short_event; + ktime_t start_ovp_fault_time; + u16 ctrl_addr; + u16 sink_addr; + u16 max_string_count; + u16 auto_detection_ovp_count; + u32 brightness; + u32 max_brightness; + u32 short_count; + u32 auto_detect_count; + bool disabled_by_short; + bool has_short_detect; + int short_irq; + int ovp_irq; + + struct wled_config cfg; + struct delayed_work ovp_work; + int (*wled_set_brightness)(struct wled *wled, u16 brightness); +}; + +static int wled3_set_brightness(struct wled *wled, u16 brightness) +{ + int rc, i; + u8 v[2]; + + v[0] = brightness & 0xff; + v[1] = (brightness >> 8) & 0xf; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + rc = regmap_bulk_write(wled->regmap, wled->ctrl_addr + + WLED3_SINK_REG_BRIGHT(i), v, 2); + if (rc < 0) + return rc; + } + + return 0; +} + +static int wled4_set_brightness(struct wled *wled, u16 brightness) +{ + int rc, i; + u16 low_limit = wled->max_brightness * 4 / 1000; + u8 v[2]; + + /* WLED4's lower limit of operation is 0.4% */ + if (brightness > 0 && brightness < low_limit) + brightness = low_limit; + + v[0] = brightness & 0xff; + v[1] = (brightness >> 8) & 0xf; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + rc = regmap_bulk_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_BRIGHT(i), v, 2); + if (rc < 0) + return rc; + } + + return 0; +} + +static void wled_ovp_work(struct work_struct *work) +{ + struct wled *wled = container_of(work, + struct wled, ovp_work.work); + enable_irq(wled->ovp_irq); +} + +static int wled_module_enable(struct wled *wled, int val) +{ + int rc; + + if (wled->disabled_by_short) + return -ENXIO; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + val << WLED3_CTRL_REG_MOD_EN_SHIFT); + if (rc < 0) + return rc; + + if (wled->ovp_irq > 0) { + if (val) { + /* + * The hardware generates a storm of spurious OVP + * interrupts during soft start operations. So defer + * enabling the IRQ for 10ms to ensure that the + * soft start is complete. + */ + schedule_delayed_work(&wled->ovp_work, HZ / 100); + } else { + if (!cancel_delayed_work_sync(&wled->ovp_work)) + disable_irq(wled->ovp_irq); + } + } + + return 0; +} + +static int wled_sync_toggle(struct wled *wled) +{ + int rc; + unsigned int mask = GENMASK(wled->max_string_count - 1, 0); + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_SYNC, + mask, mask); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_SYNC, + mask, WLED3_SINK_REG_SYNC_CLEAR); + + return rc; +} + +static int wled_update_status(struct backlight_device *bl) +{ + struct wled *wled = bl_get_data(bl); + u16 brightness = bl->props.brightness; + int rc = 0; + + if (bl->props.power != FB_BLANK_UNBLANK || + bl->props.fb_blank != FB_BLANK_UNBLANK || + bl->props.state & BL_CORE_FBBLANK) + brightness = 0; + + mutex_lock(&wled->lock); + if (brightness) { + rc = wled->wled_set_brightness(wled, brightness); + if (rc < 0) { + dev_err(wled->dev, "wled failed to set brightness rc:%d\n", + rc); + goto unlock_mutex; + } + + rc = wled_sync_toggle(wled); + if (rc < 0) { + dev_err(wled->dev, "wled sync failed rc:%d\n", rc); + goto unlock_mutex; + } + } + + if (!!brightness != !!wled->brightness) { + rc = wled_module_enable(wled, !!brightness); + if (rc < 0) { + dev_err(wled->dev, "wled enable failed rc:%d\n", rc); + goto unlock_mutex; + } + } + + wled->brightness = brightness; + +unlock_mutex: + mutex_unlock(&wled->lock); + + return rc; +} + +#define WLED_SHORT_DLY_MS 20 +#define WLED_SHORT_CNT_MAX 5 +#define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC + +static irqreturn_t wled_short_irq_handler(int irq, void *_wled) +{ + struct wled *wled = _wled; + int rc; + s64 elapsed_time; + + wled->short_count++; + mutex_lock(&wled->lock); + rc = wled_module_enable(wled, false); + if (rc < 0) { + dev_err(wled->dev, "wled disable failed rc:%d\n", rc); + goto unlock_mutex; + } + + elapsed_time = ktime_us_delta(ktime_get(), + wled->last_short_event); + if (elapsed_time > WLED_SHORT_RESET_CNT_DLY_US) + wled->short_count = 1; + + if (wled->short_count > WLED_SHORT_CNT_MAX) { + dev_err(wled->dev, "Short triggered %d times, disabling WLED forever!\n", + wled->short_count); + wled->disabled_by_short = true; + goto unlock_mutex; + } + + wled->last_short_event = ktime_get(); + + msleep(WLED_SHORT_DLY_MS); + rc = wled_module_enable(wled, true); + if (rc < 0) + dev_err(wled->dev, "wled enable failed rc:%d\n", rc); + +unlock_mutex: + mutex_unlock(&wled->lock); + + return IRQ_HANDLED; +} + +#define AUTO_DETECT_BRIGHTNESS 200 + +static void wled_auto_string_detection(struct wled *wled) +{ + int rc = 0, i; + u32 sink_config = 0, int_sts; + u8 sink_test = 0, sink_valid = 0, val; + + /* Read configured sink configuration */ + rc = regmap_read(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, &sink_config); + if (rc < 0) { + dev_err(wled->dev, "Failed to read SINK configuration rc=%d\n", + rc); + goto failed_detect; + } + + /* Disable the module before starting detection */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", rc); + goto failed_detect; + } + + /* Set low brightness across all sinks */ + rc = wled4_set_brightness(wled, AUTO_DETECT_BRIGHTNESS); + if (rc < 0) { + dev_err(wled->dev, "Failed to set brightness for auto detection rc=%d\n", + rc); + goto failed_detect; + } + + if (wled->cfg.cabc) { + for (i = 0; i < wled->cfg.num_strings; i++) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + 0); + if (rc < 0) + goto failed_detect; + } + } + + /* Disable all sinks */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable all sinks rc=%d\n", rc); + goto failed_detect; + } + + /* Iterate through the strings one by one */ + for (i = 0; i < wled->cfg.num_strings; i++) { + sink_test = BIT((WLED4_SINK_REG_CURR_SINK_SHFT + i)); + + /* Enable feedback control */ + rc = regmap_write(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_FEEDBACK_CONTROL, i + 1); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable feedback for SINK %d rc = %d\n", + i + 1, rc); + goto failed_detect; + } + + /* Enable the sink */ + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, sink_test); + if (rc < 0) { + dev_err(wled->dev, "Failed to configure SINK %d rc=%d\n", + i + 1, rc); + goto failed_detect; + } + + /* Enable the module */ + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", + rc); + goto failed_detect; + } + + usleep_range(WLED_SOFT_START_DLY_US, + WLED_SOFT_START_DLY_US + 1000); + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_INT_RT_STS, &int_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED3_CTRL_INT_RT_STS rc=%d\n", + rc); + goto failed_detect; + } + + if (int_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS) + dev_dbg(wled->dev, "WLED OVP fault detected with SINK %d\n", + i + 1); + else + sink_valid |= sink_test; + + /* Disable the module */ + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to disable WLED module rc=%d\n", + rc); + goto failed_detect; + } + } + + if (!sink_valid) { + dev_err(wled->dev, "No valid WLED sinks found\n"); + wled->disabled_by_short = true; + goto failed_detect; + } + + if (sink_valid != sink_config) { + dev_warn(wled->dev, "%x is not a valid sink configuration - using %x instead\n", + sink_config, sink_valid); + sink_config = sink_valid; + } + + /* Write the new sink configuration */ + rc = regmap_write(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + sink_config); + if (rc < 0) { + dev_err(wled->dev, "Failed to reconfigure the default sink rc=%d\n", + rc); + goto failed_detect; + } + + /* Enable valid sinks */ + for (i = 0; i < wled->cfg.num_strings; i++) { + if (wled->cfg.cabc) { + rc = regmap_update_bits(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_CABC(i), + WLED4_SINK_REG_STR_CABC_MASK, + WLED4_SINK_REG_STR_CABC_MASK); + if (rc < 0) + goto failed_detect; + } + + if (sink_config & BIT(WLED4_SINK_REG_CURR_SINK_SHFT + i)) + val = WLED4_SINK_REG_STR_MOD_MASK; + else + val = 0x0; /* Disable modulator_en for unused sink */ + + rc = regmap_write(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_STR_MOD_EN(i), val); + if (rc < 0) { + dev_err(wled->dev, "Failed to configure MODULATOR_EN rc=%d\n", + rc); + goto failed_detect; + } + } + + /* Restore the feedback setting */ + rc = regmap_write(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FEEDBACK_CONTROL, 0); + if (rc < 0) { + dev_err(wled->dev, "Failed to restore feedback setting rc=%d\n", + rc); + goto failed_detect; + } + + /* Restore brightness */ + rc = wled4_set_brightness(wled, wled->brightness); + if (rc < 0) { + dev_err(wled->dev, "Failed to set brightness after auto detection rc=%d\n", + rc); + goto failed_detect; + } + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) { + dev_err(wled->dev, "Failed to enable WLED module rc=%d\n", rc); + goto failed_detect; + } + +failed_detect: + return; +} + +#define WLED_AUTO_DETECT_OVP_COUNT 5 +#define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC +static bool wled_auto_detection_required(struct wled *wled) +{ + s64 elapsed_time_us; + + if (!wled->cfg.auto_detection_enabled) + return false; + + /* + * Check if the OVP fault was an occasional one + * or if it's firing continuously, the latter qualifies + * for an auto-detection check. + */ + if (!wled->auto_detection_ovp_count) { + wled->start_ovp_fault_time = ktime_get(); + wled->auto_detection_ovp_count++; + } else { + elapsed_time_us = ktime_us_delta(ktime_get(), + wled->start_ovp_fault_time); + if (elapsed_time_us > WLED_AUTO_DETECT_CNT_DLY_US) + wled->auto_detection_ovp_count = 0; + else + wled->auto_detection_ovp_count++; + + if (wled->auto_detection_ovp_count >= + WLED_AUTO_DETECT_OVP_COUNT) { + wled->auto_detection_ovp_count = 0; + return true; + } + } + + return false; +} + +static int wled_auto_detection_at_init(struct wled *wled) +{ + int rc; + u32 fault_status, rt_status; + + if (!wled->cfg.auto_detection_enabled) + return 0; + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, + &rt_status); + if (rc < 0) { + dev_err(wled->dev, "Failed to read RT status rc=%d\n", rc); + return rc; + } + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS, + &fault_status); + if (rc < 0) { + dev_err(wled->dev, "Failed to read fault status rc=%d\n", rc); + return rc; + } + + if ((rt_status & WLED3_CTRL_REG_OVP_FAULT_STATUS) || + (fault_status & WLED3_CTRL_REG_OVP_FAULT_BIT)) { + mutex_lock(&wled->lock); + wled_auto_string_detection(wled); + mutex_unlock(&wled->lock); + } + + return rc; +} + +static irqreturn_t wled_ovp_irq_handler(int irq, void *_wled) +{ + struct wled *wled = _wled; + int rc; + u32 int_sts, fault_sts; + + rc = regmap_read(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS, &int_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED3_INT_RT_STS rc=%d\n", + rc); + return IRQ_HANDLED; + } + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_FAULT_STATUS, &fault_sts); + if (rc < 0) { + dev_err(wled->dev, "Error in reading WLED_FAULT_STATUS rc=%d\n", + rc); + return IRQ_HANDLED; + } + + if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT | + WLED3_CTRL_REG_ILIM_FAULT_BIT)) + dev_dbg(wled->dev, "WLED OVP fault detected, int_sts=%x fault_sts= %x\n", + int_sts, fault_sts); + + if (fault_sts & WLED3_CTRL_REG_OVP_FAULT_BIT) { + if (wled_auto_detection_required(wled)) { + mutex_lock(&wled->lock); + wled_auto_string_detection(wled); + mutex_unlock(&wled->lock); + } + } + + return IRQ_HANDLED; +} + +static int wled3_setup(struct wled *wled) +{ + u16 addr; + u8 sink_en = 0; + int rc, i, j; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_OVP, + WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, + WLED3_CTRL_REG_ILIMIT_MASK, + wled->cfg.boost_i_limit); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FREQ, + WLED3_CTRL_REG_FREQ_MASK, + wled->cfg.switch_freq); + if (rc) + return rc; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + j = wled->cfg.enabled_strings[i]; + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_EN(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_MOD_MASK, + WLED3_SINK_REG_STR_MOD_MASK); + if (rc) + return rc; + + if (wled->cfg.ext_gen) { + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_MOD_SRC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_MOD_SRC_MASK, + WLED3_SINK_REG_STR_MOD_SRC_EXT); + if (rc) + return rc; + } + + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_FULL_SCALE_CURR(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_FULL_SCALE_CURR_MASK, + wled->cfg.string_i_limit); + if (rc) + return rc; + + addr = wled->ctrl_addr + WLED3_SINK_REG_STR_CABC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED3_SINK_REG_STR_CABC_MASK, + wled->cfg.cabc ? + WLED3_SINK_REG_STR_CABC_MASK : 0); + if (rc) + return rc; + + sink_en |= BIT(j + WLED3_SINK_REG_CURR_SINK_SHFT); + } + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_SINK_REG_CURR_SINK, + WLED3_SINK_REG_CURR_SINK_MASK, sink_en); + if (rc) + return rc; + + return 0; +} + +static const struct wled_config wled3_config_defaults = { + .boost_i_limit = 3, + .string_i_limit = 20, + .ovp = 2, + .num_strings = 3, + .switch_freq = 5, + .cs_out_en = false, + .ext_gen = false, + .cabc = false, + .enabled_strings = {0, 1, 2, 3}, +}; + +static int wled4_setup(struct wled *wled) +{ + int rc, temp, i, j; + u16 addr; + u8 sink_en = 0; + u32 sink_cfg; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_OVP, + WLED3_CTRL_REG_OVP_MASK, wled->cfg.ovp); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT, + WLED3_CTRL_REG_ILIMIT_MASK, + wled->cfg.boost_i_limit); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->ctrl_addr + WLED3_CTRL_REG_FREQ, + WLED3_CTRL_REG_FREQ_MASK, + wled->cfg.switch_freq); + if (rc < 0) + return rc; + + if (wled->cfg.external_pfet) { + /* Unlock the secure register access */ + rc = regmap_write(wled->regmap, wled->ctrl_addr + + WLED4_CTRL_REG_SEC_ACCESS, + WLED4_CTRL_REG_SEC_UNLOCK); + if (rc < 0) + return rc; + + rc = regmap_write(wled->regmap, + wled->ctrl_addr + WLED4_CTRL_REG_TEST1, + WLED4_CTRL_REG_TEST1_EXT_FET_DTEST2); + if (rc < 0) + return rc; + } + + rc = regmap_read(wled->regmap, wled->sink_addr + + WLED4_SINK_REG_CURR_SINK, &sink_cfg); + if (rc < 0) + return rc; + + for (i = 0; i < wled->cfg.num_strings; i++) { + j = wled->cfg.enabled_strings[i]; + temp = j + WLED4_SINK_REG_CURR_SINK_SHFT; + sink_en |= 1 << temp; + } + + if (sink_cfg == sink_en) { + rc = wled_auto_detection_at_init(wled); + return rc; + } + + rc = regmap_update_bits(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + WLED4_SINK_REG_CURR_SINK_MASK, 0); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, 0); + if (rc < 0) + return rc; + + /* Per sink/string configuration */ + for (i = 0; i < wled->cfg.num_strings; i++) { + j = wled->cfg.enabled_strings[i]; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_MOD_EN(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_MOD_MASK, + WLED4_SINK_REG_STR_MOD_MASK); + if (rc < 0) + return rc; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_FULL_SCALE_CURR(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK, + wled->cfg.string_i_limit); + if (rc < 0) + return rc; + + addr = wled->sink_addr + + WLED4_SINK_REG_STR_CABC(j); + rc = regmap_update_bits(wled->regmap, addr, + WLED4_SINK_REG_STR_CABC_MASK, + wled->cfg.cabc ? + WLED4_SINK_REG_STR_CABC_MASK : 0); + if (rc < 0) + return rc; + } + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, + WLED3_CTRL_REG_MOD_EN_MASK, + WLED3_CTRL_REG_MOD_EN_MASK); + if (rc < 0) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->sink_addr + WLED4_SINK_REG_CURR_SINK, + WLED4_SINK_REG_CURR_SINK_MASK, sink_en); + if (rc < 0) + return rc; + + rc = wled_sync_toggle(wled); + if (rc < 0) { + dev_err(wled->dev, "Failed to toggle sync reg rc:%d\n", rc); + return rc; + } + + rc = wled_auto_detection_at_init(wled); + + return rc; +} + +static const struct wled_config wled4_config_defaults = { + .boost_i_limit = 4, + .string_i_limit = 10, + .ovp = 1, + .num_strings = 4, + .switch_freq = 11, + .cabc = false, + .external_pfet = false, + .auto_detection_enabled = false, +}; + +static const u32 wled3_boost_i_limit_values[] = { + 105, 385, 525, 805, 980, 1260, 1400, 1680, +}; + +static const struct wled_var_cfg wled3_boost_i_limit_cfg = { + .values = wled3_boost_i_limit_values, + .size = ARRAY_SIZE(wled3_boost_i_limit_values), +}; + +static const u32 wled4_boost_i_limit_values[] = { + 105, 280, 450, 620, 970, 1150, 1300, 1500, +}; + +static const struct wled_var_cfg wled4_boost_i_limit_cfg = { + .values = wled4_boost_i_limit_values, + .size = ARRAY_SIZE(wled4_boost_i_limit_values), +}; + +static const u32 wled3_ovp_values[] = { + 35, 32, 29, 27, +}; + +static const struct wled_var_cfg wled3_ovp_cfg = { + .values = wled3_ovp_values, + .size = ARRAY_SIZE(wled3_ovp_values), +}; + +static const u32 wled4_ovp_values[] = { + 31100, 29600, 19600, 18100, +}; + +static const struct wled_var_cfg wled4_ovp_cfg = { + .values = wled4_ovp_values, + .size = ARRAY_SIZE(wled4_ovp_values), +}; + +static u32 wled3_num_strings_values_fn(u32 idx) +{ + return idx + 1; +} + +static const struct wled_var_cfg wled3_num_strings_cfg = { + .fn = wled3_num_strings_values_fn, + .size = 3, +}; + +static const struct wled_var_cfg wled4_num_strings_cfg = { + .fn = wled3_num_strings_values_fn, + .size = 4, +}; + +static u32 wled3_switch_freq_values_fn(u32 idx) +{ + return 19200 / (2 * (1 + idx)); +} + +static const struct wled_var_cfg wled3_switch_freq_cfg = { + .fn = wled3_switch_freq_values_fn, + .size = 16, +}; + +static const struct wled_var_cfg wled3_string_i_limit_cfg = { + .size = 26, +}; + +static const u32 wled4_string_i_limit_values[] = { + 0, 2500, 5000, 7500, 10000, 12500, 15000, 17500, 20000, + 22500, 25000, 27500, 30000, +}; + +static const struct wled_var_cfg wled4_string_i_limit_cfg = { + .values = wled4_string_i_limit_values, + .size = ARRAY_SIZE(wled4_string_i_limit_values), +}; + +static const struct wled_var_cfg wled3_string_cfg = { + .size = 8, +}; + +static const struct wled_var_cfg wled4_string_cfg = { + .size = 16, +}; + +static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx) +{ + if (idx >= cfg->size) + return UINT_MAX; + if (cfg->fn) + return cfg->fn(idx); + if (cfg->values) + return cfg->values[idx]; + return idx; +} + +static int wled_configure(struct wled *wled, int version) +{ + struct wled_config *cfg = &wled->cfg; + struct device *dev = wled->dev; + const __be32 *prop_addr; + u32 size, val, c, string_len; + int rc, i, j; + + const struct wled_u32_opts *u32_opts = NULL; + const struct wled_u32_opts wled3_opts[] = { + { + .name = "qcom,current-boost-limit", + .val_ptr = &cfg->boost_i_limit, + .cfg = &wled3_boost_i_limit_cfg, + }, + { + .name = "qcom,current-limit", + .val_ptr = &cfg->string_i_limit, + .cfg = &wled3_string_i_limit_cfg, + }, + { + .name = "qcom,ovp", + .val_ptr = &cfg->ovp, + .cfg = &wled3_ovp_cfg, + }, + { + .name = "qcom,switching-freq", + .val_ptr = &cfg->switch_freq, + .cfg = &wled3_switch_freq_cfg, + }, + { + .name = "qcom,num-strings", + .val_ptr = &cfg->num_strings, + .cfg = &wled3_num_strings_cfg, + }, + }; + + const struct wled_u32_opts wled4_opts[] = { + { + .name = "qcom,current-boost-limit", + .val_ptr = &cfg->boost_i_limit, + .cfg = &wled4_boost_i_limit_cfg, + }, + { + .name = "qcom,current-limit-microamp", + .val_ptr = &cfg->string_i_limit, + .cfg = &wled4_string_i_limit_cfg, + }, + { + .name = "qcom,ovp-millivolt", + .val_ptr = &cfg->ovp, + .cfg = &wled4_ovp_cfg, + }, + { + .name = "qcom,switching-freq", + .val_ptr = &cfg->switch_freq, + .cfg = &wled3_switch_freq_cfg, + }, + { + .name = "qcom,num-strings", + .val_ptr = &cfg->num_strings, + .cfg = &wled4_num_strings_cfg, + }, + }; + + const struct wled_bool_opts bool_opts[] = { + { "qcom,cs-out", &cfg->cs_out_en, }, + { "qcom,ext-gen", &cfg->ext_gen, }, + { "qcom,cabc", &cfg->cabc, }, + { "qcom,external-pfet", &cfg->external_pfet, }, + { "qcom,auto-string-detection", &cfg->auto_detection_enabled, }, + }; + + prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); + if (!prop_addr) { + dev_err(wled->dev, "invalid IO resources\n"); + return -EINVAL; + } + wled->ctrl_addr = be32_to_cpu(*prop_addr); + + rc = of_property_read_string(dev->of_node, "label", &wled->name); + if (rc) + wled->name = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + + switch (version) { + case 3: + u32_opts = wled3_opts; + size = ARRAY_SIZE(wled3_opts); + *cfg = wled3_config_defaults; + wled->wled_set_brightness = wled3_set_brightness; + wled->max_string_count = 3; + wled->sink_addr = wled->ctrl_addr; + break; + + case 4: + u32_opts = wled4_opts; + size = ARRAY_SIZE(wled4_opts); + *cfg = wled4_config_defaults; + wled->wled_set_brightness = wled4_set_brightness; + wled->max_string_count = 4; + + prop_addr = of_get_address(dev->of_node, 1, NULL, NULL); + if (!prop_addr) { + dev_err(wled->dev, "invalid IO resources\n"); + return -EINVAL; + } + wled->sink_addr = be32_to_cpu(*prop_addr); + break; + + default: + dev_err(wled->dev, "Invalid WLED version\n"); + return -EINVAL; + } + + for (i = 0; i < size; ++i) { + rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); + if (rc == -EINVAL) { + continue; + } else if (rc) { + dev_err(dev, "error reading '%s'\n", u32_opts[i].name); + return rc; + } + + c = UINT_MAX; + for (j = 0; c != val; j++) { + c = wled_values(u32_opts[i].cfg, j); + if (c == UINT_MAX) { + dev_err(dev, "invalid value for '%s'\n", + u32_opts[i].name); + return -EINVAL; + } + + if (c == val) + break; + } + + dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); + *u32_opts[i].val_ptr = j; + } + + for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { + if (of_property_read_bool(dev->of_node, bool_opts[i].name)) + *bool_opts[i].val_ptr = true; + } + + cfg->num_strings = cfg->num_strings + 1; + + string_len = of_property_count_elems_of_size(dev->of_node, + "qcom,enabled-strings", + sizeof(u32)); + if (string_len > 0) + of_property_read_u32_array(dev->of_node, + "qcom,enabled-strings", + wled->cfg.enabled_strings, + sizeof(u32)); + + return 0; +} + +static int wled_configure_short_irq(struct wled *wled, + struct platform_device *pdev) +{ + int rc; + + if (!wled->has_short_detect) + return 0; + + rc = regmap_update_bits(wled->regmap, wled->ctrl_addr + + WLED4_CTRL_REG_SHORT_PROTECT, + WLED4_CTRL_REG_SHORT_EN_MASK, + WLED4_CTRL_REG_SHORT_EN_MASK); + if (rc < 0) + return rc; + + wled->short_irq = platform_get_irq_byname(pdev, "short"); + if (wled->short_irq < 0) { + dev_dbg(&pdev->dev, "short irq is not used\n"); + return 0; + } + + rc = devm_request_threaded_irq(wled->dev, wled->short_irq, + NULL, wled_short_irq_handler, + IRQF_ONESHOT, + "wled_short_irq", wled); + if (rc < 0) + dev_err(wled->dev, "Unable to request short_irq (err:%d)\n", + rc); + + return rc; +} + +static int wled_configure_ovp_irq(struct wled *wled, + struct platform_device *pdev) +{ + int rc; + u32 val; + + wled->ovp_irq = platform_get_irq_byname(pdev, "ovp"); + if (wled->ovp_irq < 0) { + dev_dbg(&pdev->dev, "OVP IRQ not found - disabling automatic string detection\n"); + return 0; + } + + rc = devm_request_threaded_irq(wled->dev, wled->ovp_irq, NULL, + wled_ovp_irq_handler, IRQF_ONESHOT, + "wled_ovp_irq", wled); + if (rc < 0) { + dev_err(wled->dev, "Unable to request ovp_irq (err:%d)\n", + rc); + wled->ovp_irq = 0; + return 0; + } + + rc = regmap_read(wled->regmap, wled->ctrl_addr + + WLED3_CTRL_REG_MOD_EN, &val); + if (rc < 0) + return rc; + + /* Keep OVP irq disabled until module is enabled */ + if (!(val & WLED3_CTRL_REG_MOD_EN_MASK)) + disable_irq(wled->ovp_irq); + + return 0; +} + +static const struct backlight_ops wled_ops = { + .update_status = wled_update_status, +}; + +static int wled_probe(struct platform_device *pdev) +{ + struct backlight_properties props; + struct backlight_device *bl; + struct wled *wled; + struct regmap *regmap; + int version; + u32 val; + int rc; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "Unable to get regmap\n"); + return -EINVAL; + } + + wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); + if (!wled) + return -ENOMEM; + + wled->regmap = regmap; + wled->dev = &pdev->dev; + + version = (uintptr_t)of_device_get_match_data(&pdev->dev); + if (!version) { + dev_err(&pdev->dev, "Unknown device version\n"); + return -ENODEV; + } + + mutex_init(&wled->lock); + rc = wled_configure(wled, version); + if (rc) + return rc; + + switch (version) { + case 3: + wled->cfg.auto_detection_enabled = false; + rc = wled3_setup(wled); + if (rc) { + dev_err(&pdev->dev, "wled3_setup failed\n"); + return rc; + } + break; + + case 4: + wled->has_short_detect = true; + rc = wled4_setup(wled); + if (rc) { + dev_err(&pdev->dev, "wled4_setup failed\n"); + return rc; + } + break; + + default: + dev_err(wled->dev, "Invalid WLED version\n"); + break; + } + + INIT_DELAYED_WORK(&wled->ovp_work, wled_ovp_work); + + rc = wled_configure_short_irq(wled, pdev); + if (rc < 0) + return rc; + + rc = wled_configure_ovp_irq(wled, pdev); + if (rc < 0) + return rc; + + val = WLED_DEFAULT_BRIGHTNESS; + of_property_read_u32(pdev->dev.of_node, "default-brightness", &val); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_RAW; + props.brightness = val; + props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX; + bl = devm_backlight_device_register(&pdev->dev, wled->name, + &pdev->dev, wled, + &wled_ops, &props); + return PTR_ERR_OR_ZERO(bl); +}; + +static int wled_remove(struct platform_device *pdev) +{ + struct wled *wled = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&wled->lock); + cancel_delayed_work_sync(&wled->ovp_work); + disable_irq(wled->short_irq); + disable_irq(wled->ovp_irq); + + return 0; +} + +static const struct of_device_id wled_match_table[] = { + { .compatible = "qcom,pm8941-wled", .data = (void *)3 }, + { .compatible = "qcom,pmi8998-wled", .data = (void *)4 }, + { .compatible = "qcom,pm660l-wled", .data = (void *)4 }, + {} +}; +MODULE_DEVICE_TABLE(of, wled_match_table); + +static struct platform_driver wled_driver = { + .probe = wled_probe, + .remove = wled_remove, + .driver = { + .name = "qcom,wled", + .of_match_table = wled_match_table, + }, +}; + +module_platform_driver(wled_driver); + +MODULE_DESCRIPTION("Qualcomm WLED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c index 1275e815bd86..cff5e96fd988 100644 --- a/drivers/video/backlight/tosa_bl.c +++ b/drivers/video/backlight/tosa_bl.c @@ -18,7 +18,7 @@ #include <asm/mach/sharpsl_param.h> -#include <mach/tosa.h> +#include "tosa_bl.h" #define COMADJ_DEFAULT 97 @@ -28,6 +28,7 @@ struct tosa_bl_data { struct i2c_client *i2c; struct backlight_device *bl; + struct gpio_desc *gpio; int comadj; }; @@ -42,7 +43,7 @@ static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness) i2c_smbus_write_byte_data(data->i2c, DAC_CH2, (u8)(brightness & 0xff)); /* SetBacklightVR */ - gpio_set_value(TOSA_GPIO_BL_C20MA, brightness & 0x100); + gpiod_set_value(data->gpio, brightness & 0x100); tosa_bl_enable(spi, brightness); } @@ -87,9 +88,8 @@ static int tosa_bl_probe(struct i2c_client *client, return -ENOMEM; data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; - - ret = devm_gpio_request_one(&client->dev, TOSA_GPIO_BL_C20MA, - GPIOF_OUT_INIT_LOW, "backlight"); + data->gpio = devm_gpiod_get(&client->dev, "backlight", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(data->gpio); if (ret) { dev_dbg(&data->bl->dev, "Unable to request gpio!\n"); return ret; diff --git a/drivers/video/backlight/tosa_bl.h b/drivers/video/backlight/tosa_bl.h new file mode 100644 index 000000000000..589e17e6fdb2 --- /dev/null +++ b/drivers/video/backlight/tosa_bl.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _TOSA_BL_H +#define _TOSA_BL_H + +struct spi_device; +extern int tosa_bl_enable(struct spi_device *spi, int enable); + +#endif diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c index 29af8e27b6e5..e8ab583e5098 100644 --- a/drivers/video/backlight/tosa_lcd.c +++ b/drivers/video/backlight/tosa_lcd.c @@ -19,7 +19,7 @@ #include <asm/mach/sharpsl_param.h> -#include <mach/tosa.h> +#include "tosa_bl.h" #define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) @@ -28,12 +28,26 @@ #define TG_REG0_UD 0x0004 #define TG_REG0_LR 0x0008 +/* + * Timing Generator + */ +#define TG_PNLCTL 0x00 +#define TG_TPOSCTL 0x01 +#define TG_DUTYCTL 0x02 +#define TG_GPOSR 0x03 +#define TG_GPODR1 0x04 +#define TG_GPODR2 0x05 +#define TG_PINICTL 0x06 +#define TG_HPOSCTL 0x07 + + #define DAC_BASE 0x4e struct tosa_lcd_data { struct spi_device *spi; struct lcd_device *lcd; struct i2c_client *i2c; + struct gpio_desc *gpiod_tg; int lcd_power; bool is_vga; @@ -66,7 +80,7 @@ EXPORT_SYMBOL(tosa_bl_enable); static void tosa_lcd_tg_init(struct tosa_lcd_data *data) { /* TG on */ - gpio_set_value(TOSA_GPIO_TG_ON, 0); + gpiod_set_value(data->gpiod_tg, 0); mdelay(60); @@ -100,6 +114,7 @@ static void tosa_lcd_tg_on(struct tosa_lcd_data *data) */ struct i2c_adapter *adap = i2c_get_adapter(0); struct i2c_board_info info = { + .dev_name = "tosa-bl", .type = "tosa-bl", .addr = DAC_BASE, .platform_data = data->spi, @@ -121,7 +136,7 @@ static void tosa_lcd_tg_off(struct tosa_lcd_data *data) mdelay(50); /* TG Off */ - gpio_set_value(TOSA_GPIO_TG_ON, 1); + gpiod_set_value(data->gpiod_tg, 1); mdelay(100); } @@ -191,10 +206,9 @@ static int tosa_lcd_probe(struct spi_device *spi) data->spi = spi; spi_set_drvdata(spi, data); - ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON, - GPIOF_OUT_INIT_LOW, "tg #pwr"); - if (ret < 0) - return ret; + data->gpiod_tg = devm_gpiod_get(&spi->dev, "tg #pwr", GPIOD_OUT_LOW); + if (IS_ERR(data->gpiod_tg)) + return PTR_ERR(data->gpiod_tg); mdelay(60); diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 1e70e838530e..aa9541bf964b 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2214,6 +2214,7 @@ config FB_HYPERV select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_DEFERRED_IO help This framebuffer driver supports Microsoft Hyper-V Synthetic Video. diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 6dda5d885a03..79d548746efd 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -48,7 +48,7 @@ ******************************************************************************/ - +#include <linux/compat.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -235,6 +235,13 @@ static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); static int atyfb_blank(int blank, struct fb_info *info); static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg); +#ifdef CONFIG_COMPAT +static int atyfb_compat_ioctl(struct fb_info *info, u_int cmd, u_long arg) +{ + return atyfb_ioctl(info, cmd, (u_long)compat_ptr(arg)); +} +#endif + #ifdef __sparc__ static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma); #endif @@ -290,6 +297,9 @@ static struct fb_ops atyfb_ops = { .fb_pan_display = atyfb_pan_display, .fb_blank = atyfb_blank, .fb_ioctl = atyfb_ioctl, +#ifdef CONFIG_COMPAT + .fb_compat_ioctl = atyfb_compat_ioctl, +#endif .fb_fillrect = atyfb_fillrect, .fb_copyarea = atyfb_copyarea, .fb_imageblit = atyfb_imageblit, diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 2dcb7c58b31e..4cd27e5172a1 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -23,6 +23,14 @@ * * Portrait orientation is also supported: * For example: video=hyperv_fb:864x1152 + * + * When a Windows 10 RS5+ host is used, the virtual machine screen + * resolution is obtained from the host. The "video=hyperv_fb" option is + * not needed, but still can be used to overwrite what the host specifies. + * The VM resolution on the host could be set by executing the powershell + * "set-vmvideo" command. For example + * set-vmvideo -vmname name -horizontalresolution:1920 \ + * -verticalresolution:1200 -resolutiontype single */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -34,6 +42,7 @@ #include <linux/fb.h> #include <linux/pci.h> #include <linux/efi.h> +#include <linux/console.h> #include <linux/hyperv.h> @@ -44,6 +53,10 @@ #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major)) #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0) #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2) +#define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5) + +#define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff) +#define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16) #define SYNTHVID_DEPTH_WIN7 16 #define SYNTHVID_DEPTH_WIN8 32 @@ -82,16 +95,25 @@ enum synthvid_msg_type { SYNTHVID_POINTER_SHAPE = 8, SYNTHVID_FEATURE_CHANGE = 9, SYNTHVID_DIRT = 10, + SYNTHVID_RESOLUTION_REQUEST = 13, + SYNTHVID_RESOLUTION_RESPONSE = 14, - SYNTHVID_MAX = 11 + SYNTHVID_MAX = 15 }; +#define SYNTHVID_EDID_BLOCK_SIZE 128 +#define SYNTHVID_MAX_RESOLUTION_COUNT 64 + +struct hvd_screen_info { + u16 width; + u16 height; +} __packed; + struct synthvid_msg_hdr { u32 type; u32 size; /* size of this header + payload after this field*/ } __packed; - struct synthvid_version_req { u32 version; } __packed; @@ -102,6 +124,19 @@ struct synthvid_version_resp { u8 max_video_outputs; } __packed; +struct synthvid_supported_resolution_req { + u8 maximum_resolution_count; +} __packed; + +struct synthvid_supported_resolution_resp { + u8 edid_block[SYNTHVID_EDID_BLOCK_SIZE]; + u8 resolution_count; + u8 default_resolution_index; + u8 is_standard; + struct hvd_screen_info + supported_resolution[SYNTHVID_MAX_RESOLUTION_COUNT]; +} __packed; + struct synthvid_vram_location { u64 user_ctx; u8 is_vram_gpa_specified; @@ -187,6 +222,8 @@ struct synthvid_msg { struct synthvid_pointer_shape ptr_shape; struct synthvid_feature_change feature_chg; struct synthvid_dirt dirt; + struct synthvid_supported_resolution_req resolution_req; + struct synthvid_supported_resolution_resp resolution_resp; }; } __packed; @@ -201,6 +238,7 @@ struct synthvid_msg { #define RING_BUFSIZE (256 * 1024) #define VSP_TIMEOUT (10 * HZ) #define HVFB_UPDATE_DELAY (HZ / 20) +#define HVFB_ONDEMAND_THROTTLE (HZ / 20) struct hvfb_par { struct fb_info *info; @@ -211,6 +249,7 @@ struct hvfb_par { struct delayed_work dwork; bool update; + bool update_saved; /* The value of 'update' before hibernation */ u32 pseudo_palette[16]; u8 init_buf[MAX_VMBUS_PKT_SIZE]; @@ -220,12 +259,25 @@ struct hvfb_par { bool synchronous_fb; struct notifier_block hvfb_panic_nb; + + /* Memory for deferred IO and frame buffer itself */ + unsigned char *dio_vp; + unsigned char *mmio_vp; + unsigned long mmio_pp; + + /* Dirty rectangle, protected by delayed_refresh_lock */ + int x1, y1, x2, y2; + bool delayed_refresh; + spinlock_t delayed_refresh_lock; }; static uint screen_width = HVFB_WIDTH; static uint screen_height = HVFB_HEIGHT; +static uint screen_width_max = HVFB_WIDTH; +static uint screen_height_max = HVFB_HEIGHT; static uint screen_depth; static uint screen_fb_size; +static uint dio_fb_size; /* FB size for deferred IO */ /* Send message to Hyper-V host */ static inline int synthvid_send(struct hv_device *hdev, @@ -312,28 +364,88 @@ static int synthvid_send_ptr(struct hv_device *hdev) } /* Send updated screen area (dirty rectangle) location to host */ -static int synthvid_update(struct fb_info *info) +static int +synthvid_update(struct fb_info *info, int x1, int y1, int x2, int y2) { struct hv_device *hdev = device_to_hv_device(info->device); struct synthvid_msg msg; memset(&msg, 0, sizeof(struct synthvid_msg)); + if (x2 == INT_MAX) + x2 = info->var.xres; + if (y2 == INT_MAX) + y2 = info->var.yres; msg.vid_hdr.type = SYNTHVID_DIRT; msg.vid_hdr.size = sizeof(struct synthvid_msg_hdr) + sizeof(struct synthvid_dirt); msg.dirt.video_output = 0; msg.dirt.dirt_count = 1; - msg.dirt.rect[0].x1 = 0; - msg.dirt.rect[0].y1 = 0; - msg.dirt.rect[0].x2 = info->var.xres; - msg.dirt.rect[0].y2 = info->var.yres; + msg.dirt.rect[0].x1 = (x1 > x2) ? 0 : x1; + msg.dirt.rect[0].y1 = (y1 > y2) ? 0 : y1; + msg.dirt.rect[0].x2 = + (x2 < x1 || x2 > info->var.xres) ? info->var.xres : x2; + msg.dirt.rect[0].y2 = + (y2 < y1 || y2 > info->var.yres) ? info->var.yres : y2; synthvid_send(hdev, &msg); return 0; } +static void hvfb_docopy(struct hvfb_par *par, + unsigned long offset, + unsigned long size) +{ + if (!par || !par->mmio_vp || !par->dio_vp || !par->fb_ready || + size == 0 || offset >= dio_fb_size) + return; + + if (offset + size > dio_fb_size) + size = dio_fb_size - offset; + + memcpy(par->mmio_vp + offset, par->dio_vp + offset, size); +} + +/* Deferred IO callback */ +static void synthvid_deferred_io(struct fb_info *p, + struct list_head *pagelist) +{ + struct hvfb_par *par = p->par; + struct page *page; + unsigned long start, end; + int y1, y2, miny, maxy; + + miny = INT_MAX; + maxy = 0; + + /* + * Merge dirty pages. It is possible that last page cross + * over the end of frame buffer row yres. This is taken care of + * in synthvid_update function by clamping the y2 + * value to yres. + */ + list_for_each_entry(page, pagelist, lru) { + start = page->index << PAGE_SHIFT; + end = start + PAGE_SIZE - 1; + y1 = start / p->fix.line_length; + y2 = end / p->fix.line_length; + miny = min_t(int, miny, y1); + maxy = max_t(int, maxy, y2); + + /* Copy from dio space to mmio address */ + if (par->fb_ready) + hvfb_docopy(par, start, PAGE_SIZE); + } + + if (par->fb_ready && par->update) + synthvid_update(p, 0, miny, p->var.xres, maxy + 1); +} + +static struct fb_deferred_io synthvid_defio = { + .delay = HZ / 20, + .deferred_io = synthvid_deferred_io, +}; /* * Actions on received messages from host: @@ -354,6 +466,7 @@ static void synthvid_recv_sub(struct hv_device *hdev) /* Complete the wait event */ if (msg->vid_hdr.type == SYNTHVID_VERSION_RESPONSE || + msg->vid_hdr.type == SYNTHVID_RESOLUTION_RESPONSE || msg->vid_hdr.type == SYNTHVID_VRAM_LOCATION_ACK) { memcpy(par->init_buf, msg, MAX_VMBUS_PKT_SIZE); complete(&par->wait); @@ -400,6 +513,17 @@ static void synthvid_receive(void *ctx) } while (bytes_recvd > 0 && ret == 0); } +/* Check if the ver1 version is equal or greater than ver2 */ +static inline bool synthvid_ver_ge(u32 ver1, u32 ver2) +{ + if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || + (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && + SYNTHVID_VER_GET_MINOR(ver1) >= SYNTHVID_VER_GET_MINOR(ver2))) + return true; + + return false; +} + /* Check synthetic video protocol version with the host */ static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver) { @@ -428,6 +552,64 @@ static int synthvid_negotiate_ver(struct hv_device *hdev, u32 ver) } par->synthvid_version = ver; + pr_info("Synthvid Version major %d, minor %d\n", + SYNTHVID_VER_GET_MAJOR(ver), SYNTHVID_VER_GET_MINOR(ver)); + +out: + return ret; +} + +/* Get current resolution from the host */ +static int synthvid_get_supported_resolution(struct hv_device *hdev) +{ + struct fb_info *info = hv_get_drvdata(hdev); + struct hvfb_par *par = info->par; + struct synthvid_msg *msg = (struct synthvid_msg *)par->init_buf; + int ret = 0; + unsigned long t; + u8 index; + int i; + + memset(msg, 0, sizeof(struct synthvid_msg)); + msg->vid_hdr.type = SYNTHVID_RESOLUTION_REQUEST; + msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + + sizeof(struct synthvid_supported_resolution_req); + + msg->resolution_req.maximum_resolution_count = + SYNTHVID_MAX_RESOLUTION_COUNT; + synthvid_send(hdev, msg); + + t = wait_for_completion_timeout(&par->wait, VSP_TIMEOUT); + if (!t) { + pr_err("Time out on waiting resolution response\n"); + ret = -ETIMEDOUT; + goto out; + } + + if (msg->resolution_resp.resolution_count == 0) { + pr_err("No supported resolutions\n"); + ret = -ENODEV; + goto out; + } + + index = msg->resolution_resp.default_resolution_index; + if (index >= msg->resolution_resp.resolution_count) { + pr_err("Invalid resolution index: %d\n", index); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < msg->resolution_resp.resolution_count; i++) { + screen_width_max = max_t(unsigned int, screen_width_max, + msg->resolution_resp.supported_resolution[i].width); + screen_height_max = max_t(unsigned int, screen_height_max, + msg->resolution_resp.supported_resolution[i].height); + } + + screen_width = + msg->resolution_resp.supported_resolution[index].width; + screen_height = + msg->resolution_resp.supported_resolution[index].height; out: return ret; @@ -448,11 +630,27 @@ static int synthvid_connect_vsp(struct hv_device *hdev) } /* Negotiate the protocol version with host */ - if (vmbus_proto_version == VERSION_WS2008 || - vmbus_proto_version == VERSION_WIN7) - ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7); - else + switch (vmbus_proto_version) { + case VERSION_WIN10: + case VERSION_WIN10_V5: + ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); + if (!ret) + break; + /* Fallthrough */ + case VERSION_WIN8: + case VERSION_WIN8_1: ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8); + if (!ret) + break; + /* Fallthrough */ + case VERSION_WS2008: + case VERSION_WIN7: + ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7); + break; + default: + ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10); + break; + } if (ret) { pr_err("Synthetic video device version not accepted\n"); @@ -464,6 +662,12 @@ static int synthvid_connect_vsp(struct hv_device *hdev) else screen_depth = SYNTHVID_DEPTH_WIN8; + if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) { + ret = synthvid_get_supported_resolution(hdev); + if (ret) + pr_info("Failed to get supported resolution from host, use default\n"); + } + screen_fb_size = hdev->channel->offermsg.offer. mmio_megabytes * 1024 * 1024; @@ -488,7 +692,7 @@ static int synthvid_send_config(struct hv_device *hdev) msg->vid_hdr.type = SYNTHVID_VRAM_LOCATION; msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + sizeof(struct synthvid_vram_location); - msg->vram.user_ctx = msg->vram.vram_gpa = info->fix.smem_start; + msg->vram.user_ctx = msg->vram.vram_gpa = par->mmio_pp; msg->vram.is_vram_gpa_specified = 1; synthvid_send(hdev, msg); @@ -498,7 +702,7 @@ static int synthvid_send_config(struct hv_device *hdev) ret = -ETIMEDOUT; goto out; } - if (msg->vram_ack.user_ctx != info->fix.smem_start) { + if (msg->vram_ack.user_ctx != par->mmio_pp) { pr_err("Unable to set VRAM location\n"); ret = -ENODEV; goto out; @@ -515,19 +719,77 @@ out: /* * Delayed work callback: - * It is called at HVFB_UPDATE_DELAY or longer time interval to process - * screen updates. It is re-scheduled if further update is necessary. + * It is scheduled to call whenever update request is received and it has + * not been called in last HVFB_ONDEMAND_THROTTLE time interval. */ static void hvfb_update_work(struct work_struct *w) { struct hvfb_par *par = container_of(w, struct hvfb_par, dwork.work); struct fb_info *info = par->info; + unsigned long flags; + int x1, x2, y1, y2; + int j; + + spin_lock_irqsave(&par->delayed_refresh_lock, flags); + /* Reset the request flag */ + par->delayed_refresh = false; + + /* Store the dirty rectangle to local variables */ + x1 = par->x1; + x2 = par->x2; + y1 = par->y1; + y2 = par->y2; + + /* Clear dirty rectangle */ + par->x1 = par->y1 = INT_MAX; + par->x2 = par->y2 = 0; + + spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); + + if (x1 > info->var.xres || x2 > info->var.xres || + y1 > info->var.yres || y2 > info->var.yres || x2 <= x1) + return; + + /* Copy the dirty rectangle to frame buffer memory */ + for (j = y1; j < y2; j++) { + hvfb_docopy(par, + j * info->fix.line_length + + (x1 * screen_depth / 8), + (x2 - x1) * screen_depth / 8); + } + + /* Refresh */ + if (par->fb_ready && par->update) + synthvid_update(info, x1, y1, x2, y2); +} - if (par->fb_ready) - synthvid_update(info); +/* + * Control the on-demand refresh frequency. It schedules a delayed + * screen update if it has not yet. + */ +static void hvfb_ondemand_refresh_throttle(struct hvfb_par *par, + int x1, int y1, int w, int h) +{ + unsigned long flags; + int x2 = x1 + w; + int y2 = y1 + h; + + spin_lock_irqsave(&par->delayed_refresh_lock, flags); + + /* Merge dirty rectangle */ + par->x1 = min_t(int, par->x1, x1); + par->y1 = min_t(int, par->y1, y1); + par->x2 = max_t(int, par->x2, x2); + par->y2 = max_t(int, par->y2, y2); + + /* Schedule a delayed screen update if not yet */ + if (par->delayed_refresh == false) { + schedule_delayed_work(&par->dwork, + HVFB_ONDEMAND_THROTTLE); + par->delayed_refresh = true; + } - if (par->update) - schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); + spin_unlock_irqrestore(&par->delayed_refresh_lock, flags); } static int hvfb_on_panic(struct notifier_block *nb, @@ -539,7 +801,8 @@ static int hvfb_on_panic(struct notifier_block *nb, par = container_of(nb, struct hvfb_par, hvfb_panic_nb); par->synchronous_fb = true; info = par->info; - synthvid_update(info); + hvfb_docopy(par, 0, dio_fb_size); + synthvid_update(info, 0, 0, INT_MAX, INT_MAX); return NOTIFY_DONE; } @@ -600,7 +863,10 @@ static void hvfb_cfb_fillrect(struct fb_info *p, cfb_fillrect(p, rect); if (par->synchronous_fb) - synthvid_update(p); + synthvid_update(p, 0, 0, INT_MAX, INT_MAX); + else + hvfb_ondemand_refresh_throttle(par, rect->dx, rect->dy, + rect->width, rect->height); } static void hvfb_cfb_copyarea(struct fb_info *p, @@ -610,7 +876,10 @@ static void hvfb_cfb_copyarea(struct fb_info *p, cfb_copyarea(p, area); if (par->synchronous_fb) - synthvid_update(p); + synthvid_update(p, 0, 0, INT_MAX, INT_MAX); + else + hvfb_ondemand_refresh_throttle(par, area->dx, area->dy, + area->width, area->height); } static void hvfb_cfb_imageblit(struct fb_info *p, @@ -620,7 +889,10 @@ static void hvfb_cfb_imageblit(struct fb_info *p, cfb_imageblit(p, image); if (par->synchronous_fb) - synthvid_update(p); + synthvid_update(p, 0, 0, INT_MAX, INT_MAX); + else + hvfb_ondemand_refresh_throttle(par, image->dx, image->dy, + image->width, image->height); } static struct fb_ops hvfb_ops = { @@ -653,6 +925,8 @@ static void hvfb_get_option(struct fb_info *info) } if (x < HVFB_WIDTH_MIN || y < HVFB_HEIGHT_MIN || + (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) && + (x > screen_width_max || y > screen_height_max)) || (par->synthvid_version == SYNTHVID_VERSION_WIN8 && x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) || (par->synthvid_version == SYNTHVID_VERSION_WIN7 && @@ -677,6 +951,9 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) resource_size_t pot_start, pot_end; int ret; + dio_fb_size = + screen_width * screen_height * screen_depth / 8; + if (gen2vm) { pot_start = 0; pot_end = -1; @@ -689,8 +966,12 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM) || - pci_resource_len(pdev, 0) < screen_fb_size) + pci_resource_len(pdev, 0) < screen_fb_size) { + pr_err("Resource not available or (0x%lx < 0x%lx)\n", + (unsigned long) pci_resource_len(pdev, 0), + (unsigned long) screen_fb_size); goto err1; + } pot_end = pci_resource_end(pdev, 0); pot_start = pot_end - screen_fb_size + 1; @@ -707,9 +988,14 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) if (!fb_virt) goto err2; + /* Allocate memory for deferred IO */ + par->dio_vp = vzalloc(round_up(dio_fb_size, PAGE_SIZE)); + if (par->dio_vp == NULL) + goto err3; + info->apertures = alloc_apertures(1); if (!info->apertures) - goto err3; + goto err4; if (gen2vm) { info->apertures->ranges[0].base = screen_info.lfb_base; @@ -721,16 +1007,23 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) info->apertures->ranges[0].size = pci_resource_len(pdev, 0); } + /* Physical address of FB device */ + par->mmio_pp = par->mem->start; + /* Virtual address of FB device */ + par->mmio_vp = (unsigned char *) fb_virt; + info->fix.smem_start = par->mem->start; - info->fix.smem_len = screen_fb_size; - info->screen_base = fb_virt; - info->screen_size = screen_fb_size; + info->fix.smem_len = dio_fb_size; + info->screen_base = par->dio_vp; + info->screen_size = dio_fb_size; if (!gen2vm) pci_dev_put(pdev); return 0; +err4: + vfree(par->dio_vp); err3: iounmap(fb_virt); err2: @@ -748,6 +1041,7 @@ static void hvfb_putmem(struct fb_info *info) { struct hvfb_par *par = info->par; + vfree(par->dio_vp); iounmap(info->screen_base); vmbus_free_mmio(par->mem->start, screen_fb_size); par->mem = NULL; @@ -771,6 +1065,11 @@ static int hvfb_probe(struct hv_device *hdev, init_completion(&par->wait); INIT_DELAYED_WORK(&par->dwork, hvfb_update_work); + par->delayed_refresh = false; + spin_lock_init(&par->delayed_refresh_lock); + par->x1 = par->y1 = INT_MAX; + par->x2 = par->y2 = 0; + /* Connect to VSP */ hv_set_drvdata(hdev, info); ret = synthvid_connect_vsp(hdev); @@ -779,17 +1078,16 @@ static int hvfb_probe(struct hv_device *hdev, goto error1; } + hvfb_get_option(info); + pr_info("Screen resolution: %dx%d, Color depth: %d\n", + screen_width, screen_height, screen_depth); + ret = hvfb_getmem(hdev, info); if (ret) { pr_err("No memory for framebuffer\n"); goto error2; } - hvfb_get_option(info); - pr_info("Screen resolution: %dx%d, Color depth: %d\n", - screen_width, screen_height, screen_depth); - - /* Set up fb_info */ info->flags = FBINFO_DEFAULT; @@ -823,6 +1121,10 @@ static int hvfb_probe(struct hv_device *hdev, info->fbops = &hvfb_ops; info->pseudo_palette = par->pseudo_palette; + /* Initialize deferred IO */ + info->fbdefio = &synthvid_defio; + fb_deferred_io_init(info); + /* Send config to host */ ret = synthvid_send_config(hdev); if (ret) @@ -844,6 +1146,7 @@ static int hvfb_probe(struct hv_device *hdev, return 0; error: + fb_deferred_io_cleanup(info); hvfb_putmem(info); error2: vmbus_close(hdev->channel); @@ -866,6 +1169,8 @@ static int hvfb_remove(struct hv_device *hdev) par->update = false; par->fb_ready = false; + fb_deferred_io_cleanup(info); + unregister_framebuffer(info); cancel_delayed_work_sync(&par->dwork); @@ -878,6 +1183,61 @@ static int hvfb_remove(struct hv_device *hdev) return 0; } +static int hvfb_suspend(struct hv_device *hdev) +{ + struct fb_info *info = hv_get_drvdata(hdev); + struct hvfb_par *par = info->par; + + console_lock(); + + /* 1 means do suspend */ + fb_set_suspend(info, 1); + + cancel_delayed_work_sync(&par->dwork); + + par->update_saved = par->update; + par->update = false; + par->fb_ready = false; + + vmbus_close(hdev->channel); + + console_unlock(); + + return 0; +} + +static int hvfb_resume(struct hv_device *hdev) +{ + struct fb_info *info = hv_get_drvdata(hdev); + struct hvfb_par *par = info->par; + int ret; + + console_lock(); + + ret = synthvid_connect_vsp(hdev); + if (ret != 0) + goto out; + + ret = synthvid_send_config(hdev); + if (ret != 0) { + vmbus_close(hdev->channel); + goto out; + } + + par->fb_ready = true; + par->update = par->update_saved; + + schedule_delayed_work(&par->dwork, HVFB_UPDATE_DELAY); + + /* 0 means do resume */ + fb_set_suspend(info, 0); + +out: + console_unlock(); + + return ret; +} + static const struct pci_device_id pci_stub_id_table[] = { { @@ -901,6 +1261,8 @@ static struct hv_driver hvfb_drv = { .id_table = id_table, .probe = hvfb_probe, .remove = hvfb_remove, + .suspend = hvfb_suspend, + .resume = hvfb_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, diff --git a/drivers/video/fbdev/matrox/i2c-matroxfb.c b/drivers/video/fbdev/matrox/i2c-matroxfb.c index 34e2659c3189..e2e4705e3fe0 100644 --- a/drivers/video/fbdev/matrox/i2c-matroxfb.c +++ b/drivers/video/fbdev/matrox/i2c-matroxfb.c @@ -191,8 +191,8 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) { 0x1b, I2C_CLIENT_END }; - i2c_new_probed_device(&m2info->maven.adapter, - &maven_info, addr_list, NULL); + i2c_new_scanned_device(&m2info->maven.adapter, + &maven_info, addr_list, NULL); } } return m2info; diff --git a/drivers/virt/fsl_hypervisor.c b/drivers/virt/fsl_hypervisor.c index 93d5bebf9572..1b0b11b55d2a 100644 --- a/drivers/virt/fsl_hypervisor.c +++ b/drivers/virt/fsl_hypervisor.c @@ -706,7 +706,7 @@ static const struct file_operations fsl_hv_fops = { .poll = fsl_hv_poll, .read = fsl_hv_read, .unlocked_ioctl = fsl_hv_ioctl, - .compat_ioctl = fsl_hv_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice fsl_hv_misc_dev = { diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 58e7c100b6ad..1679e0dc869b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1485,6 +1485,7 @@ config W83627HF_WDT NCT6791 NCT6792 NCT6102D/04D/06D + NCT6116D This watchdog simply watches your kernel to make sure it doesn't freeze, and if it does, it reboots your computer after a certain @@ -1641,8 +1642,10 @@ config INDYDOG config JZ4740_WDT tristate "Ingenic jz4740 SoC hardware watchdog" - depends on MACH_JZ4740 || MACH_JZ4780 + depends on MIPS + depends on COMMON_CLK select WATCHDOG_CORE + select MFD_SYSCON help Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs. diff --git a/drivers/watchdog/acquirewdt.c b/drivers/watchdog/acquirewdt.c index 848db958411e..bc6f333565d3 100644 --- a/drivers/watchdog/acquirewdt.c +++ b/drivers/watchdog/acquirewdt.c @@ -221,6 +221,7 @@ static const struct file_operations acq_fops = { .llseek = no_llseek, .write = acq_write, .unlocked_ioctl = acq_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = acq_open, .release = acq_close, }; diff --git a/drivers/watchdog/advantechwdt.c b/drivers/watchdog/advantechwdt.c index 0d02bb275b3d..0e4c18a2aa42 100644 --- a/drivers/watchdog/advantechwdt.c +++ b/drivers/watchdog/advantechwdt.c @@ -220,6 +220,7 @@ static const struct file_operations advwdt_fops = { .llseek = no_llseek, .write = advwdt_write, .unlocked_ioctl = advwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = advwdt_open, .release = advwdt_close, }; diff --git a/drivers/watchdog/alim1535_wdt.c b/drivers/watchdog/alim1535_wdt.c index c157dd3d92a3..42338c7d4540 100644 --- a/drivers/watchdog/alim1535_wdt.c +++ b/drivers/watchdog/alim1535_wdt.c @@ -362,6 +362,7 @@ static const struct file_operations ali_fops = { .llseek = no_llseek, .write = ali_write, .unlocked_ioctl = ali_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ali_open, .release = ali_release, }; diff --git a/drivers/watchdog/alim7101_wdt.c b/drivers/watchdog/alim7101_wdt.c index c8e3ab056767..5af0358f4390 100644 --- a/drivers/watchdog/alim7101_wdt.c +++ b/drivers/watchdog/alim7101_wdt.c @@ -294,6 +294,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c index 668a1c704f28..c087027ffd5d 100644 --- a/drivers/watchdog/ar7_wdt.c +++ b/drivers/watchdog/ar7_wdt.c @@ -250,6 +250,7 @@ static const struct file_operations ar7_wdt_fops = { .owner = THIS_MODULE, .write = ar7_wdt_write, .unlocked_ioctl = ar7_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ar7_wdt_open, .release = ar7_wdt_release, .llseek = no_llseek, diff --git a/drivers/watchdog/aspeed_wdt.c b/drivers/watchdog/aspeed_wdt.c index 4ec0906bf12c..7e00960651fa 100644 --- a/drivers/watchdog/aspeed_wdt.c +++ b/drivers/watchdog/aspeed_wdt.c @@ -258,11 +258,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->base)) return PTR_ERR(wdt->base); - /* - * The ast2400 wdt can run at PCLK, or 1MHz. The ast2500 only - * runs at 1MHz. We chose to always run at 1MHz, as there's no - * good reason to have a faster watchdog counter. - */ wdt->wdd.info = &aspeed_wdt_info; wdt->wdd.ops = &aspeed_wdt_ops; wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS; @@ -278,7 +273,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev) return -EINVAL; config = ofdid->data; - wdt->ctrl = WDT_CTRL_1MHZ_CLK; + /* + * On clock rates: + * - ast2400 wdt can run at PCLK, or 1MHz + * - ast2500 only runs at 1MHz, hard coding bit 4 to 1 + * - ast2600 always runs at 1MHz + * + * Set the ast2400 to run at 1MHz as it simplifies the driver. + */ + if (of_device_is_compatible(np, "aspeed,ast2400-wdt")) + wdt->ctrl = WDT_CTRL_1MHZ_CLK; /* * Control reset on a per-device basis to ensure the diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 907a4545dee6..6d751eb8191d 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -213,6 +213,7 @@ static const struct file_operations at91wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = at91_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = at91_wdt_open, .release = at91_wdt_close, .write = at91_wdt_write, diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h index 390941c65eee..abfe34dd760a 100644 --- a/drivers/watchdog/at91sam9_wdt.h +++ b/drivers/watchdog/at91sam9_wdt.h @@ -4,33 +4,37 @@ * * Copyright (C) 2007 Andrew Victor * Copyright (C) 2007 Atmel Corporation. + * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries * * Watchdog Timer (WDT) - System peripherals regsters. * Based on AT91SAM9261 datasheet revision D. + * Based on SAM9X60 datasheet. * */ #ifndef AT91_WDT_H #define AT91_WDT_H +#include <linux/bits.h> + #define AT91_WDT_CR 0x00 /* Watchdog Control Register */ -#define AT91_WDT_WDRSTT (1 << 0) /* Restart */ -#define AT91_WDT_KEY (0xa5 << 24) /* KEY Password */ +#define AT91_WDT_WDRSTT BIT(0) /* Restart */ +#define AT91_WDT_KEY (0xa5UL << 24) /* KEY Password */ #define AT91_WDT_MR 0x04 /* Watchdog Mode Register */ -#define AT91_WDT_WDV (0xfff << 0) /* Counter Value */ -#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) -#define AT91_WDT_WDFIEN (1 << 12) /* Fault Interrupt Enable */ -#define AT91_WDT_WDRSTEN (1 << 13) /* Reset Processor */ -#define AT91_WDT_WDRPROC (1 << 14) /* Timer Restart */ -#define AT91_WDT_WDDIS (1 << 15) /* Watchdog Disable */ -#define AT91_WDT_WDD (0xfff << 16) /* Delta Value */ -#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) -#define AT91_WDT_WDDBGHLT (1 << 28) /* Debug Halt */ -#define AT91_WDT_WDIDLEHLT (1 << 29) /* Idle Halt */ +#define AT91_WDT_WDV (0xfffUL << 0) /* Counter Value */ +#define AT91_WDT_SET_WDV(x) ((x) & AT91_WDT_WDV) +#define AT91_WDT_WDFIEN BIT(12) /* Fault Interrupt Enable */ +#define AT91_WDT_WDRSTEN BIT(13) /* Reset Processor */ +#define AT91_WDT_WDRPROC BIT(14) /* Timer Restart */ +#define AT91_WDT_WDDIS BIT(15) /* Watchdog Disable */ +#define AT91_WDT_WDD (0xfffUL << 16) /* Delta Value */ +#define AT91_WDT_SET_WDD(x) (((x) << 16) & AT91_WDT_WDD) +#define AT91_WDT_WDDBGHLT BIT(28) /* Debug Halt */ +#define AT91_WDT_WDIDLEHLT BIT(29) /* Idle Halt */ -#define AT91_WDT_SR 0x08 /* Watchdog Status Register */ -#define AT91_WDT_WDUNF (1 << 0) /* Watchdog Underflow */ -#define AT91_WDT_WDERR (1 << 1) /* Watchdog Error */ +#define AT91_WDT_SR 0x08 /* Watchdog Status Register */ +#define AT91_WDT_WDUNF BIT(0) /* Watchdog Underflow */ +#define AT91_WDT_WDERR BIT(1) /* Watchdog Error */ #endif diff --git a/drivers/watchdog/ath79_wdt.c b/drivers/watchdog/ath79_wdt.c index 75de664ef4b0..d6dff97c280b 100644 --- a/drivers/watchdog/ath79_wdt.c +++ b/drivers/watchdog/ath79_wdt.c @@ -234,6 +234,7 @@ static const struct file_operations ath79_wdt_fops = { .llseek = no_llseek, .write = ath79_wdt_write, .unlocked_ioctl = ath79_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ath79_wdt_open, .release = ath79_wdt_release, }; diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c index e2af37c9a266..8a043b52aa2f 100644 --- a/drivers/watchdog/bcm63xx_wdt.c +++ b/drivers/watchdog/bcm63xx_wdt.c @@ -221,6 +221,7 @@ static const struct file_operations bcm63xx_wdt_fops = { .llseek = no_llseek, .write = bcm63xx_wdt_write, .unlocked_ioctl = bcm63xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = bcm63xx_wdt_open, .release = bcm63xx_wdt_release, }; diff --git a/drivers/watchdog/bd70528_wdt.c b/drivers/watchdog/bd70528_wdt.c index bc60e036627a..0170b37e6674 100644 --- a/drivers/watchdog/bd70528_wdt.c +++ b/drivers/watchdog/bd70528_wdt.c @@ -97,7 +97,7 @@ EXPORT_SYMBOL(bd70528_wdt_set); /** * bd70528_wdt_lock - take WDT lock * - * @bd70528: device data for the PMIC instance we want to operate on + * @data: device data for the PMIC instance we want to operate on * * Lock WDT for arming/disarming in order to avoid race condition caused * by WDT state changes initiated by WDT and RTC drivers. @@ -114,7 +114,7 @@ EXPORT_SYMBOL(bd70528_wdt_lock); /** * bd70528_wdt_unlock - unlock WDT lock * - * @bd70528: device data for the PMIC instance we want to operate on + * @data: device data for the PMIC instance we want to operate on * * Unlock WDT lock which has previously been taken by call to * bd70528_wdt_lock. diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index f8d4e91d0383..06bd4e1a5923 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -335,8 +335,10 @@ static int cdns_wdt_probe(struct platform_device *pdev) wdt->clk = devm_clk_get(dev, NULL); if (IS_ERR(wdt->clk)) { - dev_err(dev, "input clock not found\n"); - return PTR_ERR(wdt->clk); + ret = PTR_ERR(wdt->clk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "input clock not found\n"); + return ret; } ret = clk_prepare_enable(wdt->clk); diff --git a/drivers/watchdog/cpu5wdt.c b/drivers/watchdog/cpu5wdt.c index d6d53014cb68..9867a3a936df 100644 --- a/drivers/watchdog/cpu5wdt.c +++ b/drivers/watchdog/cpu5wdt.c @@ -187,6 +187,7 @@ static const struct file_operations cpu5wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = cpu5wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = cpu5wdt_open, .write = cpu5wdt_write, .release = cpu5wdt_release, diff --git a/drivers/watchdog/eurotechwdt.c b/drivers/watchdog/eurotechwdt.c index 3a83a48abcae..f5ffa7be066e 100644 --- a/drivers/watchdog/eurotechwdt.c +++ b/drivers/watchdog/eurotechwdt.c @@ -371,6 +371,7 @@ static const struct file_operations eurwdt_fops = { .llseek = no_llseek, .write = eurwdt_write, .unlocked_ioctl = eurwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = eurwdt_open, .release = eurwdt_release, }; diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c index e46104c2fd94..a3c44d75d80e 100644 --- a/drivers/watchdog/f71808e_wdt.c +++ b/drivers/watchdog/f71808e_wdt.c @@ -669,6 +669,7 @@ static const struct file_operations watchdog_fops = { .release = watchdog_release, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice watchdog_miscdev = { diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 7d5f56994f09..f6541d1b65e3 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -248,6 +248,7 @@ static const struct file_operations gef_wdt_fops = { .llseek = no_llseek, .write = gef_wdt_write, .unlocked_ioctl = gef_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = gef_wdt_open, .release = gef_wdt_release, }; diff --git a/drivers/watchdog/geodewdt.c b/drivers/watchdog/geodewdt.c index 8d105d98908e..9914a4283cb2 100644 --- a/drivers/watchdog/geodewdt.c +++ b/drivers/watchdog/geodewdt.c @@ -201,6 +201,7 @@ static const struct file_operations geodewdt_fops = { .llseek = no_llseek, .write = geodewdt_write, .unlocked_ioctl = geodewdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = geodewdt_open, .release = geodewdt_release, }; diff --git a/drivers/watchdog/ib700wdt.c b/drivers/watchdog/ib700wdt.c index 92fd7f33bc4d..2b65ea9451d1 100644 --- a/drivers/watchdog/ib700wdt.c +++ b/drivers/watchdog/ib700wdt.c @@ -259,6 +259,7 @@ static const struct file_operations ibwdt_fops = { .llseek = no_llseek, .write = ibwdt_write, .unlocked_ioctl = ibwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ibwdt_open, .release = ibwdt_close, }; diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index 897f7eda9e6a..4a22fe152086 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -344,6 +344,7 @@ static const struct file_operations asr_fops = { .llseek = no_llseek, .write = asr_write, .unlocked_ioctl = asr_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = asr_open, .release = asr_release, }; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 8d019a961ccc..f8d58bf0bf66 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -72,7 +72,6 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); - static unsigned timeout; module_param(timeout, uint, 0); MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default=" @@ -247,13 +246,14 @@ static const struct regmap_config imx2_wdt_regmap_config = { static int __init imx2_wdt_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct imx2_wdt_device *wdev; struct watchdog_device *wdog; void __iomem *base; int ret; u32 val; - wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + wdev = devm_kzalloc(dev, sizeof(*wdev), GFP_KERNEL); if (!wdev) return -ENOMEM; @@ -261,16 +261,16 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - wdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + wdev->regmap = devm_regmap_init_mmio_clk(dev, NULL, base, &imx2_wdt_regmap_config); if (IS_ERR(wdev->regmap)) { - dev_err(&pdev->dev, "regmap init failed\n"); + dev_err(dev, "regmap init failed\n"); return PTR_ERR(wdev->regmap); } - wdev->clk = devm_clk_get(&pdev->dev, NULL); + wdev->clk = devm_clk_get(dev, NULL); if (IS_ERR(wdev->clk)) { - dev_err(&pdev->dev, "can't get Watchdog clock\n"); + dev_err(dev, "can't get Watchdog clock\n"); return PTR_ERR(wdev->clk); } @@ -280,12 +280,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) wdog->min_timeout = 1; wdog->timeout = IMX2_WDT_DEFAULT_TIME; wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000; - wdog->parent = &pdev->dev; + wdog->parent = dev; ret = platform_get_irq(pdev, 0); if (ret > 0) - if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0, - dev_name(&pdev->dev), wdog)) + if (!devm_request_irq(dev, ret, imx2_wdt_isr, 0, + dev_name(dev), wdog)) wdog->info = &imx2_wdt_pretimeout_info; ret = clk_prepare_enable(wdev->clk); @@ -295,13 +295,13 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) regmap_read(wdev->regmap, IMX2_WDT_WRSR, &val); wdog->bootstatus = val & IMX2_WDT_WRSR_TOUT ? WDIOF_CARDRESET : 0; - wdev->ext_reset = of_property_read_bool(pdev->dev.of_node, + wdev->ext_reset = of_property_read_bool(dev->of_node, "fsl,ext-reset-output"); platform_set_drvdata(pdev, wdog); watchdog_set_drvdata(wdog, wdev); watchdog_set_nowayout(wdog, nowayout); watchdog_set_restart_priority(wdog, 128); - watchdog_init_timeout(wdog, timeout, &pdev->dev); + watchdog_init_timeout(wdog, timeout, dev); if (imx2_wdt_is_running(wdev)) { imx2_wdt_set_timeout(wdog, wdog->timeout); @@ -319,7 +319,7 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) if (ret) goto disable_clk; - dev_info(&pdev->dev, "timeout %d sec (nowayout=%d)\n", + dev_info(dev, "timeout %d sec (nowayout=%d)\n", wdog->timeout, nowayout); return 0; @@ -359,9 +359,8 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) } } -#ifdef CONFIG_PM_SLEEP /* Disable watchdog if it is active or non-active but still running */ -static int imx2_wdt_suspend(struct device *dev) +static int __maybe_unused imx2_wdt_suspend(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -382,7 +381,7 @@ static int imx2_wdt_suspend(struct device *dev) } /* Enable watchdog and configure it if necessary */ -static int imx2_wdt_resume(struct device *dev) +static int __maybe_unused imx2_wdt_resume(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); @@ -407,7 +406,6 @@ static int imx2_wdt_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend, imx2_wdt_resume); diff --git a/drivers/watchdog/imx7ulp_wdt.c b/drivers/watchdog/imx7ulp_wdt.c index 5ce51026989a..0a87c6f4bab2 100644 --- a/drivers/watchdog/imx7ulp_wdt.c +++ b/drivers/watchdog/imx7ulp_wdt.c @@ -17,6 +17,9 @@ #define WDOG_CS_CMD32EN BIT(13) #define WDOG_CS_ULK BIT(11) #define WDOG_CS_RCS BIT(10) +#define LPO_CLK 0x1 +#define LPO_CLK_SHIFT 8 +#define WDOG_CS_CLK (LPO_CLK << LPO_CLK_SHIFT) #define WDOG_CS_EN BIT(7) #define WDOG_CS_UPDATE BIT(5) @@ -41,24 +44,25 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); struct imx7ulp_wdt_device { - struct notifier_block restart_handler; struct watchdog_device wdd; void __iomem *base; struct clk *clk; }; -static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable) +static void imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable) { - u32 val = readl(base + WDOG_CS); + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); - writel(UNLOCK, base + WDOG_CNT); + u32 val = readl(wdt->base + WDOG_CS); + + writel(UNLOCK, wdt->base + WDOG_CNT); if (enable) - writel(val | WDOG_CS_EN, base + WDOG_CS); + writel(val | WDOG_CS_EN, wdt->base + WDOG_CS); else - writel(val & ~WDOG_CS_EN, base + WDOG_CS); + writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS); } -static inline bool imx7ulp_wdt_is_enabled(void __iomem *base) +static bool imx7ulp_wdt_is_enabled(void __iomem *base) { u32 val = readl(base + WDOG_CS); @@ -76,18 +80,15 @@ static int imx7ulp_wdt_ping(struct watchdog_device *wdog) static int imx7ulp_wdt_start(struct watchdog_device *wdog) { - struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); - imx7ulp_wdt_enable(wdt->base, true); + imx7ulp_wdt_enable(wdog, true); return 0; } static int imx7ulp_wdt_stop(struct watchdog_device *wdog) { - struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); - - imx7ulp_wdt_enable(wdt->base, false); + imx7ulp_wdt_enable(wdog, false); return 0; } @@ -106,12 +107,28 @@ static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog, return 0; } +static int imx7ulp_wdt_restart(struct watchdog_device *wdog, + unsigned long action, void *data) +{ + struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog); + + imx7ulp_wdt_enable(wdt->base, true); + imx7ulp_wdt_set_timeout(&wdt->wdd, 1); + + /* wait for wdog to fire */ + while (true) + ; + + return NOTIFY_DONE; +} + static const struct watchdog_ops imx7ulp_wdt_ops = { .owner = THIS_MODULE, .start = imx7ulp_wdt_start, .stop = imx7ulp_wdt_stop, .ping = imx7ulp_wdt_ping, .set_timeout = imx7ulp_wdt_set_timeout, + .restart = imx7ulp_wdt_restart, }; static const struct watchdog_info imx7ulp_wdt_info = { @@ -120,7 +137,7 @@ static const struct watchdog_info imx7ulp_wdt_info = { WDIOF_MAGICCLOSE, }; -static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) +static void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) { u32 val; @@ -131,7 +148,7 @@ static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout) /* set an initial timeout value in TOVAL */ writel(timeout, base + WDOG_TOVAL); /* enable 32bit command sequence and reconfigure */ - val = BIT(13) | BIT(8) | BIT(5); + val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE; writel(val, base + WDOG_CS); } diff --git a/drivers/watchdog/indydog.c b/drivers/watchdog/indydog.c index 550358528084..9857bb74a723 100644 --- a/drivers/watchdog/indydog.c +++ b/drivers/watchdog/indydog.c @@ -152,6 +152,7 @@ static const struct file_operations indydog_fops = { .llseek = no_llseek, .write = indydog_write, .unlocked_ioctl = indydog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = indydog_open, .release = indydog_release, }; diff --git a/drivers/watchdog/intel-mid_wdt.c b/drivers/watchdog/intel-mid_wdt.c index 2cdbd37c700c..470213abfd3d 100644 --- a/drivers/watchdog/intel-mid_wdt.c +++ b/drivers/watchdog/intel-mid_wdt.c @@ -134,6 +134,7 @@ static int mid_wdt_probe(struct platform_device *pdev) wdt_dev->timeout = MID_WDT_DEFAULT_TIMEOUT; wdt_dev->parent = dev; + watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT); watchdog_set_drvdata(wdt_dev, dev); ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq, diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 1c85103b750b..6ad5bf3451ec 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -412,6 +412,7 @@ static const struct file_operations intel_scu_fops = { .llseek = no_llseek, .write = intel_scu_write, .unlocked_ioctl = intel_scu_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = intel_scu_open, .release = intel_scu_release, }; diff --git a/drivers/watchdog/iop_wdt.c b/drivers/watchdog/iop_wdt.c index a9ccdb9a9159..6bf68d4750de 100644 --- a/drivers/watchdog/iop_wdt.c +++ b/drivers/watchdog/iop_wdt.c @@ -202,6 +202,7 @@ static const struct file_operations iop_wdt_fops = { .llseek = no_llseek, .write = iop_wdt_write, .unlocked_ioctl = iop_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = iop_wdt_open, .release = iop_wdt_release, }; diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 2fe1a3c499ed..2fed40d14007 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -345,6 +345,7 @@ static const struct file_operations it8712f_wdt_fops = { .llseek = no_llseek, .write = it8712f_wdt_write, .unlocked_ioctl = it8712f_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = it8712f_wdt_open, .release = it8712f_wdt_release, }; diff --git a/drivers/watchdog/ixp4xx_wdt.c b/drivers/watchdog/ixp4xx_wdt.c index 9067998759e3..09886616fd21 100644 --- a/drivers/watchdog/ixp4xx_wdt.c +++ b/drivers/watchdog/ixp4xx_wdt.c @@ -163,6 +163,7 @@ static const struct file_operations ixp4xx_wdt_fops = { .llseek = no_llseek, .write = ixp4xx_wdt_write, .unlocked_ioctl = ixp4xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = ixp4xx_wdt_open, .release = ixp4xx_wdt_release, }; diff --git a/drivers/watchdog/jz4740_wdt.c b/drivers/watchdog/jz4740_wdt.c index c6052ae54f32..bdf9564efa29 100644 --- a/drivers/watchdog/jz4740_wdt.c +++ b/drivers/watchdog/jz4740_wdt.c @@ -5,6 +5,7 @@ */ #include <linux/mfd/ingenic-tcu.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> @@ -17,19 +18,7 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/of.h> - -#include <asm/mach-jz4740/timer.h> - -#define JZ_WDT_CLOCK_PCLK 0x1 -#define JZ_WDT_CLOCK_RTC 0x2 -#define JZ_WDT_CLOCK_EXT 0x4 - -#define JZ_WDT_CLOCK_DIV_1 (0 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_4 (1 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_16 (2 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_64 (3 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_256 (4 << TCU_TCSR_PRESCALE_LSB) -#define JZ_WDT_CLOCK_DIV_1024 (5 << TCU_TCSR_PRESCALE_LSB) +#include <linux/regmap.h> #define DEFAULT_HEARTBEAT 5 #define MAX_HEARTBEAT 2048 @@ -49,15 +38,17 @@ MODULE_PARM_DESC(heartbeat, struct jz4740_wdt_drvdata { struct watchdog_device wdt; - void __iomem *base; - struct clk *rtc_clk; + struct regmap *map; + struct clk *clk; + unsigned long clk_rate; }; static int jz4740_wdt_ping(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); + regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0); + return 0; } @@ -65,35 +56,17 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, unsigned int new_timeout) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - unsigned int rtc_clk_rate; - unsigned int timeout_value; - unsigned short clock_div = JZ_WDT_CLOCK_DIV_1; - u8 tcer; - - rtc_clk_rate = clk_get_rate(drvdata->rtc_clk); - - timeout_value = rtc_clk_rate * new_timeout; - while (timeout_value > 0xffff) { - if (clock_div == JZ_WDT_CLOCK_DIV_1024) { - /* Requested timeout too high; - * use highest possible value. */ - timeout_value = 0xffff; - break; - } - timeout_value >>= 2; - clock_div += (1 << TCU_TCSR_PRESCALE_LSB); - } + u16 timeout_value = (u16)(drvdata->clk_rate * new_timeout); + unsigned int tcer; - tcer = readb(drvdata->base + TCU_REG_WDT_TCER); - writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); - writew(clock_div, drvdata->base + TCU_REG_WDT_TCSR); + regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0); - writew((u16)timeout_value, drvdata->base + TCU_REG_WDT_TDR); - writew(0x0, drvdata->base + TCU_REG_WDT_TCNT); - writew(clock_div | JZ_WDT_CLOCK_RTC, drvdata->base + TCU_REG_WDT_TCSR); + regmap_write(drvdata->map, TCU_REG_WDT_TDR, timeout_value); + regmap_write(drvdata->map, TCU_REG_WDT_TCNT, 0); if (tcer & TCU_WDT_TCER_TCEN) - writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN); wdt_dev->timeout = new_timeout; return 0; @@ -102,16 +75,20 @@ static int jz4740_wdt_set_timeout(struct watchdog_device *wdt_dev, static int jz4740_wdt_start(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - u8 tcer; + unsigned int tcer; + int ret; - tcer = readb(drvdata->base + TCU_REG_WDT_TCER); + ret = clk_prepare_enable(drvdata->clk); + if (ret) + return ret; + + regmap_read(drvdata->map, TCU_REG_WDT_TCER, &tcer); - jz4740_timer_enable_watchdog(); jz4740_wdt_set_timeout(wdt_dev, wdt_dev->timeout); /* Start watchdog if it wasn't started already */ if (!(tcer & TCU_WDT_TCER_TCEN)) - writeb(TCU_WDT_TCER_TCEN, drvdata->base + TCU_REG_WDT_TCER); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, TCU_WDT_TCER_TCEN); return 0; } @@ -120,8 +97,8 @@ static int jz4740_wdt_stop(struct watchdog_device *wdt_dev) { struct jz4740_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); - writeb(0x0, drvdata->base + TCU_REG_WDT_TCER); - jz4740_timer_disable_watchdog(); + regmap_write(drvdata->map, TCU_REG_WDT_TCER, 0); + clk_disable_unprepare(drvdata->clk); return 0; } @@ -162,33 +139,46 @@ static int jz4740_wdt_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct jz4740_wdt_drvdata *drvdata; struct watchdog_device *jz4740_wdt; + long rate; + int ret; drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; - if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) - heartbeat = DEFAULT_HEARTBEAT; + drvdata->clk = devm_clk_get(&pdev->dev, "wdt"); + if (IS_ERR(drvdata->clk)) { + dev_err(&pdev->dev, "cannot find WDT clock\n"); + return PTR_ERR(drvdata->clk); + } + + /* Set smallest clock possible */ + rate = clk_round_rate(drvdata->clk, 1); + if (rate < 0) + return rate; + ret = clk_set_rate(drvdata->clk, rate); + if (ret) + return ret; + + drvdata->clk_rate = rate; jz4740_wdt = &drvdata->wdt; jz4740_wdt->info = &jz4740_wdt_info; jz4740_wdt->ops = &jz4740_wdt_ops; - jz4740_wdt->timeout = heartbeat; jz4740_wdt->min_timeout = 1; - jz4740_wdt->max_timeout = MAX_HEARTBEAT; + jz4740_wdt->max_timeout = 0xffff / rate; + jz4740_wdt->timeout = clamp(heartbeat, + jz4740_wdt->min_timeout, + jz4740_wdt->max_timeout); jz4740_wdt->parent = dev; watchdog_set_nowayout(jz4740_wdt, nowayout); watchdog_set_drvdata(jz4740_wdt, drvdata); - drvdata->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(drvdata->base)) - return PTR_ERR(drvdata->base); - - drvdata->rtc_clk = devm_clk_get(dev, "rtc"); - if (IS_ERR(drvdata->rtc_clk)) { - dev_err(dev, "cannot find RTC clock\n"); - return PTR_ERR(drvdata->rtc_clk); + drvdata->map = device_node_to_regmap(dev->parent->of_node); + if (!drvdata->map) { + dev_err(dev, "regmap not found\n"); + return -EINVAL; } return devm_watchdog_register_device(dev, &drvdata->wdt); diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c index 752d03620f0a..22f335e1e164 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -183,6 +183,7 @@ static const struct file_operations m54xx_wdt_fops = { .llseek = no_llseek, .write = m54xx_wdt_write, .unlocked_ioctl = m54xx_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = m54xx_wdt_open, .release = m54xx_wdt_release, }; diff --git a/drivers/watchdog/machzwd.c b/drivers/watchdog/machzwd.c index cef2baf59dda..80ff94688487 100644 --- a/drivers/watchdog/machzwd.c +++ b/drivers/watchdog/machzwd.c @@ -361,6 +361,7 @@ static const struct file_operations zf_fops = { .llseek = no_llseek, .write = zf_write, .unlocked_ioctl = zf_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = zf_open, .release = zf_close, }; diff --git a/drivers/watchdog/mixcomwd.c b/drivers/watchdog/mixcomwd.c index a86faa5000f1..d387bad377c4 100644 --- a/drivers/watchdog/mixcomwd.c +++ b/drivers/watchdog/mixcomwd.c @@ -227,6 +227,7 @@ static const struct file_operations mixcomwd_fops = { .llseek = no_llseek, .write = mixcomwd_write, .unlocked_ioctl = mixcomwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = mixcomwd_open, .release = mixcomwd_release, }; diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 25a92857b217..8aa1cb4a295f 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -181,6 +181,7 @@ static const struct file_operations mtx1_wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = mtx1_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = mtx1_wdt_open, .write = mtx1_wdt_write, .release = mtx1_wdt_release, diff --git a/drivers/watchdog/mv64x60_wdt.c b/drivers/watchdog/mv64x60_wdt.c index 74bf7144a970..0bc72dd69b70 100644 --- a/drivers/watchdog/mv64x60_wdt.c +++ b/drivers/watchdog/mv64x60_wdt.c @@ -241,6 +241,7 @@ static const struct file_operations mv64x60_wdt_fops = { .llseek = no_llseek, .write = mv64x60_wdt_write, .unlocked_ioctl = mv64x60_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = mv64x60_wdt_open, .release = mv64x60_wdt_release, }; diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index 5f0082e300bd..d7a560e348d5 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -267,6 +267,7 @@ static const struct file_operations nv_tco_fops = { .llseek = no_llseek, .write = nv_tco_write, .unlocked_ioctl = nv_tco_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = nv_tco_open, .release = nv_tco_release, }; diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index 2af1a8b3f973..73fbfc99083b 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -473,6 +473,7 @@ static const struct file_operations pc87413_fops = { .llseek = no_llseek, .write = pc87413_write, .unlocked_ioctl = pc87413_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pc87413_open, .release = pc87413_release, }; diff --git a/drivers/watchdog/pcwd.c b/drivers/watchdog/pcwd.c index c3c93e00b320..7a0587fdc52c 100644 --- a/drivers/watchdog/pcwd.c +++ b/drivers/watchdog/pcwd.c @@ -752,6 +752,7 @@ static const struct file_operations pcwd_fops = { .llseek = no_llseek, .write = pcwd_write, .unlocked_ioctl = pcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pcwd_open, .release = pcwd_close, }; diff --git a/drivers/watchdog/pcwd_pci.c b/drivers/watchdog/pcwd_pci.c index e30c1f762045..81508a42a90c 100644 --- a/drivers/watchdog/pcwd_pci.c +++ b/drivers/watchdog/pcwd_pci.c @@ -646,6 +646,7 @@ static const struct file_operations pcipcwd_fops = { .llseek = no_llseek, .write = pcipcwd_write, .unlocked_ioctl = pcipcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pcipcwd_open, .release = pcipcwd_release, }; diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 6727f8ab2d18..2f44af1831d0 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -550,6 +550,7 @@ static const struct file_operations usb_pcwd_fops = { .llseek = no_llseek, .write = usb_pcwd_write, .unlocked_ioctl = usb_pcwd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = usb_pcwd_open, .release = usb_pcwd_release, }; diff --git a/drivers/watchdog/pika_wdt.c b/drivers/watchdog/pika_wdt.c index 205c3c68fca1..a98abd0d3146 100644 --- a/drivers/watchdog/pika_wdt.c +++ b/drivers/watchdog/pika_wdt.c @@ -214,6 +214,7 @@ static const struct file_operations pikawdt_fops = { .release = pikawdt_release, .write = pikawdt_write, .unlocked_ioctl = pikawdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice pikawdt_miscdev = { diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c index aa53babf2bab..4097d076aab8 100644 --- a/drivers/watchdog/pnx833x_wdt.c +++ b/drivers/watchdog/pnx833x_wdt.c @@ -215,6 +215,7 @@ static const struct file_operations pnx833x_wdt_fops = { .llseek = no_llseek, .write = pnx833x_wdt_write, .unlocked_ioctl = pnx833x_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = pnx833x_wdt_open, .release = pnx833x_wdt_release, }; diff --git a/drivers/watchdog/rc32434_wdt.c b/drivers/watchdog/rc32434_wdt.c index a8a4b3a41a90..1dfede0abf18 100644 --- a/drivers/watchdog/rc32434_wdt.c +++ b/drivers/watchdog/rc32434_wdt.c @@ -245,6 +245,7 @@ static const struct file_operations rc32434_wdt_fops = { .llseek = no_llseek, .write = rc32434_wdt_write, .unlocked_ioctl = rc32434_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = rc32434_wdt_open, .release = rc32434_wdt_release, }; diff --git a/drivers/watchdog/rdc321x_wdt.c b/drivers/watchdog/rdc321x_wdt.c index 2e608ae6cbc7..57187efeb86f 100644 --- a/drivers/watchdog/rdc321x_wdt.c +++ b/drivers/watchdog/rdc321x_wdt.c @@ -199,6 +199,7 @@ static const struct file_operations rdc321x_wdt_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = rdc321x_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = rdc321x_wdt_open, .write = rdc321x_wdt_write, .release = rdc321x_wdt_release, diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c index b35f7be20c00..dc3c06a92f93 100644 --- a/drivers/watchdog/riowd.c +++ b/drivers/watchdog/riowd.c @@ -163,6 +163,7 @@ static const struct file_operations riowd_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = riowd_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = riowd_open, .write = riowd_write, .release = riowd_release, diff --git a/drivers/watchdog/sa1100_wdt.c b/drivers/watchdog/sa1100_wdt.c index cbd8c957182f..9b93be00109f 100644 --- a/drivers/watchdog/sa1100_wdt.c +++ b/drivers/watchdog/sa1100_wdt.c @@ -141,6 +141,7 @@ static const struct file_operations sa1100dog_fops = { .llseek = no_llseek, .write = sa1100dog_write, .unlocked_ioctl = sa1100dog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sa1100dog_open, .release = sa1100dog_release, }; diff --git a/drivers/watchdog/sb_wdog.c b/drivers/watchdog/sb_wdog.c index 202fc8d8ca5f..da2dad00d473 100644 --- a/drivers/watchdog/sb_wdog.c +++ b/drivers/watchdog/sb_wdog.c @@ -237,6 +237,7 @@ static const struct file_operations sbwdog_fops = { .llseek = no_llseek, .write = sbwdog_write, .unlocked_ioctl = sbwdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sbwdog_open, .release = sbwdog_release, }; diff --git a/drivers/watchdog/sbc60xxwdt.c b/drivers/watchdog/sbc60xxwdt.c index c3151642694c..f2cbe6d880a8 100644 --- a/drivers/watchdog/sbc60xxwdt.c +++ b/drivers/watchdog/sbc60xxwdt.c @@ -280,6 +280,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c index 12cdee7d5069..520b8dd77ed4 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -194,9 +194,8 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (wdt_set_timeout(new_timeout)) return -EINVAL; - - /* Fall through */ } + /* Fall through */ case WDIOC_GETTIMEOUT: return put_user(timeout, (int __user *)arg); default: @@ -211,6 +210,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sbc_epx_c3.c b/drivers/watchdog/sbc_epx_c3.c index 86828c28843f..5e3a9ddb952e 100644 --- a/drivers/watchdog/sbc_epx_c3.c +++ b/drivers/watchdog/sbc_epx_c3.c @@ -156,6 +156,7 @@ static const struct file_operations epx_c3_fops = { .llseek = no_llseek, .write = epx_c3_write, .unlocked_ioctl = epx_c3_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = epx_c3_open, .release = epx_c3_release, }; diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index 3822a60a8d2b..1b20b33879c4 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -186,6 +186,7 @@ static const struct file_operations fitpc2_wdt_fops = { .llseek = no_llseek, .write = fitpc2_wdt_write, .unlocked_ioctl = fitpc2_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = fitpc2_wdt_open, .release = fitpc2_wdt_release, }; diff --git a/drivers/watchdog/sc1200wdt.c b/drivers/watchdog/sc1200wdt.c index 960385a766b3..9673eb12dacd 100644 --- a/drivers/watchdog/sc1200wdt.c +++ b/drivers/watchdog/sc1200wdt.c @@ -307,6 +307,7 @@ static const struct file_operations sc1200wdt_fops = { .llseek = no_llseek, .write = sc1200wdt_write, .unlocked_ioctl = sc1200wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sc1200wdt_open, .release = sc1200wdt_release, }; diff --git a/drivers/watchdog/sc520_wdt.c b/drivers/watchdog/sc520_wdt.c index a612128c5f80..fbe79bcc9297 100644 --- a/drivers/watchdog/sc520_wdt.c +++ b/drivers/watchdog/sc520_wdt.c @@ -336,6 +336,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index 3612f1df381b..83949a385f62 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -337,6 +337,7 @@ static const struct file_operations sch311x_wdt_fops = { .llseek = no_llseek, .write = sch311x_wdt_write, .unlocked_ioctl = sch311x_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = sch311x_wdt_open, .release = sch311x_wdt_close, }; diff --git a/drivers/watchdog/scx200_wdt.c b/drivers/watchdog/scx200_wdt.c index 46268309ee9b..c94098acb78f 100644 --- a/drivers/watchdog/scx200_wdt.c +++ b/drivers/watchdog/scx200_wdt.c @@ -201,6 +201,7 @@ static const struct file_operations scx200_wdt_fops = { .llseek = no_llseek, .write = scx200_wdt_write, .unlocked_ioctl = scx200_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = scx200_wdt_open, .release = scx200_wdt_release, }; diff --git a/drivers/watchdog/smsc37b787_wdt.c b/drivers/watchdog/smsc37b787_wdt.c index f5713030d0f7..43de56acd767 100644 --- a/drivers/watchdog/smsc37b787_wdt.c +++ b/drivers/watchdog/smsc37b787_wdt.c @@ -505,6 +505,7 @@ static const struct file_operations wb_smsc_wdt_fops = { .llseek = no_llseek, .write = wb_smsc_wdt_write, .unlocked_ioctl = wb_smsc_wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wb_smsc_wdt_open, .release = wb_smsc_wdt_release, }; diff --git a/drivers/watchdog/sprd_wdt.c b/drivers/watchdog/sprd_wdt.c index 0bb17b046140..65cb55f3916f 100644 --- a/drivers/watchdog/sprd_wdt.c +++ b/drivers/watchdog/sprd_wdt.c @@ -327,10 +327,9 @@ static int sprd_wdt_probe(struct platform_device *pdev) static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); - if (watchdog_active(wdd)) + if (watchdog_active(&wdt->wdd)) sprd_wdt_stop(&wdt->wdd); sprd_wdt_disable(wdt); @@ -339,7 +338,6 @@ static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev) static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) { - struct watchdog_device *wdd = dev_get_drvdata(dev); struct sprd_wdt *wdt = dev_get_drvdata(dev); int ret; @@ -347,7 +345,7 @@ static int __maybe_unused sprd_wdt_pm_resume(struct device *dev) if (ret) return ret; - if (watchdog_active(wdd)) { + if (watchdog_active(&wdt->wdd)) { ret = sprd_wdt_start(&wdt->wdd); if (ret) { sprd_wdt_disable(wdt); diff --git a/drivers/watchdog/w83627hf_wdt.c b/drivers/watchdog/w83627hf_wdt.c index 38b31e9947aa..fdf533fe0bb2 100644 --- a/drivers/watchdog/w83627hf_wdt.c +++ b/drivers/watchdog/w83627hf_wdt.c @@ -49,7 +49,7 @@ static int wdt_cfg_leave = 0xAA;/* key to lock configuration space */ enum chips { w83627hf, w83627s, w83697hf, w83697ug, w83637hf, w83627thf, w83687thf, w83627ehf, w83627dhg, w83627uhg, w83667hg, w83627dhg_p, w83667hg_b, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6102 }; + nct6795, nct6796, nct6102, nct6116 }; static int timeout; /* in seconds */ module_param(timeout, int, 0); @@ -94,6 +94,7 @@ MODULE_PARM_DESC(early_disable, "Disable watchdog at boot time (default=0)"); #define NCT6775_ID 0xb4 #define NCT6776_ID 0xc3 #define NCT6102_ID 0xc4 +#define NCT6116_ID 0xd2 #define NCT6779_ID 0xc5 #define NCT6791_ID 0xc8 #define NCT6792_ID 0xc9 @@ -211,6 +212,7 @@ static int w83627hf_init(struct watchdog_device *wdog, enum chips chip) case nct6795: case nct6796: case nct6102: + case nct6116: /* * These chips have a fixed WDTO# output pin (W83627UHG), * or support more than one WDTO# output pin. @@ -417,6 +419,12 @@ static int wdt_find(int addr) cr_wdt_control = NCT6102D_WDT_CONTROL; cr_wdt_csr = NCT6102D_WDT_CSR; break; + case NCT6116_ID: + ret = nct6102; + cr_wdt_timeout = NCT6102D_WDT_TIMEOUT; + cr_wdt_control = NCT6102D_WDT_CONTROL; + cr_wdt_csr = NCT6102D_WDT_CSR; + break; case 0xff: ret = -ENODEV; break; @@ -482,6 +490,7 @@ static int __init wdt_init(void) "NCT6795", "NCT6796", "NCT6102", + "NCT6116", }; /* Apply system-specific quirks */ diff --git a/drivers/watchdog/w83877f_wdt.c b/drivers/watchdog/w83877f_wdt.c index 6eb5185d6ea6..6b3b667e6f23 100644 --- a/drivers/watchdog/w83877f_wdt.c +++ b/drivers/watchdog/w83877f_wdt.c @@ -304,6 +304,7 @@ static const struct file_operations wdt_fops = { .open = fop_open, .release = fop_close, .unlocked_ioctl = fop_ioctl, + .compat_ioctl = compat_ptr_ioctl, }; static struct miscdevice wdt_miscdev = { diff --git a/drivers/watchdog/w83977f_wdt.c b/drivers/watchdog/w83977f_wdt.c index 16e9cbe72acc..5212e68c6b01 100644 --- a/drivers/watchdog/w83977f_wdt.c +++ b/drivers/watchdog/w83977f_wdt.c @@ -446,6 +446,7 @@ static const struct file_operations wdt_fops = { .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt_open, .release = wdt_release, }; diff --git a/drivers/watchdog/wafer5823wdt.c b/drivers/watchdog/wafer5823wdt.c index 6d2071a0590d..a6925847f76f 100644 --- a/drivers/watchdog/wafer5823wdt.c +++ b/drivers/watchdog/wafer5823wdt.c @@ -230,6 +230,7 @@ static const struct file_operations wafwdt_fops = { .llseek = no_llseek, .write = wafwdt_write, .unlocked_ioctl = wafwdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wafwdt_open, .release = wafwdt_close, }; diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index dbd2ad4c9294..4b2a85438478 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -34,7 +34,6 @@ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/hrtimer.h> /* For hrtimers */ #include <linux/kernel.h> /* For printk/panic/... */ -#include <linux/kref.h> /* For data references */ #include <linux/kthread.h> /* For kthread_work */ #include <linux/miscdevice.h> /* For handling misc devices */ #include <linux/module.h> /* For module stuff/... */ @@ -52,14 +51,14 @@ /* * struct watchdog_core_data - watchdog core internal data - * @kref: Reference count. + * @dev: The watchdog's internal device * @cdev: The watchdog's Character device. * @wdd: Pointer to watchdog device. * @lock: Lock for watchdog core. * @status: Watchdog core internal status bits. */ struct watchdog_core_data { - struct kref kref; + struct device dev; struct cdev cdev; struct watchdog_device *wdd; struct mutex lock; @@ -158,7 +157,8 @@ static inline void watchdog_update_worker(struct watchdog_device *wdd) ktime_t t = watchdog_next_keepalive(wdd); if (t > 0) - hrtimer_start(&wd_data->timer, t, HRTIMER_MODE_REL); + hrtimer_start(&wd_data->timer, t, + HRTIMER_MODE_REL_HARD); } else { hrtimer_cancel(&wd_data->timer); } @@ -177,7 +177,7 @@ static int __watchdog_ping(struct watchdog_device *wdd) if (ktime_after(earliest_keepalive, now)) { hrtimer_start(&wd_data->timer, ktime_sub(earliest_keepalive, now), - HRTIMER_MODE_REL); + HRTIMER_MODE_REL_HARD); return 0; } @@ -452,7 +452,26 @@ static ssize_t nowayout_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", !!test_bit(WDOG_NO_WAY_OUT, &wdd->status)); } -static DEVICE_ATTR_RO(nowayout); + +static ssize_t nowayout_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret) + return ret; + if (value > 1) + return -EINVAL; + /* nowayout cannot be disabled once set */ + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status) && !value) + return -EPERM; + watchdog_set_nowayout(wdd, value); + return len; +} +static DEVICE_ATTR_RW(nowayout); static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -839,7 +858,7 @@ static int watchdog_open(struct inode *inode, struct file *file) file->private_data = wd_data; if (!hw_running) - kref_get(&wd_data->kref); + get_device(&wd_data->dev); /* * open_timeout only applies for the first open from @@ -860,11 +879,11 @@ out_clear: return err; } -static void watchdog_core_data_release(struct kref *kref) +static void watchdog_core_data_release(struct device *dev) { struct watchdog_core_data *wd_data; - wd_data = container_of(kref, struct watchdog_core_data, kref); + wd_data = container_of(dev, struct watchdog_core_data, dev); kfree(wd_data); } @@ -924,7 +943,7 @@ done: */ if (!running) { module_put(wd_data->cdev.owner); - kref_put(&wd_data->kref, watchdog_core_data_release); + put_device(&wd_data->dev); } return 0; } @@ -933,6 +952,7 @@ static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = watchdog_open, .release = watchdog_release, }; @@ -943,17 +963,22 @@ static struct miscdevice watchdog_miscdev = { .fops = &watchdog_fops, }; +static struct class watchdog_class = { + .name = "watchdog", + .owner = THIS_MODULE, + .dev_groups = wdt_groups, +}; + /* * watchdog_cdev_register: register watchdog character device * @wdd: watchdog device - * @devno: character device number * * Register a watchdog character device including handling the legacy * /dev/watchdog node. /dev/watchdog is actually a miscdevice and * thus we set it up like that. */ -static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) +static int watchdog_cdev_register(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data; int err; @@ -961,7 +986,6 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) wd_data = kzalloc(sizeof(struct watchdog_core_data), GFP_KERNEL); if (!wd_data) return -ENOMEM; - kref_init(&wd_data->kref); mutex_init(&wd_data->lock); wd_data->wdd = wdd; @@ -971,7 +995,7 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) return -ENODEV; kthread_init_work(&wd_data->work, watchdog_ping_work); - hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer_init(&wd_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); wd_data->timer.function = watchdog_timer_expired; if (wdd->id == 0) { @@ -990,23 +1014,33 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) } } + device_initialize(&wd_data->dev); + wd_data->dev.devt = MKDEV(MAJOR(watchdog_devt), wdd->id); + wd_data->dev.class = &watchdog_class; + wd_data->dev.parent = wdd->parent; + wd_data->dev.groups = wdd->groups; + wd_data->dev.release = watchdog_core_data_release; + dev_set_drvdata(&wd_data->dev, wdd); + dev_set_name(&wd_data->dev, "watchdog%d", wdd->id); + /* Fill in the data structures */ cdev_init(&wd_data->cdev, &watchdog_fops); - wd_data->cdev.owner = wdd->ops->owner; /* Add the device */ - err = cdev_add(&wd_data->cdev, devno, 1); + err = cdev_device_add(&wd_data->cdev, &wd_data->dev); if (err) { pr_err("watchdog%d unable to add device %d:%d\n", wdd->id, MAJOR(watchdog_devt), wdd->id); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; - kref_put(&wd_data->kref, watchdog_core_data_release); + put_device(&wd_data->dev); } return err; } + wd_data->cdev.owner = wdd->ops->owner; + /* Record time of most recent heartbeat as 'just before now'. */ wd_data->last_hw_keepalive = ktime_sub(ktime_get(), 1); watchdog_set_open_deadline(wd_data); @@ -1017,9 +1051,10 @@ static int watchdog_cdev_register(struct watchdog_device *wdd, dev_t devno) */ if (watchdog_hw_running(wdd)) { __module_get(wdd->ops->owner); - kref_get(&wd_data->kref); + get_device(&wd_data->dev); if (handle_boot_enabled) - hrtimer_start(&wd_data->timer, 0, HRTIMER_MODE_REL); + hrtimer_start(&wd_data->timer, 0, + HRTIMER_MODE_REL_HARD); else pr_info("watchdog%d running and kernel based pre-userspace handler disabled\n", wdd->id); @@ -1040,7 +1075,7 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) { struct watchdog_core_data *wd_data = wdd->wd_data; - cdev_del(&wd_data->cdev); + cdev_device_del(&wd_data->cdev, &wd_data->dev); if (wdd->id == 0) { misc_deregister(&watchdog_miscdev); old_wd_data = NULL; @@ -1059,15 +1094,9 @@ static void watchdog_cdev_unregister(struct watchdog_device *wdd) hrtimer_cancel(&wd_data->timer); kthread_cancel_work_sync(&wd_data->work); - kref_put(&wd_data->kref, watchdog_core_data_release); + put_device(&wd_data->dev); } -static struct class watchdog_class = { - .name = "watchdog", - .owner = THIS_MODULE, - .dev_groups = wdt_groups, -}; - static int watchdog_reboot_notifier(struct notifier_block *nb, unsigned long code, void *data) { @@ -1098,27 +1127,14 @@ static int watchdog_reboot_notifier(struct notifier_block *nb, int watchdog_dev_register(struct watchdog_device *wdd) { - struct device *dev; - dev_t devno; int ret; - devno = MKDEV(MAJOR(watchdog_devt), wdd->id); - - ret = watchdog_cdev_register(wdd, devno); + ret = watchdog_cdev_register(wdd); if (ret) return ret; - dev = device_create_with_groups(&watchdog_class, wdd->parent, - devno, wdd, wdd->groups, - "watchdog%d", wdd->id); - if (IS_ERR(dev)) { - watchdog_cdev_unregister(wdd); - return PTR_ERR(dev); - } - ret = watchdog_register_pretimeout(wdd); if (ret) { - device_destroy(&watchdog_class, devno); watchdog_cdev_unregister(wdd); return ret; } @@ -1126,7 +1142,8 @@ int watchdog_dev_register(struct watchdog_device *wdd) if (test_bit(WDOG_STOP_ON_REBOOT, &wdd->status)) { wdd->reboot_nb.notifier_call = watchdog_reboot_notifier; - ret = devm_register_reboot_notifier(dev, &wdd->reboot_nb); + ret = devm_register_reboot_notifier(&wdd->wd_data->dev, + &wdd->reboot_nb); if (ret) { pr_err("watchdog%d: Cannot register reboot notifier (%d)\n", wdd->id, ret); @@ -1148,7 +1165,6 @@ int watchdog_dev_register(struct watchdog_device *wdd) void watchdog_dev_unregister(struct watchdog_device *wdd) { watchdog_unregister_pretimeout(wdd); - device_destroy(&watchdog_class, wdd->wd_data->cdev.dev); watchdog_cdev_unregister(wdd); } diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c index e7cf41aa26c3..b069349b52f5 100644 --- a/drivers/watchdog/wdat_wdt.c +++ b/drivers/watchdog/wdat_wdt.c @@ -202,7 +202,7 @@ static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat) * WDAT specification says that the watchdog is required to reboot * the system when it fires. However, it also states that it is * recommeded to make it configurable through hardware register. We - * enable reboot now if it is configrable, just in case. + * enable reboot now if it is configurable, just in case. */ ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, NULL); if (ret && ret != -EOPNOTSUPP) { diff --git a/drivers/watchdog/wdrtas.c b/drivers/watchdog/wdrtas.c index 6ad7edb4a712..184a06a74f83 100644 --- a/drivers/watchdog/wdrtas.c +++ b/drivers/watchdog/wdrtas.c @@ -472,6 +472,7 @@ static const struct file_operations wdrtas_fops = { .llseek = no_llseek, .write = wdrtas_write, .unlocked_ioctl = wdrtas_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdrtas_open, .release = wdrtas_close, }; diff --git a/drivers/watchdog/wdt.c b/drivers/watchdog/wdt.c index 7d278b37e083..f9054cb0f8e2 100644 --- a/drivers/watchdog/wdt.c +++ b/drivers/watchdog/wdt.c @@ -523,6 +523,7 @@ static const struct file_operations wdt_fops = { .llseek = no_llseek, .write = wdt_write, .unlocked_ioctl = wdt_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt_open, .release = wdt_release, }; diff --git a/drivers/watchdog/wdt285.c b/drivers/watchdog/wdt285.c index eb729d704836..e60993d0767e 100644 --- a/drivers/watchdog/wdt285.c +++ b/drivers/watchdog/wdt285.c @@ -181,6 +181,7 @@ static const struct file_operations watchdog_fops = { .llseek = no_llseek, .write = watchdog_write, .unlocked_ioctl = watchdog_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = watchdog_open, .release = watchdog_release, }; diff --git a/drivers/watchdog/wdt977.c b/drivers/watchdog/wdt977.c index 5c52c73e1839..066a4fb4d75b 100644 --- a/drivers/watchdog/wdt977.c +++ b/drivers/watchdog/wdt977.c @@ -422,6 +422,7 @@ static const struct file_operations wdt977_fops = { .llseek = no_llseek, .write = wdt977_write, .unlocked_ioctl = wdt977_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdt977_open, .release = wdt977_release, }; diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index 66303ab95685..e528024faa41 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -566,6 +566,7 @@ static const struct file_operations wdtpci_fops = { .llseek = no_llseek, .write = wdtpci_write, .unlocked_ioctl = wdtpci_ioctl, + .compat_ioctl = compat_ptr_ioctl, .open = wdtpci_open, .release = wdtpci_release, }; diff --git a/drivers/xen/gntdev-common.h b/drivers/xen/gntdev-common.h index 2f8b949c3eeb..91e44c04f787 100644 --- a/drivers/xen/gntdev-common.h +++ b/drivers/xen/gntdev-common.h @@ -21,15 +21,8 @@ struct gntdev_dmabuf_priv; struct gntdev_priv { /* Maps with visible offsets in the file descriptor. */ struct list_head maps; - /* - * Maps that are not visible; will be freed on munmap. - * Only populated if populate_freeable_maps == 1 - */ - struct list_head freeable_maps; /* lock protects maps and freeable_maps. */ struct mutex lock; - struct mm_struct *mm; - struct mmu_notifier mn; #ifdef CONFIG_XEN_GRANT_DMA_ALLOC /* Device for which DMA memory is allocated. */ @@ -49,6 +42,7 @@ struct gntdev_unmap_notify { }; struct gntdev_grant_map { + struct mmu_interval_notifier notifier; struct list_head next; struct vm_area_struct *vma; int index; diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 81401f386c9c..a04ddf2a68af 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -63,7 +63,6 @@ MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped by " static atomic_t pages_mapped = ATOMIC_INIT(0); static int use_ptemod; -#define populate_freeable_maps use_ptemod static int unmap_grant_pages(struct gntdev_grant_map *map, int offset, int pages); @@ -249,12 +248,6 @@ void gntdev_put_map(struct gntdev_priv *priv, struct gntdev_grant_map *map) evtchn_put(map->notify.event); } - if (populate_freeable_maps && priv) { - mutex_lock(&priv->lock); - list_del(&map->next); - mutex_unlock(&priv->lock); - } - if (map->pages && !use_ptemod) unmap_grant_pages(map, 0, map->count); gntdev_free_map(map); @@ -444,16 +437,9 @@ static void gntdev_vma_close(struct vm_area_struct *vma) pr_debug("gntdev_vma_close %p\n", vma); if (use_ptemod) { - /* It is possible that an mmu notifier could be running - * concurrently, so take priv->lock to ensure that the vma won't - * vanishing during the unmap_grant_pages call, since we will - * spin here until that completes. Such a concurrent call will - * not do any unmapping, since that has been done prior to - * closing the vma, but it may still iterate the unmap_ops list. - */ - mutex_lock(&priv->lock); + WARN_ON(map->vma != vma); + mmu_interval_notifier_remove(&map->notifier); map->vma = NULL; - mutex_unlock(&priv->lock); } vma->vm_private_data = NULL; gntdev_put_map(priv, map); @@ -475,109 +461,44 @@ static const struct vm_operations_struct gntdev_vmops = { /* ------------------------------------------------------------------ */ -static bool in_range(struct gntdev_grant_map *map, - unsigned long start, unsigned long end) -{ - if (!map->vma) - return false; - if (map->vma->vm_start >= end) - return false; - if (map->vma->vm_end <= start) - return false; - - return true; -} - -static int unmap_if_in_range(struct gntdev_grant_map *map, - unsigned long start, unsigned long end, - bool blockable) +static bool gntdev_invalidate(struct mmu_interval_notifier *mn, + const struct mmu_notifier_range *range, + unsigned long cur_seq) { + struct gntdev_grant_map *map = + container_of(mn, struct gntdev_grant_map, notifier); unsigned long mstart, mend; int err; - if (!in_range(map, start, end)) - return 0; + if (!mmu_notifier_range_blockable(range)) + return false; - if (!blockable) - return -EAGAIN; + /* + * If the VMA is split or otherwise changed the notifier is not + * updated, but we don't want to process VA's outside the modified + * VMA. FIXME: It would be much more understandable to just prevent + * modifying the VMA in the first place. + */ + if (map->vma->vm_start >= range->end || + map->vma->vm_end <= range->start) + return true; - mstart = max(start, map->vma->vm_start); - mend = min(end, map->vma->vm_end); + mstart = max(range->start, map->vma->vm_start); + mend = min(range->end, map->vma->vm_end); pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n", map->index, map->count, map->vma->vm_start, map->vma->vm_end, - start, end, mstart, mend); + range->start, range->end, mstart, mend); err = unmap_grant_pages(map, (mstart - map->vma->vm_start) >> PAGE_SHIFT, (mend - mstart) >> PAGE_SHIFT); WARN_ON(err); - return 0; -} - -static int mn_invl_range_start(struct mmu_notifier *mn, - const struct mmu_notifier_range *range) -{ - struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); - struct gntdev_grant_map *map; - int ret = 0; - - if (mmu_notifier_range_blockable(range)) - mutex_lock(&priv->lock); - else if (!mutex_trylock(&priv->lock)) - return -EAGAIN; - - list_for_each_entry(map, &priv->maps, next) { - ret = unmap_if_in_range(map, range->start, range->end, - mmu_notifier_range_blockable(range)); - if (ret) - goto out_unlock; - } - list_for_each_entry(map, &priv->freeable_maps, next) { - ret = unmap_if_in_range(map, range->start, range->end, - mmu_notifier_range_blockable(range)); - if (ret) - goto out_unlock; - } - -out_unlock: - mutex_unlock(&priv->lock); - - return ret; -} - -static void mn_release(struct mmu_notifier *mn, - struct mm_struct *mm) -{ - struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); - struct gntdev_grant_map *map; - int err; - - mutex_lock(&priv->lock); - list_for_each_entry(map, &priv->maps, next) { - if (!map->vma) - continue; - pr_debug("map %d+%d (%lx %lx)\n", - map->index, map->count, - map->vma->vm_start, map->vma->vm_end); - err = unmap_grant_pages(map, /* offset */ 0, map->count); - WARN_ON(err); - } - list_for_each_entry(map, &priv->freeable_maps, next) { - if (!map->vma) - continue; - pr_debug("map %d+%d (%lx %lx)\n", - map->index, map->count, - map->vma->vm_start, map->vma->vm_end); - err = unmap_grant_pages(map, /* offset */ 0, map->count); - WARN_ON(err); - } - mutex_unlock(&priv->lock); + return true; } -static const struct mmu_notifier_ops gntdev_mmu_ops = { - .release = mn_release, - .invalidate_range_start = mn_invl_range_start, +static const struct mmu_interval_notifier_ops gntdev_mmu_ops = { + .invalidate = gntdev_invalidate, }; /* ------------------------------------------------------------------ */ @@ -592,7 +513,6 @@ static int gntdev_open(struct inode *inode, struct file *flip) return -ENOMEM; INIT_LIST_HEAD(&priv->maps); - INIT_LIST_HEAD(&priv->freeable_maps); mutex_init(&priv->lock); #ifdef CONFIG_XEN_GNTDEV_DMABUF @@ -604,17 +524,6 @@ static int gntdev_open(struct inode *inode, struct file *flip) } #endif - if (use_ptemod) { - priv->mm = get_task_mm(current); - if (!priv->mm) { - kfree(priv); - return -ENOMEM; - } - priv->mn.ops = &gntdev_mmu_ops; - ret = mmu_notifier_register(&priv->mn, priv->mm); - mmput(priv->mm); - } - if (ret) { kfree(priv); return ret; @@ -644,16 +553,12 @@ static int gntdev_release(struct inode *inode, struct file *flip) list_del(&map->next); gntdev_put_map(NULL /* already removed */, map); } - WARN_ON(!list_empty(&priv->freeable_maps)); mutex_unlock(&priv->lock); #ifdef CONFIG_XEN_GNTDEV_DMABUF gntdev_dmabuf_fini(priv->dmabuf_priv); #endif - if (use_ptemod) - mmu_notifier_unregister(&priv->mn, priv->mm); - kfree(priv); return 0; } @@ -714,8 +619,6 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); if (map) { list_del(&map->next); - if (populate_freeable_maps) - list_add_tail(&map->next, &priv->freeable_maps); err = 0; } mutex_unlock(&priv->lock); @@ -1087,11 +990,6 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) goto unlock_out; if (use_ptemod && map->vma) goto unlock_out; - if (use_ptemod && priv->mm != vma->vm_mm) { - pr_warn("Huh? Other mm?\n"); - goto unlock_out; - } - refcount_inc(&map->users); vma->vm_ops = &gntdev_vmops; @@ -1102,10 +1000,6 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTCOPY; vma->vm_private_data = map; - - if (use_ptemod) - map->vma = vma; - if (map->flags) { if ((vma->vm_flags & VM_WRITE) && (map->flags & GNTMAP_readonly)) @@ -1116,8 +1010,28 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) map->flags |= GNTMAP_readonly; } + if (use_ptemod) { + map->vma = vma; + err = mmu_interval_notifier_insert_locked( + &map->notifier, vma->vm_mm, vma->vm_start, + vma->vm_end - vma->vm_start, &gntdev_mmu_ops); + if (err) + goto out_unlock_put; + } mutex_unlock(&priv->lock); + /* + * gntdev takes the address of the PTE in find_grant_ptes() and passes + * it to the hypervisor in gntdev_map_grant_pages(). The purpose of + * the notifier is to prevent the hypervisor pointer to the PTE from + * going stale. + * + * Since this vma's mappings can't be touched without the mmap_sem, + * and we are holding it now, there is no need for the notifier_range + * locking pattern. + */ + mmu_interval_read_begin(&map->notifier); + if (use_ptemod) { map->pages_vm_start = vma->vm_start; err = apply_to_page_range(vma->vm_mm, vma->vm_start, @@ -1166,8 +1080,11 @@ out_unlock_put: mutex_unlock(&priv->lock); out_put_map: if (use_ptemod) { - map->vma = NULL; unmap_grant_pages(map, 0, map->count); + if (map->vma) { + mmu_interval_notifier_remove(&map->notifier); + map->vma = NULL; + } } gntdev_put_map(priv, map); return err; |