Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2023 Renesas Electronics Corporation */ #include <linux/delay.h> #include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/reboot.h> #define PWC_PWCRST 0x00 #define PWC_PWCCKEN 0x04 #define PWC_PWCCTL 0x50 #define PWC_GPIO 0x80 #define PWC_PWCRST_RSTSOFTAX 0x1 #define PWC_PWCCKEN_ENGCKMAIN 0x1 #define PWC_PWCCTL_PWOFF 0x1 struct rzv2m_pwc_priv { void __iomem *base; struct device *dev; struct gpio_chip gp; DECLARE_BITMAP(ch_en_bits, 2); }; static void rzv2m_pwc_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct rzv2m_pwc_priv *priv = gpiochip_get_data(chip); u32 reg; /* BIT 16 enables write to BIT 0, and BIT 17 enables write to BIT 1 */ reg = BIT(offset + 16); if (value) reg |= BIT(offset); writel(reg, priv->base + PWC_GPIO); assign_bit(offset, priv->ch_en_bits, value); } static int rzv2m_pwc_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct rzv2m_pwc_priv *priv = gpiochip_get_data(chip); return test_bit(offset, priv->ch_en_bits); } static int rzv2m_pwc_gpio_direction_output(struct gpio_chip *gc, unsigned int nr, int value) { if (nr > 1) return -EINVAL; rzv2m_pwc_gpio_set(gc, nr, value); return 0; } static const struct gpio_chip rzv2m_pwc_gc = { .label = "gpio_rzv2m_pwc", .owner = THIS_MODULE, .get = rzv2m_pwc_gpio_get, .set = rzv2m_pwc_gpio_set, .direction_output = rzv2m_pwc_gpio_direction_output, .can_sleep = false, .ngpio = 2, .base = -1, }; static int rzv2m_pwc_poweroff(struct sys_off_data *data) { struct rzv2m_pwc_priv *priv = data->cb_data; writel(PWC_PWCRST_RSTSOFTAX, priv->base + PWC_PWCRST); writel(PWC_PWCCKEN_ENGCKMAIN, priv->base + PWC_PWCCKEN); writel(PWC_PWCCTL_PWOFF, priv->base + PWC_PWCCTL); mdelay(150); dev_err(priv->dev, "Failed to power off the system"); return NOTIFY_DONE; } static int rzv2m_pwc_probe(struct platform_device *pdev) { struct rzv2m_pwc_priv *priv; int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); /* * The register used by this driver cannot be read, therefore set the * outputs to their default values and initialize priv->ch_en_bits * accordingly. BIT 16 enables write to BIT 0, BIT 17 enables write to * BIT 1, and the default value of both BIT 0 and BIT 1 is 0. */ writel(BIT(17) | BIT(16), priv->base + PWC_GPIO); bitmap_zero(priv->ch_en_bits, 2); priv->gp = rzv2m_pwc_gc; priv->gp.parent = pdev->dev.parent; priv->gp.fwnode = dev_fwnode(&pdev->dev); ret = devm_gpiochip_add_data(&pdev->dev, &priv->gp, priv); if (ret) return ret; if (device_property_read_bool(&pdev->dev, "renesas,rzv2m-pwc-power")) ret = devm_register_power_off_handler(&pdev->dev, rzv2m_pwc_poweroff, priv); return ret; } static const struct of_device_id rzv2m_pwc_of_match[] = { { .compatible = "renesas,rzv2m-pwc" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzv2m_pwc_of_match); static struct platform_driver rzv2m_pwc_driver = { .probe = rzv2m_pwc_probe, .driver = { .name = "rzv2m_pwc", .of_match_table = rzv2m_pwc_of_match, }, }; module_platform_driver(rzv2m_pwc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Fabrizio Castro <castro.fabrizio.jz@renesas.com>"); MODULE_DESCRIPTION("Renesas RZ/V2M PWC driver"); |