]> Git Repo - linux.git/blob - drivers/platform/x86/silicom-platform.c
i2c: Fix conditional for substituting empty ACPI functions
[linux.git] / drivers / platform / x86 / silicom-platform.c
1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // silicom-platform.c - Silicom MEC170x platform driver
4 //
5 // Copyright (C) 2023 Henry Shi <[email protected]>
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/dmi.h>
9 #include <linux/hwmon.h>
10 #include <linux/init.h>
11 #include <linux/ioport.h>
12 #include <linux/io.h>
13 #include <linux/kernel.h>
14 #include <linux/kobject.h>
15 #include <linux/led-class-multicolor.h>
16 #include <linux/module.h>
17 #include <linux/mutex.h>
18 #include <linux/platform_device.h>
19 #include <linux/string.h>
20 #include <linux/sysfs.h>
21 #include <linux/units.h>
22
23 #include <linux/gpio/driver.h>
24
25 #define MEC_POWER_CYCLE_ADDR 0x24
26 #define MEC_EFUSE_LSB_ADDR   0x28
27 #define MEC_GPIO_IN_POS      0x08
28 #define MEC_IO_BASE          0x0800
29 #define MEC_IO_LEN           0x8
30 #define IO_REG_BANK          0x0
31 #define DEFAULT_CHAN_LO      0
32 #define DEFAULT_CHAN_HI      0
33 #define DEFAULT_CHAN_LO_T    0xc
34 #define MEC_ADDR             (MEC_IO_BASE + 0x02)
35 #define EC_ADDR_LSB          MEC_ADDR
36 #define SILICOM_MEC_MAGIC    0x5a
37
38 #define MEC_PORT_CHANNEL_MASK GENMASK(2, 0)
39 #define MEC_PORT_DWORD_OFFSET GENMASK(31, 3)
40 #define MEC_DATA_OFFSET_MASK  GENMASK(1, 0)
41 #define MEC_PORT_OFFSET_MASK  GENMASK(7, 2)
42
43 #define MEC_TEMP_LOC          GENMASK(31, 16)
44 #define MEC_VERSION_LOC       GENMASK(15, 8)
45 #define MEC_VERSION_MAJOR     GENMASK(15, 14)
46 #define MEC_VERSION_MINOR     GENMASK(13, 8)
47
48 #define EC_ADDR_MSB           (MEC_IO_BASE + 0x3)
49 #define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset))
50
51 #define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
52 #define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
53
54 static DEFINE_MUTEX(mec_io_mutex);
55 static unsigned int efuse_status;
56 static unsigned int mec_uc_version;
57 static unsigned int power_cycle;
58
59 static const struct hwmon_channel_info *silicom_fan_control_info[] = {
60         HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
61         HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
62         NULL
63 };
64
65 struct silicom_platform_info {
66         int io_base;
67         int io_len;
68         struct led_classdev_mc *led_info;
69         struct gpio_chip *gpiochip;
70         u8 *gpio_channels;
71         u16 ngpio;
72 };
73
74 static const char * const plat_0222_gpio_names[] = {
75         "AUTOM0_SFP_TX_FAULT",
76         "SLOT2_LED_OUT",
77         "SIM_M2_SLOT2_B_DET",
78         "SIM_M2_SLOT2_A_DET",
79         "SLOT1_LED_OUT",
80         "SIM_M2_SLOT1_B_DET",
81         "SIM_M2_SLOT1_A_DET",
82         "SLOT0_LED_OUT",
83         "WAN_SFP0_RX_LOS",
84         "WAN_SFP0_PRSNT_N",
85         "WAN_SFP0_TX_FAULT",
86         "AUTOM1_SFP_RX_LOS",
87         "AUTOM1_SFP_PRSNT_N",
88         "AUTOM1_SFP_TX_FAULT",
89         "AUTOM0_SFP_RX_LOS",
90         "AUTOM0_SFP_PRSNT_N",
91         "WAN_SFP1_RX_LOS",
92         "WAN_SFP1_PRSNT_N",
93         "WAN_SFP1_TX_FAULT",
94         "SIM_M2_SLOT1_MUX_SEL",
95         "W_DISABLE_M2_SLOT1_N",
96         "W_DISABLE_MPCIE_SLOT0_N",
97         "W_DISABLE_M2_SLOT0_N",
98         "BT_COMMAND_MODE",
99         "WAN_SFP1_TX_DISABLE",
100         "WAN_SFP0_TX_DISABLE",
101         "AUTOM1_SFP_TX_DISABLE",
102         "AUTOM0_SFP_TX_DISABLE",
103         "SIM_M2_SLOT2_MUX_SEL",
104         "W_DISABLE_M2_SLOT2_N",
105         "RST_CTL_M2_SLOT_1_N",
106         "RST_CTL_M2_SLOT_2_N",
107         "PM_USB_PWR_EN_BOT",
108         "PM_USB_PWR_EN_TOP",
109 };
110
111 static u8 plat_0222_gpio_channels[] = {
112         OFFSET_BIT_TO_CHANNEL(0x00, 0),
113         OFFSET_BIT_TO_CHANNEL(0x00, 1),
114         OFFSET_BIT_TO_CHANNEL(0x00, 2),
115         OFFSET_BIT_TO_CHANNEL(0x00, 3),
116         OFFSET_BIT_TO_CHANNEL(0x00, 4),
117         OFFSET_BIT_TO_CHANNEL(0x00, 5),
118         OFFSET_BIT_TO_CHANNEL(0x00, 6),
119         OFFSET_BIT_TO_CHANNEL(0x00, 7),
120         OFFSET_BIT_TO_CHANNEL(0x01, 0),
121         OFFSET_BIT_TO_CHANNEL(0x01, 1),
122         OFFSET_BIT_TO_CHANNEL(0x01, 2),
123         OFFSET_BIT_TO_CHANNEL(0x01, 3),
124         OFFSET_BIT_TO_CHANNEL(0x01, 4),
125         OFFSET_BIT_TO_CHANNEL(0x01, 5),
126         OFFSET_BIT_TO_CHANNEL(0x01, 6),
127         OFFSET_BIT_TO_CHANNEL(0x01, 7),
128         OFFSET_BIT_TO_CHANNEL(0x02, 0),
129         OFFSET_BIT_TO_CHANNEL(0x02, 1),
130         OFFSET_BIT_TO_CHANNEL(0x02, 2),
131         OFFSET_BIT_TO_CHANNEL(0x09, 0),
132         OFFSET_BIT_TO_CHANNEL(0x09, 1),
133         OFFSET_BIT_TO_CHANNEL(0x09, 2),
134         OFFSET_BIT_TO_CHANNEL(0x09, 3),
135         OFFSET_BIT_TO_CHANNEL(0x0a, 0),
136         OFFSET_BIT_TO_CHANNEL(0x0a, 1),
137         OFFSET_BIT_TO_CHANNEL(0x0a, 2),
138         OFFSET_BIT_TO_CHANNEL(0x0a, 3),
139         OFFSET_BIT_TO_CHANNEL(0x0a, 4),
140         OFFSET_BIT_TO_CHANNEL(0x0a, 5),
141         OFFSET_BIT_TO_CHANNEL(0x0a, 6),
142         OFFSET_BIT_TO_CHANNEL(0x0b, 0),
143         OFFSET_BIT_TO_CHANNEL(0x0b, 1),
144         OFFSET_BIT_TO_CHANNEL(0x0b, 2),
145         OFFSET_BIT_TO_CHANNEL(0x0b, 3),
146 };
147
148 static struct platform_device *silicom_platform_dev;
149 static struct led_classdev_mc *silicom_led_info __initdata;
150 static struct gpio_chip *silicom_gpiochip __initdata;
151 static u8 *silicom_gpio_channels __initdata;
152
153 static int silicom_mec_port_get(unsigned int offset)
154 {
155         unsigned short mec_data_addr;
156         unsigned short mec_port_addr;
157         u8 reg;
158
159         mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK;
160         mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK;
161
162         mutex_lock(&mec_io_mutex);
163         outb(mec_port_addr, MEC_ADDR);
164         reg = inb(MEC_DATA_OFFSET(mec_data_addr));
165         mutex_unlock(&mec_io_mutex);
166
167         return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01;
168 }
169
170 static enum led_brightness silicom_mec_led_get(int channel)
171 {
172         /* Outputs are active low */
173         return silicom_mec_port_get(channel) ? LED_OFF : LED_ON;
174 }
175
176 static void silicom_mec_port_set(int channel, int on)
177 {
178
179         unsigned short mec_data_addr;
180         unsigned short mec_port_addr;
181         u8 reg;
182
183         mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK;
184         mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK;
185
186         mutex_lock(&mec_io_mutex);
187         outb(mec_port_addr, MEC_ADDR);
188         reg = inb(MEC_DATA_OFFSET(mec_data_addr));
189         /* Outputs are active low, so clear the bit for on, or set it for off */
190         if (on)
191                 reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK));
192         else
193                 reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK);
194         outb(reg, MEC_DATA_OFFSET(mec_data_addr));
195         mutex_unlock(&mec_io_mutex);
196 }
197
198 static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev)
199 {
200         struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
201         enum led_brightness brightness = LED_OFF;
202         int i;
203
204         for (i = 0; i < mc_cdev->num_colors; i++) {
205                 mc_cdev->subled_info[i].brightness =
206                         silicom_mec_led_get(mc_cdev->subled_info[i].channel);
207                 /* Mark the overall brightness as LED_ON if any of the subleds are on */
208                 if (mc_cdev->subled_info[i].brightness != LED_OFF)
209                         brightness = LED_ON;
210         }
211
212         return brightness;
213 }
214
215 static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
216                                               enum led_brightness brightness)
217 {
218         struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
219         int i;
220
221         led_mc_calc_color_components(mc_cdev, brightness);
222         for (i = 0; i < mc_cdev->num_colors; i++) {
223                 silicom_mec_port_set(mc_cdev->subled_info[i].channel,
224                                      mc_cdev->subled_info[i].brightness);
225         }
226 }
227
228 static int silicom_gpio_get_direction(struct gpio_chip *gc,
229                                       unsigned int offset)
230 {
231         u8 *channels = gpiochip_get_data(gc);
232
233         /* Input registers have offsets between [0x00, 0x07] */
234         if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS)
235                 return GPIO_LINE_DIRECTION_IN;
236
237         return GPIO_LINE_DIRECTION_OUT;
238 }
239
240 static int silicom_gpio_direction_input(struct gpio_chip *gc,
241                                         unsigned int offset)
242 {
243         int direction = silicom_gpio_get_direction(gc, offset);
244
245         return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL;
246 }
247
248 static void silicom_gpio_set(struct gpio_chip *gc,
249                              unsigned int offset,
250                              int value)
251 {
252         int direction = silicom_gpio_get_direction(gc, offset);
253         u8 *channels = gpiochip_get_data(gc);
254         int channel = channels[offset];
255
256         if (direction == GPIO_LINE_DIRECTION_IN)
257                 return;
258
259         silicom_mec_port_set(channel, !value);
260 }
261
262 static int silicom_gpio_direction_output(struct gpio_chip *gc,
263                                          unsigned int offset,
264                                          int value)
265 {
266         int direction = silicom_gpio_get_direction(gc, offset);
267
268         if (direction == GPIO_LINE_DIRECTION_IN)
269                 return -EINVAL;
270
271         silicom_gpio_set(gc, offset, value);
272
273         return 0;
274 }
275
276 static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset)
277 {
278         u8 *channels = gpiochip_get_data(gc);
279         int channel = channels[offset];
280
281         return silicom_mec_port_get(channel);
282 }
283
284 static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = {
285         {
286                 .color_index = LED_COLOR_ID_WHITE,
287                 .brightness = 1,
288                 .intensity = 0,
289                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7),
290         },
291         {
292                 .color_index = LED_COLOR_ID_YELLOW,
293                 .brightness = 1,
294                 .intensity = 0,
295                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6),
296         },
297         {
298                 .color_index = LED_COLOR_ID_RED,
299                 .brightness = 1,
300                 .intensity = 0,
301                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5),
302         },
303 };
304
305 static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = {
306         {
307                 .color_index = LED_COLOR_ID_WHITE,
308                 .brightness = 1,
309                 .intensity = 0,
310                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4),
311         },
312         {
313                 .color_index = LED_COLOR_ID_AMBER,
314                 .brightness = 1,
315                 .intensity = 0,
316                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3),
317         },
318         {
319                 .color_index = LED_COLOR_ID_RED,
320                 .brightness = 1,
321                 .intensity = 0,
322                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2),
323         },
324 };
325
326 static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = {
327         {
328                 .color_index = LED_COLOR_ID_RED,
329                 .brightness = 1,
330                 .intensity = 0,
331                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1),
332         },
333         {
334                 .color_index = LED_COLOR_ID_GREEN,
335                 .brightness = 1,
336                 .intensity = 0,
337                 .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0),
338         },
339         {
340                 .color_index = LED_COLOR_ID_BLUE,
341                 .brightness = 1,
342                 .intensity = 0,
343                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7),
344         },
345         {
346                 .color_index = LED_COLOR_ID_YELLOW,
347                 .brightness = 1,
348                 .intensity = 0,
349                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6),
350         },
351 };
352
353 static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = {
354         {
355                 .color_index = LED_COLOR_ID_RED,
356                 .brightness = 1,
357                 .intensity = 0,
358                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5),
359         },
360         {
361                 .color_index = LED_COLOR_ID_GREEN,
362                 .brightness = 1,
363                 .intensity = 0,
364                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4),
365         },
366         {
367                 .color_index = LED_COLOR_ID_BLUE,
368                 .brightness = 1,
369                 .intensity = 0,
370                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3),
371         },
372         {
373                 .color_index = LED_COLOR_ID_YELLOW,
374                 .brightness = 1,
375                 .intensity = 0,
376                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2),
377         },
378 };
379
380 static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = {
381         {
382                 .color_index = LED_COLOR_ID_RED,
383                 .brightness = 1,
384                 .intensity = 0,
385                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1),
386         },
387         {
388                 .color_index = LED_COLOR_ID_GREEN,
389                 .brightness = 1,
390                 .intensity = 0,
391                 .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0),
392         },
393         {
394                 .color_index = LED_COLOR_ID_BLUE,
395                 .brightness = 1,
396                 .intensity = 0,
397                 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1),
398         },
399         {
400                 .color_index = LED_COLOR_ID_YELLOW,
401                 .brightness = 1,
402                 .intensity = 0,
403                 .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0),
404         },
405 };
406
407 static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = {
408         {
409                 .led_cdev = {
410                         .name = "platled::wan",
411                         .brightness = 0,
412                         .max_brightness = 1,
413                         .brightness_set = silicom_mec_led_mc_brightness_set,
414                         .brightness_get = silicom_mec_led_mc_brightness_get,
415                 },
416                 .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info),
417                 .subled_info = plat_0222_wan_mc_subled_info,
418         },
419         {
420                 .led_cdev = {
421                         .name = "platled::sys",
422                         .brightness = 0,
423                         .max_brightness = 1,
424                         .brightness_set = silicom_mec_led_mc_brightness_set,
425                         .brightness_get = silicom_mec_led_mc_brightness_get,
426                 },
427                 .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info),
428                 .subled_info = plat_0222_sys_mc_subled_info,
429         },
430         {
431                 .led_cdev = {
432                         .name = "platled::stat1",
433                         .brightness = 0,
434                         .max_brightness = 1,
435                         .brightness_set = silicom_mec_led_mc_brightness_set,
436                         .brightness_get = silicom_mec_led_mc_brightness_get,
437                 },
438                 .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info),
439                 .subled_info = plat_0222_stat1_mc_subled_info,
440         },
441         {
442                 .led_cdev = {
443                         .name = "platled::stat2",
444                         .brightness = 0,
445                         .max_brightness = 1,
446                         .brightness_set = silicom_mec_led_mc_brightness_set,
447                         .brightness_get = silicom_mec_led_mc_brightness_get,
448                 },
449                 .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info),
450                 .subled_info = plat_0222_stat2_mc_subled_info,
451         },
452         {
453                 .led_cdev = {
454                         .name = "platled::stat3",
455                         .brightness = 0,
456                         .max_brightness = 1,
457                         .brightness_set = silicom_mec_led_mc_brightness_set,
458                         .brightness_get = silicom_mec_led_mc_brightness_get,
459                 },
460                 .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info),
461                 .subled_info = plat_0222_stat3_mc_subled_info,
462         },
463         { },
464 };
465
466 static struct gpio_chip silicom_gpio_chip = {
467         .label = "silicom-gpio",
468         .get_direction = silicom_gpio_get_direction,
469         .direction_input = silicom_gpio_direction_input,
470         .direction_output = silicom_gpio_direction_output,
471         .get = silicom_gpio_get,
472         .set = silicom_gpio_set,
473         .base = -1,
474         .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
475         .names = plat_0222_gpio_names,
476         /*
477          * We're using a mutex to protect the indirect access, so we can sleep
478          * if the lock blocks
479          */
480         .can_sleep = true,
481 };
482
483 static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = {
484         .io_base = MEC_IO_BASE,
485         .io_len = MEC_IO_LEN,
486         .led_info = plat_0222_mc_led_info,
487         .gpiochip = &silicom_gpio_chip,
488         .gpio_channels = plat_0222_gpio_channels,
489         /*
490          * The original generic cordoba does not have the last 4 outputs of the
491          * plat_0222 variant, the rest are the same, so use the same longer list,
492          * but ignore the last entries here
493          */
494         .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
495
496 };
497
498 static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = {
499         {
500                 .color_index = LED_COLOR_ID_RED,
501                 .brightness = 1,
502                 .intensity = 0,
503                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6),
504         },
505         {
506                 .color_index = LED_COLOR_ID_GREEN,
507                 .brightness = 1,
508                 .intensity = 0,
509                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5),
510         },
511         {
512                 .color_index = LED_COLOR_ID_BLUE,
513                 .brightness = 1,
514                 .intensity = 0,
515                 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7),
516         },
517         {
518                 .color_index = LED_COLOR_ID_AMBER,
519                 .brightness = 1,
520                 .intensity = 0,
521                 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4),
522         },
523 };
524
525 static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = {
526         {
527                 .color_index = LED_COLOR_ID_RED,
528                 .brightness = 1,
529                 .intensity = 0,
530                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7),
531         },
532         {
533                 .color_index = LED_COLOR_ID_GREEN,
534                 .brightness = 1,
535                 .intensity = 0,
536                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4),
537         },
538         {
539                 .color_index = LED_COLOR_ID_BLUE,
540                 .brightness = 1,
541                 .intensity = 0,
542                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3),
543         },
544         {
545                 .color_index = LED_COLOR_ID_AMBER,
546                 .brightness = 1,
547                 .intensity = 0,
548                 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6),
549         },
550 };
551
552 static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = {
553         {
554                 .color_index = LED_COLOR_ID_RED,
555                 .brightness = 1,
556                 .intensity = 0,
557                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2),
558         },
559         {
560                 .color_index = LED_COLOR_ID_GREEN,
561                 .brightness = 1,
562                 .intensity = 0,
563                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1),
564         },
565         {
566                 .color_index = LED_COLOR_ID_BLUE,
567                 .brightness = 1,
568                 .intensity = 0,
569                 .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0),
570         },
571         {
572                 .color_index = LED_COLOR_ID_AMBER,
573                 .brightness = 1,
574                 .intensity = 0,
575                 .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5),
576         },
577 };
578
579 static struct led_classdev_mc cordoba_mc_led_info[] __initdata = {
580         {
581                 .led_cdev = {
582                         .name = "platled::fp_left",
583                         .brightness = 0,
584                         .max_brightness = 1,
585                         .brightness_set = silicom_mec_led_mc_brightness_set,
586                         .brightness_get = silicom_mec_led_mc_brightness_get,
587                 },
588                 .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info),
589                 .subled_info = cordoba_fp_left_mc_subled_info,
590         },
591         {
592                 .led_cdev = {
593                         .name = "platled::fp_center",
594                         .brightness = 0,
595                         .max_brightness = 1,
596                         .brightness_set = silicom_mec_led_mc_brightness_set,
597                         .brightness_get = silicom_mec_led_mc_brightness_get,
598                 },
599                 .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info),
600                 .subled_info = cordoba_fp_center_mc_subled_info,
601         },
602         {
603                 .led_cdev = {
604                         .name = "platled::fp_right",
605                         .brightness = 0,
606                         .max_brightness = 1,
607                         .brightness_set = silicom_mec_led_mc_brightness_set,
608                         .brightness_get = silicom_mec_led_mc_brightness_get,
609                 },
610                 .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info),
611                 .subled_info = cordoba_fp_right_mc_subled_info,
612         },
613         { },
614 };
615
616 static struct silicom_platform_info silicom_generic_cordoba_info __initdata = {
617         .io_base = MEC_IO_BASE,
618         .io_len = MEC_IO_LEN,
619         .led_info = cordoba_mc_led_info,
620         .gpiochip = &silicom_gpio_chip,
621         .gpio_channels = plat_0222_gpio_channels,
622         .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
623 };
624
625 /*
626  * sysfs interface
627  */
628 static ssize_t efuse_status_show(struct device *dev,
629                                  struct device_attribute *attr,
630                                  char *buf)
631 {
632         u32 reg;
633
634         mutex_lock(&mec_io_mutex);
635         /* Select memory region */
636         outb(IO_REG_BANK, EC_ADDR_MSB);
637         outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB);
638
639         /* Get current data from the address */
640         reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
641         mutex_unlock(&mec_io_mutex);
642
643         efuse_status = reg & 0x1;
644
645         return sysfs_emit(buf, "%u\n", efuse_status);
646 }
647 static DEVICE_ATTR_RO(efuse_status);
648
649 static ssize_t uc_version_show(struct device *dev,
650                                struct device_attribute *attr,
651                                char *buf)
652 {
653         int uc_version;
654         u32 reg;
655
656         mutex_lock(&mec_io_mutex);
657         outb(IO_REG_BANK, EC_ADDR_MSB);
658         outb(DEFAULT_CHAN_LO, EC_ADDR_LSB);
659
660         reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
661         mutex_unlock(&mec_io_mutex);
662         uc_version = FIELD_GET(MEC_VERSION_LOC, reg);
663         if (uc_version >= 192)
664                 return -EINVAL;
665
666         uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 +
667                      FIELD_GET(MEC_VERSION_MINOR, reg);
668
669         mec_uc_version = uc_version;
670
671         return sysfs_emit(buf, "%u\n", mec_uc_version);
672 }
673 static DEVICE_ATTR_RO(uc_version);
674
675 static ssize_t power_cycle_show(struct device *dev,
676                                 struct device_attribute *attr,
677                                 char *buf)
678 {
679         return sysfs_emit(buf, "%u\n", power_cycle);
680 }
681
682 static void powercycle_uc(void)
683 {
684         /* Select memory region */
685         outb(IO_REG_BANK, EC_ADDR_MSB);
686         outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB);
687
688         /* Set to 1 for current data from the address */
689         outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
690 }
691
692 static ssize_t power_cycle_store(struct device *dev,
693                                  struct device_attribute *attr,
694                                  const char *buf, size_t count)
695 {
696         int rc;
697         unsigned int power_cycle_cmd;
698
699         rc = kstrtou32(buf, 0, &power_cycle_cmd);
700         if (rc)
701                 return -EINVAL;
702
703         if (power_cycle_cmd > 0) {
704                 mutex_lock(&mec_io_mutex);
705                 power_cycle = power_cycle_cmd;
706                 powercycle_uc();
707                 mutex_unlock(&mec_io_mutex);
708         }
709
710         return count;
711 }
712 static DEVICE_ATTR_RW(power_cycle);
713
714 static struct attribute *silicom_attrs[] = {
715         &dev_attr_efuse_status.attr,
716         &dev_attr_uc_version.attr,
717         &dev_attr_power_cycle.attr,
718         NULL,
719 };
720 ATTRIBUTE_GROUPS(silicom);
721
722 static struct platform_driver silicom_platform_driver = {
723         .driver = {
724                 .name = "silicom-platform",
725                 .dev_groups = silicom_groups,
726         },
727 };
728
729 static int __init silicom_mc_leds_register(struct device *dev,
730                                            const struct led_classdev_mc *mc_leds)
731 {
732         int size = sizeof(struct mc_subled);
733         struct led_classdev_mc *led;
734         int i, err;
735
736         for (i = 0; mc_leds[i].led_cdev.name; i++) {
737
738                 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
739                 if (!led)
740                         return -ENOMEM;
741                 memcpy(led, &mc_leds[i], sizeof(*led));
742
743                 led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL);
744                 if (!led->subled_info)
745                         return -ENOMEM;
746                 memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size);
747
748                 err = devm_led_classdev_multicolor_register(dev, led);
749                 if (err)
750                         return err;
751         }
752
753         return 0;
754 }
755
756 static u32 rpm_get(void)
757 {
758         u32 reg;
759
760         mutex_lock(&mec_io_mutex);
761         /* Select memory region */
762         outb(IO_REG_BANK, EC_ADDR_MSB);
763         outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
764         reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
765         mutex_unlock(&mec_io_mutex);
766
767         return reg;
768 }
769
770 static u32 temp_get(void)
771 {
772         u32 reg;
773
774         mutex_lock(&mec_io_mutex);
775         /* Select memory region */
776         outb(IO_REG_BANK, EC_ADDR_MSB);
777         outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
778         reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
779         mutex_unlock(&mec_io_mutex);
780
781         return FIELD_GET(MEC_TEMP_LOC, reg) * 100;
782 }
783
784 static umode_t silicom_fan_control_fan_is_visible(const u32 attr)
785 {
786         switch (attr) {
787         case hwmon_fan_input:
788         case hwmon_fan_label:
789                 return 0444;
790         default:
791                 return 0;
792         }
793 }
794
795 static umode_t silicom_fan_control_temp_is_visible(const u32 attr)
796 {
797         switch (attr) {
798         case hwmon_temp_input:
799         case hwmon_temp_label:
800                 return 0444;
801         default:
802                 return 0;
803         }
804 }
805
806 static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val)
807 {
808         switch (attr) {
809         case hwmon_fan_input:
810                 *val = rpm_get();
811                 return 0;
812         default:
813                 return -EOPNOTSUPP;
814         }
815 }
816
817 static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val)
818 {
819         switch (attr) {
820         case hwmon_temp_input:
821                 *val = temp_get();
822                 return 0;
823         default:
824                 return -EOPNOTSUPP;
825         }
826 }
827
828 static umode_t silicom_fan_control_is_visible(const void *data,
829                                               enum hwmon_sensor_types type,
830                                               u32 attr, int channel)
831 {
832         switch (type) {
833         case hwmon_fan:
834                 return silicom_fan_control_fan_is_visible(attr);
835         case hwmon_temp:
836                 return silicom_fan_control_temp_is_visible(attr);
837         default:
838                 return 0;
839         }
840 }
841
842 static int silicom_fan_control_read(struct device *dev,
843                                     enum hwmon_sensor_types type,
844                                     u32 attr, int channel,
845                                     long *val)
846 {
847         switch (type) {
848         case hwmon_fan:
849                 return silicom_fan_control_read_fan(dev, attr, val);
850         case hwmon_temp:
851                 return silicom_fan_control_read_temp(dev, attr, val);
852         default:
853                 return -EOPNOTSUPP;
854         }
855 }
856
857 static int silicom_fan_control_read_labels(struct device *dev,
858                                            enum hwmon_sensor_types type,
859                                            u32 attr, int channel,
860                                            const char **str)
861 {
862         switch (type) {
863         case hwmon_fan:
864                 *str = "Silicom_platform: Fan Speed";
865                 return 0;
866         case hwmon_temp:
867                 *str = "Silicom_platform: Thermostat Sensor";
868                 return 0;
869         default:
870                 return -EOPNOTSUPP;
871         }
872 }
873
874 static const struct hwmon_ops silicom_fan_control_hwmon_ops = {
875         .is_visible = silicom_fan_control_is_visible,
876         .read = silicom_fan_control_read,
877         .read_string = silicom_fan_control_read_labels,
878 };
879
880 static const struct hwmon_chip_info silicom_chip_info = {
881         .ops = &silicom_fan_control_hwmon_ops,
882         .info = silicom_fan_control_info,
883 };
884
885 static int __init silicom_platform_probe(struct platform_device *device)
886 {
887         struct device *hwmon_dev;
888         u8 magic, ver;
889         int err;
890
891         if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) {
892                 dev_err(&device->dev, "couldn't reserve MEC io ports\n");
893                 return -EBUSY;
894         }
895
896         /* Sanity check magic number read for EC */
897         outb(IO_REG_BANK, MEC_ADDR);
898         magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
899         ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI));
900         dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver);
901
902         if (magic != SILICOM_MEC_MAGIC) {
903                 dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic);
904                 return -ENODEV;
905         }
906
907         err = silicom_mc_leds_register(&device->dev, silicom_led_info);
908         if (err) {
909                 dev_err(&device->dev, "Failed to register LEDs\n");
910                 return err;
911         }
912
913         err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip,
914                                      silicom_gpio_channels);
915         if (err) {
916                 dev_err(&device->dev, "Failed to register gpiochip: %d\n", err);
917                 return err;
918         }
919
920         hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL,
921                                                          &silicom_chip_info, NULL);
922         err = PTR_ERR_OR_ZERO(hwmon_dev);
923         if (err) {
924                 dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err);
925                 return err;
926         }
927
928         return err;
929 }
930
931 static int __init silicom_platform_info_init(const struct dmi_system_id *id)
932 {
933         struct silicom_platform_info *info = id->driver_data;
934
935         silicom_led_info = info->led_info;
936         silicom_gpio_channels = info->gpio_channels;
937         silicom_gpiochip = info->gpiochip;
938         silicom_gpiochip->ngpio = info->ngpio;
939
940         return 1;
941 }
942
943 static const struct dmi_system_id silicom_dmi_ids[] __initconst = {
944         {
945                 .callback = silicom_platform_info_init,
946                 .ident = "Silicom Cordoba (Generic)",
947                 .matches = {
948                         DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
949                         DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"),
950                 },
951                 .driver_data = &silicom_generic_cordoba_info,
952         },
953         {
954                 .callback = silicom_platform_info_init,
955                 .ident = "Silicom Cordoba (Generic)",
956                 .matches = {
957                         DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
958                         DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"),
959                 },
960                 .driver_data = &silicom_generic_cordoba_info,
961         },
962         {
963                  .callback = silicom_platform_info_init,
964                  .ident = "Silicom Cordoba (plat_0222)",
965                  .matches = {
966                         DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
967                         DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"),
968                  },
969                 .driver_data = &silicom_plat_0222_cordoba_info,
970         },
971         { },
972 };
973 MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
974
975 static int __init silicom_platform_init(void)
976 {
977         if (!dmi_check_system(silicom_dmi_ids)) {
978                 pr_err("No DMI match for this platform\n");
979                 return -ENODEV;
980         }
981         silicom_platform_dev = platform_create_bundle(&silicom_platform_driver,
982                                                       silicom_platform_probe,
983                                                       NULL, 0, NULL, 0);
984
985         return PTR_ERR_OR_ZERO(silicom_platform_dev);
986 }
987
988 static void __exit silicom_platform_exit(void)
989 {
990         platform_device_unregister(silicom_platform_dev);
991         platform_driver_unregister(&silicom_platform_driver);
992 }
993
994 module_init(silicom_platform_init);
995 module_exit(silicom_platform_exit);
996
997 MODULE_LICENSE("GPL");
998 MODULE_AUTHOR("Henry Shi <[email protected]>");
999 MODULE_DESCRIPTION("Platform driver for Silicom network appliances");
This page took 0.085205 seconds and 4 git commands to generate.