• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/powerpc/platforms/83xx/
1/*
2 * Power Management and GPIO expander driver for MPC8349E-mITX-compatible MCU
3 *
4 * Copyright (c) 2008  MontaVista Software, Inc.
5 *
6 * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/device.h>
18#include <linux/mutex.h>
19#include <linux/i2c.h>
20#include <linux/gpio.h>
21#include <linux/of.h>
22#include <linux/of_gpio.h>
23#include <linux/slab.h>
24#include <asm/prom.h>
25#include <asm/machdep.h>
26
27/*
28 * I don't have specifications for the MCU firmware, I found this register
29 * and bits positions by the trial&error method.
30 */
31#define MCU_REG_CTRL	0x20
32#define MCU_CTRL_POFF	0x40
33
34#define MCU_NUM_GPIO	2
35
36struct mcu {
37	struct mutex lock;
38	struct i2c_client *client;
39	struct gpio_chip gc;
40	u8 reg_ctrl;
41};
42
43static struct mcu *glob_mcu;
44
45static void mcu_power_off(void)
46{
47	struct mcu *mcu = glob_mcu;
48
49	pr_info("Sending power-off request to the MCU...\n");
50	mutex_lock(&mcu->lock);
51	i2c_smbus_write_byte_data(glob_mcu->client, MCU_REG_CTRL,
52				  mcu->reg_ctrl | MCU_CTRL_POFF);
53	mutex_unlock(&mcu->lock);
54}
55
56static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
57{
58	struct mcu *mcu = container_of(gc, struct mcu, gc);
59	u8 bit = 1 << (4 + gpio);
60
61	mutex_lock(&mcu->lock);
62	if (val)
63		mcu->reg_ctrl &= ~bit;
64	else
65		mcu->reg_ctrl |= bit;
66
67	i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl);
68	mutex_unlock(&mcu->lock);
69}
70
71static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
72{
73	mcu_gpio_set(gc, gpio, val);
74	return 0;
75}
76
77static int mcu_gpiochip_add(struct mcu *mcu)
78{
79	struct device_node *np;
80	struct gpio_chip *gc = &mcu->gc;
81
82	np = of_find_compatible_node(NULL, NULL, "fsl,mcu-mpc8349emitx");
83	if (!np)
84		return -ENODEV;
85
86	gc->owner = THIS_MODULE;
87	gc->label = np->full_name;
88	gc->can_sleep = 1;
89	gc->ngpio = MCU_NUM_GPIO;
90	gc->base = -1;
91	gc->set = mcu_gpio_set;
92	gc->direction_output = mcu_gpio_dir_out;
93	gc->of_node = np;
94
95	return gpiochip_add(gc);
96}
97
98static int mcu_gpiochip_remove(struct mcu *mcu)
99{
100	return gpiochip_remove(&mcu->gc);
101}
102
103static int __devinit mcu_probe(struct i2c_client *client,
104			       const struct i2c_device_id *id)
105{
106	struct mcu *mcu;
107	int ret;
108
109	mcu = kzalloc(sizeof(*mcu), GFP_KERNEL);
110	if (!mcu)
111		return -ENOMEM;
112
113	mutex_init(&mcu->lock);
114	mcu->client = client;
115	i2c_set_clientdata(client, mcu);
116
117	ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
118	if (ret < 0)
119		goto err;
120	mcu->reg_ctrl = ret;
121
122	ret = mcu_gpiochip_add(mcu);
123	if (ret)
124		goto err;
125
126	if (!ppc_md.power_off) {
127		glob_mcu = mcu;
128		ppc_md.power_off = mcu_power_off;
129		dev_info(&client->dev, "will provide power-off service\n");
130	}
131
132	return 0;
133err:
134	kfree(mcu);
135	return ret;
136}
137
138static int __devexit mcu_remove(struct i2c_client *client)
139{
140	struct mcu *mcu = i2c_get_clientdata(client);
141	int ret;
142
143	if (glob_mcu == mcu) {
144		ppc_md.power_off = NULL;
145		glob_mcu = NULL;
146	}
147
148	ret = mcu_gpiochip_remove(mcu);
149	if (ret)
150		return ret;
151	i2c_set_clientdata(client, NULL);
152	kfree(mcu);
153	return 0;
154}
155
156static const struct i2c_device_id mcu_ids[] = {
157	{ "mcu-mpc8349emitx", },
158	{},
159};
160MODULE_DEVICE_TABLE(i2c, mcu_ids);
161
162static struct of_device_id mcu_of_match_table[] __devinitdata = {
163	{ .compatible = "fsl,mcu-mpc8349emitx", },
164	{ },
165};
166
167static struct i2c_driver mcu_driver = {
168	.driver = {
169		.name = "mcu-mpc8349emitx",
170		.owner = THIS_MODULE,
171		.of_match_table = mcu_of_match_table,
172	},
173	.probe = mcu_probe,
174	.remove	= __devexit_p(mcu_remove),
175	.id_table = mcu_ids,
176};
177
178static int __init mcu_init(void)
179{
180	return i2c_add_driver(&mcu_driver);
181}
182module_init(mcu_init);
183
184static void __exit mcu_exit(void)
185{
186	i2c_del_driver(&mcu_driver);
187}
188module_exit(mcu_exit);
189
190MODULE_DESCRIPTION("Power Management and GPIO expander driver for "
191		   "MPC8349E-mITX-compatible MCU");
192MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
193MODULE_LICENSE("GPL");
194