Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 /*
0004  * Cisco Meraki MX100 (Tinkerbell) board platform driver
0005  *
0006  * Based off of arch/x86/platform/meraki/tink.c from the
0007  * Meraki GPL release meraki-firmware-sources-r23-20150601
0008  *
0009  * Format inspired by platform/x86/pcengines-apuv2.c
0010  *
0011  * Copyright (C) 2021 Chris Blake <chrisrblake93@gmail.com>
0012  */
0013 
0014 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0015 
0016 #include <linux/dmi.h>
0017 #include <linux/err.h>
0018 #include <linux/gpio_keys.h>
0019 #include <linux/gpio/machine.h>
0020 #include <linux/input.h>
0021 #include <linux/io.h>
0022 #include <linux/kernel.h>
0023 #include <linux/leds.h>
0024 #include <linux/module.h>
0025 #include <linux/platform_device.h>
0026 
0027 #define TINK_GPIO_DRIVER_NAME "gpio_ich"
0028 
0029 /* LEDs */
0030 static const struct gpio_led tink_leds[] = {
0031     {
0032         .name = "mx100:green:internet",
0033         .default_trigger = "default-on",
0034     },
0035     {
0036         .name = "mx100:green:lan2",
0037     },
0038     {
0039         .name = "mx100:green:lan3",
0040     },
0041     {
0042         .name = "mx100:green:lan4",
0043     },
0044     {
0045         .name = "mx100:green:lan5",
0046     },
0047     {
0048         .name = "mx100:green:lan6",
0049     },
0050     {
0051         .name = "mx100:green:lan7",
0052     },
0053     {
0054         .name = "mx100:green:lan8",
0055     },
0056     {
0057         .name = "mx100:green:lan9",
0058     },
0059     {
0060         .name = "mx100:green:lan10",
0061     },
0062     {
0063         .name = "mx100:green:lan11",
0064     },
0065     {
0066         .name = "mx100:green:ha",
0067     },
0068     {
0069         .name = "mx100:orange:ha",
0070     },
0071     {
0072         .name = "mx100:green:usb",
0073     },
0074     {
0075         .name = "mx100:orange:usb",
0076     },
0077 };
0078 
0079 static const struct gpio_led_platform_data tink_leds_pdata = {
0080     .num_leds   = ARRAY_SIZE(tink_leds),
0081     .leds       = tink_leds,
0082 };
0083 
0084 static struct gpiod_lookup_table tink_leds_table = {
0085     .dev_id = "leds-gpio",
0086     .table = {
0087         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 11,
0088                 NULL, 0, GPIO_ACTIVE_LOW),
0089         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 18,
0090                 NULL, 1, GPIO_ACTIVE_HIGH),
0091         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 20,
0092                 NULL, 2, GPIO_ACTIVE_HIGH),
0093         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 22,
0094                 NULL, 3, GPIO_ACTIVE_HIGH),
0095         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 23,
0096                 NULL, 4, GPIO_ACTIVE_HIGH),
0097         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 32,
0098                 NULL, 5, GPIO_ACTIVE_HIGH),
0099         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 34,
0100                 NULL, 6, GPIO_ACTIVE_HIGH),
0101         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 35,
0102                 NULL, 7, GPIO_ACTIVE_HIGH),
0103         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 36,
0104                 NULL, 8, GPIO_ACTIVE_HIGH),
0105         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 37,
0106                 NULL, 9, GPIO_ACTIVE_HIGH),
0107         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 48,
0108                 NULL, 10, GPIO_ACTIVE_HIGH),
0109         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 16,
0110                 NULL, 11, GPIO_ACTIVE_LOW),
0111         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 7,
0112                 NULL, 12, GPIO_ACTIVE_LOW),
0113         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 21,
0114                 NULL, 13, GPIO_ACTIVE_LOW),
0115         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 19,
0116                 NULL, 14, GPIO_ACTIVE_LOW),
0117         {} /* Terminating entry */
0118     }
0119 };
0120 
0121 /* Reset Button */
0122 static struct gpio_keys_button tink_buttons[] = {
0123     {
0124         .desc           = "Reset",
0125         .type           = EV_KEY,
0126         .code           = KEY_RESTART,
0127         .active_low             = 1,
0128         .debounce_interval      = 100,
0129     },
0130 };
0131 
0132 static const struct gpio_keys_platform_data tink_buttons_pdata = {
0133     .buttons    = tink_buttons,
0134     .nbuttons   = ARRAY_SIZE(tink_buttons),
0135     .poll_interval  = 20,
0136     .rep        = 0,
0137     .name       = "mx100-keys",
0138 };
0139 
0140 static struct gpiod_lookup_table tink_keys_table = {
0141     .dev_id = "gpio-keys-polled",
0142     .table = {
0143         GPIO_LOOKUP_IDX(TINK_GPIO_DRIVER_NAME, 60,
0144                 NULL, 0, GPIO_ACTIVE_LOW),
0145         {} /* Terminating entry */
0146     }
0147 };
0148 
0149 /* Board setup */
0150 static const struct dmi_system_id tink_systems[] __initconst = {
0151     {
0152         .matches = {
0153             DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Cisco"),
0154             DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MX100-HW"),
0155         },
0156     },
0157     {} /* Terminating entry */
0158 };
0159 MODULE_DEVICE_TABLE(dmi, tink_systems);
0160 
0161 static struct platform_device *tink_leds_pdev;
0162 static struct platform_device *tink_keys_pdev;
0163 
0164 static struct platform_device * __init tink_create_dev(
0165     const char *name, const void *pdata, size_t sz)
0166 {
0167     struct platform_device *pdev;
0168 
0169     pdev = platform_device_register_data(NULL,
0170         name, PLATFORM_DEVID_NONE, pdata, sz);
0171     if (IS_ERR(pdev))
0172         pr_err("failed registering %s: %ld\n", name, PTR_ERR(pdev));
0173 
0174     return pdev;
0175 }
0176 
0177 static int __init tink_board_init(void)
0178 {
0179     int ret;
0180 
0181     if (!dmi_first_match(tink_systems))
0182         return -ENODEV;
0183 
0184     /*
0185      * We need to make sure that GPIO60 isn't set to native mode as is default since it's our
0186      * Reset Button. To do this, write to GPIO_USE_SEL2 to have GPIO60 set to GPIO mode.
0187      * This is documented on page 1609 of the PCH datasheet, order number 327879-005US
0188      */
0189     outl(inl(0x530) | BIT(28), 0x530);
0190 
0191     gpiod_add_lookup_table(&tink_leds_table);
0192     gpiod_add_lookup_table(&tink_keys_table);
0193 
0194     tink_leds_pdev = tink_create_dev("leds-gpio",
0195         &tink_leds_pdata, sizeof(tink_leds_pdata));
0196     if (IS_ERR(tink_leds_pdev)) {
0197         ret = PTR_ERR(tink_leds_pdev);
0198         goto err;
0199     }
0200 
0201     tink_keys_pdev = tink_create_dev("gpio-keys-polled",
0202         &tink_buttons_pdata, sizeof(tink_buttons_pdata));
0203     if (IS_ERR(tink_keys_pdev)) {
0204         ret = PTR_ERR(tink_keys_pdev);
0205         platform_device_unregister(tink_leds_pdev);
0206         goto err;
0207     }
0208 
0209     return 0;
0210 
0211 err:
0212     gpiod_remove_lookup_table(&tink_keys_table);
0213     gpiod_remove_lookup_table(&tink_leds_table);
0214     return ret;
0215 }
0216 module_init(tink_board_init);
0217 
0218 static void __exit tink_board_exit(void)
0219 {
0220     platform_device_unregister(tink_keys_pdev);
0221     platform_device_unregister(tink_leds_pdev);
0222     gpiod_remove_lookup_table(&tink_keys_table);
0223     gpiod_remove_lookup_table(&tink_leds_table);
0224 }
0225 module_exit(tink_board_exit);
0226 
0227 MODULE_AUTHOR("Chris Blake <chrisrblake93@gmail.com>");
0228 MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver");
0229 MODULE_LICENSE("GPL");
0230 MODULE_ALIAS("platform:meraki-mx100");