231 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Cisco Meraki MX100 (Tinkerbell) board platform driver5 *6 * Based off of arch/x86/platform/meraki/tink.c from the7 * Meraki GPL release meraki-firmware-sources-r23-201506018 *9 * Format inspired by platform/x86/pcengines-apuv2.c10 *11 * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com>12 */13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt15 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 */30static 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 79static const struct gpio_led_platform_data tink_leds_pdata = {80 .num_leds = ARRAY_SIZE(tink_leds),81 .leds = tink_leds,82};83 84static 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 */122static 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 132static 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 140static 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 */150static 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};159MODULE_DEVICE_TABLE(dmi, tink_systems);160 161static struct platform_device *tink_leds_pdev;162static struct platform_device *tink_keys_pdev;163 164static 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 177static 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 our186 * 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-005US188 */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 211err:212 gpiod_remove_lookup_table(&tink_keys_table);213 gpiod_remove_lookup_table(&tink_leds_table);214 return ret;215}216module_init(tink_board_init);217 218static 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}225module_exit(tink_board_exit);226 227MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");228MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");229MODULE_LICENSE("GPL");230MODULE_ALIAS("platform:meraki-mx100");231