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 #include <linux/slab.h> 18 #include <linux/io.h> 19 #include <linux/of_i2c.h> 20 21 #define I2C_CONTROL 0x00 22 #define I2C_CONTROLS 0x00 23 #define I2C_CONTROLC 0x04 24 #define SCL (1 << 0) 25 #define SDA (1 << 1) 26 27 struct i2c_versatile { 28 struct i2c_adapter adap; 29 struct i2c_algo_bit_data algo; 30 void __iomem *base; 31 }; 32 33 static void i2c_versatile_setsda(void *data, int state) 34 { 35 struct i2c_versatile *i2c = data; 36 37 writel(SDA, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); 38 } 39 40 static void i2c_versatile_setscl(void *data, int state) 41 { 42 struct i2c_versatile *i2c = data; 43 44 writel(SCL, i2c->base + (state ? I2C_CONTROLS : I2C_CONTROLC)); 45 } 46 47 static int i2c_versatile_getsda(void *data) 48 { 49 struct i2c_versatile *i2c = data; 50 return !!(readl(i2c->base + I2C_CONTROL) & SDA); 51 } 52 53 static int i2c_versatile_getscl(void *data) 54 { 55 struct i2c_versatile *i2c = data; 56 return !!(readl(i2c->base + I2C_CONTROL) & SCL); 57 } 58 59 static struct i2c_algo_bit_data i2c_versatile_algo = { 60 .setsda = i2c_versatile_setsda, 61 .setscl = i2c_versatile_setscl, 62 .getsda = i2c_versatile_getsda, 63 .getscl = i2c_versatile_getscl, 64 .udelay = 30, 65 .timeout = HZ, 66 }; 67 68 static int i2c_versatile_probe(struct platform_device *dev) 69 { 70 struct i2c_versatile *i2c; 71 struct resource *r; 72 int ret; 73 74 r = platform_get_resource(dev, IORESOURCE_MEM, 0); 75 if (!r) { 76 ret = -EINVAL; 77 goto err_out; 78 } 79 80 if (!request_mem_region(r->start, resource_size(r), "versatile-i2c")) { 81 ret = -EBUSY; 82 goto err_out; 83 } 84 85 i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL); 86 if (!i2c) { 87 ret = -ENOMEM; 88 goto err_release; 89 } 90 91 i2c->base = ioremap(r->start, resource_size(r)); 92 if (!i2c->base) { 93 ret = -ENOMEM; 94 goto err_free; 95 } 96 97 writel(SCL | SDA, i2c->base + I2C_CONTROLS); 98 99 i2c->adap.owner = THIS_MODULE; 100 strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name)); 101 i2c->adap.algo_data = &i2c->algo; 102 i2c->adap.dev.parent = &dev->dev; 103 i2c->adap.dev.of_node = dev->dev.of_node; 104 i2c->algo = i2c_versatile_algo; 105 i2c->algo.data = i2c; 106 107 i2c->adap.nr = dev->id; 108 ret = i2c_bit_add_numbered_bus(&i2c->adap); 109 if (ret >= 0) { 110 platform_set_drvdata(dev, i2c); 111 of_i2c_register_devices(&i2c->adap); 112 return 0; 113 } 114 115 iounmap(i2c->base); 116 err_free: 117 kfree(i2c); 118 err_release: 119 release_mem_region(r->start, resource_size(r)); 120 err_out: 121 return ret; 122 } 123 124 static int i2c_versatile_remove(struct platform_device *dev) 125 { 126 struct i2c_versatile *i2c = platform_get_drvdata(dev); 127 128 i2c_del_adapter(&i2c->adap); 129 return 0; 130 } 131 132 static const struct of_device_id i2c_versatile_match[] = { 133 { .compatible = "arm,versatile-i2c", }, 134 {}, 135 }; 136 MODULE_DEVICE_TABLE(of, i2c_versatile_match); 137 138 static struct platform_driver i2c_versatile_driver = { 139 .probe = i2c_versatile_probe, 140 .remove = i2c_versatile_remove, 141 .driver = { 142 .name = "versatile-i2c", 143 .owner = THIS_MODULE, 144 .of_match_table = i2c_versatile_match, 145 }, 146 }; 147 148 static int __init i2c_versatile_init(void) 149 { 150 return platform_driver_register(&i2c_versatile_driver); 151 } 152 153 static void __exit i2c_versatile_exit(void) 154 { 155 platform_driver_unregister(&i2c_versatile_driver); 156 } 157 158 subsys_initcall(i2c_versatile_init); 159 module_exit(i2c_versatile_exit); 160 161 MODULE_DESCRIPTION("ARM Versatile I2C bus driver"); 162 MODULE_LICENSE("GPL"); 163 MODULE_ALIAS("platform:versatile-i2c"); 164