mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 08:26:29 +00:00 
			
		
		
		
	 6d5aefb8ea
			
		
	
	
		6d5aefb8ea
		
	
	
	
	
		
			
			Fix up arch-specific work items where possible to use the new work_struct and delayed_work structs. Three places that enqueue bits of their stack and then return have been marked with #error as this is not permitted. Signed-Off-By: David Howells <dhowells@redhat.com>
		
			
				
	
	
		
			223 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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(struct work_struct *private_);
 | |
| 
 | |
| static struct device *akita_ioexp_device;
 | |
| static unsigned char ioexp_output_value = AKITA_IOEXP_IO_OUT;
 | |
| DECLARE_WORK(akita_ioexp, akita_ioexp_work);
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * 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(struct work_struct *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);
 | |
| 
 |