| Russell King | 6b65cd7 | 2006-12-10 21:21:32 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | *  i2c-versatile.c | 
|  | 3 | * | 
|  | 4 | *  Copyright (C) 2006 ARM Ltd. | 
|  | 5 | *  written by Russell King, Deep Blue Solutions Ltd. | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. | 
|  | 10 | */ | 
|  | 11 | #include <linux/kernel.h> | 
|  | 12 | #include <linux/module.h> | 
|  | 13 | #include <linux/i2c.h> | 
|  | 14 | #include <linux/i2c-algo-bit.h> | 
|  | 15 | #include <linux/init.h> | 
|  | 16 | #include <linux/platform_device.h> | 
|  | 17 |  | 
|  | 18 | #include <asm/io.h> | 
|  | 19 |  | 
|  | 20 | #define I2C_CONTROL	0x00 | 
|  | 21 | #define I2C_CONTROLS	0x00 | 
|  | 22 | #define I2C_CONTROLC	0x04 | 
|  | 23 | #define SCL		(1 << 0) | 
|  | 24 | #define SDA		(1 << 1) | 
|  | 25 |  | 
|  | 26 | struct i2c_versatile { | 
|  | 27 | struct i2c_adapter	 adap; | 
|  | 28 | struct i2c_algo_bit_data algo; | 
|  | 29 | void __iomem		 *base; | 
|  | 30 | }; | 
|  | 31 |  | 
|  | 32 | static void i2c_versatile_setsda(void *data, int state) | 
|  | 33 | { | 
|  | 34 | struct i2c_versatile *i2c = data; | 
|  | 35 |  | 
|  | 36 | writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); | 
|  | 37 | } | 
|  | 38 |  | 
|  | 39 | static void i2c_versatile_setscl(void *data, int state) | 
|  | 40 | { | 
|  | 41 | struct i2c_versatile *i2c = data; | 
|  | 42 |  | 
|  | 43 | writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); | 
|  | 44 | } | 
|  | 45 |  | 
|  | 46 | static int i2c_versatile_getsda(void *data) | 
|  | 47 | { | 
|  | 48 | struct i2c_versatile *i2c = data; | 
|  | 49 | return !!(readl(i2c->base + I2C_CONTROL) & SDA); | 
|  | 50 | } | 
|  | 51 |  | 
|  | 52 | static int i2c_versatile_getscl(void *data) | 
|  | 53 | { | 
|  | 54 | struct i2c_versatile *i2c = data; | 
|  | 55 | return !!(readl(i2c->base + I2C_CONTROL) & SCL); | 
|  | 56 | } | 
|  | 57 |  | 
|  | 58 | static struct i2c_algo_bit_data i2c_versatile_algo = { | 
|  | 59 | .setsda	= i2c_versatile_setsda, | 
|  | 60 | .setscl = i2c_versatile_setscl, | 
|  | 61 | .getsda	= i2c_versatile_getsda, | 
|  | 62 | .getscl = i2c_versatile_getscl, | 
|  | 63 | .udelay	= 30, | 
|  | 64 | .timeout = HZ, | 
|  | 65 | }; | 
|  | 66 |  | 
|  | 67 | static int i2c_versatile_probe(struct platform_device *dev) | 
|  | 68 | { | 
|  | 69 | struct i2c_versatile *i2c; | 
|  | 70 | struct resource *r; | 
|  | 71 | int ret; | 
|  | 72 |  | 
|  | 73 | r = platform_get_resource(dev, IORESOURCE_MEM, 0); | 
|  | 74 | if (!r) { | 
|  | 75 | ret = -EINVAL; | 
|  | 76 | goto err_out; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | if (!request_mem_region(r->start, r->end - r->start + 1, "versatile-i2c")) { | 
|  | 80 | ret = -EBUSY; | 
|  | 81 | goto err_out; | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL); | 
|  | 85 | if (!i2c) { | 
|  | 86 | ret = -ENOMEM; | 
|  | 87 | goto err_release; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | i2c->base = ioremap(r->start, r->end - r->start + 1); | 
|  | 91 | if (!i2c->base) { | 
|  | 92 | ret = -ENOMEM; | 
|  | 93 | goto err_free; | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | writel(SCL | SDA, i2c->base + I2C_CONTROLS); | 
|  | 97 |  | 
|  | 98 | i2c->adap.owner = THIS_MODULE; | 
|  | 99 | strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); | 
|  | 100 | i2c->adap.algo_data = &i2c->algo; | 
|  | 101 | i2c->adap.dev.parent = &dev->dev; | 
|  | 102 | i2c->algo = i2c_versatile_algo; | 
|  | 103 | i2c->algo.data = i2c; | 
|  | 104 |  | 
| Catalin Marinas | 2514cca | 2009-02-12 15:57:20 +0100 | [diff] [blame] | 105 | if (dev->id >= 0) { | 
|  | 106 | /* static bus numbering */ | 
|  | 107 | i2c->adap.nr = dev->id; | 
|  | 108 | ret = i2c_bit_add_numbered_bus(&i2c->adap); | 
|  | 109 | } else | 
|  | 110 | /* dynamic bus numbering */ | 
|  | 111 | ret = i2c_bit_add_bus(&i2c->adap); | 
| Russell King | 6b65cd7 | 2006-12-10 21:21:32 +0100 | [diff] [blame] | 112 | if (ret >= 0) { | 
|  | 113 | platform_set_drvdata(dev, i2c); | 
|  | 114 | return 0; | 
|  | 115 | } | 
|  | 116 |  | 
|  | 117 | iounmap(i2c->base); | 
|  | 118 | err_free: | 
|  | 119 | kfree(i2c); | 
|  | 120 | err_release: | 
|  | 121 | release_mem_region(r->start, r->end - r->start + 1); | 
|  | 122 | err_out: | 
|  | 123 | return ret; | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | static int i2c_versatile_remove(struct platform_device *dev) | 
|  | 127 | { | 
|  | 128 | struct i2c_versatile *i2c = platform_get_drvdata(dev); | 
|  | 129 |  | 
|  | 130 | platform_set_drvdata(dev, NULL); | 
|  | 131 |  | 
|  | 132 | i2c_del_adapter(&i2c->adap); | 
|  | 133 | return 0; | 
|  | 134 | } | 
|  | 135 |  | 
|  | 136 | static struct platform_driver i2c_versatile_driver = { | 
|  | 137 | .probe		= i2c_versatile_probe, | 
|  | 138 | .remove		= i2c_versatile_remove, | 
|  | 139 | .driver		= { | 
|  | 140 | .name	= "versatile-i2c", | 
|  | 141 | .owner	= THIS_MODULE, | 
|  | 142 | }, | 
|  | 143 | }; | 
|  | 144 |  | 
|  | 145 | static int __init i2c_versatile_init(void) | 
|  | 146 | { | 
|  | 147 | return platform_driver_register(&i2c_versatile_driver); | 
|  | 148 | } | 
|  | 149 |  | 
|  | 150 | static void __exit i2c_versatile_exit(void) | 
|  | 151 | { | 
|  | 152 | platform_driver_unregister(&i2c_versatile_driver); | 
|  | 153 | } | 
|  | 154 |  | 
| Catalin Marinas | 2514cca | 2009-02-12 15:57:20 +0100 | [diff] [blame] | 155 | subsys_initcall(i2c_versatile_init); | 
| Russell King | 6b65cd7 | 2006-12-10 21:21:32 +0100 | [diff] [blame] | 156 | module_exit(i2c_versatile_exit); | 
|  | 157 |  | 
|  | 158 | MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); | 
|  | 159 | MODULE_LICENSE("GPL"); | 
| Kay Sievers | add8eda | 2008-04-22 22:16:49 +0200 | [diff] [blame] | 160 | MODULE_ALIAS("platform:versatile-i2c"); |