Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Power Management and GPIO expander driver for MPC8349E-mITX-compatible MCU
0004  *
0005  * Copyright (c) 2008  MontaVista Software, Inc.
0006  *
0007  * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/mod_devicetable.h>
0012 #include <linux/module.h>
0013 #include <linux/device.h>
0014 #include <linux/mutex.h>
0015 #include <linux/i2c.h>
0016 #include <linux/gpio/driver.h>
0017 #include <linux/slab.h>
0018 #include <linux/kthread.h>
0019 #include <linux/property.h>
0020 #include <linux/reboot.h>
0021 #include <asm/machdep.h>
0022 
0023 /*
0024  * I don't have specifications for the MCU firmware, I found this register
0025  * and bits positions by the trial&error method.
0026  */
0027 #define MCU_REG_CTRL    0x20
0028 #define MCU_CTRL_POFF   0x40
0029 #define MCU_CTRL_BTN    0x80
0030 
0031 #define MCU_NUM_GPIO    2
0032 
0033 struct mcu {
0034     struct mutex lock;
0035     struct i2c_client *client;
0036     struct gpio_chip gc;
0037     u8 reg_ctrl;
0038 };
0039 
0040 static struct mcu *glob_mcu;
0041 
0042 struct task_struct *shutdown_thread;
0043 static int shutdown_thread_fn(void *data)
0044 {
0045     int ret;
0046     struct mcu *mcu = glob_mcu;
0047 
0048     while (!kthread_should_stop()) {
0049         ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
0050         if (ret < 0)
0051             pr_err("MCU status reg read failed.\n");
0052         mcu->reg_ctrl = ret;
0053 
0054 
0055         if (mcu->reg_ctrl & MCU_CTRL_BTN) {
0056             i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL,
0057                           mcu->reg_ctrl & ~MCU_CTRL_BTN);
0058 
0059             ctrl_alt_del();
0060         }
0061 
0062         set_current_state(TASK_INTERRUPTIBLE);
0063         schedule_timeout(HZ);
0064     }
0065 
0066     return 0;
0067 }
0068 
0069 static ssize_t show_status(struct device *d,
0070                struct device_attribute *attr, char *buf)
0071 {
0072     int ret;
0073     struct mcu *mcu = glob_mcu;
0074 
0075     ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
0076     if (ret < 0)
0077         return -ENODEV;
0078     mcu->reg_ctrl = ret;
0079 
0080     return sprintf(buf, "%02x\n", ret);
0081 }
0082 static DEVICE_ATTR(status, 0444, show_status, NULL);
0083 
0084 static void mcu_power_off(void)
0085 {
0086     struct mcu *mcu = glob_mcu;
0087 
0088     pr_info("Sending power-off request to the MCU...\n");
0089     mutex_lock(&mcu->lock);
0090     i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL,
0091                   mcu->reg_ctrl | MCU_CTRL_POFF);
0092     mutex_unlock(&mcu->lock);
0093 }
0094 
0095 static void mcu_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
0096 {
0097     struct mcu *mcu = gpiochip_get_data(gc);
0098     u8 bit = 1 << (4 + gpio);
0099 
0100     mutex_lock(&mcu->lock);
0101     if (val)
0102         mcu->reg_ctrl &= ~bit;
0103     else
0104         mcu->reg_ctrl |= bit;
0105 
0106     i2c_smbus_write_byte_data(mcu->client, MCU_REG_CTRL, mcu->reg_ctrl);
0107     mutex_unlock(&mcu->lock);
0108 }
0109 
0110 static int mcu_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
0111 {
0112     mcu_gpio_set(gc, gpio, val);
0113     return 0;
0114 }
0115 
0116 static int mcu_gpiochip_add(struct mcu *mcu)
0117 {
0118     struct device *dev = &mcu->client->dev;
0119     struct gpio_chip *gc = &mcu->gc;
0120 
0121     gc->owner = THIS_MODULE;
0122     gc->label = kasprintf(GFP_KERNEL, "%pfw", dev_fwnode(dev));
0123     gc->can_sleep = 1;
0124     gc->ngpio = MCU_NUM_GPIO;
0125     gc->base = -1;
0126     gc->set = mcu_gpio_set;
0127     gc->direction_output = mcu_gpio_dir_out;
0128     gc->parent = dev;
0129 
0130     return gpiochip_add_data(gc, mcu);
0131 }
0132 
0133 static void mcu_gpiochip_remove(struct mcu *mcu)
0134 {
0135     kfree(mcu->gc.label);
0136     gpiochip_remove(&mcu->gc);
0137 }
0138 
0139 static int mcu_probe(struct i2c_client *client)
0140 {
0141     struct mcu *mcu;
0142     int ret;
0143 
0144     mcu = kzalloc(sizeof(*mcu), GFP_KERNEL);
0145     if (!mcu)
0146         return -ENOMEM;
0147 
0148     mutex_init(&mcu->lock);
0149     mcu->client = client;
0150     i2c_set_clientdata(client, mcu);
0151 
0152     ret = i2c_smbus_read_byte_data(mcu->client, MCU_REG_CTRL);
0153     if (ret < 0)
0154         goto err;
0155     mcu->reg_ctrl = ret;
0156 
0157     ret = mcu_gpiochip_add(mcu);
0158     if (ret)
0159         goto err;
0160 
0161     /* XXX: this is potentially racy, but there is no lock for pm_power_off */
0162     if (!pm_power_off) {
0163         glob_mcu = mcu;
0164         pm_power_off = mcu_power_off;
0165         dev_info(&client->dev, "will provide power-off service\n");
0166     }
0167 
0168     if (device_create_file(&client->dev, &dev_attr_status))
0169         dev_err(&client->dev,
0170             "couldn't create device file for status\n");
0171 
0172     shutdown_thread = kthread_run(shutdown_thread_fn, NULL,
0173                       "mcu-i2c-shdn");
0174 
0175     return 0;
0176 err:
0177     kfree(mcu);
0178     return ret;
0179 }
0180 
0181 static int mcu_remove(struct i2c_client *client)
0182 {
0183     struct mcu *mcu = i2c_get_clientdata(client);
0184 
0185     kthread_stop(shutdown_thread);
0186 
0187     device_remove_file(&client->dev, &dev_attr_status);
0188 
0189     if (glob_mcu == mcu) {
0190         pm_power_off = NULL;
0191         glob_mcu = NULL;
0192     }
0193 
0194     mcu_gpiochip_remove(mcu);
0195     kfree(mcu);
0196     return 0;
0197 }
0198 
0199 static const struct i2c_device_id mcu_ids[] = {
0200     { "mcu-mpc8349emitx", },
0201     {},
0202 };
0203 MODULE_DEVICE_TABLE(i2c, mcu_ids);
0204 
0205 static const struct of_device_id mcu_of_match_table[] = {
0206     { .compatible = "fsl,mcu-mpc8349emitx", },
0207     { },
0208 };
0209 
0210 static struct i2c_driver mcu_driver = {
0211     .driver = {
0212         .name = "mcu-mpc8349emitx",
0213         .of_match_table = mcu_of_match_table,
0214     },
0215     .probe_new = mcu_probe,
0216     .remove = mcu_remove,
0217     .id_table = mcu_ids,
0218 };
0219 
0220 module_i2c_driver(mcu_driver);
0221 
0222 MODULE_DESCRIPTION("Power Management and GPIO expander driver for "
0223            "MPC8349E-mITX-compatible MCU");
0224 MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
0225 MODULE_LICENSE("GPL");