16c27219eSNeil Armstrong // SPDX-License-Identifier: GPL-2.0 26c27219eSNeil Armstrong /* 36c27219eSNeil Armstrong * Driver for Khadas System control Microcontroller 46c27219eSNeil Armstrong * 56c27219eSNeil Armstrong * Copyright (C) 2020 BayLibre SAS 66c27219eSNeil Armstrong * 76c27219eSNeil Armstrong * Author(s): Neil Armstrong <narmstrong@baylibre.com> 86c27219eSNeil Armstrong */ 96c27219eSNeil Armstrong #include <linux/bitfield.h> 106c27219eSNeil Armstrong #include <linux/i2c.h> 116c27219eSNeil Armstrong #include <linux/mfd/core.h> 126c27219eSNeil Armstrong #include <linux/mfd/khadas-mcu.h> 136c27219eSNeil Armstrong #include <linux/module.h> 146c27219eSNeil Armstrong #include <linux/regmap.h> 156c27219eSNeil Armstrong 166c27219eSNeil Armstrong static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg) 176c27219eSNeil Armstrong { 186c27219eSNeil Armstrong if (reg >= KHADAS_MCU_USER_DATA_0_REG && 196c27219eSNeil Armstrong reg < KHADAS_MCU_PWR_OFF_CMD_REG) 206c27219eSNeil Armstrong return true; 216c27219eSNeil Armstrong 226c27219eSNeil Armstrong switch (reg) { 236c27219eSNeil Armstrong case KHADAS_MCU_PWR_OFF_CMD_REG: 246c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_START_REG: 256c27219eSNeil Armstrong case KHADAS_MCU_CHECK_VEN_PASSWD_REG: 266c27219eSNeil Armstrong case KHADAS_MCU_CHECK_USER_PASSWD_REG: 276c27219eSNeil Armstrong case KHADAS_MCU_WOL_INIT_START_REG: 286c27219eSNeil Armstrong case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG: 296c27219eSNeil Armstrong return true; 306c27219eSNeil Armstrong default: 316c27219eSNeil Armstrong return false; 326c27219eSNeil Armstrong } 336c27219eSNeil Armstrong } 346c27219eSNeil Armstrong 356c27219eSNeil Armstrong static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg) 366c27219eSNeil Armstrong { 376c27219eSNeil Armstrong switch (reg) { 386c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_0_REG: 396c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_1_REG: 406c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_2_REG: 416c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_3_REG: 426c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_4_REG: 436c27219eSNeil Armstrong case KHADAS_MCU_PASSWD_VEN_5_REG: 446c27219eSNeil Armstrong case KHADAS_MCU_MAC_0_REG: 456c27219eSNeil Armstrong case KHADAS_MCU_MAC_1_REG: 466c27219eSNeil Armstrong case KHADAS_MCU_MAC_2_REG: 476c27219eSNeil Armstrong case KHADAS_MCU_MAC_3_REG: 486c27219eSNeil Armstrong case KHADAS_MCU_MAC_4_REG: 496c27219eSNeil Armstrong case KHADAS_MCU_MAC_5_REG: 506c27219eSNeil Armstrong case KHADAS_MCU_USID_0_REG: 516c27219eSNeil Armstrong case KHADAS_MCU_USID_1_REG: 526c27219eSNeil Armstrong case KHADAS_MCU_USID_2_REG: 536c27219eSNeil Armstrong case KHADAS_MCU_USID_3_REG: 546c27219eSNeil Armstrong case KHADAS_MCU_USID_4_REG: 556c27219eSNeil Armstrong case KHADAS_MCU_USID_5_REG: 566c27219eSNeil Armstrong case KHADAS_MCU_VERSION_0_REG: 576c27219eSNeil Armstrong case KHADAS_MCU_VERSION_1_REG: 586c27219eSNeil Armstrong case KHADAS_MCU_DEVICE_NO_0_REG: 596c27219eSNeil Armstrong case KHADAS_MCU_DEVICE_NO_1_REG: 606c27219eSNeil Armstrong case KHADAS_MCU_FACTORY_TEST_REG: 616c27219eSNeil Armstrong case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG: 626c27219eSNeil Armstrong return false; 636c27219eSNeil Armstrong default: 646c27219eSNeil Armstrong return true; 656c27219eSNeil Armstrong } 666c27219eSNeil Armstrong } 676c27219eSNeil Armstrong 686c27219eSNeil Armstrong static const struct regmap_config khadas_mcu_regmap_config = { 696c27219eSNeil Armstrong .reg_bits = 8, 706c27219eSNeil Armstrong .reg_stride = 1, 716c27219eSNeil Armstrong .val_bits = 8, 726c27219eSNeil Armstrong .max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, 736c27219eSNeil Armstrong .volatile_reg = khadas_mcu_reg_volatile, 746c27219eSNeil Armstrong .writeable_reg = khadas_mcu_reg_writeable, 756c27219eSNeil Armstrong .cache_type = REGCACHE_RBTREE, 766c27219eSNeil Armstrong }; 776c27219eSNeil Armstrong 786c27219eSNeil Armstrong static struct mfd_cell khadas_mcu_fan_cells[] = { 796c27219eSNeil Armstrong /* VIM1/2 Rev13+ and VIM3 only */ 806c27219eSNeil Armstrong { .name = "khadas-mcu-fan-ctrl", }, 816c27219eSNeil Armstrong }; 826c27219eSNeil Armstrong 836c27219eSNeil Armstrong static struct mfd_cell khadas_mcu_cells[] = { 846c27219eSNeil Armstrong { .name = "khadas-mcu-user-mem", }, 856c27219eSNeil Armstrong }; 866c27219eSNeil Armstrong 876c27219eSNeil Armstrong static int khadas_mcu_probe(struct i2c_client *client, 886c27219eSNeil Armstrong const struct i2c_device_id *id) 896c27219eSNeil Armstrong { 906c27219eSNeil Armstrong struct device *dev = &client->dev; 916c27219eSNeil Armstrong struct khadas_mcu *ddata; 926c27219eSNeil Armstrong int ret; 936c27219eSNeil Armstrong 946c27219eSNeil Armstrong ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); 956c27219eSNeil Armstrong if (!ddata) 966c27219eSNeil Armstrong return -ENOMEM; 976c27219eSNeil Armstrong 986c27219eSNeil Armstrong i2c_set_clientdata(client, ddata); 996c27219eSNeil Armstrong 1006c27219eSNeil Armstrong ddata->dev = dev; 1016c27219eSNeil Armstrong 1026c27219eSNeil Armstrong ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config); 1036c27219eSNeil Armstrong if (IS_ERR(ddata->regmap)) { 1046c27219eSNeil Armstrong ret = PTR_ERR(ddata->regmap); 1056c27219eSNeil Armstrong dev_err(dev, "Failed to allocate register map: %d\n", ret); 1066c27219eSNeil Armstrong return ret; 1076c27219eSNeil Armstrong } 1086c27219eSNeil Armstrong 1096c27219eSNeil Armstrong ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 1106c27219eSNeil Armstrong khadas_mcu_cells, 1116c27219eSNeil Armstrong ARRAY_SIZE(khadas_mcu_cells), 1126c27219eSNeil Armstrong NULL, 0, NULL); 1136c27219eSNeil Armstrong if (ret) 1146c27219eSNeil Armstrong return ret; 1156c27219eSNeil Armstrong 1166c27219eSNeil Armstrong if (of_find_property(dev->of_node, "#cooling-cells", NULL)) 1176c27219eSNeil Armstrong return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 1186c27219eSNeil Armstrong khadas_mcu_fan_cells, 1196c27219eSNeil Armstrong ARRAY_SIZE(khadas_mcu_fan_cells), 1206c27219eSNeil Armstrong NULL, 0, NULL); 1216c27219eSNeil Armstrong 1226c27219eSNeil Armstrong return 0; 1236c27219eSNeil Armstrong } 1246c27219eSNeil Armstrong 1256c27219eSNeil Armstrong static const struct of_device_id khadas_mcu_of_match[] = { 1266c27219eSNeil Armstrong { .compatible = "khadas,mcu", }, 1276c27219eSNeil Armstrong {}, 1286c27219eSNeil Armstrong }; 1296c27219eSNeil Armstrong MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); 1306c27219eSNeil Armstrong 1316c27219eSNeil Armstrong static struct i2c_driver khadas_mcu_driver = { 1326c27219eSNeil Armstrong .driver = { 1336c27219eSNeil Armstrong .name = "khadas-mcu-core", 1346c27219eSNeil Armstrong .of_match_table = of_match_ptr(khadas_mcu_of_match), 1356c27219eSNeil Armstrong }, 1366c27219eSNeil Armstrong .probe = khadas_mcu_probe, 1376c27219eSNeil Armstrong }; 1386c27219eSNeil Armstrong module_i2c_driver(khadas_mcu_driver); 1396c27219eSNeil Armstrong 1406c27219eSNeil Armstrong MODULE_DESCRIPTION("Khadas MCU core driver"); 1416c27219eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 1426c27219eSNeil Armstrong MODULE_LICENSE("GPL v2"); 143