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), addr: 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), addr: pd->reg); |
44 | } |
45 | |
46 | static int simtec_i2c_getsda(void *pw) |
47 | { |
48 | struct simtec_i2c_data *pd = pw; |
49 | return readb(addr: 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(addr: 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(size: sizeof(struct simtec_i2c_data), GFP_KERNEL); |
68 | if (pd == NULL) |
69 | return -ENOMEM; |
70 | |
71 | platform_set_drvdata(pdev: dev, data: 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(offset: 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 | strscpy(p: pd->adap.name, q: "Simtec I2C" , size: 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(addr: pd->reg); |
120 | |
121 | err_res: |
122 | release_mem_region(pd->ioarea->start, size); |
123 | |
124 | err: |
125 | kfree(objp: pd); |
126 | return ret; |
127 | } |
128 | |
129 | static void simtec_i2c_remove(struct platform_device *dev) |
130 | { |
131 | struct simtec_i2c_data *pd = platform_get_drvdata(pdev: dev); |
132 | |
133 | i2c_del_adapter(adap: &pd->adap); |
134 | |
135 | iounmap(addr: pd->reg); |
136 | release_mem_region(pd->ioarea->start, resource_size(pd->ioarea)); |
137 | kfree(objp: pd); |
138 | } |
139 | |
140 | /* device driver */ |
141 | |
142 | static struct platform_driver simtec_i2c_driver = { |
143 | .driver = { |
144 | .name = "simtec-i2c" , |
145 | }, |
146 | .probe = simtec_i2c_probe, |
147 | .remove_new = simtec_i2c_remove, |
148 | }; |
149 | |
150 | module_platform_driver(simtec_i2c_driver); |
151 | |
152 | MODULE_DESCRIPTION("Simtec Generic I2C Bus driver" ); |
153 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>" ); |
154 | MODULE_LICENSE("GPL" ); |
155 | MODULE_ALIAS("platform:simtec-i2c" ); |
156 | |