0001
0002
0003
0004
0005
0006
0007
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/init.h>
0013 #include <linux/types.h>
0014 #include <linux/acpi.h>
0015
0016 MODULE_AUTHOR("Azael Avalos <coproscefalo@gmail.com>");
0017 MODULE_DESCRIPTION("Toshiba HDD Active Protection Sensor");
0018 MODULE_LICENSE("GPL");
0019
0020 struct toshiba_haps_dev {
0021 struct acpi_device *acpi_dev;
0022
0023 int protection_level;
0024 };
0025
0026 static struct toshiba_haps_dev *toshiba_haps;
0027
0028
0029 static int toshiba_haps_reset_protection(acpi_handle handle)
0030 {
0031 acpi_status status;
0032
0033 status = acpi_evaluate_object(handle, "RSSS", NULL, NULL);
0034 if (ACPI_FAILURE(status)) {
0035 pr_err("Unable to reset the HDD protection\n");
0036 return -EIO;
0037 }
0038
0039 return 0;
0040 }
0041
0042 static int toshiba_haps_protection_level(acpi_handle handle, int level)
0043 {
0044 acpi_status status;
0045
0046 status = acpi_execute_simple_method(handle, "PTLV", level);
0047 if (ACPI_FAILURE(status)) {
0048 pr_err("Error while setting the protection level\n");
0049 return -EIO;
0050 }
0051
0052 pr_debug("HDD protection level set to: %d\n", level);
0053
0054 return 0;
0055 }
0056
0057
0058 static ssize_t protection_level_show(struct device *dev,
0059 struct device_attribute *attr, char *buf)
0060 {
0061 struct toshiba_haps_dev *haps = dev_get_drvdata(dev);
0062
0063 return sprintf(buf, "%i\n", haps->protection_level);
0064 }
0065
0066 static ssize_t protection_level_store(struct device *dev,
0067 struct device_attribute *attr,
0068 const char *buf, size_t count)
0069 {
0070 struct toshiba_haps_dev *haps = dev_get_drvdata(dev);
0071 int level;
0072 int ret;
0073
0074 ret = kstrtoint(buf, 0, &level);
0075 if (ret)
0076 return ret;
0077
0078
0079
0080
0081 if (level < 0 || level > 3)
0082 return -EINVAL;
0083
0084
0085 ret = toshiba_haps_protection_level(haps->acpi_dev->handle, level);
0086 if (ret != 0)
0087 return ret;
0088
0089 haps->protection_level = level;
0090
0091 return count;
0092 }
0093 static DEVICE_ATTR_RW(protection_level);
0094
0095 static ssize_t reset_protection_store(struct device *dev,
0096 struct device_attribute *attr,
0097 const char *buf, size_t count)
0098 {
0099 struct toshiba_haps_dev *haps = dev_get_drvdata(dev);
0100 int reset;
0101 int ret;
0102
0103 ret = kstrtoint(buf, 0, &reset);
0104 if (ret)
0105 return ret;
0106
0107 if (reset != 1)
0108 return -EINVAL;
0109
0110
0111 ret = toshiba_haps_reset_protection(haps->acpi_dev->handle);
0112 if (ret != 0)
0113 return ret;
0114
0115 return count;
0116 }
0117 static DEVICE_ATTR_WO(reset_protection);
0118
0119 static struct attribute *haps_attributes[] = {
0120 &dev_attr_protection_level.attr,
0121 &dev_attr_reset_protection.attr,
0122 NULL,
0123 };
0124
0125 static const struct attribute_group haps_attr_group = {
0126 .attrs = haps_attributes,
0127 };
0128
0129
0130
0131
0132 static void toshiba_haps_notify(struct acpi_device *device, u32 event)
0133 {
0134 pr_debug("Received event: 0x%x\n", event);
0135
0136 acpi_bus_generate_netlink_event(device->pnp.device_class,
0137 dev_name(&device->dev),
0138 event, 0);
0139 }
0140
0141 static int toshiba_haps_remove(struct acpi_device *device)
0142 {
0143 sysfs_remove_group(&device->dev.kobj, &haps_attr_group);
0144
0145 if (toshiba_haps)
0146 toshiba_haps = NULL;
0147
0148 return 0;
0149 }
0150
0151
0152 static int toshiba_haps_available(acpi_handle handle)
0153 {
0154 acpi_status status;
0155 u64 hdd_present;
0156
0157
0158
0159
0160
0161 status = acpi_evaluate_integer(handle, "_STA", NULL, &hdd_present);
0162 if (ACPI_FAILURE(status)) {
0163 pr_err("ACPI call to query HDD protection failed\n");
0164 return 0;
0165 }
0166
0167 if (!hdd_present) {
0168 pr_info("HDD protection not available or using SSD\n");
0169 return 0;
0170 }
0171
0172 return 1;
0173 }
0174
0175 static int toshiba_haps_add(struct acpi_device *acpi_dev)
0176 {
0177 struct toshiba_haps_dev *haps;
0178 int ret;
0179
0180 if (toshiba_haps)
0181 return -EBUSY;
0182
0183 if (!toshiba_haps_available(acpi_dev->handle))
0184 return -ENODEV;
0185
0186 pr_info("Toshiba HDD Active Protection Sensor device\n");
0187
0188 haps = kzalloc(sizeof(struct toshiba_haps_dev), GFP_KERNEL);
0189 if (!haps)
0190 return -ENOMEM;
0191
0192 haps->acpi_dev = acpi_dev;
0193 haps->protection_level = 2;
0194 acpi_dev->driver_data = haps;
0195 dev_set_drvdata(&acpi_dev->dev, haps);
0196
0197
0198 ret = toshiba_haps_protection_level(acpi_dev->handle, 2);
0199 if (ret != 0)
0200 return ret;
0201
0202 ret = sysfs_create_group(&acpi_dev->dev.kobj, &haps_attr_group);
0203 if (ret)
0204 return ret;
0205
0206 toshiba_haps = haps;
0207
0208 return 0;
0209 }
0210
0211 #ifdef CONFIG_PM_SLEEP
0212 static int toshiba_haps_suspend(struct device *device)
0213 {
0214 struct toshiba_haps_dev *haps;
0215 int ret;
0216
0217 haps = acpi_driver_data(to_acpi_device(device));
0218
0219
0220 ret = toshiba_haps_protection_level(haps->acpi_dev->handle, 0);
0221
0222 return ret;
0223 }
0224
0225 static int toshiba_haps_resume(struct device *device)
0226 {
0227 struct toshiba_haps_dev *haps;
0228 int ret;
0229
0230 haps = acpi_driver_data(to_acpi_device(device));
0231
0232
0233 ret = toshiba_haps_protection_level(haps->acpi_dev->handle,
0234 haps->protection_level);
0235
0236
0237 ret = toshiba_haps_reset_protection(haps->acpi_dev->handle);
0238 if (ret != 0)
0239 return ret;
0240
0241 return ret;
0242 }
0243 #endif
0244
0245 static SIMPLE_DEV_PM_OPS(toshiba_haps_pm,
0246 toshiba_haps_suspend, toshiba_haps_resume);
0247
0248 static const struct acpi_device_id haps_device_ids[] = {
0249 {"TOS620A", 0},
0250 {"", 0},
0251 };
0252 MODULE_DEVICE_TABLE(acpi, haps_device_ids);
0253
0254 static struct acpi_driver toshiba_haps_driver = {
0255 .name = "Toshiba HAPS",
0256 .owner = THIS_MODULE,
0257 .ids = haps_device_ids,
0258 .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
0259 .ops = {
0260 .add = toshiba_haps_add,
0261 .remove = toshiba_haps_remove,
0262 .notify = toshiba_haps_notify,
0263 },
0264 .drv.pm = &toshiba_haps_pm,
0265 };
0266
0267 module_acpi_driver(toshiba_haps_driver);