|  | /* | 
|  | * Support for the Extra GPIOs on the Sharp SL-C1000 (Akita) | 
|  | * (uses a Maxim MAX7310 8 Port IO Expander) | 
|  | * | 
|  | * Copyright 2005 Openedhand Ltd. | 
|  | * | 
|  | * Author: Richard Purdie <richard@openedhand.com> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License version 2 as | 
|  | * published by the Free Software Foundation. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/i2c.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/workqueue.h> | 
|  | #include <asm/arch/akita.h> | 
|  |  | 
|  | /* MAX7310 Regiser Map */ | 
|  | #define MAX7310_INPUT    0x00 | 
|  | #define MAX7310_OUTPUT   0x01 | 
|  | #define MAX7310_POLINV   0x02 | 
|  | #define MAX7310_IODIR    0x03 /* 1 = Input, 0 = Output */ | 
|  | #define MAX7310_TIMEOUT  0x04 | 
|  |  | 
|  | /* Addresses to scan */ | 
|  | static unsigned short normal_i2c[] = { 0x18, I2C_CLIENT_END }; | 
|  |  | 
|  | /* I2C Magic */ | 
|  | I2C_CLIENT_INSMOD; | 
|  |  | 
|  | static int max7310_write(struct i2c_client *client, int address, int data); | 
|  | static struct i2c_client max7310_template; | 
|  | static void akita_ioexp_work(void *private_); | 
|  |  | 
|  | static struct device *akita_ioexp_device; | 
|  | static unsigned char ioexp_output_value = AKITA_IOEXP_IO_OUT; | 
|  | DECLARE_WORK(akita_ioexp, akita_ioexp_work, NULL); | 
|  |  | 
|  |  | 
|  | /* | 
|  | * MAX7310 Access | 
|  | */ | 
|  | static int max7310_config(struct device *dev, int iomode, int polarity) | 
|  | { | 
|  | int ret; | 
|  | struct i2c_client *client = to_i2c_client(dev); | 
|  |  | 
|  | ret = max7310_write(client, MAX7310_POLINV, polarity); | 
|  | if (ret < 0) | 
|  | return ret; | 
|  | ret = max7310_write(client, MAX7310_IODIR, iomode); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int max7310_set_ouputs(struct device *dev, int outputs) | 
|  | { | 
|  | struct i2c_client *client = to_i2c_client(dev); | 
|  |  | 
|  | return max7310_write(client, MAX7310_OUTPUT, outputs); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * I2C Functions | 
|  | */ | 
|  | static int max7310_write(struct i2c_client *client, int address, int value) | 
|  | { | 
|  | u8 data[2]; | 
|  |  | 
|  | data[0] = address & 0xff; | 
|  | data[1] = value & 0xff; | 
|  |  | 
|  | if (i2c_master_send(client, data, 2) == 2) | 
|  | return 0; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int max7310_detect(struct i2c_adapter *adapter, int address, int kind) | 
|  | { | 
|  | struct i2c_client *new_client; | 
|  | int err; | 
|  |  | 
|  | if (!(new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) | 
|  | return -ENOMEM; | 
|  |  | 
|  | max7310_template.adapter = adapter; | 
|  | max7310_template.addr = address; | 
|  |  | 
|  | memcpy(new_client, &max7310_template, sizeof(struct i2c_client)); | 
|  |  | 
|  | if ((err = i2c_attach_client(new_client))) { | 
|  | kfree(new_client); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | max7310_config(&new_client->dev, AKITA_IOEXP_IO_DIR, 0); | 
|  | akita_ioexp_device = &new_client->dev; | 
|  | schedule_work(&akita_ioexp); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int max7310_attach_adapter(struct i2c_adapter *adapter) | 
|  | { | 
|  | return i2c_probe(adapter, &addr_data, max7310_detect); | 
|  | } | 
|  |  | 
|  | static int max7310_detach_client(struct i2c_client *client) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | akita_ioexp_device = NULL; | 
|  |  | 
|  | if ((err = i2c_detach_client(client))) | 
|  | return err; | 
|  |  | 
|  | kfree(client); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct i2c_driver max7310_i2c_driver = { | 
|  | .driver = { | 
|  | .name	= "akita-max7310", | 
|  | }, | 
|  | .id		= I2C_DRIVERID_AKITAIOEXP, | 
|  | .attach_adapter	= max7310_attach_adapter, | 
|  | .detach_client	= max7310_detach_client, | 
|  | }; | 
|  |  | 
|  | static struct i2c_client max7310_template = { | 
|  | name:   "akita-max7310", | 
|  | driver: &max7310_i2c_driver, | 
|  | }; | 
|  |  | 
|  | void akita_set_ioexp(struct device *dev, unsigned char bit) | 
|  | { | 
|  | ioexp_output_value |= bit; | 
|  |  | 
|  | if (akita_ioexp_device) | 
|  | schedule_work(&akita_ioexp); | 
|  | return; | 
|  | } | 
|  |  | 
|  | void akita_reset_ioexp(struct device *dev, unsigned char bit) | 
|  | { | 
|  | ioexp_output_value &= ~bit; | 
|  |  | 
|  | if (akita_ioexp_device) | 
|  | schedule_work(&akita_ioexp); | 
|  | return; | 
|  | } | 
|  |  | 
|  | EXPORT_SYMBOL(akita_set_ioexp); | 
|  | EXPORT_SYMBOL(akita_reset_ioexp); | 
|  |  | 
|  | static void akita_ioexp_work(void *private_) | 
|  | { | 
|  | if (akita_ioexp_device) | 
|  | max7310_set_ouputs(akita_ioexp_device, ioexp_output_value); | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef CONFIG_PM | 
|  | static int akita_ioexp_suspend(struct platform_device *pdev, pm_message_t state) | 
|  | { | 
|  | flush_scheduled_work(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int akita_ioexp_resume(struct platform_device *pdev) | 
|  | { | 
|  | schedule_work(&akita_ioexp); | 
|  | return 0; | 
|  | } | 
|  | #else | 
|  | #define akita_ioexp_suspend NULL | 
|  | #define akita_ioexp_resume NULL | 
|  | #endif | 
|  |  | 
|  | static int __init akita_ioexp_probe(struct platform_device *pdev) | 
|  | { | 
|  | return i2c_add_driver(&max7310_i2c_driver); | 
|  | } | 
|  |  | 
|  | static int akita_ioexp_remove(struct platform_device *pdev) | 
|  | { | 
|  | i2c_del_driver(&max7310_i2c_driver); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct platform_driver akita_ioexp_driver = { | 
|  | .probe		= akita_ioexp_probe, | 
|  | .remove		= akita_ioexp_remove, | 
|  | .suspend	= akita_ioexp_suspend, | 
|  | .resume		= akita_ioexp_resume, | 
|  | .driver		= { | 
|  | .name	= "akita-ioexp", | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static int __init akita_ioexp_init(void) | 
|  | { | 
|  | return platform_driver_register(&akita_ioexp_driver); | 
|  | } | 
|  |  | 
|  | static void __exit akita_ioexp_exit(void) | 
|  | { | 
|  | platform_driver_unregister(&akita_ioexp_driver); | 
|  | } | 
|  |  | 
|  | MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); | 
|  | MODULE_DESCRIPTION("Akita IO-Expander driver"); | 
|  | MODULE_LICENSE("GPL"); | 
|  |  | 
|  | fs_initcall(akita_ioexp_init); | 
|  | module_exit(akita_ioexp_exit); | 
|  |  |