| /* |
| * 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 = { |
| .owner = THIS_MODULE, |
| .name = "akita-max7310", |
| .id = I2C_DRIVERID_AKITAIOEXP, |
| .flags = I2C_DF_NOTIFY, |
| .attach_adapter = max7310_attach_adapter, |
| .detach_client = max7310_detach_client, |
| }; |
| |
| static struct i2c_client max7310_template = { |
| name: "akita-max7310", |
| flags: I2C_CLIENT_ALLOW_USE, |
| 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); |
| |