18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Support for rfkill on some Fujitsu-Siemens Amilo laptops. 48c2ecf20Sopenharmony_ci * Copyright 2011 Ben Hutchings. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Based in part on the fsam7440 driver, which is: 78c2ecf20Sopenharmony_ci * Copyright 2005 Alejandro Vidal Mata & Javier Vidal Mata. 88c2ecf20Sopenharmony_ci * and on the fsaa1655g driver, which is: 98c2ecf20Sopenharmony_ci * Copyright 2006 Martin Večeřa. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/dmi.h> 148c2ecf20Sopenharmony_ci#include <linux/i8042.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/rfkill.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * These values were obtained from disassembling and debugging the 228c2ecf20Sopenharmony_ci * PM.exe program installed in the Fujitsu-Siemens AMILO A1655G 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#define A1655_WIFI_COMMAND 0x10C5 258c2ecf20Sopenharmony_ci#define A1655_WIFI_ON 0x25 268c2ecf20Sopenharmony_ci#define A1655_WIFI_OFF 0x45 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int amilo_a1655_rfkill_set_block(void *data, bool blocked) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci u8 param = blocked ? A1655_WIFI_OFF : A1655_WIFI_ON; 318c2ecf20Sopenharmony_ci int rc; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci i8042_lock_chip(); 348c2ecf20Sopenharmony_ci rc = i8042_command(¶m, A1655_WIFI_COMMAND); 358c2ecf20Sopenharmony_ci i8042_unlock_chip(); 368c2ecf20Sopenharmony_ci return rc; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic const struct rfkill_ops amilo_a1655_rfkill_ops = { 408c2ecf20Sopenharmony_ci .set_block = amilo_a1655_rfkill_set_block 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * These values were obtained from disassembling the PM.exe program 458c2ecf20Sopenharmony_ci * installed in the Fujitsu-Siemens AMILO M 7440 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define M7440_PORT1 0x118f 488c2ecf20Sopenharmony_ci#define M7440_PORT2 0x118e 498c2ecf20Sopenharmony_ci#define M7440_RADIO_ON1 0x12 508c2ecf20Sopenharmony_ci#define M7440_RADIO_ON2 0x80 518c2ecf20Sopenharmony_ci#define M7440_RADIO_OFF1 0x10 528c2ecf20Sopenharmony_ci#define M7440_RADIO_OFF2 0x00 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int amilo_m7440_rfkill_set_block(void *data, bool blocked) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci u8 val1 = blocked ? M7440_RADIO_OFF1 : M7440_RADIO_ON1; 578c2ecf20Sopenharmony_ci u8 val2 = blocked ? M7440_RADIO_OFF2 : M7440_RADIO_ON2; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci outb(val1, M7440_PORT1); 608c2ecf20Sopenharmony_ci outb(val2, M7440_PORT2); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Check whether the state has changed correctly */ 638c2ecf20Sopenharmony_ci if (inb(M7440_PORT1) != val1 || inb(M7440_PORT2) != val2) 648c2ecf20Sopenharmony_ci return -EIO; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct rfkill_ops amilo_m7440_rfkill_ops = { 708c2ecf20Sopenharmony_ci .set_block = amilo_m7440_rfkill_set_block 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct dmi_system_id amilo_rfkill_id_table[] = { 748c2ecf20Sopenharmony_ci { 758c2ecf20Sopenharmony_ci .matches = { 768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "AMILO A1655"), 788c2ecf20Sopenharmony_ci }, 798c2ecf20Sopenharmony_ci .driver_data = (void *)&amilo_a1655_rfkill_ops 808c2ecf20Sopenharmony_ci }, 818c2ecf20Sopenharmony_ci { 828c2ecf20Sopenharmony_ci .matches = { 838c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 848c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "AMILO L1310"), 858c2ecf20Sopenharmony_ci }, 868c2ecf20Sopenharmony_ci .driver_data = (void *)&amilo_a1655_rfkill_ops 878c2ecf20Sopenharmony_ci }, 888c2ecf20Sopenharmony_ci { 898c2ecf20Sopenharmony_ci .matches = { 908c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 918c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_NAME, "AMILO M7440"), 928c2ecf20Sopenharmony_ci }, 938c2ecf20Sopenharmony_ci .driver_data = (void *)&amilo_m7440_rfkill_ops 948c2ecf20Sopenharmony_ci }, 958c2ecf20Sopenharmony_ci {} 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct platform_device *amilo_rfkill_pdev; 998c2ecf20Sopenharmony_cistatic struct rfkill *amilo_rfkill_dev; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic int amilo_rfkill_probe(struct platform_device *device) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int rc; 1048c2ecf20Sopenharmony_ci const struct dmi_system_id *system_id = 1058c2ecf20Sopenharmony_ci dmi_first_match(amilo_rfkill_id_table); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (!system_id) 1088c2ecf20Sopenharmony_ci return -ENXIO; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev, 1118c2ecf20Sopenharmony_ci RFKILL_TYPE_WLAN, 1128c2ecf20Sopenharmony_ci system_id->driver_data, NULL); 1138c2ecf20Sopenharmony_ci if (!amilo_rfkill_dev) 1148c2ecf20Sopenharmony_ci return -ENOMEM; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci rc = rfkill_register(amilo_rfkill_dev); 1178c2ecf20Sopenharmony_ci if (rc) 1188c2ecf20Sopenharmony_ci goto fail; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cifail: 1238c2ecf20Sopenharmony_ci rfkill_destroy(amilo_rfkill_dev); 1248c2ecf20Sopenharmony_ci return rc; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int amilo_rfkill_remove(struct platform_device *device) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci rfkill_unregister(amilo_rfkill_dev); 1308c2ecf20Sopenharmony_ci rfkill_destroy(amilo_rfkill_dev); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic struct platform_driver amilo_rfkill_driver = { 1358c2ecf20Sopenharmony_ci .driver = { 1368c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 1378c2ecf20Sopenharmony_ci }, 1388c2ecf20Sopenharmony_ci .probe = amilo_rfkill_probe, 1398c2ecf20Sopenharmony_ci .remove = amilo_rfkill_remove, 1408c2ecf20Sopenharmony_ci}; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int __init amilo_rfkill_init(void) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int rc; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (dmi_first_match(amilo_rfkill_id_table) == NULL) 1478c2ecf20Sopenharmony_ci return -ENODEV; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci rc = platform_driver_register(&amilo_rfkill_driver); 1508c2ecf20Sopenharmony_ci if (rc) 1518c2ecf20Sopenharmony_ci return rc; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci amilo_rfkill_pdev = platform_device_register_simple(KBUILD_MODNAME, -1, 1548c2ecf20Sopenharmony_ci NULL, 0); 1558c2ecf20Sopenharmony_ci if (IS_ERR(amilo_rfkill_pdev)) { 1568c2ecf20Sopenharmony_ci rc = PTR_ERR(amilo_rfkill_pdev); 1578c2ecf20Sopenharmony_ci goto fail; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return 0; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cifail: 1638c2ecf20Sopenharmony_ci platform_driver_unregister(&amilo_rfkill_driver); 1648c2ecf20Sopenharmony_ci return rc; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void __exit amilo_rfkill_exit(void) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci platform_device_unregister(amilo_rfkill_pdev); 1708c2ecf20Sopenharmony_ci platform_driver_unregister(&amilo_rfkill_driver); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Hutchings <ben@decadent.org.uk>"); 1748c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, amilo_rfkill_id_table); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cimodule_init(amilo_rfkill_init); 1788c2ecf20Sopenharmony_cimodule_exit(amilo_rfkill_exit); 179