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