12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 #include <linux/kernel.h>
16 #include <linux/module.h>
18 #include <linux/pci.h>
24 #include <linux/rfkill.h>
28 #include <linux/ctype.h>
42 #define MAX_BRIGHT 0x07
45 #define SABI_IFACE_MAIN 0x00
46 #define SABI_IFACE_SUB 0x02
47 #define SABI_IFACE_COMPLETE 0x04
48 #define SABI_IFACE_DATA 0x05
50 #define WL_STATUS_WLAN 0x0
51 #define WL_STATUS_BT 0x2
157 .test_string =
"SECLINUX",
159 .main_function = 0x4c49,
167 .data_segment = 0x07,
171 .get_brightness = 0x00,
172 .set_brightness = 0x01,
174 .get_wireless_button = 0x02,
175 .set_wireless_button = 0x03,
177 .get_backlight = 0x04,
178 .set_backlight = 0x05,
180 .get_recovery_mode = 0x06,
181 .set_recovery_mode = 0x07,
183 .get_performance_level = 0x08,
184 .set_performance_level = 0x09,
186 .get_battery_life_extender = 0xFFFF,
187 .set_battery_life_extender = 0xFFFF,
189 .get_usb_charge = 0xFFFF,
190 .set_usb_charge = 0xFFFF,
192 .get_wireless_status = 0xFFFF,
193 .set_wireless_status = 0xFFFF,
195 .kbd_backlight = 0xFFFF,
200 .performance_levels = {
217 .test_string =
"SwSmi@",
219 .main_function = 0x5843,
227 .data_segment = 0x07,
231 .get_brightness = 0x10,
232 .set_brightness = 0x11,
234 .get_wireless_button = 0x12,
235 .set_wireless_button = 0x13,
237 .get_backlight = 0x2d,
238 .set_backlight = 0x2e,
240 .get_recovery_mode = 0xff,
241 .set_recovery_mode = 0xff,
243 .get_performance_level = 0x31,
244 .set_performance_level = 0x32,
246 .get_battery_life_extender = 0x65,
247 .set_battery_life_extender = 0x66,
249 .get_usb_charge = 0x67,
250 .set_usb_charge = 0x68,
252 .get_wireless_status = 0x69,
253 .set_wireless_status = 0x6a,
255 .kbd_backlight = 0x78,
260 .performance_levels = {
355 .broken_acpi_video =
true,
361 "Disable the DMI check and forces the driver to be loaded");
381 "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
382 command, in->
d0, in->
d1, in->
d2, in->
d3);
384 pr_info(
"SABI command:0x%04x", command);
413 if (complete != 0xaa || (iface_data == 0xff &&
debug))
414 pr_warn(
"SABI command 0x%04x failed with"
415 " completion flag 0x%02x and interface data 0x%02x",
416 command, complete, iface_data);
418 if (complete != 0xaa || iface_data == 0xff) {
431 pr_info(
"SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
432 out->
d0, out->
d1, out->
d2, out->
d3);
444 struct sabi_data in = { { { .
d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
447 return sabi_command(samsung, command, &in,
NULL);
455 int user_brightness = 0;
463 user_brightness = sretval.data[0];
469 return user_brightness;
472 static void set_brightness(
struct samsung_laptop *samsung,
u8 user_brightness)
483 if (user_brightness == read_brightness(samsung))
496 return read_brightness(samsung);
499 static void check_for_stepping_quirk(
struct samsung_laptop *samsung)
503 int orig_level = read_brightness(samsung);
513 set_brightness(samsung, 1);
515 initial_level = read_brightness(samsung);
517 if (initial_level <= 2)
518 check_level = initial_level + 2;
520 check_level = initial_level - 2;
523 set_brightness(samsung, check_level);
525 if (read_brightness(samsung) != check_level) {
527 pr_info(
"enabled workaround for brightness stepping quirk\n");
530 set_brightness(samsung, orig_level);
553 static int seclinux_rfkill_set(
void *
data,
bool blocked)
563 static struct rfkill_ops seclinux_rfkill_ops = {
564 .set_block = seclinux_rfkill_set,
576 static int swsmi_rfkill_set(
void *
priv,
bool blocked)
584 ret = swsmi_wireless_status(samsung, &
data);
589 for (i = 0; i < 4; i++)
590 if (
data.data[i] == 0x02)
602 static void swsmi_rfkill_query(
struct rfkill *
rfkill,
void *priv)
609 ret = swsmi_wireless_status(samsung, &
data);
624 .set_block = swsmi_rfkill_set,
625 .query = swsmi_rfkill_query,
649 return sprintf(buf,
"%s\n",
"unknown");
668 sabi_set_commandb(samsung,
682 get_performance_level, set_performance_level);
684 static int read_battery_life_extender(
struct samsung_laptop *samsung)
701 if (
data.data[0] != 0 &&
data.data[0] != 1)
707 static int write_battery_life_extender(
struct samsung_laptop *samsung,
726 ret = read_battery_life_extender(samsung);
730 return sprintf(buf,
"%d\n", ret);
735 const char *buf,
size_t count)
740 if (!count ||
sscanf(buf,
"%i", &value) != 1)
743 ret = write_battery_life_extender(samsung, !!value);
751 get_battery_life_extender, set_battery_life_extender);
770 if (
data.data[0] != 0 &&
data.data[0] != 1)
795 ret = read_usb_charge(samsung);
799 return sprintf(buf,
"%d\n", ret);
804 const char *buf,
size_t count)
809 if (!count ||
sscanf(buf,
"%i", &value) != 1)
812 ret = write_usb_charge(samsung, !!value);
820 get_usb_charge, set_usb_charge);
822 static struct attribute *platform_attributes[] = {
823 &dev_attr_performance_level.attr,
824 &dev_attr_battery_life_extender.attr,
825 &dev_attr_usb_charge.attr,
829 static int find_signature(
void __iomem *memcheck,
const char *testStr)
834 for (loca = 0; loca < 0xffff; loca++) {
837 if (temp == testStr[i]) {
838 if (i ==
strlen(testStr)-1)
850 if (samsung->
wlan.rfkill) {
868 struct rfkill **rfkill = &arfkill->
rfkill;
894 return samsung_new_rfkill(samsung, &samsung->
wlan,
"samsung-wlan",
903 ret = swsmi_wireless_status(samsung, &
data);
908 ret = samsung_rfkill_init_seclinux(samsung);
915 ret = samsung_new_rfkill(samsung, &samsung->
wlan,
924 ret = samsung_new_rfkill(samsung, &samsung->
bluetooth,
934 samsung_rfkill_exit(samsung);
941 if (samsung->
config->sabi_version == 2)
942 return samsung_rfkill_init_seclinux(samsung);
943 if (samsung->
config->sabi_version == 3)
944 return samsung_rfkill_init_swsmi(samsung);
965 if (
data.d0 != 0xccdd)
993 data.d0 = 0x82 | ((brightness & 0xFF) << 8);
1003 kbd_backlight_write(samsung, samsung->
kbd_led_wk);
1013 if (value > samsung->
kbd_led.max_brightness)
1014 value = samsung->
kbd_led.max_brightness;
1027 return kbd_backlight_read(samsung);
1032 if (!IS_ERR_OR_NULL(samsung->
kbd_led.dev))
1046 if (kbd_backlight_enable(samsung) >= 0) {
1049 samsung->
kbd_led.name =
"samsung::kbd_backlight";
1050 samsung->
kbd_led.brightness_set = kbd_led_set;
1051 samsung->
kbd_led.brightness_get = kbd_led_get;
1052 samsung->
kbd_led.max_brightness = 8;
1059 samsung_leds_exit(samsung);
1064 static void samsung_backlight_exit(
struct samsung_laptop *samsung)
1082 props.max_brightness = samsung->
config->max_brightness -
1083 samsung->
config->min_brightness;
1087 samsung, &backlight_ops,
1108 if (attr == &dev_attr_performance_level.attr)
1109 ok = !!samsung->
config->performance_levels[0].name;
1110 if (attr == &dev_attr_battery_life_extender.attr)
1111 ok = !!(read_battery_life_extender(samsung) >= 0);
1112 if (attr == &dev_attr_usb_charge.attr)
1113 ok = !!(read_usb_charge(samsung) >= 0);
1115 return ok ? attr->
mode : 0;
1119 .is_visible = samsung_sysfs_is_visible,
1120 .attrs = platform_attributes
1144 seq_printf(m,
"SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1145 samsung->
debug.command,
1146 sdata->
d0, sdata->
d1, sdata->
d2, sdata->
d3);
1148 ret = sabi_command(samsung, samsung->
debug.command, sdata, sdata);
1151 seq_printf(m,
"SABI command 0x%04x failed\n",
1152 samsung->
debug.command);
1156 seq_printf(m,
"SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1157 sdata->
d0, sdata->
d1, sdata->
d2, sdata->
d3);
1168 .open = samsung_debugfs_open,
1184 if (!samsung->
debug.root) {
1185 pr_err(
"failed to create debugfs directory");
1190 samsung->
debug.f0000_wrapper.size = 0xffff;
1192 samsung->
debug.data_wrapper.data = &samsung->
debug.data;
1193 samsung->
debug.data_wrapper.size =
sizeof(samsung->
debug.data);
1195 samsung->
debug.sdiag_wrapper.data = samsung->
sdiag;
1199 samsung->
debug.root, &samsung->
debug.command);
1204 &samsung->
debug.data.d0);
1209 &samsung->
debug.data.d1);
1214 &samsung->
debug.data.d2);
1219 &samsung->
debug.data.d3);
1224 samsung->
debug.root,
1225 &samsung->
debug.data_wrapper);
1230 samsung->
debug.root,
1231 &samsung->
debug.f0000_wrapper);
1236 samsung->
debug.root, samsung,
1237 &samsung_laptop_call_io_ops);
1242 samsung->
debug.root,
1243 &samsung->
debug.sdiag_wrapper);
1250 samsung_debugfs_exit(samsung);
1259 if (config && config->
commands.set_linux != 0xff)
1260 sabi_set_commandb(samsung, config->
commands.set_linux, 0x80);
1275 unsigned int ifaceP)
1280 loca + 0xf0000 - 6);
1301 int loca = find_signature(samsung->
f0000_segment,
"SDiaG@");
1314 for (i = 0; loca < 0xffff && i <
sizeof(samsung->
sdiag) - 1; loca++) {
1317 if (
isalnum(temp) || temp ==
'/' || temp ==
'-')
1331 unsigned int ifaceP;
1339 pr_err(
"Can't map the segment at 0xf0000\n");
1344 samsung_sabi_diag(samsung);
1348 samsung->
config = &sabi_configs[
i];
1350 samsung->
config->test_string);
1355 if (loca == 0xffff) {
1357 pr_err(
"This computer does not support SABI\n");
1362 config = samsung->
config;
1374 samsung_sabi_infos(samsung, loca, ifaceP);
1378 pr_err(
"Can't remap %x\n", ifaceP);
1385 int retval = sabi_set_commandb(samsung,
1388 pr_warn(
"Linux mode was not set!\n");
1396 check_for_stepping_quirk(samsung);
1398 pr_info(
"detected SABI interface: %s\n",
1399 samsung->
config->test_string);
1403 samsung_sabi_exit(samsung);
1408 static void samsung_platform_exit(
struct samsung_laptop *samsung)
1420 pdev = platform_device_register_simple(
"samsung", -1,
NULL, 0);
1422 return PTR_ERR(pdev);
1433 quirks = d->driver_data;
1441 "SAMSUNG ELECTRONICS CO., LTD."),
1448 "SAMSUNG ELECTRONICS CO., LTD."),
1455 "SAMSUNG ELECTRONICS CO., LTD."),
1462 "SAMSUNG ELECTRONICS CO., LTD."),
1477 .callback = samsung_dmi_matched,
1484 .driver_data = &samsung_broken_acpi_video,
1487 .callback = samsung_dmi_matched,
1488 .ident =
"N145P/N250P/N260P",
1494 .driver_data = &samsung_broken_acpi_video,
1497 .callback = samsung_dmi_matched,
1498 .ident =
"N150/N210/N220",
1504 .driver_data = &samsung_broken_acpi_video,
1507 .callback = samsung_dmi_matched,
1508 .ident =
"NF110/NF210/NF310",
1514 .driver_data = &samsung_broken_acpi_video,
1517 .callback = samsung_dmi_matched,
1524 .driver_data = &samsung_broken_acpi_video,
1532 static int __init samsung_init(
void)
1537 quirks = &samsung_unknown;
1541 samsung = kzalloc(
sizeof(*samsung),
GFP_KERNEL);
1551 if (samsung->
quirks->broken_acpi_video)
1557 }
else if (samsung->
quirks->broken_acpi_video) {
1558 pr_info(
"Disabling ACPI video driver\n");
1563 ret = samsung_platform_init(samsung);
1565 goto error_platform;
1567 ret = samsung_sabi_init(samsung);
1574 pr_info(
"Backlight controlled by ACPI video driver\n");
1577 ret = samsung_sysfs_init(samsung);
1581 ret = samsung_backlight_init(samsung);
1583 goto error_backlight;
1585 ret = samsung_rfkill_init(samsung);
1589 ret = samsung_leds_init(samsung);
1593 ret = samsung_debugfs_init(samsung);
1601 samsung_leds_exit(samsung);
1603 samsung_rfkill_exit(samsung);
1605 samsung_backlight_exit(samsung);
1607 samsung_sysfs_exit(samsung);
1609 samsung_sabi_exit(samsung);
1611 samsung_platform_exit(samsung);
1617 static void __exit samsung_exit(
void)
1621 samsung = platform_get_drvdata(samsung_platform_device);
1623 samsung_debugfs_exit(samsung);
1624 samsung_leds_exit(samsung);
1625 samsung_rfkill_exit(samsung);
1626 samsung_backlight_exit(samsung);
1627 samsung_sysfs_exit(samsung);
1628 samsung_sabi_exit(samsung);
1629 samsung_platform_exit(samsung);
1632 samsung_platform_device =
NULL;