f28e40231c9ea0fc47e10ffbe33251cdbdcd2b27
[firefly-linux-kernel-4.4.55.git] / drivers / thunderbolt / eeprom.c
1 /*
2  * Thunderbolt Cactus Ridge driver - eeprom access
3  *
4  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
5  */
6
7 #include "tb.h"
8
9 /**
10  * tb_eeprom_ctl_write() - write control word
11  */
12 static int tb_eeprom_ctl_write(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
13 {
14         return tb_sw_write(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
15 }
16
17 /**
18  * tb_eeprom_ctl_write() - read control word
19  */
20 static int tb_eeprom_ctl_read(struct tb_switch *sw, struct tb_eeprom_ctl *ctl)
21 {
22         return tb_sw_read(sw, ctl, TB_CFG_SWITCH, sw->cap_plug_events + 4, 1);
23 }
24
25 enum tb_eeprom_transfer {
26         TB_EEPROM_IN,
27         TB_EEPROM_OUT,
28 };
29
30 /**
31  * tb_eeprom_active - enable rom access
32  *
33  * WARNING: Always disable access after usage. Otherwise the controller will
34  * fail to reprobe.
35  */
36 static int tb_eeprom_active(struct tb_switch *sw, bool enable)
37 {
38         struct tb_eeprom_ctl ctl;
39         int res = tb_eeprom_ctl_read(sw, &ctl);
40         if (res)
41                 return res;
42         if (enable) {
43                 ctl.access_high = 1;
44                 res = tb_eeprom_ctl_write(sw, &ctl);
45                 if (res)
46                         return res;
47                 ctl.access_low = 0;
48                 return tb_eeprom_ctl_write(sw, &ctl);
49         } else {
50                 ctl.access_low = 1;
51                 res = tb_eeprom_ctl_write(sw, &ctl);
52                 if (res)
53                         return res;
54                 ctl.access_high = 0;
55                 return tb_eeprom_ctl_write(sw, &ctl);
56         }
57 }
58
59 /**
60  * tb_eeprom_transfer - transfer one bit
61  *
62  * If TB_EEPROM_IN is passed, then the bit can be retrieved from ctl->data_in.
63  * If TB_EEPROM_OUT is passed, then ctl->data_out will be written.
64  */
65 static int tb_eeprom_transfer(struct tb_switch *sw, struct tb_eeprom_ctl *ctl,
66                               enum tb_eeprom_transfer direction)
67 {
68         int res;
69         if (direction == TB_EEPROM_OUT) {
70                 res = tb_eeprom_ctl_write(sw, ctl);
71                 if (res)
72                         return res;
73         }
74         ctl->clock = 1;
75         res = tb_eeprom_ctl_write(sw, ctl);
76         if (res)
77                 return res;
78         if (direction == TB_EEPROM_IN) {
79                 res = tb_eeprom_ctl_read(sw, ctl);
80                 if (res)
81                         return res;
82         }
83         ctl->clock = 0;
84         return tb_eeprom_ctl_write(sw, ctl);
85 }
86
87 /**
88  * tb_eeprom_out - write one byte to the bus
89  */
90 static int tb_eeprom_out(struct tb_switch *sw, u8 val)
91 {
92         struct tb_eeprom_ctl ctl;
93         int i;
94         int res = tb_eeprom_ctl_read(sw, &ctl);
95         if (res)
96                 return res;
97         for (i = 0; i < 8; i++) {
98                 ctl.data_out = val & 0x80;
99                 res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_OUT);
100                 if (res)
101                         return res;
102                 val <<= 1;
103         }
104         return 0;
105 }
106
107 /**
108  * tb_eeprom_in - read one byte from the bus
109  */
110 static int tb_eeprom_in(struct tb_switch *sw, u8 *val)
111 {
112         struct tb_eeprom_ctl ctl;
113         int i;
114         int res = tb_eeprom_ctl_read(sw, &ctl);
115         if (res)
116                 return res;
117         *val = 0;
118         for (i = 0; i < 8; i++) {
119                 *val <<= 1;
120                 res = tb_eeprom_transfer(sw, &ctl, TB_EEPROM_IN);
121                 if (res)
122                         return res;
123                 *val |= ctl.data_in;
124         }
125         return 0;
126 }
127
128 /**
129  * tb_eeprom_read_n - read count bytes from offset into val
130  */
131 static int tb_eeprom_read_n(struct tb_switch *sw, u16 offset, u8 *val,
132                 size_t count)
133 {
134         int i, res;
135         res = tb_eeprom_active(sw, true);
136         if (res)
137                 return res;
138         res = tb_eeprom_out(sw, 3);
139         if (res)
140                 return res;
141         res = tb_eeprom_out(sw, offset >> 8);
142         if (res)
143                 return res;
144         res = tb_eeprom_out(sw, offset);
145         if (res)
146                 return res;
147         for (i = 0; i < count; i++) {
148                 res = tb_eeprom_in(sw, val + i);
149                 if (res)
150                         return res;
151         }
152         return tb_eeprom_active(sw, false);
153 }
154
155 int tb_eeprom_read_uid(struct tb_switch *sw, u64 *uid)
156 {
157         u8 data[9];
158         struct tb_cap_plug_events cap;
159         int res;
160         if (!sw->cap_plug_events) {
161                 tb_sw_warn(sw, "no TB_CAP_PLUG_EVENTS, cannot read eeprom\n");
162                 return -ENOSYS;
163         }
164         res = tb_sw_read(sw, &cap, TB_CFG_SWITCH, sw->cap_plug_events,
165                              sizeof(cap) / 4);
166         if (res)
167                 return res;
168         if (!cap.eeprom_ctl.present || cap.eeprom_ctl.not_present) {
169                 tb_sw_warn(sw, "no NVM\n");
170                 return -ENOSYS;
171         }
172
173         if (cap.drom_offset > 0xffff) {
174                 tb_sw_warn(sw, "drom offset is larger than 0xffff: %#x\n",
175                                 cap.drom_offset);
176                 return -ENXIO;
177         }
178
179         /* read uid */
180         res = tb_eeprom_read_n(sw, cap.drom_offset, data, 9);
181         if (res)
182                 return res;
183         /* TODO: check checksum in data[0] */
184         *uid = *(u64 *)(data+1);
185         return 0;
186 }
187
188
189