1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020 NovaTech LLC |
4 | * George McCollister <george.mccollister@gmail.com> |
5 | */ |
6 | |
7 | #include <linux/bits.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/module.h> |
10 | #include "xrs700x.h" |
11 | #include "xrs700x_reg.h" |
12 | |
13 | struct xrs700x_i2c_cmd { |
14 | __be32 reg; |
15 | __be16 val; |
16 | } __packed; |
17 | |
18 | static int xrs700x_i2c_reg_read(void *context, unsigned int reg, |
19 | unsigned int *val) |
20 | { |
21 | struct device *dev = context; |
22 | struct i2c_client *i2c = to_i2c_client(dev); |
23 | struct xrs700x_i2c_cmd cmd; |
24 | int ret; |
25 | |
26 | cmd.reg = cpu_to_be32(reg | 1); |
27 | |
28 | ret = i2c_master_send(client: i2c, buf: (char *)&cmd.reg, count: sizeof(cmd.reg)); |
29 | if (ret < 0) { |
30 | dev_err(dev, "xrs i2c_master_send returned %d\n" , ret); |
31 | return ret; |
32 | } |
33 | |
34 | ret = i2c_master_recv(client: i2c, buf: (char *)&cmd.val, count: sizeof(cmd.val)); |
35 | if (ret < 0) { |
36 | dev_err(dev, "xrs i2c_master_recv returned %d\n" , ret); |
37 | return ret; |
38 | } |
39 | |
40 | *val = be16_to_cpu(cmd.val); |
41 | return 0; |
42 | } |
43 | |
44 | static int xrs700x_i2c_reg_write(void *context, unsigned int reg, |
45 | unsigned int val) |
46 | { |
47 | struct device *dev = context; |
48 | struct i2c_client *i2c = to_i2c_client(dev); |
49 | struct xrs700x_i2c_cmd cmd; |
50 | int ret; |
51 | |
52 | cmd.reg = cpu_to_be32(reg); |
53 | cmd.val = cpu_to_be16(val); |
54 | |
55 | ret = i2c_master_send(client: i2c, buf: (char *)&cmd, count: sizeof(cmd)); |
56 | if (ret < 0) { |
57 | dev_err(dev, "xrs i2c_master_send returned %d\n" , ret); |
58 | return ret; |
59 | } |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | static const struct regmap_config xrs700x_i2c_regmap_config = { |
65 | .val_bits = 16, |
66 | .reg_stride = 2, |
67 | .reg_bits = 32, |
68 | .pad_bits = 0, |
69 | .write_flag_mask = 0, |
70 | .read_flag_mask = 0, |
71 | .reg_read = xrs700x_i2c_reg_read, |
72 | .reg_write = xrs700x_i2c_reg_write, |
73 | .max_register = 0, |
74 | .cache_type = REGCACHE_NONE, |
75 | .reg_format_endian = REGMAP_ENDIAN_BIG, |
76 | .val_format_endian = REGMAP_ENDIAN_BIG |
77 | }; |
78 | |
79 | static int xrs700x_i2c_probe(struct i2c_client *i2c) |
80 | { |
81 | struct xrs700x *priv; |
82 | int ret; |
83 | |
84 | priv = xrs700x_switch_alloc(base: &i2c->dev, devpriv: i2c); |
85 | if (!priv) |
86 | return -ENOMEM; |
87 | |
88 | priv->regmap = devm_regmap_init(&i2c->dev, NULL, &i2c->dev, |
89 | &xrs700x_i2c_regmap_config); |
90 | if (IS_ERR(ptr: priv->regmap)) { |
91 | ret = PTR_ERR(ptr: priv->regmap); |
92 | dev_err(&i2c->dev, "Failed to initialize regmap: %d\n" , ret); |
93 | return ret; |
94 | } |
95 | |
96 | i2c_set_clientdata(client: i2c, data: priv); |
97 | |
98 | ret = xrs700x_switch_register(priv); |
99 | |
100 | /* Main DSA driver may not be started yet. */ |
101 | if (ret) |
102 | return ret; |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static void xrs700x_i2c_remove(struct i2c_client *i2c) |
108 | { |
109 | struct xrs700x *priv = i2c_get_clientdata(client: i2c); |
110 | |
111 | if (!priv) |
112 | return; |
113 | |
114 | xrs700x_switch_remove(priv); |
115 | } |
116 | |
117 | static void xrs700x_i2c_shutdown(struct i2c_client *i2c) |
118 | { |
119 | struct xrs700x *priv = i2c_get_clientdata(client: i2c); |
120 | |
121 | if (!priv) |
122 | return; |
123 | |
124 | xrs700x_switch_shutdown(priv); |
125 | |
126 | i2c_set_clientdata(client: i2c, NULL); |
127 | } |
128 | |
129 | static const struct i2c_device_id xrs700x_i2c_id[] = { |
130 | { "xrs700x-switch" , 0 }, |
131 | {}, |
132 | }; |
133 | |
134 | MODULE_DEVICE_TABLE(i2c, xrs700x_i2c_id); |
135 | |
136 | static const struct of_device_id __maybe_unused xrs700x_i2c_dt_ids[] = { |
137 | { .compatible = "arrow,xrs7003e" , .data = &xrs7003e_info }, |
138 | { .compatible = "arrow,xrs7003f" , .data = &xrs7003f_info }, |
139 | { .compatible = "arrow,xrs7004e" , .data = &xrs7004e_info }, |
140 | { .compatible = "arrow,xrs7004f" , .data = &xrs7004f_info }, |
141 | {}, |
142 | }; |
143 | MODULE_DEVICE_TABLE(of, xrs700x_i2c_dt_ids); |
144 | |
145 | static struct i2c_driver xrs700x_i2c_driver = { |
146 | .driver = { |
147 | .name = "xrs700x-i2c" , |
148 | .of_match_table = of_match_ptr(xrs700x_i2c_dt_ids), |
149 | }, |
150 | .probe = xrs700x_i2c_probe, |
151 | .remove = xrs700x_i2c_remove, |
152 | .shutdown = xrs700x_i2c_shutdown, |
153 | .id_table = xrs700x_i2c_id, |
154 | }; |
155 | |
156 | module_i2c_driver(xrs700x_i2c_driver); |
157 | |
158 | MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>" ); |
159 | MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA I2C driver" ); |
160 | MODULE_LICENSE("GPL v2" ); |
161 | |