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