1 // SPDX-License-Identifier: GPL-2.0+ 2 3 /* 4 * Cisco Meraki MX100 (Tinkerbell) board platform driver 5 * 6 * Based off of arch/x86/platform/meraki/tink.c from the 7 * Meraki GPL release meraki-firmware-sources-r23-20150601 8 * 9 * Format inspired by platform/x86/pcengines-apuv2.c 10 * 11 * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com> 12 */ 13 14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 16 #include <linux/dmi.h> 17 #include <linux/err.h> 18 #include <linux/gpio_keys.h> 19 #include <linux/gpio/machine.h> 20 #include <linux/input.h> 21 #include <linux/io.h> 22 #include <linux/kernel.h> 23 #include <linux/leds.h> 24 #include <linux/module.h> 25 #include <linux/platform_device.h> 26 27 #define TINK_GPIO_DRIVER_NAME "gpio_ich" 28 29 /* LEDs */ 30 static const struct gpio_led tink_leds[] = { 31 { 32 .name = "mx100:green:internet", 33 .default_trigger = "default-on", 34 }, 35 { 36 .name = "mx100:green:lan2", 37 }, 38 { 39 .name = "mx100:green:lan3", 40 }, 41 { 42 .name = "mx100:green:lan4", 43 }, 44 { 45 .name = "mx100:green:lan5", 46 }, 47 { 48 .name = "mx100:green:lan6", 49 }, 50 { 51 .name = "mx100:green:lan7", 52 }, 53 { 54 .name = "mx100:green:lan8", 55 }, 56 { 57 .name = "mx100:green:lan9", 58 }, 59 { 60 .name = "mx100:green:lan10", 61 }, 62 { 63 .name = "mx100:green:lan11", 64 }, 65 { 66 .name = "mx100:green:ha", 67 }, 68 { 69 .name = "mx100:orange:ha", 70 }, 71 { 72 .name = "mx100:green:usb", 73 }, 74 { 75 .name = "mx100:orange:usb", 76 }, 77 }; 78 79 static const struct gpio_led_platform_data tink_leds_pdata = { 80 .num_leds = ARRAY_SIZE(tink_leds), 81 .leds = tink_leds, 82 }; 83 84 static struct gpiod_lookup_table tink_leds_table = { 85 .dev_id = "leds-gpio", 86 .table = { 87 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 11, 88 NULL, 0, GPIO_ACTIVE_LOW), 89 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 18, 90 NULL, 1, GPIO_ACTIVE_HIGH), 91 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 20, 92 NULL, 2, GPIO_ACTIVE_HIGH), 93 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 22, 94 NULL, 3, GPIO_ACTIVE_HIGH), 95 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 23, 96 NULL, 4, GPIO_ACTIVE_HIGH), 97 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 32, 98 NULL, 5, GPIO_ACTIVE_HIGH), 99 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 34, 100 NULL, 6, GPIO_ACTIVE_HIGH), 101 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 35, 102 NULL, 7, GPIO_ACTIVE_HIGH), 103 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 36, 104 NULL, 8, GPIO_ACTIVE_HIGH), 105 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 37, 106 NULL, 9, GPIO_ACTIVE_HIGH), 107 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 48, 108 NULL, 10, GPIO_ACTIVE_HIGH), 109 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 16, 110 NULL, 11, GPIO_ACTIVE_LOW), 111 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 7, 112 NULL, 12, GPIO_ACTIVE_LOW), 113 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 21, 114 NULL, 13, GPIO_ACTIVE_LOW), 115 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 19, 116 NULL, 14, GPIO_ACTIVE_LOW), 117 {} /* Terminating entry */ 118 } 119 }; 120 121 /* Reset Button */ 122 static struct gpio_keys_button tink_buttons[] = { 123 { 124 .desc = "Reset", 125 .type = EV_KEY, 126 .code = KEY_RESTART, 127 .active_low = 1, 128 .debounce_interval = 100, 129 }, 130 }; 131 132 static const struct gpio_keys_platform_data tink_buttons_pdata = { 133 .buttons = tink_buttons, 134 .nbuttons = ARRAY_SIZE(tink_buttons), 135 .poll_interval = 20, 136 .rep = 0, 137 .name = "mx100-keys", 138 }; 139 140 static struct gpiod_lookup_table tink_keys_table = { 141 .dev_id = "gpio-keys-polled", 142 .table = { 143 GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 60, 144 NULL, 0, GPIO_ACTIVE_LOW), 145 {} /* Terminating entry */ 146 } 147 }; 148 149 /* Board setup */ 150 static const struct dmi_system_id tink_systems[] __initconst = { 151 { 152 .matches = { 153 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"), 154 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"), 155 }, 156 }, 157 {} /* Terminating entry */ 158 }; 159 MODULE_DEVICE_TABLE(dmi, tink_systems); 160 161 static struct platform_device *tink_leds_pdev; 162 static struct platform_device *tink_keys_pdev; 163 164 static struct platform_device * __init tink_create_dev( 165 const char *name, const void *pdata, size_t sz) 166 { 167 struct platform_device *pdev; 168 169 pdev = platform_device_register_data(NULL, 170 name, PLATFORM_DEVID_NONE, pdata, sz); 171 if (IS_ERR(pdev)) 172 pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev)); 173 174 return pdev; 175 } 176 177 static int __init tink_board_init(void) 178 { 179 int ret; 180 181 if (!dmi_first_match(tink_systems)) 182 return -ENODEV; 183 184 /* 185 * We need to make sure that GPIO60 isn't set to native mode as is default since it's our 186 * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode. 187 * This is documented on page 1609 of the PCH datasheet, order number 327879-005US 188 */ 189 outl(inl(0x530) | BIT(28), 0x530); 190 191 gpiod_add_lookup_table(&tink_leds_table); 192 gpiod_add_lookup_table(&tink_keys_table); 193 194 tink_leds_pdev = tink_create_dev("leds-gpio", 195 &tink_leds_pdata, sizeof(tink_leds_pdata)); 196 if (IS_ERR(tink_leds_pdev)) { 197 ret = PTR_ERR(tink_leds_pdev); 198 goto err; 199 } 200 201 tink_keys_pdev = tink_create_dev("gpio-keys-polled", 202 &tink_buttons_pdata, sizeof(tink_buttons_pdata)); 203 if (IS_ERR(tink_keys_pdev)) { 204 ret = PTR_ERR(tink_keys_pdev); 205 platform_device_unregister(tink_leds_pdev); 206 goto err; 207 } 208 209 return 0; 210 211 err: 212 gpiod_remove_lookup_table(&tink_keys_table); 213 gpiod_remove_lookup_table(&tink_leds_table); 214 return ret; 215 } 216 module_init(tink_board_init); 217 218 static void __exit tink_board_exit(void) 219 { 220 platform_device_unregister(tink_keys_pdev); 221 platform_device_unregister(tink_leds_pdev); 222 gpiod_remove_lookup_table(&tink_keys_table); 223 gpiod_remove_lookup_table(&tink_leds_table); 224 } 225 module_exit(tink_board_exit); 226 227 MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>"); 228 MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver"); 229 MODULE_LICENSE("GPL"); 230 MODULE_ALIAS("platform:meraki-mx100"); 231