diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-01 11:42:21 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-01 11:42:21 -0400 |
| commit | eb7046e9bf466cebfcfbcdf640e41d9e3a80086c (patch) | |
| tree | 7d998f56e306de4c1a78355f219e38b75cfe3866 /drivers/firmware | |
| parent | 5b7449810ae6d652629c550d3974c8453836d229 (diff) | |
| parent | 3b692c55e58d06ba9b17c66784cab5a95ba5be9b (diff) | |
Merge tag 'platform-drivers-x86-v4.20-1' of git://git.infradead.org/linux-platform-drivers-x86
Pull x86 platform driver updates from Darren Hart:
- Move the Dell dcdbas and dell_rbu drivers into platform/drivers/x86
as they are closely coupled with other drivers in this location.
- Improve _init* usage for acerhdf and fix some usage issues with
messages and module parameters.
- Simplify asus-wmi by calling ACPI/WMI methods directly, eliminating
workqueue overhead, eliminate double reporting of keyboard backlight.
- Fix wake from USB failure on Bay Trail devices (intel_int0002_vgpio).
- Notify intel_telemetry users when IPC1 device is not enabled.
- Update various drivers with new laptop model IDs.
- Update several intel drivers to use SPDX identifers and order headers
alphabetically.
* tag 'platform-drivers-x86-v4.20-1' of git://git.infradead.org/linux-platform-drivers-x86: (64 commits)
HID: asus: only support backlight when it's not driven by WMI
platform/x86: asus-wmi: export function for evaluating WMI methods
platform/x86: asus-wmi: Only notify kbd LED hw_change by fn-key pressed
platform/x86: wmi: declare device_type structure as constant
platform/x86: ideapad: Add Y530-15ICH to no_hw_rfkill
platform/x86: Add Intel AtomISP2 dummy / power-management driver
platform/x86: touchscreen_dmi: Add min-x and min-y settings for various models
platform/x86: touchscreen_dmi: Add info for the Onda V80 Plus v3 tablet
platform/x86: touchscreen_dmi: Add info for the Trekstor Primetab T13B tablet
platform/x86: intel_telemetry: Get rid of custom macro
platform/x86: intel_telemetry: report debugfs failure
MAINTAINERS: intel_telemetry: Update maintainers info
platform/x86: Add LG Gram laptop special features driver
platform/x86: asus-wmi: Simplify the keyboard brightness updating process
platform/x86: touchscreen_dmi: Add info for the Trekstor Primebook C11 convertible
platform/x86: mlx-platform: Properly use mlxplat_mlxcpld_msn201x_items
MAINTAINERS: intel_pmc_core: Update MAINTAINERS
firmware: dcdbas: include linux/io.h
platform/x86: intel-wmi-thunderbolt: Add dynamic debugging
platform/x86: intel-wmi-thunderbolt: Convert to use SPDX identifier
...
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/Kconfig | 28 | ||||
| -rw-r--r-- | drivers/firmware/Makefile | 2 | ||||
| -rw-r--r-- | drivers/firmware/dcdbas.c | 650 | ||||
| -rw-r--r-- | drivers/firmware/dcdbas.h | 107 | ||||
| -rw-r--r-- | drivers/firmware/dell_rbu.c | 745 |
5 files changed, 0 insertions, 1532 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 7670e8dda829..7273e5082b41 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
| @@ -145,34 +145,6 @@ config EFI_PCDP | |||
| 145 | See DIG64_HCDPv20_042804.pdf available from | 145 | See DIG64_HCDPv20_042804.pdf available from |
| 146 | <http://www.dig64.org/specifications/> | 146 | <http://www.dig64.org/specifications/> |
| 147 | 147 | ||
| 148 | config DELL_RBU | ||
| 149 | tristate "BIOS update support for DELL systems via sysfs" | ||
| 150 | depends on X86 | ||
| 151 | select FW_LOADER | ||
| 152 | select FW_LOADER_USER_HELPER | ||
| 153 | help | ||
| 154 | Say m if you want to have the option of updating the BIOS for your | ||
| 155 | DELL system. Note you need a Dell OpenManage or Dell Update package (DUP) | ||
| 156 | supporting application to communicate with the BIOS regarding the new | ||
| 157 | image for the image update to take effect. | ||
| 158 | See <file:Documentation/dell_rbu.txt> for more details on the driver. | ||
| 159 | |||
| 160 | config DCDBAS | ||
| 161 | tristate "Dell Systems Management Base Driver" | ||
| 162 | depends on X86 | ||
| 163 | help | ||
| 164 | The Dell Systems Management Base Driver provides a sysfs interface | ||
| 165 | for systems management software to perform System Management | ||
| 166 | Interrupts (SMIs) and Host Control Actions (system power cycle or | ||
| 167 | power off after OS shutdown) on certain Dell systems. | ||
| 168 | |||
| 169 | See <file:Documentation/dcdbas.txt> for more details on the driver | ||
| 170 | and the Dell systems on which Dell systems management software makes | ||
| 171 | use of this driver. | ||
| 172 | |||
| 173 | Say Y or M here to enable the driver for use by Dell systems | ||
| 174 | management software such as Dell OpenManage. | ||
| 175 | |||
| 176 | config DMIID | 148 | config DMIID |
| 177 | bool "Export DMI identification via sysfs to userspace" | 149 | bool "Export DMI identification via sysfs to userspace" |
| 178 | depends on DMI | 150 | depends on DMI |
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 13660a951437..3158dffd9914 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile | |||
| @@ -11,8 +11,6 @@ obj-$(CONFIG_DMI) += dmi_scan.o | |||
| 11 | obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o | 11 | obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o |
| 12 | obj-$(CONFIG_EDD) += edd.o | 12 | obj-$(CONFIG_EDD) += edd.o |
| 13 | obj-$(CONFIG_EFI_PCDP) += pcdp.o | 13 | obj-$(CONFIG_EFI_PCDP) += pcdp.o |
| 14 | obj-$(CONFIG_DELL_RBU) += dell_rbu.o | ||
| 15 | obj-$(CONFIG_DCDBAS) += dcdbas.o | ||
| 16 | obj-$(CONFIG_DMIID) += dmi-id.o | 14 | obj-$(CONFIG_DMIID) += dmi-id.o |
| 17 | obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o | 15 | obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o |
| 18 | obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o | 16 | obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o |
diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c deleted file mode 100644 index 0bdea60c65dd..000000000000 --- a/drivers/firmware/dcdbas.c +++ /dev/null | |||
| @@ -1,650 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * dcdbas.c: Dell Systems Management Base Driver | ||
| 3 | * | ||
| 4 | * The Dell Systems Management Base Driver provides a sysfs interface for | ||
| 5 | * systems management software to perform System Management Interrupts (SMIs) | ||
| 6 | * and Host Control Actions (power cycle or power off after OS shutdown) on | ||
| 7 | * Dell systems. | ||
| 8 | * | ||
| 9 | * See Documentation/dcdbas.txt for more information. | ||
| 10 | * | ||
| 11 | * Copyright (C) 1995-2006 Dell Inc. | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License v2.0 as published by | ||
| 15 | * the Free Software Foundation. | ||
| 16 | * | ||
| 17 | * This program is distributed in the hope that it will be useful, | ||
| 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | * GNU General Public License for more details. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/platform_device.h> | ||
| 24 | #include <linux/dma-mapping.h> | ||
| 25 | #include <linux/errno.h> | ||
| 26 | #include <linux/cpu.h> | ||
| 27 | #include <linux/gfp.h> | ||
| 28 | #include <linux/init.h> | ||
| 29 | #include <linux/kernel.h> | ||
| 30 | #include <linux/mc146818rtc.h> | ||
| 31 | #include <linux/module.h> | ||
| 32 | #include <linux/reboot.h> | ||
| 33 | #include <linux/sched.h> | ||
| 34 | #include <linux/smp.h> | ||
| 35 | #include <linux/spinlock.h> | ||
| 36 | #include <linux/string.h> | ||
| 37 | #include <linux/types.h> | ||
| 38 | #include <linux/mutex.h> | ||
| 39 | #include <asm/io.h> | ||
| 40 | |||
| 41 | #include "dcdbas.h" | ||
| 42 | |||
| 43 | #define DRIVER_NAME "dcdbas" | ||
| 44 | #define DRIVER_VERSION "5.6.0-3.2" | ||
| 45 | #define DRIVER_DESCRIPTION "Dell Systems Management Base Driver" | ||
| 46 | |||
| 47 | static struct platform_device *dcdbas_pdev; | ||
| 48 | |||
| 49 | static u8 *smi_data_buf; | ||
| 50 | static dma_addr_t smi_data_buf_handle; | ||
| 51 | static unsigned long smi_data_buf_size; | ||
| 52 | static u32 smi_data_buf_phys_addr; | ||
| 53 | static DEFINE_MUTEX(smi_data_lock); | ||
| 54 | |||
| 55 | static unsigned int host_control_action; | ||
| 56 | static unsigned int host_control_smi_type; | ||
| 57 | static unsigned int host_control_on_shutdown; | ||
| 58 | |||
| 59 | /** | ||
| 60 | * smi_data_buf_free: free SMI data buffer | ||
| 61 | */ | ||
| 62 | static void smi_data_buf_free(void) | ||
| 63 | { | ||
| 64 | if (!smi_data_buf) | ||
| 65 | return; | ||
| 66 | |||
| 67 | dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", | ||
| 68 | __func__, smi_data_buf_phys_addr, smi_data_buf_size); | ||
| 69 | |||
| 70 | dma_free_coherent(&dcdbas_pdev->dev, smi_data_buf_size, smi_data_buf, | ||
| 71 | smi_data_buf_handle); | ||
| 72 | smi_data_buf = NULL; | ||
| 73 | smi_data_buf_handle = 0; | ||
| 74 | smi_data_buf_phys_addr = 0; | ||
| 75 | smi_data_buf_size = 0; | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * smi_data_buf_realloc: grow SMI data buffer if needed | ||
| 80 | */ | ||
| 81 | static int smi_data_buf_realloc(unsigned long size) | ||
| 82 | { | ||
| 83 | void *buf; | ||
| 84 | dma_addr_t handle; | ||
| 85 | |||
| 86 | if (smi_data_buf_size >= size) | ||
| 87 | return 0; | ||
| 88 | |||
| 89 | if (size > MAX_SMI_DATA_BUF_SIZE) | ||
| 90 | return -EINVAL; | ||
| 91 | |||
| 92 | /* new buffer is needed */ | ||
| 93 | buf = dma_alloc_coherent(&dcdbas_pdev->dev, size, &handle, GFP_KERNEL); | ||
| 94 | if (!buf) { | ||
| 95 | dev_dbg(&dcdbas_pdev->dev, | ||
| 96 | "%s: failed to allocate memory size %lu\n", | ||
| 97 | __func__, size); | ||
| 98 | return -ENOMEM; | ||
| 99 | } | ||
| 100 | /* memory zeroed by dma_alloc_coherent */ | ||
| 101 | |||
| 102 | if (smi_data_buf) | ||
| 103 | memcpy(buf, smi_data_buf, smi_data_buf_size); | ||
| 104 | |||
| 105 | /* free any existing buffer */ | ||
| 106 | smi_data_buf_free(); | ||
| 107 | |||
| 108 | /* set up new buffer for use */ | ||
| 109 | smi_data_buf = buf; | ||
| 110 | smi_data_buf_handle = handle; | ||
| 111 | smi_data_buf_phys_addr = (u32) virt_to_phys(buf); | ||
| 112 | smi_data_buf_size = size; | ||
| 113 | |||
| 114 | dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", | ||
| 115 | __func__, smi_data_buf_phys_addr, smi_data_buf_size); | ||
| 116 | |||
| 117 | return 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | static ssize_t smi_data_buf_phys_addr_show(struct device *dev, | ||
| 121 | struct device_attribute *attr, | ||
| 122 | char *buf) | ||
| 123 | { | ||
| 124 | return sprintf(buf, "%x\n", smi_data_buf_phys_addr); | ||
| 125 | } | ||
| 126 | |||
| 127 | static ssize_t smi_data_buf_size_show(struct device *dev, | ||
| 128 | struct device_attribute *attr, | ||
| 129 | char *buf) | ||
| 130 | { | ||
| 131 | return sprintf(buf, "%lu\n", smi_data_buf_size); | ||
| 132 | } | ||
| 133 | |||
| 134 | static ssize_t smi_data_buf_size_store(struct device *dev, | ||
| 135 | struct device_attribute *attr, | ||
| 136 | const char *buf, size_t count) | ||
| 137 | { | ||
| 138 | unsigned long buf_size; | ||
| 139 | ssize_t ret; | ||
| 140 | |||
| 141 | buf_size = simple_strtoul(buf, NULL, 10); | ||
| 142 | |||
| 143 | /* make sure SMI data buffer is at least buf_size */ | ||
| 144 | mutex_lock(&smi_data_lock); | ||
| 145 | ret = smi_data_buf_realloc(buf_size); | ||
| 146 | mutex_unlock(&smi_data_lock); | ||
| 147 | if (ret) | ||
| 148 | return ret; | ||
| 149 | |||
| 150 | return count; | ||
| 151 | } | ||
| 152 | |||
| 153 | static ssize_t smi_data_read(struct file *filp, struct kobject *kobj, | ||
| 154 | struct bin_attribute *bin_attr, | ||
| 155 | char *buf, loff_t pos, size_t count) | ||
| 156 | { | ||
| 157 | ssize_t ret; | ||
| 158 | |||
| 159 | mutex_lock(&smi_data_lock); | ||
| 160 | ret = memory_read_from_buffer(buf, count, &pos, smi_data_buf, | ||
| 161 | smi_data_buf_size); | ||
| 162 | mutex_unlock(&smi_data_lock); | ||
| 163 | return ret; | ||
| 164 | } | ||
| 165 | |||
| 166 | static ssize_t smi_data_write(struct file *filp, struct kobject *kobj, | ||
| 167 | struct bin_attribute *bin_attr, | ||
| 168 | char *buf, loff_t pos, size_t count) | ||
| 169 | { | ||
| 170 | ssize_t ret; | ||
| 171 | |||
| 172 | if ((pos + count) > MAX_SMI_DATA_BUF_SIZE) | ||
| 173 | return -EINVAL; | ||
| 174 | |||
| 175 | mutex_lock(&smi_data_lock); | ||
| 176 | |||
| 177 | ret = smi_data_buf_realloc(pos + count); | ||
| 178 | if (ret) | ||
| 179 | goto out; | ||
| 180 | |||
| 181 | memcpy(smi_data_buf + pos, buf, count); | ||
| 182 | ret = count; | ||
| 183 | out: | ||
| 184 | mutex_unlock(&smi_data_lock); | ||
| 185 | return ret; | ||
| 186 | } | ||
| 187 | |||
| 188 | static ssize_t host_control_action_show(struct device *dev, | ||
| 189 | struct device_attribute *attr, | ||
| 190 | char *buf) | ||
| 191 | { | ||
| 192 | return sprintf(buf, "%u\n", host_control_action); | ||
| 193 | } | ||
| 194 | |||
| 195 | static ssize_t host_control_action_store(struct device *dev, | ||
| 196 | struct device_attribute *attr, | ||
| 197 | const char *buf, size_t count) | ||
| 198 | { | ||
| 199 | ssize_t ret; | ||
| 200 | |||
| 201 | /* make sure buffer is available for host control command */ | ||
| 202 | mutex_lock(&smi_data_lock); | ||
| 203 | ret = smi_data_buf_realloc(sizeof(struct apm_cmd)); | ||
| 204 | mutex_unlock(&smi_data_lock); | ||
| 205 | if (ret) | ||
| 206 | return ret; | ||
| 207 | |||
| 208 | host_control_action = simple_strtoul(buf, NULL, 10); | ||
| 209 | return count; | ||
| 210 | } | ||
| 211 | |||
| 212 | static ssize_t host_control_smi_type_show(struct device *dev, | ||
| 213 | struct device_attribute *attr, | ||
| 214 | char *buf) | ||
| 215 | { | ||
| 216 | return sprintf(buf, "%u\n", host_control_smi_type); | ||
| 217 | } | ||
| 218 | |||
| 219 | static ssize_t host_control_smi_type_store(struct device *dev, | ||
| 220 | struct device_attribute *attr, | ||
| 221 | const char *buf, size_t count) | ||
| 222 | { | ||
| 223 | host_control_smi_type = simple_strtoul(buf, NULL, 10); | ||
| 224 | return count; | ||
| 225 | } | ||
| 226 | |||
| 227 | static ssize_t host_control_on_shutdown_show(struct device *dev, | ||
| 228 | struct device_attribute *attr, | ||
| 229 | char *buf) | ||
| 230 | { | ||
| 231 | return sprintf(buf, "%u\n", host_control_on_shutdown); | ||
| 232 | } | ||
| 233 | |||
| 234 | static ssize_t host_control_on_shutdown_store(struct device *dev, | ||
| 235 | struct device_attribute *attr, | ||
| 236 | const char *buf, size_t count) | ||
| 237 | { | ||
| 238 | host_control_on_shutdown = simple_strtoul(buf, NULL, 10); | ||
| 239 | return count; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int raise_smi(void *par) | ||
| 243 | { | ||
| 244 | struct smi_cmd *smi_cmd = par; | ||
| 245 | |||
| 246 | if (smp_processor_id() != 0) { | ||
| 247 | dev_dbg(&dcdbas_pdev->dev, "%s: failed to get CPU 0\n", | ||
| 248 | __func__); | ||
| 249 | return -EBUSY; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* generate SMI */ | ||
| 253 | /* inb to force posted write through and make SMI happen now */ | ||
| 254 | asm volatile ( | ||
| 255 | "outb %b0,%w1\n" | ||
| 256 | "inb %w1" | ||
| 257 | : /* no output args */ | ||
| 258 | : "a" (smi_cmd->command_code), | ||
| 259 | "d" (smi_cmd->command_address), | ||
| 260 | "b" (smi_cmd->ebx), | ||
| 261 | "c" (smi_cmd->ecx) | ||
| 262 | : "memory" | ||
| 263 | ); | ||
| 264 | |||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | /** | ||
| 268 | * dcdbas_smi_request: generate SMI request | ||
| 269 | * | ||
| 270 | * Called with smi_data_lock. | ||
| 271 | */ | ||
| 272 | int dcdbas_smi_request(struct smi_cmd *smi_cmd) | ||
| 273 | { | ||
| 274 | int ret; | ||
| 275 | |||
| 276 | if (smi_cmd->magic != SMI_CMD_MAGIC) { | ||
| 277 | dev_info(&dcdbas_pdev->dev, "%s: invalid magic value\n", | ||
| 278 | __func__); | ||
| 279 | return -EBADR; | ||
| 280 | } | ||
| 281 | |||
| 282 | /* SMI requires CPU 0 */ | ||
| 283 | get_online_cpus(); | ||
| 284 | ret = smp_call_on_cpu(0, raise_smi, smi_cmd, true); | ||
| 285 | put_online_cpus(); | ||
| 286 | |||
| 287 | return ret; | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * smi_request_store: | ||
| 292 | * | ||
| 293 | * The valid values are: | ||
| 294 | * 0: zero SMI data buffer | ||
| 295 | * 1: generate calling interface SMI | ||
| 296 | * 2: generate raw SMI | ||
| 297 | * | ||
| 298 | * User application writes smi_cmd to smi_data before telling driver | ||
| 299 | * to generate SMI. | ||
| 300 | */ | ||
| 301 | static ssize_t smi_request_store(struct device *dev, | ||
| 302 | struct device_attribute *attr, | ||
| 303 | const char *buf, size_t count) | ||
| 304 | { | ||
| 305 | struct smi_cmd *smi_cmd; | ||
| 306 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
| 307 | ssize_t ret; | ||
| 308 | |||
| 309 | mutex_lock(&smi_data_lock); | ||
| 310 | |||
| 311 | if (smi_data_buf_size < sizeof(struct smi_cmd)) { | ||
| 312 | ret = -ENODEV; | ||
| 313 | goto out; | ||
| 314 | } | ||
| 315 | smi_cmd = (struct smi_cmd *)smi_data_buf; | ||
| 316 | |||
| 317 | switch (val) { | ||
| 318 | case 2: | ||
| 319 | /* Raw SMI */ | ||
| 320 | ret = dcdbas_smi_request(smi_cmd); | ||
| 321 | if (!ret) | ||
| 322 | ret = count; | ||
| 323 | break; | ||
| 324 | case 1: | ||
| 325 | /* Calling Interface SMI */ | ||
| 326 | smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); | ||
| 327 | ret = dcdbas_smi_request(smi_cmd); | ||
| 328 | if (!ret) | ||
| 329 | ret = count; | ||
| 330 | break; | ||
| 331 | case 0: | ||
| 332 | memset(smi_data_buf, 0, smi_data_buf_size); | ||
| 333 | ret = count; | ||
| 334 | break; | ||
| 335 | default: | ||
| 336 | ret = -EINVAL; | ||
| 337 | break; | ||
| 338 | } | ||
| 339 | |||
| 340 | out: | ||
| 341 | mutex_unlock(&smi_data_lock); | ||
| 342 | return ret; | ||
| 343 | } | ||
| 344 | EXPORT_SYMBOL(dcdbas_smi_request); | ||
| 345 | |||
| 346 | /** | ||
| 347 | * host_control_smi: generate host control SMI | ||
| 348 | * | ||
| 349 | * Caller must set up the host control command in smi_data_buf. | ||
| 350 | */ | ||
| 351 | static int host_control_smi(void) | ||
| 352 | { | ||
| 353 | struct apm_cmd *apm_cmd; | ||
| 354 | u8 *data; | ||
| 355 | unsigned long flags; | ||
| 356 | u32 num_ticks; | ||
| 357 | s8 cmd_status; | ||
| 358 | u8 index; | ||
| 359 | |||
| 360 | apm_cmd = (struct apm_cmd *)smi_data_buf; | ||
| 361 | apm_cmd->status = ESM_STATUS_CMD_UNSUCCESSFUL; | ||
| 362 | |||
| 363 | switch (host_control_smi_type) { | ||
| 364 | case HC_SMITYPE_TYPE1: | ||
| 365 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 366 | /* write SMI data buffer physical address */ | ||
| 367 | data = (u8 *)&smi_data_buf_phys_addr; | ||
| 368 | for (index = PE1300_CMOS_CMD_STRUCT_PTR; | ||
| 369 | index < (PE1300_CMOS_CMD_STRUCT_PTR + 4); | ||
| 370 | index++, data++) { | ||
| 371 | outb(index, | ||
| 372 | (CMOS_BASE_PORT + CMOS_PAGE2_INDEX_PORT_PIIX4)); | ||
| 373 | outb(*data, | ||
| 374 | (CMOS_BASE_PORT + CMOS_PAGE2_DATA_PORT_PIIX4)); | ||
| 375 | } | ||
| 376 | |||
| 377 | /* first set status to -1 as called by spec */ | ||
| 378 | cmd_status = ESM_STATUS_CMD_UNSUCCESSFUL; | ||
| 379 | outb((u8) cmd_status, PCAT_APM_STATUS_PORT); | ||
| 380 | |||
| 381 | /* generate SMM call */ | ||
| 382 | outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); | ||
| 383 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 384 | |||
| 385 | /* wait a few to see if it executed */ | ||
| 386 | num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; | ||
| 387 | while ((cmd_status = inb(PCAT_APM_STATUS_PORT)) | ||
| 388 | == ESM_STATUS_CMD_UNSUCCESSFUL) { | ||
| 389 | num_ticks--; | ||
| 390 | if (num_ticks == EXPIRED_TIMER) | ||
| 391 | return -ETIME; | ||
| 392 | } | ||
| 393 | break; | ||
| 394 | |||
| 395 | case HC_SMITYPE_TYPE2: | ||
| 396 | case HC_SMITYPE_TYPE3: | ||
| 397 | spin_lock_irqsave(&rtc_lock, flags); | ||
| 398 | /* write SMI data buffer physical address */ | ||
| 399 | data = (u8 *)&smi_data_buf_phys_addr; | ||
| 400 | for (index = PE1400_CMOS_CMD_STRUCT_PTR; | ||
| 401 | index < (PE1400_CMOS_CMD_STRUCT_PTR + 4); | ||
| 402 | index++, data++) { | ||
| 403 | outb(index, (CMOS_BASE_PORT + CMOS_PAGE1_INDEX_PORT)); | ||
| 404 | outb(*data, (CMOS_BASE_PORT + CMOS_PAGE1_DATA_PORT)); | ||
| 405 | } | ||
| 406 | |||
| 407 | /* generate SMM call */ | ||
| 408 | if (host_control_smi_type == HC_SMITYPE_TYPE3) | ||
| 409 | outb(ESM_APM_CMD, PCAT_APM_CONTROL_PORT); | ||
| 410 | else | ||
| 411 | outb(ESM_APM_CMD, PE1400_APM_CONTROL_PORT); | ||
| 412 | |||
| 413 | /* restore RTC index pointer since it was written to above */ | ||
| 414 | CMOS_READ(RTC_REG_C); | ||
| 415 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
| 416 | |||
| 417 | /* read control port back to serialize write */ | ||
| 418 | cmd_status = inb(PE1400_APM_CONTROL_PORT); | ||
| 419 | |||
| 420 | /* wait a few to see if it executed */ | ||
| 421 | num_ticks = TIMEOUT_USEC_SHORT_SEMA_BLOCKING; | ||
| 422 | while (apm_cmd->status == ESM_STATUS_CMD_UNSUCCESSFUL) { | ||
| 423 | num_ticks--; | ||
| 424 | if (num_ticks == EXPIRED_TIMER) | ||
| 425 | return -ETIME; | ||
| 426 | } | ||
| 427 | break; | ||
| 428 | |||
| 429 | default: | ||
| 430 | dev_dbg(&dcdbas_pdev->dev, "%s: invalid SMI type %u\n", | ||
| 431 | __func__, host_control_smi_type); | ||
| 432 | return -ENOSYS; | ||
| 433 | } | ||
| 434 | |||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 438 | /** | ||
| 439 | * dcdbas_host_control: initiate host control | ||
| 440 | * | ||
| 441 | * This function is called by the driver after the system has | ||
| 442 | * finished shutting down if the user application specified a | ||
| 443 | * host control action to perform on shutdown. It is safe to | ||
| 444 | * use smi_data_buf at this point because the system has finished | ||
| 445 | * shutting down and no userspace apps are running. | ||
| 446 | */ | ||
| 447 | static void dcdbas_host_control(void) | ||
| 448 | { | ||
| 449 | struct apm_cmd *apm_cmd; | ||
| 450 | u8 action; | ||
| 451 | |||
| 452 | if (host_control_action == HC_ACTION_NONE) | ||
| 453 | return; | ||
| 454 | |||
| 455 | action = host_control_action; | ||
| 456 | host_control_action = HC_ACTION_NONE; | ||
| 457 | |||
| 458 | if (!smi_data_buf) { | ||
| 459 | dev_dbg(&dcdbas_pdev->dev, "%s: no SMI buffer\n", __func__); | ||
| 460 | return; | ||
| 461 | } | ||
| 462 | |||
| 463 | if (smi_data_buf_size < sizeof(struct apm_cmd)) { | ||
| 464 | dev_dbg(&dcdbas_pdev->dev, "%s: SMI buffer too small\n", | ||
| 465 | __func__); | ||
| 466 | return; | ||
| 467 | } | ||
| 468 | |||
| 469 | apm_cmd = (struct apm_cmd *)smi_data_buf; | ||
| 470 | |||
| 471 | /* power off takes precedence */ | ||
| 472 | if (action & HC_ACTION_HOST_CONTROL_POWEROFF) { | ||
| 473 | apm_cmd->command = ESM_APM_POWER_CYCLE; | ||
| 474 | apm_cmd->reserved = 0; | ||
| 475 | *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 0; | ||
| 476 | host_control_smi(); | ||
| 477 | } else if (action & HC_ACTION_HOST_CONTROL_POWERCYCLE) { | ||
| 478 | apm_cmd->command = ESM_APM_POWER_CYCLE; | ||
| 479 | apm_cmd->reserved = 0; | ||
| 480 | *((s16 *)&apm_cmd->parameters.shortreq.parm[0]) = (s16) 20; | ||
| 481 | host_control_smi(); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | /** | ||
| 486 | * dcdbas_reboot_notify: handle reboot notification for host control | ||
| 487 | */ | ||
| 488 | static int dcdbas_reboot_notify(struct notifier_block *nb, unsigned long code, | ||
| 489 | void *unused) | ||
| 490 | { | ||
| 491 | switch (code) { | ||
| 492 | case SYS_DOWN: | ||
| 493 | case SYS_HALT: | ||
| 494 | case SYS_POWER_OFF: | ||
| 495 | if (host_control_on_shutdown) { | ||
| 496 | /* firmware is going to perform host control action */ | ||
| 497 | printk(KERN_WARNING "Please wait for shutdown " | ||
| 498 | "action to complete...\n"); | ||
| 499 | dcdbas_host_control(); | ||
| 500 | } | ||
| 501 | break; | ||
| 502 | } | ||
| 503 | |||
| 504 | return NOTIFY_DONE; | ||
| 505 | } | ||
| 506 | |||
| 507 | static struct notifier_block dcdbas_reboot_nb = { | ||
| 508 | .notifier_call = dcdbas_reboot_notify, | ||
| 509 | .next = NULL, | ||
| 510 | .priority = INT_MIN | ||
| 511 | }; | ||
| 512 | |||
| 513 | static DCDBAS_BIN_ATTR_RW(smi_data); | ||
| 514 | |||
| 515 | static struct bin_attribute *dcdbas_bin_attrs[] = { | ||
| 516 | &bin_attr_smi_data, | ||
| 517 | NULL | ||
| 518 | }; | ||
| 519 | |||
| 520 | static DCDBAS_DEV_ATTR_RW(smi_data_buf_size); | ||
| 521 | static DCDBAS_DEV_ATTR_RO(smi_data_buf_phys_addr); | ||
| 522 | static DCDBAS_DEV_ATTR_WO(smi_request); | ||
| 523 | static DCDBAS_DEV_ATTR_RW(host_control_action); | ||
| 524 | static DCDBAS_DEV_ATTR_RW(host_control_smi_type); | ||
| 525 | static DCDBAS_DEV_ATTR_RW(host_control_on_shutdown); | ||
| 526 | |||
| 527 | static struct attribute *dcdbas_dev_attrs[] = { | ||
| 528 | &dev_attr_smi_data_buf_size.attr, | ||
| 529 | &dev_attr_smi_data_buf_phys_addr.attr, | ||
| 530 | &dev_attr_smi_request.attr, | ||
| 531 | &dev_attr_host_control_action.attr, | ||
| 532 | &dev_attr_host_control_smi_type.attr, | ||
| 533 | &dev_attr_host_control_on_shutdown.attr, | ||
| 534 | NULL | ||
| 535 | }; | ||
| 536 | |||
| 537 | static const struct attribute_group dcdbas_attr_group = { | ||
| 538 | .attrs = dcdbas_dev_attrs, | ||
| 539 | .bin_attrs = dcdbas_bin_attrs, | ||
| 540 | }; | ||
| 541 | |||
| 542 | static int dcdbas_probe(struct platform_device *dev) | ||
| 543 | { | ||
| 544 | int error; | ||
| 545 | |||
| 546 | host_control_action = HC_ACTION_NONE; | ||
| 547 | host_control_smi_type = HC_SMITYPE_NONE; | ||
| 548 | |||
| 549 | dcdbas_pdev = dev; | ||
| 550 | |||
| 551 | /* | ||
| 552 | * BIOS SMI calls require buffer addresses be in 32-bit address space. | ||
| 553 | * This is done by setting the DMA mask below. | ||
| 554 | */ | ||
| 555 | error = dma_set_coherent_mask(&dcdbas_pdev->dev, DMA_BIT_MASK(32)); | ||
| 556 | if (error) | ||
| 557 | return error; | ||
| 558 | |||
| 559 | error = sysfs_create_group(&dev->dev.kobj, &dcdbas_attr_group); | ||
| 560 | if (error) | ||
| 561 | return error; | ||
| 562 | |||
| 563 | register_reboot_notifier(&dcdbas_reboot_nb); | ||
| 564 | |||
| 565 | dev_info(&dev->dev, "%s (version %s)\n", | ||
| 566 | DRIVER_DESCRIPTION, DRIVER_VERSION); | ||
| 567 | |||
| 568 | return 0; | ||
| 569 | } | ||
| 570 | |||
| 571 | static int dcdbas_remove(struct platform_device *dev) | ||
| 572 | { | ||
| 573 | unregister_reboot_notifier(&dcdbas_reboot_nb); | ||
| 574 | sysfs_remove_group(&dev->dev.kobj, &dcdbas_attr_group); | ||
| 575 | |||
| 576 | return 0; | ||
| 577 | } | ||
| 578 | |||
| 579 | static struct platform_driver dcdbas_driver = { | ||
| 580 | .driver = { | ||
| 581 | .name = DRIVER_NAME, | ||
| 582 | }, | ||
| 583 | .probe = dcdbas_probe, | ||
| 584 | .remove = dcdbas_remove, | ||
| 585 | }; | ||
| 586 | |||
| 587 | static const struct platform_device_info dcdbas_dev_info __initconst = { | ||
| 588 | .name = DRIVER_NAME, | ||
| 589 | .id = -1, | ||
| 590 | .dma_mask = DMA_BIT_MASK(32), | ||
| 591 | }; | ||
| 592 | |||
| 593 | static struct platform_device *dcdbas_pdev_reg; | ||
| 594 | |||
| 595 | /** | ||
| 596 | * dcdbas_init: initialize driver | ||
| 597 | */ | ||
| 598 | static int __init dcdbas_init(void) | ||
| 599 | { | ||
| 600 | int error; | ||
| 601 | |||
| 602 | error = platform_driver_register(&dcdbas_driver); | ||
| 603 | if (error) | ||
| 604 | return error; | ||
| 605 | |||
| 606 | dcdbas_pdev_reg = platform_device_register_full(&dcdbas_dev_info); | ||
| 607 | if (IS_ERR(dcdbas_pdev_reg)) { | ||
| 608 | error = PTR_ERR(dcdbas_pdev_reg); | ||
| 609 | goto err_unregister_driver; | ||
| 610 | } | ||
| 611 | |||
| 612 | return 0; | ||
| 613 | |||
| 614 | err_unregister_driver: | ||
| 615 | platform_driver_unregister(&dcdbas_driver); | ||
| 616 | return error; | ||
| 617 | } | ||
| 618 | |||
| 619 | /** | ||
| 620 | * dcdbas_exit: perform driver cleanup | ||
| 621 | */ | ||
| 622 | static void __exit dcdbas_exit(void) | ||
| 623 | { | ||
| 624 | /* | ||
| 625 | * make sure functions that use dcdbas_pdev are called | ||
| 626 | * before platform_device_unregister | ||
| 627 | */ | ||
| 628 | unregister_reboot_notifier(&dcdbas_reboot_nb); | ||
| 629 | |||
| 630 | /* | ||
| 631 | * We have to free the buffer here instead of dcdbas_remove | ||
| 632 | * because only in module exit function we can be sure that | ||
| 633 | * all sysfs attributes belonging to this module have been | ||
| 634 | * released. | ||
| 635 | */ | ||
| 636 | if (dcdbas_pdev) | ||
| 637 | smi_data_buf_free(); | ||
| 638 | platform_device_unregister(dcdbas_pdev_reg); | ||
| 639 | platform_driver_unregister(&dcdbas_driver); | ||
| 640 | } | ||
| 641 | |||
| 642 | subsys_initcall_sync(dcdbas_init); | ||
| 643 | module_exit(dcdbas_exit); | ||
| 644 | |||
| 645 | MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")"); | ||
| 646 | MODULE_VERSION(DRIVER_VERSION); | ||
| 647 | MODULE_AUTHOR("Dell Inc."); | ||
| 648 | MODULE_LICENSE("GPL"); | ||
| 649 | /* Any System or BIOS claiming to be by Dell */ | ||
| 650 | MODULE_ALIAS("dmi:*:[bs]vnD[Ee][Ll][Ll]*:*"); | ||
diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h deleted file mode 100644 index ca3cb0a54ab6..000000000000 --- a/drivers/firmware/dcdbas.h +++ /dev/null | |||
| @@ -1,107 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * dcdbas.h: Definitions for Dell Systems Management Base driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 1995-2005 Dell Inc. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License v2.0 as published by | ||
| 8 | * the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #ifndef _DCDBAS_H_ | ||
| 17 | #define _DCDBAS_H_ | ||
| 18 | |||
| 19 | #include <linux/device.h> | ||
| 20 | #include <linux/sysfs.h> | ||
| 21 | #include <linux/types.h> | ||
| 22 | |||
| 23 | #define MAX_SMI_DATA_BUF_SIZE (256 * 1024) | ||
| 24 | |||
| 25 | #define HC_ACTION_NONE (0) | ||
| 26 | #define HC_ACTION_HOST_CONTROL_POWEROFF BIT(1) | ||
| 27 | #define HC_ACTION_HOST_CONTROL_POWERCYCLE BIT(2) | ||
| 28 | |||
| 29 | #define HC_SMITYPE_NONE (0) | ||
| 30 | #define HC_SMITYPE_TYPE1 (1) | ||
| 31 | #define HC_SMITYPE_TYPE2 (2) | ||
| 32 | #define HC_SMITYPE_TYPE3 (3) | ||
| 33 | |||
| 34 | #define ESM_APM_CMD (0x0A0) | ||
| 35 | #define ESM_APM_POWER_CYCLE (0x10) | ||
| 36 | #define ESM_STATUS_CMD_UNSUCCESSFUL (-1) | ||
| 37 | |||
| 38 | #define CMOS_BASE_PORT (0x070) | ||
| 39 | #define CMOS_PAGE1_INDEX_PORT (0) | ||
| 40 | #define CMOS_PAGE1_DATA_PORT (1) | ||
| 41 | #define CMOS_PAGE2_INDEX_PORT_PIIX4 (2) | ||
| 42 | #define CMOS_PAGE2_DATA_PORT_PIIX4 (3) | ||
| 43 | #define PE1400_APM_CONTROL_PORT (0x0B0) | ||
| 44 | #define PCAT_APM_CONTROL_PORT (0x0B2) | ||
| 45 | #define PCAT_APM_STATUS_PORT (0x0B3) | ||
| 46 | #define PE1300_CMOS_CMD_STRUCT_PTR (0x38) | ||
| 47 | #define PE1400_CMOS_CMD_STRUCT_PTR (0x70) | ||
| 48 | |||
| 49 | #define MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN (14) | ||
| 50 | #define MAX_SYSMGMT_LONGCMD_SGENTRY_NUM (16) | ||
| 51 | |||
| 52 | #define TIMEOUT_USEC_SHORT_SEMA_BLOCKING (10000) | ||
| 53 | #define EXPIRED_TIMER (0) | ||
| 54 | |||
| 55 | #define SMI_CMD_MAGIC (0x534D4931) | ||
| 56 | |||
| 57 | #define DCDBAS_DEV_ATTR_RW(_name) \ | ||
| 58 | DEVICE_ATTR(_name,0600,_name##_show,_name##_store); | ||
| 59 | |||
| 60 | #define DCDBAS_DEV_ATTR_RO(_name) \ | ||
| 61 | DEVICE_ATTR(_name,0400,_name##_show,NULL); | ||
| 62 | |||
| 63 | #define DCDBAS_DEV_ATTR_WO(_name) \ | ||
| 64 | DEVICE_ATTR(_name,0200,NULL,_name##_store); | ||
| 65 | |||
| 66 | #define DCDBAS_BIN_ATTR_RW(_name) \ | ||
| 67 | struct bin_attribute bin_attr_##_name = { \ | ||
| 68 | .attr = { .name = __stringify(_name), \ | ||
| 69 | .mode = 0600 }, \ | ||
| 70 | .read = _name##_read, \ | ||
| 71 | .write = _name##_write, \ | ||
| 72 | } | ||
| 73 | |||
| 74 | struct smi_cmd { | ||
| 75 | __u32 magic; | ||
| 76 | __u32 ebx; | ||
| 77 | __u32 ecx; | ||
| 78 | __u16 command_address; | ||
| 79 | __u8 command_code; | ||
| 80 | __u8 reserved; | ||
| 81 | __u8 command_buffer[1]; | ||
| 82 | } __attribute__ ((packed)); | ||
| 83 | |||
| 84 | struct apm_cmd { | ||
| 85 | __u8 command; | ||
| 86 | __s8 status; | ||
| 87 | __u16 reserved; | ||
| 88 | union { | ||
| 89 | struct { | ||
| 90 | __u8 parm[MAX_SYSMGMT_SHORTCMD_PARMBUF_LEN]; | ||
| 91 | } __attribute__ ((packed)) shortreq; | ||
| 92 | |||
| 93 | struct { | ||
| 94 | __u16 num_sg_entries; | ||
| 95 | struct { | ||
| 96 | __u32 size; | ||
| 97 | __u64 addr; | ||
| 98 | } __attribute__ ((packed)) | ||
| 99 | sglist[MAX_SYSMGMT_LONGCMD_SGENTRY_NUM]; | ||
| 100 | } __attribute__ ((packed)) longreq; | ||
| 101 | } __attribute__ ((packed)) parameters; | ||
| 102 | } __attribute__ ((packed)); | ||
| 103 | |||
| 104 | int dcdbas_smi_request(struct smi_cmd *smi_cmd); | ||
| 105 | |||
| 106 | #endif /* _DCDBAS_H_ */ | ||
| 107 | |||
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c deleted file mode 100644 index fb8af5cb7c9b..000000000000 --- a/drivers/firmware/dell_rbu.c +++ /dev/null | |||
| @@ -1,745 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * dell_rbu.c | ||
| 3 | * Bios Update driver for Dell systems | ||
| 4 | * Author: Dell Inc | ||
| 5 | * Abhay Salunke <abhay_salunke@dell.com> | ||
| 6 | * | ||
| 7 | * Copyright (C) 2005 Dell Inc. | ||
| 8 | * | ||
| 9 | * Remote BIOS Update (rbu) driver is used for updating DELL BIOS by | ||
| 10 | * creating entries in the /sys file systems on Linux 2.6 and higher | ||
| 11 | * kernels. The driver supports two mechanism to update the BIOS namely | ||
| 12 | * contiguous and packetized. Both these methods still require having some | ||
| 13 | * application to set the CMOS bit indicating the BIOS to update itself | ||
| 14 | * after a reboot. | ||
| 15 | * | ||
| 16 | * Contiguous method: | ||
| 17 | * This driver writes the incoming data in a monolithic image by allocating | ||
| 18 | * contiguous physical pages large enough to accommodate the incoming BIOS | ||
| 19 | * image size. | ||
| 20 | * | ||
| 21 | * Packetized method: | ||
| 22 | * The driver writes the incoming packet image by allocating a new packet | ||
| 23 | * on every time the packet data is written. This driver requires an | ||
| 24 | * application to break the BIOS image in to fixed sized packet chunks. | ||
| 25 | * | ||
| 26 | * See Documentation/dell_rbu.txt for more info. | ||
| 27 | * | ||
| 28 | * This program is free software; you can redistribute it and/or modify | ||
| 29 | * it under the terms of the GNU General Public License v2.0 as published by | ||
| 30 | * the Free Software Foundation | ||
| 31 | * | ||
| 32 | * This program is distributed in the hope that it will be useful, | ||
| 33 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 34 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 35 | * GNU General Public License for more details. | ||
| 36 | */ | ||
| 37 | #include <linux/init.h> | ||
| 38 | #include <linux/module.h> | ||
| 39 | #include <linux/slab.h> | ||
| 40 | #include <linux/string.h> | ||
| 41 | #include <linux/errno.h> | ||
| 42 | #include <linux/blkdev.h> | ||
| 43 | #include <linux/platform_device.h> | ||
| 44 | #include <linux/spinlock.h> | ||
| 45 | #include <linux/moduleparam.h> | ||
| 46 | #include <linux/firmware.h> | ||
| 47 | #include <linux/dma-mapping.h> | ||
| 48 | |||
| 49 | MODULE_AUTHOR("Abhay Salunke <abhay_salunke@dell.com>"); | ||
| 50 | MODULE_DESCRIPTION("Driver for updating BIOS image on DELL systems"); | ||
| 51 | MODULE_LICENSE("GPL"); | ||
| 52 | MODULE_VERSION("3.2"); | ||
| 53 | |||
| 54 | #define BIOS_SCAN_LIMIT 0xffffffff | ||
| 55 | #define MAX_IMAGE_LENGTH 16 | ||
| 56 | static struct _rbu_data { | ||
| 57 | void *image_update_buffer; | ||
| 58 | unsigned long image_update_buffer_size; | ||
| 59 | unsigned long bios_image_size; | ||
| 60 | int image_update_ordernum; | ||
| 61 | int dma_alloc; | ||
| 62 | spinlock_t lock; | ||
| 63 | unsigned long packet_read_count; | ||
| 64 | unsigned long num_packets; | ||
| 65 | unsigned long packetsize; | ||
| 66 | unsigned long imagesize; | ||
| 67 | int entry_created; | ||
| 68 | } rbu_data; | ||
| 69 | |||
| 70 | static char image_type[MAX_IMAGE_LENGTH + 1] = "mono"; | ||
| 71 | module_param_string(image_type, image_type, sizeof (image_type), 0); | ||
| 72 | MODULE_PARM_DESC(image_type, | ||
| 73 | "BIOS image type. choose- mono or packet or init"); | ||
| 74 | |||
| 75 | static unsigned long allocation_floor = 0x100000; | ||
| 76 | module_param(allocation_floor, ulong, 0644); | ||
| 77 | MODULE_PARM_DESC(allocation_floor, | ||
| 78 | "Minimum address for allocations when using Packet mode"); | ||
| 79 | |||
| 80 | struct packet_data { | ||
| 81 | struct list_head list; | ||
| 82 | size_t length; | ||
| 83 | void *data; | ||
| 84 | int ordernum; | ||
| 85 | }; | ||
| 86 | |||
| 87 | static struct packet_data packet_data_head; | ||
| 88 | |||
| 89 | static struct platform_device *rbu_device; | ||
| 90 | static int context; | ||
| 91 | static dma_addr_t dell_rbu_dmaaddr; | ||
| 92 | |||
| 93 | static void init_packet_head(void) | ||
| 94 | { | ||
| 95 | INIT_LIST_HEAD(&packet_data_head.list); | ||
| 96 | rbu_data.packet_read_count = 0; | ||
| 97 | rbu_data.num_packets = 0; | ||
| 98 | rbu_data.packetsize = 0; | ||
| 99 | rbu_data.imagesize = 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int create_packet(void *data, size_t length) | ||
| 103 | { | ||
| 104 | struct packet_data *newpacket; | ||
| 105 | int ordernum = 0; | ||
| 106 | int retval = 0; | ||
| 107 | unsigned int packet_array_size = 0; | ||
| 108 | void **invalid_addr_packet_array = NULL; | ||
| 109 | void *packet_data_temp_buf = NULL; | ||
| 110 | unsigned int idx = 0; | ||
| 111 | |||
| 112 | pr_debug("create_packet: entry \n"); | ||
| 113 | |||
| 114 | if (!rbu_data.packetsize) { | ||
| 115 | pr_debug("create_packet: packetsize not specified\n"); | ||
| 116 | retval = -EINVAL; | ||
| 117 | goto out_noalloc; | ||
| 118 | } | ||
| 119 | |||
| 120 | spin_unlock(&rbu_data.lock); | ||
| 121 | |||
| 122 | newpacket = kzalloc(sizeof (struct packet_data), GFP_KERNEL); | ||
| 123 | |||
| 124 | if (!newpacket) { | ||
| 125 | printk(KERN_WARNING | ||
| 126 | "dell_rbu:%s: failed to allocate new " | ||
| 127 | "packet\n", __func__); | ||
| 128 | retval = -ENOMEM; | ||
| 129 | spin_lock(&rbu_data.lock); | ||
| 130 | goto out_noalloc; | ||
| 131 | } | ||
| 132 | |||
| 133 | ordernum = get_order(length); | ||
| 134 | |||
| 135 | /* | ||
| 136 | * BIOS errata mean we cannot allocate packets below 1MB or they will | ||
| 137 | * be overwritten by BIOS. | ||
| 138 | * | ||
| 139 | * array to temporarily hold packets | ||
| 140 | * that are below the allocation floor | ||
| 141 | * | ||
| 142 | * NOTE: very simplistic because we only need the floor to be at 1MB | ||
| 143 | * due to BIOS errata. This shouldn't be used for higher floors | ||
| 144 | * or you will run out of mem trying to allocate the array. | ||
| 145 | */ | ||
| 146 | packet_array_size = max( | ||
| 147 | (unsigned int)(allocation_floor / rbu_data.packetsize), | ||
| 148 | (unsigned int)1); | ||
| 149 | invalid_addr_packet_array = kcalloc(packet_array_size, sizeof(void *), | ||
| 150 | GFP_KERNEL); | ||
| 151 | |||
| 152 | if (!invalid_addr_packet_array) { | ||
| 153 | printk(KERN_WARNING | ||
| 154 | "dell_rbu:%s: failed to allocate " | ||
| 155 | "invalid_addr_packet_array \n", | ||
| 156 | __func__); | ||
| 157 | retval = -ENOMEM; | ||
| 158 | spin_lock(&rbu_data.lock); | ||
| 159 | goto out_alloc_packet; | ||
| 160 | } | ||
| 161 | |||
| 162 | while (!packet_data_temp_buf) { | ||
| 163 | packet_data_temp_buf = (unsigned char *) | ||
| 164 | __get_free_pages(GFP_KERNEL, ordernum); | ||
| 165 | if (!packet_data_temp_buf) { | ||
| 166 | printk(KERN_WARNING | ||
| 167 | "dell_rbu:%s: failed to allocate new " | ||
| 168 | "packet\n", __func__); | ||
| 169 | retval = -ENOMEM; | ||
| 170 | spin_lock(&rbu_data.lock); | ||
| 171 | goto out_alloc_packet_array; | ||
| 172 | } | ||
| 173 | |||
| 174 | if ((unsigned long)virt_to_phys(packet_data_temp_buf) | ||
| 175 | < allocation_floor) { | ||
| 176 | pr_debug("packet 0x%lx below floor at 0x%lx.\n", | ||
| 177 | (unsigned long)virt_to_phys( | ||
| 178 | packet_data_temp_buf), | ||
| 179 | allocation_floor); | ||
| 180 | invalid_addr_packet_array[idx++] = packet_data_temp_buf; | ||
| 181 | packet_data_temp_buf = NULL; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | spin_lock(&rbu_data.lock); | ||
| 185 | |||
| 186 | newpacket->data = packet_data_temp_buf; | ||
| 187 | |||
| 188 | pr_debug("create_packet: newpacket at physical addr %lx\n", | ||
| 189 | (unsigned long)virt_to_phys(newpacket->data)); | ||
| 190 | |||
| 191 | /* packets may not have fixed size */ | ||
| 192 | newpacket->length = length; | ||
| 193 | newpacket->ordernum = ordernum; | ||
| 194 | ++rbu_data.num_packets; | ||
| 195 | |||
| 196 | /* initialize the newly created packet headers */ | ||
| 197 | INIT_LIST_HEAD(&newpacket->list); | ||
| 198 | list_add_tail(&newpacket->list, &packet_data_head.list); | ||
| 199 | |||
| 200 | memcpy(newpacket->data, data, length); | ||
| 201 | |||
| 202 | pr_debug("create_packet: exit \n"); | ||
| 203 | |||
| 204 | out_alloc_packet_array: | ||
| 205 | /* always free packet array */ | ||
| 206 | for (;idx>0;idx--) { | ||
| 207 | pr_debug("freeing unused packet below floor 0x%lx.\n", | ||
| 208 | (unsigned long)virt_to_phys( | ||
| 209 | invalid_addr_packet_array[idx-1])); | ||
| 210 | free_pages((unsigned long)invalid_addr_packet_array[idx-1], | ||
| 211 | ordernum); | ||
| 212 | } | ||
| 213 | kfree(invalid_addr_packet_array); | ||
| 214 | |||
| 215 | out_alloc_packet: | ||
| 216 | /* if error, free data */ | ||
| 217 | if (retval) | ||
| 218 | kfree(newpacket); | ||
| 219 | |||
| 220 | out_noalloc: | ||
| 221 | return retval; | ||
| 222 | } | ||
| 223 | |||
| 224 | static int packetize_data(const u8 *data, size_t length) | ||
| 225 | { | ||
| 226 | int rc = 0; | ||
| 227 | int done = 0; | ||
| 228 | int packet_length; | ||
| 229 | u8 *temp; | ||
| 230 | u8 *end = (u8 *) data + length; | ||
| 231 | pr_debug("packetize_data: data length %zd\n", length); | ||
| 232 | if (!rbu_data.packetsize) { | ||
| 233 | printk(KERN_WARNING | ||
| 234 | "dell_rbu: packetsize not specified\n"); | ||
| 235 | return -EIO; | ||
| 236 | } | ||
| 237 | |||
| 238 | temp = (u8 *) data; | ||
| 239 | |||
| 240 | /* packetize the hunk */ | ||
| 241 | while (!done) { | ||
| 242 | if ((temp + rbu_data.packetsize) < end) | ||
| 243 | packet_length = rbu_data.packetsize; | ||
| 244 | else { | ||
| 245 | /* this is the last packet */ | ||
| 246 | packet_length = end - temp; | ||
| 247 | done = 1; | ||
| 248 | } | ||
| 249 | |||
| 250 | if ((rc = create_packet(temp, packet_length))) | ||
| 251 | return rc; | ||
| 252 | |||
| 253 | pr_debug("%p:%td\n", temp, (end - temp)); | ||
| 254 | temp += packet_length; | ||
| 255 | } | ||
| 256 | |||
| 257 | rbu_data.imagesize = length; | ||
| 258 | |||
| 259 | return rc; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int do_packet_read(char *data, struct list_head *ptemp_list, | ||
| 263 | int length, int bytes_read, int *list_read_count) | ||
| 264 | { | ||
| 265 | void *ptemp_buf; | ||
| 266 | struct packet_data *newpacket = NULL; | ||
| 267 | int bytes_copied = 0; | ||
| 268 | int j = 0; | ||
| 269 | |||
| 270 | newpacket = list_entry(ptemp_list, struct packet_data, list); | ||
| 271 | *list_read_count += newpacket->length; | ||
| 272 | |||
| 273 | if (*list_read_count > bytes_read) { | ||
| 274 | /* point to the start of unread data */ | ||
| 275 | j = newpacket->length - (*list_read_count - bytes_read); | ||
| 276 | /* point to the offset in the packet buffer */ | ||
| 277 | ptemp_buf = (u8 *) newpacket->data + j; | ||
| 278 | /* | ||
| 279 | * check if there is enough room in | ||
| 280 | * * the incoming buffer | ||
| 281 | */ | ||
| 282 | if (length > (*list_read_count - bytes_read)) | ||
| 283 | /* | ||
| 284 | * copy what ever is there in this | ||
| 285 | * packet and move on | ||
| 286 | */ | ||
| 287 | bytes_copied = (*list_read_count - bytes_read); | ||
| 288 | else | ||
| 289 | /* copy the remaining */ | ||
| 290 | bytes_copied = length; | ||
| 291 | memcpy(data, ptemp_buf, bytes_copied); | ||
| 292 | } | ||
| 293 | return bytes_copied; | ||
| 294 | } | ||
| 295 | |||
| 296 | static int packet_read_list(char *data, size_t * pread_length) | ||
| 297 | { | ||
| 298 | struct list_head *ptemp_list; | ||
| 299 | int temp_count = 0; | ||
| 300 | int bytes_copied = 0; | ||
| 301 | int bytes_read = 0; | ||
| 302 | int remaining_bytes = 0; | ||
| 303 | char *pdest = data; | ||
| 304 | |||
| 305 | /* check if we have any packets */ | ||
| 306 | if (0 == rbu_data.num_packets) | ||
| 307 | return -ENOMEM; | ||
| 308 | |||
| 309 | remaining_bytes = *pread_length; | ||
| 310 | bytes_read = rbu_data.packet_read_count; | ||
| 311 | |||
| 312 | ptemp_list = (&packet_data_head.list)->next; | ||
| 313 | while (!list_empty(ptemp_list)) { | ||
| 314 | bytes_copied = do_packet_read(pdest, ptemp_list, | ||
| 315 | remaining_bytes, bytes_read, &temp_count); | ||
| 316 | remaining_bytes -= bytes_copied; | ||
| 317 | bytes_read += bytes_copied; | ||
| 318 | pdest += bytes_copied; | ||
| 319 | /* | ||
| 320 | * check if we reached end of buffer before reaching the | ||
| 321 | * last packet | ||
| 322 | */ | ||
| 323 | if (remaining_bytes == 0) | ||
| 324 | break; | ||
| 325 | |||
| 326 | ptemp_list = ptemp_list->next; | ||
| 327 | } | ||
| 328 | /*finally set the bytes read */ | ||
| 329 | *pread_length = bytes_read - rbu_data.packet_read_count; | ||
| 330 | rbu_data.packet_read_count = bytes_read; | ||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | static void packet_empty_list(void) | ||
| 335 | { | ||
| 336 | struct list_head *ptemp_list; | ||
| 337 | struct list_head *pnext_list; | ||
| 338 | struct packet_data *newpacket; | ||
| 339 | |||
| 340 | ptemp_list = (&packet_data_head.list)->next; | ||
| 341 | while (!list_empty(ptemp_list)) { | ||
| 342 | newpacket = | ||
| 343 | list_entry(ptemp_list, struct packet_data, list); | ||
| 344 | pnext_list = ptemp_list->next; | ||
| 345 | list_del(ptemp_list); | ||
| 346 | ptemp_list = pnext_list; | ||
| 347 | /* | ||
| 348 | * zero out the RBU packet memory before freeing | ||
| 349 | * to make sure there are no stale RBU packets left in memory | ||
| 350 | */ | ||
| 351 | memset(newpacket->data, 0, rbu_data.packetsize); | ||
| 352 | free_pages((unsigned long) newpacket->data, | ||
| 353 | newpacket->ordernum); | ||
| 354 | kfree(newpacket); | ||
| 355 | } | ||
| 356 | rbu_data.packet_read_count = 0; | ||
| 357 | rbu_data.num_packets = 0; | ||
| 358 | rbu_data.imagesize = 0; | ||
| 359 | } | ||
| 360 | |||
| 361 | /* | ||
| 362 | * img_update_free: Frees the buffer allocated for storing BIOS image | ||
| 363 | * Always called with lock held and returned with lock held | ||
| 364 | */ | ||
| 365 | static void img_update_free(void) | ||
| 366 | { | ||
| 367 | if (!rbu_data.image_update_buffer) | ||
| 368 | return; | ||
| 369 | /* | ||
| 370 | * zero out this buffer before freeing it to get rid of any stale | ||
| 371 | * BIOS image copied in memory. | ||
| 372 | */ | ||
| 373 | memset(rbu_data.image_update_buffer, 0, | ||
| 374 | rbu_data.image_update_buffer_size); | ||
| 375 | if (rbu_data.dma_alloc == 1) | ||
| 376 | dma_free_coherent(NULL, rbu_data.bios_image_size, | ||
| 377 | rbu_data.image_update_buffer, dell_rbu_dmaaddr); | ||
| 378 | else | ||
| 379 | free_pages((unsigned long) rbu_data.image_update_buffer, | ||
| 380 | rbu_data.image_update_ordernum); | ||
| 381 | |||
| 382 | /* | ||
| 383 | * Re-initialize the rbu_data variables after a free | ||
| 384 | */ | ||
| 385 | rbu_data.image_update_ordernum = -1; | ||
| 386 | rbu_data.image_update_buffer = NULL; | ||
| 387 | rbu_data.image_update_buffer_size = 0; | ||
| 388 | rbu_data.bios_image_size = 0; | ||
| 389 | rbu_data.dma_alloc = 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | /* | ||
| 393 | * img_update_realloc: This function allocates the contiguous pages to | ||
| 394 | * accommodate the requested size of data. The memory address and size | ||
| 395 | * values are stored globally and on every call to this function the new | ||
| 396 | * size is checked to see if more data is required than the existing size. | ||
| 397 | * If true the previous memory is freed and new allocation is done to | ||
| 398 | * accommodate the new size. If the incoming size is less then than the | ||
| 399 | * already allocated size, then that memory is reused. This function is | ||
| 400 | * called with lock held and returns with lock held. | ||
| 401 | */ | ||
| 402 | static int img_update_realloc(unsigned long size) | ||
| 403 | { | ||
| 404 | unsigned char *image_update_buffer = NULL; | ||
| 405 | unsigned long rc; | ||
| 406 | unsigned long img_buf_phys_addr; | ||
| 407 | int ordernum; | ||
| 408 | int dma_alloc = 0; | ||
| 409 | |||
| 410 | /* | ||
| 411 | * check if the buffer of sufficient size has been | ||
| 412 | * already allocated | ||
| 413 | */ | ||
| 414 | if (rbu_data.image_update_buffer_size >= size) { | ||
| 415 | /* | ||
| 416 | * check for corruption | ||
| 417 | */ | ||
| 418 | if ((size != 0) && (rbu_data.image_update_buffer == NULL)) { | ||
| 419 | printk(KERN_ERR "dell_rbu:%s: corruption " | ||
| 420 | "check failed\n", __func__); | ||
| 421 | return -EINVAL; | ||
| 422 | } | ||
| 423 | /* | ||
| 424 | * we have a valid pre-allocated buffer with | ||
| 425 | * sufficient size | ||
| 426 | */ | ||
| 427 | return 0; | ||
| 428 | } | ||
| 429 | |||
| 430 | /* | ||
| 431 | * free any previously allocated buffer | ||
| 432 | */ | ||
| 433 | img_update_free(); | ||
| 434 | |||
| 435 | spin_unlock(&rbu_data.lock); | ||
| 436 | |||
| 437 | ordernum = get_order(size); | ||
| 438 | image_update_buffer = | ||
| 439 | (unsigned char *) __get_free_pages(GFP_KERNEL, ordernum); | ||
| 440 | |||
| 441 | img_buf_phys_addr = | ||
| 442 | (unsigned long) virt_to_phys(image_update_buffer); | ||
| 443 | |||
| 444 | if (img_buf_phys_addr > BIOS_SCAN_LIMIT) { | ||
| 445 | free_pages((unsigned long) image_update_buffer, ordernum); | ||
| 446 | ordernum = -1; | ||
| 447 | image_update_buffer = dma_alloc_coherent(NULL, size, | ||
| 448 | &dell_rbu_dmaaddr, GFP_KERNEL); | ||
| 449 | dma_alloc = 1; | ||
| 450 | } | ||
| 451 | |||
| 452 | spin_lock(&rbu_data.lock); | ||
| 453 | |||
| 454 | if (image_update_buffer != NULL) { | ||
| 455 | rbu_data.image_update_buffer = image_update_buffer; | ||
| 456 | rbu_data.image_update_buffer_size = size; | ||
| 457 | rbu_data.bios_image_size = | ||
| 458 | rbu_data.image_update_buffer_size; | ||
| 459 | rbu_data.image_update_ordernum = ordernum; | ||
| 460 | rbu_data.dma_alloc = dma_alloc; | ||
| 461 | rc = 0; | ||
| 462 | } else { | ||
| 463 | pr_debug("Not enough memory for image update:" | ||
| 464 | "size = %ld\n", size); | ||
| 465 | rc = -ENOMEM; | ||
| 466 | } | ||
| 467 | |||
| 468 | return rc; | ||
| 469 | } | ||
| 470 | |||
| 471 | static ssize_t read_packet_data(char *buffer, loff_t pos, size_t count) | ||
| 472 | { | ||
| 473 | int retval; | ||
| 474 | size_t bytes_left; | ||
| 475 | size_t data_length; | ||
| 476 | char *ptempBuf = buffer; | ||
| 477 | |||
| 478 | /* check to see if we have something to return */ | ||
| 479 | if (rbu_data.num_packets == 0) { | ||
| 480 | pr_debug("read_packet_data: no packets written\n"); | ||
| 481 | retval = -ENOMEM; | ||
| 482 | goto read_rbu_data_exit; | ||
| 483 | } | ||
| 484 | |||
| 485 | if (pos > rbu_data.imagesize) { | ||
| 486 | retval = 0; | ||
| 487 | printk(KERN_WARNING "dell_rbu:read_packet_data: " | ||
| 488 | "data underrun\n"); | ||
| 489 | goto read_rbu_data_exit; | ||
| 490 | } | ||
| 491 | |||
| 492 | bytes_left = rbu_data.imagesize - pos; | ||
| 493 | data_length = min(bytes_left, count); | ||
| 494 | |||
| 495 | if ((retval = packet_read_list(ptempBuf, &data_length)) < 0) | ||
| 496 | goto read_rbu_data_exit; | ||
| 497 | |||
| 498 | if ((pos + count) > rbu_data.imagesize) { | ||
| 499 | rbu_data.packet_read_count = 0; | ||
| 500 | /* this was the last copy */ | ||
| 501 | retval = bytes_left; | ||
| 502 | } else | ||
| 503 | retval = count; | ||
| 504 | |||
| 505 | read_rbu_data_exit: | ||
| 506 | return retval; | ||
| 507 | } | ||
| 508 | |||
| 509 | static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) | ||
| 510 | { | ||
| 511 | /* check to see if we have something to return */ | ||
| 512 | if ((rbu_data.image_update_buffer == NULL) || | ||
| 513 | (rbu_data.bios_image_size == 0)) { | ||
| 514 | pr_debug("read_rbu_data_mono: image_update_buffer %p ," | ||
| 515 | "bios_image_size %lu\n", | ||
| 516 | rbu_data.image_update_buffer, | ||
| 517 | rbu_data.bios_image_size); | ||
| 518 | return -ENOMEM; | ||
| 519 | } | ||
| 520 | |||
| 521 | return memory_read_from_buffer(buffer, count, &pos, | ||
| 522 | rbu_data.image_update_buffer, rbu_data.bios_image_size); | ||
| 523 | } | ||
| 524 | |||
| 525 | static ssize_t read_rbu_data(struct file *filp, struct kobject *kobj, | ||
| 526 | struct bin_attribute *bin_attr, | ||
| 527 | char *buffer, loff_t pos, size_t count) | ||
| 528 | { | ||
| 529 | ssize_t ret_count = 0; | ||
| 530 | |||
| 531 | spin_lock(&rbu_data.lock); | ||
| 532 | |||
| 533 | if (!strcmp(image_type, "mono")) | ||
| 534 | ret_count = read_rbu_mono_data(buffer, pos, count); | ||
| 535 | else if (!strcmp(image_type, "packet")) | ||
| 536 | ret_count = read_packet_data(buffer, pos, count); | ||
| 537 | else | ||
| 538 | pr_debug("read_rbu_data: invalid image type specified\n"); | ||
| 539 | |||
| 540 | spin_unlock(&rbu_data.lock); | ||
| 541 | return ret_count; | ||
| 542 | } | ||
| 543 | |||
| 544 | static void callbackfn_rbu(const struct firmware *fw, void *context) | ||
| 545 | { | ||
| 546 | rbu_data.entry_created = 0; | ||
| 547 | |||
| 548 | if (!fw) | ||
| 549 | return; | ||
| 550 | |||
| 551 | if (!fw->size) | ||
| 552 | goto out; | ||
| 553 | |||
| 554 | spin_lock(&rbu_data.lock); | ||
| 555 | if (!strcmp(image_type, "mono")) { | ||
| 556 | if (!img_update_realloc(fw->size)) | ||
| 557 | memcpy(rbu_data.image_update_buffer, | ||
| 558 | fw->data, fw->size); | ||
| 559 | } else if (!strcmp(image_type, "packet")) { | ||
| 560 | /* | ||
| 561 | * we need to free previous packets if a | ||
| 562 | * new hunk of packets needs to be downloaded | ||
| 563 | */ | ||
| 564 | packet_empty_list(); | ||
| 565 | if (packetize_data(fw->data, fw->size)) | ||
| 566 | /* Incase something goes wrong when we are | ||
| 567 | * in middle of packetizing the data, we | ||
| 568 | * need to free up whatever packets might | ||
| 569 | * have been created before we quit. | ||
| 570 | */ | ||
| 571 | packet_empty_list(); | ||
| 572 | } else | ||
| 573 | pr_debug("invalid image type specified.\n"); | ||
| 574 | spin_unlock(&rbu_data.lock); | ||
| 575 | out: | ||
| 576 | release_firmware(fw); | ||
| 577 | } | ||
| 578 | |||
| 579 | static ssize_t read_rbu_image_type(struct file *filp, struct kobject *kobj, | ||
| 580 | struct bin_attribute *bin_attr, | ||
| 581 | char *buffer, loff_t pos, size_t count) | ||
| 582 | { | ||
| 583 | int size = 0; | ||
| 584 | if (!pos) | ||
| 585 | size = scnprintf(buffer, count, "%s\n", image_type); | ||
| 586 | return size; | ||
| 587 | } | ||
| 588 | |||
| 589 | static ssize_t write_rbu_image_type(struct file *filp, struct kobject *kobj, | ||
| 590 | struct bin_attribute *bin_attr, | ||
| 591 | char *buffer, loff_t pos, size_t count) | ||
| 592 | { | ||
| 593 | int rc = count; | ||
| 594 | int req_firm_rc = 0; | ||
| 595 | int i; | ||
| 596 | spin_lock(&rbu_data.lock); | ||
| 597 | /* | ||
| 598 | * Find the first newline or space | ||
| 599 | */ | ||
| 600 | for (i = 0; i < count; ++i) | ||
| 601 | if (buffer[i] == '\n' || buffer[i] == ' ') { | ||
| 602 | buffer[i] = '\0'; | ||
| 603 | break; | ||
| 604 | } | ||
| 605 | if (i == count) | ||
| 606 | buffer[count] = '\0'; | ||
| 607 | |||
| 608 | if (strstr(buffer, "mono")) | ||
| 609 | strcpy(image_type, "mono"); | ||
| 610 | else if (strstr(buffer, "packet")) | ||
| 611 | strcpy(image_type, "packet"); | ||
| 612 | else if (strstr(buffer, "init")) { | ||
| 613 | /* | ||
| 614 | * If due to the user error the driver gets in a bad | ||
| 615 | * state where even though it is loaded , the | ||
| 616 | * /sys/class/firmware/dell_rbu entries are missing. | ||
| 617 | * to cover this situation the user can recreate entries | ||
| 618 | * by writing init to image_type. | ||
| 619 | */ | ||
| 620 | if (!rbu_data.entry_created) { | ||
| 621 | spin_unlock(&rbu_data.lock); | ||
| 622 | req_firm_rc = request_firmware_nowait(THIS_MODULE, | ||
| 623 | FW_ACTION_NOHOTPLUG, "dell_rbu", | ||
| 624 | &rbu_device->dev, GFP_KERNEL, &context, | ||
| 625 | callbackfn_rbu); | ||
| 626 | if (req_firm_rc) { | ||
| 627 | printk(KERN_ERR | ||
| 628 | "dell_rbu:%s request_firmware_nowait" | ||
| 629 | " failed %d\n", __func__, rc); | ||
| 630 | rc = -EIO; | ||
| 631 | } else | ||
| 632 | rbu_data.entry_created = 1; | ||
| 633 | |||
| 634 | spin_lock(&rbu_data.lock); | ||
| 635 | } | ||
| 636 | } else { | ||
| 637 | printk(KERN_WARNING "dell_rbu: image_type is invalid\n"); | ||
| 638 | spin_unlock(&rbu_data.lock); | ||
| 639 | return -EINVAL; | ||
| 640 | } | ||
| 641 | |||
| 642 | /* we must free all previous allocations */ | ||
| 643 | packet_empty_list(); | ||
| 644 | img_update_free(); | ||
| 645 | spin_unlock(&rbu_data.lock); | ||
| 646 | |||
| 647 | return rc; | ||
| 648 | } | ||
| 649 | |||
| 650 | static ssize_t read_rbu_packet_size(struct file *filp, struct kobject *kobj, | ||
| 651 | struct bin_attribute *bin_attr, | ||
| 652 | char *buffer, loff_t pos, size_t count) | ||
| 653 | { | ||
| 654 | int size = 0; | ||
| 655 | if (!pos) { | ||
| 656 | spin_lock(&rbu_data.lock); | ||
| 657 | size = scnprintf(buffer, count, "%lu\n", rbu_data.packetsize); | ||
| 658 | spin_unlock(&rbu_data.lock); | ||
| 659 | } | ||
| 660 | return size; | ||
| 661 | } | ||
| 662 | |||
| 663 | static ssize_t write_rbu_packet_size(struct file *filp, struct kobject *kobj, | ||
| 664 | struct bin_attribute *bin_attr, | ||
| 665 | char *buffer, loff_t pos, size_t count) | ||
| 666 | { | ||
| 667 | unsigned long temp; | ||
| 668 | spin_lock(&rbu_data.lock); | ||
| 669 | packet_empty_list(); | ||
| 670 | sscanf(buffer, "%lu", &temp); | ||
| 671 | if (temp < 0xffffffff) | ||
| 672 | rbu_data.packetsize = temp; | ||
| 673 | |||
| 674 | spin_unlock(&rbu_data.lock); | ||
| 675 | return count; | ||
| 676 | } | ||
| 677 | |||
| 678 | static struct bin_attribute rbu_data_attr = { | ||
| 679 | .attr = {.name = "data", .mode = 0444}, | ||
| 680 | .read = read_rbu_data, | ||
| 681 | }; | ||
| 682 | |||
| 683 | static struct bin_attribute rbu_image_type_attr = { | ||
| 684 | .attr = {.name = "image_type", .mode = 0644}, | ||
| 685 | .read = read_rbu_image_type, | ||
| 686 | .write = write_rbu_image_type, | ||
| 687 | }; | ||
| 688 | |||
| 689 | static struct bin_attribute rbu_packet_size_attr = { | ||
| 690 | .attr = {.name = "packet_size", .mode = 0644}, | ||
| 691 | .read = read_rbu_packet_size, | ||
| 692 | .write = write_rbu_packet_size, | ||
| 693 | }; | ||
| 694 | |||
| 695 | static int __init dcdrbu_init(void) | ||
| 696 | { | ||
| 697 | int rc; | ||
| 698 | spin_lock_init(&rbu_data.lock); | ||
| 699 | |||
| 700 | init_packet_head(); | ||
| 701 | rbu_device = platform_device_register_simple("dell_rbu", -1, NULL, 0); | ||
| 702 | if (IS_ERR(rbu_device)) { | ||
| 703 | printk(KERN_ERR | ||
| 704 | "dell_rbu:%s:platform_device_register_simple " | ||
| 705 | "failed\n", __func__); | ||
| 706 | return PTR_ERR(rbu_device); | ||
| 707 | } | ||
| 708 | |||
| 709 | rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); | ||
| 710 | if (rc) | ||
| 711 | goto out_devreg; | ||
| 712 | rc = sysfs_create_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); | ||
| 713 | if (rc) | ||
| 714 | goto out_data; | ||
| 715 | rc = sysfs_create_bin_file(&rbu_device->dev.kobj, | ||
| 716 | &rbu_packet_size_attr); | ||
| 717 | if (rc) | ||
| 718 | goto out_imtype; | ||
| 719 | |||
| 720 | rbu_data.entry_created = 0; | ||
| 721 | return 0; | ||
| 722 | |||
| 723 | out_imtype: | ||
| 724 | sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_image_type_attr); | ||
| 725 | out_data: | ||
| 726 | sysfs_remove_bin_file(&rbu_device->dev.kobj, &rbu_data_attr); | ||
| 727 | out_devreg: | ||
| 728 | platform_device_unregister(rbu_device); | ||
| 729 | return rc; | ||
| 730 | } | ||
| 731 | |||
| 732 | static __exit void dcdrbu_exit(void) | ||
| 733 | { | ||
| 734 | spin_lock(&rbu_data.lock); | ||
| 735 | packet_empty_list(); | ||
| 736 | img_update_free(); | ||
| 737 | spin_unlock(&rbu_data.lock); | ||
| 738 | platform_device_unregister(rbu_device); | ||
| 739 | } | ||
| 740 | |||
| 741 | module_exit(dcdrbu_exit); | ||
| 742 | module_init(dcdrbu_init); | ||
| 743 | |||
| 744 | /* vim:noet:ts=8:sw=8 | ||
| 745 | */ | ||
