1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for Khadas System control Microcontroller 4 * 5 * Copyright (C) 2020 BayLibre SAS 6 * 7 * Author(s): Neil Armstrong <narmstrong@baylibre.com> 8 */ 9 #include <linux/bitfield.h> 10 #include <linux/i2c.h> 11 #include <linux/mfd/core.h> 12 #include <linux/mfd/khadas-mcu.h> 13 #include <linux/module.h> 14 #include <linux/regmap.h> 15 16 static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg) 17 { 18 if (reg >= KHADAS_MCU_USER_DATA_0_REG && 19 reg < KHADAS_MCU_PWR_OFF_CMD_REG) 20 return true; 21 22 switch (reg) { 23 case KHADAS_MCU_PWR_OFF_CMD_REG: 24 case KHADAS_MCU_PASSWD_START_REG: 25 case KHADAS_MCU_CHECK_VEN_PASSWD_REG: 26 case KHADAS_MCU_CHECK_USER_PASSWD_REG: 27 case KHADAS_MCU_WOL_INIT_START_REG: 28 case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG: 29 return true; 30 default: 31 return false; 32 } 33 } 34 35 static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg) 36 { 37 switch (reg) { 38 case KHADAS_MCU_PASSWD_VEN_0_REG: 39 case KHADAS_MCU_PASSWD_VEN_1_REG: 40 case KHADAS_MCU_PASSWD_VEN_2_REG: 41 case KHADAS_MCU_PASSWD_VEN_3_REG: 42 case KHADAS_MCU_PASSWD_VEN_4_REG: 43 case KHADAS_MCU_PASSWD_VEN_5_REG: 44 case KHADAS_MCU_MAC_0_REG: 45 case KHADAS_MCU_MAC_1_REG: 46 case KHADAS_MCU_MAC_2_REG: 47 case KHADAS_MCU_MAC_3_REG: 48 case KHADAS_MCU_MAC_4_REG: 49 case KHADAS_MCU_MAC_5_REG: 50 case KHADAS_MCU_USID_0_REG: 51 case KHADAS_MCU_USID_1_REG: 52 case KHADAS_MCU_USID_2_REG: 53 case KHADAS_MCU_USID_3_REG: 54 case KHADAS_MCU_USID_4_REG: 55 case KHADAS_MCU_USID_5_REG: 56 case KHADAS_MCU_VERSION_0_REG: 57 case KHADAS_MCU_VERSION_1_REG: 58 case KHADAS_MCU_DEVICE_NO_0_REG: 59 case KHADAS_MCU_DEVICE_NO_1_REG: 60 case KHADAS_MCU_FACTORY_TEST_REG: 61 case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG: 62 return false; 63 default: 64 return true; 65 } 66 } 67 68 static const struct regmap_config khadas_mcu_regmap_config = { 69 .reg_bits = 8, 70 .reg_stride = 1, 71 .val_bits = 8, 72 .max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, 73 .volatile_reg = khadas_mcu_reg_volatile, 74 .writeable_reg = khadas_mcu_reg_writeable, 75 .cache_type = REGCACHE_RBTREE, 76 }; 77 78 static struct mfd_cell khadas_mcu_fan_cells[] = { 79 /* VIM1/2 Rev13+ and VIM3 only */ 80 { .name = "khadas-mcu-fan-ctrl", }, 81 }; 82 83 static struct mfd_cell khadas_mcu_cells[] = { 84 { .name = "khadas-mcu-user-mem", }, 85 }; 86 87 static int khadas_mcu_probe(struct i2c_client *client) 88 { 89 struct device *dev = &client->dev; 90 struct khadas_mcu *ddata; 91 int ret; 92 93 ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); 94 if (!ddata) 95 return -ENOMEM; 96 97 i2c_set_clientdata(client, ddata); 98 99 ddata->dev = dev; 100 101 ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config); 102 if (IS_ERR(ddata->regmap)) { 103 ret = PTR_ERR(ddata->regmap); 104 dev_err(dev, "Failed to allocate register map: %d\n", ret); 105 return ret; 106 } 107 108 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 109 khadas_mcu_cells, 110 ARRAY_SIZE(khadas_mcu_cells), 111 NULL, 0, NULL); 112 if (ret) 113 return ret; 114 115 if (of_property_present(dev->of_node, "#cooling-cells")) 116 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 117 khadas_mcu_fan_cells, 118 ARRAY_SIZE(khadas_mcu_fan_cells), 119 NULL, 0, NULL); 120 121 return 0; 122 } 123 124 #ifdef CONFIG_OF 125 static const struct of_device_id khadas_mcu_of_match[] = { 126 { .compatible = "khadas,mcu", }, 127 {}, 128 }; 129 MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); 130 #endif 131 132 static struct i2c_driver khadas_mcu_driver = { 133 .driver = { 134 .name = "khadas-mcu-core", 135 .of_match_table = of_match_ptr(khadas_mcu_of_match), 136 }, 137 .probe = khadas_mcu_probe, 138 }; 139 module_i2c_driver(khadas_mcu_driver); 140 141 MODULE_DESCRIPTION("Khadas MCU core driver"); 142 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 143 MODULE_LICENSE("GPL v2"); 144