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 <asm/prom.h>
  24#include <asm/machdep.h>
  25
  26/*
  27 * I don't have specifications for the MCU firmware, I found this register
  28 * and bits positions by the trial&error method.
  29 */
  30#define MCU_REG_CTRL    0x20
  31#define MCU_CTRL_POFF   0x40
  32
  33#define MCU_NUM_GPIO    2
  34
  35struct mcu {
  36        struct mutex lock;
  37        struct device_node *np;
  38        struct i2c_client *client;
  39        struct of_gpio_chip of_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 of_gpio_chip *of_gc = to_of_gpio_chip(gc);
  59        struct mcu *mcu = container_of(of_gc, struct mcu, of_gc);
  60        u8 bit = 1 << (4 + gpio);
  61
  62        mutex_lock(&mcu->lock);
  63        if (val)
  64                mcu->reg_ctrl &= ~bit;
  65        else
  66                mcu->reg_ctrl |= bit;
  67
  68        i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl);
  69        mutex_unlock(&mcu->lock);
  70}
  71
  72static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
  73{
  74        mcu_gpio_set(gc, gpio, val);
  75        return 0;
  76}
  77
  78static int mcu_gpiochip_add(struct mcu *mcu)
  79{
  80        struct device_node *np;
  81        struct of_gpio_chip *of_gc = &mcu->of_gc;
  82        struct gpio_chip *gc = &of_gc->gc;
  83        int ret;
  84
  85        np = of_find_compatible_node(NULL, NULL, "fsl,mcu-mpc8349emitx");
  86        if (!np)
  87                return -ENODEV;
  88
  89        gc->owner = THIS_MODULE;
  90        gc->label = np->full_name;
  91        gc->can_sleep = 1;
  92        gc->ngpio = MCU_NUM_GPIO;
  93        gc->base = -1;
  94        gc->set = mcu_gpio_set;
  95        gc->direction_output = mcu_gpio_dir_out;
  96        of_gc->gpio_cells = 2;
  97        of_gc->xlate = of_gpio_simple_xlate;
  98
  99        np->data = of_gc;
 100        mcu->np = np;
 101
 102        /*
 103         * We don't want to lose the node, its ->data and ->full_name...
 104         * So, if succeeded, we don't put the node here.
 105         */
 106        ret = gpiochip_add(gc);
 107        if (ret)
 108                of_node_put(np);
 109        return ret;
 110}
 111
 112static int mcu_gpiochip_remove(struct mcu *mcu)
 113{
 114        int ret;
 115
 116        ret = gpiochip_remove(&mcu->of_gc.gc);
 117        if (ret)
 118                return ret;
 119        of_node_put(mcu->np);
 120
 121        return 0;
 122}
 123
 124static int __devinit mcu_probe(struct i2c_client *client,
 125                               const struct i2c_device_id *id)
 126{
 127        struct mcu *mcu;
 128        int ret;
 129
 130        mcu = kzalloc(sizeof(*mcu), GFP_KERNEL);
 131        if (!mcu)
 132                return -ENOMEM;
 133
 134        mutex_init(&mcu->lock);
 135        mcu->client = client;
 136        i2c_set_clientdata(client, mcu);
 137
 138        ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
 139        if (ret < 0)
 140                goto err;
 141        mcu->reg_ctrl = ret;
 142
 143        ret = mcu_gpiochip_add(mcu);
 144        if (ret)
 145                goto err;
 146
 147        /* XXX: this is potentially racy, but there is no lock for ppc_md */
 148        if (!ppc_md.power_off) {
 149                glob_mcu = mcu;
 150                ppc_md.power_off = mcu_power_off;
 151                dev_info(&client->dev, "will provide power-off service\n");
 152        }
 153
 154        return 0;
 155err:
 156        kfree(mcu);
 157        return ret;
 158}
 159
 160static int __devexit mcu_remove(struct i2c_client *client)
 161{
 162        struct mcu *mcu = i2c_get_clientdata(client);
 163        int ret;
 164
 165        if (glob_mcu == mcu) {
 166                ppc_md.power_off = NULL;
 167                glob_mcu = NULL;
 168        }
 169
 170        ret = mcu_gpiochip_remove(mcu);
 171        if (ret)
 172                return ret;
 173        i2c_set_clientdata(client, NULL);
 174        kfree(mcu);
 175        return 0;
 176}
 177
 178static const struct i2c_device_id mcu_ids[] = {
 179        { "mcu-mpc8349emitx", },
 180        {},
 181};
 182MODULE_DEVICE_TABLE(i2c, mcu_ids);
 183
 184static struct i2c_driver mcu_driver = {
 185        .driver = {
 186                .name = "mcu-mpc8349emitx",
 187                .owner = THIS_MODULE,
 188        },
 189        .probe = mcu_probe,
 190        .remove = __devexit_p(mcu_remove),
 191        .id_table = mcu_ids,
 192};
 193
 194static int __init mcu_init(void)
 195{
 196        return i2c_add_driver(&mcu_driver);
 197}
 198module_init(mcu_init);
 199
 200static void __exit mcu_exit(void)
 201{
 202        i2c_del_driver(&mcu_driver);
 203}
 204module_exit(mcu_exit);
 205
 206MODULE_DESCRIPTION("Power Management and GPIO expander driver for "
 207                   "MPC8349E-mITX-compatible MCU");
 208MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
 209MODULE_LICENSE("GPL");
 210