Merge tag 'pmdomain-v6.6-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / drivers / mfd / khadas-mcu.c
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");