1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2021 Michael Walle <michael@walle.cc>
4 */
5
6 #include <dm.h>
7 #include <i2c.h>
8
9 struct sl28cpld_child_plat {
10 uint offset;
11 };
12
13 /*
14 * The access methods works either with the first argument being a child
15 * device or with the MFD device itself.
16 */
sl28cpld_read_child(struct udevice * dev,uint offset)17 static int sl28cpld_read_child(struct udevice *dev, uint offset)
18 {
19 struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev);
20 struct udevice *mfd = dev_get_parent(dev);
21
22 return dm_i2c_reg_read(mfd, offset + plat->offset);
23 }
24
sl28cpld_read(struct udevice * dev,uint offset)25 int sl28cpld_read(struct udevice *dev, uint offset)
26 {
27 if (dev->driver == DM_DRIVER_GET(sl28cpld))
28 return dm_i2c_reg_read(dev, offset);
29 else
30 return sl28cpld_read_child(dev, offset);
31 }
32
sl28cpld_write_child(struct udevice * dev,uint offset,uint8_t value)33 static int sl28cpld_write_child(struct udevice *dev, uint offset,
34 uint8_t value)
35 {
36 struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev);
37 struct udevice *mfd = dev_get_parent(dev);
38
39 return dm_i2c_reg_write(mfd, offset + plat->offset, value);
40 }
41
sl28cpld_write(struct udevice * dev,uint offset,uint8_t value)42 int sl28cpld_write(struct udevice *dev, uint offset, uint8_t value)
43 {
44 if (dev->driver == DM_DRIVER_GET(sl28cpld))
45 return dm_i2c_reg_write(dev, offset, value);
46 else
47 return sl28cpld_write_child(dev, offset, value);
48 }
49
sl28cpld_update(struct udevice * dev,uint offset,uint8_t clear,uint8_t set)50 int sl28cpld_update(struct udevice *dev, uint offset, uint8_t clear,
51 uint8_t set)
52 {
53 int val;
54
55 val = sl28cpld_read(dev, offset);
56 if (val < 0)
57 return val;
58
59 val &= ~clear;
60 val |= set;
61
62 return sl28cpld_write(dev, offset, val);
63 }
64
sl28cpld_probe(struct udevice * dev)65 static int sl28cpld_probe(struct udevice *dev)
66 {
67 i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS |
68 DM_I2C_CHIP_WR_ADDRESS);
69
70 return 0;
71 }
72
sl28cpld_child_post_bind(struct udevice * dev)73 static int sl28cpld_child_post_bind(struct udevice *dev)
74 {
75 struct sl28cpld_child_plat *plat = dev_get_parent_plat(dev);
76 int offset;
77
78 if (!dev_has_ofnode(dev))
79 return 0;
80
81 offset = dev_read_u32_default(dev, "reg", -1);
82 if (offset == -1)
83 return -EINVAL;
84
85 plat->offset = offset;
86
87 return 0;
88 }
89
90 static const struct udevice_id sl28cpld_ids[] = {
91 { .compatible = "kontron,sl28cpld" },
92 {}
93 };
94
95 U_BOOT_DRIVER(sl28cpld) = {
96 .name = "sl28cpld",
97 .id = UCLASS_NOP,
98 .of_match = sl28cpld_ids,
99 .probe = sl28cpld_probe,
100 .bind = dm_scan_fdt_dev,
101 .flags = DM_FLAG_PRE_RELOC,
102 .per_child_plat_auto = sizeof(struct sl28cpld_child_plat),
103 .child_post_bind = sl28cpld_child_post_bind,
104 };
105