1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Power Management and GPIO expander driver for MPC8349E-mITX-compatible MCU
4 *
5 * Copyright (c) 2008  MontaVista Software, Inc.
6 *
7 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/device.h>
13#include <linux/mutex.h>
14#include <linux/i2c.h>
15#include <linux/gpio/driver.h>
16#include <linux/of.h>
17#include <linux/of_gpio.h>
18#include <linux/slab.h>
19#include <linux/kthread.h>
20#include <linux/reboot.h>
21#include <asm/prom.h>
22#include <asm/machdep.h>
23
24/*
25 * I don't have specifications for the MCU firmware, I found this register
26 * and bits positions by the trial&error method.
27 */
28#define MCU_REG_CTRL	0x20
29#define MCU_CTRL_POFF	0x40
30#define MCU_CTRL_BTN	0x80
31
32#define MCU_NUM_GPIO	2
33
34struct mcu {
35	struct mutex lock;
36	struct i2c_client *client;
37	struct gpio_chip gc;
38	u8 reg_ctrl;
39};
40
41static struct mcu *glob_mcu;
42
43struct task_struct *shutdown_thread;
44static int shutdown_thread_fn(void *data)
45{
46	int ret;
47	struct mcu *mcu = glob_mcu;
48
49	while (!kthread_should_stop()) {
50		ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
51		if (ret < 0)
52			pr_err("MCU status reg read failed.\n");
53		mcu->reg_ctrl = ret;
54
55
56		if (mcu->reg_ctrl & MCU_CTRL_BTN) {
57			i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL,
58						  mcu->reg_ctrl & ~MCU_CTRL_BTN);
59
60			ctrl_alt_del();
61		}
62
63		set_current_state(TASK_INTERRUPTIBLE);
64		schedule_timeout(HZ);
65	}
66
67	return 0;
68}
69
70static ssize_t show_status(struct device *d,
71			   struct device_attribute *attr, char *buf)
72{
73	int ret;
74	struct mcu *mcu = glob_mcu;
75
76	ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
77	if (ret < 0)
78		return -ENODEV;
79	mcu->reg_ctrl = ret;
80
81	return sprintf(buf, "%02x\n", ret);
82}
83static DEVICE_ATTR(status, 0444, show_status, NULL);
84
85static void mcu_power_off(void)
86{
87	struct mcu *mcu = glob_mcu;
88
89	pr_info("Sending power-off request to the MCU...\n");
90	mutex_lock(&mcu->lock);
91	i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL,
92				  mcu->reg_ctrl | MCU_CTRL_POFF);
93	mutex_unlock(&mcu->lock);
94}
95
96static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
97{
98	struct mcu *mcu = gpiochip_get_data(gc);
99	u8 bit = 1 << (4 + gpio);
100
101	mutex_lock(&mcu->lock);
102	if (val)
103		mcu->reg_ctrl &= ~bit;
104	else
105		mcu->reg_ctrl |= bit;
106
107	i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl);
108	mutex_unlock(&mcu->lock);
109}
110
111static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
112{
113	mcu_gpio_set(gc, gpio, val);
114	return 0;
115}
116
117static int mcu_gpiochip_add(struct mcu *mcu)
118{
119	struct device_node *np;
120	struct gpio_chip *gc = &mcu->gc;
121
122	np = of_find_compatible_node(NULL, NULL, "fsl,mcu-mpc8349emitx");
123	if (!np)
124		return -ENODEV;
125
126	gc->owner = THIS_MODULE;
127	gc->label = kasprintf(GFP_KERNEL, "%pOF", np);
128	gc->can_sleep = 1;
129	gc->ngpio = MCU_NUM_GPIO;
130	gc->base = -1;
131	gc->set = mcu_gpio_set;
132	gc->direction_output = mcu_gpio_dir_out;
133	gc->of_node = np;
134
135	return gpiochip_add_data(gc, mcu);
136}
137
138static int mcu_gpiochip_remove(struct mcu *mcu)
139{
140	kfree(mcu->gc.label);
141	gpiochip_remove(&mcu->gc);
142	return 0;
143}
144
145static int mcu_probe(struct i2c_client *client)
146{
147	struct mcu *mcu;
148	int ret;
149
150	mcu = kzalloc(sizeof(*mcu), GFP_KERNEL);
151	if (!mcu)
152		return -ENOMEM;
153
154	mutex_init(&mcu->lock);
155	mcu->client = client;
156	i2c_set_clientdata(client, mcu);
157
158	ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
159	if (ret < 0)
160		goto err;
161	mcu->reg_ctrl = ret;
162
163	ret = mcu_gpiochip_add(mcu);
164	if (ret)
165		goto err;
166
167	/* XXX: this is potentially racy, but there is no lock for pm_power_off */
168	if (!pm_power_off) {
169		glob_mcu = mcu;
170		pm_power_off = mcu_power_off;
171		dev_info(&client->dev, "will provide power-off service\n");
172	}
173
174	if (device_create_file(&client->dev, &dev_attr_status))
175		dev_err(&client->dev,
176			"couldn't create device file for status\n");
177
178	shutdown_thread = kthread_run(shutdown_thread_fn, NULL,
179				      "mcu-i2c-shdn");
180
181	return 0;
182err:
183	kfree(mcu);
184	return ret;
185}
186
187static int mcu_remove(struct i2c_client *client)
188{
189	struct mcu *mcu = i2c_get_clientdata(client);
190	int ret;
191
192	kthread_stop(shutdown_thread);
193
194	device_remove_file(&client->dev, &dev_attr_status);
195
196	if (glob_mcu == mcu) {
197		pm_power_off = NULL;
198		glob_mcu = NULL;
199	}
200
201	ret = mcu_gpiochip_remove(mcu);
202	if (ret)
203		return ret;
204	kfree(mcu);
205	return 0;
206}
207
208static const struct i2c_device_id mcu_ids[] = {
209	{ "mcu-mpc8349emitx", },
210	{},
211};
212MODULE_DEVICE_TABLE(i2c, mcu_ids);
213
214static const struct of_device_id mcu_of_match_table[] = {
215	{ .compatible = "fsl,mcu-mpc8349emitx", },
216	{ },
217};
218
219static struct i2c_driver mcu_driver = {
220	.driver = {
221		.name = "mcu-mpc8349emitx",
222		.of_match_table = mcu_of_match_table,
223	},
224	.probe_new = mcu_probe,
225	.remove	= mcu_remove,
226	.id_table = mcu_ids,
227};
228
229module_i2c_driver(mcu_driver);
230
231MODULE_DESCRIPTION("Power Management and GPIO expander driver for "
232		   "MPC8349E-mITX-compatible MCU");
233MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
234MODULE_LICENSE("GPL");
235