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/machine.h> 19 #include <linux/gpio/property.h> 20 #include <linux/input-event-codes.h> 21 #include <linux/io.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/platform_device.h> 25 #include <linux/property.h> 26 27 #define TINK_GPIO_DRIVER_NAME "gpio_ich" 28 29 static const struct software_node gpio_ich_node = { 30 .name = TINK_GPIO_DRIVER_NAME, 31 }; 32 33 /* LEDs */ 34 static const struct software_node tink_gpio_leds_node = { 35 .name = "meraki-mx100-leds", 36 }; 37 38 static const struct property_entry tink_internet_led_props[] = { 39 PROPERTY_ENTRY_STRING("label", "mx100:green:internet"), 40 PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"), 41 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW), 42 { } 43 }; 44 45 static const struct software_node tink_internet_led_node = { 46 .name = "internet-led", 47 .parent = &tink_gpio_leds_node, 48 .properties = tink_internet_led_props, 49 }; 50 51 static const struct property_entry tink_lan2_led_props[] = { 52 PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"), 53 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH), 54 { } 55 }; 56 57 static const struct software_node tink_lan2_led_node = { 58 .name = "lan2-led", 59 .parent = &tink_gpio_leds_node, 60 .properties = tink_lan2_led_props, 61 }; 62 63 static const struct property_entry tink_lan3_led_props[] = { 64 PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"), 65 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH), 66 { } 67 }; 68 69 static const struct software_node tink_lan3_led_node = { 70 .name = "lan3-led", 71 .parent = &tink_gpio_leds_node, 72 .properties = tink_lan3_led_props, 73 }; 74 75 static const struct property_entry tink_lan4_led_props[] = { 76 PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"), 77 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH), 78 { } 79 }; 80 81 static const struct software_node tink_lan4_led_node = { 82 .name = "lan4-led", 83 .parent = &tink_gpio_leds_node, 84 .properties = tink_lan4_led_props, 85 }; 86 87 static const struct property_entry tink_lan5_led_props[] = { 88 PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"), 89 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH), 90 { } 91 }; 92 93 static const struct software_node tink_lan5_led_node = { 94 .name = "lan5-led", 95 .parent = &tink_gpio_leds_node, 96 .properties = tink_lan5_led_props, 97 }; 98 99 static const struct property_entry tink_lan6_led_props[] = { 100 PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"), 101 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH), 102 { } 103 }; 104 105 static const struct software_node tink_lan6_led_node = { 106 .name = "lan6-led", 107 .parent = &tink_gpio_leds_node, 108 .properties = tink_lan6_led_props, 109 }; 110 111 static const struct property_entry tink_lan7_led_props[] = { 112 PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"), 113 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH), 114 { } 115 }; 116 117 static const struct software_node tink_lan7_led_node = { 118 .name = "lan7-led", 119 .parent = &tink_gpio_leds_node, 120 .properties = tink_lan7_led_props, 121 }; 122 123 static const struct property_entry tink_lan8_led_props[] = { 124 PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"), 125 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH), 126 { } 127 }; 128 129 static const struct software_node tink_lan8_led_node = { 130 .name = "lan8-led", 131 .parent = &tink_gpio_leds_node, 132 .properties = tink_lan8_led_props, 133 }; 134 135 static const struct property_entry tink_lan9_led_props[] = { 136 PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"), 137 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH), 138 { } 139 }; 140 141 static const struct software_node tink_lan9_led_node = { 142 .name = "lan9-led", 143 .parent = &tink_gpio_leds_node, 144 .properties = tink_lan9_led_props, 145 }; 146 147 static const struct property_entry tink_lan10_led_props[] = { 148 PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"), 149 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH), 150 { } 151 }; 152 153 static const struct software_node tink_lan10_led_node = { 154 .name = "lan10-led", 155 .parent = &tink_gpio_leds_node, 156 .properties = tink_lan10_led_props, 157 }; 158 159 static const struct property_entry tink_lan11_led_props[] = { 160 PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"), 161 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH), 162 { } 163 }; 164 165 static const struct software_node tink_lan11_led_node = { 166 .name = "lan11-led", 167 .parent = &tink_gpio_leds_node, 168 .properties = tink_lan11_led_props, 169 }; 170 171 static const struct property_entry tink_ha_green_led_props[] = { 172 PROPERTY_ENTRY_STRING("label", "mx100:green:ha"), 173 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW), 174 { } 175 }; 176 177 static const struct software_node tink_ha_green_led_node = { 178 .name = "ha-green-led", 179 .parent = &tink_gpio_leds_node, 180 .properties = tink_ha_green_led_props, 181 }; 182 183 static const struct property_entry tink_ha_orange_led_props[] = { 184 PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"), 185 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW), 186 { } 187 }; 188 189 static const struct software_node tink_ha_orange_led_node = { 190 .name = "ha-orange-led", 191 .parent = &tink_gpio_leds_node, 192 .properties = tink_ha_orange_led_props, 193 }; 194 195 static const struct property_entry tink_usb_green_led_props[] = { 196 PROPERTY_ENTRY_STRING("label", "mx100:green:usb"), 197 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW), 198 { } 199 }; 200 201 static const struct software_node tink_usb_green_led_node = { 202 .name = "usb-green-led", 203 .parent = &tink_gpio_leds_node, 204 .properties = tink_usb_green_led_props, 205 }; 206 207 static const struct property_entry tink_usb_orange_led_props[] = { 208 PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"), 209 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW), 210 { } 211 }; 212 213 static const struct software_node tink_usb_orange_led_node = { 214 .name = "usb-orange-led", 215 .parent = &tink_gpio_leds_node, 216 .properties = tink_usb_orange_led_props, 217 }; 218 219 /* Reset Button */ 220 static const struct property_entry tink_gpio_keys_props[] = { 221 PROPERTY_ENTRY_U32("poll-interval", 20), 222 { } 223 }; 224 225 static const struct software_node tink_gpio_keys_node = { 226 .name = "mx100-keys", 227 .properties = tink_gpio_keys_props, 228 }; 229 230 static const struct property_entry tink_reset_key_props[] = { 231 PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), 232 PROPERTY_ENTRY_STRING("label", "Reset"), 233 PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW), 234 PROPERTY_ENTRY_U32("linux,input-type", EV_KEY), 235 PROPERTY_ENTRY_U32("debounce-interval", 100), 236 { } 237 }; 238 239 static const struct software_node tink_reset_key_node = { 240 .name = "reset", 241 .parent = &tink_gpio_keys_node, 242 .properties = tink_reset_key_props, 243 }; 244 245 static const struct software_node *tink_swnodes[] = { 246 &gpio_ich_node, 247 /* LEDs nodes */ 248 &tink_gpio_leds_node, 249 &tink_internet_led_node, 250 &tink_lan2_led_node, 251 &tink_lan3_led_node, 252 &tink_lan4_led_node, 253 &tink_lan5_led_node, 254 &tink_lan6_led_node, 255 &tink_lan7_led_node, 256 &tink_lan8_led_node, 257 &tink_lan9_led_node, 258 &tink_lan10_led_node, 259 &tink_lan11_led_node, 260 &tink_ha_green_led_node, 261 &tink_ha_orange_led_node, 262 &tink_usb_green_led_node, 263 &tink_usb_orange_led_node, 264 /* Keys nodes */ 265 &tink_gpio_keys_node, 266 &tink_reset_key_node, 267 NULL 268 }; 269 270 /* Board setup */ 271 static const struct dmi_system_id tink_systems[] __initconst = { 272 { 273 .matches = { 274 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"), 275 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"), 276 }, 277 }, 278 {} /* Terminating entry */ 279 }; 280 MODULE_DEVICE_TABLE(dmi, tink_systems); 281 282 static struct platform_device *tink_leds_pdev; 283 static struct platform_device *tink_keys_pdev; 284 285 static int __init tink_board_init(void) 286 { 287 struct platform_device_info keys_info = { 288 .name = "gpio-keys-polled", 289 .id = PLATFORM_DEVID_NONE, 290 }; 291 struct platform_device_info leds_info = { 292 .name = "leds-gpio", 293 .id = PLATFORM_DEVID_NONE, 294 }; 295 int err; 296 297 if (!dmi_first_match(tink_systems)) 298 return -ENODEV; 299 300 /* 301 * We need to make sure that GPIO60 isn't set to native mode as is default since it's our 302 * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode. 303 * This is documented on page 1609 of the PCH datasheet, order number 327879-005US 304 */ 305 outl(inl(0x530) | BIT(28), 0x530); 306 307 err = software_node_register_node_group(tink_swnodes); 308 if (err) { 309 pr_err("failed to register software nodes: %d\n", err); 310 return err; 311 } 312 313 leds_info.fwnode = software_node_fwnode(&tink_gpio_leds_node); 314 tink_leds_pdev = platform_device_register_full(&leds_info); 315 if (IS_ERR(tink_leds_pdev)) { 316 err = PTR_ERR(tink_leds_pdev); 317 pr_err("failed to create LED device: %d\n", err); 318 goto err_unregister_swnodes; 319 } 320 321 keys_info.fwnode = software_node_fwnode(&tink_gpio_keys_node); 322 tink_keys_pdev = platform_device_register_full(&keys_info); 323 if (IS_ERR(tink_keys_pdev)) { 324 err = PTR_ERR(tink_keys_pdev); 325 pr_err("failed to create key device: %d\n", err); 326 goto err_unregister_leds; 327 } 328 329 return 0; 330 331 err_unregister_leds: 332 platform_device_unregister(tink_leds_pdev); 333 err_unregister_swnodes: 334 software_node_unregister_node_group(tink_swnodes); 335 return err; 336 } 337 module_init(tink_board_init); 338 339 static void __exit tink_board_exit(void) 340 { 341 platform_device_unregister(tink_keys_pdev); 342 platform_device_unregister(tink_leds_pdev); 343 software_node_unregister_node_group(tink_swnodes); 344 } 345 module_exit(tink_board_exit); 346 347 MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>"); 348 MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver"); 349 MODULE_LICENSE("GPL"); 350 MODULE_ALIAS("platform:meraki-mx100"); 351