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