Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: MPC8349E-mITX Power Management and GPIO expander driver

On MPC8349E-mITX, MPC8315E-RDB and MPC837x-RDB boards there is a
Freescale MC9S08QG8 (MCU) chip with the custom firmware
pre-programmed. The chip is used to power-off the board by the
software, and to control some GPIO pins.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>

authored by

Anton Vorontsov and committed by
Kumar Gala
d6c3db83 7de0c22b

+221
+11
drivers/i2c/chips/Kconfig
··· 172 172 and other features that are often used in portable devices like 173 173 cell phones and PDAs. 174 174 175 + config MCU_MPC8349EMITX 176 + tristate "MPC8349E-mITX MCU driver" 177 + depends on I2C && PPC_83xx 178 + select GENERIC_GPIO 179 + select ARCH_REQUIRE_GPIOLIB 180 + help 181 + Say Y here to enable soft power-off functionality on the Freescale 182 + boards with the MPC8349E-mITX-compatible MCU chips. This driver will 183 + also register MCU GPIOs with the generic GPIO API, so you'll able 184 + to use MCU pins as GPIOs. 185 + 175 186 endmenu
+1
drivers/i2c/chips/Makefile
··· 21 21 obj-$(CONFIG_TPS65010) += tps65010.o 22 22 obj-$(CONFIG_MENELAUS) += menelaus.o 23 23 obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o 24 + obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o 24 25 25 26 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) 26 27 EXTRA_CFLAGS += -DDEBUG
+209
drivers/i2c/chips/mcu_mpc8349emitx.c
··· 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 + 35 + struct 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 + 43 + static struct mcu *glob_mcu; 44 + 45 + static 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 + 56 + static 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 + 72 + static 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 + 78 + static 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 + 112 + static 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 + 124 + static 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; 155 + err: 156 + kfree(mcu); 157 + return ret; 158 + } 159 + 160 + static 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 + 178 + static const struct i2c_device_id mcu_ids[] = { 179 + { "mcu-mpc8349emitx", }, 180 + {}, 181 + }; 182 + MODULE_DEVICE_TABLE(i2c, mcu_ids); 183 + 184 + static 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 + 194 + static int __init mcu_init(void) 195 + { 196 + return i2c_add_driver(&mcu_driver); 197 + } 198 + module_init(mcu_init); 199 + 200 + static void __exit mcu_exit(void) 201 + { 202 + i2c_del_driver(&mcu_driver); 203 + } 204 + module_exit(mcu_exit); 205 + 206 + MODULE_DESCRIPTION("Power Management and GPIO expander driver for " 207 + "MPC8349E-mITX-compatible MCU"); 208 + MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); 209 + MODULE_LICENSE("GPL");