Merge branch 'i2c/for-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-microblaze.git] / drivers / i2c / busses / i2c-simtec.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2005 Simtec Electronics
4  *      Ben Dooks <ben@simtec.co.uk>
5  *
6  * Simtec Generic I2C Controller
7 */
8
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/delay.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
14 #include <linux/io.h>
15
16 #include <linux/i2c.h>
17 #include <linux/i2c-algo-bit.h>
18
19 struct simtec_i2c_data {
20         struct resource         *ioarea;
21         void __iomem            *reg;
22         struct i2c_adapter       adap;
23         struct i2c_algo_bit_data bit;
24 };
25
26 #define CMD_SET_SDA     (1<<2)
27 #define CMD_SET_SCL     (1<<3)
28
29 #define STATE_SDA       (1<<0)
30 #define STATE_SCL       (1<<1)
31
32 /* i2c bit-bus functions */
33
34 static void simtec_i2c_setsda(void *pw, int state)
35 {
36         struct simtec_i2c_data *pd = pw;
37         writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg);
38 }
39
40 static void simtec_i2c_setscl(void *pw, int state)
41 {
42         struct simtec_i2c_data *pd = pw;
43         writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg);
44 }
45
46 static int simtec_i2c_getsda(void *pw)
47 {
48         struct simtec_i2c_data *pd = pw;
49         return readb(pd->reg) & STATE_SDA ? 1 : 0;
50 }
51
52 static int simtec_i2c_getscl(void *pw)
53 {
54         struct simtec_i2c_data *pd = pw;
55         return readb(pd->reg) & STATE_SCL ? 1 : 0;
56 }
57
58 /* device registration */
59
60 static int simtec_i2c_probe(struct platform_device *dev)
61 {
62         struct simtec_i2c_data *pd;
63         struct resource *res;
64         int size;
65         int ret;
66
67         pd = kzalloc(sizeof(struct simtec_i2c_data), GFP_KERNEL);
68         if (pd == NULL)
69                 return -ENOMEM;
70
71         platform_set_drvdata(dev, pd);
72
73         res = platform_get_resource(dev, IORESOURCE_MEM, 0);
74         if (res == NULL) {
75                 dev_err(&dev->dev, "cannot find IO resource\n");
76                 ret = -ENOENT;
77                 goto err;
78         }
79
80         size = resource_size(res);
81
82         pd->ioarea = request_mem_region(res->start, size, dev->name);
83         if (pd->ioarea == NULL) {
84                 dev_err(&dev->dev, "cannot request IO\n");
85                 ret = -ENXIO;
86                 goto err;
87         }
88
89         pd->reg = ioremap(res->start, size);
90         if (pd->reg == NULL) {
91                 dev_err(&dev->dev, "cannot map IO\n");
92                 ret = -ENXIO;
93                 goto err_res;
94         }
95
96         /* setup the private data */
97
98         pd->adap.owner = THIS_MODULE;
99         pd->adap.algo_data = &pd->bit;
100         pd->adap.dev.parent = &dev->dev;
101
102         strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name));
103
104         pd->bit.data = pd;
105         pd->bit.setsda = simtec_i2c_setsda;
106         pd->bit.setscl = simtec_i2c_setscl;
107         pd->bit.getsda = simtec_i2c_getsda;
108         pd->bit.getscl = simtec_i2c_getscl;
109         pd->bit.timeout = HZ;
110         pd->bit.udelay = 20;
111
112         ret = i2c_bit_add_bus(&pd->adap);
113         if (ret)
114                 goto err_all;
115
116         return 0;
117
118  err_all:
119         iounmap(pd->reg);
120
121  err_res:
122         release_mem_region(pd->ioarea->start, size);
123
124  err:
125         kfree(pd);
126         return ret;
127 }
128
129 static int simtec_i2c_remove(struct platform_device *dev)
130 {
131         struct simtec_i2c_data *pd = platform_get_drvdata(dev);
132
133         i2c_del_adapter(&pd->adap);
134
135         iounmap(pd->reg);
136         release_mem_region(pd->ioarea->start, resource_size(pd->ioarea));
137         kfree(pd);
138
139         return 0;
140 }
141
142 /* device driver */
143
144 static struct platform_driver simtec_i2c_driver = {
145         .driver         = {
146                 .name           = "simtec-i2c",
147         },
148         .probe          = simtec_i2c_probe,
149         .remove         = simtec_i2c_remove,
150 };
151
152 module_platform_driver(simtec_i2c_driver);
153
154 MODULE_DESCRIPTION("Simtec Generic I2C Bus driver");
155 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
156 MODULE_LICENSE("GPL");
157 MODULE_ALIAS("platform:simtec-i2c");