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