1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * I2C driver for stand-alone PCF8584 style adapters on Zorro cards |
4 | * |
5 | * Original ICY documentation can be found on Aminet: |
6 | * https://aminet.net/package/docs/hard/icy |
7 | * |
8 | * There has been a modern community re-print of this design in 2019: |
9 | * https://www.a1k.org/forum/index.php?threads/70106/ |
10 | * |
11 | * The card is basically a Philips PCF8584 connected straight to the |
12 | * beginning of the AutoConfig'd address space (register S1 on base+2), |
13 | * with /INT on /INT2 on the Zorro bus. |
14 | * |
15 | * Copyright (c) 2019 Max Staudt <max@enpas.org> |
16 | * |
17 | * This started as a fork of i2c-elektor.c and has evolved since. |
18 | * Thanks go to its authors for providing a base to grow on. |
19 | * |
20 | * |
21 | * IRQ support is currently not implemented. |
22 | * |
23 | * As it turns out, i2c-algo-pcf is really written with i2c-elektor's |
24 | * edge-triggered ISA interrupts in mind, while the Amiga's Zorro bus has |
25 | * level-triggered interrupts. This means that once an interrupt occurs, we |
26 | * have to tell the PCF8584 to shut up immediately, or it will keep the |
27 | * interrupt line busy and cause an IRQ storm. |
28 | |
29 | * However, because of the PCF8584's host-side protocol, there is no good |
30 | * way to just quieten it without side effects. Rather, we have to perform |
31 | * the next read/write operation straight away, which will reset the /INT |
32 | * pin. This entails re-designing the core of i2c-algo-pcf in the future. |
33 | * For now, we never request an IRQ from the PCF8584, and poll it instead. |
34 | */ |
35 | |
36 | #include <linux/delay.h> |
37 | #include <linux/init.h> |
38 | #include <linux/io.h> |
39 | #include <linux/ioport.h> |
40 | #include <linux/kernel.h> |
41 | #include <linux/module.h> |
42 | |
43 | #include <linux/i2c.h> |
44 | #include <linux/i2c-algo-pcf.h> |
45 | |
46 | #include <asm/amigahw.h> |
47 | #include <asm/amigaints.h> |
48 | #include <linux/zorro.h> |
49 | |
50 | #include "../algos/i2c-algo-pcf.h" |
51 | |
52 | struct icy_i2c { |
53 | struct i2c_adapter adapter; |
54 | |
55 | void __iomem *reg_s0; |
56 | void __iomem *reg_s1; |
57 | struct i2c_client *ltc2990_client; |
58 | }; |
59 | |
60 | /* |
61 | * Functions called by i2c-algo-pcf |
62 | */ |
63 | static void icy_pcf_setpcf(void *data, int ctl, int val) |
64 | { |
65 | struct icy_i2c *i2c = (struct icy_i2c *)data; |
66 | |
67 | u8 __iomem *address = ctl ? i2c->reg_s1 : i2c->reg_s0; |
68 | |
69 | z_writeb(val, address); |
70 | } |
71 | |
72 | static int icy_pcf_getpcf(void *data, int ctl) |
73 | { |
74 | struct icy_i2c *i2c = (struct icy_i2c *)data; |
75 | |
76 | u8 __iomem *address = ctl ? i2c->reg_s1 : i2c->reg_s0; |
77 | |
78 | return z_readb(address); |
79 | } |
80 | |
81 | static int icy_pcf_getown(void *data) |
82 | { |
83 | return 0x55; |
84 | } |
85 | |
86 | static int icy_pcf_getclock(void *data) |
87 | { |
88 | return 0x1c; |
89 | } |
90 | |
91 | static void icy_pcf_waitforpin(void *data) |
92 | { |
93 | usleep_range(min: 50, max: 150); |
94 | } |
95 | |
96 | /* |
97 | * Main i2c-icy part |
98 | */ |
99 | static unsigned short const icy_ltc2990_addresses[] = { |
100 | 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END |
101 | }; |
102 | |
103 | /* |
104 | * Additional sensors exposed once this property is applied: |
105 | * |
106 | * in1 will be the voltage of the 5V rail, divided by 2. |
107 | * in2 will be the voltage of the 12V rail, divided by 4. |
108 | * temp3 will be measured using a PCB loop next the chip. |
109 | */ |
110 | static const u32 icy_ltc2990_meas_mode[] = {0, 3}; |
111 | |
112 | static const struct property_entry icy_ltc2990_props[] = { |
113 | PROPERTY_ENTRY_U32_ARRAY("lltc,meas-mode" , icy_ltc2990_meas_mode), |
114 | { } |
115 | }; |
116 | |
117 | static const struct software_node icy_ltc2990_node = { |
118 | .properties = icy_ltc2990_props, |
119 | }; |
120 | |
121 | static int icy_probe(struct zorro_dev *z, |
122 | const struct zorro_device_id *ent) |
123 | { |
124 | struct icy_i2c *i2c; |
125 | struct i2c_algo_pcf_data *algo_data; |
126 | struct i2c_board_info ltc2990_info = { |
127 | .type = "ltc2990" , |
128 | .swnode = &icy_ltc2990_node, |
129 | }; |
130 | |
131 | i2c = devm_kzalloc(dev: &z->dev, size: sizeof(*i2c), GFP_KERNEL); |
132 | if (!i2c) |
133 | return -ENOMEM; |
134 | |
135 | algo_data = devm_kzalloc(dev: &z->dev, size: sizeof(*algo_data), GFP_KERNEL); |
136 | if (!algo_data) |
137 | return -ENOMEM; |
138 | |
139 | dev_set_drvdata(dev: &z->dev, data: i2c); |
140 | i2c->adapter.dev.parent = &z->dev; |
141 | i2c->adapter.owner = THIS_MODULE; |
142 | /* i2c->adapter.algo assigned by i2c_pcf_add_bus() */ |
143 | i2c->adapter.algo_data = algo_data; |
144 | strscpy(i2c->adapter.name, "ICY I2C Zorro adapter" , |
145 | sizeof(i2c->adapter.name)); |
146 | |
147 | if (!devm_request_mem_region(&z->dev, |
148 | z->resource.start, |
149 | 4, i2c->adapter.name)) |
150 | return -ENXIO; |
151 | |
152 | /* Driver private data */ |
153 | i2c->reg_s0 = ZTWO_VADDR(z->resource.start); |
154 | i2c->reg_s1 = ZTWO_VADDR(z->resource.start + 2); |
155 | |
156 | algo_data->data = i2c; |
157 | algo_data->setpcf = icy_pcf_setpcf; |
158 | algo_data->getpcf = icy_pcf_getpcf; |
159 | algo_data->getown = icy_pcf_getown; |
160 | algo_data->getclock = icy_pcf_getclock; |
161 | algo_data->waitforpin = icy_pcf_waitforpin; |
162 | |
163 | if (i2c_pcf_add_bus(&i2c->adapter)) { |
164 | dev_err(&z->dev, "i2c_pcf_add_bus() failed\n" ); |
165 | return -ENXIO; |
166 | } |
167 | |
168 | dev_info(&z->dev, "ICY I2C controller at %pa, IRQ not implemented\n" , |
169 | &z->resource.start); |
170 | |
171 | /* |
172 | * The 2019 a1k.org PCBs have an LTC2990 at 0x4c, so start |
173 | * it automatically once ltc2990 is modprobed. |
174 | * |
175 | * in0 is the voltage of the internal 5V power supply. |
176 | * temp1 is the temperature inside the chip. |
177 | * |
178 | * See property_entry above for in1, in2, temp3. |
179 | */ |
180 | i2c->ltc2990_client = i2c_new_scanned_device(adap: &i2c->adapter, |
181 | info: <c2990_info, |
182 | addr_list: icy_ltc2990_addresses, |
183 | NULL); |
184 | return 0; |
185 | } |
186 | |
187 | static void icy_remove(struct zorro_dev *z) |
188 | { |
189 | struct icy_i2c *i2c = dev_get_drvdata(dev: &z->dev); |
190 | |
191 | i2c_unregister_device(client: i2c->ltc2990_client); |
192 | i2c_del_adapter(adap: &i2c->adapter); |
193 | } |
194 | |
195 | static const struct zorro_device_id icy_zorro_tbl[] = { |
196 | { ZORRO_ID(VMC, 15, 0), }, |
197 | { 0 } |
198 | }; |
199 | |
200 | MODULE_DEVICE_TABLE(zorro, icy_zorro_tbl); |
201 | |
202 | static struct zorro_driver icy_driver = { |
203 | .name = "i2c-icy" , |
204 | .id_table = icy_zorro_tbl, |
205 | .probe = icy_probe, |
206 | .remove = icy_remove, |
207 | }; |
208 | |
209 | module_driver(icy_driver, |
210 | zorro_register_driver, |
211 | zorro_unregister_driver); |
212 | |
213 | MODULE_AUTHOR("Max Staudt <max@enpas.org>" ); |
214 | MODULE_DESCRIPTION("I2C bus via PCF8584 on ICY Zorro card" ); |
215 | MODULE_LICENSE("GPL v2" ); |
216 | |