From deaf39efbc0829f26ae0b8fbe5de820588982f72 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:36 -0800 Subject: mfd: Add ChromeOS EC messages header This file is included verbatim from the ChromeOS EC respository. Ideally we would prefer to avoid changing it, to make it easier to track this rapidly-changing file. Signed-off-by: Simon Glass Signed-off-by: Che-Liang Chiou Signed-off-by: Vincent Palatin Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h new file mode 100644 index 0000000..86fd069 --- /dev/null +++ b/include/linux/mfd/cros_ec_commands.h @@ -0,0 +1,1369 @@ +/* + * Host communication command constants for ChromeOS EC + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + * + * NOTE: This file is copied verbatim from the ChromeOS EC Open Source + * project in an attempt to make future updates easy to make. + */ + +#ifndef __CROS_EC_COMMANDS_H +#define __CROS_EC_COMMANDS_H + +/* + * Protocol overview + * + * request: CMD [ P0 P1 P2 ... Pn S ] + * response: ERR [ P0 P1 P2 ... Pn S ] + * + * where the bytes are defined as follow : + * - CMD is the command code. (defined by EC_CMD_ constants) + * - ERR is the error code. (defined by EC_RES_ constants) + * - Px is the optional payload. + * it is not sent if the error code is not success. + * (defined by ec_params_ and ec_response_ structures) + * - S is the checksum which is the sum of all payload bytes. + * + * On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD + * and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM. + * On I2C, all bytes are sent serially in the same message. + */ + +/* Current version of this protocol */ +#define EC_PROTO_VERSION 0x00000002 + +/* Command version mask */ +#define EC_VER_MASK(version) (1UL << (version)) + +/* I/O addresses for ACPI commands */ +#define EC_LPC_ADDR_ACPI_DATA 0x62 +#define EC_LPC_ADDR_ACPI_CMD 0x66 + +/* I/O addresses for host command */ +#define EC_LPC_ADDR_HOST_DATA 0x200 +#define EC_LPC_ADDR_HOST_CMD 0x204 + +/* I/O addresses for host command args and params */ +#define EC_LPC_ADDR_HOST_ARGS 0x800 +#define EC_LPC_ADDR_HOST_PARAM 0x804 +#define EC_HOST_PARAM_SIZE 0x0fc /* Size of param area in bytes */ + +/* I/O addresses for host command params, old interface */ +#define EC_LPC_ADDR_OLD_PARAM 0x880 +#define EC_OLD_PARAM_SIZE 0x080 /* Size of param area in bytes */ + +/* EC command register bit functions */ +#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ +#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ + +#define EC_LPC_ADDR_MEMMAP 0x900 +#define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ +#define EC_MEMMAP_TEXT_MAX 8 /* Size of a string in the memory map */ + +/* The offset address of each type of data in mapped memory. */ +#define EC_MEMMAP_TEMP_SENSOR 0x00 /* Temp sensors */ +#define EC_MEMMAP_FAN 0x10 /* Fan speeds */ +#define EC_MEMMAP_TEMP_SENSOR_B 0x18 /* Temp sensors (second set) */ +#define EC_MEMMAP_ID 0x20 /* 'E' 'C' */ +#define EC_MEMMAP_ID_VERSION 0x22 /* Version of data in 0x20 - 0x2f */ +#define EC_MEMMAP_THERMAL_VERSION 0x23 /* Version of data in 0x00 - 0x1f */ +#define EC_MEMMAP_BATTERY_VERSION 0x24 /* Version of data in 0x40 - 0x7f */ +#define EC_MEMMAP_SWITCHES_VERSION 0x25 /* Version of data in 0x30 - 0x33 */ +#define EC_MEMMAP_EVENTS_VERSION 0x26 /* Version of data in 0x34 - 0x3f */ +#define EC_MEMMAP_HOST_CMD_FLAGS 0x27 /* Host command interface flags */ +#define EC_MEMMAP_SWITCHES 0x30 +#define EC_MEMMAP_HOST_EVENTS 0x34 +#define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ +#define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ +#define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ +#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ +#define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ +#define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ +#define EC_MEMMAP_BATT_CCNT 0x5c /* Battery Cycle Count */ +#define EC_MEMMAP_BATT_MFGR 0x60 /* Battery Manufacturer String */ +#define EC_MEMMAP_BATT_MODEL 0x68 /* Battery Model Number String */ +#define EC_MEMMAP_BATT_SERIAL 0x70 /* Battery Serial Number String */ +#define EC_MEMMAP_BATT_TYPE 0x78 /* Battery Type String */ + +/* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ +#define EC_TEMP_SENSOR_ENTRIES 16 +/* + * Number of temp sensors at EC_MEMMAP_TEMP_SENSOR_B. + * + * Valid only if EC_MEMMAP_THERMAL_VERSION returns >= 2. + */ +#define EC_TEMP_SENSOR_B_ENTRIES 8 +#define EC_TEMP_SENSOR_NOT_PRESENT 0xff +#define EC_TEMP_SENSOR_ERROR 0xfe +#define EC_TEMP_SENSOR_NOT_POWERED 0xfd +#define EC_TEMP_SENSOR_NOT_CALIBRATED 0xfc +/* + * The offset of temperature value stored in mapped memory. This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ +#define EC_TEMP_SENSOR_OFFSET 200 + +#define EC_FAN_SPEED_ENTRIES 4 /* Number of fans at EC_MEMMAP_FAN */ +#define EC_FAN_SPEED_NOT_PRESENT 0xffff /* Entry not present */ +#define EC_FAN_SPEED_STALLED 0xfffe /* Fan stalled */ + +/* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ +#define EC_BATT_FLAG_AC_PRESENT 0x01 +#define EC_BATT_FLAG_BATT_PRESENT 0x02 +#define EC_BATT_FLAG_DISCHARGING 0x04 +#define EC_BATT_FLAG_CHARGING 0x08 +#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 + +/* Switch flags at EC_MEMMAP_SWITCHES */ +#define EC_SWITCH_LID_OPEN 0x01 +#define EC_SWITCH_POWER_BUTTON_PRESSED 0x02 +#define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 +/* Recovery requested via keyboard */ +#define EC_SWITCH_KEYBOARD_RECOVERY 0x08 +/* Recovery requested via dedicated signal (from servo board) */ +#define EC_SWITCH_DEDICATED_RECOVERY 0x10 +/* Was fake developer mode switch; now unused. Remove in next refactor. */ +#define EC_SWITCH_IGNORE0 0x20 + +/* Host command interface flags */ +/* Host command interface supports LPC args (LPC interface only) */ +#define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED 0x01 + +/* Wireless switch flags */ +#define EC_WIRELESS_SWITCH_WLAN 0x01 +#define EC_WIRELESS_SWITCH_BLUETOOTH 0x02 + +/* + * This header file is used in coreboot both in C and ACPI code. The ACPI code + * is pre-processed to handle constants but the ASL compiler is unable to + * handle actual C code so keep it separate. + */ +#ifndef __ACPI__ + +/* LPC command status byte masks */ +/* EC has written a byte in the data register and host hasn't read it yet */ +#define EC_LPC_STATUS_TO_HOST 0x01 +/* Host has written a command/data byte and the EC hasn't read it yet */ +#define EC_LPC_STATUS_FROM_HOST 0x02 +/* EC is processing a command */ +#define EC_LPC_STATUS_PROCESSING 0x04 +/* Last write to EC was a command, not data */ +#define EC_LPC_STATUS_LAST_CMD 0x08 +/* EC is in burst mode. Unsupported by Chrome EC, so this bit is never set */ +#define EC_LPC_STATUS_BURST_MODE 0x10 +/* SCI event is pending (requesting SCI query) */ +#define EC_LPC_STATUS_SCI_PENDING 0x20 +/* SMI event is pending (requesting SMI query) */ +#define EC_LPC_STATUS_SMI_PENDING 0x40 +/* (reserved) */ +#define EC_LPC_STATUS_RESERVED 0x80 + +/* + * EC is busy. This covers both the EC processing a command, and the host has + * written a new command but the EC hasn't picked it up yet. + */ +#define EC_LPC_STATUS_BUSY_MASK \ + (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) + +/* Host command response codes */ +enum ec_status { + EC_RES_SUCCESS = 0, + EC_RES_INVALID_COMMAND = 1, + EC_RES_ERROR = 2, + EC_RES_INVALID_PARAM = 3, + EC_RES_ACCESS_DENIED = 4, + EC_RES_INVALID_RESPONSE = 5, + EC_RES_INVALID_VERSION = 6, + EC_RES_INVALID_CHECKSUM = 7, + EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ + EC_RES_UNAVAILABLE = 9, /* No response available */ + EC_RES_TIMEOUT = 10, /* We got a timeout */ + EC_RES_OVERFLOW = 11, /* Table / data overflow */ +}; + +/* + * Host event codes. Note these are 1-based, not 0-based, because ACPI query + * EC command uses code 0 to mean "no event pending". We explicitly specify + * each value in the enum listing so they won't change if we delete/insert an + * item or rearrange the list (it needs to be stable across platforms, not + * just within a single compiled instance). + */ +enum host_event_code { + EC_HOST_EVENT_LID_CLOSED = 1, + EC_HOST_EVENT_LID_OPEN = 2, + EC_HOST_EVENT_POWER_BUTTON = 3, + EC_HOST_EVENT_AC_CONNECTED = 4, + EC_HOST_EVENT_AC_DISCONNECTED = 5, + EC_HOST_EVENT_BATTERY_LOW = 6, + EC_HOST_EVENT_BATTERY_CRITICAL = 7, + EC_HOST_EVENT_BATTERY = 8, + EC_HOST_EVENT_THERMAL_THRESHOLD = 9, + EC_HOST_EVENT_THERMAL_OVERLOAD = 10, + EC_HOST_EVENT_THERMAL = 11, + EC_HOST_EVENT_USB_CHARGER = 12, + EC_HOST_EVENT_KEY_PRESSED = 13, + /* + * EC has finished initializing the host interface. The host can check + * for this event following sending a EC_CMD_REBOOT_EC command to + * determine when the EC is ready to accept subsequent commands. + */ + EC_HOST_EVENT_INTERFACE_READY = 14, + /* Keyboard recovery combo has been pressed */ + EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, + + /* Shutdown due to thermal overload */ + EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, + /* Shutdown due to battery level too low */ + EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, + + /* + * The high bit of the event mask is not used as a host event code. If + * it reads back as set, then the entire event mask should be + * considered invalid by the host. This can happen when reading the + * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is + * not initialized on the EC, or improperly configured on the host. + */ + EC_HOST_EVENT_INVALID = 32 +}; +/* Host event mask */ +#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) + +/* Arguments at EC_LPC_ADDR_HOST_ARGS */ +struct ec_lpc_host_args { + uint8_t flags; + uint8_t command_version; + uint8_t data_size; + /* + * Checksum; sum of command + flags + command_version + data_size + + * all params/response data bytes. + */ + uint8_t checksum; +} __packed; + +/* Flags for ec_lpc_host_args.flags */ +/* + * Args are from host. Data area at EC_LPC_ADDR_HOST_PARAM contains command + * params. + * + * If EC gets a command and this flag is not set, this is an old-style command. + * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with + * unknown length. EC must respond with an old-style response (that is, + * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + */ +#define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 +/* + * Args are from EC. Data area at EC_LPC_ADDR_HOST_PARAM contains response. + * + * If EC responds to a command and this flag is not set, this is an old-style + * response. Command version is 0 and response data from EC is at + * EC_LPC_ADDR_OLD_PARAM with unknown length. + */ +#define EC_HOST_ARGS_FLAG_TO_HOST 0x02 + +/* + * Notes on commands: + * + * Each command is an 8-byte command value. Commands which take params or + * return response data specify structs for that data. If no struct is + * specified, the command does not input or output data, respectively. + * Parameter/response length is implicit in the structs. Some underlying + * communication protocols (I2C, SPI) may add length or checksum headers, but + * those are implementation-dependent and not defined here. + */ + +/*****************************************************************************/ +/* General / test commands */ + +/* + * Get protocol version, used to deal with non-backward compatible protocol + * changes. + */ +#define EC_CMD_PROTO_VERSION 0x00 + +struct ec_response_proto_version { + uint32_t version; +} __packed; + +/* + * Hello. This is a simple command to test the EC is responsive to + * commands. + */ +#define EC_CMD_HELLO 0x01 + +struct ec_params_hello { + uint32_t in_data; /* Pass anything here */ +} __packed; + +struct ec_response_hello { + uint32_t out_data; /* Output will be in_data + 0x01020304 */ +} __packed; + +/* Get version number */ +#define EC_CMD_GET_VERSION 0x02 + +enum ec_current_image { + EC_IMAGE_UNKNOWN = 0, + EC_IMAGE_RO, + EC_IMAGE_RW +}; + +struct ec_response_get_version { + /* Null-terminated version strings for RO, RW */ + char version_string_ro[32]; + char version_string_rw[32]; + char reserved[32]; /* Was previously RW-B string */ + uint32_t current_image; /* One of ec_current_image */ +} __packed; + +/* Read test */ +#define EC_CMD_READ_TEST 0x03 + +struct ec_params_read_test { + uint32_t offset; /* Starting value for read buffer */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +struct ec_response_read_test { + uint32_t data[32]; +} __packed; + +/* + * Get build information + * + * Response is null-terminated string. + */ +#define EC_CMD_GET_BUILD_INFO 0x04 + +/* Get chip info */ +#define EC_CMD_GET_CHIP_INFO 0x05 + +struct ec_response_get_chip_info { + /* Null-terminated strings */ + char vendor[32]; + char name[32]; + char revision[32]; /* Mask version */ +} __packed; + +/* Get board HW version */ +#define EC_CMD_GET_BOARD_VERSION 0x06 + +struct ec_response_board_version { + uint16_t board_version; /* A monotonously incrementing number. */ +} __packed; + +/* + * Read memory-mapped data. + * + * This is an alternate interface to memory-mapped data for bus protocols + * which don't support direct-mapped memory - I2C, SPI, etc. + * + * Response is params.size bytes of data. + */ +#define EC_CMD_READ_MEMMAP 0x07 + +struct ec_params_read_memmap { + uint8_t offset; /* Offset in memmap (EC_MEMMAP_*) */ + uint8_t size; /* Size to read in bytes */ +} __packed; + +/* Read versions supported for a command */ +#define EC_CMD_GET_CMD_VERSIONS 0x08 + +struct ec_params_get_cmd_versions { + uint8_t cmd; /* Command to check */ +} __packed; + +struct ec_response_get_cmd_versions { + /* + * Mask of supported versions; use EC_VER_MASK() to compare with a + * desired version. + */ + uint32_t version_mask; +} __packed; + +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS 0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { + EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ +}; + +struct ec_response_get_comms_status { + uint32_t flags; /* Mask of enum ec_comms_status */ +} __packed; + + +/*****************************************************************************/ +/* Flash commands */ + +/* Get flash info */ +#define EC_CMD_FLASH_INFO 0x10 + +struct ec_response_flash_info { + /* Usable flash size, in bytes */ + uint32_t flash_size; + /* + * Write block size. Write offset and size must be a multiple + * of this. + */ + uint32_t write_block_size; + /* + * Erase block size. Erase offset and size must be a multiple + * of this. + */ + uint32_t erase_block_size; + /* + * Protection block size. Protection offset and size must be a + * multiple of this. + */ + uint32_t protect_block_size; +} __packed; + +/* + * Read flash + * + * Response is params.size bytes of data. + */ +#define EC_CMD_FLASH_READ 0x11 + +struct ec_params_flash_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write flash */ +#define EC_CMD_FLASH_WRITE 0x12 + +struct ec_params_flash_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + /* + * Data to write. Could really use EC_PARAM_SIZE - 8, but tidiest to + * use a power of 2 so writes stay aligned. + */ + uint8_t data[64]; +} __packed; + +/* Erase flash */ +#define EC_CMD_FLASH_ERASE 0x13 + +struct ec_params_flash_erase { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ +} __packed; + +/* + * Get/set flash protection. + * + * If mask!=0, sets/clear the requested bits of flags. Depending on the + * firmware write protect GPIO, not all flags will take effect immediately; + * some flags require a subsequent hard reset to take effect. Check the + * returned flags bits to see what actually happened. + * + * If mask=0, simply returns the current flags state. + */ +#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_VER_FLASH_PROTECT 1 /* Command version 1 */ + +/* Flags for flash protection */ +/* RO flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RO_AT_BOOT (1 << 0) +/* + * RO flash code protected now. If this bit is set, at-boot status cannot + * be changed. + */ +#define EC_FLASH_PROTECT_RO_NOW (1 << 1) +/* Entire flash code protected now, until reboot. */ +#define EC_FLASH_PROTECT_ALL_NOW (1 << 2) +/* Flash write protect GPIO is asserted now */ +#define EC_FLASH_PROTECT_GPIO_ASSERTED (1 << 3) +/* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ +#define EC_FLASH_PROTECT_ERROR_STUCK (1 << 4) +/* + * Error - flash protection is in inconsistent state. At least one bank of + * flash which should be protected is not protected. Usually fixed by + * re-requesting the desired flags, or by a hard reset if that fails. + */ +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) +/* Entile flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6) + +struct ec_params_flash_protect { + uint32_t mask; /* Bits in flags to apply */ + uint32_t flags; /* New flags to apply */ +} __packed; + +struct ec_response_flash_protect { + /* Current value of flash protect flags */ + uint32_t flags; + /* + * Flags which are valid on this platform. This allows the caller + * to distinguish between flags which aren't set vs. flags which can't + * be set on this platform. + */ + uint32_t valid_flags; + /* Flags which can be changed given the current protection state */ + uint32_t writable_flags; +} __packed; + +/* + * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash + * write protect. These commands may be reused with version > 0. + */ + +/* Get the region offset/size */ +#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_VER_FLASH_REGION_INFO 1 + +enum ec_flash_region { + /* Region which holds read-only EC image */ + EC_FLASH_REGION_RO, + /* Region which holds rewritable EC image */ + EC_FLASH_REGION_RW, + /* + * Region which should be write-protected in the factory (a superset of + * EC_FLASH_REGION_RO) + */ + EC_FLASH_REGION_WP_RO, +}; + +struct ec_params_flash_region_info { + uint32_t region; /* enum ec_flash_region */ +} __packed; + +struct ec_response_flash_region_info { + uint32_t offset; + uint32_t size; +} __packed; + +/* Read/write VbNvContext */ +#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_VER_VBNV_CONTEXT 1 +#define EC_VBNV_BLOCK_SIZE 16 + +enum ec_vbnvcontext_op { + EC_VBNV_CONTEXT_OP_READ, + EC_VBNV_CONTEXT_OP_WRITE, +}; + +struct ec_params_vbnvcontext { + uint32_t op; + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +struct ec_response_vbnvcontext { + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +/*****************************************************************************/ +/* PWM commands */ + +/* Get fan target RPM */ +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 + +struct ec_response_pwm_get_fan_rpm { + uint32_t rpm; +} __packed; + +/* Set target fan RPM */ +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 + +struct ec_params_pwm_set_fan_target_rpm { + uint32_t rpm; +} __packed; + +/* Get keyboard backlight */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 + +struct ec_response_pwm_get_keyboard_backlight { + uint8_t percent; + uint8_t enabled; +} __packed; + +/* Set keyboard backlight */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 + +struct ec_params_pwm_set_keyboard_backlight { + uint8_t percent; +} __packed; + +/* Set target fan PWM duty cycle */ +#define EC_CMD_PWM_SET_FAN_DUTY 0x24 + +struct ec_params_pwm_set_fan_duty { + uint32_t percent; +} __packed; + +/*****************************************************************************/ +/* + * Lightbar commands. This looks worse than it is. Since we only use one HOST + * command to say "talk to the lightbar", we put the "and tell it to do X" part + * into a subcommand. We'll make separate structs for subcommands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_LIGHTBAR_CMD 0x28 + +struct rgb_s { + uint8_t r, g, b; +}; + +#define LB_BATTERY_LEVELS 4 +/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a + * host command, but the alignment is the same regardless. Keep it that way. + */ +struct lightbar_params { + /* Timing */ + int google_ramp_up; + int google_ramp_down; + int s3s0_ramp_up; + int s0_tick_delay[2]; /* AC=0/1 */ + int s0a_tick_delay[2]; /* AC=0/1 */ + int s0s3_ramp_down; + int s3_sleep_for; + int s3_ramp_up; + int s3_ramp_down; + + /* Oscillation */ + uint8_t new_s0; + uint8_t osc_min[2]; /* AC=0/1 */ + uint8_t osc_max[2]; /* AC=0/1 */ + uint8_t w_ofs[2]; /* AC=0/1 */ + + /* Brightness limits based on the backlight and AC. */ + uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */ + uint8_t bright_bl_on_min[2]; /* AC=0/1 */ + uint8_t bright_bl_on_max[2]; /* AC=0/1 */ + + /* Battery level thresholds */ + uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; + + /* Map [AC][battery_level] to color index */ + uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ + uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + + /* Color palette */ + struct rgb_s color[8]; /* 0-3 are Google colors */ +} __packed; + +struct ec_params_lightbar { + uint8_t cmd; /* Command (see enum lightbar_command) */ + union { + struct { + /* no args */ + } dump, off, on, init, get_seq, get_params; + + struct num { + uint8_t num; + } brightness, seq, demo; + + struct reg { + uint8_t ctrl, reg, value; + } reg; + + struct rgb { + uint8_t led, red, green, blue; + } rgb; + + struct lightbar_params set_params; + }; +} __packed; + +struct ec_response_lightbar { + union { + struct dump { + struct { + uint8_t reg; + uint8_t ic0; + uint8_t ic1; + } vals[23]; + } dump; + + struct get_seq { + uint8_t num; + } get_seq; + + struct lightbar_params get_params; + + struct { + /* no return params */ + } off, on, init, brightness, seq, reg, rgb, demo, set_params; + }; +} __packed; + +/* Lightbar commands */ +enum lightbar_command { + LIGHTBAR_CMD_DUMP = 0, + LIGHTBAR_CMD_OFF = 1, + LIGHTBAR_CMD_ON = 2, + LIGHTBAR_CMD_INIT = 3, + LIGHTBAR_CMD_BRIGHTNESS = 4, + LIGHTBAR_CMD_SEQ = 5, + LIGHTBAR_CMD_REG = 6, + LIGHTBAR_CMD_RGB = 7, + LIGHTBAR_CMD_GET_SEQ = 8, + LIGHTBAR_CMD_DEMO = 9, + LIGHTBAR_CMD_GET_PARAMS = 10, + LIGHTBAR_CMD_SET_PARAMS = 11, + LIGHTBAR_NUM_CMDS +}; + +/*****************************************************************************/ +/* Verified boot commands */ + +/* + * Note: command code 0x29 version 0 was VBOOT_CMD in Link EVT; it may be + * reused for other purposes with version > 0. + */ + +/* Verified boot hash command */ +#define EC_CMD_VBOOT_HASH 0x2A + +struct ec_params_vboot_hash { + uint8_t cmd; /* enum ec_vboot_hash_cmd */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t nonce_size; /* Nonce size; may be 0 */ + uint8_t reserved0; /* Reserved; set 0 */ + uint32_t offset; /* Offset in flash to hash */ + uint32_t size; /* Number of bytes to hash */ + uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */ +} __packed; + +struct ec_response_vboot_hash { + uint8_t status; /* enum ec_vboot_hash_status */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t digest_size; /* Size of hash digest in bytes */ + uint8_t reserved0; /* Ignore; will be 0 */ + uint32_t offset; /* Offset in flash which was hashed */ + uint32_t size; /* Number of bytes hashed */ + uint8_t hash_digest[64]; /* Hash digest data */ +} __packed; + +enum ec_vboot_hash_cmd { + EC_VBOOT_HASH_GET = 0, /* Get current hash status */ + EC_VBOOT_HASH_ABORT = 1, /* Abort calculating current hash */ + EC_VBOOT_HASH_START = 2, /* Start computing a new hash */ + EC_VBOOT_HASH_RECALC = 3, /* Synchronously compute a new hash */ +}; + +enum ec_vboot_hash_type { + EC_VBOOT_HASH_TYPE_SHA256 = 0, /* SHA-256 */ +}; + +enum ec_vboot_hash_status { + EC_VBOOT_HASH_STATUS_NONE = 0, /* No hash (not started, or aborted) */ + EC_VBOOT_HASH_STATUS_DONE = 1, /* Finished computing a hash */ + EC_VBOOT_HASH_STATUS_BUSY = 2, /* Busy computing a hash */ +}; + +/* + * Special values for offset for EC_VBOOT_HASH_START and EC_VBOOT_HASH_RECALC. + * If one of these is specified, the EC will automatically update offset and + * size to the correct values for the specified image (RO or RW). + */ +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd + +/*****************************************************************************/ +/* USB charging control commands */ + +/* Set USB port charging mode */ +#define EC_CMD_USB_CHARGE_SET_MODE 0x30 + +struct ec_params_usb_charge_set_mode { + uint8_t usb_port_id; + uint8_t mode; +} __packed; + +/*****************************************************************************/ +/* Persistent storage for host */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_PSTORE_SIZE_MAX 64 + +/* Get persistent storage info */ +#define EC_CMD_PSTORE_INFO 0x40 + +struct ec_response_pstore_info { + /* Persistent storage size, in bytes */ + uint32_t pstore_size; + /* Access size; read/write offset and size must be a multiple of this */ + uint32_t access_size; +} __packed; + +/* + * Read persistent storage + * + * Response is params.size bytes of data. + */ +#define EC_CMD_PSTORE_READ 0x41 + +struct ec_params_pstore_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write persistent storage */ +#define EC_CMD_PSTORE_WRITE 0x42 + +struct ec_params_pstore_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + uint8_t data[EC_PSTORE_SIZE_MAX]; +} __packed; + +/*****************************************************************************/ +/* Real-time clock */ + +/* RTC params and response structures */ +struct ec_params_rtc { + uint32_t time; +} __packed; + +struct ec_response_rtc { + uint32_t time; +} __packed; + +/* These use ec_response_rtc */ +#define EC_CMD_RTC_GET_VALUE 0x44 +#define EC_CMD_RTC_GET_ALARM 0x45 + +/* These all use ec_params_rtc */ +#define EC_CMD_RTC_SET_VALUE 0x46 +#define EC_CMD_RTC_SET_ALARM 0x47 + +/*****************************************************************************/ +/* Port80 log access */ + +/* Get last port80 code from previous boot */ +#define EC_CMD_PORT80_LAST_BOOT 0x48 + +struct ec_response_port80_last_boot { + uint16_t code; +} __packed; + +/*****************************************************************************/ +/* Thermal engine commands */ + +/* Set thershold value */ +#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 + +struct ec_params_thermal_set_threshold { + uint8_t sensor_type; + uint8_t threshold_id; + uint16_t value; +} __packed; + +/* Get threshold value */ +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 + +struct ec_params_thermal_get_threshold { + uint8_t sensor_type; + uint8_t threshold_id; +} __packed; + +struct ec_response_thermal_get_threshold { + uint16_t value; +} __packed; + +/* Toggle automatic fan control */ +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 + +/* Get TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x53 + +struct ec_params_tmp006_get_calibration { + uint8_t index; +} __packed; + +struct ec_response_tmp006_get_calibration { + float s0; + float b0; + float b1; + float b2; +} __packed; + +/* Set TMP006 calibration data */ +#define EC_CMD_TMP006_SET_CALIBRATION 0x54 + +struct ec_params_tmp006_set_calibration { + uint8_t index; + uint8_t reserved[3]; /* Reserved; set 0 */ + float s0; + float b0; + float b1; + float b2; +} __packed; + +/*****************************************************************************/ +/* MKBP - Matrix KeyBoard Protocol */ + +/* + * Read key state + * + * Returns raw data for keyboard cols; see ec_response_mkbp_info.cols for + * expected response size. + */ +#define EC_CMD_MKBP_STATE 0x60 + +/* Provide information about the matrix : number of rows and columns */ +#define EC_CMD_MKBP_INFO 0x61 + +struct ec_response_mkbp_info { + uint32_t rows; + uint32_t cols; + uint8_t switches; +} __packed; + +/* Simulate key press */ +#define EC_CMD_MKBP_SIMULATE_KEY 0x62 + +struct ec_params_mkbp_simulate_key { + uint8_t col; + uint8_t row; + uint8_t pressed; +} __packed; + +/* Configure keyboard scanning */ +#define EC_CMD_MKBP_SET_CONFIG 0x64 +#define EC_CMD_MKBP_GET_CONFIG 0x65 + +/* flags */ +enum mkbp_config_flags { + EC_MKBP_FLAGS_ENABLE = 1, /* Enable keyboard scanning */ +}; + +enum mkbp_config_valid { + EC_MKBP_VALID_SCAN_PERIOD = 1 << 0, + EC_MKBP_VALID_POLL_TIMEOUT = 1 << 1, + EC_MKBP_VALID_MIN_POST_SCAN_DELAY = 1 << 3, + EC_MKBP_VALID_OUTPUT_SETTLE = 1 << 4, + EC_MKBP_VALID_DEBOUNCE_DOWN = 1 << 5, + EC_MKBP_VALID_DEBOUNCE_UP = 1 << 6, + EC_MKBP_VALID_FIFO_MAX_DEPTH = 1 << 7, +}; + +/* Configuration for our key scanning algorithm */ +struct ec_mkbp_config { + uint32_t valid_mask; /* valid fields */ + uint8_t flags; /* some flags (enum mkbp_config_flags) */ + uint8_t valid_flags; /* which flags are valid */ + uint16_t scan_period_us; /* period between start of scans */ + /* revert to interrupt mode after no activity for this long */ + uint32_t poll_timeout_us; + /* + * minimum post-scan relax time. Once we finish a scan we check + * the time until we are due to start the next one. If this time is + * shorter this field, we use this instead. + */ + uint16_t min_post_scan_delay_us; + /* delay between setting up output and waiting for it to settle */ + uint16_t output_settle_us; + uint16_t debounce_down_us; /* time for debounce on key down */ + uint16_t debounce_up_us; /* time for debounce on key up */ + /* maximum depth to allow for fifo (0 = no keyscan output) */ + uint8_t fifo_max_depth; +} __packed; + +struct ec_params_mkbp_set_config { + struct ec_mkbp_config config; +} __packed; + +struct ec_response_mkbp_get_config { + struct ec_mkbp_config config; +} __packed; + +/* Run the key scan emulation */ +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 + +enum ec_keyscan_seq_cmd { + EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ + EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */ + EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */ + EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */ + EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */ +}; + +enum ec_collect_flags { + /* + * Indicates this scan was processed by the EC. Due to timing, some + * scans may be skipped. + */ + EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0, +}; + +struct ec_collect_item { + uint8_t flags; /* some flags (enum ec_collect_flags) */ +}; + +struct ec_params_keyscan_seq_ctrl { + uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ + union { + struct { + uint8_t active; /* still active */ + uint8_t num_items; /* number of items */ + /* Current item being presented */ + uint8_t cur_item; + } status; + struct { + /* + * Absolute time for this scan, measured from the + * start of the sequence. + */ + uint32_t time_us; + uint8_t scan[0]; /* keyscan data */ + } add; + struct { + uint8_t start_item; /* First item to return */ + uint8_t num_items; /* Number of items to return */ + } collect; + }; +} __packed; + +struct ec_result_keyscan_seq_ctrl { + union { + struct { + uint8_t num_items; /* Number of items */ + /* Data for each item */ + struct ec_collect_item item[0]; + } collect; + }; +} __packed; + +/*****************************************************************************/ +/* Temperature sensor commands */ + +/* Read temperature sensor info */ +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 + +struct ec_params_temp_sensor_get_info { + uint8_t id; +} __packed; + +struct ec_response_temp_sensor_get_info { + char sensor_name[32]; + uint8_t sensor_type; +} __packed; + +/*****************************************************************************/ + +/* + * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI + * commands accidentally sent to the wrong interface. See the ACPI section + * below. + */ + +/*****************************************************************************/ +/* Host event commands */ + +/* + * Host event mask params and response structures, shared by all of the host + * event commands below. + */ +struct ec_params_host_event_mask { + uint32_t mask; +} __packed; + +struct ec_response_host_event_mask { + uint32_t mask; +} __packed; + +/* These all use ec_response_host_event_mask */ +#define EC_CMD_HOST_EVENT_GET_B 0x87 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d + +/* These all use ec_params_host_event_mask */ +#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a +#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b +#define EC_CMD_HOST_EVENT_CLEAR 0x8c +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e +#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f + +/*****************************************************************************/ +/* Switch commands */ + +/* Enable/disable LCD backlight */ +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 + +struct ec_params_switch_enable_backlight { + uint8_t enabled; +} __packed; + +/* Enable/disable WLAN/Bluetooth */ +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 + +struct ec_params_switch_enable_wireless { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* GPIO commands. Only available on EC if write protect has been disabled. */ + +/* Set GPIO output value */ +#define EC_CMD_GPIO_SET 0x92 + +struct ec_params_gpio_set { + char name[32]; + uint8_t val; +} __packed; + +/* Get GPIO value */ +#define EC_CMD_GPIO_GET 0x93 + +struct ec_params_gpio_get { + char name[32]; +} __packed; +struct ec_response_gpio_get { + uint8_t val; +} __packed; + +/*****************************************************************************/ +/* I2C commands. Only available when flash write protect is unlocked. */ + +/* Read I2C bus */ +#define EC_CMD_I2C_READ 0x94 + +struct ec_params_i2c_read { + uint16_t addr; + uint8_t read_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; +struct ec_response_i2c_read { + uint16_t data; +} __packed; + +/* Write I2C bus */ +#define EC_CMD_I2C_WRITE 0x95 + +struct ec_params_i2c_write { + uint16_t data; + uint16_t addr; + uint8_t write_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; + +/*****************************************************************************/ +/* Charge state commands. Only available when flash write protect unlocked. */ + +/* Force charge state machine to stop in idle mode */ +#define EC_CMD_CHARGE_FORCE_IDLE 0x96 + +struct ec_params_force_idle { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* Console commands. Only available when flash write protect is unlocked. */ + +/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ +#define EC_CMD_CONSOLE_SNAPSHOT 0x97 + +/* + * Read next chunk of data from saved snapshot. + * + * Response is null-terminated string. Empty string, if there is no more + * remaining output. + */ +#define EC_CMD_CONSOLE_READ 0x98 + +/*****************************************************************************/ + +/* + * Cut off battery power output if the battery supports. + * + * For unsupported battery, just don't implement this command and lets EC + * return EC_RES_INVALID_COMMAND. + */ +#define EC_CMD_BATTERY_CUT_OFF 0x99 + +/*****************************************************************************/ +/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */ + +/* + * Dump charge state machine context. + * + * Response is a binary dump of charge state machine context. + */ +#define EC_CMD_CHARGE_DUMP 0xa0 + +/* + * Set maximum battery charging current. + */ +#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 + +struct ec_params_current_limit { + uint32_t limit; +} __packed; + +/*****************************************************************************/ +/* System commands */ + +/* + * TODO: this is a confusing name, since it doesn't necessarily reboot the EC. + * Rename to "set image" or something similar. + */ +#define EC_CMD_REBOOT_EC 0xd2 + +/* Command */ +enum ec_reboot_cmd { + EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */ + EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */ + EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */ + /* (command 3 was jump to RW-B) */ + EC_REBOOT_COLD = 4, /* Cold-reboot */ + EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */ + EC_REBOOT_HIBERNATE = 6 /* Hibernate EC */ +}; + +/* Flags for ec_params_reboot_ec.reboot_flags */ +#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */ + +struct ec_params_reboot_ec { + uint8_t cmd; /* enum ec_reboot_cmd */ + uint8_t flags; /* See EC_REBOOT_FLAG_* */ +} __packed; + +/* + * Get information on last EC panic. + * + * Returns variable-length platform-dependent panic information. See panic.h + * for details. + */ +#define EC_CMD_GET_PANIC_INFO 0xd3 + +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_DATA bit to set + * - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x80 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x81 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x84 + +/* Valid addresses in ACPI memory space, for read/write commands */ +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION 0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST 0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 1 + + +/*****************************************************************************/ +/* + * Special commands + * + * These do not follow the normal rules for commands. See each command for + * details. + */ + +/* + * Reboot NOW + * + * This command will work even when the EC LPC interface is busy, because the + * reboot command is processed at interrupt level. Note that when the EC + * reboots, the host will reboot too, so there is no response to this command. + * + * Use EC_CMD_REBOOT_EC to reboot the EC more politely. + */ +#define EC_CMD_REBOOT 0xd1 /* Think "die" */ + +/* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* + * This header byte on a command indicate version 0. Any header byte less + * than this means that we are talking to an old EC which doesn't support + * versioning. In that case, we assume version 0. + * + * Header bytes greater than this indicate a later version. For example, + * EC_CMD_VERSION0 + 1 means we are using version 1. + * + * The old EC interface must not use commands 0dc or higher. + */ +#define EC_CMD_VERSION0 0xdc + +#endif /* !__ACPI__ */ + +#endif /* __CROS_EC_COMMANDS_H */ -- cgit v0.10.2 From 4ab6174e8cdb007cf500e484bdf454b8d14d524a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:37 -0800 Subject: mfd: Add ChromeOS EC implementation This is the base EC implementation, which provides a high level interface to the EC for use by the rest of the kernel. The actual communcations is dealt with by a separate protocol driver which registers itself with this interface. Interrupts are passed on through a notifier. A simple message structure is used to pass messages to the protocol driver. Signed-off-by: Simon Glass Signed-off-by: Che-Liang Chiou Signed-off-by: Jonathan Kliegman Signed-off-by: Luigi Semenzato Signed-off-by: Olof Johansson Signed-off-by: Vincent Palatin Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/mfd/cros-ec.txt b/Documentation/devicetree/bindings/mfd/cros-ec.txt new file mode 100644 index 0000000..e0e59c5 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/cros-ec.txt @@ -0,0 +1,56 @@ +ChromeOS Embedded Controller + +Google's ChromeOS EC is a Cortex-M device which talks to the AP and +implements various function such as keyboard and battery charging. + +The EC can be connect through various means (I2C, SPI, LPC) and the +compatible string used depends on the inteface. Each connection method has +its own driver which connects to the top level interface-agnostic EC driver. +Other Linux driver (such as cros-ec-keyb for the matrix keyboard) connect to +the top-level driver. + +Required properties (I2C): +- compatible: "google,cros-ec-i2c" +- reg: I2C slave address + +Required properties (SPI): +- compatible: "google,cros-ec-spi" +- reg: SPI chip select + +Required properties (LPC): +- compatible: "google,cros-ec-lpc" +- reg: List of (IO address, size) pairs defining the interface uses + + +Example for I2C: + +i2c@12CA0000 { + cros-ec@1e { + reg = <0x1e>; + compatible = "google,cros-ec-i2c"; + interrupts = <14 0>; + interrupt-parent = <&wakeup_eint>; + wakeup-source; + }; + + +Example for SPI: + +spi@131b0000 { + ec@0 { + compatible = "google,cros-ec-spi"; + reg = <0x0>; + interrupts = <14 0>; + interrupt-parent = <&wakeup_eint>; + wakeup-source; + spi-max-frequency = <5000000>; + controller-data { + cs-gpio = <&gpf0 3 4 3 0>; + samsung,spi-cs; + samsung,spi-feedback-delay = <2>; + }; + }; +}; + + +Example for LPC is not supplied as it is not yet implemented. diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c346941..a8bafb5 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -21,6 +21,14 @@ config MFD_88PM860X select individual components like voltage regulators, RTC and battery-charger under the corresponding menus. +config MFD_CROS_EC + tristate "Support ChromeOS Embedded Controller" + help + If you say Y here you get support for the ChromeOS Embedded + Controller (EC) providing keyboard, battery and power services. + You also ned to enable the driver for the bus you are using. The + protocol for talking to the EC is defined by the bus driver. + config MFD_88PM800 tristate "Support Marvell 88PM800" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b90409c..967767d 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MFD_88PM800) += 88pm800.o 88pm80x.o obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o +obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c new file mode 100644 index 0000000..ac824cc --- /dev/null +++ b/drivers/mfd/cros_ec.c @@ -0,0 +1,189 @@ +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * The ChromeOS EC multi function device is used to mux all the requests + * to the EC device for its multiple features: keyboard controller, + * battery charging and regulator control, firmware update. + */ + +#include +#include +#include +#include +#include + +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, + struct cros_ec_msg *msg) +{ + uint8_t *out; + int csum, i; + + BUG_ON(msg->out_len > EC_HOST_PARAM_SIZE); + out = ec_dev->dout; + out[0] = EC_CMD_VERSION0 + msg->version; + out[1] = msg->cmd; + out[2] = msg->out_len; + csum = out[0] + out[1] + out[2]; + for (i = 0; i < msg->out_len; i++) + csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->out_buf[i]; + out[EC_MSG_TX_HEADER_BYTES + msg->out_len] = (uint8_t)(csum & 0xff); + + return EC_MSG_TX_PROTO_BYTES + msg->out_len; +} + +static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, + uint16_t cmd, void *out_buf, int out_len, + void *in_buf, int in_len) +{ + struct cros_ec_msg msg; + + msg.version = cmd >> 8; + msg.cmd = cmd & 0xff; + msg.out_buf = out_buf; + msg.out_len = out_len; + msg.in_buf = in_buf; + msg.in_len = in_len; + + return ec_dev->command_xfer(ec_dev, &msg); +} + +static int cros_ec_command_recv(struct cros_ec_device *ec_dev, + uint16_t cmd, void *buf, int buf_len) +{ + return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len); +} + +static int cros_ec_command_send(struct cros_ec_device *ec_dev, + uint16_t cmd, void *buf, int buf_len) +{ + return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0); +} + +static irqreturn_t ec_irq_thread(int irq, void *data) +{ + struct cros_ec_device *ec_dev = data; + + if (device_may_wakeup(ec_dev->dev)) + pm_wakeup_event(ec_dev->dev, 0); + + blocking_notifier_call_chain(&ec_dev->event_notifier, 1, ec_dev); + + return IRQ_HANDLED; +} + +static struct mfd_cell cros_devs[] = { + { + .name = "cros-ec-keyb", + .id = 1, + .of_compatible = "google,cros-ec-keyb", + }, +}; + +int cros_ec_register(struct cros_ec_device *ec_dev) +{ + struct device *dev = ec_dev->dev; + int err = 0; + + BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier); + + ec_dev->command_send = cros_ec_command_send; + ec_dev->command_recv = cros_ec_command_recv; + ec_dev->command_sendrecv = cros_ec_command_sendrecv; + + if (ec_dev->din_size) { + ec_dev->din = kmalloc(ec_dev->din_size, GFP_KERNEL); + if (!ec_dev->din) { + err = -ENOMEM; + goto fail_din; + } + } + if (ec_dev->dout_size) { + ec_dev->dout = kmalloc(ec_dev->dout_size, GFP_KERNEL); + if (!ec_dev->dout) { + err = -ENOMEM; + goto fail_dout; + } + } + + if (!ec_dev->irq) { + dev_dbg(dev, "no valid IRQ: %d\n", ec_dev->irq); + goto fail_irq; + } + + err = request_threaded_irq(ec_dev->irq, NULL, ec_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "chromeos-ec", ec_dev); + if (err) { + dev_err(dev, "request irq %d: error %d\n", ec_dev->irq, err); + goto fail_irq; + } + + err = mfd_add_devices(dev, 0, cros_devs, + ARRAY_SIZE(cros_devs), + NULL, ec_dev->irq, NULL); + if (err) { + dev_err(dev, "failed to add mfd devices\n"); + goto fail_mfd; + } + + dev_info(dev, "Chrome EC (%s)\n", ec_dev->name); + + return 0; + +fail_mfd: + free_irq(ec_dev->irq, ec_dev); +fail_irq: + kfree(ec_dev->dout); +fail_dout: + kfree(ec_dev->din); +fail_din: + return err; +} + +int cros_ec_remove(struct cros_ec_device *ec_dev) +{ + mfd_remove_devices(ec_dev->dev); + free_irq(ec_dev->irq, ec_dev); + kfree(ec_dev->dout); + kfree(ec_dev->din); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +int cros_ec_suspend(struct cros_ec_device *ec_dev) +{ + struct device *dev = ec_dev->dev; + + if (device_may_wakeup(dev)) + ec_dev->wake_enabled = !enable_irq_wake(ec_dev->irq); + + disable_irq(ec_dev->irq); + ec_dev->was_wake_device = ec_dev->wake_enabled; + + return 0; +} + +int cros_ec_resume(struct cros_ec_device *ec_dev) +{ + enable_irq(ec_dev->irq); + + if (ec_dev->wake_enabled) { + disable_irq_wake(ec_dev->irq); + ec_dev->wake_enabled = 0; + } + + return 0; +} +#endif diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h new file mode 100644 index 0000000..032af7f --- /dev/null +++ b/include/linux/mfd/cros_ec.h @@ -0,0 +1,170 @@ +/* + * ChromeOS EC multi-function device + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LINUX_MFD_CROS_EC_H +#define __LINUX_MFD_CROS_EC_H + +#include + +/* + * Command interface between EC and AP, for LPC, I2C and SPI interfaces. + */ +enum { + EC_MSG_TX_HEADER_BYTES = 3, + EC_MSG_TX_TRAILER_BYTES = 1, + EC_MSG_TX_PROTO_BYTES = EC_MSG_TX_HEADER_BYTES + + EC_MSG_TX_TRAILER_BYTES, + EC_MSG_RX_PROTO_BYTES = 3, + + /* Max length of messages */ + EC_MSG_BYTES = EC_HOST_PARAM_SIZE + EC_MSG_TX_PROTO_BYTES, + +}; + +/** + * struct cros_ec_msg - A message sent to the EC, and its reply + * + * @version: Command version number (often 0) + * @cmd: Command to send (EC_CMD_...) + * @out_buf: Outgoing payload (to EC) + * @outlen: Outgoing length + * @in_buf: Incoming payload (from EC) + * @in_len: Incoming length + */ +struct cros_ec_msg { + u8 version; + u8 cmd; + uint8_t *out_buf; + int out_len; + uint8_t *in_buf; + int in_len; +}; + +/** + * struct cros_ec_device - Information about a ChromeOS EC device + * + * @name: Name of this EC interface + * @priv: Private data + * @irq: Interrupt to use + * @din: input buffer (from EC) + * @dout: output buffer (to EC) + * \note + * These two buffers will always be dword-aligned and include enough + * space for up to 7 word-alignment bytes also, so we can ensure that + * the body of the message is always dword-aligned (64-bit). + * + * We use this alignment to keep ARM and x86 happy. Probably word + * alignment would be OK, there might be a small performance advantage + * to using dword. + * @din_size: size of din buffer + * @dout_size: size of dout buffer + * @command_send: send a command + * @command_recv: receive a command + * @ec_name: name of EC device (e.g. 'chromeos-ec') + * @phys_name: name of physical comms layer (e.g. 'i2c-4') + * @parent: pointer to parent device (e.g. i2c or spi device) + * @dev: Device pointer + * dev_lock: Lock to prevent concurrent access + * @wake_enabled: true if this device can wake the system from sleep + * @was_wake_device: true if this device was set to wake the system from + * sleep at the last suspend + * @event_notifier: interrupt event notifier for transport devices + */ +struct cros_ec_device { + const char *name; + void *priv; + int irq; + uint8_t *din; + uint8_t *dout; + int din_size; + int dout_size; + int (*command_send)(struct cros_ec_device *ec, + uint16_t cmd, void *out_buf, int out_len); + int (*command_recv)(struct cros_ec_device *ec, + uint16_t cmd, void *in_buf, int in_len); + int (*command_sendrecv)(struct cros_ec_device *ec, + uint16_t cmd, void *out_buf, int out_len, + void *in_buf, int in_len); + int (*command_xfer)(struct cros_ec_device *ec, + struct cros_ec_msg *msg); + + const char *ec_name; + const char *phys_name; + struct device *parent; + + /* These are --private-- fields - do not assign */ + struct device *dev; + struct mutex dev_lock; + bool wake_enabled; + bool was_wake_device; + struct blocking_notifier_head event_notifier; +}; + +/** + * cros_ec_suspend - Handle a suspend operation for the ChromeOS EC device + * + * This can be called by drivers to handle a suspend event. + * + * ec_dev: Device to suspend + * @return 0 if ok, -ve on error + */ +int cros_ec_suspend(struct cros_ec_device *ec_dev); + +/** + * cros_ec_resume - Handle a resume operation for the ChromeOS EC device + * + * This can be called by drivers to handle a resume event. + * + * @ec_dev: Device to resume + * @return 0 if ok, -ve on error + */ +int cros_ec_resume(struct cros_ec_device *ec_dev); + +/** + * cros_ec_prepare_tx - Prepare an outgoing message in the output buffer + * + * This is intended to be used by all ChromeOS EC drivers, but at present + * only SPI uses it. Once LPC uses the same protocol it can start using it. + * I2C could use it now, with a refactor of the existing code. + * + * @ec_dev: Device to register + * @msg: Message to write + */ +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, + struct cros_ec_msg *msg); + +/** + * cros_ec_remove - Remove a ChromeOS EC + * + * Call this to deregister a ChromeOS EC. After this you should call + * cros_ec_free(). + * + * @ec_dev: Device to register + * @return 0 if ok, -ve on error + */ +int cros_ec_remove(struct cros_ec_device *ec_dev); + +/** + * cros_ec_register - Register a new ChromeOS EC, using the provided info + * + * Before calling this, allocate a pointer to a new device and then fill + * in all the fields up to the --private-- marker. + * + * @ec_dev: Device to register + * @return 0 if ok, -ve on error + */ +int cros_ec_register(struct cros_ec_device *ec_dev); + +#endif /* __LINUX_MFD_CROS_EC_H */ -- cgit v0.10.2 From 89969009485fa9e62814afaa438c12c45d7d2def Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:38 -0800 Subject: mfd: Add ChromeOS EC I2C driver This uses an I2C bus to talk to the ChromeOS EC. The protocol is defined by the EC and is fairly simple, with a length byte, checksum, command byte and version byte (to permit easy creation of new commands). Signed-off-by: Simon Glass Signed-off-by: Che-Liang Chiou Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index a8bafb5..4e54b5b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -29,6 +29,16 @@ config MFD_CROS_EC You also ned to enable the driver for the bus you are using. The protocol for talking to the EC is defined by the bus driver. +config MFD_CROS_EC_I2C + tristate "ChromeOS Embedded Controller (I2C)" + depends on MFD_CROS_EC && I2C + + help + If you say Y here, you get support for talking to the ChromeOS + EC through an I2C bus. This uses a simple byte-level protocol with + a checksum. Failing accesses will be retried three times to + improve reliability. + config MFD_88PM800 tristate "Support Marvell 88PM800" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 967767d..895257b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MFD_88PM805) += 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o +obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o diff --git a/drivers/mfd/cros_ec_i2c.c b/drivers/mfd/cros_ec_i2c.c new file mode 100644 index 0000000..1230446 --- /dev/null +++ b/drivers/mfd/cros_ec_i2c.c @@ -0,0 +1,201 @@ +/* + * ChromeOS EC multi-function device (I2C) + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static inline struct cros_ec_device *to_ec_dev(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_get_clientdata(client); +} + +static int cros_ec_command_xfer(struct cros_ec_device *ec_dev, + struct cros_ec_msg *msg) +{ + struct i2c_client *client = ec_dev->priv; + int ret = -ENOMEM; + int i; + int packet_len; + u8 *out_buf = NULL; + u8 *in_buf = NULL; + u8 sum; + struct i2c_msg i2c_msg[2]; + + i2c_msg[0].addr = client->addr; + i2c_msg[0].flags = 0; + i2c_msg[1].addr = client->addr; + i2c_msg[1].flags = I2C_M_RD; + + /* + * allocate larger packet (one byte for checksum, one byte for + * length, and one for result code) + */ + packet_len = msg->in_len + 3; + in_buf = kzalloc(packet_len, GFP_KERNEL); + if (!in_buf) + goto done; + i2c_msg[1].len = packet_len; + i2c_msg[1].buf = (char *)in_buf; + + /* + * allocate larger packet (one byte for checksum, one for + * command code, one for length, and one for command version) + */ + packet_len = msg->out_len + 4; + out_buf = kzalloc(packet_len, GFP_KERNEL); + if (!out_buf) + goto done; + i2c_msg[0].len = packet_len; + i2c_msg[0].buf = (char *)out_buf; + + out_buf[0] = EC_CMD_VERSION0 + msg->version; + out_buf[1] = msg->cmd; + out_buf[2] = msg->out_len; + + /* copy message payload and compute checksum */ + sum = out_buf[0] + out_buf[1] + out_buf[2]; + for (i = 0; i < msg->out_len; i++) { + out_buf[3 + i] = msg->out_buf[i]; + sum += out_buf[3 + i]; + } + out_buf[3 + msg->out_len] = sum; + + /* send command to EC and read answer */ + ret = i2c_transfer(client->adapter, i2c_msg, 2); + if (ret < 0) { + dev_err(ec_dev->dev, "i2c transfer failed: %d\n", ret); + goto done; + } else if (ret != 2) { + dev_err(ec_dev->dev, "failed to get response: %d\n", ret); + ret = -EIO; + goto done; + } + + /* check response error code */ + if (i2c_msg[1].buf[0]) { + dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", + msg->cmd, i2c_msg[1].buf[0]); + ret = -EINVAL; + goto done; + } + + /* copy response packet payload and compute checksum */ + sum = in_buf[0] + in_buf[1]; + for (i = 0; i < msg->in_len; i++) { + msg->in_buf[i] = in_buf[2 + i]; + sum += in_buf[2 + i]; + } + dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n", + i2c_msg[1].len, in_buf, sum); + if (sum != in_buf[2 + msg->in_len]) { + dev_err(ec_dev->dev, "bad packet checksum\n"); + ret = -EBADMSG; + goto done; + } + + ret = 0; + done: + kfree(in_buf); + kfree(out_buf); + return ret; +} + +static int cros_ec_probe_i2c(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct device *dev = &client->dev; + struct cros_ec_device *ec_dev = NULL; + int err; + + ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); + if (!ec_dev) + return -ENOMEM; + + i2c_set_clientdata(client, ec_dev); + ec_dev->name = "I2C"; + ec_dev->dev = dev; + ec_dev->priv = client; + ec_dev->irq = client->irq; + ec_dev->command_xfer = cros_ec_command_xfer; + ec_dev->ec_name = client->name; + ec_dev->phys_name = client->adapter->name; + ec_dev->parent = &client->dev; + + err = cros_ec_register(ec_dev); + if (err) { + dev_err(dev, "cannot register EC\n"); + return err; + } + + return 0; +} + +static int cros_ec_remove_i2c(struct i2c_client *client) +{ + struct cros_ec_device *ec_dev = i2c_get_clientdata(client); + + cros_ec_remove(ec_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_i2c_suspend(struct device *dev) +{ + struct cros_ec_device *ec_dev = to_ec_dev(dev); + + return cros_ec_suspend(ec_dev); +} + +static int cros_ec_i2c_resume(struct device *dev) +{ + struct cros_ec_device *ec_dev = to_ec_dev(dev); + + return cros_ec_resume(ec_dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_i2c_pm_ops, cros_ec_i2c_suspend, + cros_ec_i2c_resume); + +static const struct i2c_device_id cros_ec_i2c_id[] = { + { "cros-ec-i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cros_ec_i2c_id); + +static struct i2c_driver cros_ec_driver = { + .driver = { + .name = "cros-ec-i2c", + .owner = THIS_MODULE, + .pm = &cros_ec_i2c_pm_ops, + }, + .probe = cros_ec_probe_i2c, + .remove = cros_ec_remove_i2c, + .id_table = cros_ec_i2c_id, +}; + +module_i2c_driver(cros_ec_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC multi function device"); -- cgit v0.10.2 From a17d94f0b6e1581391378dcb11c18ddebf6dcf8e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:39 -0800 Subject: mfd: Add ChromeOS EC SPI driver This uses a SPI bus to talk to the ChromeOS EC. The protocol is defined by the EC and is fairly simple, with a length byte, checksum, command byte and version byte (to permit easy creation of new commands). Signed-off-by: Simon Glass Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 4e54b5b..94b4547 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -39,6 +39,16 @@ config MFD_CROS_EC_I2C a checksum. Failing accesses will be retried three times to improve reliability. +config MFD_CROS_EC_SPI + tristate "ChromeOS Embedded Controller (SPI)" + depends on MFD_CROS_EC && SPI + + ---help--- + If you say Y here, you get support for talking to the ChromeOS EC + through a SPI bus, using a byte-level protocol. Since the EC's + response time cannot be guaranteed, we support ignoring + 'pre-amble' bytes before the response actually starts. + config MFD_88PM800 tristate "Support Marvell 88PM800" depends on I2C=y && GENERIC_HARDIRQS diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 895257b..f62a583 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o +obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c new file mode 100644 index 0000000..da2d1b4 --- /dev/null +++ b/drivers/mfd/cros_ec_spi.c @@ -0,0 +1,375 @@ +/* + * ChromeOS EC multi-function device (SPI) + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* The header byte, which follows the preamble */ +#define EC_MSG_HEADER 0xec + +/* + * Number of EC preamble bytes we read at a time. Since it takes + * about 400-500us for the EC to respond there is not a lot of + * point in tuning this. If the EC could respond faster then + * we could increase this so that might expect the preamble and + * message to occur in a single transaction. However, the maximum + * SPI transfer size is 256 bytes, so at 5MHz we need a response + * time of perhaps <320us (200 bytes / 1600 bits). + */ +#define EC_MSG_PREAMBLE_COUNT 32 + +/* + * We must get a response from the EC in 5ms. This is a very long + * time, but the flash write command can take 2-3ms. The EC command + * processing is currently not very fast (about 500us). We could + * look at speeding this up and making the flash write command a + * 'slow' command, requiring a GET_STATUS wait loop, like flash + * erase. + */ +#define EC_MSG_DEADLINE_MS 5 + +/* + * Time between raising the SPI chip select (for the end of a + * transaction) and dropping it again (for the next transaction). + * If we go too fast, the EC will miss the transaction. It seems + * that 50us is enough with the 16MHz STM32 EC. + */ +#define EC_SPI_RECOVERY_TIME_NS (50 * 1000) + +/** + * struct cros_ec_spi - information about a SPI-connected EC + * + * @spi: SPI device we are connected to + * @last_transfer_ns: time that we last finished a transfer, or 0 if there + * if no record + */ +struct cros_ec_spi { + struct spi_device *spi; + s64 last_transfer_ns; +}; + +static void debug_packet(struct device *dev, const char *name, u8 *ptr, + int len) +{ +#ifdef DEBUG + int i; + + dev_dbg(dev, "%s: ", name); + for (i = 0; i < len; i++) + dev_cont(dev, " %02x", ptr[i]); +#endif +} + +/** + * cros_ec_spi_receive_response - Receive a response from the EC. + * + * This function has two phases: reading the preamble bytes (since if we read + * data from the EC before it is ready to send, we just get preamble) and + * reading the actual message. + * + * The received data is placed into ec_dev->din. + * + * @ec_dev: ChromeOS EC device + * @need_len: Number of message bytes we need to read + */ +static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, + int need_len) +{ + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct spi_transfer trans; + struct spi_message msg; + u8 *ptr, *end; + int ret; + unsigned long deadline; + int todo; + + /* Receive data until we see the header byte */ + deadline = jiffies + msecs_to_jiffies(EC_MSG_DEADLINE_MS); + do { + memset(&trans, '\0', sizeof(trans)); + trans.cs_change = 1; + trans.rx_buf = ptr = ec_dev->din; + trans.len = EC_MSG_PREAMBLE_COUNT; + + spi_message_init(&msg); + spi_message_add_tail(&trans, &msg); + ret = spi_sync(ec_spi->spi, &msg); + if (ret < 0) { + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + return ret; + } + + for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { + if (*ptr == EC_MSG_HEADER) { + dev_dbg(ec_dev->dev, "msg found at %d\n", + ptr - ec_dev->din); + break; + } + } + + if (time_after(jiffies, deadline)) { + dev_warn(ec_dev->dev, "EC failed to respond in time\n"); + return -ETIMEDOUT; + } + } while (ptr == end); + + /* + * ptr now points to the header byte. Copy any valid data to the + * start of our buffer + */ + todo = end - ++ptr; + BUG_ON(todo < 0 || todo > ec_dev->din_size); + todo = min(todo, need_len); + memmove(ec_dev->din, ptr, todo); + ptr = ec_dev->din + todo; + dev_dbg(ec_dev->dev, "need %d, got %d bytes from preamble\n", + need_len, todo); + need_len -= todo; + + /* Receive data until we have it all */ + while (need_len > 0) { + /* + * We can't support transfers larger than the SPI FIFO size + * unless we have DMA. We don't have DMA on the ISP SPI ports + * for Exynos. We need a way of asking SPI driver for + * maximum-supported transfer size. + */ + todo = min(need_len, 256); + dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%d\n", + todo, need_len, ptr - ec_dev->din); + + memset(&trans, '\0', sizeof(trans)); + trans.cs_change = 1; + trans.rx_buf = ptr; + trans.len = todo; + spi_message_init(&msg); + spi_message_add_tail(&trans, &msg); + + /* send command to EC and read answer */ + BUG_ON((u8 *)trans.rx_buf - ec_dev->din + todo > + ec_dev->din_size); + ret = spi_sync(ec_spi->spi, &msg); + if (ret < 0) { + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + return ret; + } + + debug_packet(ec_dev->dev, "interim", ptr, todo); + ptr += todo; + need_len -= todo; + } + + dev_dbg(ec_dev->dev, "loop done, ptr=%d\n", ptr - ec_dev->din); + + return 0; +} + +/** + * cros_ec_command_spi_xfer - Transfer a message over SPI and receive the reply + * + * @ec_dev: ChromeOS EC device + * @ec_msg: Message to transfer + */ +static int cros_ec_command_spi_xfer(struct cros_ec_device *ec_dev, + struct cros_ec_msg *ec_msg) +{ + struct cros_ec_spi *ec_spi = ec_dev->priv; + struct spi_transfer trans; + struct spi_message msg; + int i, len; + u8 *ptr; + int sum; + int ret = 0, final_ret; + struct timespec ts; + + len = cros_ec_prepare_tx(ec_dev, ec_msg); + dev_dbg(ec_dev->dev, "prepared, len=%d\n", len); + + /* If it's too soon to do another transaction, wait */ + if (ec_spi->last_transfer_ns) { + struct timespec ts; + unsigned long delay; /* The delay completed so far */ + + ktime_get_ts(&ts); + delay = timespec_to_ns(&ts) - ec_spi->last_transfer_ns; + if (delay < EC_SPI_RECOVERY_TIME_NS) + ndelay(delay); + } + + /* Transmit phase - send our message */ + debug_packet(ec_dev->dev, "out", ec_dev->dout, len); + memset(&trans, '\0', sizeof(trans)); + trans.tx_buf = ec_dev->dout; + trans.len = len; + trans.cs_change = 1; + spi_message_init(&msg); + spi_message_add_tail(&trans, &msg); + ret = spi_sync(ec_spi->spi, &msg); + + /* Get the response */ + if (!ret) { + ret = cros_ec_spi_receive_response(ec_dev, + ec_msg->in_len + EC_MSG_TX_PROTO_BYTES); + } else { + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + } + + /* turn off CS */ + spi_message_init(&msg); + final_ret = spi_sync(ec_spi->spi, &msg); + ktime_get_ts(&ts); + ec_spi->last_transfer_ns = timespec_to_ns(&ts); + if (!ret) + ret = final_ret; + if (ret < 0) { + dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); + return ret; + } + + /* check response error code */ + ptr = ec_dev->din; + if (ptr[0]) { + dev_warn(ec_dev->dev, "command 0x%02x returned an error %d\n", + ec_msg->cmd, ptr[0]); + debug_packet(ec_dev->dev, "in_err", ptr, len); + return -EINVAL; + } + len = ptr[1]; + sum = ptr[0] + ptr[1]; + if (len > ec_msg->in_len) { + dev_err(ec_dev->dev, "packet too long (%d bytes, expected %d)", + len, ec_msg->in_len); + return -ENOSPC; + } + + /* copy response packet payload and compute checksum */ + for (i = 0; i < len; i++) { + sum += ptr[i + 2]; + if (ec_msg->in_len) + ec_msg->in_buf[i] = ptr[i + 2]; + } + sum &= 0xff; + + debug_packet(ec_dev->dev, "in", ptr, len + 3); + + if (sum != ptr[len + 2]) { + dev_err(ec_dev->dev, + "bad packet checksum, expected %02x, got %02x\n", + sum, ptr[len + 2]); + return -EBADMSG; + } + + return 0; +} + +static int cros_ec_probe_spi(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct cros_ec_device *ec_dev; + struct cros_ec_spi *ec_spi; + int err; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + ec_spi = devm_kzalloc(dev, sizeof(*ec_spi), GFP_KERNEL); + if (ec_spi == NULL) + return -ENOMEM; + ec_spi->spi = spi; + ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); + if (!ec_dev) + return -ENOMEM; + + spi_set_drvdata(spi, ec_dev); + ec_dev->name = "SPI"; + ec_dev->dev = dev; + ec_dev->priv = ec_spi; + ec_dev->irq = spi->irq; + ec_dev->command_xfer = cros_ec_command_spi_xfer; + ec_dev->ec_name = ec_spi->spi->modalias; + ec_dev->phys_name = dev_name(&ec_spi->spi->dev); + ec_dev->parent = &ec_spi->spi->dev; + ec_dev->din_size = EC_MSG_BYTES + EC_MSG_PREAMBLE_COUNT; + ec_dev->dout_size = EC_MSG_BYTES; + + err = cros_ec_register(ec_dev); + if (err) { + dev_err(dev, "cannot register EC\n"); + return err; + } + + return 0; +} + +static int cros_ec_remove_spi(struct spi_device *spi) +{ + struct cros_ec_device *ec_dev; + + ec_dev = spi_get_drvdata(spi); + cros_ec_remove(ec_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_spi_suspend(struct device *dev) +{ + struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + + return cros_ec_suspend(ec_dev); +} + +static int cros_ec_spi_resume(struct device *dev) +{ + struct cros_ec_device *ec_dev = dev_get_drvdata(dev); + + return cros_ec_resume(ec_dev); +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_spi_pm_ops, cros_ec_spi_suspend, + cros_ec_spi_resume); + +static const struct spi_device_id cros_ec_spi_id[] = { + { "cros-ec-spi", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, cros_ec_spi_id); + +static struct spi_driver cros_ec_driver_spi = { + .driver = { + .name = "cros-ec-spi", + .owner = THIS_MODULE, + .pm = &cros_ec_spi_pm_ops, + }, + .probe = cros_ec_probe_spi, + .remove = cros_ec_remove_spi, + .id_table = cros_ec_spi_id, +}; + +module_spi_driver(cros_ec_driver_spi); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC multi function device (SPI)"); -- cgit v0.10.2 From 43840415339f1600f281211cfb5400fab696536e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:40 -0800 Subject: input: matrix-keymap: Add function to read the new DT binding We now have a binding which adds two parameters to the matrix keypad DT node. This is separate from the GPIO-driven matrix keypad binding, and unfortunately incompatible, since that uses row-gpios/col-gpios for the row and column counts. So the easiest option here is to provide a function for non-GPIO drivers to use to decode the binding. Note: We could in fact create an entirely separate structure to hold these two fields, but it does not seem worth it, yet. If we have more parameters then we can add this, and then refactor each driver to hold such a structure. Signed-off-by: Simon Glass Acked-by: Dmitry Torokhov Tested-by: Sourav Poddar (v2) Signed-off-by: Samuel Ortiz diff --git a/drivers/input/keyboard/lpc32xx-keys.c b/drivers/input/keyboard/lpc32xx-keys.c index 1b8add6..4218143 100644 --- a/drivers/input/keyboard/lpc32xx-keys.c +++ b/drivers/input/keyboard/lpc32xx-keys.c @@ -144,12 +144,13 @@ static int lpc32xx_parse_dt(struct device *dev, { struct device_node *np = dev->of_node; u32 rows = 0, columns = 0; + int err; - of_property_read_u32(np, "keypad,num-rows", &rows); - of_property_read_u32(np, "keypad,num-columns", &columns); - if (!rows || rows != columns) { - dev_err(dev, - "rows and columns must be specified and be equal!\n"); + err = matrix_keypad_parse_of_params(dev, &rows, &columns); + if (err) + return err; + if (rows != columns) { + dev_err(dev, "rows and columns must be equal!\n"); return -EINVAL; } diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index e25b022..1b28909 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -215,18 +215,12 @@ static int omap4_keypad_parse_dt(struct device *dev, struct omap4_keypad *keypad_data) { struct device_node *np = dev->of_node; + int err; - if (!np) { - dev_err(dev, "missing DT data"); - return -EINVAL; - } - - of_property_read_u32(np, "keypad,num-rows", &keypad_data->rows); - of_property_read_u32(np, "keypad,num-columns", &keypad_data->cols); - if (!keypad_data->rows || !keypad_data->cols) { - dev_err(dev, "number of keypad rows/columns not specified\n"); - return -EINVAL; - } + err = matrix_keypad_parse_of_params(dev, &keypad_data->rows, + &keypad_data->cols); + if (err) + return err; if (of_get_property(np, "linux,input-no-autorepeat", NULL)) keypad_data->no_autorepeat = true; diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index a34cc67..55c1530 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -288,8 +288,11 @@ static int tca8418_keypad_probe(struct i2c_client *client, irq_is_gpio = pdata->irq_is_gpio; } else { struct device_node *np = dev->of_node; - of_property_read_u32(np, "keypad,num-rows", &rows); - of_property_read_u32(np, "keypad,num-columns", &cols); + int err; + + err = matrix_keypad_parse_of_params(dev, &rows, &cols); + if (err) + return err; rep = of_property_read_bool(np, "keypad,autorepeat"); } diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index 3ae496e..619b382 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -50,6 +50,25 @@ static bool matrix_keypad_map_key(struct input_dev *input_dev, } #ifdef CONFIG_OF +int matrix_keypad_parse_of_params(struct device *dev, + unsigned int *rows, unsigned int *cols) +{ + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "missing DT data"); + return -EINVAL; + } + of_property_read_u32(np, "keypad,num-rows", rows); + of_property_read_u32(np, "keypad,num-columns", cols); + if (!*rows || !*cols) { + dev_err(dev, "number of keypad rows/columns not specified\n"); + return -EINVAL; + } + + return 0; +} + static int matrix_keypad_parse_of_keymap(const char *propname, unsigned int rows, unsigned int cols, struct input_dev *input_dev) diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index 5f3aa6b..27e06ac 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -81,4 +81,23 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, unsigned short *keymap, struct input_dev *input_dev); +#ifdef CONFIG_OF +/** + * matrix_keypad_parse_of_params() - Read parameters from matrix-keypad node + * + * @dev: Device containing of_node + * @rows: Returns number of matrix rows + * @cols: Returns number of matrix columns + * @return 0 if OK, <0 on error + */ +int matrix_keypad_parse_of_params(struct device *dev, + unsigned int *rows, unsigned int *cols); +#else +static inline int matrix_keypad_parse_of_params(struct device *dev, + unsigned int *rows, unsigned int *cols) +{ + return -ENOSYS; +} +#endif /* CONFIG_OF */ + #endif /* _MATRIX_KEYPAD_H */ -- cgit v0.10.2 From 6af6dc2d2aa654e928ed0a64c28724d1cd2c36c1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 25 Feb 2013 14:08:41 -0800 Subject: input: Add ChromeOS EC keyboard driver Use the key-matrix layer to interpret key scan information from the EC and inject input based on the FDT-supplied key map. This driver registers itself with the ChromeOS EC driver to perform communications. The matrix-keypad FDT binding is used with a small addition to control ghosting. Signed-off-by: Simon Glass Signed-off-by: Luigi Semenzato Signed-off-by: Vincent Palatin Acked-by: Dmitry Torokhov Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/input/cros-ec-keyb.txt b/Documentation/devicetree/bindings/input/cros-ec-keyb.txt new file mode 100644 index 0000000..0f6355c --- /dev/null +++ b/Documentation/devicetree/bindings/input/cros-ec-keyb.txt @@ -0,0 +1,72 @@ +ChromeOS EC Keyboard + +Google's ChromeOS EC Keyboard is a simple matrix keyboard implemented on +a separate EC (Embedded Controller) device. It provides a message for reading +key scans from the EC. These are then converted into keycodes for processing +by the kernel. + +This binding is based on matrix-keymap.txt and extends/modifies it as follows: + +Required properties: +- compatible: "google,cros-ec-keyb" + +Optional properties: +- google,needs-ghost-filter: True to enable a ghost filter for the matrix +keyboard. This is recommended if the EC does not have its own logic or +hardware for this. + + +Example: + +cros-ec-keyb { + compatible = "google,cros-ec-keyb"; + keypad,num-rows = <8>; + keypad,num-columns = <13>; + google,needs-ghost-filter; + /* + * Keymap entries take the form of 0xRRCCKKKK where + * RR=Row CC=Column KKKK=Key Code + * The values below are for a US keyboard layout and + * are taken from the Linux driver. Note that the + * 102ND key is not used for US keyboards. + */ + linux,keymap = < + /* CAPSLCK F1 B F10 */ + 0x0001003a 0x0002003b 0x00030030 0x00040044 + /* N = R_ALT ESC */ + 0x00060031 0x0008000d 0x000a0064 0x01010001 + /* F4 G F7 H */ + 0x0102003e 0x01030022 0x01040041 0x01060023 + /* ' F9 BKSPACE L_CTRL */ + 0x01080028 0x01090043 0x010b000e 0x0200001d + /* TAB F3 T F6 */ + 0x0201000f 0x0202003d 0x02030014 0x02040040 + /* ] Y 102ND [ */ + 0x0205001b 0x02060015 0x02070056 0x0208001a + /* F8 GRAVE F2 5 */ + 0x02090042 0x03010029 0x0302003c 0x03030006 + /* F5 6 - \ */ + 0x0304003f 0x03060007 0x0308000c 0x030b002b + /* R_CTRL A D F */ + 0x04000061 0x0401001e 0x04020020 0x04030021 + /* S K J ; */ + 0x0404001f 0x04050025 0x04060024 0x04080027 + /* L ENTER Z C */ + 0x04090026 0x040b001c 0x0501002c 0x0502002e + /* V X , M */ + 0x0503002f 0x0504002d 0x05050033 0x05060032 + /* L_SHIFT / . SPACE */ + 0x0507002a 0x05080035 0x05090034 0x050B0039 + /* 1 3 4 2 */ + 0x06010002 0x06020004 0x06030005 0x06040003 + /* 8 7 0 9 */ + 0x06050009 0x06060008 0x0608000b 0x0609000a + /* L_ALT DOWN RIGHT Q */ + 0x060a0038 0x060b006c 0x060c006a 0x07010010 + /* E R W I */ + 0x07020012 0x07030013 0x07040011 0x07050017 + /* U R_SHIFT P O */ + 0x07060016 0x07070036 0x07080019 0x07090018 + /* UP LEFT */ + 0x070b0067 0x070c0069>; +}; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ac05006..6a195d5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -628,4 +628,16 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +config KEYBOARD_CROS_EC + tristate "ChromeOS EC keyboard" + select INPUT_MATRIXKMAP + depends on MFD_CROS_EC + help + Say Y here to enable the matrix keyboard used by ChromeOS devices + and implemented on the ChromeOS EC. You must enable one bus option + (MFD_CROS_EC_I2C or MFD_CROS_EC_SPI) to use this. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_keyb. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 49b1645..0c43e8c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c new file mode 100644 index 0000000..49557f2 --- /dev/null +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -0,0 +1,334 @@ +/* + * ChromeOS EC keyboard driver + * + * Copyright (C) 2012 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This driver uses the Chrome OS EC byte-level message-based protocol for + * communicating the keyboard state (which keys are pressed) from a keyboard EC + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, + * but everything else (including deghosting) is done here. The main + * motivation for this is to keep the EC firmware as simple as possible, since + * it cannot be easily upgraded and EC flash/IRAM space is relatively + * expensive. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * @rows: Number of rows in the keypad + * @cols: Number of columns in the keypad + * @row_shift: log2 or number of rows, rounded up + * @keymap_data: Matrix keymap data used to convert to keyscan values + * @ghost_filter: true to enable the matrix key-ghosting filter + * @dev: Device pointer + * @idev: Input device + * @ec: Top level ChromeOS device to use to talk to EC + * @event_notifier: interrupt event notifier for transport devices + */ +struct cros_ec_keyb { + unsigned int rows; + unsigned int cols; + int row_shift; + const struct matrix_keymap_data *keymap_data; + bool ghost_filter; + + struct device *dev; + struct input_dev *idev; + struct cros_ec_device *ec; + struct notifier_block notifier; +}; + + +static bool cros_ec_keyb_row_has_ghosting(struct cros_ec_keyb *ckdev, + uint8_t *buf, int row) +{ + int pressed_in_row = 0; + int row_has_teeth = 0; + int col, mask; + + mask = 1 << row; + for (col = 0; col < ckdev->cols; col++) { + if (buf[col] & mask) { + pressed_in_row++; + row_has_teeth |= buf[col] & ~mask; + if (pressed_in_row > 1 && row_has_teeth) { + /* ghosting */ + dev_dbg(ckdev->dev, + "ghost found at: r%d c%d, pressed %d, teeth 0x%x\n", + row, col, pressed_in_row, + row_has_teeth); + return true; + } + } + } + + return false; +} + +/* + * Returns true when there is at least one combination of pressed keys that + * results in ghosting. + */ +static bool cros_ec_keyb_has_ghosting(struct cros_ec_keyb *ckdev, uint8_t *buf) +{ + int row; + + /* + * Ghosting happens if for any pressed key X there are other keys + * pressed both in the same row and column of X as, for instance, + * in the following diagram: + * + * . . Y . g . + * . . . . . . + * . . . . . . + * . . X . Z . + * + * In this case only X, Y, and Z are pressed, but g appears to be + * pressed too (see Wikipedia). + * + * We can detect ghosting in a single pass (*) over the keyboard state + * by maintaining two arrays. pressed_in_row counts how many pressed + * keys we have found in a row. row_has_teeth is true if any of the + * pressed keys for this row has other pressed keys in its column. If + * at any point of the scan we find that a row has multiple pressed + * keys, and at least one of them is at the intersection with a column + * with multiple pressed keys, we're sure there is ghosting. + * Conversely, if there is ghosting, we will detect such situation for + * at least one key during the pass. + * + * (*) This looks linear in the number of keys, but it's not. We can + * cheat because the number of rows is small. + */ + for (row = 0; row < ckdev->rows; row++) + if (cros_ec_keyb_row_has_ghosting(ckdev, buf, row)) + return true; + + return false; +} + +/* + * Compares the new keyboard state to the old one and produces key + * press/release events accordingly. The keyboard state is 13 bytes (one byte + * per column) + */ +static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, + uint8_t *kb_state, int len) +{ + struct input_dev *idev = ckdev->idev; + int col, row; + int new_state; + int num_cols; + + num_cols = len; + + if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) { + /* + * Simple-minded solution: ignore this state. The obvious + * improvement is to only ignore changes to keys involved in + * the ghosting, but process the other changes. + */ + dev_dbg(ckdev->dev, "ghosting found\n"); + return; + } + + for (col = 0; col < ckdev->cols; col++) { + for (row = 0; row < ckdev->rows; row++) { + int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + const unsigned short *keycodes = idev->keycode; + int code; + + code = keycodes[pos]; + new_state = kb_state[col] & (1 << row); + if (!!new_state != test_bit(code, idev->key)) { + dev_dbg(ckdev->dev, + "changed: [r%d c%d]: byte %02x\n", + row, col, new_state); + + input_report_key(idev, code, new_state); + } + } + } + input_sync(ckdev->idev); +} + +static int cros_ec_keyb_open(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + + return blocking_notifier_chain_register(&ckdev->ec->event_notifier, + &ckdev->notifier); +} + +static void cros_ec_keyb_close(struct input_dev *dev) +{ + struct cros_ec_keyb *ckdev = input_get_drvdata(dev); + + blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, + &ckdev->notifier); +} + +static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) +{ + return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE, + kb_state, ckdev->cols); +} + +static int cros_ec_keyb_work(struct notifier_block *nb, + unsigned long state, void *_notify) +{ + int ret; + struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb, + notifier); + uint8_t kb_state[ckdev->cols]; + + ret = cros_ec_keyb_get_state(ckdev, kb_state); + if (ret >= 0) + cros_ec_keyb_process(ckdev, kb_state, ret); + + return NOTIFY_DONE; +} + +/* Clear any keys in the buffer */ +static void cros_ec_keyb_clear_keyboard(struct cros_ec_keyb *ckdev) +{ + uint8_t old_state[ckdev->cols]; + uint8_t new_state[ckdev->cols]; + unsigned long duration; + int i, ret; + + /* + * Keep reading until we see that the scan state does not change. + * That indicates that we are done. + * + * Assume that the EC keyscan buffer is at most 32 deep. + */ + duration = jiffies; + ret = cros_ec_keyb_get_state(ckdev, new_state); + for (i = 1; !ret && i < 32; i++) { + memcpy(old_state, new_state, sizeof(old_state)); + ret = cros_ec_keyb_get_state(ckdev, new_state); + if (0 == memcmp(old_state, new_state, sizeof(old_state))) + break; + } + duration = jiffies - duration; + dev_info(ckdev->dev, "Discarded %d keyscan(s) in %dus\n", i, + jiffies_to_usecs(duration)); +} + +static int cros_ec_keyb_probe(struct platform_device *pdev) +{ + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct device *dev = ec->dev; + struct cros_ec_keyb *ckdev; + struct input_dev *idev; + struct device_node *np; + int err; + + np = pdev->dev.of_node; + if (!np) + return -ENODEV; + + ckdev = devm_kzalloc(&pdev->dev, sizeof(*ckdev), GFP_KERNEL); + if (!ckdev) + return -ENOMEM; + err = matrix_keypad_parse_of_params(&pdev->dev, &ckdev->rows, + &ckdev->cols); + if (err) + return err; + + idev = devm_input_allocate_device(&pdev->dev); + if (!idev) + return -ENOMEM; + + ckdev->ec = ec; + ckdev->notifier.notifier_call = cros_ec_keyb_work; + ckdev->dev = dev; + dev_set_drvdata(&pdev->dev, ckdev); + + idev->name = ec->ec_name; + idev->phys = ec->phys_name; + __set_bit(EV_REP, idev->evbit); + + idev->id.bustype = BUS_VIRTUAL; + idev->id.version = 1; + idev->id.product = 0; + idev->dev.parent = &pdev->dev; + idev->open = cros_ec_keyb_open; + idev->close = cros_ec_keyb_close; + + ckdev->ghost_filter = of_property_read_bool(np, + "google,needs-ghost-filter"); + + err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, + NULL, idev); + if (err) { + dev_err(dev, "cannot build key matrix\n"); + return err; + } + + ckdev->row_shift = get_count_order(ckdev->cols); + + input_set_capability(idev, EV_MSC, MSC_SCAN); + input_set_drvdata(idev, ckdev); + ckdev->idev = idev; + err = input_register_device(ckdev->idev); + if (err) { + dev_err(dev, "cannot register input device\n"); + return err; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_keyb_resume(struct device *dev) +{ + struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + + /* + * When the EC is not a wake source, then it could not have caused the + * resume, so we clear the EC's key scan buffer. If the EC was a + * wake source (e.g. the lid is open and the user might press a key to + * wake) then the key scan buffer should be preserved. + */ + if (ckdev->ec->was_wake_device) + cros_ec_keyb_clear_keyboard(ckdev); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); + +static struct platform_driver cros_ec_keyb_driver = { + .probe = cros_ec_keyb_probe, + .driver = { + .name = "cros-ec-keyb", + .pm = &cros_ec_keyb_pm_ops, + }, +}; + +module_platform_driver(cros_ec_keyb_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS EC keyboard driver"); +MODULE_ALIAS("platform:cros-ec-keyb"); -- cgit v0.10.2 From 5ebeaff5c5be9f21680b91aac77ee155d935f888 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 20 Mar 2013 09:46:15 +0100 Subject: mfd: cros_ec: Export needed symbols for the i2c and spi modules In order to fix those, when all cros code is modular: ERROR: "cros_ec_prepare_tx" [drivers/mfd/cros_ec_spi.ko] undefined! ERROR: "cros_ec_register" [drivers/mfd/cros_ec_spi.ko] undefined! ERROR: "cros_ec_remove" [drivers/mfd/cros_ec_spi.ko] undefined! ERROR: "cros_ec_suspend" [drivers/mfd/cros_ec_spi.ko] undefined! ERROR: "cros_ec_resume" [drivers/mfd/cros_ec_spi.ko] undefined! ERROR: "cros_ec_register" [drivers/mfd/cros_ec_i2c.ko] undefined! ERROR: "cros_ec_remove" [drivers/mfd/cros_ec_i2c.ko] undefined! ERROR: "cros_ec_suspend" [drivers/mfd/cros_ec_i2c.ko] undefined! ERROR: "cros_ec_resume" [drivers/mfd/cros_ec_i2c.ko] undefined! Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c index ac824cc..10cd14e 100644 --- a/drivers/mfd/cros_ec.c +++ b/drivers/mfd/cros_ec.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -41,6 +42,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, return EC_MSG_TX_PROTO_BYTES + msg->out_len; } +EXPORT_SYMBOL(cros_ec_prepare_tx); static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev, uint16_t cmd, void *out_buf, int out_len, @@ -150,6 +152,7 @@ fail_dout: fail_din: return err; } +EXPORT_SYMBOL(cros_ec_register); int cros_ec_remove(struct cros_ec_device *ec_dev) { @@ -160,6 +163,7 @@ int cros_ec_remove(struct cros_ec_device *ec_dev) return 0; } +EXPORT_SYMBOL(cros_ec_remove); #ifdef CONFIG_PM_SLEEP int cros_ec_suspend(struct cros_ec_device *ec_dev) @@ -174,6 +178,7 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev) return 0; } +EXPORT_SYMBOL(cros_ec_suspend); int cros_ec_resume(struct cros_ec_device *ec_dev) { @@ -186,4 +191,6 @@ int cros_ec_resume(struct cros_ec_device *ec_dev) return 0; } +EXPORT_SYMBOL(cros_ec_resume); + #endif -- cgit v0.10.2 From 5c29e47e6ac55b63c76999eb5b283a80208726c5 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Wed, 20 Mar 2013 09:49:00 +0100 Subject: mfd: cros_ec_spi: Warnings fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To silent those: CC [M] drivers/mfd/cros_ec_spi.o drivers/mfd/cros_ec_spi.c: In function ‘cros_ec_spi_receive_response’: drivers/mfd/cros_ec_spi.c:123:5: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long int’ [-Wformat] drivers/mfd/cros_ec_spi.c:157:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 6 has type ‘long int’ [-Wformat] drivers/mfd/cros_ec_spi.c:181:2: warning: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘long int’ [-Wformat] Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c index da2d1b4..19193cf 100644 --- a/drivers/mfd/cros_ec_spi.c +++ b/drivers/mfd/cros_ec_spi.c @@ -120,7 +120,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, for (end = ptr + EC_MSG_PREAMBLE_COUNT; ptr != end; ptr++) { if (*ptr == EC_MSG_HEADER) { - dev_dbg(ec_dev->dev, "msg found at %d\n", + dev_dbg(ec_dev->dev, "msg found at %ld\n", ptr - ec_dev->din); break; } @@ -154,7 +154,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, * maximum-supported transfer size. */ todo = min(need_len, 256); - dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%d\n", + dev_dbg(ec_dev->dev, "loop, todo=%d, need_len=%d, ptr=%ld\n", todo, need_len, ptr - ec_dev->din); memset(&trans, '\0', sizeof(trans)); @@ -178,7 +178,7 @@ static int cros_ec_spi_receive_response(struct cros_ec_device *ec_dev, need_len -= todo; } - dev_dbg(ec_dev->dev, "loop done, ptr=%d\n", ptr - ec_dev->din); + dev_dbg(ec_dev->dev, "loop done, ptr=%ld\n", ptr - ec_dev->din); return 0; } -- cgit v0.10.2 From 5a324acfce6f7f05ab2a044f7d0ece5f44e812c6 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Thu, 21 Mar 2013 12:08:33 -0700 Subject: mfd: cros_ec: Add MFD_CORE dependency CONFIG_MFD_CROS_EC requires CONFIG_MFD_CORE for a couple of functions that are declared but not defined: ERROR: "mfd_remove_devices" [drivers/mfd/cros_ec.ko] undefined! ERROR: "mfd_add_devices" [drivers/mfd/cros_ec.ko] undefined! Fix it by selecting CONFIG_MFD_CORE anytime CONFIG_MFD_CROS_EC is enabled. Signed-off-by: David Rientjes Acked-by: Simon Glass Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 94b4547..67e56dc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -23,6 +23,7 @@ config MFD_88PM860X config MFD_CROS_EC tristate "Support ChromeOS Embedded Controller" + select MFD_CORE help If you say Y here you get support for the ChromeOS Embedded Controller (EC) providing keyboard, battery and power services. -- cgit v0.10.2 From 64710af3e2c6a223c443ff85e5db4bc4bd9174e5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 22 Mar 2013 17:15:47 +0100 Subject: mfd: as3711: Add OF support Add Flat Device Tree support to the AS3711 MFD driver. This patch just allows to bind the driver to I2C devices, instantiated from the DT. DT support for AS3711 cell drivers will be added in separate drivers. Signed-off-by: Guennadi Liakhovetski Reviwed-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/mfd/as3711.txt b/Documentation/devicetree/bindings/mfd/as3711.txt new file mode 100644 index 0000000..d98cf18 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/as3711.txt @@ -0,0 +1,73 @@ +AS3711 is an I2C PMIC from Austria MicroSystems with multiple DCDC and LDO power +supplies, a battery charger and an RTC. So far only bindings for the two stepup +DCDC converters are defined. Other DCDC and LDO supplies are configured, using +standard regulator properties, they must belong to a sub-node, called +"regulators" and be called "sd1" to "sd4" and "ldo1" to "ldo8." Stepup converter +configuration should be placed in a subnode, called "backlight." + +Compulsory properties: +- compatible : must be "ams,as3711" +- reg : specifies the I2C address + +To use the SU1 converter as a backlight source the following two properties must +be provided: +- su1-dev : framebuffer phandle +- su1-max-uA : maximum current + +To use the SU2 converter as a backlight source the following two properties must +be provided: +- su2-dev : framebuffer phandle +- su1-max-uA : maximum current + +Additionally one of these properties must be provided to select the type of +feedback used: +- su2-feedback-voltage : voltage feedback is used +- su2-feedback-curr1 : CURR1 input used for current feedback +- su2-feedback-curr2 : CURR2 input used for current feedback +- su2-feedback-curr3 : CURR3 input used for current feedback +- su2-feedback-curr-auto: automatic current feedback selection + +and one of these to select the over-voltage protection pin +- su2-fbprot-lx-sd4 : LX_SD4 is used for over-voltage protection +- su2-fbprot-gpio2 : GPIO2 is used for over-voltage protection +- su2-fbprot-gpio3 : GPIO3 is used for over-voltage protection +- su2-fbprot-gpio4 : GPIO4 is used for over-voltage protection + +If "su2-feedback-curr-auto" is selected, one or more of the following properties +have to be specified: +- su2-auto-curr1 : use CURR1 input for current feedback +- su2-auto-curr2 : use CURR2 input for current feedback +- su2-auto-curr3 : use CURR3 input for current feedback + +Example: + +as3711@40 { + compatible = "ams,as3711"; + reg = <0x40>; + + regulators { + sd4 { + regulator-name = "1.215V"; + regulator-min-microvolt = <1215000>; + regulator-max-microvolt = <1235000>; + }; + ldo2 { + regulator-name = "2.8V CPU"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-always-on; + regulator-boot-on; + }; + }; + + backlight { + compatible = "ams,as3711-bl"; + su2-dev = <&lcdc>; + su2-max-uA = <36000>; + su2-feedback-curr-auto; + su2-fbprot-gpio4; + su2-auto-curr1; + su2-auto-curr2; + su2-auto-curr3; + }; +}; diff --git a/drivers/mfd/as3711.c b/drivers/mfd/as3711.c index e994c96..01e4141 100644 --- a/drivers/mfd/as3711.c +++ b/drivers/mfd/as3711.c @@ -112,16 +112,34 @@ static const struct regmap_config as3711_regmap_config = { .cache_type = REGCACHE_RBTREE, }; +#ifdef CONFIG_OF +static struct of_device_id as3711_of_match[] = { + {.compatible = "ams,as3711",}, + {} +}; +MODULE_DEVICE_TABLE(of, as3711_of_match); +#endif + static int as3711_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct as3711 *as3711; - struct as3711_platform_data *pdata = client->dev.platform_data; + struct as3711_platform_data *pdata; unsigned int id1, id2; int ret; - if (!pdata) - dev_dbg(&client->dev, "Platform data not found\n"); + if (!client->dev.of_node) { + pdata = client->dev.platform_data; + if (!pdata) + dev_dbg(&client->dev, "Platform data not found\n"); + } else { + pdata = devm_kzalloc(&client->dev, + sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate pdata\n"); + return -ENOMEM; + } + } as3711 = devm_kzalloc(&client->dev, sizeof(struct as3711), GFP_KERNEL); if (!as3711) { @@ -193,7 +211,8 @@ static struct i2c_driver as3711_i2c_driver = { .driver = { .name = "as3711", .owner = THIS_MODULE, - }, + .of_match_table = of_match_ptr(as3711_of_match), + }, .probe = as3711_i2c_probe, .remove = as3711_i2c_remove, .id_table = as3711_i2c_id, -- cgit v0.10.2 From 2154a2b347ca65e25ae916b9f923f0952a08e161 Mon Sep 17 00:00:00 2001 From: J Keerthy Date: Mon, 18 Feb 2013 10:42:44 +0530 Subject: mfd: palmas: Change the DT node property names to follow the convention DT node properties should not have "_". Replacing them by "-". Signed-off-by: J Keerthy Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index 73bf76d..c01b69a5 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -278,20 +278,20 @@ static void palmas_dt_to_pdata(struct i2c_client *i2c, int ret; u32 prop; - ret = of_property_read_u32(node, "ti,mux_pad1", &prop); + ret = of_property_read_u32(node, "ti,mux-pad1", &prop); if (!ret) { pdata->mux_from_pdata = 1; pdata->pad1 = prop; } - ret = of_property_read_u32(node, "ti,mux_pad2", &prop); + ret = of_property_read_u32(node, "ti,mux-pad2", &prop); if (!ret) { pdata->mux_from_pdata = 1; pdata->pad2 = prop; } /* The default for this register is all masked */ - ret = of_property_read_u32(node, "ti,power_ctrl", &prop); + ret = of_property_read_u32(node, "ti,power-ctrl", &prop); if (!ret) pdata->power_ctrl = prop; else -- cgit v0.10.2 From ed21465a0987e5fd81d6b47349115f2dd3d8ef9c Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 13 Mar 2013 21:34:19 +0400 Subject: mfd: syscon: Removed unneeded field "dev" from private driver structure Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 61aea63..674af14 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -25,17 +25,15 @@ static struct platform_driver syscon_driver; struct syscon { - struct device *dev; void __iomem *base; struct regmap *regmap; }; static int syscon_match(struct device *dev, void *data) { - struct syscon *syscon = dev_get_drvdata(dev); struct device_node *dn = data; - return (syscon->dev->of_node == dn) ? 1 : 0; + return (dev->of_node == dn) ? 1 : 0; } struct regmap *syscon_node_to_regmap(struct device_node *np) @@ -130,7 +128,6 @@ static int syscon_probe(struct platform_device *pdev) return PTR_ERR(syscon->regmap); } - syscon->dev = dev; platform_set_drvdata(pdev, syscon); dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", -- cgit v0.10.2 From 5ab3a89a741fa4c331f42dec17fde59586dc59e6 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Wed, 13 Mar 2013 21:34:20 +0400 Subject: mfd: syscon: Add non-DT support This patch allow using syscon driver from the platform data, i.e. possibility using driver on systems without oftree support. For search syscon device from the client drivers, "syscon_regmap_lookup_by_pdevname" function was added. Signed-off-by: Alexander Shiyan Acked-by: Arnd Bergmann Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 67e56dc..3c50c38 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1100,7 +1100,6 @@ config MFD_STA2X11 config MFD_SYSCON bool "System Controller Register R/W Based on Regmap" - depends on OF select REGMAP_MMIO help Select this option to enable accessing system control registers diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 674af14..7905d36 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -29,7 +29,7 @@ struct syscon { struct regmap *regmap; }; -static int syscon_match(struct device *dev, void *data) +static int syscon_match_node(struct device *dev, void *data) { struct device_node *dn = data; @@ -42,7 +42,7 @@ struct regmap *syscon_node_to_regmap(struct device_node *np) struct device *dev; dev = driver_find_device(&syscon_driver.driver, NULL, np, - syscon_match); + syscon_match_node); if (!dev) return ERR_PTR(-EPROBE_DEFER); @@ -68,6 +68,34 @@ struct regmap *syscon_regmap_lookup_by_compatible(const char *s) } EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_compatible); +static int syscon_match_pdevname(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct platform_device_id *id = platform_get_device_id(pdev); + + if (id) + if (!strcmp(id->name, (const char *)data)) + return 1; + + return !strcmp(dev_name(dev), (const char *)data); +} + +struct regmap *syscon_regmap_lookup_by_pdevname(const char *s) +{ + struct device *dev; + struct syscon *syscon; + + dev = driver_find_device(&syscon_driver.driver, NULL, (void *)s, + syscon_match_pdevname); + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + + syscon = dev_get_drvdata(dev); + + return syscon->regmap; +} +EXPORT_SYMBOL_GPL(syscon_regmap_lookup_by_pdevname); + struct regmap *syscon_regmap_lookup_by_phandle(struct device_node *np, const char *property) { @@ -99,28 +127,22 @@ static struct regmap_config syscon_regmap_config = { static int syscon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct syscon *syscon; - struct resource res; - int ret; - - if (!np) - return -ENOENT; + struct resource *res; - syscon = devm_kzalloc(dev, sizeof(struct syscon), - GFP_KERNEL); + syscon = devm_kzalloc(dev, sizeof(*syscon), GFP_KERNEL); if (!syscon) return -ENOMEM; - syscon->base = of_iomap(np, 0); - if (!syscon->base) - return -EADDRNOTAVAIL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; - ret = of_address_to_resource(np, 0, &res); - if (ret) - return ret; + syscon->base = devm_ioremap(dev, res->start, resource_size(res)); + if (!syscon->base) + return -ENOMEM; - syscon_regmap_config.max_register = res.end - res.start - 3; + syscon_regmap_config.max_register = res->end - res->start - 3; syscon->regmap = devm_regmap_init_mmio(dev, syscon->base, &syscon_regmap_config); if (IS_ERR(syscon->regmap)) { @@ -130,22 +152,15 @@ static int syscon_probe(struct platform_device *pdev) platform_set_drvdata(pdev, syscon); - dev_info(dev, "syscon regmap start 0x%x end 0x%x registered\n", - res.start, res.end); + dev_info(dev, "regmap 0x%x-0x%x registered\n", res->start, res->end); return 0; } -static int syscon_remove(struct platform_device *pdev) -{ - struct syscon *syscon; - - syscon = platform_get_drvdata(pdev); - iounmap(syscon->base); - platform_set_drvdata(pdev, NULL); - - return 0; -} +static const struct platform_device_id syscon_ids[] = { + { "syscon", }, + { } +}; static struct platform_driver syscon_driver = { .driver = { @@ -154,7 +169,7 @@ static struct platform_driver syscon_driver = { .of_match_table = of_syscon_match, }, .probe = syscon_probe, - .remove = syscon_remove, + .id_table = syscon_ids, }; static int __init syscon_init(void) diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index 6aeb6b8..5c9ee6e 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -17,6 +17,7 @@ extern struct regmap *syscon_node_to_regmap(struct device_node *np); extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); +extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s); extern struct regmap *syscon_regmap_lookup_by_phandle( struct device_node *np, const char *property); -- cgit v0.10.2 From a200e320f7ba7daa42e5fae3a1c67ed2f704b05c Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Fri, 1 Mar 2013 13:07:16 +0100 Subject: mfd: stmpe: DT: Enable no-irq mode configuration If there is no interrupt property into stmpe node then activate the no-irq mode by setting the irq value to -1. Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Gabriel Fernandez Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index 4b11202..e550dcb 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -1057,6 +1057,9 @@ int stmpe_probe(struct stmpe_client_info *ci, int partnum) return -ENOMEM; stmpe_of_probe(pdata, np); + + if (of_find_property(np, "interrupts", NULL) == NULL) + ci->irq = -1; } stmpe = devm_kzalloc(ci->dev, sizeof(struct stmpe), GFP_KERNEL); -- cgit v0.10.2 From 408a3fa867be13844af13bcbba25e70232fd99e5 Mon Sep 17 00:00:00 2001 From: Gabriel Fernandez Date: Fri, 1 Mar 2013 13:07:26 +0100 Subject: mfd: stmpe: DT: Add stmpe-i2c dt alias to get id device This patch augments the STMP driver to read the device id from the stmpe-i2c dt alias if present. Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: Gabriel Fernandez Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index e550dcb..bc85877 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -1015,7 +1015,10 @@ void stmpe_of_probe(struct stmpe_platform_data *pdata, struct device_node *np) { struct device_node *child; - pdata->id = -1; + pdata->id = of_alias_get_id(np, "stmpe-i2c"); + if (pdata->id < 0) + pdata->id = -1; + pdata->irq_trigger = IRQF_TRIGGER_NONE; of_property_read_u32(np, "st,autosleep-timeout", -- cgit v0.10.2 From bbc48c6aaab9cff03e6e966bf752c3b7ad91d16a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 5 Mar 2013 13:47:06 +0900 Subject: mfd: ab3100-otp: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ab3100-otp.c b/drivers/mfd/ab3100-otp.c index 8440010..d7ce016 100644 --- a/drivers/mfd/ab3100-otp.c +++ b/drivers/mfd/ab3100-otp.c @@ -248,19 +248,7 @@ static struct platform_driver ab3100_otp_driver = { .remove = __exit_p(ab3100_otp_remove), }; -static int __init ab3100_otp_init(void) -{ - return platform_driver_probe(&ab3100_otp_driver, - ab3100_otp_probe); -} - -static void __exit ab3100_otp_exit(void) -{ - platform_driver_unregister(&ab3100_otp_driver); -} - -module_init(ab3100_otp_init); -module_exit(ab3100_otp_exit); +module_platform_driver_probe(ab3100_otp_driver, ab3100_otp_probe); MODULE_AUTHOR("Linus Walleij "); MODULE_DESCRIPTION("AB3100 OTP Readout Driver"); -- cgit v0.10.2 From e8fe6a827939cf723a3871ff455f6346337c649a Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 5 Mar 2013 13:47:59 +0900 Subject: mfd: htc-pasic3: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 9e5453d..0285fce 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -208,18 +208,7 @@ static struct platform_driver pasic3_driver = { .remove = pasic3_remove, }; -static int __init pasic3_base_init(void) -{ - return platform_driver_probe(&pasic3_driver, pasic3_probe); -} - -static void __exit pasic3_base_exit(void) -{ - platform_driver_unregister(&pasic3_driver); -} - -module_init(pasic3_base_init); -module_exit(pasic3_base_exit); +module_platform_driver_probe(pasic3_driver, pasic3_probe); MODULE_AUTHOR("Philipp Zabel "); MODULE_DESCRIPTION("Core driver for HTC PASIC3"); -- cgit v0.10.2 From 3de764d4aa40109806cf7f9a9d811b970ff49374 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Tue, 5 Mar 2013 13:48:28 +0900 Subject: mfd: davinci_voicecodec: use module_platform_driver_probe() This patch uses module_platform_driver_probe() macro which makes the code smaller and simpler. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index c0bcc87..c60ab0c 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -177,17 +177,7 @@ static struct platform_driver davinci_vc_driver = { .remove = davinci_vc_remove, }; -static int __init davinci_vc_init(void) -{ - return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe); -} -module_init(davinci_vc_init); - -static void __exit davinci_vc_exit(void) -{ - platform_driver_unregister(&davinci_vc_driver); -} -module_exit(davinci_vc_exit); +module_platform_driver_probe(davinci_vc_driver, davinci_vc_probe); MODULE_AUTHOR("Miguel Aguilar"); MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface"); -- cgit v0.10.2 From 58eb893da99815ebdeee6cb3eeac897b99a246f7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 14 Mar 2013 15:54:24 +0100 Subject: mfd: abx500: Move the AB8500 Kconfig fragment Move the AB8500 Kconfig fragment below the AB3100 so the menuconfig menu gets hierarchically nested and looks nice. Having the EZX PCAP in the middle disturbs the nice hierarchical layout from kconfig. Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3c50c38..d5b9b05 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -877,13 +877,6 @@ config AB3100_OTP programmable memory) support. This exposes a sysfs file to read out OTP values. -config EZX_PCAP - bool "PCAP Support" - depends on GENERIC_HARDIRQS && SPI_MASTER - help - This enables the PCAP ASIC present on EZX Phones. This is - needed for MMC, TouchScreen, Sound, USB, etc.. - config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU @@ -912,6 +905,13 @@ config AB8500_GPADC help AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage +config EZX_PCAP + bool "PCAP Support" + depends on GENERIC_HARDIRQS && SPI_MASTER + help + This enables the PCAP ASIC present on EZX Phones. This is + needed for MMC, TouchScreen, Sound, USB, etc.. + config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 -- cgit v0.10.2 From 9d66b568a215fe2da2a9db736ebf9b8d66082d88 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 4 Apr 2013 21:34:11 +0200 Subject: MFD: rtsx_pcr: Fix probe fail path When rtsx_pci_acquire_irq fails in rtsx_pci_probe, we forget to disable an MSI (if we enabled it). This results in this warning on the next attempt to load the module: WARNING: at drivers/pci/msi.c:834 pci_enable_msi_block+0x2a4/0x2b0() Hardware name: HP EliteBook 840 G1 Modules linked in: rtsx_pci(+) ... Pid: 4056, comm: modprobe Tainted: G I 3.8.0-9405-gd895cb1-1-vanilla #1 Call Trace: [] warn_slowpath_common+0x7a/0xc0 [] warn_slowpath_null+0x15/0x20 [] pci_enable_msi_block+0x2a4/0x2b0 [] rtsx_pci_probe+0x55a/0x720 [rtsx_pci] ... So properly disable MSI in that case. Signed-off-by: Jiri Slaby Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 2f12cc1..578a113 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -1138,7 +1138,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, ret = rtsx_pci_acquire_irq(pcr); if (ret < 0) - goto free_dma; + goto disable_msi; pci_set_master(pcidev); synchronize_irq(pcr->irq); @@ -1162,7 +1162,9 @@ static int rtsx_pci_probe(struct pci_dev *pcidev, disable_irq: free_irq(pcr->irq, (void *)pcr); -free_dma: +disable_msi: + if (pcr->msi_en) + pci_disable_msi(pcr->pci); dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); unmap: -- cgit v0.10.2 From 099bb952b064aebd76b30ca25b51ff6440663195 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 15 Mar 2013 17:10:28 +0530 Subject: mfd: adp5520: Use module_i2c_driver() module_i2c_driver() removes some boilerplate and makes the code simple. Signed-off-by: Sachin Kamat Acked-by: Michael Hennerich Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 210dd03..5ab021f 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -360,17 +360,7 @@ static struct i2c_driver adp5520_driver = { .id_table = adp5520_id, }; -static int __init adp5520_init(void) -{ - return i2c_add_driver(&adp5520_driver); -} -module_init(adp5520_init); - -static void __exit adp5520_exit(void) -{ - i2c_del_driver(&adp5520_driver); -} -module_exit(adp5520_exit); +module_i2c_driver(adp5520_driver); MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("ADP5520(01) PMIC-MFD Driver"); -- cgit v0.10.2 From d1ac2c092d9dbd5ef2b903eea77629929c3314f2 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 26 Mar 2013 10:13:36 +0530 Subject: mfd: max77686: Use NULL instead of 0 'data' is a pointer and hence use NULL instead of 0. Silences the following warning: drivers/mfd/max77686.c:49:50: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Cc: Jonghwa Lee Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/max77686.c b/drivers/mfd/max77686.c index 4d73963..1cbb176 100644 --- a/drivers/mfd/max77686.c +++ b/drivers/mfd/max77686.c @@ -46,7 +46,7 @@ static struct regmap_config max77686_regmap_config = { #ifdef CONFIG_OF static struct of_device_id max77686_pmic_dt_match[] = { - {.compatible = "maxim,max77686", .data = 0}, + {.compatible = "maxim,max77686", .data = NULL}, {}, }; -- cgit v0.10.2 From 222bc784dcdc62d9402c580502d579914dbf07bc Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 11:53:36 +0900 Subject: mfd: wm8994: Silence bogus warning in wm8994_device_init() 'patch_regs' cannot be used uninitialized in wm8994_device_init(), because 'patch_regs' was already guarded by 'regmap_patch'. Thus, that's a bogus warning. Without this patch, the build warning happens as below: drivers/mfd/wm8994-core.c: In function 'wm8994_i2c_probe': drivers/mfd/wm8994-core.c:595:7: warning: 'patch_regs' may be used uninitialized in this function [-Wuninitialized] drivers/mfd/wm8994-core.c:408:14: note: 'patch_regs' was declared here Signed-off-by: Jingoo Han Acked-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 803e93f..97e929e 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -405,7 +405,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) struct regmap_config *regmap_config; const struct reg_default *regmap_patch = NULL; const char *devname; - int ret, i, patch_regs; + int ret, i, patch_regs = 0; int pulls = 0; if (dev_get_platdata(wm8994->dev)) { -- cgit v0.10.2 From 1816cb3409f1f6909e25e52effc57857803c678c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 17 Jan 2013 16:54:18 +0900 Subject: mfd: arizona: Disable all wake sources by default Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 2bec5f0..846262c 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -192,6 +192,9 @@ int arizona_irq_init(struct arizona *arizona) return -EINVAL; } + /* Disable all wake sources by default */ + regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0); + if (arizona->pdata.irq_active_high) { ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1, ARIZONA_IRQ_POL, 0); -- cgit v0.10.2 From 4816bd1c6d5589135523b4263be2e1b8b8c97599 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 14 Jan 2013 15:50:38 +0900 Subject: mfd: arizona: Clean up on failed runtime resume Make sure that we don't leave the device enabled needlessly. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b562c7b..0962c55 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -235,18 +235,21 @@ static int arizona_runtime_resume(struct device *dev) ret = arizona_wait_for_boot(arizona); if (ret != 0) { - regulator_disable(arizona->dcvdd); - return ret; + goto err; } ret = regcache_sync(arizona->regmap); if (ret != 0) { dev_err(arizona->dev, "Failed to restore register cache\n"); - regulator_disable(arizona->dcvdd); - return ret; + goto err; } return 0; + +err: + regcache_cache_only(arizona->regmap, true); + regulator_disable(arizona->dcvdd); + return ret; } static int arizona_runtime_suspend(struct device *dev) -- cgit v0.10.2 From 7994c664bdf365124a920457f9c3c9c111f89bcf Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Mar 2013 09:51:14 +0000 Subject: mfd: arizona: Clarify error message for failed primary IRQ request regmap has a very similar looking error, help identify where the error comes from by changing the error message. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 846262c..0aa39e2 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -264,7 +264,7 @@ int arizona_irq_init(struct arizona *arizona) flags, "arizona", arizona); if (ret != 0) { - dev_err(arizona->dev, "Failed to request IRQ %d: %d\n", + dev_err(arizona->dev, "Failed to request primary IRQ %d: %d\n", arizona->irq, ret); goto err_main_irq; } -- cgit v0.10.2 From f8a0941f1bbdbaa68441142675986501b48da8f5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Mar 2013 12:59:33 +0100 Subject: mfd: arizona: Basic support for edge triggered IRQs Allow the user to configure edge triggered IRQs, though we do not yet fully handle new interrupts occurring while an interrupt is being handled. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index bf3d1c0..40b0e75 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -208,7 +208,7 @@ static const struct i2c_board_info wm1277_devs[] = { static struct arizona_pdata wm5102_reva_pdata = { .ldoena = S3C64XX_GPN(7), .gpio_base = CODEC_GPIO_BASE, - .irq_active_high = true, + .irq_flags = IRQF_TRIGGER_HIGH, .micd_pol_gpio = CODEC_GPIO_BASE + 4, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ @@ -237,7 +237,7 @@ static struct spi_board_info wm5102_reva_spi_devs[] = { static struct arizona_pdata wm5102_pdata = { .ldoena = S3C64XX_GPN(7), .gpio_base = CODEC_GPIO_BASE, - .irq_active_high = true, + .irq_flags = IRQF_TRIGGER_HIGH, .micd_pol_gpio = CODEC_GPIO_BASE + 2, .gpio_defaults = { [2] = 0x10000, /* AIF3TXLRCLK */ diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index 0aa39e2..aa25468 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -195,7 +195,11 @@ int arizona_irq_init(struct arizona *arizona) /* Disable all wake sources by default */ regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0); - if (arizona->pdata.irq_active_high) { + if (!arizona->pdata.irq_flags) + arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; + + if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH | + IRQF_TRIGGER_RISING)) { ret = regmap_update_bits(arizona->regmap, ARIZONA_IRQ_CTRL_1, ARIZONA_IRQ_POL, 0); if (ret != 0) { @@ -203,12 +207,10 @@ int arizona_irq_init(struct arizona *arizona) ret); goto err; } - - flags |= IRQF_TRIGGER_HIGH; - } else { - flags |= IRQF_TRIGGER_LOW; } + flags |= arizona->pdata.irq_flags; + /* Allocate a virtual IRQ domain to distribute to the regmap domains */ arizona->virq = irq_domain_add_linear(NULL, 2, &arizona_domain_ops, arizona); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 455c51d..84fefed 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -99,7 +99,8 @@ struct arizona_pdata { /** If a direct 32kHz clock is provided on an MCLK specify it here */ int clk32k_src; - bool irq_active_high; /** IRQ polarity */ + /** Mode for primary IRQ (defaults to active low) */ + unsigned int irq_flags; /* Base GPIO */ int gpio_base; -- cgit v0.10.2 From 3092f8050eccce8463afe771f0910634a433e24b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Mar 2013 23:05:58 +0000 Subject: mfd: arizona: Allow GPIO to be specified for IRQ line If a GPIO is specified for the chip IRQ line then request it. This improves support for systems that do not put pins into input mode when used as interrupts. Also use this GPIO when the primary IRQ is in edge triggered mode to detect if we have handled pending interrupts in order to improve robustness. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index aa25468..f761cc1 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -94,6 +94,7 @@ static irqreturn_t arizona_ctrlif_err(int irq, void *data) static irqreturn_t arizona_irq_thread(int irq, void *data) { struct arizona *arizona = data; + bool poll; unsigned int val; int ret; @@ -103,20 +104,39 @@ static irqreturn_t arizona_irq_thread(int irq, void *data) return IRQ_NONE; } - /* Always handle the AoD domain */ - handle_nested_irq(irq_find_mapping(arizona->virq, 0)); + do { + poll = false; + + /* Always handle the AoD domain */ + handle_nested_irq(irq_find_mapping(arizona->virq, 0)); + + /* + * Check if one of the main interrupts is asserted and only + * check that domain if it is. + */ + ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, + &val); + if (ret == 0 && val & ARIZONA_IRQ1_STS) { + handle_nested_irq(irq_find_mapping(arizona->virq, 1)); + } else if (ret != 0) { + dev_err(arizona->dev, + "Failed to read main IRQ status: %d\n", ret); + } - /* - * Check if one of the main interrupts is asserted and only - * check that domain if it is. - */ - ret = regmap_read(arizona->regmap, ARIZONA_IRQ_PIN_STATUS, &val); - if (ret == 0 && val & ARIZONA_IRQ1_STS) { - handle_nested_irq(irq_find_mapping(arizona->virq, 1)); - } else if (ret != 0) { - dev_err(arizona->dev, "Failed to read main IRQ status: %d\n", - ret); - } + /* + * Poll the IRQ pin status to see if we're really done + * if the interrupt controller can't do it for us. + */ + if (!arizona->pdata.irq_gpio) { + break; + } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_RISING && + gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { + poll = true; + } else if (arizona->pdata.irq_flags & IRQF_TRIGGER_FALLING && + !gpio_get_value_cansleep(arizona->pdata.irq_gpio)) { + poll = true; + } + } while (poll); pm_runtime_mark_last_busy(arizona->dev); pm_runtime_put_autosuspend(arizona->dev); @@ -262,6 +282,26 @@ int arizona_irq_init(struct arizona *arizona) } } + /* Used to emulate edge trigger and to work around broken pinmux */ + if (arizona->pdata.irq_gpio) { + if (gpio_to_irq(arizona->pdata.irq_gpio) != arizona->irq) { + dev_warn(arizona->dev, "IRQ %d is not GPIO %d (%d)\n", + arizona->irq, arizona->pdata.irq_gpio, + gpio_to_irq(arizona->pdata.irq_gpio)); + arizona->irq = gpio_to_irq(arizona->pdata.irq_gpio); + } + + ret = devm_gpio_request_one(arizona->dev, + arizona->pdata.irq_gpio, + GPIOF_IN, "arizona IRQ"); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to request IRQ GPIO %d:: %d\n", + arizona->pdata.irq_gpio, ret); + arizona->pdata.irq_gpio = 0; + } + } + ret = request_threaded_irq(arizona->irq, NULL, arizona_irq_thread, flags, "arizona", arizona); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 84fefed..8755dd0 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -163,6 +163,9 @@ struct arizona_pdata { /** Haptic actuator type */ unsigned int hap_act; + + /** GPIO for primary IRQ (used for edge triggered emulation) */ + int irq_gpio; }; #endif -- cgit v0.10.2 From 22c75fe7c772c4c47df47364d9e807dcf204d7c2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 24 Mar 2013 23:16:56 +0000 Subject: mfd: arizona: Try to use interrupt flags from interrupt controller If no irq_flags are passed in platform data then query the interrupt controller for the trigger type and try to use that. This provides default operation with a wider range of hardware and will be needed for device tree support where the interrupt flags are configured on the interrupt controller. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index f761cc1..64cd9b6 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -189,6 +189,7 @@ int arizona_irq_init(struct arizona *arizona) int ret, i; const struct regmap_irq_chip *aod, *irq; bool ctrlif_error = true; + struct irq_data *irq_data; switch (arizona->type) { #ifdef CONFIG_MFD_WM5102 @@ -215,8 +216,30 @@ int arizona_irq_init(struct arizona *arizona) /* Disable all wake sources by default */ regmap_write(arizona->regmap, ARIZONA_WAKE_CONTROL, 0); - if (!arizona->pdata.irq_flags) - arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; + /* Read the flags from the interrupt controller if not specified */ + if (!arizona->pdata.irq_flags) { + irq_data = irq_get_irq_data(arizona->irq); + if (!irq_data) { + dev_err(arizona->dev, "Invalid IRQ: %d\n", + arizona->irq); + return -EINVAL; + } + + arizona->pdata.irq_flags = irqd_get_trigger_type(irq_data); + switch (arizona->pdata.irq_flags) { + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_FALLING: + break; + + case IRQ_TYPE_NONE: + default: + /* Device default */ + arizona->pdata.irq_flags = IRQF_TRIGGER_LOW; + break; + } + } if (arizona->pdata.irq_flags & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_RISING)) { -- cgit v0.10.2 From 544c7aadd7d4309ed01fcd787d393db67eb7eaea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 18:44:41 +0800 Subject: mfd: arizona: Support configuring MICBIASes into bypass mode Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 0962c55..dd679e8 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -514,10 +514,16 @@ int arizona_dev_init(struct arizona *arizona) } for (i = 0; i < ARIZONA_MAX_MICBIAS; i++) { - if (!arizona->pdata.micbias[i].mV) + if (!arizona->pdata.micbias[i].mV && + !arizona->pdata.micbias[i].bypass) continue; + /* Apply default for bypass mode */ + if (!arizona->pdata.micbias[i].mV) + arizona->pdata.micbias[i].mV = 2800; + val = (arizona->pdata.micbias[i].mV - 1500) / 100; + val <<= ARIZONA_MICB1_LVL_SHIFT; if (arizona->pdata.micbias[i].ext_cap) @@ -529,10 +535,14 @@ int arizona_dev_init(struct arizona *arizona) if (arizona->pdata.micbias[i].fast_start) val |= ARIZONA_MICB1_RATE; + if (arizona->pdata.micbias[i].bypass) + val |= ARIZONA_MICB1_BYPASS; + regmap_update_bits(arizona->regmap, ARIZONA_MIC_BIAS_CTRL_1 + i, ARIZONA_MICB1_LVL_MASK | ARIZONA_MICB1_DISCH | + ARIZONA_MICB1_BYPASS | ARIZONA_MICB1_RATE, val); } diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8755dd0..554d903 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -78,6 +78,7 @@ struct arizona_micbias { unsigned int ext_cap:1; /** External capacitor fitted */ unsigned int discharge:1; /** Actively discharge */ unsigned int fast_start:1; /** Enable aggressive startup ramp rate */ + unsigned int bypass:1; /** Use bypass mode */ }; struct arizona_micd_config { -- cgit v0.10.2 From 7adafc5d97c0e23f1eac1cf1ee98d514026e7915 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 8 Jan 2013 10:37:06 +0000 Subject: mfd: wm5102: Update patch for latest evaluation Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index a433f58..909e043 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -57,15 +57,20 @@ static const struct reg_default wm5102_reva_patch[] = { }; static const struct reg_default wm5102_revb_patch[] = { + { 0x19, 0x0001 }, { 0x80, 0x0003 }, { 0x081, 0xE022 }, - { 0x410, 0x4080 }, - { 0x418, 0x4080 }, - { 0x420, 0x4080 }, - { 0x428, 0xC000 }, + { 0x410, 0x6080 }, + { 0x418, 0xa080 }, + { 0x420, 0xa080 }, + { 0x428, 0xe000 }, + { 0x443, 0xDC1A }, { 0x4B0, 0x0066 }, { 0x458, 0x000b }, { 0x212, 0x0000 }, + { 0x171, 0x0000 }, + { 0x35E, 0x000C }, + { 0x2D4, 0x0000 }, { 0x80, 0x0000 }, }; @@ -282,7 +287,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000155, 0x0000 }, /* R341 - Rate Estimator 4 */ { 0x00000156, 0x0000 }, /* R342 - Rate Estimator 5 */ { 0x00000161, 0x0000 }, /* R353 - Dynamic Frequency Scaling 1 */ - { 0x00000171, 0x0002 }, /* R369 - FLL1 Control 1 */ + { 0x00000171, 0x0000 }, /* R369 - FLL1 Control 1 */ { 0x00000172, 0x0008 }, /* R370 - FLL1 Control 2 */ { 0x00000173, 0x0018 }, /* R371 - FLL1 Control 3 */ { 0x00000174, 0x007D }, /* R372 - FLL1 Control 4 */ -- cgit v0.10.2 From 247fa1920deeb1064e36c0a34410f4d63503b3d4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Mar 2013 14:47:47 +0100 Subject: mfd: arizona: Fully support the use of MCLK1 as the 32kHz clock source MCLK1 is not in the AoD power domain so if it is used as the 32kHz clock source we need to hold a runtime PM reference to keep the device from going into low power mode. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index dd679e8..c2c9693 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -39,11 +39,21 @@ int arizona_clk32k_enable(struct arizona *arizona) arizona->clk32k_ref++; - if (arizona->clk32k_ref == 1) + if (arizona->clk32k_ref == 1) { + switch (arizona->pdata.clk32k_src) { + case ARIZONA_32KZ_MCLK1: + ret = pm_runtime_get_sync(arizona->dev); + if (ret != 0) + goto out; + break; + } + ret = regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, ARIZONA_CLK_32K_ENA, ARIZONA_CLK_32K_ENA); + } +out: if (ret != 0) arizona->clk32k_ref--; @@ -63,10 +73,17 @@ int arizona_clk32k_disable(struct arizona *arizona) arizona->clk32k_ref--; - if (arizona->clk32k_ref == 0) + if (arizona->clk32k_ref == 0) { regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, ARIZONA_CLK_32K_ENA, 0); + switch (arizona->pdata.clk32k_src) { + case ARIZONA_32KZ_MCLK1: + pm_runtime_put_sync(arizona->dev); + break; + } + } + mutex_unlock(&arizona->clk_lock); return ret; -- cgit v0.10.2 From 767c6dc032d5a28765654db20c7badf1ec6e7387 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Mar 2013 19:04:46 +0100 Subject: mfd: arizona: Unconditionally enable 32kHz clock If we have a directly provided 32kHz clock unconditionally enable it, substantial chip functionality relies on it so dynamic management is not worthwhile. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index c2c9693..1eb963c 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -518,6 +518,7 @@ int arizona_dev_init(struct arizona *arizona) regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, ARIZONA_CLK_32K_SRC_MASK, arizona->pdata.clk32k_src - 1); + arizona_clk32k_enable(arizona); break; case ARIZONA_32KZ_NONE: regmap_update_bits(arizona->regmap, ARIZONA_CLOCK_32K_1, -- cgit v0.10.2 From c6d6bfb186add845d3f9a7f5809bc5b302579308 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 11:14:52 +0000 Subject: mfd: wm5102: Only use the lowest three bits of device revision Only the lowest three bits contain device revision for WM5102, the high bits have been repurposed. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1eb963c..f120571 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -444,6 +444,7 @@ int arizona_dev_init(struct arizona *arizona) arizona->type = WM5102; } apply_patch = wm5102_patch; + arizona->rev &= 0x7; break; #endif #ifdef CONFIG_MFD_WM5110 -- cgit v0.10.2 From 4c9bb8bc352a14c9613c77bc3f1e9038cd086b9b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Mar 2013 18:01:49 +0000 Subject: mfd: wm5102: Manually apply register patch Future updates will require us to manually apply the register patch for wm5102. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index f120571..b71bf7b 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -255,6 +255,16 @@ static int arizona_runtime_resume(struct device *dev) goto err; } + switch (arizona->type) { + case WM5102: + ret = wm5102_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, "Failed to apply patch: %d\n", + ret); + goto err; + } + } + ret = regcache_sync(arizona->regmap); if (ret != 0) { dev_err(arizona->dev, "Failed to restore register cache\n"); diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 909e043..44cb7be 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include @@ -77,16 +78,34 @@ static const struct reg_default wm5102_revb_patch[] = { /* We use a function so we can use ARRAY_SIZE() */ int wm5102_patch(struct arizona *arizona) { + const struct reg_default *wm5102_patch; + int ret = 0; + int i, patch_size; + switch (arizona->rev) { case 0: - return regmap_register_patch(arizona->regmap, - wm5102_reva_patch, - ARRAY_SIZE(wm5102_reva_patch)); + wm5102_patch = wm5102_reva_patch; + patch_size = ARRAY_SIZE(wm5102_reva_patch); default: - return regmap_register_patch(arizona->regmap, - wm5102_revb_patch, - ARRAY_SIZE(wm5102_revb_patch)); + wm5102_patch = wm5102_revb_patch; + patch_size = ARRAY_SIZE(wm5102_revb_patch); } + + regcache_cache_bypass(arizona->regmap, true); + + for (i = 0; i < patch_size; i++) { + ret = regmap_write(arizona->regmap, wm5102_patch[i].reg, + wm5102_patch[i].def); + if (ret != 0) { + dev_err(arizona->dev, "Failed to write %x = %x: %d\n", + wm5102_patch[i].reg, wm5102_patch[i].def, ret); + goto out; + } + } + +out: + regcache_cache_bypass(arizona->regmap, false); + return ret; } static const struct regmap_irq wm5102_aod_irqs[ARIZONA_NUM_IRQ] = { -- cgit v0.10.2 From 9d53dfdc82503b819f7b854e627a555cc65224e3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Mar 2013 17:38:45 +0000 Subject: mfd: arizona: Factor out register polling Factor out the polling of the interrupt status register whilst we wait for boot done to allow the polling to be reused in other situations. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b71bf7b..b09b119 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -196,42 +196,50 @@ static irqreturn_t arizona_overclocked(int irq, void *data) return IRQ_HANDLED; } -static int arizona_wait_for_boot(struct arizona *arizona) +static int arizona_poll_reg(struct arizona *arizona, + int timeout, unsigned int reg, + unsigned int mask, unsigned int target) { - unsigned int reg; + unsigned int val = 0; int ret, i; - /* - * We can't use an interrupt as we need to runtime resume to do so, - * we won't race with the interrupt handler as it'll be blocked on - * runtime resume. - */ - for (i = 0; i < 5; i++) { - msleep(1); - - ret = regmap_read(arizona->regmap, - ARIZONA_INTERRUPT_RAW_STATUS_5, ®); + for (i = 0; i < timeout; i++) { + ret = regmap_read(arizona->regmap, reg, &val); if (ret != 0) { - dev_err(arizona->dev, "Failed to read boot state: %d\n", - ret); + dev_err(arizona->dev, "Failed to read reg %u: %d\n", + reg, ret); continue; } - if (reg & ARIZONA_BOOT_DONE_STS) - break; + if ((val & mask) == target) + return 0; + + msleep(1); } - if (reg & ARIZONA_BOOT_DONE_STS) { + dev_err(arizona->dev, "Polling reg %u timed out: %x\n", reg, val); + return -ETIMEDOUT; +} + +static int arizona_wait_for_boot(struct arizona *arizona) +{ + int ret; + + /* + * We can't use an interrupt as we need to runtime resume to do so, + * we won't race with the interrupt handler as it'll be blocked on + * runtime resume. + */ + ret = arizona_poll_reg(arizona, 5, ARIZONA_INTERRUPT_RAW_STATUS_5, + ARIZONA_BOOT_DONE_STS, ARIZONA_BOOT_DONE_STS); + + if (!ret) regmap_write(arizona->regmap, ARIZONA_INTERRUPT_STATUS_5, ARIZONA_BOOT_DONE_STS); - } else { - dev_err(arizona->dev, "Device boot timed out: %x\n", reg); - return -ETIMEDOUT; - } pm_runtime_mark_last_busy(arizona->dev); - return 0; + return ret; } #ifdef CONFIG_PM_RUNTIME -- cgit v0.10.2 From e80436bbb241abbf2168c65622fb98d9378432fa Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 26 Mar 2013 18:46:15 +0000 Subject: mfd: arizona: Add a hardware patch mechanism This patch adds facilities for apply a register patch contained within the chip using the write sequencer. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index b09b119..d6b40df 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -242,6 +242,90 @@ static int arizona_wait_for_boot(struct arizona *arizona) return ret; } +static int arizona_apply_hardware_patch(struct arizona* arizona) +{ + unsigned int fll, sysclk; + int ret, err; + + regcache_cache_bypass(arizona->regmap, true); + + /* Cache existing FLL and SYSCLK settings */ + ret = regmap_read(arizona->regmap, ARIZONA_FLL1_CONTROL_1, &fll); + if (ret != 0) { + dev_err(arizona->dev, "Failed to cache FLL settings: %d\n", + ret); + return ret; + } + ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &sysclk); + if (ret != 0) { + dev_err(arizona->dev, "Failed to cache SYSCLK settings: %d\n", + ret); + return ret; + } + + /* Start up SYSCLK using the FLL in free running mode */ + ret = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, + ARIZONA_FLL1_ENA | ARIZONA_FLL1_FREERUN); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to start FLL in freerunning mode: %d\n", + ret); + return ret; + } + ret = arizona_poll_reg(arizona, 25, ARIZONA_INTERRUPT_RAW_STATUS_5, + ARIZONA_FLL1_CLOCK_OK_STS, + ARIZONA_FLL1_CLOCK_OK_STS); + if (ret != 0) { + ret = -ETIMEDOUT; + goto err_fll; + } + + ret = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, 0x0144); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start SYSCLK: %d\n", ret); + goto err_fll; + } + + /* Start the write sequencer and wait for it to finish */ + ret = regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, + ARIZONA_WSEQ_ENA | ARIZONA_WSEQ_START | 160); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start write sequencer: %d\n", + ret); + goto err_sysclk; + } + ret = arizona_poll_reg(arizona, 5, ARIZONA_WRITE_SEQUENCER_CTRL_1, + ARIZONA_WSEQ_BUSY, 0); + if (ret != 0) { + regmap_write(arizona->regmap, ARIZONA_WRITE_SEQUENCER_CTRL_0, + ARIZONA_WSEQ_ABORT); + ret = -ETIMEDOUT; + } + +err_sysclk: + err = regmap_write(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, sysclk); + if (err != 0) { + dev_err(arizona->dev, + "Failed to re-apply old SYSCLK settings: %d\n", + err); + } + +err_fll: + err = regmap_write(arizona->regmap, ARIZONA_FLL1_CONTROL_1, fll); + if (err != 0) { + dev_err(arizona->dev, + "Failed to re-apply old FLL settings: %d\n", + err); + } + + regcache_cache_bypass(arizona->regmap, false); + + if (ret != 0) + return ret; + else + return err; +} + #ifdef CONFIG_PM_RUNTIME static int arizona_runtime_resume(struct device *dev) { @@ -271,6 +355,17 @@ static int arizona_runtime_resume(struct device *dev) ret); goto err; } + + ret = arizona_apply_hardware_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to apply hardware patch: %d\n", + ret); + goto err; + } + break; + default: + break; } ret = regcache_sync(arizona->regmap); @@ -513,6 +608,20 @@ int arizona_dev_init(struct arizona *arizona) ret); goto err_reset; } + + switch (arizona->type) { + case WM5102: + ret = arizona_apply_hardware_patch(arizona); + if (ret != 0) { + dev_err(arizona->dev, + "Failed to apply hardware patch: %d\n", + ret); + goto err_reset; + } + break; + default: + break; + } } for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) { -- cgit v0.10.2 From 12bb68ed459e52187c7e70cb6dd4bfcc672cc171 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 27 Mar 2013 09:49:40 +0000 Subject: mfd: wm5102: Deactivate standard boot sequence This patch deactivates the standard, currently noop, boot sequence because we now have facilities in place for running a custom boot sequence. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index d6b40df..7d1abe7 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -342,11 +342,6 @@ static int arizona_runtime_resume(struct device *dev) regcache_cache_only(arizona->regmap, false); - ret = arizona_wait_for_boot(arizona); - if (ret != 0) { - goto err; - } - switch (arizona->type) { case WM5102: ret = wm5102_patch(arizona); @@ -365,6 +360,11 @@ static int arizona_runtime_resume(struct device *dev) } break; default: + ret = arizona_wait_for_boot(arizona); + if (ret != 0) { + goto err; + } + break; } -- cgit v0.10.2 From 9bf7e0e83fc98642584d0edd97cc6a755b9b36f3 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 27 Mar 2013 11:13:25 +0000 Subject: mfd: wm5102: Update defaults to match patch Registers which have defaults and are updated by the patch file should have their defaults updated to match the value set by the patch file, otherwise incorrect values will be read from the cache. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 44cb7be..ad1cc3b 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -382,7 +382,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000400, 0x0000 }, /* R1024 - Output Enables 1 */ { 0x00000408, 0x0000 }, /* R1032 - Output Rate 1 */ { 0x00000409, 0x0022 }, /* R1033 - Output Volume Ramp */ - { 0x00000410, 0x4080 }, /* R1040 - Output Path Config 1L */ + { 0x00000410, 0x6080 }, /* R1040 - Output Path Config 1L */ { 0x00000411, 0x0180 }, /* R1041 - DAC Digital Volume 1L */ { 0x00000412, 0x0081 }, /* R1042 - DAC Volume Limit 1L */ { 0x00000413, 0x0001 }, /* R1043 - Noise Gate Select 1L */ @@ -390,7 +390,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000415, 0x0180 }, /* R1045 - DAC Digital Volume 1R */ { 0x00000416, 0x0081 }, /* R1046 - DAC Volume Limit 1R */ { 0x00000417, 0x0002 }, /* R1047 - Noise Gate Select 1R */ - { 0x00000418, 0x4080 }, /* R1048 - Output Path Config 2L */ + { 0x00000418, 0xA080 }, /* R1048 - Output Path Config 2L */ { 0x00000419, 0x0180 }, /* R1049 - DAC Digital Volume 2L */ { 0x0000041A, 0x0081 }, /* R1050 - DAC Volume Limit 2L */ { 0x0000041B, 0x0004 }, /* R1051 - Noise Gate Select 2L */ @@ -398,11 +398,11 @@ static const struct reg_default wm5102_reg_default[] = { { 0x0000041D, 0x0180 }, /* R1053 - DAC Digital Volume 2R */ { 0x0000041E, 0x0081 }, /* R1054 - DAC Volume Limit 2R */ { 0x0000041F, 0x0008 }, /* R1055 - Noise Gate Select 2R */ - { 0x00000420, 0x4080 }, /* R1056 - Output Path Config 3L */ + { 0x00000420, 0xA080 }, /* R1056 - Output Path Config 3L */ { 0x00000421, 0x0180 }, /* R1057 - DAC Digital Volume 3L */ { 0x00000422, 0x0081 }, /* R1058 - DAC Volume Limit 3L */ { 0x00000423, 0x0010 }, /* R1059 - Noise Gate Select 3L */ - { 0x00000428, 0xC000 }, /* R1064 - Output Path Config 4L */ + { 0x00000428, 0xE000 }, /* R1064 - Output Path Config 4L */ { 0x00000429, 0x0180 }, /* R1065 - DAC Digital Volume 4L */ { 0x0000042A, 0x0081 }, /* R1066 - Out Volume 4L */ { 0x0000042B, 0x0040 }, /* R1067 - Noise Gate Select 4L */ @@ -417,7 +417,7 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000436, 0x0081 }, /* R1078 - DAC Volume Limit 5R */ { 0x00000437, 0x0200 }, /* R1079 - Noise Gate Select 5R */ { 0x00000450, 0x0000 }, /* R1104 - DAC AEC Control 1 */ - { 0x00000458, 0x0001 }, /* R1112 - Noise Gate Control */ + { 0x00000458, 0x000B }, /* R1112 - Noise Gate Control */ { 0x00000490, 0x0069 }, /* R1168 - PDM SPK1 CTRL 1 */ { 0x00000491, 0x0000 }, /* R1169 - PDM SPK1 CTRL 2 */ { 0x00000500, 0x000C }, /* R1280 - AIF1 BCLK Ctrl */ -- cgit v0.10.2 From 87d3af4ac0554afc2e86069d7578f74b12f3f67a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 26 Mar 2013 12:15:26 +0000 Subject: mfd: arizona: Hold device in reset while ramping supplies Acquire the /RESET GPIO before we enable regulators and hold the device in reset while the regulators power up in order to improve robustness during the initial power up. Also fix the error path so that the device is left in reset while we're at it. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 7d1abe7..ae10af7 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -504,6 +504,17 @@ int arizona_dev_init(struct arizona *arizona) goto err_early; } + if (arizona->pdata.reset) { + /* Start out with /RESET low to put the chip into reset */ + ret = gpio_request_one(arizona->pdata.reset, + GPIOF_DIR_OUT | GPIOF_INIT_LOW, + "arizona /RESET"); + if (ret != 0) { + dev_err(dev, "Failed to request /RESET: %d\n", ret); + goto err_early; + } + } + ret = regulator_bulk_enable(arizona->num_core_supplies, arizona->core_supplies); if (ret != 0) { @@ -518,18 +529,8 @@ int arizona_dev_init(struct arizona *arizona) goto err_enable; } - if (arizona->pdata.reset) { - /* Start out with /RESET low to put the chip into reset */ - ret = gpio_request_one(arizona->pdata.reset, - GPIOF_DIR_OUT | GPIOF_INIT_LOW, - "arizona /RESET"); - if (ret != 0) { - dev_err(dev, "Failed to request /RESET: %d\n", ret); - goto err_dcvdd; - } - + if (arizona->pdata.reset) gpio_set_value_cansleep(arizona->pdata.reset, 1); - } regcache_cache_only(arizona->regmap, false); @@ -769,10 +770,9 @@ err_irq: arizona_irq_exit(arizona); err_reset: if (arizona->pdata.reset) { - gpio_set_value_cansleep(arizona->pdata.reset, 1); + gpio_set_value_cansleep(arizona->pdata.reset, 0); gpio_free(arizona->pdata.reset); } -err_dcvdd: regulator_disable(arizona->dcvdd); err_enable: regulator_bulk_disable(arizona->num_core_supplies, -- cgit v0.10.2 From c25feaa53cca696321540ca0f8564bf484224cac Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Apr 2013 12:53:37 +0100 Subject: mfd: arizona: Wait for internal clocks to startup after reset Signed-off-by: Mark Brown Signed-off-by: Charles Keepax Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index ae10af7..41856b4c 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -529,8 +529,10 @@ int arizona_dev_init(struct arizona *arizona) goto err_enable; } - if (arizona->pdata.reset) + if (arizona->pdata.reset) { gpio_set_value_cansleep(arizona->pdata.reset, 1); + msleep(1); + } regcache_cache_only(arizona->regmap, false); @@ -589,6 +591,8 @@ int arizona_dev_init(struct arizona *arizona) goto err_reset; } + msleep(1); + ret = regcache_sync(arizona->regmap); if (ret != 0) { dev_err(dev, "Failed to sync device: %d\n", ret); -- cgit v0.10.2 From d955cba86d32e9b4b6fe7611b4c41b7bbe286183 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 3 Apr 2013 09:45:29 +0100 Subject: mfd: wm5102: Don't wait for boot when boot sequencer is disabled As we are using a custom boot sequence we don't need to wait for the standard boot sequence in device init when the normal write sequence is disabled. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 41856b4c..6ab0304 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -600,10 +600,24 @@ int arizona_dev_init(struct arizona *arizona) } } - ret = arizona_wait_for_boot(arizona); - if (ret != 0) { - dev_err(arizona->dev, "Device failed initial boot: %d\n", ret); - goto err_reset; + switch (arizona->type) { + case WM5102: + ret = regmap_read(arizona->regmap, 0x19, &val); + if (ret != 0) + dev_err(dev, + "Failed to check write sequencer state: %d\n", + ret); + else if (val & 0x01) + break; + /* Fall through */ + default: + ret = arizona_wait_for_boot(arizona); + if (ret != 0) { + dev_err(arizona->dev, + "Device failed initial boot: %d\n", ret); + goto err_reset; + } + break; } if (apply_patch) { -- cgit v0.10.2 From a5055d5d683605210f77d46614bbdb389c1e4a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 15 Feb 2013 23:56:49 +0100 Subject: mfd: twl4030-madc: Add support for raw value in twl4030_madc_conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver twl4030-madc has hardcoded channel types (10 - battery current, 1 - battery temperature) and also conversation data in variable twl4030_divider_ratios. These hardcoded channels are incorrect for Nokia RX-51 board (where is channel 0 - battery temperature). For Nokia RX-51 there is rx51_battery power_supply driver which reporting battery information via twl4030_madc_conversion. But this driver needs raw values (not converted via some hardcoded functions). So this patch adding new parameter "raw" to struct twl4030_madc_request which tell twl4030-madc driver to not convert values, but rather return raw. Signed-off-by: Pali Rohár Reviewed-by: Anton Vorontsov Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 942b666..42bd3ea 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -211,12 +211,14 @@ static int twl4030battery_current(int raw_volt) * @reg_base - Base address of the first channel * @Channels - 16 bit bitmap. If the bit is set, channel value is read * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion * value is stored * Returns the number of successfully read channels. */ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, u8 reg_base, unsigned - long channels, int *buf) + long channels, int *buf, + bool raw) { int count = 0, count_req = 0, i; u8 reg; @@ -230,6 +232,10 @@ static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, count_req++; continue; } + if (raw) { + count++; + continue; + } switch (i) { case 10: buf[i] = twl4030battery_current(buf[i]); @@ -371,7 +377,7 @@ static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) method = &twl4030_conversion_methods[r->method]; /* Read results */ len = twl4030_madc_read_channels(madc, method->rbase, - r->channels, r->rbuf); + r->channels, r->rbuf, r->raw); /* Return results to caller */ if (r->func_cb != NULL) { r->func_cb(len, r->channels, r->rbuf); @@ -397,7 +403,7 @@ err_i2c: method = &twl4030_conversion_methods[r->method]; /* Read results */ len = twl4030_madc_read_channels(madc, method->rbase, - r->channels, r->rbuf); + r->channels, r->rbuf, r->raw); /* Return results to caller */ if (r->func_cb != NULL) { r->func_cb(len, r->channels, r->rbuf); @@ -585,7 +591,7 @@ int twl4030_madc_conversion(struct twl4030_madc_request *req) goto out; } ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, - req->channels, req->rbuf); + req->channels, req->rbuf, req->raw); twl4030_madc->requests[req->method].active = 0; out: diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h index 530e11b..01f5951 100644 --- a/include/linux/i2c/twl4030-madc.h +++ b/include/linux/i2c/twl4030-madc.h @@ -39,6 +39,7 @@ struct twl4030_madc_conversion_method { * @do_avgP: sample the input channel for 4 consecutive cycles * @method: RT, SW1, SW2 * @type: Polling or interrupt based method + * @raw: Return raw value, do not convert it */ struct twl4030_madc_request { @@ -48,6 +49,7 @@ struct twl4030_madc_request { u16 type; bool active; bool result_pending; + bool raw; int rbuf[TWL4030_MADC_MAX_CHANNELS]; void (*func_cb)(int len, int channels, int *buf); }; -- cgit v0.10.2 From 8059c1c6be45973ea72342567a6ede5f4f0bf8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Fri, 15 Feb 2013 23:56:50 +0100 Subject: power: rx51_battery: Fix reporting correct values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tell twl4030_madc_conversion that this driver needs raw values. Driver twl4030_madc has some hardcoded values and conversation functions which are incorrect for Nokia RX-51 board. This driver rx51_battery expects raw values which convert itself. This patch fixing values reported by power supply interface. Before this patch driver reported always incorrect values on 3.8 kernel (sometimes design capacity was negative). Signed-off-by: Pali Rohár Acked-by: Anton Vorontsov Signed-off-by: Samuel Ortiz diff --git a/drivers/power/rx51_battery.c b/drivers/power/rx51_battery.c index 8208888..35f625e 100644 --- a/drivers/power/rx51_battery.c +++ b/drivers/power/rx51_battery.c @@ -42,6 +42,7 @@ static int rx51_battery_read_adc(int channel) req.method = TWL4030_MADC_SW1; req.func_cb = NULL; req.type = TWL4030_MADC_WAIT; + req.raw = true; if (twl4030_madc_conversion(&req) <= 0) return -ENODATA; -- cgit v0.10.2 From c6cc25fda58da8685ecef3f179adc7b99c8253b2 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Feb 2013 11:51:22 +0100 Subject: mfd: adp5520: Restore mode bits on resume The adp5520 unfortunately also clears the BL_EN bit when the nSTNDBY bit is cleared. So we need to make sure to restore it during resume if it was set before suspend. Cc: stable@vger.kernel.org Signed-off-by: Lars-Peter Clausen Acked-by: Michael Hennerich Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 5ab021f..0d2eba0 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -36,6 +36,7 @@ struct adp5520_chip { struct blocking_notifier_head notifier_list; int irq; unsigned long id; + uint8_t mode; }; static int __adp5520_read(struct i2c_client *client, @@ -326,7 +327,10 @@ static int adp5520_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); - adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); + adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode); + /* All other bits are W1C */ + chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY; + adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0); return 0; } @@ -335,7 +339,7 @@ static int adp5520_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); - adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); + adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode); return 0; } #endif -- cgit v0.10.2 From 85529575b67c318e296ae183cc80617d79aea23b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:27:21 +0900 Subject: mfd: 88pm860x: Drop devm_kfree of devm_kzalloc'd data devm_kfree() allocates memory that is released when a driver detaches. Thus, there is no reason to explicitly call devm_kfree() in probe or remove functions. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 893fc1b..31ca555 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -1144,17 +1144,15 @@ static int pm860x_probe(struct i2c_client *client, return -ENOMEM; ret = pm860x_dt_init(node, &client->dev, pdata); if (ret) - goto err; + return ret; } else if (!pdata) { pr_info("No platform data in %s!\n", __func__); return -EINVAL; } chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); - if (chip == NULL) { - ret = -ENOMEM; - goto err; - } + if (chip == NULL) + return -ENOMEM; chip->id = verify_addr(client); chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config); @@ -1194,10 +1192,6 @@ static int pm860x_probe(struct i2c_client *client, pm860x_device_init(chip, pdata); return 0; -err: - if (node) - devm_kfree(&client->dev, pdata); - return ret; } static int pm860x_remove(struct i2c_client *client) -- cgit v0.10.2 From bf5ea28a985f274446c5fd42f170c62eeb97ad67 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:28:15 +0900 Subject: mfd: lm3533: Use devm_gpio_request_one() Use devm_gpio_request_one() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/lm3533-core.c b/drivers/mfd/lm3533-core.c index ceebf2c..4b7e6da 100644 --- a/drivers/mfd/lm3533-core.c +++ b/drivers/mfd/lm3533-core.c @@ -496,8 +496,8 @@ static int lm3533_device_init(struct lm3533 *lm3533) dev_set_drvdata(lm3533->dev, lm3533); if (gpio_is_valid(lm3533->gpio_hwen)) { - ret = gpio_request_one(lm3533->gpio_hwen, GPIOF_OUT_INIT_LOW, - "lm3533-hwen"); + ret = devm_gpio_request_one(lm3533->dev, lm3533->gpio_hwen, + GPIOF_OUT_INIT_LOW, "lm3533-hwen"); if (ret < 0) { dev_err(lm3533->dev, "failed to request HWEN GPIO %d\n", @@ -528,8 +528,6 @@ err_unregister: mfd_remove_devices(lm3533->dev); err_disable: lm3533_disable(lm3533); - if (gpio_is_valid(lm3533->gpio_hwen)) - gpio_free(lm3533->gpio_hwen); return ret; } @@ -542,8 +540,6 @@ static void lm3533_device_exit(struct lm3533 *lm3533) mfd_remove_devices(lm3533->dev); lm3533_disable(lm3533); - if (gpio_is_valid(lm3533->gpio_hwen)) - gpio_free(lm3533->gpio_hwen); } static bool lm3533_readable_register(struct device *dev, unsigned int reg) -- cgit v0.10.2 From 54d34920ff6cefab0aa404fef841f9e6d3b6b9a1 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:28:49 +0900 Subject: mfd: intel_msic: Use devm_gpio_request_one() Use devm_gpio_request_one() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c index 1804331..5be3b5e 100644 --- a/drivers/mfd/intel_msic.c +++ b/drivers/mfd/intel_msic.c @@ -323,7 +323,8 @@ static int intel_msic_init_devices(struct intel_msic *msic) if (pdata->ocd) { unsigned gpio = pdata->ocd->gpio; - ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio"); + ret = devm_gpio_request_one(&pdev->dev, gpio, + GPIOF_IN, "ocd_gpio"); if (ret) { dev_err(&pdev->dev, "failed to register OCD GPIO\n"); return ret; @@ -332,7 +333,6 @@ static int intel_msic_init_devices(struct intel_msic *msic) ret = gpio_to_irq(gpio); if (ret < 0) { dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n"); - gpio_free(gpio); return ret; } @@ -359,8 +359,6 @@ static int intel_msic_init_devices(struct intel_msic *msic) fail: mfd_remove_devices(&pdev->dev); - if (pdata->ocd) - gpio_free(pdata->ocd->gpio); return ret; } @@ -368,12 +366,8 @@ fail: static void intel_msic_remove_devices(struct intel_msic *msic) { struct platform_device *pdev = msic->pdev; - struct intel_msic_platform_data *pdata = pdev->dev.platform_data; mfd_remove_devices(&pdev->dev); - - if (pdata->ocd) - gpio_free(pdata->ocd->gpio); } static int intel_msic_probe(struct platform_device *pdev) -- cgit v0.10.2 From 71f4b9cdfccfb82cff702fe61f4ace97a1dfb0e0 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:29:30 +0900 Subject: mfd: omap-usb-host: Use devm_gpio_request_one() Use devm_gpio_request_one() to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 4febc5c..35a96e7 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -437,11 +437,11 @@ static void omap_usbhs_init(struct device *dev) if (pdata->phy_reset) { if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_request_one(pdata->reset_gpio_port[0], + devm_gpio_request_one(dev, pdata->reset_gpio_port[0], GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_request_one(pdata->reset_gpio_port[1], + devm_gpio_request_one(dev, pdata->reset_gpio_port[1], GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); /* Hold the PHY in RESET for enough time till DIR is high */ @@ -492,21 +492,6 @@ static void omap_usbhs_init(struct device *dev) } } -static void omap_usbhs_deinit(struct device *dev) -{ - struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = omap->pdata; - - if (pdata->phy_reset) { - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_free(pdata->reset_gpio_port[0]); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_free(pdata->reset_gpio_port[1]); - } -} - - /** * usbhs_omap_probe - initialize TI-based HCDs * @@ -709,8 +694,6 @@ static int usbhs_omap_probe(struct platform_device *pdev) return 0; err_alloc: - omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); @@ -755,8 +738,6 @@ static int usbhs_omap_remove(struct platform_device *pdev) struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); int i; - omap_usbhs_deinit(&pdev->dev); - for (i = 0; i < omap->nports; i++) { if (!IS_ERR(omap->utmi_clk[i])) clk_put(omap->utmi_clk[i]); -- cgit v0.10.2 From 0287714b45c601789401e2ce28e45ea50925e419 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:29:53 +0900 Subject: mfd: aat2870: Use use devm_*() functions Use devm_*() functions to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c index f1beb49..dfdb0a2 100644 --- a/drivers/mfd/aat2870-core.c +++ b/drivers/mfd/aat2870-core.c @@ -367,12 +367,12 @@ static int aat2870_i2c_probe(struct i2c_client *client, int i, j; int ret = 0; - aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL); + aat2870 = devm_kzalloc(&client->dev, sizeof(struct aat2870_data), + GFP_KERNEL); if (!aat2870) { dev_err(&client->dev, "Failed to allocate memory for aat2870\n"); - ret = -ENOMEM; - goto out; + return -ENOMEM; } aat2870->dev = &client->dev; @@ -400,12 +400,12 @@ static int aat2870_i2c_probe(struct i2c_client *client, aat2870->init(aat2870); if (aat2870->en_pin >= 0) { - ret = gpio_request_one(aat2870->en_pin, GPIOF_OUT_INIT_HIGH, - "aat2870-en"); + ret = devm_gpio_request_one(&client->dev, aat2870->en_pin, + GPIOF_OUT_INIT_HIGH, "aat2870-en"); if (ret < 0) { dev_err(&client->dev, "Failed to request GPIO %d\n", aat2870->en_pin); - goto out_kfree; + return ret; } } @@ -436,11 +436,6 @@ static int aat2870_i2c_probe(struct i2c_client *client, out_disable: aat2870_disable(aat2870); - if (aat2870->en_pin >= 0) - gpio_free(aat2870->en_pin); -out_kfree: - kfree(aat2870); -out: return ret; } @@ -452,11 +447,8 @@ static int aat2870_i2c_remove(struct i2c_client *client) mfd_remove_devices(aat2870->dev); aat2870_disable(aat2870); - if (aat2870->en_pin >= 0) - gpio_free(aat2870->en_pin); if (aat2870->uninit) aat2870->uninit(aat2870); - kfree(aat2870); return 0; } -- cgit v0.10.2 From 990810b035b6f151085595c7dadf55fe23e65bbf Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:30:21 +0900 Subject: mfd: twl6040: Use devm_*() functions Use devm_*() functions to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index f361bf3..492ee2c 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c @@ -554,7 +554,7 @@ static int twl6040_probe(struct i2c_client *client, twl6040->supplies[0].supply = "vio"; twl6040->supplies[1].supply = "v2v1"; - ret = regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, + ret = devm_regulator_bulk_get(&client->dev, TWL6040_NUM_SUPPLIES, twl6040->supplies); if (ret != 0) { dev_err(&client->dev, "Failed to get supplies: %d\n", ret); @@ -564,7 +564,7 @@ static int twl6040_probe(struct i2c_client *client, ret = regulator_bulk_enable(TWL6040_NUM_SUPPLIES, twl6040->supplies); if (ret != 0) { dev_err(&client->dev, "Failed to enable supplies: %d\n", ret); - goto power_err; + goto regulator_get_err; } twl6040->dev = &client->dev; @@ -586,8 +586,8 @@ static int twl6040_probe(struct i2c_client *client, twl6040->audpwron = -EINVAL; if (gpio_is_valid(twl6040->audpwron)) { - ret = gpio_request_one(twl6040->audpwron, GPIOF_OUT_INIT_LOW, - "audpwron"); + ret = devm_gpio_request_one(&client->dev, twl6040->audpwron, + GPIOF_OUT_INIT_LOW, "audpwron"); if (ret) goto gpio_err; } @@ -596,14 +596,14 @@ static int twl6040_probe(struct i2c_client *client, IRQF_ONESHOT, 0, &twl6040_irq_chip, &twl6040->irq_data); if (ret < 0) - goto irq_init_err; + goto gpio_err; twl6040->irq_ready = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_READY); twl6040->irq_th = regmap_irq_get_virq(twl6040->irq_data, TWL6040_IRQ_TH); - ret = request_threaded_irq(twl6040->irq_ready, NULL, + ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_ready, NULL, twl6040_readyint_handler, IRQF_ONESHOT, "twl6040_irq_ready", twl6040); if (ret) { @@ -611,7 +611,7 @@ static int twl6040_probe(struct i2c_client *client, goto readyirq_err; } - ret = request_threaded_irq(twl6040->irq_th, NULL, + ret = devm_request_threaded_irq(twl6040->dev, twl6040->irq_th, NULL, twl6040_thint_handler, IRQF_ONESHOT, "twl6040_irq_th", twl6040); if (ret) { @@ -681,18 +681,13 @@ static int twl6040_probe(struct i2c_client *client, return 0; mfd_err: - free_irq(twl6040->irq_th, twl6040); + devm_free_irq(&client->dev, twl6040->irq_th, twl6040); thirq_err: - free_irq(twl6040->irq_ready, twl6040); + devm_free_irq(&client->dev, twl6040->irq_ready, twl6040); readyirq_err: regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); -irq_init_err: - if (gpio_is_valid(twl6040->audpwron)) - gpio_free(twl6040->audpwron); gpio_err: regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); -power_err: - regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); regulator_get_err: i2c_set_clientdata(client, NULL); err: @@ -706,18 +701,14 @@ static int twl6040_remove(struct i2c_client *client) if (twl6040->power_count) twl6040_power(twl6040, 0); - if (gpio_is_valid(twl6040->audpwron)) - gpio_free(twl6040->audpwron); - - free_irq(twl6040->irq_ready, twl6040); - free_irq(twl6040->irq_th, twl6040); + devm_free_irq(&client->dev, twl6040->irq_ready, twl6040); + devm_free_irq(&client->dev, twl6040->irq_th, twl6040); regmap_del_irq_chip(twl6040->irq, twl6040->irq_data); mfd_remove_devices(&client->dev); i2c_set_clientdata(client, NULL); regulator_bulk_disable(TWL6040_NUM_SUPPLIES, twl6040->supplies); - regulator_bulk_free(TWL6040_NUM_SUPPLIES, twl6040->supplies); return 0; } -- cgit v0.10.2 From 1ba895e0487810ee44eb08585e6810ad66159988 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:30:55 +0900 Subject: mfd: ezx-pcap: Use devm_*() functions Use devm_*() functions to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index b7a61f0..f83cc3d 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -403,7 +403,7 @@ static int ezx_pcap_remove(struct spi_device *spi) /* cleanup ADC */ adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); - free_irq(adc_irq, pcap); + devm_free_irq(&spi->dev, adc_irq, pcap); mutex_lock(&pcap->adc_mutex); for (i = 0; i < PCAP_ADC_MAXQ; i++) kfree(pcap->adc_queue[i]); @@ -415,8 +415,6 @@ static int ezx_pcap_remove(struct spi_device *spi) destroy_workqueue(pcap->workqueue); - kfree(pcap); - return 0; } @@ -431,7 +429,7 @@ static int ezx_pcap_probe(struct spi_device *spi) if (!pdata) goto ret; - pcap = kzalloc(sizeof(*pcap), GFP_KERNEL); + pcap = devm_kzalloc(&spi->dev, sizeof(*pcap), GFP_KERNEL); if (!pcap) { ret = -ENOMEM; goto ret; @@ -448,7 +446,7 @@ static int ezx_pcap_probe(struct spi_device *spi) spi->mode = SPI_MODE_0 | (pdata->config & PCAP_CS_AH ? SPI_CS_HIGH : 0); ret = spi_setup(spi); if (ret) - goto free_pcap; + goto ret; pcap->spi = spi; @@ -458,7 +456,7 @@ static int ezx_pcap_probe(struct spi_device *spi) if (!pcap->workqueue) { ret = -ENOMEM; dev_err(&spi->dev, "can't create pcap thread\n"); - goto free_pcap; + goto ret; } /* redirect interrupts to AP, except adcdone2 */ @@ -491,7 +489,8 @@ static int ezx_pcap_probe(struct spi_device *spi) adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? PCAP_IRQ_ADCDONE2 : PCAP_IRQ_ADCDONE); - ret = request_irq(adc_irq, pcap_adc_irq, 0, "ADC", pcap); + ret = devm_request_irq(&spi->dev, adc_irq, pcap_adc_irq, 0, "ADC", + pcap); if (ret) goto free_irqchip; @@ -511,14 +510,12 @@ static int ezx_pcap_probe(struct spi_device *spi) remove_subdevs: device_for_each_child(&spi->dev, NULL, pcap_remove_subdev); /* free_adc: */ - free_irq(adc_irq, pcap); + devm_free_irq(&spi->dev, adc_irq, pcap); free_irqchip: for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) irq_set_chip_and_handler(i, NULL, NULL); /* destroy_workqueue: */ destroy_workqueue(pcap->workqueue); -free_pcap: - kfree(pcap); ret: return ret; } -- cgit v0.10.2 From aa4dcf5bec80501933c6935f50f438594823c614 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:31:26 +0900 Subject: mfd: da903x: Use devm_*() functions Use devm_*() functions to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/da903x.c b/drivers/mfd/da903x.c index 05176cd..f1a316e 100644 --- a/drivers/mfd/da903x.c +++ b/drivers/mfd/da903x.c @@ -499,7 +499,8 @@ static int da903x_probe(struct i2c_client *client, unsigned int tmp; int ret; - chip = kzalloc(sizeof(struct da903x_chip), GFP_KERNEL); + chip = devm_kzalloc(&client->dev, sizeof(struct da903x_chip), + GFP_KERNEL); if (chip == NULL) return -ENOMEM; @@ -515,33 +516,27 @@ static int da903x_probe(struct i2c_client *client, ret = chip->ops->init_chip(chip); if (ret) - goto out_free_chip; + return ret; /* mask and clear all IRQs */ chip->events_mask = 0xffffffff; chip->ops->mask_events(chip, chip->events_mask); chip->ops->read_events(chip, &tmp); - ret = request_irq(client->irq, da903x_irq_handler, + ret = devm_request_irq(&client->dev, client->irq, da903x_irq_handler, IRQF_TRIGGER_FALLING, "da903x", chip); if (ret) { dev_err(&client->dev, "failed to request irq %d\n", client->irq); - goto out_free_chip; + return ret; } ret = da903x_add_subdevs(chip, pdata); if (ret) - goto out_free_irq; + return ret; return 0; - -out_free_irq: - free_irq(client->irq, chip); -out_free_chip: - kfree(chip); - return ret; } static int da903x_remove(struct i2c_client *client) @@ -549,8 +544,6 @@ static int da903x_remove(struct i2c_client *client) struct da903x_chip *chip = i2c_get_clientdata(client); da903x_remove_subdevs(chip); - free_irq(client->irq, chip); - kfree(chip); return 0; } -- cgit v0.10.2 From 1383e00f79a7bd6333083a12b43481b6bf2bdcb4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 20 Feb 2013 18:31:52 +0900 Subject: mfd: tc3589x: Use devm_*() functions Use devm_*() functions to make cleanup paths more simple. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c index ecc092c..4cb92bb 100644 --- a/drivers/mfd/tc3589x.c +++ b/drivers/mfd/tc3589x.c @@ -350,7 +350,8 @@ static int tc3589x_probe(struct i2c_client *i2c, | I2C_FUNC_SMBUS_I2C_BLOCK)) return -EIO; - tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); + tc3589x = devm_kzalloc(&i2c->dev, sizeof(struct tc3589x), + GFP_KERNEL); if (!tc3589x) return -ENOMEM; @@ -366,33 +367,27 @@ static int tc3589x_probe(struct i2c_client *i2c, ret = tc3589x_chip_init(tc3589x); if (ret) - goto out_free; + return ret; ret = tc3589x_irq_init(tc3589x, np); if (ret) - goto out_free; + return ret; ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "tc3589x", tc3589x); if (ret) { dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); - goto out_free; + return ret; } ret = tc3589x_device_init(tc3589x); if (ret) { dev_err(tc3589x->dev, "failed to add child devices\n"); - goto out_freeirq; + return ret; } return 0; - -out_freeirq: - free_irq(tc3589x->i2c->irq, tc3589x); -out_free: - kfree(tc3589x); - return ret; } static int tc3589x_remove(struct i2c_client *client) @@ -401,10 +396,6 @@ static int tc3589x_remove(struct i2c_client *client) mfd_remove_devices(tc3589x->dev); - free_irq(tc3589x->i2c->irq, tc3589x); - - kfree(tc3589x); - return 0; } -- cgit v0.10.2 From 895ce2d891a094aeb7e9e3f3ec6a0e4c8bf038aa Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Fri, 22 Feb 2013 10:39:59 +0100 Subject: mfd: sta2x11: Build warning fix This driver cannot be a module, so "remove" is never called. The mishap is mine, and back then there was no warning due to __devexit(). Signed-off-by: Alessandro Rubini Acked-by: Giancarlo Asnaghi Acked-by: Davide Ciminaghi Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/sta2x11-mfd.c b/drivers/mfd/sta2x11-mfd.c index 9bd3316..d70a3430 100644 --- a/drivers/mfd/sta2x11-mfd.c +++ b/drivers/mfd/sta2x11-mfd.c @@ -98,17 +98,6 @@ static int sta2x11_mfd_add(struct pci_dev *pdev, gfp_t flags) return 0; } -static int mfd_remove(struct pci_dev *pdev) -{ - struct sta2x11_mfd *mfd = sta2x11_mfd_find(pdev); - - if (!mfd) - return -ENODEV; - list_del(&mfd->list); - kfree(mfd); - return 0; -} - /* This function is exported and is not expected to fail */ u32 __sta2x11_mfd_mask(struct pci_dev *pdev, u32 reg, u32 mask, u32 val, enum sta2x11_mfd_plat_dev index) -- cgit v0.10.2 From 654003e98213991e4d534487907baad2afd9828d Mon Sep 17 00:00:00 2001 From: Ian Lartey Date: Fri, 22 Mar 2013 14:55:12 +0000 Subject: mfd: palmas: is_palmas_charger needed by multiple drivers is_palmas_charger checks for the presence of charging functionality in the device Signed-off-by: Graeme Gregory Signed-off-by: Ian Lartey Acked-by: Laxman Dewangani Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 3bbda22..4a066d0 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -1,9 +1,10 @@ /* * TI Palmas * - * Copyright 2011 Texas Instruments Inc. + * Copyright 2011-2013 Texas Instruments Inc. * * Author: Graeme Gregory + * Author: Ian Lartey * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -22,6 +23,15 @@ #define PALMAS_NUM_CLIENTS 3 +/* The ID_REVISION NUMBERS */ +#define PALMAS_CHIP_OLD_ID 0x0000 +#define PALMAS_CHIP_ID 0xC035 +#define PALMAS_CHIP_CHARGER_ID 0xC036 + +#define is_palmas(a) (((a) == PALMAS_CHIP_OLD_ID) || \ + ((a) == PALMAS_CHIP_ID)) +#define is_palmas_charger(a) ((a) == PALMAS_CHIP_CHARGER_ID) + struct palmas_pmic; struct palmas_gpadc; struct palmas_resource; -- cgit v0.10.2 From 751391c74f3d41a8d5c5dd5100385f723660c368 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 12 Mar 2013 18:08:06 -0400 Subject: mfd: tps65090: Fix enum in header file The enum is missing the definition for the first bit, which makes all the rest off by one. Add definition for the TPS65090_IRQ_INTERRUPT bit which at 0. Signed-off-by: Rhyland Klein Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6694cf4..9ce231f 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -27,6 +27,7 @@ /* TPS65090 IRQs */ enum { + TPS65090_IRQ_INTERRUPT, TPS65090_IRQ_VAC_STATUS_CHANGE, TPS65090_IRQ_VSYS_STATUS_CHANGE, TPS65090_IRQ_BAT_STATUS_CHANGE, -- cgit v0.10.2 From 36c772e31fa72006e80d12d1a94db2a326ba00a5 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Tue, 12 Mar 2013 18:08:07 -0400 Subject: mfd: tps65090: Add resources for charger Add irq resources to pass to the charger mfd sub dev so the charger can listen for interrupts. Signed-off-by: Rhyland Klein Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 98edb5be..88846ae 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -56,12 +56,22 @@ #define TPS65090_INT2_MASK_OVERLOAD_FET6 6 #define TPS65090_INT2_MASK_OVERLOAD_FET7 7 +static struct resource charger_resources[] = { + { + .start = TPS65090_IRQ_VAC_STATUS_CHANGE, + .end = TPS65090_IRQ_VAC_STATUS_CHANGE, + .flags = IORESOURCE_IRQ, + } +}; + static struct mfd_cell tps65090s[] = { { .name = "tps65090-pmic", }, { .name = "tps65090-charger", + .num_resources = ARRAY_SIZE(charger_resources), + .resources = &charger_resources[0], }, }; -- cgit v0.10.2 From c4fbec3c6b8d61366ce377cebd3b25ce6c41a0b0 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 19 Mar 2013 14:28:20 +0530 Subject: mfd: palmas: Initialise client->of_node for dummy created client Palmas device have three different i2c addresses. The device creates the two new dummy i2c clients for accessing the register by using primary client adapter. This new dummy i2c client have their of_node as NULL. The dummy i2c client is used for registering interrupt and on this, it creates irq domain handle. This created irq domain handle has their of_node as NULL. Now when any child of this device is registered through the DT as follows: palmas: tps65913@58 { :::::::::::::::::: #interrupt-cells = <2>; interrupt-controller; palmas_rtc { compatible = "ti,palmas-rtc"; interrupt-parent = <&palmas>; interrupts = <8 0>; }; ::::::::::;;; }; And child driver (palam-rtc in this case) get their irq number as irq = platform_get_irq(pdev, 0); The returned irq number is error in this case. The reason is that the created irq_domain handle for the palmas interrupt does not have valid node and so matching of node fails with palmas node. Hence initialising the newly dummy created client->of_node with the primary clients of_node so that irq_domain handle have proper of_node for matching. Signed-off-by: Laxman Dewangan Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/palmas.c b/drivers/mfd/palmas.c index c01b69a5..53e9fe6 100644 --- a/drivers/mfd/palmas.c +++ b/drivers/mfd/palmas.c @@ -349,6 +349,7 @@ static int palmas_i2c_probe(struct i2c_client *i2c, ret = -ENOMEM; goto err; } + palmas->i2c_clients[i]->dev.of_node = of_node_get(node); } palmas->regmap[i] = devm_regmap_init_i2c(palmas->i2c_clients[i], &palmas_regmap_config[i]); -- cgit v0.10.2 From 57e1b48805a92304e4f50ebf5b885e627731a444 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Mar 2013 14:28:13 +0800 Subject: mfd: arizona: Define additional FLL control registers Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/arizona/registers.h b/include/linux/mfd/arizona/registers.h index 3403551..a61ce90 100644 --- a/include/linux/mfd/arizona/registers.h +++ b/include/linux/mfd/arizona/registers.h @@ -85,12 +85,14 @@ #define ARIZONA_FLL1_CONTROL_6 0x176 #define ARIZONA_FLL1_LOOP_FILTER_TEST_1 0x177 #define ARIZONA_FLL1_NCO_TEST_0 0x178 +#define ARIZONA_FLL1_CONTROL_7 0x179 #define ARIZONA_FLL1_SYNCHRONISER_1 0x181 #define ARIZONA_FLL1_SYNCHRONISER_2 0x182 #define ARIZONA_FLL1_SYNCHRONISER_3 0x183 #define ARIZONA_FLL1_SYNCHRONISER_4 0x184 #define ARIZONA_FLL1_SYNCHRONISER_5 0x185 #define ARIZONA_FLL1_SYNCHRONISER_6 0x186 +#define ARIZONA_FLL1_SYNCHRONISER_7 0x187 #define ARIZONA_FLL1_SPREAD_SPECTRUM 0x189 #define ARIZONA_FLL1_GPIO_CLOCK 0x18A #define ARIZONA_FLL2_CONTROL_1 0x191 @@ -101,12 +103,14 @@ #define ARIZONA_FLL2_CONTROL_6 0x196 #define ARIZONA_FLL2_LOOP_FILTER_TEST_1 0x197 #define ARIZONA_FLL2_NCO_TEST_0 0x198 +#define ARIZONA_FLL2_CONTROL_7 0x199 #define ARIZONA_FLL2_SYNCHRONISER_1 0x1A1 #define ARIZONA_FLL2_SYNCHRONISER_2 0x1A2 #define ARIZONA_FLL2_SYNCHRONISER_3 0x1A3 #define ARIZONA_FLL2_SYNCHRONISER_4 0x1A4 #define ARIZONA_FLL2_SYNCHRONISER_5 0x1A5 #define ARIZONA_FLL2_SYNCHRONISER_6 0x1A6 +#define ARIZONA_FLL2_SYNCHRONISER_7 0x1A7 #define ARIZONA_FLL2_SPREAD_SPECTRUM 0x1A9 #define ARIZONA_FLL2_GPIO_CLOCK 0x1AA #define ARIZONA_MIC_CHARGE_PUMP_1 0x200 @@ -1678,6 +1682,13 @@ #define ARIZONA_FLL1_FRC_INTEG_VAL_WIDTH 12 /* FLL1_FRC_INTEG_VAL - [11:0] */ /* + * R377 (0x179) - FLL1 Control 7 + */ +#define ARIZONA_FLL1_GAIN_MASK 0x003c /* FLL1_GAIN */ +#define ARIZONA_FLL1_GAIN_SHIFT 2 /* FLL1_GAIN */ +#define ARIZONA_FLL1_GAIN_WIDTH 4 /* FLL1_GAIN */ + +/* * R385 (0x181) - FLL1 Synchroniser 1 */ #define ARIZONA_FLL1_SYNC_ENA 0x0001 /* FLL1_SYNC_ENA */ @@ -1724,6 +1735,17 @@ #define ARIZONA_FLL1_CLK_SYNC_SRC_WIDTH 4 /* FLL1_CLK_SYNC_SRC - [3:0] */ /* + * R391 (0x187) - FLL1 Synchroniser 7 + */ +#define ARIZONA_FLL1_SYNC_GAIN_MASK 0x003c /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_GAIN_SHIFT 2 /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_GAIN_WIDTH 4 /* FLL1_SYNC_GAIN */ +#define ARIZONA_FLL1_SYNC_BW 0x0001 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_MASK 0x0001 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_SHIFT 0 /* FLL1_SYNC_BW */ +#define ARIZONA_FLL1_SYNC_BW_WIDTH 1 /* FLL1_SYNC_BW */ + +/* * R393 (0x189) - FLL1 Spread Spectrum */ #define ARIZONA_FLL1_SS_AMPL_MASK 0x0030 /* FLL1_SS_AMPL - [5:4] */ @@ -1816,6 +1838,13 @@ #define ARIZONA_FLL2_FRC_INTEG_VAL_WIDTH 12 /* FLL2_FRC_INTEG_VAL - [11:0] */ /* + * R409 (0x199) - FLL2 Control 7 + */ +#define ARIZONA_FLL2_GAIN_MASK 0x003c /* FLL2_GAIN */ +#define ARIZONA_FLL2_GAIN_SHIFT 2 /* FLL2_GAIN */ +#define ARIZONA_FLL2_GAIN_WIDTH 4 /* FLL2_GAIN */ + +/* * R417 (0x1A1) - FLL2 Synchroniser 1 */ #define ARIZONA_FLL2_SYNC_ENA 0x0001 /* FLL2_SYNC_ENA */ @@ -1862,6 +1891,17 @@ #define ARIZONA_FLL2_CLK_SYNC_SRC_WIDTH 4 /* FLL2_CLK_SYNC_SRC - [3:0] */ /* + * R423 (0x1A7) - FLL2 Synchroniser 7 + */ +#define ARIZONA_FLL2_SYNC_GAIN_MASK 0x003c /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_GAIN_SHIFT 2 /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_GAIN_WIDTH 4 /* FLL2_SYNC_GAIN */ +#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_MASK 0x0001 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_SHIFT 0 /* FLL2_SYNC_BW */ +#define ARIZONA_FLL2_SYNC_BW_WIDTH 1 /* FLL2_SYNC_BW */ + +/* * R425 (0x1A9) - FLL2 Spread Spectrum */ #define ARIZONA_FLL2_SS_AMPL_MASK 0x0030 /* FLL2_SS_AMPL - [5:4] */ -- cgit v0.10.2 From b5bdc0463e451dff90c1f0b2f9ca560364d43d35 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 6 Mar 2013 14:28:14 +0800 Subject: mfd: wm5102: Map in additional FLL control registers Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index ad1cc3b..268664d 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -314,12 +314,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000176, 0x0000 }, /* R374 - FLL1 Control 6 */ { 0x00000177, 0x0181 }, /* R375 - FLL1 Loop Filter Test 1 */ { 0x00000178, 0x0000 }, /* R376 - FLL1 NCO Test 0 */ + { 0x00000179, 0x0000 }, /* R377 - FLL1 Control 7 */ { 0x00000181, 0x0000 }, /* R385 - FLL1 Synchroniser 1 */ { 0x00000182, 0x0000 }, /* R386 - FLL1 Synchroniser 2 */ { 0x00000183, 0x0000 }, /* R387 - FLL1 Synchroniser 3 */ { 0x00000184, 0x0000 }, /* R388 - FLL1 Synchroniser 4 */ { 0x00000185, 0x0000 }, /* R389 - FLL1 Synchroniser 5 */ { 0x00000186, 0x0000 }, /* R390 - FLL1 Synchroniser 6 */ + { 0x00000187, 0x0001 }, /* R391 - FLL1 Synchroniser 7 */ { 0x00000189, 0x0000 }, /* R393 - FLL1 Spread Spectrum */ { 0x0000018A, 0x0004 }, /* R394 - FLL1 GPIO Clock */ { 0x00000191, 0x0000 }, /* R401 - FLL2 Control 1 */ @@ -330,12 +332,14 @@ static const struct reg_default wm5102_reg_default[] = { { 0x00000196, 0x0000 }, /* R406 - FLL2 Control 6 */ { 0x00000197, 0x0000 }, /* R407 - FLL2 Loop Filter Test 1 */ { 0x00000198, 0x0000 }, /* R408 - FLL2 NCO Test 0 */ + { 0x00000199, 0x0000 }, /* R409 - FLL2 Control 7 */ { 0x000001A1, 0x0000 }, /* R417 - FLL2 Synchroniser 1 */ { 0x000001A2, 0x0000 }, /* R418 - FLL2 Synchroniser 2 */ { 0x000001A3, 0x0000 }, /* R419 - FLL2 Synchroniser 3 */ { 0x000001A4, 0x0000 }, /* R420 - FLL2 Synchroniser 4 */ { 0x000001A5, 0x0000 }, /* R421 - FLL2 Synchroniser 5 */ { 0x000001A6, 0x0000 }, /* R422 - FLL2 Synchroniser 6 */ + { 0x000001A7, 0x0001 }, /* R423 - FLL2 Synchroniser 7 */ { 0x000001A9, 0x0000 }, /* R425 - FLL2 Spread Spectrum */ { 0x000001AA, 0x0004 }, /* R426 - FLL2 GPIO Clock */ { 0x00000200, 0x0006 }, /* R512 - Mic Charge Pump 1 */ @@ -1075,12 +1079,14 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL1_CONTROL_6: case ARIZONA_FLL1_LOOP_FILTER_TEST_1: case ARIZONA_FLL1_NCO_TEST_0: + case ARIZONA_FLL1_CONTROL_7: case ARIZONA_FLL1_SYNCHRONISER_1: case ARIZONA_FLL1_SYNCHRONISER_2: case ARIZONA_FLL1_SYNCHRONISER_3: case ARIZONA_FLL1_SYNCHRONISER_4: case ARIZONA_FLL1_SYNCHRONISER_5: case ARIZONA_FLL1_SYNCHRONISER_6: + case ARIZONA_FLL1_SYNCHRONISER_7: case ARIZONA_FLL1_SPREAD_SPECTRUM: case ARIZONA_FLL1_GPIO_CLOCK: case ARIZONA_FLL2_CONTROL_1: @@ -1091,12 +1097,14 @@ static bool wm5102_readable_register(struct device *dev, unsigned int reg) case ARIZONA_FLL2_CONTROL_6: case ARIZONA_FLL2_LOOP_FILTER_TEST_1: case ARIZONA_FLL2_NCO_TEST_0: + case ARIZONA_FLL2_CONTROL_7: case ARIZONA_FLL2_SYNCHRONISER_1: case ARIZONA_FLL2_SYNCHRONISER_2: case ARIZONA_FLL2_SYNCHRONISER_3: case ARIZONA_FLL2_SYNCHRONISER_4: case ARIZONA_FLL2_SYNCHRONISER_5: case ARIZONA_FLL2_SYNCHRONISER_6: + case ARIZONA_FLL2_SYNCHRONISER_7: case ARIZONA_FLL2_SPREAD_SPECTRUM: case ARIZONA_FLL2_GPIO_CLOCK: case ARIZONA_MIC_CHARGE_PUMP_1: -- cgit v0.10.2 From f2d614c4beb6d1588e515dd64e7e44ba5510404e Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sat, 9 Mar 2013 17:32:34 +0100 Subject: mfd: da9055: Drop "select PMIC_DA9055" The Kconfig entry for DA9055 PMIC Support selects PMIC_DA9055. That was probably inspired by the similar select statement in the entry for DA9052/53 PMIC with I2C. But the DA9055 PMIC only comes in an I2C variant and its driver doesn't need a separate Kconfig symbol for shared code. In any case, this select can be dropped as PMIC_DA9055 doesn't exist. Signed-off-by: Paul Bolle Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index d5b9b05..cc7f6a2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -516,7 +516,6 @@ config MFD_DA9055 bool "Dialog Semiconductor DA9055 PMIC Support" select REGMAP_I2C select REGMAP_IRQ - select PMIC_DA9055 select MFD_CORE depends on I2C=y && GENERIC_HARDIRQS help -- cgit v0.10.2 From 662e469e91d85438444703aa60dc35ac27b06773 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 8 Jan 2013 16:01:52 +0200 Subject: mfd: omap-usb-host: Update nports in platform_data EHCI driver would need to know the number of ports available on the platform. We set the nports parameter of platform_data based on IP version if it was not already provided. Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 35a96e7..2520fac 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -560,6 +560,7 @@ static int usbhs_omap_probe(struct platform_device *pdev) omap->usbhs_rev, omap->nports); break; } + pdata->nports = omap->nports; } i = sizeof(struct clk *) * omap->nports; -- cgit v0.10.2 From 9f4a3ece05a6c2f2dc5e4967602e9927addab97e Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 29 Jan 2013 15:00:03 +0200 Subject: mfd: omap-usb-tll: Move configuration code to omap_tll_init() This is because we want to get rid of platform_data usage from probe(). The only information we need is PORT_MODE, and this can be supplied to us by the user (i.e. omap-usb-host.c). We also move channel clock management from runtime PM handlers into omap_tll_enable/disable(). Signed-off-by: Roger Quadros Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 2520fac..eb5db28 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -278,7 +278,7 @@ static int usbhs_runtime_resume(struct device *dev) dev_dbg(dev, "usbhs_runtime_resume\n"); - omap_tll_enable(); + omap_tll_enable(pdata); if (!IS_ERR(omap->ehci_logic_fck)) clk_enable(omap->ehci_logic_fck); @@ -353,7 +353,7 @@ static int usbhs_runtime_suspend(struct device *dev) if (!IS_ERR(omap->ehci_logic_fck)) clk_disable(omap->ehci_logic_fck); - omap_tll_disable(); + omap_tll_disable(pdata); return 0; } @@ -527,6 +527,9 @@ static int usbhs_omap_probe(struct platform_device *pdev) omap->pdata = pdata; + /* Initialize the TLL subsystem */ + omap_tll_init(pdata); + pm_runtime_enable(dev); platform_set_drvdata(pdev, omap); diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 0aef1a7..f7d2568 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -1,8 +1,9 @@ /** * omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com * Author: Keshava Munegowda + * Author: Roger Quadros * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 of @@ -105,8 +106,8 @@ struct usbtll_omap { int nch; /* num. of channels */ - struct usbhs_omap_platform_data *pdata; struct clk **ch_clk; + void __iomem *base; }; /*-------------------------------------------------------------------------*/ @@ -210,14 +211,10 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) static int usbtll_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbhs_omap_platform_data *pdata = dev->platform_data; - void __iomem *base; struct resource *res; struct usbtll_omap *tll; - unsigned reg; int ret = 0; int i, ver; - bool needs_tll; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); @@ -227,16 +224,9 @@ static int usbtll_omap_probe(struct platform_device *pdev) return -ENOMEM; } - if (!pdata) { - dev_err(dev, "Platform data missing\n"); - return -ENODEV; - } - - tll->pdata = pdata; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_request_and_ioremap(dev, res); - if (!base) { + tll->base = devm_request_and_ioremap(dev, res); + if (!tll->base) { ret = -EADDRNOTAVAIL; dev_err(dev, "Resource request/ioremap failed:%d\n", ret); return ret; @@ -246,7 +236,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - ver = usbtll_read(base, OMAP_USBTLL_REVISION); + ver = usbtll_read(tll->base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: case OMAP_USBTLL_REV4: @@ -283,11 +273,77 @@ static int usbtll_omap_probe(struct platform_device *pdev) dev_dbg(dev, "can't get clock : %s\n", clkname); } + pm_runtime_put_sync(dev); + /* only after this can omap_tll_enable/disable work */ + spin_lock(&tll_lock); + tll_dev = dev; + spin_unlock(&tll_lock); + + return 0; + +err_clk_alloc: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + return ret; +} + +/** + * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usbtll_omap_probe(). + */ +static int usbtll_omap_remove(struct platform_device *pdev) +{ + struct usbtll_omap *tll = platform_get_drvdata(pdev); + int i; + + spin_lock(&tll_lock); + tll_dev = NULL; + spin_unlock(&tll_lock); + + for (i = 0; i < tll->nch; i++) + if (!IS_ERR(tll->ch_clk[i])) + clk_put(tll->ch_clk[i]); + + pm_runtime_disable(&pdev->dev); + return 0; +} + +static struct platform_driver usbtll_omap_driver = { + .driver = { + .name = (char *)usbtll_driver_name, + .owner = THIS_MODULE, + }, + .probe = usbtll_omap_probe, + .remove = usbtll_omap_remove, +}; + +int omap_tll_init(struct usbhs_omap_platform_data *pdata) +{ + int i; + bool needs_tll; + unsigned reg; + struct usbtll_omap *tll; + + spin_lock(&tll_lock); + + if (!tll_dev) { + spin_unlock(&tll_lock); + return -ENODEV; + } + + tll = dev_get_drvdata(tll_dev); + needs_tll = false; for (i = 0; i < tll->nch; i++) needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); + pm_runtime_get_sync(tll_dev); + if (needs_tll) { + void __iomem *base = tll->base; /* Program Common TLL register */ reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); @@ -336,51 +392,29 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } - pm_runtime_put_sync(dev); - /* only after this can omap_tll_enable/disable work */ - spin_lock(&tll_lock); - tll_dev = dev; + pm_runtime_put_sync(tll_dev); + spin_unlock(&tll_lock); return 0; - -err_clk_alloc: - pm_runtime_put_sync(dev); - pm_runtime_disable(dev); - - return ret; } +EXPORT_SYMBOL_GPL(omap_tll_init); -/** - * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs - * @pdev: USB Host Controller being removed - * - * Reverses the effect of usbtll_omap_probe(). - */ -static int usbtll_omap_remove(struct platform_device *pdev) +int omap_tll_enable(struct usbhs_omap_platform_data *pdata) { - struct usbtll_omap *tll = platform_get_drvdata(pdev); int i; + struct usbtll_omap *tll; spin_lock(&tll_lock); - tll_dev = NULL; - spin_unlock(&tll_lock); - - for (i = 0; i < tll->nch; i++) - if (!IS_ERR(tll->ch_clk[i])) - clk_put(tll->ch_clk[i]); - pm_runtime_disable(&pdev->dev); - return 0; -} + if (!tll_dev) { + spin_unlock(&tll_lock); + return -ENODEV; + } -static int usbtll_runtime_resume(struct device *dev) -{ - struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = tll->pdata; - int i; + tll = dev_get_drvdata(tll_dev); - dev_dbg(dev, "usbtll_runtime_resume\n"); + pm_runtime_get_sync(tll_dev); for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { @@ -391,22 +425,31 @@ static int usbtll_runtime_resume(struct device *dev) r = clk_enable(tll->ch_clk[i]); if (r) { - dev_err(dev, + dev_err(tll_dev, "Error enabling ch %d clock: %d\n", i, r); } } } + spin_unlock(&tll_lock); + return 0; } +EXPORT_SYMBOL_GPL(omap_tll_enable); -static int usbtll_runtime_suspend(struct device *dev) +int omap_tll_disable(struct usbhs_omap_platform_data *pdata) { - struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = tll->pdata; int i; + struct usbtll_omap *tll; + + spin_lock(&tll_lock); + + if (!tll_dev) { + spin_unlock(&tll_lock); + return -ENODEV; + } - dev_dbg(dev, "usbtll_runtime_suspend\n"); + tll = dev_get_drvdata(tll_dev); for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { @@ -415,60 +458,11 @@ static int usbtll_runtime_suspend(struct device *dev) } } - return 0; -} - -static const struct dev_pm_ops usbtllomap_dev_pm_ops = { - SET_RUNTIME_PM_OPS(usbtll_runtime_suspend, - usbtll_runtime_resume, - NULL) -}; - -static struct platform_driver usbtll_omap_driver = { - .driver = { - .name = (char *)usbtll_driver_name, - .owner = THIS_MODULE, - .pm = &usbtllomap_dev_pm_ops, - }, - .probe = usbtll_omap_probe, - .remove = usbtll_omap_remove, -}; - -int omap_tll_enable(void) -{ - int ret; - - spin_lock(&tll_lock); - - if (!tll_dev) { - pr_err("%s: OMAP USB TLL not initialized\n", __func__); - ret = -ENODEV; - } else { - ret = pm_runtime_get_sync(tll_dev); - } + pm_runtime_put_sync(tll_dev); spin_unlock(&tll_lock); - return ret; -} -EXPORT_SYMBOL_GPL(omap_tll_enable); - -int omap_tll_disable(void) -{ - int ret; - - spin_lock(&tll_lock); - - if (!tll_dev) { - pr_err("%s: OMAP USB TLL not initialized\n", __func__); - ret = -ENODEV; - } else { - ret = pm_runtime_put_sync(tll_dev); - } - - spin_unlock(&tll_lock); - - return ret; + return 0; } EXPORT_SYMBOL_GPL(omap_tll_disable); diff --git a/drivers/mfd/omap-usb.h b/drivers/mfd/omap-usb.h index 972aa96..2a508b6 100644 --- a/drivers/mfd/omap-usb.h +++ b/drivers/mfd/omap-usb.h @@ -1,2 +1,3 @@ -extern int omap_tll_enable(void); -extern int omap_tll_disable(void); +extern int omap_tll_init(struct usbhs_omap_platform_data *pdata); +extern int omap_tll_enable(struct usbhs_omap_platform_data *pdata); +extern int omap_tll_disable(struct usbhs_omap_platform_data *pdata); -- cgit v0.10.2 From 48130b8f5c6d77608257dc50bf914bd77f0b41a0 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 29 Jan 2013 15:20:55 +0200 Subject: mfd: omap-usb-tll: Add device tree support and binding information Allows the OMAP USB TLL module to be specified via device tree. Signed-off-by: Roger Quadros Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt new file mode 100644 index 0000000..62fe697 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/omap-usb-tll.txt @@ -0,0 +1,17 @@ +OMAP HS USB Host TLL (Transceiver-Less Interface) + +Required properties: + +- compatible : should be "ti,usbhs-tll" +- reg : should contain one register range i.e. start and length +- interrupts : should contain the TLL module's interrupt +- ti,hwmod : must contain "usb_tll_hs" + +Example: + + usbhstll: usbhstll@4a062000 { + compatible = "ti,usbhs-tll"; + reg = <0x4a062000 0x1000>; + interrupts = <78>; + ti,hwmods = "usb_tll_hs"; + }; diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index f7d2568..8f4d5a1 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -28,6 +28,7 @@ #include #include #include +#include #define USBTLL_DRIVER_NAME "usbhs_tll" @@ -311,10 +312,18 @@ static int usbtll_omap_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id usbtll_omap_dt_ids[] = { + { .compatible = "ti,usbhs-tll" }, + { } +}; + +MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids); + static struct platform_driver usbtll_omap_driver = { .driver = { .name = (char *)usbtll_driver_name, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(usbtll_omap_dt_ids), }, .probe = usbtll_omap_probe, .remove = usbtll_omap_remove, @@ -467,6 +476,7 @@ int omap_tll_disable(struct usbhs_omap_platform_data *pdata) EXPORT_SYMBOL_GPL(omap_tll_disable); MODULE_AUTHOR("Keshava Munegowda "); +MODULE_AUTHOR("Roger Quadros "); MODULE_ALIAS("platform:" USBHS_DRIVER_NAME); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("usb tll driver for TI OMAP EHCI and OHCI controllers"); -- cgit v0.10.2 From 3cec5f4e7536de1c2c1e9ae6be9bf2d62c783262 Mon Sep 17 00:00:00 2001 From: Ashish Jangam Date: Mon, 18 Mar 2013 14:42:38 +0530 Subject: mfd: da9055: Change irq state to default This patch changes the irq state from high to the now default low. Because the default PMIC irq state of DA9055 got changed from high to low. This change should not affect any of the existing users since all of them use active low state. This patch has been tested on smdkv6410. Signed-off-by: Ashish Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c index f56a1a9..49cb23d 100644 --- a/drivers/mfd/da9055-core.c +++ b/drivers/mfd/da9055-core.c @@ -391,7 +391,7 @@ int da9055_device_init(struct da9055 *da9055) da9055->irq_base = pdata->irq_base; ret = regmap_add_irq_chip(da9055->regmap, da9055->chip_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, da9055->irq_base, &da9055_regmap_irq_chip, &da9055->irq_data); if (ret < 0) -- cgit v0.10.2 From d95c785500f60c59e4257385af2cfee7d4044809 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Wed, 20 Mar 2013 10:09:39 +0100 Subject: mfd: Remove twelve unused Kconfig symbols Twelve Kconfig symbols, all related to WM8350, WM8351, and WM8352, are unused. Commit 19d57ed5a308472a02e773f33c03ad4cb2ec6a9 ("mfd: Remove custom wm8350 cache implementation") removed all their (actual) users. Remove these symbols too. Signed-off-by: Paul Bolle Acked-by: Mark Brown Acked-by: Kukjin Kim Signed-off-by: Samuel Ortiz diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 4c9c6f9..6d9ae09 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -481,8 +481,6 @@ config MACH_MX31ADS_WM1133_EV1 depends on MACH_MX31ADS depends on MFD_WM8350_I2C depends on REGULATOR_WM8350 = y - select MFD_WM8350_CONFIG_MODE_0 - select MFD_WM8352_CONFIG_MODE_0 help Include support for the Wolfson Microelectronics 1133-EV1 PMU and audio module for the MX31ADS platform. diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index 131c862..041da51 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig @@ -198,10 +198,7 @@ endchoice config SMDK6410_WM1190_EV1 bool "Support Wolfson Microelectronics 1190-EV1 PMIC card" depends on MACH_SMDK6410 - select MFD_WM8350_CONFIG_MODE_0 - select MFD_WM8350_CONFIG_MODE_3 select MFD_WM8350_I2C - select MFD_WM8352_CONFIG_MODE_0 select REGULATOR select REGULATOR_WM8350 select SAMSUNG_GPIO_EXTRA64 diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index cc7f6a2..533ce2c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -715,54 +715,6 @@ config MFD_WM8350 bool depends on GENERIC_HARDIRQS -config MFD_WM8350_CONFIG_MODE_0 - bool - depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_1 - bool - depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_2 - bool - depends on MFD_WM8350 - -config MFD_WM8350_CONFIG_MODE_3 - bool - depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_0 - bool - depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_1 - bool - depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_2 - bool - depends on MFD_WM8350 - -config MFD_WM8351_CONFIG_MODE_3 - bool - depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_0 - bool - depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_1 - bool - depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_2 - bool - depends on MFD_WM8350 - -config MFD_WM8352_CONFIG_MODE_3 - bool - depends on MFD_WM8350 - config MFD_WM8350_I2C bool "Support Wolfson Microelectronics WM8350 with I2C" select MFD_WM8350 -- cgit v0.10.2 From 7a93fb375437225ee89a15652a887547450f3d2a Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Thu, 21 Mar 2013 14:49:44 +0100 Subject: mfd: ab8500-core: Ignore masked out interrupts AB8500 asserts LATCH bits for masked out interrupts. This patch explicitly masks those out using the cached mask value to prevent handle_nested_irq() being called for masked IRQ on the same register as unmasked ones. Acked-by: Linus Walleij Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index f276352..36751f3 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -458,22 +458,23 @@ static void update_latch_offset(u8 *offset, int i) static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, int latch_offset, u8 latch_val) { - int int_bit = __ffs(latch_val); - int line, i; + int int_bit, line, i; - do { - int_bit = __ffs(latch_val); + for (i = 0; i < ab8500->mask_size; i++) + if (ab8500->irq_reg_offset[i] == latch_offset) + break; - for (i = 0; i < ab8500->mask_size; i++) - if (ab8500->irq_reg_offset[i] == latch_offset) - break; + if (i >= ab8500->mask_size) { + dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", + latch_offset); + return -ENXIO; + } - if (i >= ab8500->mask_size) { - dev_err(ab8500->dev, "Register offset 0x%2x not declared\n", - latch_offset); - return -ENXIO; - } + /* ignore masked out interrupts */ + latch_val &= ~ab8500->mask[i]; + while (latch_val) { + int_bit = __ffs(latch_val); line = (i << 3) + int_bit; latch_val &= ~(1 << int_bit); @@ -491,7 +492,7 @@ static int ab8500_handle_hierarchical_line(struct ab8500 *ab8500, line += 1; handle_nested_irq(ab8500->irq_base + line); - } while (latch_val); + } return 0; } -- cgit v0.10.2 From 6394678e6e563b196122e911daa911973f9cf7ab Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 22 Mar 2013 12:40:22 +0100 Subject: mfd: wm8994: Don't use system PM operations For realistic systems the power management is controlled entirely via runtime PM - if the device is not runtime suspended then the suspend will abort without doing anything as functionality such as accessory detection or audio bypass will require the device to be enabled while if the device is runtime suspended it is already in the lowest power state. This means that system suspend is redundant and can be removed which avoids issues with attempting to double disable the regulators. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 97e929e..cdea84e 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -191,7 +191,7 @@ static const char *wm8958_main_supplies[] = { "SPKVDD2", }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_RUNTIME static int wm8994_suspend(struct device *dev) { struct wm8994 *wm8994 = dev_get_drvdata(dev); @@ -724,8 +724,9 @@ static const struct i2c_device_id wm8994_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id); -static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, - NULL); +static const struct dev_pm_ops wm8994_pm_ops = { + SET_RUNTIME_PM_OPS(wm8994_suspend, wm8994_resume, NULL) +}; static struct i2c_driver wm8994_i2c_driver = { .driver = { -- cgit v0.10.2 From 40b0d68a8c39c6d6cb7e975c9b180e2203864556 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 9 Apr 2013 11:39:17 +0300 Subject: mfd: omap-usb-host: Remove PHY reset handling code PHY reset GPIO handling will be done in the PHY driver Signed-off-by: Roger Quadros Acked-by: Felipe Balbi Acked-by: Samuel Ortiz Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index eb5db28..138ee98 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -430,24 +430,10 @@ static unsigned omap_usbhs_rev2_hostconfig(struct usbhs_hcd_omap *omap, static void omap_usbhs_init(struct device *dev) { struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); - struct usbhs_omap_platform_data *pdata = omap->pdata; unsigned reg; dev_dbg(dev, "starting TI HSUSB Controller\n"); - if (pdata->phy_reset) { - if (gpio_is_valid(pdata->reset_gpio_port[0])) - devm_gpio_request_one(dev, pdata->reset_gpio_port[0], - GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - devm_gpio_request_one(dev, pdata->reset_gpio_port[1], - GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); - - /* Hold the PHY in RESET for enough time till DIR is high */ - udelay(10); - } - pm_runtime_get_sync(dev); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); @@ -476,20 +462,6 @@ static void omap_usbhs_init(struct device *dev) dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg); pm_runtime_put_sync(dev); - if (pdata->phy_reset) { - /* Hold the PHY in RESET for enough time till - * PHY is settled and ready - */ - udelay(10); - - if (gpio_is_valid(pdata->reset_gpio_port[0])) - gpio_set_value_cansleep - (pdata->reset_gpio_port[0], 1); - - if (gpio_is_valid(pdata->reset_gpio_port[1])) - gpio_set_value_cansleep - (pdata->reset_gpio_port[1], 1); - } } /** -- cgit v0.10.2 From 03a8f438f55c7abaaa1ddf5422a6c4abd1bdc1f6 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Tue, 9 Apr 2013 11:39:18 +0300 Subject: mfd: omap-usb-host: Add device tree support and binding information Allows the OMAP HS USB host controller to be specified via device tree. Signed-off-by: Roger Quadros Reviewed-by: Mark Rutland Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/mfd/omap-usb-host.txt b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt new file mode 100644 index 0000000..b381fa6 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/omap-usb-host.txt @@ -0,0 +1,80 @@ +OMAP HS USB Host + +Required properties: + +- compatible: should be "ti,usbhs-host" +- reg: should contain one register range i.e. start and length +- ti,hwmods: must contain "usb_host_hs" + +Optional properties: + +- num-ports: number of USB ports. Usually this is automatically detected + from the IP's revision register but can be overridden by specifying + this property. A maximum of 3 ports are supported at the moment. + +- portN-mode: String specifying the port mode for port N, where N can be + from 1 to 3. If the port mode is not specified, that port is treated + as unused. When specified, it must be one of the following. + "ehci-phy", + "ehci-tll", + "ehci-hsic", + "ohci-phy-6pin-datse0", + "ohci-phy-6pin-dpdm", + "ohci-phy-3pin-datse0", + "ohci-phy-4pin-dpdm", + "ohci-tll-6pin-datse0", + "ohci-tll-6pin-dpdm", + "ohci-tll-3pin-datse0", + "ohci-tll-4pin-dpdm", + "ohci-tll-2pin-datse0", + "ohci-tll-2pin-dpdm", + +- single-ulpi-bypass: Must be present if the controller contains a single + ULPI bypass control bit. e.g. OMAP3 silicon <= ES2.1 + +Required properties if child node exists: + +- #address-cells: Must be 1 +- #size-cells: Must be 1 +- ranges: must be present + +Properties for children: + +The OMAP HS USB Host subsystem contains EHCI and OHCI controllers. +See Documentation/devicetree/bindings/usb/omap-ehci.txt and +omap3-ohci.txt + +Example for OMAP4: + +usbhshost: usbhshost@4a064000 { + compatible = "ti,usbhs-host"; + reg = <0x4a064000 0x800>; + ti,hwmods = "usb_host_hs"; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usbhsohci: ohci@4a064800 { + compatible = "ti,ohci-omap3", "usb-ohci"; + reg = <0x4a064800 0x400>; + interrupt-parent = <&gic>; + interrupts = <0 76 0x4>; + }; + + usbhsehci: ehci@4a064c00 { + compatible = "ti,ehci-omap", "usb-ehci"; + reg = <0x4a064c00 0x400>; + interrupt-parent = <&gic>; + interrupts = <0 77 0x4>; + }; +}; + +&usbhshost { + port1-mode = "ehci-phy"; + port2-mode = "ehci-tll"; + port3-mode = "ehci-phy"; +}; + +&usbhsehci { + phys = <&hsusb1_phy 0 &hsusb3_phy>; +}; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 138ee98..d3b6e94 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -1,8 +1,9 @@ /** * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI * - * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2011-2013 Texas Instruments Incorporated - http://www.ti.com * Author: Keshava Munegowda + * Author: Roger Quadros * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 of @@ -27,6 +28,8 @@ #include #include #include +#include +#include #include "omap-usb.h" @@ -137,6 +140,49 @@ static inline u8 usbhs_readb(void __iomem *base, u8 reg) /*-------------------------------------------------------------------------*/ +/** + * Map 'enum usbhs_omap_port_mode' found in + * to the device tree binding portN-mode found in + * 'Documentation/devicetree/bindings/mfd/omap-usb-host.txt' + */ +static const char * const port_modes[] = { + [OMAP_USBHS_PORT_MODE_UNUSED] = "", + [OMAP_EHCI_PORT_MODE_PHY] = "ehci-phy", + [OMAP_EHCI_PORT_MODE_TLL] = "ehci-tll", + [OMAP_EHCI_PORT_MODE_HSIC] = "ehci-hsic", + [OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0] = "ohci-phy-6pin-datse0", + [OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM] = "ohci-phy-6pin-dpdm", + [OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0] = "ohci-phy-3pin-datse0", + [OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM] = "ohci-phy-4pin-dpdm", + [OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0] = "ohci-tll-6pin-datse0", + [OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM] = "ohci-tll-6pin-dpdm", + [OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0] = "ohci-tll-3pin-datse0", + [OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM] = "ohci-tll-4pin-dpdm", + [OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0] = "ohci-tll-2pin-datse0", + [OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM] = "ohci-tll-2pin-dpdm", +}; + +/** + * omap_usbhs_get_dt_port_mode - Get the 'enum usbhs_omap_port_mode' + * from the port mode string. + * @mode: The port mode string, usually obtained from device tree. + * + * The function returns the 'enum usbhs_omap_port_mode' that matches the + * provided port mode string as per the port_modes table. + * If no match is found it returns -ENODEV + */ +static const int omap_usbhs_get_dt_port_mode(const char *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(port_modes); i++) { + if (!strcmp(mode, port_modes[i])) + return i; + } + + return -ENODEV; +} + static struct platform_device *omap_usbhs_alloc_child(const char *name, struct resource *res, int num_resources, void *pdata, size_t pdata_size, struct device *dev) @@ -464,6 +510,58 @@ static void omap_usbhs_init(struct device *dev) pm_runtime_put_sync(dev); } +static int usbhs_omap_get_dt_pdata(struct device *dev, + struct usbhs_omap_platform_data *pdata) +{ + int ret, i; + struct device_node *node = dev->of_node; + + ret = of_property_read_u32(node, "num-ports", &pdata->nports); + if (ret) + pdata->nports = 0; + + if (pdata->nports > OMAP3_HS_USB_PORTS) { + dev_warn(dev, "Too many num_ports <%d> in device tree. Max %d\n", + pdata->nports, OMAP3_HS_USB_PORTS); + return -ENODEV; + } + + /* get port modes */ + for (i = 0; i < OMAP3_HS_USB_PORTS; i++) { + char prop[11]; + const char *mode; + + pdata->port_mode[i] = OMAP_USBHS_PORT_MODE_UNUSED; + + snprintf(prop, sizeof(prop), "port%d-mode", i + 1); + ret = of_property_read_string(node, prop, &mode); + if (ret < 0) + continue; + + ret = omap_usbhs_get_dt_port_mode(mode); + if (ret < 0) { + dev_warn(dev, "Invalid port%d-mode \"%s\" in device tree\n", + i, mode); + return -ENODEV; + } + + dev_dbg(dev, "port%d-mode: %s -> %d\n", i, mode, ret); + pdata->port_mode[i] = ret; + } + + /* get flags */ + pdata->single_ulpi_bypass = of_property_read_bool(node, + "single-ulpi-bypass"); + + return 0; +} + +static struct of_device_id usbhs_child_match_table[] = { + { .compatible = "ti,omap-ehci", }, + { .compatible = "ti,omap-ohci", }, + { } +}; + /** * usbhs_omap_probe - initialize TI-based HCDs * @@ -479,18 +577,37 @@ static int usbhs_omap_probe(struct platform_device *pdev) int i; bool need_logic_fck; + if (dev->of_node) { + /* For DT boot we populate platform data from OF node */ + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = usbhs_omap_get_dt_pdata(dev, pdata); + if (ret) + return ret; + + dev->platform_data = pdata; + } + if (!pdata) { dev_err(dev, "Missing platform data\n"); return -ENODEV; } + if (pdata->nports > OMAP3_HS_USB_PORTS) { + dev_info(dev, "Too many num_ports <%d> in platform_data. Max %d\n", + pdata->nports, OMAP3_HS_USB_PORTS); + return -ENODEV; + } + omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL); if (!omap) { dev_err(dev, "Memory allocation failed\n"); return -ENOMEM; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); omap->uhh_base = devm_request_and_ioremap(dev, res); if (!omap->uhh_base) { dev_err(dev, "Resource request/ioremap failed\n"); @@ -661,10 +778,23 @@ static int usbhs_omap_probe(struct platform_device *pdev) } omap_usbhs_init(dev); - ret = omap_usbhs_alloc_children(pdev); - if (ret) { - dev_err(dev, "omap_usbhs_alloc_children failed\n"); - goto err_alloc; + + if (dev->of_node) { + ret = of_platform_populate(dev->of_node, + usbhs_child_match_table, NULL, dev); + + if (ret) { + dev_err(dev, "Failed to create DT children: %d\n", ret); + goto err_alloc; + } + + } else { + ret = omap_usbhs_alloc_children(pdev); + if (ret) { + dev_err(dev, "omap_usbhs_alloc_children failed: %d\n", + ret); + goto err_alloc; + } } return 0; @@ -703,6 +833,13 @@ err_mem: return ret; } +static int usbhs_omap_remove_child(struct device *dev, void *data) +{ + dev_info(dev, "unregistering\n"); + platform_device_unregister(to_platform_device(dev)); + return 0; +} + /** * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs * @pdev: USB Host Controller being removed @@ -734,6 +871,8 @@ static int usbhs_omap_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); + /* remove children */ + device_for_each_child(&pdev->dev, NULL, usbhs_omap_remove_child); return 0; } @@ -742,16 +881,26 @@ static const struct dev_pm_ops usbhsomap_dev_pm_ops = { .runtime_resume = usbhs_runtime_resume, }; +static const struct of_device_id usbhs_omap_dt_ids[] = { + { .compatible = "ti,usbhs-host" }, + { } +}; + +MODULE_DEVICE_TABLE(of, usbhs_omap_dt_ids); + + static struct platform_driver usbhs_omap_driver = { .driver = { .name = (char *)usbhs_driver_name, .owner = THIS_MODULE, .pm = &usbhsomap_dev_pm_ops, + .of_match_table = of_match_ptr(usbhs_omap_dt_ids), }, .remove = usbhs_omap_remove, }; MODULE_AUTHOR("Keshava Munegowda "); +MODULE_AUTHOR("Roger Quadros "); MODULE_ALIAS("platform:" USBHS_DRIVER_NAME); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); -- cgit v0.10.2 From f201f730b82393aa4969171d724025a82f7099da Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Tue, 9 Apr 2013 10:57:21 +0200 Subject: mfd: ab8500-core: Add of_compatilbe property for ab8500-usb Add of_comptabile string to the ab8500-usb cell to allow the driver to grab properties from device-tree when available. Signed-off-by: Fabio Baltieri Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index 36751f3..8e8a016 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -1108,6 +1108,7 @@ static struct mfd_cell ab8500_devs[] = { }, { .name = "ab8500-usb", + .of_compatible = "stericsson,ab8500-usb", .num_resources = ARRAY_SIZE(ab8500_usb_resources), .resources = ab8500_usb_resources, }, -- cgit v0.10.2 From 5fb1c2dd8f9e9f3fd4ecbaefc75b83ab0ec40cfa Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Mon, 25 Mar 2013 17:56:23 +0100 Subject: mfd: syscon: Add missing struct device_node declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit syscon.h header uses struct device_node in functions' declarations without declaring it. This causes compilation warnings like: include/linux/mfd/syscon.h:20: warning: ‘struct device_node’ declared inside parameter list include/linux/mfd/syscon.h:20: warning: its scope is only this definition or declaration, which is probably not what you want Fix it by adding a forward declaration of struct device_node. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Dong Aisheng Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/syscon.h b/include/linux/mfd/syscon.h index 5c9ee6e..b473577f 100644 --- a/include/linux/mfd/syscon.h +++ b/include/linux/mfd/syscon.h @@ -15,6 +15,8 @@ #ifndef __LINUX_MFD_SYSCON_H__ #define __LINUX_MFD_SYSCON_H__ +struct device_node; + extern struct regmap *syscon_node_to_regmap(struct device_node *np); extern struct regmap *syscon_regmap_lookup_by_compatible(const char *s); extern struct regmap *syscon_regmap_lookup_by_pdevname(const char *s); -- cgit v0.10.2 From 741cdecfc5255085d348ccaad1d1befbd6ae9222 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 4 Apr 2013 11:39:00 +0100 Subject: mfd: db8500-prcmu: Return early if the TCPM cannot be located Currently we check to see if we obtained the Tightly Coupled Program Memory (TCPM) base and only execute the code within the check if we have it. It's more traditional to return early if we don't have it. This way we can flatten most of the function's code down to a single tab spacing. Signed-off-by: Lee Jones Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 21434be..25dda8d 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -2704,6 +2704,7 @@ static void dbx500_fw_version_init(struct platform_device *pdev, { struct resource *res; void __iomem *tcpm_base; + u32 version; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "prcmu-tcpm"); @@ -2713,26 +2714,27 @@ static void dbx500_fw_version_init(struct platform_device *pdev, return; } tcpm_base = ioremap(res->start, resource_size(res)); - if (tcpm_base != NULL) { - u32 version; - - version = readl(tcpm_base + version_offset); - fw_info.version.project = (version & 0xFF); - fw_info.version.api_version = (version >> 8) & 0xFF; - fw_info.version.func_version = (version >> 16) & 0xFF; - fw_info.version.errata = (version >> 24) & 0xFF; - strncpy(fw_info.version.project_name, - fw_project_name(fw_info.version.project), - PRCMU_FW_PROJECT_NAME_LEN); - fw_info.valid = true; - pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n", - fw_info.version.project_name, - fw_info.version.project, - fw_info.version.api_version, - fw_info.version.func_version, - fw_info.version.errata); - iounmap(tcpm_base); + if (!tcpm_base) { + dev_err(&pdev->dev, "no prcmu tcpm mem region provided\n"); + return; } + + version = readl(tcpm_base + version_offset); + fw_info.version.project = (version & 0xFF); + fw_info.version.api_version = (version >> 8) & 0xFF; + fw_info.version.func_version = (version >> 16) & 0xFF; + fw_info.version.errata = (version >> 24) & 0xFF; + strncpy(fw_info.version.project_name, + fw_project_name(fw_info.version.project), + PRCMU_FW_PROJECT_NAME_LEN); + fw_info.valid = true; + pr_info("PRCMU firmware: %s(%d), version %d.%d.%d\n", + fw_info.version.project_name, + fw_info.version.project, + fw_info.version.api_version, + fw_info.version.func_version, + fw_info.version.errata); + iounmap(tcpm_base); } void __init db8500_prcmu_early_init(u32 phy_base, u32 size) -- cgit v0.10.2 From caa62d642634e25cf4316b286abac54a70fd5a6a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 3 Apr 2013 01:06:27 +0200 Subject: mfd: ab8500: sysctrl: Initialize driver at arch_initcall The abx500-clk driver is initiated at arch_initcall level. Moreover it is relying on the ab8500-sysctrl API to be available. Therefore move ab8500-sysctrl to arch_initcall level as well. The device is already added before the abx500 clk device, thus it will be probed before as well, which is exactly what we want. Signed-off-by: Ulf Hansson Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c index 272479c..fbca1ce 100644 --- a/drivers/mfd/ab8500-sysctrl.c +++ b/drivers/mfd/ab8500-sysctrl.c @@ -242,7 +242,7 @@ static int __init ab8500_sysctrl_init(void) { return platform_driver_register(&ab8500_sysctrl_driver); } -subsys_initcall(ab8500_sysctrl_init); +arch_initcall(ab8500_sysctrl_init); MODULE_AUTHOR("Mattias Nilsson Date: Wed, 3 Apr 2013 13:26:27 +0100 Subject: mfd: vexpress: Give the "leds-gpio" device a real parent The registration of the "leds-gpio" device was using "vexpress_sysreg_dev" as a parent before it was actually set to something different than NULL. Trivial fix by reordering the code. Reported-by: Lorenzo Pieralisi Signed-off-by: Pawel Moll Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c index bf75e96..96a020b 100644 --- a/drivers/mfd/vexpress-sysreg.c +++ b/drivers/mfd/vexpress-sysreg.c @@ -490,12 +490,12 @@ static int vexpress_sysreg_probe(struct platform_device *pdev) return err; } + vexpress_sysreg_dev = &pdev->dev; + platform_device_register_data(vexpress_sysreg_dev, "leds-gpio", PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata, sizeof(vexpress_sysreg_leds_pdata)); - vexpress_sysreg_dev = &pdev->dev; - device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id); return 0; -- cgit v0.10.2 From 0c0d2ab069fa668daeda4204580e359dba178169 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:42:48 +0900 Subject: mfd: wm831x: Use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 4e70e15..e7ed14f66 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -37,7 +37,7 @@ static int wm831x_spi_probe(struct spi_device *spi) spi->bits_per_word = 16; spi->mode = SPI_MODE_0; - dev_set_drvdata(&spi->dev, wm831x); + spi_set_drvdata(spi, wm831x); wm831x->dev = &spi->dev; wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config); @@ -53,7 +53,7 @@ static int wm831x_spi_probe(struct spi_device *spi) static int wm831x_spi_remove(struct spi_device *spi) { - struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + struct wm831x *wm831x = spi_get_drvdata(spi); wm831x_device_exit(wm831x); @@ -69,7 +69,7 @@ static int wm831x_spi_suspend(struct device *dev) static void wm831x_spi_shutdown(struct spi_device *spi) { - struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + struct wm831x *wm831x = spi_get_drvdata(spi); wm831x_device_shutdown(wm831x); } -- cgit v0.10.2 From 1b1ba09c39a7262f322290820ad8f29f964b9eee Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:43:23 +0900 Subject: mfd: da9052: Use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index 61d63b9..0680bcb 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -38,7 +38,7 @@ static int da9052_spi_probe(struct spi_device *spi) da9052->dev = &spi->dev; da9052->chip_irq = spi->irq; - dev_set_drvdata(&spi->dev, da9052); + spi_set_drvdata(spi, da9052); da9052_regmap_config.read_flag_mask = 1; da9052_regmap_config.write_flag_mask = 0; @@ -60,7 +60,7 @@ static int da9052_spi_probe(struct spi_device *spi) static int da9052_spi_remove(struct spi_device *spi) { - struct da9052 *da9052 = dev_get_drvdata(&spi->dev); + struct da9052 *da9052 = spi_get_drvdata(spi); da9052_device_exit(da9052); return 0; -- cgit v0.10.2 From 7c478b405837e91f68208b423acd20dad184998c Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:43:48 +0900 Subject: mfd: ezx-pcap: Use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index f83cc3d..5502106 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -393,7 +393,7 @@ static int pcap_add_subdev(struct pcap_chip *pcap, static int ezx_pcap_remove(struct spi_device *spi) { - struct pcap_chip *pcap = dev_get_drvdata(&spi->dev); + struct pcap_chip *pcap = spi_get_drvdata(spi); struct pcap_platform_data *pdata = spi->dev.platform_data; int i, adc_irq; @@ -439,7 +439,7 @@ static int ezx_pcap_probe(struct spi_device *spi) mutex_init(&pcap->adc_mutex); INIT_WORK(&pcap->isr_work, pcap_isr_work); INIT_WORK(&pcap->msr_work, pcap_msr_work); - dev_set_drvdata(&spi->dev, pcap); + spi_set_drvdata(spi, pcap); /* setup spi */ spi->bits_per_word = 32; -- cgit v0.10.2 From a720c3053322231d6fc6c5e7459ea1ea068904c4 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:44:14 +0900 Subject: mfd: mc13xxx: Use spi_get_drvdata() and spi_set_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/mc13xxx-spi.c b/drivers/mfd/mc13xxx-spi.c index 3032bae..77189da 100644 --- a/drivers/mfd/mc13xxx-spi.c +++ b/drivers/mfd/mc13xxx-spi.c @@ -131,7 +131,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi) if (!mc13xxx) return -ENOMEM; - dev_set_drvdata(&spi->dev, mc13xxx); + spi_set_drvdata(spi, mc13xxx); spi->mode = SPI_MODE_0 | SPI_CS_HIGH; mc13xxx->dev = &spi->dev; @@ -144,7 +144,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi) ret = PTR_ERR(mc13xxx->regmap); dev_err(mc13xxx->dev, "Failed to initialize register map: %d\n", ret); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return ret; } @@ -164,7 +164,7 @@ static int mc13xxx_spi_probe(struct spi_device *spi) static int mc13xxx_spi_remove(struct spi_device *spi) { - struct mc13xxx *mc13xxx = dev_get_drvdata(&spi->dev); + struct mc13xxx *mc13xxx = spi_get_drvdata(spi); mc13xxx_common_cleanup(mc13xxx); -- cgit v0.10.2 From e9642d5e162c2a219f770cf4a52b4c6b622879bb Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:44:30 +0900 Subject: mfd: arizona: Use spi_get_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/arizona-spi.c b/drivers/mfd/arizona-spi.c index 1b9fdd6..b57e642 100644 --- a/drivers/mfd/arizona-spi.c +++ b/drivers/mfd/arizona-spi.c @@ -67,7 +67,7 @@ static int arizona_spi_probe(struct spi_device *spi) static int arizona_spi_remove(struct spi_device *spi) { - struct arizona *arizona = dev_get_drvdata(&spi->dev); + struct arizona *arizona = spi_get_drvdata(spi); arizona_dev_exit(arizona); return 0; } -- cgit v0.10.2 From e65ad41e3b3f28316565ad0061b8152a3a8227ba Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Sat, 6 Apr 2013 15:44:51 +0900 Subject: mfd: stmpe: Use spi_get_drvdata() Use the wrapper functions for getting and setting the driver data using spi_device instead of using dev_{get|set}_drvdata with &spi->dev, so we can directly pass a struct spi_device. Signed-off-by: Jingoo Han Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/stmpe-spi.c b/drivers/mfd/stmpe-spi.c index 973659f..a81badb 100644 --- a/drivers/mfd/stmpe-spi.c +++ b/drivers/mfd/stmpe-spi.c @@ -103,7 +103,7 @@ stmpe_spi_probe(struct spi_device *spi) static int stmpe_spi_remove(struct spi_device *spi) { - struct stmpe *stmpe = dev_get_drvdata(&spi->dev); + struct stmpe *stmpe = spi_get_drvdata(spi); return stmpe_remove(stmpe); } -- cgit v0.10.2 From 230f13a5035fd4725a6623af83953623fd51173a Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Graux Date: Tue, 9 Apr 2013 10:35:19 +0200 Subject: mfd: support stmpe1801 18 bits enhanced port expander Provides support for 1801 variant of stmpe gpio port expanders. This chip has 18 gpios configurable as GPI, GPO, keypad matrix, special key or dedicated key function. Note that special/dedicated key function is not supported yet. Signed-off-by: Jean-Nicolas Graux Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 533ce2c..9d64e32 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -393,6 +393,7 @@ config MFD_STMPE STMPE811: GPIO, Touchscreen STMPE1601: GPIO, Keypad + STMPE1801: GPIO, Keypad STMPE2401: GPIO, Keypad STMPE2403: GPIO, Keypad diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c index fd5fcb6..0da02e1 100644 --- a/drivers/mfd/stmpe-i2c.c +++ b/drivers/mfd/stmpe-i2c.c @@ -75,6 +75,7 @@ static const struct i2c_device_id stmpe_i2c_id[] = { { "stmpe801", STMPE801 }, { "stmpe811", STMPE811 }, { "stmpe1601", STMPE1601 }, + { "stmpe1801", STMPE1801 }, { "stmpe2401", STMPE2401 }, { "stmpe2403", STMPE2403 }, { } diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index bc85877..bbccd51 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "stmpe.h" static int __stmpe_enable(struct stmpe *stmpe, unsigned int blocks) @@ -643,6 +644,88 @@ static struct stmpe_variant_info stmpe1601 = { }; /* + * STMPE1801 + */ +static const u8 stmpe1801_regs[] = { + [STMPE_IDX_CHIP_ID] = STMPE1801_REG_CHIP_ID, + [STMPE_IDX_ICR_LSB] = STMPE1801_REG_INT_CTRL_LOW, + [STMPE_IDX_IER_LSB] = STMPE1801_REG_INT_EN_MASK_LOW, + [STMPE_IDX_ISR_LSB] = STMPE1801_REG_INT_STA_LOW, + [STMPE_IDX_GPMR_LSB] = STMPE1801_REG_GPIO_MP_LOW, + [STMPE_IDX_GPSR_LSB] = STMPE1801_REG_GPIO_SET_LOW, + [STMPE_IDX_GPCR_LSB] = STMPE1801_REG_GPIO_CLR_LOW, + [STMPE_IDX_GPDR_LSB] = STMPE1801_REG_GPIO_SET_DIR_LOW, + [STMPE_IDX_GPRER_LSB] = STMPE1801_REG_GPIO_RE_LOW, + [STMPE_IDX_GPFER_LSB] = STMPE1801_REG_GPIO_FE_LOW, + [STMPE_IDX_IEGPIOR_LSB] = STMPE1801_REG_INT_EN_GPIO_MASK_LOW, + [STMPE_IDX_ISGPIOR_LSB] = STMPE1801_REG_INT_STA_GPIO_LOW, +}; + +static struct stmpe_variant_block stmpe1801_blocks[] = { + { + .cell = &stmpe_gpio_cell, + .irq = STMPE1801_IRQ_GPIOC, + .block = STMPE_BLOCK_GPIO, + }, + { + .cell = &stmpe_keypad_cell, + .irq = STMPE1801_IRQ_KEYPAD, + .block = STMPE_BLOCK_KEYPAD, + }, +}; + +static int stmpe1801_enable(struct stmpe *stmpe, unsigned int blocks, + bool enable) +{ + unsigned int mask = 0; + if (blocks & STMPE_BLOCK_GPIO) + mask |= STMPE1801_MSK_INT_EN_GPIO; + + if (blocks & STMPE_BLOCK_KEYPAD) + mask |= STMPE1801_MSK_INT_EN_KPC; + + return __stmpe_set_bits(stmpe, STMPE1801_REG_INT_EN_MASK_LOW, mask, + enable ? mask : 0); +} + +static int stmpe1801_reset(struct stmpe *stmpe) +{ + unsigned long timeout; + int ret = 0; + + ret = __stmpe_set_bits(stmpe, STMPE1801_REG_SYS_CTRL, + STMPE1801_MSK_SYS_CTRL_RESET, STMPE1801_MSK_SYS_CTRL_RESET); + if (ret < 0) + return ret; + + timeout = jiffies + msecs_to_jiffies(100); + while (time_before(jiffies, timeout)) { + ret = __stmpe_reg_read(stmpe, STMPE1801_REG_SYS_CTRL); + if (ret < 0) + return ret; + if (!(ret & STMPE1801_MSK_SYS_CTRL_RESET)) + return 0; + usleep_range(100, 200); + }; + return -EIO; +} + +static struct stmpe_variant_info stmpe1801 = { + .name = "stmpe1801", + .id_val = STMPE1801_ID, + .id_mask = 0xfff0, + .num_gpios = 18, + .af_bits = 0, + .regs = stmpe1801_regs, + .blocks = stmpe1801_blocks, + .num_blocks = ARRAY_SIZE(stmpe1801_blocks), + .num_irqs = STMPE1801_NR_INTERNAL_IRQS, + .enable = stmpe1801_enable, + /* stmpe1801 do not have any gpio alternate function */ + .get_altfunc = NULL, +}; + +/* * STMPE24XX */ @@ -740,6 +823,7 @@ static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = { [STMPE801] = &stmpe801, [STMPE811] = &stmpe811, [STMPE1601] = &stmpe1601, + [STMPE1801] = &stmpe1801, [STMPE2401] = &stmpe2401, [STMPE2403] = &stmpe2403, }; @@ -759,7 +843,7 @@ static irqreturn_t stmpe_irq(int irq, void *data) struct stmpe *stmpe = data; struct stmpe_variant_info *variant = stmpe->variant; int num = DIV_ROUND_UP(variant->num_irqs, 8); - u8 israddr = stmpe->regs[STMPE_IDX_ISR_MSB]; + u8 israddr; u8 isr[num]; int ret; int i; @@ -771,6 +855,11 @@ static irqreturn_t stmpe_irq(int irq, void *data) return IRQ_HANDLED; } + if (variant->id_val == STMPE1801_ID) + israddr = stmpe->regs[STMPE_IDX_ISR_LSB]; + else + israddr = stmpe->regs[STMPE_IDX_ISR_MSB]; + ret = stmpe_block_read(stmpe, israddr, num, isr); if (ret < 0) return IRQ_NONE; @@ -938,6 +1027,12 @@ static int stmpe_chip_init(struct stmpe *stmpe) if (ret) return ret; + if (id == STMPE1801_ID) { + ret = stmpe1801_reset(stmpe); + if (ret < 0) + return ret; + } + if (stmpe->irq >= 0) { if (id == STMPE801_ID) icr = STMPE801_REG_SYS_CTRL_INT_EN; diff --git a/drivers/mfd/stmpe.h b/drivers/mfd/stmpe.h index 7b8e13f..ff2b09b 100644 --- a/drivers/mfd/stmpe.h +++ b/drivers/mfd/stmpe.h @@ -199,6 +199,55 @@ int stmpe_remove(struct stmpe *stmpe); #define STPME1601_AUTOSLEEP_ENABLE (1 << 3) /* + * STMPE1801 + */ +#define STMPE1801_ID 0xc110 +#define STMPE1801_NR_INTERNAL_IRQS 5 +#define STMPE1801_IRQ_KEYPAD_COMBI 4 +#define STMPE1801_IRQ_GPIOC 3 +#define STMPE1801_IRQ_KEYPAD_OVER 2 +#define STMPE1801_IRQ_KEYPAD 1 +#define STMPE1801_IRQ_WAKEUP 0 + +#define STMPE1801_REG_CHIP_ID 0x00 +#define STMPE1801_REG_SYS_CTRL 0x02 +#define STMPE1801_REG_INT_CTRL_LOW 0x04 +#define STMPE1801_REG_INT_EN_MASK_LOW 0x06 +#define STMPE1801_REG_INT_STA_LOW 0x08 +#define STMPE1801_REG_INT_EN_GPIO_MASK_LOW 0x0A +#define STMPE1801_REG_INT_EN_GPIO_MASK_MID 0x0B +#define STMPE1801_REG_INT_EN_GPIO_MASK_HIGH 0x0C +#define STMPE1801_REG_INT_STA_GPIO_LOW 0x0D +#define STMPE1801_REG_INT_STA_GPIO_MID 0x0E +#define STMPE1801_REG_INT_STA_GPIO_HIGH 0x0F +#define STMPE1801_REG_GPIO_SET_LOW 0x10 +#define STMPE1801_REG_GPIO_SET_MID 0x11 +#define STMPE1801_REG_GPIO_SET_HIGH 0x12 +#define STMPE1801_REG_GPIO_CLR_LOW 0x13 +#define STMPE1801_REG_GPIO_CLR_MID 0x14 +#define STMPE1801_REG_GPIO_CLR_HIGH 0x15 +#define STMPE1801_REG_GPIO_MP_LOW 0x16 +#define STMPE1801_REG_GPIO_MP_MID 0x17 +#define STMPE1801_REG_GPIO_MP_HIGH 0x18 +#define STMPE1801_REG_GPIO_SET_DIR_LOW 0x19 +#define STMPE1801_REG_GPIO_SET_DIR_MID 0x1A +#define STMPE1801_REG_GPIO_SET_DIR_HIGH 0x1B +#define STMPE1801_REG_GPIO_RE_LOW 0x1C +#define STMPE1801_REG_GPIO_RE_MID 0x1D +#define STMPE1801_REG_GPIO_RE_HIGH 0x1E +#define STMPE1801_REG_GPIO_FE_LOW 0x1F +#define STMPE1801_REG_GPIO_FE_MID 0x20 +#define STMPE1801_REG_GPIO_FE_HIGH 0x21 +#define STMPE1801_REG_GPIO_PULL_UP_LOW 0x22 +#define STMPE1801_REG_GPIO_PULL_UP_MID 0x23 +#define STMPE1801_REG_GPIO_PULL_UP_HIGH 0x24 + +#define STMPE1801_MSK_SYS_CTRL_RESET (1 << 7) + +#define STMPE1801_MSK_INT_EN_KPC (1 << 1) +#define STMPE1801_MSK_INT_EN_GPIO (1 << 3) + +/* * STMPE24xx */ diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h index 383ac15..48395a6 100644 --- a/include/linux/mfd/stmpe.h +++ b/include/linux/mfd/stmpe.h @@ -26,6 +26,7 @@ enum stmpe_partnum { STMPE801, STMPE811, STMPE1601, + STMPE1801, STMPE2401, STMPE2403, STMPE_NBR_PARTS @@ -39,6 +40,7 @@ enum { STMPE_IDX_CHIP_ID, STMPE_IDX_ICR_LSB, STMPE_IDX_IER_LSB, + STMPE_IDX_ISR_LSB, STMPE_IDX_ISR_MSB, STMPE_IDX_GPMR_LSB, STMPE_IDX_GPSR_LSB, @@ -49,6 +51,7 @@ enum { STMPE_IDX_GPFER_LSB, STMPE_IDX_GPAFR_U_MSB, STMPE_IDX_IEGPIOR_LSB, + STMPE_IDX_ISGPIOR_LSB, STMPE_IDX_ISGPIOR_MSB, STMPE_IDX_MAX, }; -- cgit v0.10.2 From ab85b120e6925203e4b63022166eacac19009380 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Tue, 9 Apr 2013 14:40:09 +0200 Subject: mfd: Kconfig alphabetical re-ordering Ordering so many Kconfig alphabetically makes it a lot easier to read e.g. from menuconfig. Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9d64e32..2f3ce18 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -10,19 +10,45 @@ config MFD_CORE select IRQ_DOMAIN default n -config MFD_88PM860X - bool "Support Marvell 88PM8606/88PM8607" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_CS5535 + tristate "AMD CS5535 and CS5536 southbridge core functions" + select MFD_CORE + depends on PCI && X86 + ---help--- + This is the core driver for CS5535/CS5536 MFD functions. This is + necessary for using the board's GPIO and MFGPT functionality. + +config MFD_AS3711 + bool "AMS AS3711" + select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ + depends on I2C=y && GENERIC_HARDIRQS + help + Support for the AS3711 PMIC from AMS + +config PMIC_ADP5520 + bool "Analog Devices ADP5520/01 MFD PMIC Core Support" + depends on I2C=y + help + Say yes here to add support for Analog Devices AD5520 and ADP5501, + Multifunction Power Management IC. This includes + the I2C driver and the core APIs _only_, you have to select + individual components like LCD backlight, LEDs, GPIOs and Kepad + under the corresponding menus. + +config MFD_AAT2870_CORE + bool "AnalogicTech AAT2870" select MFD_CORE + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS help - This supports for Marvell 88PM8606/88PM8607 Power Management IC. - This includes the I2C driver and the core APIs _only_, you have to - select individual components like voltage regulators, RTC and - battery-charger under the corresponding menus. + If you say yes here you get support for the AAT2870. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. config MFD_CROS_EC - tristate "Support ChromeOS Embedded Controller" + tristate "ChromeOS Embedded Controller" select MFD_CORE help If you say Y here you get support for the ChromeOS Embedded @@ -50,100 +76,98 @@ config MFD_CROS_EC_SPI response time cannot be guaranteed, we support ignoring 'pre-amble' bytes before the response actually starts. -config MFD_88PM800 - tristate "Support Marvell 88PM800" - depends on I2C=y && GENERIC_HARDIRQS - select REGMAP_I2C - select REGMAP_IRQ +config MFD_ASIC3 + bool "Compaq ASIC3" + depends on GENERIC_HARDIRQS && GPIOLIB && ARM select MFD_CORE + ---help--- + This driver supports the ASIC3 multifunction chip found on many + PDAs (mainly iPAQ and HTC based ones) + +config PMIC_DA903X + bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" + depends on I2C=y help - This supports for Marvell 88PM800 Power Management IC. - This includes the I2C driver and the core APIs _only_, you have to - select individual components like voltage regulators, RTC and - battery-charger under the corresponding menus. + Say yes here to support for Dialog Semiconductor DA9030 (a.k.a + ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC + usually found on PXA processors-based platforms. This includes + the I2C driver and the core APIs _only_, you have to select + individual components like LCD backlight, voltage regulators, + LEDs and battery-charger under the corresponding menus. -config MFD_88PM805 - tristate "Support Marvell 88PM805" - depends on I2C=y && GENERIC_HARDIRQS - select REGMAP_I2C - select REGMAP_IRQ +config PMIC_DA9052 + bool select MFD_CORE - help - This supports for Marvell 88PM805 Power Management IC. This includes - the I2C driver and the core APIs _only_, you have to select individual - components like codec device, headset/Mic device under the - corresponding menus. -config MFD_SM501 - tristate "Support for Silicon Motion SM501" - ---help--- - This is the core driver for the Silicon Motion SM501 multimedia - companion chip. This device is a multifunction device which may - provide numerous interfaces including USB host controller, USB gadget, - asynchronous serial ports, audio functions, and a dual display video - interface. The device may be connected by PCI or local bus with - varying functions enabled. +config MFD_DA9052_SPI + bool "Dialog Semiconductor DA9052/53 PMIC variants with SPI" + select REGMAP_SPI + select REGMAP_IRQ + select PMIC_DA9052 + depends on SPI_MASTER=y && GENERIC_HARDIRQS + help + Support for the Dialog Semiconductor DA9052 PMIC + when controlled using SPI. This driver provides common support + for accessing the device, additional drivers must be enabled in + order to use the functionality of the device. -config MFD_SM501_GPIO - bool "Export GPIO via GPIO layer" - depends on MFD_SM501 && GPIOLIB - ---help--- - This option uses the gpio library layer to export the 64 GPIO - lines on the SM501. The platform data is used to supply the - base number for the first GPIO line to register. +config MFD_DA9052_I2C + bool "Dialog Semiconductor DA9052/53 PMIC variants with I2C" + select REGMAP_I2C + select REGMAP_IRQ + select PMIC_DA9052 + depends on I2C=y && GENERIC_HARDIRQS + help + Support for the Dialog Semiconductor DA9052 PMIC + when controlled using I2C. This driver provides common support + for accessing the device, additional drivers must be enabled in + order to use the functionality of the device. -config MFD_RTSX_PCI - tristate "Support for Realtek PCI-E card reader" - depends on PCI && GENERIC_HARDIRQS +config MFD_DA9055 + bool "Dialog Semiconductor DA9055 PMIC Support" + select REGMAP_I2C + select REGMAP_IRQ select MFD_CORE + depends on I2C=y && GENERIC_HARDIRQS help - This supports for Realtek PCI-Express card reader including rts5209, - rts5229, rtl8411, etc. Realtek card reader supports access to many - types of memory cards, such as Memory Stick, Memory Stick Pro, - Secure Digital and MultiMediaCard. + Say yes here for support of Dialog Semiconductor DA9055. This is + a Power Management IC. This driver provides common support for + accessing the device as well as the I2C interface to the chip itself. + Additional drivers must be enabled in order to use the functionality + of the device. -config MFD_ASIC3 - bool "Support for Compaq ASIC3" - depends on GENERIC_HARDIRQS && GPIOLIB && ARM - select MFD_CORE - ---help--- - This driver supports the ASIC3 multifunction chip found on many - PDAs (mainly iPAQ and HTC based ones) + This driver can be built as a module. If built as a module it will be + called "da9055" -config MFD_DAVINCI_VOICECODEC +config MFD_MC13783 tristate - select MFD_CORE - -config MFD_DM355EVM_MSP - bool "DaVinci DM355 EVM microcontroller" - depends on I2C=y && MACH_DAVINCI_DM355_EVM - help - This driver supports the MSP430 microcontroller used on these - boards. MSP430 firmware manages resets and power sequencing, - inputs from buttons and the IR remote, LEDs, an RTC, and more. -config MFD_TI_SSP - tristate "TI Sequencer Serial Port support" - depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS +config MFD_MC13XXX + tristate + depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS select MFD_CORE - ---help--- - Say Y here if you want support for the Sequencer Serial Port - in a Texas Instruments TNETV107X SoC. + select MFD_MC13783 + help + Enable support for the Freescale MC13783 and MC13892 PMICs. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. - To compile this driver as a module, choose M here: the - module will be called ti-ssp. +config MFD_MC13XXX_SPI + tristate "Freescale MC13783 and MC13892 SPI interface" + depends on SPI_MASTER && GENERIC_HARDIRQS + select REGMAP_SPI + select MFD_MC13XXX + help + Select this if your MC13xxx is connected via an SPI bus. -config MFD_TI_AM335X_TSCADC - tristate "TI ADC / Touch Screen chip support" - select MFD_CORE - select REGMAP - select REGMAP_MMIO - depends on GENERIC_HARDIRQS +config MFD_MC13XXX_I2C + tristate "Freescale MC13892 I2C interface" + depends on I2C && GENERIC_HARDIRQS + select REGMAP_I2C + select MFD_MC13XXX help - If you say yes here you get support for Texas Instruments series - of Touch Screen /ADC chips. - To compile this driver as a module, choose M here: the - module will be called ti_am335x_tscadc. + Select this if your MC13xxx is connected via an I2C bus. config HTC_EGPIO bool "HTC EGPIO support" @@ -172,281 +196,317 @@ config HTC_I2CPLD This device provides input and output GPIOs through an I2C interface to one or more sub-chips. -config UCB1400_CORE - tristate "Philips UCB1400 Core driver" - depends on AC97_BUS - depends on GPIOLIB +config LPC_ICH + tristate "Intel ICH LPC" + depends on PCI && GENERIC_HARDIRQS + select MFD_CORE help - This enables support for the Philips UCB1400 core functions. - The UCB1400 is an AC97 audio codec. - - To compile this driver as a module, choose M here: the - module will be called ucb1400_core. + The LPC bridge function of the Intel ICH provides support for + many functional units. This driver provides needed support for + other drivers to control these functions, currently GPIO and + watchdog. -config MFD_LM3533 - tristate "LM3533 Lighting Power chip" - depends on I2C +config LPC_SCH + tristate "Intel SCH LPC" + depends on PCI && GENERIC_HARDIRQS select MFD_CORE - select REGMAP_I2C - depends on GENERIC_HARDIRQS help - Say yes here to enable support for National Semiconductor / TI - LM3533 Lighting Power chips. - - This driver provides common support for accessing the device; - additional drivers must be enabled in order to use the LED, - backlight or ambient-light-sensor functionality of the device. + LPC bridge function of the Intel SCH provides support for + System Management Bus and General Purpose I/O. -config TPS6105X - tristate "TPS61050/61052 Boost Converters" - depends on I2C - select REGULATOR +config MFD_INTEL_MSIC + bool "Intel MSIC" + depends on INTEL_SCU_IPC select MFD_CORE - select REGULATOR_FIXED_VOLTAGE - depends on GENERIC_HARDIRQS help - This option enables a driver for the TP61050/TPS61052 - high-power "white LED driver". This boost converter is - sometimes used for other things than white LEDs, and - also contains a GPIO pin. + Select this option to enable access to Intel MSIC (Avatele + Passage) chip. This chip embeds audio, battery, GPIO, etc. + devices used in Intel Medfield platforms. -config TPS65010 - tristate "TPS6501x Power Management chips" - depends on I2C && GPIOLIB - default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK +config MFD_JANZ_CMODIO + tristate "Janz CMOD-IO PCI MODULbus Carrier Board" + select MFD_CORE + depends on PCI && GENERIC_HARDIRQS help - If you say yes here you get support for the TPS6501x series of - Power Management chips. These include voltage regulators, - lithium ion/polymer battery charging, and other features that - are often used in portable devices like cell phones and cameras. - - This driver can also be built as a module. If so, the module - will be called tps65010. + This is the core driver for the Janz CMOD-IO PCI MODULbus + carrier board. This device is a PCI to MODULbus bridge which may + host many different types of MODULbus daughterboards, including + CAN and GPIO controllers. -config TPS6507X - tristate "TPS6507x Power Management / Touch Screen chips" +config MFD_JZ4740_ADC + bool "Janz JZ4740 ADC core" select MFD_CORE - depends on I2C && GENERIC_HARDIRQS + select GENERIC_IRQ_CHIP + depends on MACH_JZ4740 help - If you say yes here you get support for the TPS6507x series of - Power Management / Touch Screen chips. These include voltage - regulators, lithium ion/polymer battery charging, touch screen - and other features that are often used in portable devices. - This driver can also be built as a module. If so, the module - will be called tps6507x. + Say yes here if you want support for the ADC unit in the JZ4740 SoC. + This driver is necessary for jz4740-battery and jz4740-hwmon driver. -config MFD_TPS65217 - tristate "TPS65217 Power Management / White LED chips" - depends on I2C && GENERIC_HARDIRQS - select MFD_CORE +config MFD_88PM800 + tristate "Marvell 88PM800" + depends on I2C=y && GENERIC_HARDIRQS select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE help - If you say yes here you get support for the TPS65217 series of - Power Management / White LED chips. - These include voltage regulators, lithium ion/polymer battery - charger, wled and other features that are often used in portable - devices. - - This driver can also be built as a module. If so, the module - will be called tps65217. + This supports for Marvell 88PM800 Power Management IC. + This includes the I2C driver and the core APIs _only_, you have to + select individual components like voltage regulators, RTC and + battery-charger under the corresponding menus. -config MFD_TPS6586X - bool "TPS6586x Power Management chips" +config MFD_88PM805 + tristate "Marvell 88PM805" depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE select REGMAP_I2C + select REGMAP_IRQ + select MFD_CORE help - If you say yes here you get support for the TPS6586X series of - Power Management chips. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + This supports for Marvell 88PM805 Power Management IC. This includes + the I2C driver and the core APIs _only_, you have to select individual + components like codec device, headset/Mic device under the + corresponding menus. - This driver can also be built as a module. If so, the module - will be called tps6586x. +config MFD_88PM860X + bool "Marvell 88PM8606/88PM8607" + depends on I2C=y && GENERIC_HARDIRQS + select REGMAP_I2C + select MFD_CORE + help + This supports for Marvell 88PM8606/88PM8607 Power Management IC. + This includes the I2C driver and the core APIs _only_, you have to + select individual components like voltage regulators, RTC and + battery-charger under the corresponding menus. -config MFD_TPS65910 - bool "TPS65910 Power Management chip" - depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS +config MFD_MAX77686 + bool "Maxim Semiconductor MAX77686 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C - select REGMAP_IRQ select IRQ_DOMAIN help - if you say yes here you get support for the TPS65910 series of - Power Management chips. - -config MFD_TPS65912 - bool - depends on GPIOLIB + Say yes here to support for Maxim Semiconductor MAX77686. + This is a Power Management IC with RTC on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. -config MFD_TPS65912_I2C - bool "TPS65912 Power Management chip with I2C" +config MFD_MAX77693 + bool "Maxim Semiconductor MAX77693 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE - select MFD_TPS65912 - depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS + select REGMAP_I2C help - If you say yes here you get support for the TPS65912 series of - PM chips with I2C interface. + Say yes here to support for Maxim Semiconductor MAX77693. + This is a companion Power Management IC with Flash, Haptic, Charger, + and MUIC(Micro USB Interface Controller) controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. -config MFD_TPS65912_SPI - bool "TPS65912 Power Management chip with SPI" +config MFD_MAX8907 + tristate "Maxim Semiconductor MAX8907 PMIC Support" select MFD_CORE - select MFD_TPS65912 - depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS - help - If you say yes here you get support for the TPS65912 series of - PM chips with SPI interface. - -config MFD_TPS80031 - bool "TI TPS80031/TPS80032 Power Management chips" depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE select REGMAP_I2C select REGMAP_IRQ help - If you say yes here you get support for the Texas Instruments - TPS80031/ TPS80032 Fully Integrated Power Management with Power - Path and Battery Charger. The device provides five configurable - step-down converters, 11 general purpose LDOs, USB OTG Module, - ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with - Power Path from USB, 32K clock generator. + Say yes here to support for Maxim Semiconductor MAX8907. This is + a Power Management IC. This driver provides common support for + accessing the device; additional drivers must be enabled in order + to use the functionality of the device. -config MENELAUS - bool "Texas Instruments TWL92330/Menelaus PM chip" - depends on I2C=y && ARCH_OMAP2 +config MFD_MAX8925 + bool "Maxim Semiconductor MAX8925 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE help - If you say yes here you get support for the Texas Instruments - TWL92330/Menelaus Power Management chip. This include voltage - regulators, Dual slot memory card transceivers, real-time clock - and other features that are often used in portable devices like - cell phones and PDAs. + Say yes here to support for Maxim Semiconductor MAX8925. This is + a Power Management IC. This driver provides common support for + accessing the device, additional drivers must be enabled in order + to use the functionality of the device. -config TWL4030_CORE - bool "Texas Instruments TWL4030/TWL5030/TWL6030/TPS659x0 Support" +config MFD_MAX8997 + bool "Maxim Semiconductor MAX8997/8966 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE select IRQ_DOMAIN - select REGMAP_I2C help - Say yes here if you have TWL4030 / TWL6030 family chip on your board. - This core driver provides register access and IRQ handling - facilities, and registers devices for the various functions - so that function-specific drivers can bind to them. - - These multi-function chips are found on many OMAP2 and OMAP3 - boards, providing power management, RTC, GPIO, keypad, a - high speed USB OTG transceiver, an audio codec (on most - versions) and many other features. + Say yes here to support for Maxim Semiconductor MAX8997/8966. + This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, + MUIC controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. -config TWL4030_MADC - tristate "Texas Instruments TWL4030 MADC" - depends on TWL4030_CORE +config MFD_MAX8998 + bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE help - This driver provides support for triton TWL4030-MADC. The - driver supports both RT and SW conversion methods. - - This driver can be built as a module. If so it will be - named twl4030-madc + Say yes here to support for Maxim Semiconductor MAX8998 and + National Semiconductor LP3974. This is a Power Management IC. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device. -config TWL4030_POWER - bool "Support power resources on TWL4030 family chips" - depends on TWL4030_CORE && ARM +config EZX_PCAP + bool "Motorola EZXPCAP Support" + depends on GENERIC_HARDIRQS && SPI_MASTER help - Say yes here if you want to use the power resources on the - TWL4030 family chips. Most of these resources are regulators, - which have a separate driver; some are control signals, such - as clock request handshaking. - - This driver uses board-specific data to initialize the resources - and load scripts controlling which resources are switched off/on - or reset when a sleep, wakeup or warm reset event occurs. + This enables the PCAP ASIC present on EZX Phones. This is + needed for MMC, TouchScreen, Sound, USB, etc.. -config MFD_TWL4030_AUDIO - bool - depends on TWL4030_CORE && GENERIC_HARDIRQS +config MFD_VIPERBOARD + tristate "Nano River Technologies Viperboard" select MFD_CORE + depends on USB && GENERIC_HARDIRQS default n + help + Say yes here if you want support for Nano River Technologies + Viperboard. + There are mfd cell drivers available for i2c master, adc and + both gpios found on the board. The spi part does not yet + have a driver. + You need to select the mfd cell drivers separately. + The drivers do not support all features the board exposes. -config TWL6040_CORE - bool "Support for TWL6040 audio codec" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_RETU + tristate "Nokia Retu multi-function device" select MFD_CORE - select REGMAP_I2C + depends on I2C && GENERIC_HARDIRQS select REGMAP_IRQ - default n help - Say yes here if you want support for Texas Instruments TWL6040 audio - codec. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device (audio, vibra). + Retu is a multi-function device found on Nokia Internet Tablets + (770, N800 and N810). -config MFD_STMPE - bool "Support STMicroelectronics STMPE" - depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS - select MFD_CORE +config MFD_PCF50633 + tristate "NXP PCF50633" + depends on I2C + select REGMAP_I2C help - Support for the STMPE family of I/O Expanders from - STMicroelectronics. - - Currently supported devices are: - - STMPE811: GPIO, Touchscreen - STMPE1601: GPIO, Keypad - STMPE1801: GPIO, Keypad - STMPE2401: GPIO, Keypad - STMPE2403: GPIO, Keypad - - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the functionality - of the device. Currently available sub drivers are: - - GPIO: stmpe-gpio - Keypad: stmpe-keypad - Touchscreen: stmpe-ts - -menu "STMPE Interface Drivers" -depends on MFD_STMPE + Say yes here if you have NXP PCF50633 chip on your board. + This core driver provides register access and IRQ handling + facilities, and registers devices for the various functions + so that function-specific drivers can bind to them. -config STMPE_I2C - bool "STMPE I2C Inteface" - depends on I2C=y - default y +config PCF50633_ADC + tristate "NXP PCF50633 ADC" + depends on MFD_PCF50633 help - This is used to enable I2C interface of STMPE + Say yes here if you want to include support for ADC in the + NXP PCF50633 chip. -config STMPE_SPI - bool "STMPE SPI Inteface" - depends on SPI_MASTER +config PCF50633_GPIO + tristate "NXP PCF50633 GPIO" + depends on MFD_PCF50633 help - This is used to enable SPI interface of STMPE -endmenu + Say yes here if you want to include support GPIO for pins on + the PCF50633 chip. -config MFD_TC3589X - bool "Support Toshiba TC35892 and variants" - depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE +config UCB1400_CORE + tristate "Philips UCB1400 Core driver" + depends on AC97_BUS + depends on GPIOLIB help - Support for the Toshiba TC35892 and variants I/O Expander. + This enables support for the Philips UCB1400 core functions. + The UCB1400 is an AC97 audio codec. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + To compile this driver as a module, choose M here: the + module will be called ucb1400_core. -config MFD_TMIO - bool - default n +config MFD_PM8XXX + tristate -config MFD_T7L66XB - bool "Support Toshiba T7L66XB" - depends on ARM && HAVE_CLK && GENERIC_HARDIRQS +config MFD_PM8921_CORE + tristate "Qualcomm PM8921 PMIC chip" + depends on MSM_SSBI select MFD_CORE - select MFD_TMIO + select MFD_PM8XXX help - Support for Toshiba Mobile IO Controller T7L66XB + If you say yes to this option, support will be included for the + built-in PM8921 PMIC chip. + + This is required if your board has a PM8921 and uses its features, + such as: MPPs, GPIOs, regulators, interrupts, and PWM. + + Say M here if you want to include support for PM8921 chip as a module. + This will build a module called "pm8921-core". + +config MFD_PM8XXX_IRQ + bool "Qualcomm PM8xxx IRQ features" + depends on MFD_PM8XXX + default y if MFD_PM8XXX + help + This is the IRQ driver for Qualcomm PM 8xxx PMIC chips. + + This is required to use certain other PM 8xxx features, such as GPIO + and MPP. + +config MFD_RDC321X + tristate "RDC R-321x southbridge" + select MFD_CORE + depends on PCI && GENERIC_HARDIRQS + help + Say yes here if you want to have support for the RDC R-321x SoC + southbridge which provides access to GPIOs and Watchdog using the + southbridge PCI device configuration space. + +config MFD_RTSX_PCI + tristate "Realtek PCI-E card reader" + depends on PCI && GENERIC_HARDIRQS + select MFD_CORE + help + This supports for Realtek PCI-Express card reader including rts5209, + rts5229, rtl8411, etc. Realtek card reader supports access to many + types of memory cards, such as Memory Stick, Memory Stick Pro, + Secure Digital and MultiMediaCard. + +config MFD_RC5T583 + bool "Ricoh RC5T583 Power Management system device" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + help + Select this option to get support for the RICOH583 Power + Management system device. + This driver provides common support for accessing the device + through i2c interface. The device supports multiple sub-devices + like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. + Additional drivers must be enabled in order to use the + different functionality of the device. + +config MFD_SEC_CORE + bool "SAMSUNG Electronics PMIC Series Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C + select REGMAP_IRQ + help + Support for the Samsung Electronics MFD series. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device + +config MFD_SM501 + tristate "Silicon Motion SM501" + ---help--- + This is the core driver for the Silicon Motion SM501 multimedia + companion chip. This device is a multifunction device which may + provide numerous interfaces including USB host controller, USB gadget, + asynchronous serial ports, audio functions, and a dual display video + interface. The device may be connected by PCI or local bus with + varying functions enabled. + +config MFD_SM501_GPIO + bool "Export GPIO via GPIO layer" + depends on MFD_SM501 && GPIOLIB + ---help--- + This option uses the gpio library layer to export the 64 GPIO + lines on the SM501. The platform data is used to supply the + base number for the first GPIO line to register. config MFD_SMSC - bool "Support for the SMSC ECE1099 series chips" + bool "SMSC ECE1099 series chips" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C @@ -457,433 +517,425 @@ config MFD_SMSC To compile this driver as a module, choose M here: the module will be called smsc. -config MFD_TC6387XB - bool "Support Toshiba TC6387XB" - depends on ARM && HAVE_CLK - select MFD_CORE - select MFD_TMIO +config ABX500_CORE + bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" + default y if ARCH_U300 || ARCH_U8500 help - Support for Toshiba Mobile IO Controller TC6387XB + Say yes here if you have the ABX500 Mixed Signal IC family + chips. This core driver expose register access functions. + Functionality specific drivers using these functions can + remain unchanged when IC changes. Binding of the functions to + actual register access is done by the IC core driver. -config MFD_TC6393XB - bool "Support Toshiba TC6393XB" - depends on ARM && HAVE_CLK - select GPIOLIB +config AB3100_CORE + bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" + depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS select MFD_CORE - select MFD_TMIO + default y if ARCH_U300 help - Support for Toshiba Mobile IO Controller TC6393XB + Select this to enable the AB3100 Mixed Signal IC core + functionality. This connects to a AB3100 on the I2C bus + and expose a number of symbols needed for dependent devices + to read and write registers and subscribe to events from + this multi-functional IC. This is needed to use other features + of the AB3100 such as battery-backed RTC, charging control, + LEDs, vibrator, system power and temperature, power management + and ALSA sound. -config PMIC_DA903X - bool "Dialog Semiconductor DA9030/DA9034 PMIC Support" - depends on I2C=y +config AB3100_OTP + tristate "ST-Ericsson AB3100 OTP functions" + depends on AB3100_CORE + default y if AB3100_CORE help - Say yes here to support for Dialog Semiconductor DA9030 (a.k.a - ARAVA) and DA9034 (a.k.a MICCO), these are Power Management IC - usually found on PXA processors-based platforms. This includes - the I2C driver and the core APIs _only_, you have to select - individual components like LCD backlight, voltage regulators, - LEDs and battery-charger under the corresponding menus. + Select this to enable the AB3100 Mixed Signal IC OTP (one-time + programmable memory) support. This exposes a sysfs file to read + out OTP values. -config PMIC_DA9052 - bool +config AB8500_CORE + bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" + depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU + select POWER_SUPPLY select MFD_CORE + select IRQ_DOMAIN + help + Select this option to enable access to AB8500 power management + chip. This connects to U8500 either on the SSP/SPI bus (deprecated + since hardware version v1.0) or the I2C bus via PRCMU. It also adds + the irq_chip parts for handling the Mixed Signal chip events. + This chip embeds various other multimedia funtionalities as well. -config MFD_DA9052_SPI - bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI" - select REGMAP_SPI - select REGMAP_IRQ - select PMIC_DA9052 - depends on SPI_MASTER=y && GENERIC_HARDIRQS +config AB8500_DEBUG + bool "Enable debug info via debugfs" + depends on AB8500_CORE && DEBUG_FS + default y if DEBUG_FS + help + Select this option if you want debug information using the debug + filesystem, debugfs. + +config AB8500_GPADC + bool "ST-Ericsson AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y help - Support for the Dialog Semiconductor DA9052 PMIC - when controlled using SPI. This driver provides common support - for accessing the device, additional drivers must be enabled in - order to use the functionality of the device. + AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage -config MFD_DA9052_I2C - bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C" - select REGMAP_I2C - select REGMAP_IRQ - select PMIC_DA9052 - depends on I2C=y && GENERIC_HARDIRQS +config MFD_DB8500_PRCMU + bool "ST-Ericsson DB8500 Power Reset Control Management Unit" + depends on UX500_SOC_DB8500 + select MFD_CORE help - Support for the Dialog Semiconductor DA9052 PMIC - when controlled using I2C. This driver provides common support - for accessing the device, additional drivers must be enabled in - order to use the functionality of the device. + Select this option to enable support for the DB8500 Power Reset + and Control Management Unit. This is basically an autonomous + system controller running an XP70 microprocessor, which is accessed + through a register map. -config MFD_DA9055 - bool "Dialog Semiconductor DA9055 PMIC Support" - select REGMAP_I2C - select REGMAP_IRQ +config MFD_STMPE + bool "STMicroelectronics STMPE" + depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS select MFD_CORE - depends on I2C=y && GENERIC_HARDIRQS help - Say yes here for support of Dialog Semiconductor DA9055. This is - a Power Management IC. This driver provides common support for - accessing the device as well as the I2C interface to the chip itself. - Additional drivers must be enabled in order to use the functionality - of the device. + Support for the STMPE family of I/O Expanders from + STMicroelectronics. - This driver can be built as a module. If built as a module it will be - called "da9055" + Currently supported devices are: -config PMIC_ADP5520 - bool "Analog Devices ADP5520/01 MFD PMIC Core Support" + STMPE811: GPIO, Touchscreen + STMPE1601: GPIO, Keypad + STMPE1801: GPIO, Keypad + STMPE2401: GPIO, Keypad + STMPE2403: GPIO, Keypad + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device. Currently available sub drivers are: + + GPIO: stmpe-gpio + Keypad: stmpe-keypad + Touchscreen: stmpe-ts + +menu "STMicroelectronics STMPE Interface Drivers" +depends on MFD_STMPE + +config STMPE_I2C + bool "STMicroelectronics STMPE I2C Inteface" depends on I2C=y + default y help - Say yes here to add support for Analog Devices AD5520 and ADP5501, - Multifunction Power Management IC. This includes - the I2C driver and the core APIs _only_, you have to select - individual components like LCD backlight, LEDs, GPIOs and Kepad - under the corresponding menus. + This is used to enable I2C interface of STMPE -config MFD_LP8788 - bool "Texas Instruments LP8788 Power Management Unit Driver" - depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE - select REGMAP_I2C - select IRQ_DOMAIN +config STMPE_SPI + bool "STMicroelectronics STMPE SPI Inteface" + depends on SPI_MASTER help - TI LP8788 PMU supports regulators, battery charger, RTC, - ADC, backlight driver and current sinks. + This is used to enable SPI interface of STMPE +endmenu -config MFD_MAX77686 - bool "Maxim Semiconductor MAX77686 PMIC Support" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_STA2X11 + bool "STMicroelectronics STA2X11" + depends on STA2X11 && GENERIC_HARDIRQS select MFD_CORE - select REGMAP_I2C - select IRQ_DOMAIN + select REGMAP_MMIO + +config MFD_SYSCON + bool "System Controller Register R/W Based on Regmap" + select REGMAP_MMIO help - Say yes here to support for Maxim Semiconductor MAX77686. - This is a Power Management IC with RTC on chip. - This driver provides common support for accessing the device; - additional drivers must be enabled in order to use the functionality - of the device. + Select this option to enable accessing system control registers + via regmap. -config MFD_MAX77693 - bool "Maxim Semiconductor MAX77693 PMIC Support" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_DAVINCI_VOICECODEC + tristate select MFD_CORE - select REGMAP_I2C - help - Say yes here to support for Maxim Semiconductor MAX77693. - This is a companion Power Management IC with Flash, Haptic, Charger, - and MUIC(Micro USB Interface Controller) controls on chip. - This driver provides common support for accessing the device; - additional drivers must be enabled in order to use the functionality - of the device. -config MFD_MAX8907 - tristate "Maxim Semiconductor MAX8907 PMIC Support" +config MFD_TI_AM335X_TSCADC + tristate "TI ADC / Touch Screen chip support" select MFD_CORE - depends on I2C=y && GENERIC_HARDIRQS - select REGMAP_I2C - select REGMAP_IRQ + select REGMAP + select REGMAP_MMIO + depends on GENERIC_HARDIRQS help - Say yes here to support for Maxim Semiconductor MAX8907. This is - a Power Management IC. This driver provides common support for - accessing the device; additional drivers must be enabled in order - to use the functionality of the device. + If you say yes here you get support for Texas Instruments series + of Touch Screen /ADC chips. + To compile this driver as a module, choose M here: the + module will be called ti_am335x_tscadc. -config MFD_MAX8925 - bool "Maxim Semiconductor MAX8925 PMIC Support" - depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE +config MFD_DM355EVM_MSP + bool "TI DaVinci DM355 EVM microcontroller" + depends on I2C=y && MACH_DAVINCI_DM355_EVM help - Say yes here to support for Maxim Semiconductor MAX8925. This is - a Power Management IC. This driver provides common support for - accessing the device, additional drivers must be enabled in order - to use the functionality of the device. + This driver supports the MSP430 microcontroller used on these + boards. MSP430 firmware manages resets and power sequencing, + inputs from buttons and the IR remote, LEDs, an RTC, and more. -config MFD_MAX8997 - bool "Maxim Semiconductor MAX8997/8966 PMIC Support" +config MFD_LP8788 + bool "TI LP8788 Power Management Unit Driver" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE + select REGMAP_I2C select IRQ_DOMAIN help - Say yes here to support for Maxim Semiconductor MAX8997/8966. - This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, - MUIC controls on chip. - This driver provides common support for accessing the device; - additional drivers must be enabled in order to use the functionality - of the device. + TI LP8788 PMU supports regulators, battery charger, RTC, + ADC, backlight driver and current sinks. -config MFD_MAX8998 - bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" - depends on I2C=y && GENERIC_HARDIRQS - select MFD_CORE +config MFD_OMAP_USB_HOST + bool "TI OMAP USBHS core and TLL driver" + depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 + default y help - Say yes here to support for Maxim Semiconductor MAX8998 and - National Semiconductor LP3974. This is a Power Management IC. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the functionality - of the device. + This is the core driver for the OAMP EHCI and OHCI drivers. + This MFD driver does the required setup functionalities for + OMAP USB Host drivers. -config MFD_SEC_CORE - bool "SAMSUNG Electronics PMIC Series Support" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_PALMAS + bool "TI Palmas series chips" select MFD_CORE select REGMAP_I2C select REGMAP_IRQ + depends on I2C=y && GENERIC_HARDIRQS help - Support for the Samsung Electronics MFD series. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the functionality - of the device + If you say yes here you get support for the Palmas + series of PMIC chips from Texas Instruments. -config MFD_ARIZONA - select REGMAP - select REGMAP_IRQ +config MFD_TI_SSP + tristate "TI Sequencer Serial Port support" + depends on ARCH_DAVINCI_TNETV107X && GENERIC_HARDIRQS select MFD_CORE - bool + ---help--- + Say Y here if you want support for the Sequencer Serial Port + in a Texas Instruments TNETV107X SoC. -config MFD_ARIZONA_I2C - tristate "Support Wolfson Microelectronics Arizona platform with I2C" - select MFD_ARIZONA + To compile this driver as a module, choose M here: the + module will be called ti-ssp. + +config TPS6105X + tristate "TI TPS61050/61052 Boost Converters" + depends on I2C + select REGULATOR + select MFD_CORE + select REGULATOR_FIXED_VOLTAGE + depends on GENERIC_HARDIRQS + help + This option enables a driver for the TP61050/TPS61052 + high-power "white LED driver". This boost converter is + sometimes used for other things than white LEDs, and + also contains a GPIO pin. + +config TPS65010 + tristate "TI TPS6501x Power Management chips" + depends on I2C && GPIOLIB + default y if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_OSK + help + If you say yes here you get support for the TPS6501x series of + Power Management chips. These include voltage regulators, + lithium ion/polymer battery charging, and other features that + are often used in portable devices like cell phones and cameras. + + This driver can also be built as a module. If so, the module + will be called tps65010. + +config TPS6507X + tristate "TI TPS6507x Power Management / Touch Screen chips" select MFD_CORE - select REGMAP_I2C depends on I2C && GENERIC_HARDIRQS help - Support for the Wolfson Microelectronics Arizona platform audio SoC - core functionality controlled via I2C. + If you say yes here you get support for the TPS6507x series of + Power Management / Touch Screen chips. These include voltage + regulators, lithium ion/polymer battery charging, touch screen + and other features that are often used in portable devices. + This driver can also be built as a module. If so, the module + will be called tps6507x. -config MFD_ARIZONA_SPI - tristate "Support Wolfson Microelectronics Arizona platform with SPI" - select MFD_ARIZONA +config TPS65911_COMPARATOR + tristate + +config MFD_TPS65090 + bool "TI TPS65090 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE - select REGMAP_SPI - depends on SPI_MASTER && GENERIC_HARDIRQS + select REGMAP_I2C + select REGMAP_IRQ help - Support for the Wolfson Microelectronics Arizona platform audio SoC - core functionality controlled via I2C. + If you say yes here you get support for the TPS65090 series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. -config MFD_WM5102 - bool "Support Wolfson Microelectronics WM5102" - depends on MFD_ARIZONA +config MFD_TPS65217 + tristate "TI TPS65217 Power Management / White LED chips" + depends on I2C && GENERIC_HARDIRQS + select MFD_CORE + select REGMAP_I2C help - Support for Wolfson Microelectronics WM5102 low power audio SoC + If you say yes here you get support for the TPS65217 series of + Power Management / White LED chips. + These include voltage regulators, lithium ion/polymer battery + charger, wled and other features that are often used in portable + devices. -config MFD_WM5110 - bool "Support Wolfson Microelectronics WM5110" - depends on MFD_ARIZONA - help - Support for Wolfson Microelectronics WM5110 low power audio SoC + This driver can also be built as a module. If so, the module + will be called tps65217. -config MFD_WM8400 - bool "Support Wolfson Microelectronics WM8400" - select MFD_CORE +config MFD_TPS6586X + bool "TI TPS6586x Power Management chips" depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE select REGMAP_I2C help - Support for the Wolfson Microelecronics WM8400 PMIC and audio - CODEC. This driver provides common support for accessing - the device, additional drivers must be enabled in order to use - the functionality of the device. + If you say yes here you get support for the TPS6586X series of + Power Management chips. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. -config MFD_WM831X - bool - depends on GENERIC_HARDIRQS + This driver can also be built as a module. If so, the module + will be called tps6586x. -config MFD_WM831X_I2C - bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C" +config MFD_TPS65910 + bool "TI TPS65910 Power Management chip" + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS select MFD_CORE - select MFD_WM831X select REGMAP_I2C + select REGMAP_IRQ select IRQ_DOMAIN - depends on I2C=y && GENERIC_HARDIRQS help - Support for the Wolfson Microelecronics WM831x and WM832x PMICs - when controlled using I2C. This driver provides common support - for accessing the device, additional drivers must be enabled in - order to use the functionality of the device. + if you say yes here you get support for the TPS65910 series of + Power Management chips. -config MFD_WM831X_SPI - bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI" - select MFD_CORE - select MFD_WM831X - select REGMAP_SPI - select IRQ_DOMAIN - depends on SPI_MASTER && GENERIC_HARDIRQS +config MFD_TPS65912 + bool "TI TPS65912 Power Management chip" + depends on GPIOLIB help - Support for the Wolfson Microelecronics WM831x and WM832x PMICs - when controlled using SPI. This driver provides common support - for accessing the device, additional drivers must be enabled in - order to use the functionality of the device. + If you say yes here you get support for the TPS65912 series of + PM chips. -config MFD_WM8350 - bool - depends on GENERIC_HARDIRQS +config MFD_TPS65912_I2C + bool "TI TPS65912 Power Management chip with I2C" + select MFD_CORE + select MFD_TPS65912 + depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS + help + If you say yes here you get support for the TPS65912 series of + PM chips with I2C interface. -config MFD_WM8350_I2C - bool "Support Wolfson Microelectronics WM8350 with I2C" - select MFD_WM8350 - depends on I2C=y && GENERIC_HARDIRQS +config MFD_TPS65912_SPI + bool "TI TPS65912 Power Management chip with SPI" + select MFD_CORE + select MFD_TPS65912 + depends on SPI_MASTER && GPIOLIB && GENERIC_HARDIRQS help - The WM8350 is an integrated audio and power management - subsystem with watchdog and RTC functionality for embedded - systems. This option enables core support for the WM8350 with - I2C as the control interface. Additional options must be - selected to enable support for the functionality of the chip. + If you say yes here you get support for the TPS65912 series of + PM chips with SPI interface. -config MFD_WM8994 - bool "Support Wolfson Microelectronics WM8994" +config MFD_TPS80031 + bool "TI TPS80031/TPS80032 Power Management chips" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE select REGMAP_I2C select REGMAP_IRQ - depends on I2C=y && GENERIC_HARDIRQS help - The WM8994 is a highly integrated hi-fi CODEC designed for - smartphone applicatiosn. As well as audio functionality it - has on board GPIO and regulator functionality which is - supported via the relevant subsystems. This driver provides - core support for the WM8994, in order to use the actual - functionaltiy of the device other drivers must be enabled. + If you say yes here you get support for the Texas Instruments + TPS80031/ TPS80032 Fully Integrated Power Management with Power + Path and Battery Charger. The device provides five configurable + step-down converters, 11 general purpose LDOs, USB OTG Module, + ADC, RTC, 2 PWM, System Voltage Regulator/Battery Charger with + Power Path from USB, 32K clock generator. -config MFD_PCF50633 - tristate "Support for NXP PCF50633" - depends on I2C +config TWL4030_CORE + bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support" + depends on I2C=y && GENERIC_HARDIRQS + select IRQ_DOMAIN select REGMAP_I2C help - Say yes here if you have NXP PCF50633 chip on your board. + Say yes here if you have TWL4030 / TWL6030 family chip on your board. This core driver provides register access and IRQ handling facilities, and registers devices for the various functions so that function-specific drivers can bind to them. -config PCF50633_ADC - tristate "Support for NXP PCF50633 ADC" - depends on MFD_PCF50633 - help - Say yes here if you want to include support for ADC in the - NXP PCF50633 chip. - -config PCF50633_GPIO - tristate "Support for NXP PCF50633 GPIO" - depends on MFD_PCF50633 - help - Say yes here if you want to include support GPIO for pins on - the PCF50633 chip. - -config MFD_MC13783 - tristate + These multi-function chips are found on many OMAP2 and OMAP3 + boards, providing power management, RTC, GPIO, keypad, a + high speed USB OTG transceiver, an audio codec (on most + versions) and many other features. -config MFD_MC13XXX - tristate - depends on (SPI_MASTER || I2C) && GENERIC_HARDIRQS - select MFD_CORE - select MFD_MC13783 +config TWL4030_MADC + tristate "TI TWL4030 MADC" + depends on TWL4030_CORE help - Enable support for the Freescale MC13783 and MC13892 PMICs. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + This driver provides support for triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. -config MFD_MC13XXX_SPI - tristate "Freescale MC13783 and MC13892 SPI interface" - depends on SPI_MASTER && GENERIC_HARDIRQS - select REGMAP_SPI - select MFD_MC13XXX - help - Select this if your MC13xxx is connected via an SPI bus. + This driver can be built as a module. If so it will be + named twl4030-madc -config MFD_MC13XXX_I2C - tristate "Freescale MC13892 I2C interface" - depends on I2C && GENERIC_HARDIRQS - select REGMAP_I2C - select MFD_MC13XXX +config TWL4030_POWER + bool "TI TWL4030 power resources" + depends on TWL4030_CORE && ARM help - Select this if your MC13xxx is connected via an I2C bus. + Say yes here if you want to use the power resources on the + TWL4030 family chips. Most of these resources are regulators, + which have a separate driver; some are control signals, such + as clock request handshaking. -config ABX500_CORE - bool "ST-Ericsson ABX500 Mixed Signal Circuit register functions" - default y if ARCH_U300 || ARCH_U8500 - help - Say yes here if you have the ABX500 Mixed Signal IC family - chips. This core driver expose register access functions. - Functionality specific drivers using these functions can - remain unchanged when IC changes. Binding of the functions to - actual register access is done by the IC core driver. + This driver uses board-specific data to initialize the resources + and load scripts controlling which resources are switched off/on + or reset when a sleep, wakeup or warm reset event occurs. -config AB3100_CORE - bool "ST-Ericsson AB3100 Mixed Signal Circuit core functions" - depends on I2C=y && ABX500_CORE && GENERIC_HARDIRQS +config MFD_TWL4030_AUDIO + bool "TI TWL4030 Audio" + depends on TWL4030_CORE && GENERIC_HARDIRQS select MFD_CORE - default y if ARCH_U300 - help - Select this to enable the AB3100 Mixed Signal IC core - functionality. This connects to a AB3100 on the I2C bus - and expose a number of symbols needed for dependent devices - to read and write registers and subscribe to events from - this multi-functional IC. This is needed to use other features - of the AB3100 such as battery-backed RTC, charging control, - LEDs, vibrator, system power and temperature, power management - and ALSA sound. - -config AB3100_OTP - tristate "ST-Ericsson AB3100 OTP functions" - depends on AB3100_CORE - default y if AB3100_CORE - help - Select this to enable the AB3100 Mixed Signal IC OTP (one-time - programmable memory) support. This exposes a sysfs file to read - out OTP values. + default n -config AB8500_CORE - bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" - depends on GENERIC_HARDIRQS && ABX500_CORE && MFD_DB8500_PRCMU - select POWER_SUPPLY +config TWL6040_CORE + bool "TI TWL6040 audio codec" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE - select IRQ_DOMAIN - help - Select this option to enable access to AB8500 power management - chip. This connects to U8500 either on the SSP/SPI bus (deprecated - since hardware version v1.0) or the I2C bus via PRCMU. It also adds - the irq_chip parts for handling the Mixed Signal chip events. - This chip embeds various other multimedia funtionalities as well. - -config AB8500_DEBUG - bool "Enable debug info via debugfs" - depends on AB8500_CORE && DEBUG_FS - default y if DEBUG_FS - help - Select this option if you want debug information using the debug - filesystem, debugfs. - -config AB8500_GPADC - bool "AB8500 GPADC driver" - depends on AB8500_CORE && REGULATOR_AB8500 - default y + select REGMAP_I2C + select REGMAP_IRQ + default n help - AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage + Say yes here if you want support for Texas Instruments TWL6040 audio + codec. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device (audio, vibra). -config EZX_PCAP - bool "PCAP Support" - depends on GENERIC_HARDIRQS && SPI_MASTER +config MENELAUS + bool "TI TWL92330/Menelaus PM chip" + depends on I2C=y && ARCH_OMAP2 help - This enables the PCAP ASIC present on EZX Phones. This is - needed for MMC, TouchScreen, Sound, USB, etc.. + If you say yes here you get support for the Texas Instruments + TWL92330/Menelaus Power Management chip. This include voltage + regulators, Dual slot memory card transceivers, real-time clock + and other features that are often used in portable devices like + cell phones and PDAs. -config MFD_DB8500_PRCMU - bool "ST-Ericsson DB8500 Power Reset Control Management Unit" - depends on UX500_SOC_DB8500 +config MFD_WL1273_CORE + tristate "TI WL1273 FM radio" + depends on I2C && GENERIC_HARDIRQS select MFD_CORE + default n help - Select this option to enable support for the DB8500 Power Reset - and Control Management Unit. This is basically an autonomous - system controller running an XP70 microprocessor, which is accessed - through a register map. + This is the core driver for the TI WL1273 FM radio. This MFD + driver connects the radio-wl1273 V4L2 module and the wl1273 + audio codec. -config MFD_CS5535 - tristate "Support for CS5535 and CS5536 southbridge core functions" +config MFD_LM3533 + tristate "TI/National Semiconductor LM3533 Lighting Power chip" + depends on I2C select MFD_CORE - depends on PCI && X86 - ---help--- - This is the core driver for CS5535/CS5536 MFD functions. This is - necessary for using the board's GPIO and MFGPT functionality. + select REGMAP_I2C + depends on GENERIC_HARDIRQS + help + Say yes here to enable support for National Semiconductor / TI + LM3533 Lighting Power chips. + + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the LED, + backlight or ambient-light-sensor functionality of the device. config MFD_TIMBERDALE - tristate "Support for the Timberdale FPGA" + tristate "Timberdale FPGA" select MFD_CORE depends on PCI && GPIOLIB ---help--- @@ -893,54 +945,48 @@ config MFD_TIMBERDALE The timberdale FPGA can be found on the Intel Atom development board for in-vehicle infontainment, called Russellville. -config LPC_SCH - tristate "Intel SCH LPC" - depends on PCI && GENERIC_HARDIRQS +config MFD_TC3589X + bool "Toshiba TC35892 and variants" + depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE help - LPC bridge function of the Intel SCH provides support for - System Management Bus and General Purpose I/O. + Support for the Toshiba TC35892 and variants I/O Expander. -config LPC_ICH - tristate "Intel ICH LPC" - depends on PCI && GENERIC_HARDIRQS - select MFD_CORE - help - The LPC bridge function of the Intel ICH provides support for - many functional units. This driver provides needed support for - other drivers to control these functions, currently GPIO and - watchdog. + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the + functionality of the device. -config MFD_RDC321X - tristate "Support for RDC-R321x southbridge" +config MFD_TMIO + bool + default n + +config MFD_T7L66XB + bool "Toshiba T7L66XB" + depends on ARM && HAVE_CLK && GENERIC_HARDIRQS select MFD_CORE - depends on PCI && GENERIC_HARDIRQS + select MFD_TMIO help - Say yes here if you want to have support for the RDC R-321x SoC - southbridge which provides access to GPIOs and Watchdog using the - southbridge PCI device configuration space. + Support for Toshiba Mobile IO Controller T7L66XB -config MFD_JANZ_CMODIO - tristate "Support for Janz CMOD-IO PCI MODULbus Carrier Board" +config MFD_TC6387XB + bool "Toshiba TC6387XB" + depends on ARM && HAVE_CLK select MFD_CORE - depends on PCI && GENERIC_HARDIRQS + select MFD_TMIO help - This is the core driver for the Janz CMOD-IO PCI MODULbus - carrier board. This device is a PCI to MODULbus bridge which may - host many different types of MODULbus daughterboards, including - CAN and GPIO controllers. + Support for Toshiba Mobile IO Controller TC6387XB -config MFD_JZ4740_ADC - bool "Support for the JZ4740 SoC ADC core" +config MFD_TC6393XB + bool "Toshiba TC6393XB" + depends on ARM && HAVE_CLK + select GPIOLIB select MFD_CORE - select GENERIC_IRQ_CHIP - depends on MACH_JZ4740 + select MFD_TMIO help - Say yes here if you want support for the ADC unit in the JZ4740 SoC. - This driver is necessary for jz4740-battery and jz4740-hwmon driver. + Support for Toshiba Mobile IO Controller TC6393XB config MFD_VX855 - tristate "Support for VIA VX855/VX875 integrated south bridge" + tristate "VIA VX855/VX875 integrated south bridge" depends on PCI && GENERIC_HARDIRQS select MFD_CORE help @@ -948,156 +994,113 @@ config MFD_VX855 VIA VX855/VX875 south bridge. You will need to enable the vx855_spi and/or vx855_gpio drivers for this to do anything useful. -config MFD_WL1273_CORE - tristate "Support for TI WL1273 FM radio." - depends on I2C && GENERIC_HARDIRQS +config MFD_ARIZONA + select REGMAP + select REGMAP_IRQ select MFD_CORE - default n - help - This is the core driver for the TI WL1273 FM radio. This MFD - driver connects the radio-wl1273 V4L2 module and the wl1273 - audio codec. - -config MFD_OMAP_USB_HOST - bool "Support OMAP USBHS core and TLL driver" - depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 - default y - help - This is the core driver for the OAMP EHCI and OHCI drivers. - This MFD driver does the required setup functionalities for - OMAP USB Host drivers. - -config MFD_PM8XXX - tristate + bool -config MFD_PM8921_CORE - tristate "Qualcomm PM8921 PMIC chip" - depends on MSM_SSBI +config MFD_ARIZONA_I2C + tristate "Wolfson Microelectronics Arizona platform with I2C" + select MFD_ARIZONA select MFD_CORE - select MFD_PM8XXX - help - If you say yes to this option, support will be included for the - built-in PM8921 PMIC chip. - - This is required if your board has a PM8921 and uses its features, - such as: MPPs, GPIOs, regulators, interrupts, and PWM. - - Say M here if you want to include support for PM8921 chip as a module. - This will build a module called "pm8921-core". - -config MFD_PM8XXX_IRQ - bool "Support for Qualcomm PM8xxx IRQ features" - depends on MFD_PM8XXX - default y if MFD_PM8XXX + select REGMAP_I2C + depends on I2C && GENERIC_HARDIRQS help - This is the IRQ driver for Qualcomm PM 8xxx PMIC chips. - - This is required to use certain other PM 8xxx features, such as GPIO - and MPP. - -config TPS65911_COMPARATOR - tristate + Support for the Wolfson Microelectronics Arizona platform audio SoC + core functionality controlled via I2C. -config MFD_TPS65090 - bool "TPS65090 Power Management chips" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_ARIZONA_SPI + tristate "Wolfson Microelectronics Arizona platform with SPI" + select MFD_ARIZONA select MFD_CORE - select REGMAP_I2C - select REGMAP_IRQ + select REGMAP_SPI + depends on SPI_MASTER && GENERIC_HARDIRQS help - If you say yes here you get support for the TPS65090 series of - Power Management chips. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + Support for the Wolfson Microelectronics Arizona platform audio SoC + core functionality controlled via I2C. -config MFD_AAT2870_CORE - bool "Support for the AnalogicTech AAT2870" - select MFD_CORE - depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS +config MFD_WM5102 + bool "Wolfson Microelectronics WM5102" + depends on MFD_ARIZONA help - If you say yes here you get support for the AAT2870. - This driver provides common support for accessing the device, - additional drivers must be enabled in order to use the - functionality of the device. + Support for Wolfson Microelectronics WM5102 low power audio SoC -config MFD_INTEL_MSIC - bool "Support for Intel MSIC" - depends on INTEL_SCU_IPC - select MFD_CORE +config MFD_WM5110 + bool "Wolfson Microelectronics WM5110" + depends on MFD_ARIZONA help - Select this option to enable access to Intel MSIC (Avatele - Passage) chip. This chip embeds audio, battery, GPIO, etc. - devices used in Intel Medfield platforms. + Support for Wolfson Microelectronics WM5110 low power audio SoC -config MFD_RC5T583 - bool "Ricoh RC5T583 Power Management system device" - depends on I2C=y && GENERIC_HARDIRQS +config MFD_WM8400 + bool "Wolfson Microelectronics WM8400" select MFD_CORE + depends on I2C=y && GENERIC_HARDIRQS select REGMAP_I2C help - Select this option to get support for the RICOH583 Power - Management system device. - This driver provides common support for accessing the device - through i2c interface. The device supports multiple sub-devices - like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. - Additional drivers must be enabled in order to use the - different functionality of the device. - -config MFD_STA2X11 - bool "STA2X11 multi function device support" - depends on STA2X11 && GENERIC_HARDIRQS - select MFD_CORE - select REGMAP_MMIO + Support for the Wolfson Microelecronics WM8400 PMIC and audio + CODEC. This driver provides common support for accessing + the device, additional drivers must be enabled in order to use + the functionality of the device. -config MFD_SYSCON - bool "System Controller Register R/W Based on Regmap" - select REGMAP_MMIO - help - Select this option to enable accessing system control registers - via regmap. +config MFD_WM831X + bool + depends on GENERIC_HARDIRQS -config MFD_PALMAS - bool "Support for the TI Palmas series chips" +config MFD_WM831X_I2C + bool "Wolfson Microelectronics WM831x/2x PMICs with I2C" select MFD_CORE + select MFD_WM831X select REGMAP_I2C - select REGMAP_IRQ + select IRQ_DOMAIN depends on I2C=y && GENERIC_HARDIRQS help - If you say yes here you get support for the Palmas - series of PMIC chips from Texas Instruments. + Support for the Wolfson Microelecronics WM831x and WM832x PMICs + when controlled using I2C. This driver provides common support + for accessing the device, additional drivers must be enabled in + order to use the functionality of the device. -config MFD_VIPERBOARD - tristate "Support for Nano River Technologies Viperboard" +config MFD_WM831X_SPI + bool "Wolfson Microelectronics WM831x/2x PMICs with SPI" select MFD_CORE - depends on USB && GENERIC_HARDIRQS - default n + select MFD_WM831X + select REGMAP_SPI + select IRQ_DOMAIN + depends on SPI_MASTER && GENERIC_HARDIRQS help - Say yes here if you want support for Nano River Technologies - Viperboard. - There are mfd cell drivers available for i2c master, adc and - both gpios found on the board. The spi part does not yet - have a driver. - You need to select the mfd cell drivers separately. - The drivers do not support all features the board exposes. + Support for the Wolfson Microelecronics WM831x and WM832x PMICs + when controlled using SPI. This driver provides common support + for accessing the device, additional drivers must be enabled in + order to use the functionality of the device. -config MFD_RETU - tristate "Support for Retu multi-function device" - select MFD_CORE - depends on I2C && GENERIC_HARDIRQS - select REGMAP_IRQ +config MFD_WM8350 + bool + depends on GENERIC_HARDIRQS + +config MFD_WM8350_I2C + bool "Wolfson Microelectronics WM8350 with I2C" + select MFD_WM8350 + depends on I2C=y && GENERIC_HARDIRQS help - Retu is a multi-function device found on Nokia Internet Tablets - (770, N800 and N810). + The WM8350 is an integrated audio and power management + subsystem with watchdog and RTC functionality for embedded + systems. This option enables core support for the WM8350 with + I2C as the control interface. Additional options must be + selected to enable support for the functionality of the chip. -config MFD_AS3711 - bool "Support for AS3711" +config MFD_WM8994 + bool "Wolfson Microelectronics WM8994" select MFD_CORE select REGMAP_I2C select REGMAP_IRQ depends on I2C=y && GENERIC_HARDIRQS help - Support for the AS3711 PMIC from AMS + The WM8994 is a highly integrated hi-fi CODEC designed for + smartphone applicatiosn. As well as audio functionality it + has on board GPIO and regulator functionality which is + supported via the relevant subsystems. This driver provides + core support for the WM8994, in order to use the actual + functionaltiy of the device other drivers must be enabled. endmenu endif -- cgit v0.10.2 From d011c450814eb6b5418c84dd0cac8b6eb99c42dc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 9 Apr 2013 16:16:36 +0530 Subject: mfd: omap-usb-host: Convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. Signed-off-by: Sachin Kamat Acked-by: Roger Quadros Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index d3b6e94..759fae3 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "omap-usb.h" @@ -608,11 +609,9 @@ static int usbhs_omap_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - omap->uhh_base = devm_request_and_ioremap(dev, res); - if (!omap->uhh_base) { - dev_err(dev, "Resource request/ioremap failed\n"); - return -EADDRNOTAVAIL; - } + omap->uhh_base = devm_ioremap_resource(dev, res); + if (IS_ERR(omap->uhh_base)) + return PTR_ERR(omap->uhh_base); omap->pdata = pdata; -- cgit v0.10.2 From 651da3d4026ece555853694f51dbad566f113793 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 9 Apr 2013 16:16:37 +0530 Subject: mfd: omap-usb-tll: Convert to devm_ioremap_resource() Use the newly introduced devm_ioremap_resource() instead of devm_request_and_ioremap() which provides more consistent error handling. devm_ioremap_resource() provides its own error messages; so all explicit error messages can be removed from the failure code paths. Signed-off-by: Sachin Kamat Acked-by: Roger Quadros Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index 8f4d5a1..e59ac4c 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -226,12 +226,9 @@ static int usbtll_omap_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - tll->base = devm_request_and_ioremap(dev, res); - if (!tll->base) { - ret = -EADDRNOTAVAIL; - dev_err(dev, "Resource request/ioremap failed:%d\n", ret); - return ret; - } + tll->base = devm_ioremap_resource(dev, res); + if (IS_ERR(tll->base)) + return PTR_ERR(tll->base); platform_set_drvdata(pdev, tll); pm_runtime_enable(dev); -- cgit v0.10.2 From 066ec1dd44e84a911fe04556e4a9b89f1c83b0cb Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 9 Apr 2013 19:47:40 +0400 Subject: of: Add stub of_get_parent for non-OF builds Fixes build error on x86_64 allmodconfig, introduced by commit 5ab3a89a741f ("mfd: syscon: Add non-DT support"). drivers/regulator/anatop-regulator.c: In function 'anatop_regulator_probe': drivers/regulator/anatop-regulator.c:134:2: error: implicit declaration of function 'of_get_parent' [-Werror=implicit-function-declaration] Signed-off-by: Alexander Shiyan Acked-by: Rob Herring Signed-off-by: Samuel Ortiz diff --git a/include/linux/of.h b/include/linux/of.h index a0f1292..9f5923f 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -353,6 +353,11 @@ static inline struct device_node *of_find_node_by_name(struct device_node *from, return NULL; } +static inline struct device_node *of_get_parent(const struct device_node *node) +{ + return NULL; +} + static inline bool of_have_populated_dt(void) { return false; -- cgit v0.10.2 From daba96d6f38601552d08d52c15ef3c5660f1275f Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Tue, 9 Apr 2013 21:50:02 +0400 Subject: mfd: syscon: Fix warnings when printing resource_size_t Gets rid of these warnings when compile module for 64 bit targets: CC drivers/mfd/syscon.o drivers/mfd/syscon.c: In function 'syscon_probe': drivers/mfd/syscon.c:155:2: warning: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'resource_size_t' [-Wformat] drivers/mfd/syscon.c:155:2: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'resource_size_t' [-Wformat] Signed-off-by: Alexander Shiyan Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c index 7905d36..962a6e1 100644 --- a/drivers/mfd/syscon.c +++ b/drivers/mfd/syscon.c @@ -152,7 +152,7 @@ static int syscon_probe(struct platform_device *pdev) platform_set_drvdata(pdev, syscon); - dev_info(dev, "regmap 0x%x-0x%x registered\n", res->start, res->end); + dev_info(dev, "regmap %pR registered\n", res); return 0; } -- cgit v0.10.2 From d98a5384d7dbe47b8673eb281aaf339173376245 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Tue, 9 Apr 2013 20:52:58 +0100 Subject: mfd: db8500-prcmu: Support platform dependant device selection The main aim for this cycle is to have the u8540 booting to a console. However, the u8540 doesn't support all of the u8500 platform devices yet. After this stage is complete we can then fill in the inadequacies, such as specific clock support at a later date. To achieve this we're placing devices supported by all platforms into a common device structure and the remaining ones into a platform specific one. Cc: Samuel Ortiz Signed-off-by: Lee Jones Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 25dda8d..319b8ab 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -3067,6 +3068,15 @@ static struct db8500_thsens_platform_data db8500_thsens_data = { .num_trips = 4, }; +static struct mfd_cell common_prcmu_devs[] = { + { + .name = "ux500_wdt", + .platform_data = &db8500_wdt_pdata, + .pdata_size = sizeof(db8500_wdt_pdata), + .id = -1, + }, +}; + static struct mfd_cell db8500_prcmu_devs[] = { { .name = "db8500-prcmu-regulators", @@ -3081,12 +3091,6 @@ static struct mfd_cell db8500_prcmu_devs[] = { .pdata_size = sizeof(db8500_cpufreq_table), }, { - .name = "ux500_wdt", - .platform_data = &db8500_wdt_pdata, - .pdata_size = sizeof(db8500_wdt_pdata), - .id = -1, - }, - { .name = "db8500-thermal", .num_resources = ARRAY_SIZE(db8500_thsens_resources), .resources = db8500_thsens_resources, @@ -3175,13 +3179,25 @@ static int db8500_prcmu_probe(struct platform_device *pdev) db8500_prcmu_update_cpufreq(); - err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, - ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, db8500_irq_domain); + err = mfd_add_devices(&pdev->dev, 0, common_prcmu_devs, + ARRAY_SIZE(common_prcmu_devs), NULL, 0, db8500_irq_domain); if (err) { pr_err("prcmu: Failed to add subdevices\n"); return err; } + /* TODO: Remove restriction when clk definitions are available. */ + if (!of_machine_is_compatible("st-ericsson,u8540")) { + err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs, + ARRAY_SIZE(db8500_prcmu_devs), NULL, 0, + db8500_irq_domain); + if (err) { + mfd_remove_devices(&pdev->dev); + pr_err("prcmu: Failed to add subdevices\n"); + goto no_irq_return; + } + } + err = db8500_prcmu_register_ab8500(&pdev->dev, pdata->ab_platdata, pdata->ab_irq); if (err) { -- cgit v0.10.2 From b50cf35bf41f8aa451667d8ff01e13b9ab17dc86 Mon Sep 17 00:00:00 2001 From: Rhyland Klein Date: Wed, 10 Apr 2013 13:14:55 -0400 Subject: mfd: tps65090: Add compatible string for charger subnode This patch adds the of_compatible value for the tps65090 charger subnode used when loading the driver to find its properties and of_node. Signed-off-by: Rhyland Klein Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index 88846ae..fbd6ee6 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -72,6 +72,7 @@ static struct mfd_cell tps65090s[] = { .name = "tps65090-charger", .num_resources = ARRAY_SIZE(charger_resources), .resources = &charger_resources[0], + .of_compatible = "ti,tps65090-charger", }, }; -- cgit v0.10.2 From a79fc765b5c53465f0b34b491775c63ba652c9a9 Mon Sep 17 00:00:00 2001 From: Michal Marek Date: Tue, 16 Apr 2013 10:50:54 +0200 Subject: input: Export matrix_keypad_parse_of_params() Couple of input drivers depend on this symbol: ERROR: "matrix_keypad_parse_of_params" [drivers/input/keyboard/tca8418_keypad.ko] undefined! ERROR: "matrix_keypad_parse_of_params" [drivers/input/keyboard/cros_ec_keyb.ko] undefined! Reported-by: Randy Dunlap Acked-by: Dmitry Torokhov Acked-by: Randy Dunlap Acked-by: Simon Glass Signed-off-by: Michal Marek Signed-off-by: Samuel Ortiz diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index 619b382..08b61f5 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -68,6 +68,7 @@ int matrix_keypad_parse_of_params(struct device *dev, return 0; } +EXPORT_SYMBOL_GPL(matrix_keypad_parse_of_params); static int matrix_keypad_parse_of_keymap(const char *propname, unsigned int rows, unsigned int cols, -- cgit v0.10.2 From 39aa3b5a59ab1baff809821ed1db9682a8245656 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Apr 2013 18:11:50 +0100 Subject: mfd: wm8994: Add device ID data to WM8994 OF device IDs We can actually read this back from the device but we use this when registered using standard I2C board data registration so make sure it's there for OF too. Signed-off-by: Mark Brown Tested-by: Sylwester Nawrocki Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index cdea84e..3f8d591 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -673,9 +673,9 @@ static void wm8994_device_exit(struct wm8994 *wm8994) } static const struct of_device_id wm8994_of_match[] = { - { .compatible = "wlf,wm1811", }, - { .compatible = "wlf,wm8994", }, - { .compatible = "wlf,wm8958", }, + { .compatible = "wlf,wm1811", .data = (void *)WM1811 }, + { .compatible = "wlf,wm8994", .data = (void *)WM8994 }, + { .compatible = "wlf,wm8958", .data = (void *)WM8958 }, { } }; MODULE_DEVICE_TABLE(of, wm8994_of_match); -- cgit v0.10.2 From 20fb277250816d6c3ff326552be0fea4173fd8ca Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Apr 2013 18:11:51 +0100 Subject: mfd: wm8994: Add some OF properties Add properties for some of the more important bits of platform data and fill out the binding document. Not all of the current platform data is suitable for the sort of fixed configuration that is done using DT, some of it should have runtime mechanisms added instead and some is unlikely to ever be used in practical systems. Signed-off-by: Mark Brown Reviewed-by: Sylwester Nawrocki Tested-by: Sylwester Nawrocki Signed-off-by: Samuel Ortiz diff --git a/Documentation/devicetree/bindings/sound/wm8994.txt b/Documentation/devicetree/bindings/sound/wm8994.txt index 7a7eb1e..f2f3e80 100644 --- a/Documentation/devicetree/bindings/sound/wm8994.txt +++ b/Documentation/devicetree/bindings/sound/wm8994.txt @@ -5,14 +5,70 @@ on the board). Required properties: - - compatible : "wlf,wm1811", "wlf,wm8994", "wlf,wm8958" + - compatible : One of "wlf,wm1811", "wlf,wm8994" or "wlf,wm8958". - reg : the I2C address of the device for I2C, the chip select number for SPI. + - gpio-controller : Indicates this device is a GPIO controller. + - #gpio-cells : Must be 2. The first cell is the pin number and the + second cell is used to specify optional parameters (currently unused). + + - AVDD2-supply, DBVDD1-supply, DBVDD2-supply, DBVDD3-supply, CPVDD-supply, + SPKVDD1-supply, SPKVDD2-supply : power supplies for the device, as covered + in Documentation/devicetree/bindings/regulator/regulator.txt + +Optional properties: + + - interrupts : The interrupt line the IRQ signal for the device is + connected to. This is optional, if it is not connected then none + of the interrupt related properties should be specified. + - interrupt-controller : These devices contain interrupt controllers + and may provide interrupt services to other devices if they have an + interrupt line connected. + - interrupt-parent : The parent interrupt controller. + - #interrupt-cells: the number of cells to describe an IRQ, this should be 2. + The first cell is the IRQ number. + The second cell is the flags, encoded as the trigger masks from + Documentation/devicetree/bindings/interrupts.txt + + - wlf,gpio-cfg : A list of GPIO configuration register values. If absent, + no configuration of these registers is performed. If any value is + over 0xffff then the register will be left as default. If present 11 + values must be supplied. + + - wlf,micbias-cfg : Two MICBIAS register values for WM1811 or + WM8958. If absent the register defaults will be used. + + - wlf,ldo1ena : GPIO specifier for control of LDO1ENA input to device. + - wlf,ldo2ena : GPIO specifier for control of LDO2ENA input to device. + + - wlf,lineout1-se : If present LINEOUT1 is in single ended mode. + - wlf,lineout2-se : If present LINEOUT2 is in single ended mode. + + - wlf,lineout1-feedback : If present LINEOUT1 has common mode feedback + connected. + - wlf,lineout2-feedback : If present LINEOUT2 has common mode feedback + connected. + + - wlf,ldoena-always-driven : If present LDOENA is always driven. + Example: codec: wm8994@1a { compatible = "wlf,wm8994"; reg = <0x1a>; + + gpio-controller; + #gpio-cells = <2>; + + lineout1-se; + + AVDD2-supply = <®ulator>; + CPVDD-supply = <®ulator>; + DBVDD1-supply = <®ulator>; + DBVDD2-supply = <®ulator>; + DBVDD3-supply = <®ulator>; + SPKVDD1-supply = <®ulator>; + SPKVDD2-supply = <®ulator>; }; diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 3f8d591..00e4fe2 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -19,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -396,6 +399,60 @@ static const struct reg_default wm1811_reva_patch[] = { { 0x102, 0x0 }, }; +#ifdef CONFIG_OF +static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) +{ + struct device_node *np = wm8994->dev->of_node; + struct wm8994_pdata *pdata = &wm8994->pdata; + int i; + + if (!np) + return 0; + + if (of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_defaults, + ARRAY_SIZE(pdata->gpio_defaults)) >= 0) { + for (i = 0; i < ARRAY_SIZE(pdata->gpio_defaults); i++) { + if (wm8994->pdata.gpio_defaults[i] == 0) + pdata->gpio_defaults[i] + = WM8994_CONFIGURE_GPIO; + } + } + + of_property_read_u32_array(np, "wlf,micbias-cfg", pdata->micbias, + ARRAY_SIZE(pdata->micbias)); + + pdata->lineout1_diff = true; + pdata->lineout2_diff = true; + if (of_find_property(np, "wlf,lineout1-se", NULL)) + pdata->lineout1_diff = false; + if (of_find_property(np, "wlf,lineout2-se", NULL)) + pdata->lineout2_diff = false; + + if (of_find_property(np, "wlf,lineout1-feedback", NULL)) + pdata->lineout1fb = true; + if (of_find_property(np, "wlf,lineout2-feedback", NULL)) + pdata->lineout2fb = true; + + if (of_find_property(np, "wlf,ldoena-always-driven", NULL)) + pdata->lineout2fb = true; + + pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0); + if (pdata->ldo[0].enable < 0) + pdata->ldo[0].enable = 0; + + pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0); + if (pdata->ldo[1].enable < 0) + pdata->ldo[1].enable = 0; + + return 0; +} +#else +static int wm8994_set_pdata_from_of(struct wm8994 *wm8994) +{ + return 0; +} +#endif + /* * Instantiate the generic non-control parts of the device. */ @@ -414,6 +471,10 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) } pdata = &wm8994->pdata; + ret = wm8994_set_pdata_from_of(wm8994); + if (ret != 0) + return ret; + dev_set_drvdata(wm8994->dev, wm8994); /* Add the on-chip regulators first for bootstrapping */ @@ -683,6 +744,7 @@ MODULE_DEVICE_TABLE(of, wm8994_of_match); static int wm8994_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + const struct of_device_id *of_id; struct wm8994 *wm8994; int ret; @@ -693,7 +755,14 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, wm8994); wm8994->dev = &i2c->dev; wm8994->irq = i2c->irq; - wm8994->type = id->driver_data; + + if (i2c->dev.of_node) { + of_id = of_match_device(wm8994_of_match, &i2c->dev); + if (of_id) + wm8994->type = (int)of_id->data; + } else { + wm8994->type = id->driver_data; + } wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config); if (IS_ERR(wm8994->regmap)) { @@ -733,7 +802,7 @@ static struct i2c_driver wm8994_i2c_driver = { .name = "wm8994", .owner = THIS_MODULE, .pm = &wm8994_pm_ops, - .of_match_table = wm8994_of_match, + .of_match_table = of_match_ptr(wm8994_of_match), }, .probe = wm8994_i2c_probe, .remove = wm8994_i2c_remove, -- cgit v0.10.2 From 360e64d8bbe7c78784d769a60d152804f5079577 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 14 Apr 2013 20:35:48 +0200 Subject: mfd: ucb1400: Pass ucb1400-gpio data through ac97 bus Cc: Jean Delvare Cc: Guenter Roeck Cc: Grant Likely Acked-by: Linus Walleij Reviewed-by: Mark Brown Signed-off-by: Marek Vasut Signed-off-by: Samuel Ortiz diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c index 26405ef..6d0feb2 100644 --- a/drivers/gpio/gpio-ucb1400.c +++ b/drivers/gpio/gpio-ucb1400.c @@ -12,8 +12,6 @@ #include #include -struct ucb1400_gpio_data *ucbdata; - static int ucb1400_gpio_dir_in(struct gpio_chip *gc, unsigned off) { struct ucb1400_gpio *gpio; @@ -50,7 +48,7 @@ static int ucb1400_gpio_probe(struct platform_device *dev) struct ucb1400_gpio *ucb = dev->dev.platform_data; int err = 0; - if (!(ucbdata && ucbdata->gpio_offset)) { + if (!(ucb && ucb->gpio_offset)) { err = -EINVAL; goto err; } @@ -58,7 +56,7 @@ static int ucb1400_gpio_probe(struct platform_device *dev) platform_set_drvdata(dev, ucb); ucb->gc.label = "ucb1400_gpio"; - ucb->gc.base = ucbdata->gpio_offset; + ucb->gc.base = ucb->gpio_offset; ucb->gc.ngpio = 10; ucb->gc.owner = THIS_MODULE; @@ -72,8 +70,8 @@ static int ucb1400_gpio_probe(struct platform_device *dev) if (err) goto err; - if (ucbdata && ucbdata->gpio_setup) - err = ucbdata->gpio_setup(&dev->dev, ucb->gc.ngpio); + if (ucb && ucb->gpio_setup) + err = ucb->gpio_setup(&dev->dev, ucb->gc.ngpio); err: return err; @@ -85,8 +83,8 @@ static int ucb1400_gpio_remove(struct platform_device *dev) int err = 0; struct ucb1400_gpio *ucb = platform_get_drvdata(dev); - if (ucbdata && ucbdata->gpio_teardown) { - err = ucbdata->gpio_teardown(&dev->dev, ucb->gc.ngpio); + if (ucb && ucb->gpio_teardown) { + err = ucb->gpio_teardown(&dev->dev, ucb->gc.ngpio); if (err) return err; } @@ -103,11 +101,6 @@ static struct platform_driver ucb1400_gpio_driver = { }, }; -void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) -{ - ucbdata = data; -} - module_platform_driver(ucb1400_gpio_driver); MODULE_DESCRIPTION("Philips UCB1400 GPIO driver"); diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index daf6952..e9031fa 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -75,6 +75,11 @@ static int ucb1400_core_probe(struct device *dev) /* GPIO */ ucb_gpio.ac97 = ac97; + if (pdata) { + ucb_gpio.gpio_setup = pdata->gpio_setup; + ucb_gpio.gpio_teardown = pdata->gpio_teardown; + ucb_gpio.gpio_offset = pdata->gpio_offset; + } ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); if (!ucb->ucb1400_gpio) { err = -ENOMEM; diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index d21b33c..2e9ee4d 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -83,15 +83,12 @@ #define UCB_ID 0x7e #define UCB_ID_1400 0x4304 -struct ucb1400_gpio_data { - int gpio_offset; - int (*gpio_setup)(struct device *dev, int ngpio); - int (*gpio_teardown)(struct device *dev, int ngpio); -}; - struct ucb1400_gpio { struct gpio_chip gc; struct snd_ac97 *ac97; + int gpio_offset; + int (*gpio_setup)(struct device *dev, int ngpio); + int (*gpio_teardown)(struct device *dev, int ngpio); }; struct ucb1400_ts { @@ -110,6 +107,9 @@ struct ucb1400 { struct ucb1400_pdata { int irq; + int gpio_offset; + int (*gpio_setup)(struct device *dev, int ngpio); + int (*gpio_teardown)(struct device *dev, int ngpio); }; static inline u16 ucb1400_reg_read(struct snd_ac97 *ac97, u16 reg) @@ -162,10 +162,4 @@ static inline void ucb1400_adc_disable(struct snd_ac97 *ac97) unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel, int adcsync); -#ifdef CONFIG_GPIO_UCB1400 -void __init ucb1400_gpio_set_data(struct ucb1400_gpio_data *data); -#else -static inline void ucb1400_gpio_set_data(struct ucb1400_gpio_data *data) {} -#endif - #endif -- cgit v0.10.2 From 95e50f6a2fe9ece6503e355400c171e0f5de61be Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Tue, 9 Apr 2013 22:51:25 +0300 Subject: mfd: retu: Add Tahvo support Tahvo is a multi-function device on Nokia 770, implementing USB transceiver and charge/battery control. It's so close to Retu that a single driver can support both. Signed-off-by: Aaro Koskinen Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 2f3ce18..9de0502 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -371,13 +371,13 @@ config MFD_VIPERBOARD The drivers do not support all features the board exposes. config MFD_RETU - tristate "Nokia Retu multi-function device" + tristate "Nokia Retu and Tahvo multi-function device" select MFD_CORE depends on I2C && GENERIC_HARDIRQS select REGMAP_IRQ help - Retu is a multi-function device found on Nokia Internet Tablets - (770, N800 and N810). + Retu and Tahvo are a multi-function devices found on Nokia + Internet Tablets (770, N800 and N810). config MFD_PCF50633 tristate "NXP PCF50633" diff --git a/drivers/mfd/retu-mfd.c b/drivers/mfd/retu-mfd.c index 3ba0486..a183098 100644 --- a/drivers/mfd/retu-mfd.c +++ b/drivers/mfd/retu-mfd.c @@ -1,5 +1,5 @@ /* - * Retu MFD driver + * Retu/Tahvo MFD driver * * Copyright (C) 2004, 2005 Nokia Corporation * @@ -33,7 +33,8 @@ #define RETU_REG_ASICR 0x00 /* ASIC ID and revision */ #define RETU_REG_ASICR_VILMA (1 << 7) /* Bit indicating Vilma */ #define RETU_REG_IDR 0x01 /* Interrupt ID */ -#define RETU_REG_IMR 0x02 /* Interrupt mask */ +#define RETU_REG_IMR 0x02 /* Interrupt mask (Retu) */ +#define TAHVO_REG_IMR 0x03 /* Interrupt mask (Tahvo) */ /* Interrupt sources */ #define RETU_INT_PWR 0 /* Power button */ @@ -84,6 +85,62 @@ static struct regmap_irq_chip retu_irq_chip = { /* Retu device registered for the power off. */ static struct retu_dev *retu_pm_power_off; +static struct resource tahvo_usb_res[] = { + { + .name = "tahvo-usb", + .start = TAHVO_INT_VBUS, + .end = TAHVO_INT_VBUS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tahvo_devs[] = { + { + .name = "tahvo-usb", + .resources = tahvo_usb_res, + .num_resources = ARRAY_SIZE(tahvo_usb_res), + }, +}; + +static struct regmap_irq tahvo_irqs[] = { + [TAHVO_INT_VBUS] = { + .mask = 1 << TAHVO_INT_VBUS, + } +}; + +static struct regmap_irq_chip tahvo_irq_chip = { + .name = "TAHVO", + .irqs = tahvo_irqs, + .num_irqs = ARRAY_SIZE(tahvo_irqs), + .num_regs = 1, + .status_base = RETU_REG_IDR, + .mask_base = TAHVO_REG_IMR, + .ack_base = RETU_REG_IDR, +}; + +static const struct retu_data { + char *chip_name; + char *companion_name; + struct regmap_irq_chip *irq_chip; + struct mfd_cell *children; + int nchildren; +} retu_data[] = { + [0] = { + .chip_name = "Retu", + .companion_name = "Vilma", + .irq_chip = &retu_irq_chip, + .children = retu_devs, + .nchildren = ARRAY_SIZE(retu_devs), + }, + [1] = { + .chip_name = "Tahvo", + .companion_name = "Betty", + .irq_chip = &tahvo_irq_chip, + .children = tahvo_devs, + .nchildren = ARRAY_SIZE(tahvo_devs), + } +}; + int retu_read(struct retu_dev *rdev, u8 reg) { int ret; @@ -173,9 +230,14 @@ static struct regmap_config retu_config = { static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct retu_data const *rdat; struct retu_dev *rdev; int ret; + if (i2c->addr > ARRAY_SIZE(retu_data)) + return -ENODEV; + rdat = &retu_data[i2c->addr - 1]; + rdev = devm_kzalloc(&i2c->dev, sizeof(*rdev), GFP_KERNEL); if (rdev == NULL) return -ENOMEM; @@ -190,25 +252,27 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) ret = retu_read(rdev, RETU_REG_ASICR); if (ret < 0) { - dev_err(rdev->dev, "could not read Retu revision: %d\n", ret); + dev_err(rdev->dev, "could not read %s revision: %d\n", + rdat->chip_name, ret); return ret; } - dev_info(rdev->dev, "Retu%s v%d.%d found\n", - (ret & RETU_REG_ASICR_VILMA) ? " & Vilma" : "", + dev_info(rdev->dev, "%s%s%s v%d.%d found\n", rdat->chip_name, + (ret & RETU_REG_ASICR_VILMA) ? " & " : "", + (ret & RETU_REG_ASICR_VILMA) ? rdat->companion_name : "", (ret >> 4) & 0x7, ret & 0xf); - /* Mask all RETU interrupts. */ - ret = retu_write(rdev, RETU_REG_IMR, 0xffff); + /* Mask all interrupts. */ + ret = retu_write(rdev, rdat->irq_chip->mask_base, 0xffff); if (ret < 0) return ret; ret = regmap_add_irq_chip(rdev->regmap, i2c->irq, IRQF_ONESHOT, -1, - &retu_irq_chip, &rdev->irq_data); + rdat->irq_chip, &rdev->irq_data); if (ret < 0) return ret; - ret = mfd_add_devices(rdev->dev, -1, retu_devs, ARRAY_SIZE(retu_devs), + ret = mfd_add_devices(rdev->dev, -1, rdat->children, rdat->nchildren, NULL, regmap_irq_chip_get_base(rdev->irq_data), NULL); if (ret < 0) { @@ -216,7 +280,7 @@ static int retu_probe(struct i2c_client *i2c, const struct i2c_device_id *id) return ret; } - if (!pm_power_off) { + if (i2c->addr == 1 && !pm_power_off) { retu_pm_power_off = rdev; pm_power_off = retu_power_off; } @@ -240,6 +304,7 @@ static int retu_remove(struct i2c_client *i2c) static const struct i2c_device_id retu_id[] = { { "retu-mfd", 0 }, + { "tahvo-mfd", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, retu_id); diff --git a/include/linux/mfd/retu.h b/include/linux/mfd/retu.h index 1e2715d..65471c4 100644 --- a/include/linux/mfd/retu.h +++ b/include/linux/mfd/retu.h @@ -1,5 +1,5 @@ /* - * Retu MFD driver interface + * Retu/Tahvo MFD driver interface * * This file is subject to the terms and conditions of the GNU General * Public License. See the file "COPYING" in the main directory of this @@ -19,4 +19,10 @@ int retu_write(struct retu_dev *, u8, u16); #define RETU_REG_CC1 0x0d /* Common control register 1 */ #define RETU_REG_STATUS 0x16 /* Status register */ +/* Interrupt sources */ +#define TAHVO_INT_VBUS 0 /* VBUS state */ + +/* Interrupt status */ +#define TAHVO_STAT_VBUS (1 << TAHVO_INT_VBUS) + #endif /* __LINUX_MFD_RETU_H */ -- cgit v0.10.2 From 4c4b8c105a7bbd4a8d41ab4458f01174fdf3fcbb Mon Sep 17 00:00:00 2001 From: Wei WANG Date: Thu, 11 Apr 2013 10:43:40 +0800 Subject: mfd: rtsx: Support RTS5249 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RTS5249 supports SD UHS-II interface. In order to support SD UHS-II,the definitions of some internal registers of RTS5249 have to be modified and are different from its predecessors. So we need this patch to ensure RTS5249 can work, even SD/MMC stack doesn't support UHS-II interface. Signed-off-by: Wei WANG Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f62a583..0676566 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec.o obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o -rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o +rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c new file mode 100644 index 0000000..15dc848 --- /dev/null +++ b/drivers/mfd/rts5249.c @@ -0,0 +1,241 @@ +/* Driver for Realtek PCI-Express card reader + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + * + * Author: + * Wei WANG + * No. 128, West Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include +#include +#include + +#include "rtsx_pcr.h" + +static u8 rts5249_get_ic_version(struct rtsx_pcr *pcr) +{ + u8 val; + + rtsx_pci_read_register(pcr, DUMMY_REG_RESET_0, &val); + return val & 0x0F; +} + +static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) +{ + rtsx_pci_init_cmd(pcr); + + /* Configure GPIO as output */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, GPIO_CTL, 0x02, 0x02); + /* Switch LDO3318 source from DV33 to card_3v3 */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, LDO_PWR_SEL, 0x03, 0x01); + /* LED shine disabled, set initial shine cycle period */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, OLT_LED_CTL, 0x0F, 0x02); + /* Correct driving */ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CLK_DRIVE_SEL, 0xFF, 0x99); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_CMD_DRIVE_SEL, 0xFF, 0x99); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD30_DAT_DRIVE_SEL, 0xFF, 0x92); + + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5249_optimize_phy(struct rtsx_pcr *pcr) +{ + int err; + + err = rtsx_pci_write_phy_register(pcr, PHY_REG_REV, 0xFE46); + if (err < 0) + return err; + + msleep(1); + + return rtsx_pci_write_phy_register(pcr, PHY_BPCR, 0x05C0); +} + +static int rts5249_turn_on_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x02); +} + +static int rts5249_turn_off_led(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, GPIO_CTL, 0x02, 0x00); +} + +static int rts5249_enable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x08); +} + +static int rts5249_disable_auto_blink(struct rtsx_pcr *pcr) +{ + return rtsx_pci_write_register(pcr, OLT_LED_CTL, 0x08, 0x00); +} + +static int rts5249_card_power_on(struct rtsx_pcr *pcr, int card) +{ + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_VCC_PARTIAL_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x02); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + msleep(5); + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_VCC_POWER_ON); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x06); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static int rts5249_card_power_off(struct rtsx_pcr *pcr, int card) +{ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_PWR_CTL, + SD_POWER_MASK, SD_POWER_OFF); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PWR_GATE_CTRL, + LDO3318_PWR_MASK, 0x00); + return rtsx_pci_send_cmd(pcr, 100); +} + +static int rts5249_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) +{ + int err; + u8 clk_drive, cmd_drive, dat_drive; + + if (voltage == OUTPUT_3V3) { + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4FC0 | 0x24); + if (err < 0) + return err; + clk_drive = 0x99; + cmd_drive = 0x99; + dat_drive = 0x92; + } else if (voltage == OUTPUT_1V8) { + err = rtsx_pci_write_phy_register(pcr, PHY_BACR, 0x3C02); + if (err < 0) + return err; + err = rtsx_pci_write_phy_register(pcr, PHY_TUNE, 0x4C40 | 0x24); + if (err < 0) + return err; + clk_drive = 0xb3; + cmd_drive = 0xb3; + dat_drive = 0xb3; + } else { + return -EINVAL; + } + + /* set pad drive */ + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CLK_DRIVE_SEL, + 0xFF, clk_drive); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_CMD_DRIVE_SEL, + 0xFF, cmd_drive); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD30_DAT_DRIVE_SEL, + 0xFF, dat_drive); + return rtsx_pci_send_cmd(pcr, 100); +} + +static const struct pcr_ops rts5249_pcr_ops = { + .extra_init_hw = rts5249_extra_init_hw, + .optimize_phy = rts5249_optimize_phy, + .turn_on_led = rts5249_turn_on_led, + .turn_off_led = rts5249_turn_off_led, + .enable_auto_blink = rts5249_enable_auto_blink, + .disable_auto_blink = rts5249_disable_auto_blink, + .card_power_on = rts5249_card_power_on, + .card_power_off = rts5249_card_power_off, + .switch_output_voltage = rts5249_switch_output_voltage, +}; + +/* SD Pull Control Enable: + * SD_DAT[3:0] ==> pull up + * SD_CD ==> pull up + * SD_WP ==> pull up + * SD_CMD ==> pull up + * SD_CLK ==> pull down + */ +static const u32 rts5249_sd_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0xAA), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xE9), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0xAA), + 0, +}; + +/* SD Pull Control Disable: + * SD_DAT[3:0] ==> pull down + * SD_CD ==> pull up + * SD_WP ==> pull down + * SD_CMD ==> pull down + * SD_CLK ==> pull down + */ +static const u32 rts5249_sd_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL1, 0x66), + RTSX_REG_PAIR(CARD_PULL_CTL2, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL3, 0xD5), + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + 0, +}; + +/* MS Pull Control Enable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5249_ms_pull_ctl_enable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +/* MS Pull Control Disable: + * MS CD ==> pull up + * others ==> pull down + */ +static const u32 rts5249_ms_pull_ctl_disable_tbl[] = { + RTSX_REG_PAIR(CARD_PULL_CTL4, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL5, 0x55), + RTSX_REG_PAIR(CARD_PULL_CTL6, 0x15), + 0, +}; + +void rts5249_init_params(struct rtsx_pcr *pcr) +{ + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; + pcr->num_slots = 2; + pcr->ops = &rts5249_pcr_ops; + + pcr->ic_version = rts5249_get_ic_version(pcr); + pcr->sd_pull_ctl_enable_tbl = rts5249_sd_pull_ctl_enable_tbl; + pcr->sd_pull_ctl_disable_tbl = rts5249_sd_pull_ctl_disable_tbl; + pcr->ms_pull_ctl_enable_tbl = rts5249_ms_pull_ctl_enable_tbl; + pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; +} diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 578a113..e968c01 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -56,6 +56,7 @@ static DEFINE_PCI_DEVICE_TABLE(rtsx_pci_ids) = { { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, + { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; @@ -1033,6 +1034,10 @@ static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) case 0x5227: rts5227_init_params(pcr); break; + + case 0x5249: + rts5249_init_params(pcr); + break; } dev_dbg(&(pcr->pci->dev), "PID: 0x%04x, IC version: 0x%02x\n", diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 2b3ab8a..55fcfc2 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -32,5 +32,6 @@ void rts5209_init_params(struct rtsx_pcr *pcr); void rts5229_init_params(struct rtsx_pcr *pcr); void rtl8411_init_params(struct rtsx_pcr *pcr); void rts5227_init_params(struct rtsx_pcr *pcr); +void rts5249_init_params(struct rtsx_pcr *pcr); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 26ea7f1..86bc635 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -500,6 +500,8 @@ #define BPP_POWER_15_PERCENT_ON 0x08 #define BPP_POWER_ON 0x00 #define BPP_POWER_MASK 0x0F +#define SD_VCC_PARTIAL_POWER_ON 0x02 +#define SD_VCC_POWER_ON 0x00 /* PWR_GATE_CTRL */ #define PWR_GATE_EN 0x01 @@ -689,6 +691,40 @@ #define IMAGE_FLAG_ADDR0 0xCE80 #define IMAGE_FLAG_ADDR1 0xCE81 +/* Phy register */ +#define PHY_PCR 0x00 +#define PHY_RCR0 0x01 +#define PHY_RCR1 0x02 +#define PHY_RCR2 0x03 +#define PHY_RTCR 0x04 +#define PHY_RDR 0x05 +#define PHY_TCR0 0x06 +#define PHY_TCR1 0x07 +#define PHY_TUNE 0x08 +#define PHY_IMR 0x09 +#define PHY_BPCR 0x0A +#define PHY_BIST 0x0B +#define PHY_RAW_L 0x0C +#define PHY_RAW_H 0x0D +#define PHY_RAW_DATA 0x0E +#define PHY_HOST_CLK_CTRL 0x0F +#define PHY_DMR 0x10 +#define PHY_BACR 0x11 +#define PHY_IER 0x12 +#define PHY_BCSR 0x13 +#define PHY_BPR 0x14 +#define PHY_BPNR2 0x15 +#define PHY_BPNR 0x16 +#define PHY_BRNR2 0x17 +#define PHY_BENR 0x18 +#define PHY_REG_REV 0x19 +#define PHY_FLD0 0x1A +#define PHY_FLD1 0x1B +#define PHY_FLD2 0x1C +#define PHY_FLD3 0x1D +#define PHY_FLD4 0x1E +#define PHY_DUM_REG 0x1F + #define rtsx_pci_init_cmd(pcr) ((pcr)->ci = 0) struct rtsx_pcr; -- cgit v0.10.2 From ed4a8fe892ccbeb75a2ab380ef6ef5ef6d9d2636 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 18 Apr 2013 09:58:27 -0700 Subject: mfd: si476x: Add commands abstraction layer This patch adds all the functions used for exchanging commands with the chip. Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/si476x-cmd.c b/drivers/mfd/si476x-cmd.c new file mode 100644 index 0000000..de48b4e --- /dev/null +++ b/drivers/mfd/si476x-cmd.c @@ -0,0 +1,1553 @@ +/* + * drivers/mfd/si476x-cmd.c -- Subroutines implementing command + * protocol of si476x series of chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define msb(x) ((u8)((u16) x >> 8)) +#define lsb(x) ((u8)((u16) x & 0x00FF)) + + + +#define CMD_POWER_UP 0x01 +#define CMD_POWER_UP_A10_NRESP 1 +#define CMD_POWER_UP_A10_NARGS 5 + +#define CMD_POWER_UP_A20_NRESP 1 +#define CMD_POWER_UP_A20_NARGS 5 + +#define POWER_UP_DELAY_MS 110 + +#define CMD_POWER_DOWN 0x11 +#define CMD_POWER_DOWN_A10_NRESP 1 + +#define CMD_POWER_DOWN_A20_NRESP 1 +#define CMD_POWER_DOWN_A20_NARGS 1 + +#define CMD_FUNC_INFO 0x12 +#define CMD_FUNC_INFO_NRESP 7 + +#define CMD_SET_PROPERTY 0x13 +#define CMD_SET_PROPERTY_NARGS 5 +#define CMD_SET_PROPERTY_NRESP 1 + +#define CMD_GET_PROPERTY 0x14 +#define CMD_GET_PROPERTY_NARGS 3 +#define CMD_GET_PROPERTY_NRESP 4 + +#define CMD_AGC_STATUS 0x17 +#define CMD_AGC_STATUS_NRESP_A10 2 +#define CMD_AGC_STATUS_NRESP_A20 6 + +#define PIN_CFG_BYTE(x) (0x7F & (x)) +#define CMD_DIG_AUDIO_PIN_CFG 0x18 +#define CMD_DIG_AUDIO_PIN_CFG_NARGS 4 +#define CMD_DIG_AUDIO_PIN_CFG_NRESP 5 + +#define CMD_ZIF_PIN_CFG 0x19 +#define CMD_ZIF_PIN_CFG_NARGS 4 +#define CMD_ZIF_PIN_CFG_NRESP 5 + +#define CMD_IC_LINK_GPO_CTL_PIN_CFG 0x1A +#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS 4 +#define CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP 5 + +#define CMD_ANA_AUDIO_PIN_CFG 0x1B +#define CMD_ANA_AUDIO_PIN_CFG_NARGS 1 +#define CMD_ANA_AUDIO_PIN_CFG_NRESP 2 + +#define CMD_INTB_PIN_CFG 0x1C +#define CMD_INTB_PIN_CFG_NARGS 2 +#define CMD_INTB_PIN_CFG_A10_NRESP 6 +#define CMD_INTB_PIN_CFG_A20_NRESP 3 + +#define CMD_FM_TUNE_FREQ 0x30 +#define CMD_FM_TUNE_FREQ_A10_NARGS 5 +#define CMD_FM_TUNE_FREQ_A20_NARGS 3 +#define CMD_FM_TUNE_FREQ_NRESP 1 + +#define CMD_FM_RSQ_STATUS 0x32 + +#define CMD_FM_RSQ_STATUS_A10_NARGS 1 +#define CMD_FM_RSQ_STATUS_A10_NRESP 17 +#define CMD_FM_RSQ_STATUS_A30_NARGS 1 +#define CMD_FM_RSQ_STATUS_A30_NRESP 23 + + +#define CMD_FM_SEEK_START 0x31 +#define CMD_FM_SEEK_START_NARGS 1 +#define CMD_FM_SEEK_START_NRESP 1 + +#define CMD_FM_RDS_STATUS 0x36 +#define CMD_FM_RDS_STATUS_NARGS 1 +#define CMD_FM_RDS_STATUS_NRESP 16 + +#define CMD_FM_RDS_BLOCKCOUNT 0x37 +#define CMD_FM_RDS_BLOCKCOUNT_NARGS 1 +#define CMD_FM_RDS_BLOCKCOUNT_NRESP 8 + +#define CMD_FM_PHASE_DIVERSITY 0x38 +#define CMD_FM_PHASE_DIVERSITY_NARGS 1 +#define CMD_FM_PHASE_DIVERSITY_NRESP 1 + +#define CMD_FM_PHASE_DIV_STATUS 0x39 +#define CMD_FM_PHASE_DIV_STATUS_NRESP 2 + +#define CMD_AM_TUNE_FREQ 0x40 +#define CMD_AM_TUNE_FREQ_NARGS 3 +#define CMD_AM_TUNE_FREQ_NRESP 1 + +#define CMD_AM_RSQ_STATUS 0x42 +#define CMD_AM_RSQ_STATUS_NARGS 1 +#define CMD_AM_RSQ_STATUS_NRESP 13 + +#define CMD_AM_SEEK_START 0x41 +#define CMD_AM_SEEK_START_NARGS 1 +#define CMD_AM_SEEK_START_NRESP 1 + + +#define CMD_AM_ACF_STATUS 0x45 +#define CMD_AM_ACF_STATUS_NRESP 6 +#define CMD_AM_ACF_STATUS_NARGS 1 + +#define CMD_FM_ACF_STATUS 0x35 +#define CMD_FM_ACF_STATUS_NRESP 8 +#define CMD_FM_ACF_STATUS_NARGS 1 + +#define CMD_MAX_ARGS_COUNT (10) + + +enum si476x_acf_status_report_bits { + SI476X_ACF_BLEND_INT = (1 << 4), + SI476X_ACF_HIBLEND_INT = (1 << 3), + SI476X_ACF_HICUT_INT = (1 << 2), + SI476X_ACF_CHBW_INT = (1 << 1), + SI476X_ACF_SOFTMUTE_INT = (1 << 0), + + SI476X_ACF_SMUTE = (1 << 0), + SI476X_ACF_SMATTN = 0b11111, + SI476X_ACF_PILOT = (1 << 7), + SI476X_ACF_STBLEND = ~SI476X_ACF_PILOT, +}; + +enum si476x_agc_status_report_bits { + SI476X_AGC_MXHI = (1 << 5), + SI476X_AGC_MXLO = (1 << 4), + SI476X_AGC_LNAHI = (1 << 3), + SI476X_AGC_LNALO = (1 << 2), +}; + +enum si476x_errors { + SI476X_ERR_BAD_COMMAND = 0x10, + SI476X_ERR_BAD_ARG1 = 0x11, + SI476X_ERR_BAD_ARG2 = 0x12, + SI476X_ERR_BAD_ARG3 = 0x13, + SI476X_ERR_BAD_ARG4 = 0x14, + SI476X_ERR_BUSY = 0x18, + SI476X_ERR_BAD_INTERNAL_MEMORY = 0x20, + SI476X_ERR_BAD_PATCH = 0x30, + SI476X_ERR_BAD_BOOT_MODE = 0x31, + SI476X_ERR_BAD_PROPERTY = 0x40, +}; + +static int si476x_core_parse_and_nag_about_error(struct si476x_core *core) +{ + int err; + char *cause; + u8 buffer[2]; + + if (core->revision != SI476X_REVISION_A10) { + err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, + buffer, sizeof(buffer)); + if (err == sizeof(buffer)) { + switch (buffer[1]) { + case SI476X_ERR_BAD_COMMAND: + cause = "Bad command"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_ARG1: + cause = "Bad argument #1"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_ARG2: + cause = "Bad argument #2"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_ARG3: + cause = "Bad argument #3"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_ARG4: + cause = "Bad argument #4"; + err = -EINVAL; + break; + case SI476X_ERR_BUSY: + cause = "Chip is busy"; + err = -EBUSY; + break; + case SI476X_ERR_BAD_INTERNAL_MEMORY: + cause = "Bad internal memory"; + err = -EIO; + break; + case SI476X_ERR_BAD_PATCH: + cause = "Bad patch"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_BOOT_MODE: + cause = "Bad boot mode"; + err = -EINVAL; + break; + case SI476X_ERR_BAD_PROPERTY: + cause = "Bad property"; + err = -EINVAL; + break; + default: + cause = "Unknown"; + err = -EIO; + } + + dev_err(&core->client->dev, + "[Chip error status]: %s\n", cause); + } else { + dev_err(&core->client->dev, + "Failed to fetch error code\n"); + err = (err >= 0) ? -EIO : err; + } + } else { + err = -EIO; + } + + return err; +} + +/** + * si476x_core_send_command() - sends a command to si476x and waits its + * response + * @core: si476x_device structure for the device we are + * communicating with + * @command: command id + * @args: command arguments we are sending + * @argn: actual size of @args + * @response: buffer to place the expected response from the device + * @respn: actual size of @response + * @usecs: amount of time to wait before reading the response (in + * usecs) + * + * Function returns 0 on succsess and negative error code on + * failure + */ +static int si476x_core_send_command(struct si476x_core *core, + const u8 command, + const u8 args[], + const int argn, + u8 resp[], + const int respn, + const int usecs) +{ + struct i2c_client *client = core->client; + int err; + u8 data[CMD_MAX_ARGS_COUNT + 1]; + + if (argn > CMD_MAX_ARGS_COUNT) { + err = -ENOMEM; + goto exit; + } + + if (!client->adapter) { + err = -ENODEV; + goto exit; + } + + /* First send the command and its arguments */ + data[0] = command; + memcpy(&data[1], args, argn); + dev_dbg(&client->dev, "Command:\n %*ph\n", argn + 1, data); + + err = si476x_core_i2c_xfer(core, SI476X_I2C_SEND, + (char *) data, argn + 1); + if (err != argn + 1) { + dev_err(&core->client->dev, + "Error while sending command 0x%02x\n", + command); + err = (err >= 0) ? -EIO : err; + goto exit; + } + /* Set CTS to zero only after the command is send to avoid + * possible racing conditions when working in polling mode */ + atomic_set(&core->cts, 0); + + /* if (unlikely(command == CMD_POWER_DOWN) */ + if (!wait_event_timeout(core->command, + atomic_read(&core->cts), + usecs_to_jiffies(usecs) + 1)) + dev_warn(&core->client->dev, + "(%s) [CMD 0x%02x] Answer timeout.\n", + __func__, command); + + /* + When working in polling mode, for some reason the tuner will + report CTS bit as being set in the first status byte read, + but all the consequtive ones will return zeros until the + tuner is actually completed the POWER_UP command. To + workaround that we wait for second CTS to be reported + */ + if (unlikely(!core->client->irq && command == CMD_POWER_UP)) { + if (!wait_event_timeout(core->command, + atomic_read(&core->cts), + usecs_to_jiffies(usecs) + 1)) + dev_warn(&core->client->dev, + "(%s) Power up took too much time.\n", + __func__); + } + + /* Then get the response */ + err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, resp, respn); + if (err != respn) { + dev_err(&core->client->dev, + "Error while reading response for command 0x%02x\n", + command); + err = (err >= 0) ? -EIO : err; + goto exit; + } + dev_dbg(&client->dev, "Response:\n %*ph\n", respn, resp); + + err = 0; + + if (resp[0] & SI476X_ERR) { + dev_err(&core->client->dev, + "[CMD 0x%02x] Chip set error flag\n", command); + err = si476x_core_parse_and_nag_about_error(core); + goto exit; + } + + if (!(resp[0] & SI476X_CTS)) + err = -EBUSY; +exit: + return err; +} + +static int si476x_cmd_clear_stc(struct si476x_core *core) +{ + int err; + struct si476x_rsq_status_args args = { + .primary = false, + .rsqack = false, + .attune = false, + .cancel = false, + .stcack = true, + }; + + switch (core->power_up_parameters.func) { + case SI476X_FUNC_FM_RECEIVER: + err = si476x_core_cmd_fm_rsq_status(core, &args, NULL); + break; + case SI476X_FUNC_AM_RECEIVER: + err = si476x_core_cmd_am_rsq_status(core, &args, NULL); + break; + default: + err = -EINVAL; + } + + return err; +} + +static int si476x_cmd_tune_seek_freq(struct si476x_core *core, + uint8_t cmd, + const uint8_t args[], size_t argn, + uint8_t *resp, size_t respn) +{ + int err; + + + atomic_set(&core->stc, 0); + err = si476x_core_send_command(core, cmd, args, argn, resp, respn, + SI476X_TIMEOUT_TUNE); + if (!err) { + wait_event_killable(core->tuning, + atomic_read(&core->stc)); + si476x_cmd_clear_stc(core); + } + + return err; +} + +/** + * si476x_cmd_func_info() - send 'FUNC_INFO' command to the device + * @core: device to send the command to + * @info: struct si476x_func_info to fill all the information + * returned by the command + * + * The command requests the firmware and patch version for currently + * loaded firmware (dependent on the function of the device FM/AM/WB) + * + * Function returns 0 on succsess and negative error code on + * failure + */ +int si476x_core_cmd_func_info(struct si476x_core *core, + struct si476x_func_info *info) +{ + int err; + u8 resp[CMD_FUNC_INFO_NRESP]; + + err = si476x_core_send_command(core, CMD_FUNC_INFO, + NULL, 0, + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + + info->firmware.major = resp[1]; + info->firmware.minor[0] = resp[2]; + info->firmware.minor[1] = resp[3]; + + info->patch_id = ((u16) resp[4] << 8) | resp[5]; + info->func = resp[6]; + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_func_info); + +/** + * si476x_cmd_set_property() - send 'SET_PROPERTY' command to the device + * @core: device to send the command to + * @property: property address + * @value: property value + * + * Function returns 0 on succsess and negative error code on + * failure + */ +int si476x_core_cmd_set_property(struct si476x_core *core, + u16 property, u16 value) +{ + u8 resp[CMD_SET_PROPERTY_NRESP]; + const u8 args[CMD_SET_PROPERTY_NARGS] = { + 0x00, + msb(property), + lsb(property), + msb(value), + lsb(value), + }; + + return si476x_core_send_command(core, CMD_SET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_set_property); + +/** + * si476x_cmd_get_property() - send 'GET_PROPERTY' command to the device + * @core: device to send the command to + * @property: property address + * + * Function return the value of property as u16 on success or a + * negative error on failure + */ +int si476x_core_cmd_get_property(struct si476x_core *core, u16 property) +{ + int err; + u8 resp[CMD_GET_PROPERTY_NRESP]; + const u8 args[CMD_GET_PROPERTY_NARGS] = { + 0x00, + msb(property), + lsb(property), + }; + + err = si476x_core_send_command(core, CMD_GET_PROPERTY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + if (err < 0) + return err; + else + return be16_to_cpup((__be16 *)(resp + 2)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_get_property); + +/** + * si476x_cmd_dig_audio_pin_cfg() - send 'DIG_AUDIO_PIN_CFG' command to + * the device + * @core: device to send the command to + * @dclk: DCLK pin function configuration: + * #SI476X_DCLK_NOOP - do not modify the behaviour + * #SI476X_DCLK_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * #SI476X_DCLK_DAUDIO - set the pin to be a part of digital + * audio interface + * @dfs: DFS pin function configuration: + * #SI476X_DFS_NOOP - do not modify the behaviour + * #SI476X_DFS_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_DFS_DAUDIO - set the pin to be a part of digital + * audio interface + * @dout - DOUT pin function configuration: + * SI476X_DOUT_NOOP - do not modify the behaviour + * SI476X_DOUT_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_DOUT_I2S_OUTPUT - set this pin to be digital out on I2S + * port 1 + * SI476X_DOUT_I2S_INPUT - set this pin to be digital in on I2S + * port 1 + * @xout - XOUT pin function configuration: + * SI476X_XOUT_NOOP - do not modify the behaviour + * SI476X_XOUT_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_XOUT_I2S_INPUT - set this pin to be digital in on I2S + * port 1 + * SI476X_XOUT_MODE_SELECT - set this pin to be the input that + * selects the mode of the I2S audio + * combiner (analog or HD) + * [SI4761/63/65/67 Only] + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *core, + enum si476x_dclk_config dclk, + enum si476x_dfs_config dfs, + enum si476x_dout_config dout, + enum si476x_xout_config xout) +{ + u8 resp[CMD_DIG_AUDIO_PIN_CFG_NRESP]; + const u8 args[CMD_DIG_AUDIO_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(dclk), + PIN_CFG_BYTE(dfs), + PIN_CFG_BYTE(dout), + PIN_CFG_BYTE(xout), + }; + + return si476x_core_send_command(core, CMD_DIG_AUDIO_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_dig_audio_pin_cfg); + +/** + * si476x_cmd_zif_pin_cfg - send 'ZIF_PIN_CFG_COMMAND' + * @core - device to send the command to + * @iqclk - IQCL pin function configuration: + * SI476X_IQCLK_NOOP - do not modify the behaviour + * SI476X_IQCLK_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_IQCLK_IQ - set pin to be a part of I/Q interace + * in master mode + * @iqfs - IQFS pin function configuration: + * SI476X_IQFS_NOOP - do not modify the behaviour + * SI476X_IQFS_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_IQFS_IQ - set pin to be a part of I/Q interace + * in master mode + * @iout - IOUT pin function configuration: + * SI476X_IOUT_NOOP - do not modify the behaviour + * SI476X_IOUT_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_IOUT_OUTPUT - set pin to be I out + * @qout - QOUT pin function configuration: + * SI476X_QOUT_NOOP - do not modify the behaviour + * SI476X_QOUT_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_QOUT_OUTPUT - set pin to be Q out + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_zif_pin_cfg(struct si476x_core *core, + enum si476x_iqclk_config iqclk, + enum si476x_iqfs_config iqfs, + enum si476x_iout_config iout, + enum si476x_qout_config qout) +{ + u8 resp[CMD_ZIF_PIN_CFG_NRESP]; + const u8 args[CMD_ZIF_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(iqclk), + PIN_CFG_BYTE(iqfs), + PIN_CFG_BYTE(iout), + PIN_CFG_BYTE(qout), + }; + + return si476x_core_send_command(core, CMD_ZIF_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_zif_pin_cfg); + +/** + * si476x_cmd_ic_link_gpo_ctl_pin_cfg - send + * 'IC_LINK_GPIO_CTL_PIN_CFG' comand to the device + * @core - device to send the command to + * @icin - ICIN pin function configuration: + * SI476X_ICIN_NOOP - do not modify the behaviour + * SI476X_ICIN_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_ICIN_GPO1_HIGH - set pin to be an output, drive it high + * SI476X_ICIN_GPO1_LOW - set pin to be an output, drive it low + * SI476X_ICIN_IC_LINK - set the pin to be a part of Inter-Chip link + * @icip - ICIP pin function configuration: + * SI476X_ICIP_NOOP - do not modify the behaviour + * SI476X_ICIP_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_ICIP_GPO1_HIGH - set pin to be an output, drive it high + * SI476X_ICIP_GPO1_LOW - set pin to be an output, drive it low + * SI476X_ICIP_IC_LINK - set the pin to be a part of Inter-Chip link + * @icon - ICON pin function configuration: + * SI476X_ICON_NOOP - do not modify the behaviour + * SI476X_ICON_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_ICON_I2S - set the pin to be a part of audio + * interface in slave mode (DCLK) + * SI476X_ICON_IC_LINK - set the pin to be a part of Inter-Chip link + * @icop - ICOP pin function configuration: + * SI476X_ICOP_NOOP - do not modify the behaviour + * SI476X_ICOP_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_ICOP_I2S - set the pin to be a part of audio + * interface in slave mode (DOUT) + * [Si4761/63/65/67 Only] + * SI476X_ICOP_IC_LINK - set the pin to be a part of Inter-Chip link + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *core, + enum si476x_icin_config icin, + enum si476x_icip_config icip, + enum si476x_icon_config icon, + enum si476x_icop_config icop) +{ + u8 resp[CMD_IC_LINK_GPO_CTL_PIN_CFG_NRESP]; + const u8 args[CMD_IC_LINK_GPO_CTL_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(icin), + PIN_CFG_BYTE(icip), + PIN_CFG_BYTE(icon), + PIN_CFG_BYTE(icop), + }; + + return si476x_core_send_command(core, CMD_IC_LINK_GPO_CTL_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_ic_link_gpo_ctl_pin_cfg); + +/** + * si476x_cmd_ana_audio_pin_cfg - send 'ANA_AUDIO_PIN_CFG' to the + * device + * @core - device to send the command to + * @lrout - LROUT pin function configuration: + * SI476X_LROUT_NOOP - do not modify the behaviour + * SI476X_LROUT_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_LROUT_AUDIO - set pin to be audio output + * SI476X_LROUT_MPX - set pin to be MPX output + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *core, + enum si476x_lrout_config lrout) +{ + u8 resp[CMD_ANA_AUDIO_PIN_CFG_NRESP]; + const u8 args[CMD_ANA_AUDIO_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(lrout), + }; + + return si476x_core_send_command(core, CMD_ANA_AUDIO_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_ana_audio_pin_cfg); + + +/** + * si476x_cmd_intb_pin_cfg - send 'INTB_PIN_CFG' command to the device + * @core - device to send the command to + * @intb - INTB pin function configuration: + * SI476X_INTB_NOOP - do not modify the behaviour + * SI476X_INTB_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_INTB_DAUDIO - set pin to be a part of digital + * audio interface in slave mode + * SI476X_INTB_IRQ - set pin to be an interrupt request line + * @a1 - A1 pin function configuration: + * SI476X_A1_NOOP - do not modify the behaviour + * SI476X_A1_TRISTATE - put the pin in tristate condition, + * enable 1MOhm pulldown + * SI476X_A1_IRQ - set pin to be an interrupt request line + * + * Function returns 0 on success and negative error code on failure + */ +static int si476x_core_cmd_intb_pin_cfg_a10(struct si476x_core *core, + enum si476x_intb_config intb, + enum si476x_a1_config a1) +{ + u8 resp[CMD_INTB_PIN_CFG_A10_NRESP]; + const u8 args[CMD_INTB_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(intb), + PIN_CFG_BYTE(a1), + }; + + return si476x_core_send_command(core, CMD_INTB_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_intb_pin_cfg_a20(struct si476x_core *core, + enum si476x_intb_config intb, + enum si476x_a1_config a1) +{ + u8 resp[CMD_INTB_PIN_CFG_A20_NRESP]; + const u8 args[CMD_INTB_PIN_CFG_NARGS] = { + PIN_CFG_BYTE(intb), + PIN_CFG_BYTE(a1), + }; + + return si476x_core_send_command(core, CMD_INTB_PIN_CFG, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} + + + +/** + * si476x_cmd_am_rsq_status - send 'AM_RSQ_STATUS' command to the + * device + * @core - device to send the command to + * @rsqack - if set command clears RSQINT, SNRINT, SNRLINT, RSSIHINT, + * RSSSILINT, BLENDINT, MULTHINT and MULTLINT + * @attune - when set the values in the status report are the values + * that were calculated at tune + * @cancel - abort ongoing seek/tune opertation + * @stcack - clear the STCINT bin in status register + * @report - all signal quality information retured by the command + * (if NULL then the output of the command is ignored) + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_am_rsq_status(struct si476x_core *core, + struct si476x_rsq_status_args *rsqargs, + struct si476x_rsq_status_report *report) +{ + int err; + u8 resp[CMD_AM_RSQ_STATUS_NRESP]; + const u8 args[CMD_AM_RSQ_STATUS_NARGS] = { + rsqargs->rsqack << 3 | rsqargs->attune << 2 | + rsqargs->cancel << 1 | rsqargs->stcack, + }; + + err = si476x_core_send_command(core, CMD_AM_RSQ_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + /* + * Besides getting received signal quality information this + * command can be used to just acknowledge different interrupt + * flags in those cases it is useless to copy and parse + * received data so user can pass NULL, and thus avoid + * unnecessary copying. + */ + if (!report) + return err; + + report->snrhint = 0b00001000 & resp[1]; + report->snrlint = 0b00000100 & resp[1]; + report->rssihint = 0b00000010 & resp[1]; + report->rssilint = 0b00000001 & resp[1]; + + report->bltf = 0b10000000 & resp[2]; + report->snr_ready = 0b00100000 & resp[2]; + report->rssiready = 0b00001000 & resp[2]; + report->afcrl = 0b00000010 & resp[2]; + report->valid = 0b00000001 & resp[2]; + + report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); + report->freqoff = resp[5]; + report->rssi = resp[6]; + report->snr = resp[7]; + report->lassi = resp[9]; + report->hassi = resp[10]; + report->mult = resp[11]; + report->dev = resp[12]; + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_rsq_status); + +int si476x_core_cmd_fm_acf_status(struct si476x_core *core, + struct si476x_acf_status_report *report) +{ + int err; + u8 resp[CMD_FM_ACF_STATUS_NRESP]; + const u8 args[CMD_FM_ACF_STATUS_NARGS] = { + 0x0, + }; + + if (!report) + return -EINVAL; + + err = si476x_core_send_command(core, CMD_FM_ACF_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + if (err < 0) + return err; + + report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; + report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; + report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; + report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; + report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; + report->smute = resp[2] & SI476X_ACF_SMUTE; + report->smattn = resp[3] & SI476X_ACF_SMATTN; + report->chbw = resp[4]; + report->hicut = resp[5]; + report->hiblend = resp[6]; + report->pilot = resp[7] & SI476X_ACF_PILOT; + report->stblend = resp[7] & SI476X_ACF_STBLEND; + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_acf_status); + +int si476x_core_cmd_am_acf_status(struct si476x_core *core, + struct si476x_acf_status_report *report) +{ + int err; + u8 resp[CMD_AM_ACF_STATUS_NRESP]; + const u8 args[CMD_AM_ACF_STATUS_NARGS] = { + 0x0, + }; + + if (!report) + return -EINVAL; + + err = si476x_core_send_command(core, CMD_AM_ACF_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + if (err < 0) + return err; + + report->blend_int = resp[1] & SI476X_ACF_BLEND_INT; + report->hblend_int = resp[1] & SI476X_ACF_HIBLEND_INT; + report->hicut_int = resp[1] & SI476X_ACF_HICUT_INT; + report->chbw_int = resp[1] & SI476X_ACF_CHBW_INT; + report->softmute_int = resp[1] & SI476X_ACF_SOFTMUTE_INT; + report->smute = resp[2] & SI476X_ACF_SMUTE; + report->smattn = resp[3] & SI476X_ACF_SMATTN; + report->chbw = resp[4]; + report->hicut = resp[5]; + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_acf_status); + + +/** + * si476x_cmd_fm_seek_start - send 'FM_SEEK_START' command to the + * device + * @core - device to send the command to + * @seekup - if set the direction of the search is 'up' + * @wrap - if set seek wraps when hitting band limit + * + * This function begins search for a valid station. The station is + * considered valid when 'FM_VALID_SNR_THRESHOLD' and + * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria + * are met. +} * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_fm_seek_start(struct si476x_core *core, + bool seekup, bool wrap) +{ + u8 resp[CMD_FM_SEEK_START_NRESP]; + const u8 args[CMD_FM_SEEK_START_NARGS] = { + seekup << 3 | wrap << 2, + }; + + return si476x_cmd_tune_seek_freq(core, CMD_FM_SEEK_START, + args, sizeof(args), + resp, sizeof(resp)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_seek_start); + +/** + * si476x_cmd_fm_rds_status - send 'FM_RDS_STATUS' command to the + * device + * @core - device to send the command to + * @status_only - if set the data is not removed from RDSFIFO, + * RDSFIFOUSED is not decremented and data in all the + * rest RDS data contains the last valid info received + * @mtfifo if set the command clears RDS receive FIFO + * @intack if set the command clards the RDSINT bit. + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_fm_rds_status(struct si476x_core *core, + bool status_only, + bool mtfifo, + bool intack, + struct si476x_rds_status_report *report) +{ + int err; + u8 resp[CMD_FM_RDS_STATUS_NRESP]; + const u8 args[CMD_FM_RDS_STATUS_NARGS] = { + status_only << 2 | mtfifo << 1 | intack, + }; + + err = si476x_core_send_command(core, CMD_FM_RDS_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + /* + * Besides getting RDS status information this command can be + * used to just acknowledge different interrupt flags in those + * cases it is useless to copy and parse received data so user + * can pass NULL, and thus avoid unnecessary copying. + */ + if (err < 0 || report == NULL) + return err; + + report->rdstpptyint = 0b00010000 & resp[1]; + report->rdspiint = 0b00001000 & resp[1]; + report->rdssyncint = 0b00000010 & resp[1]; + report->rdsfifoint = 0b00000001 & resp[1]; + + report->tpptyvalid = 0b00010000 & resp[2]; + report->pivalid = 0b00001000 & resp[2]; + report->rdssync = 0b00000010 & resp[2]; + report->rdsfifolost = 0b00000001 & resp[2]; + + report->tp = 0b00100000 & resp[3]; + report->pty = 0b00011111 & resp[3]; + + report->pi = be16_to_cpup((__be16 *)(resp + 4)); + report->rdsfifoused = resp[6]; + + report->ble[V4L2_RDS_BLOCK_A] = 0b11000000 & resp[7]; + report->ble[V4L2_RDS_BLOCK_B] = 0b00110000 & resp[7]; + report->ble[V4L2_RDS_BLOCK_C] = 0b00001100 & resp[7]; + report->ble[V4L2_RDS_BLOCK_D] = 0b00000011 & resp[7]; + + report->rds[V4L2_RDS_BLOCK_A].block = V4L2_RDS_BLOCK_A; + report->rds[V4L2_RDS_BLOCK_A].msb = resp[8]; + report->rds[V4L2_RDS_BLOCK_A].lsb = resp[9]; + + report->rds[V4L2_RDS_BLOCK_B].block = V4L2_RDS_BLOCK_B; + report->rds[V4L2_RDS_BLOCK_B].msb = resp[10]; + report->rds[V4L2_RDS_BLOCK_B].lsb = resp[11]; + + report->rds[V4L2_RDS_BLOCK_C].block = V4L2_RDS_BLOCK_C; + report->rds[V4L2_RDS_BLOCK_C].msb = resp[12]; + report->rds[V4L2_RDS_BLOCK_C].lsb = resp[13]; + + report->rds[V4L2_RDS_BLOCK_D].block = V4L2_RDS_BLOCK_D; + report->rds[V4L2_RDS_BLOCK_D].msb = resp[14]; + report->rds[V4L2_RDS_BLOCK_D].lsb = resp[15]; + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_status); + +int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *core, + bool clear, + struct si476x_rds_blockcount_report *report) +{ + int err; + u8 resp[CMD_FM_RDS_BLOCKCOUNT_NRESP]; + const u8 args[CMD_FM_RDS_BLOCKCOUNT_NARGS] = { + clear, + }; + + if (!report) + return -EINVAL; + + err = si476x_core_send_command(core, CMD_FM_RDS_BLOCKCOUNT, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + + if (!err) { + report->expected = be16_to_cpup((__be16 *)(resp + 2)); + report->received = be16_to_cpup((__be16 *)(resp + 4)); + report->uncorrectable = be16_to_cpup((__be16 *)(resp + 6)); + } + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rds_blockcount); + +int si476x_core_cmd_fm_phase_diversity(struct si476x_core *core, + enum si476x_phase_diversity_mode mode) +{ + u8 resp[CMD_FM_PHASE_DIVERSITY_NRESP]; + const u8 args[CMD_FM_PHASE_DIVERSITY_NARGS] = { + mode & 0b111, + }; + + return si476x_core_send_command(core, CMD_FM_PHASE_DIVERSITY, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_diversity); +/** + * si476x_core_cmd_fm_phase_div_status() - get the phase diversity + * status + * + * @core: si476x device + * + * NOTE caller must hold core lock + * + * Function returns the value of the status bit in case of success and + * negative error code in case of failre. + */ +int si476x_core_cmd_fm_phase_div_status(struct si476x_core *core) +{ + int err; + u8 resp[CMD_FM_PHASE_DIV_STATUS_NRESP]; + + err = si476x_core_send_command(core, CMD_FM_PHASE_DIV_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + + return (err < 0) ? err : resp[1]; +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_phase_div_status); + + +/** + * si476x_cmd_am_seek_start - send 'FM_SEEK_START' command to the + * device + * @core - device to send the command to + * @seekup - if set the direction of the search is 'up' + * @wrap - if set seek wraps when hitting band limit + * + * This function begins search for a valid station. The station is + * considered valid when 'FM_VALID_SNR_THRESHOLD' and + * 'FM_VALID_RSSI_THRESHOLD' and 'FM_VALID_MAX_TUNE_ERROR' criteria + * are met. + * + * Function returns 0 on success and negative error code on failure + */ +int si476x_core_cmd_am_seek_start(struct si476x_core *core, + bool seekup, bool wrap) +{ + u8 resp[CMD_AM_SEEK_START_NRESP]; + const u8 args[CMD_AM_SEEK_START_NARGS] = { + seekup << 3 | wrap << 2, + }; + + return si476x_cmd_tune_seek_freq(core, CMD_AM_SEEK_START, + args, sizeof(args), + resp, sizeof(resp)); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_seek_start); + + + +static int si476x_core_cmd_power_up_a10(struct si476x_core *core, + struct si476x_power_up_args *puargs) +{ + u8 resp[CMD_POWER_UP_A10_NRESP]; + const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); + const bool ctsen = (core->client->irq != 0); + const u8 args[CMD_POWER_UP_A10_NARGS] = { + 0xF7, /* Reserved, always 0xF7 */ + 0x3F & puargs->xcload, /* First two bits are reserved to be + * zeros */ + ctsen << 7 | intsel << 6 | 0x07, /* Last five bits + * are reserved to + * be written as 0x7 */ + puargs->func << 4 | puargs->freq, + 0x11, /* Reserved, always 0x11 */ + }; + + return si476x_core_send_command(core, CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_TIMEOUT_POWER_UP); +} + +static int si476x_core_cmd_power_up_a20(struct si476x_core *core, + struct si476x_power_up_args *puargs) +{ + u8 resp[CMD_POWER_UP_A20_NRESP]; + const bool intsel = (core->pinmux.a1 == SI476X_A1_IRQ); + const bool ctsen = (core->client->irq != 0); + const u8 args[CMD_POWER_UP_A20_NARGS] = { + puargs->ibias6x << 7 | puargs->xstart, + 0x3F & puargs->xcload, /* First two bits are reserved to be + * zeros */ + ctsen << 7 | intsel << 6 | puargs->fastboot << 5 | + puargs->xbiashc << 3 | puargs->xbias, + puargs->func << 4 | puargs->freq, + 0x10 | puargs->xmode, + }; + + return si476x_core_send_command(core, CMD_POWER_UP, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_TIMEOUT_POWER_UP); +} + +static int si476x_core_cmd_power_down_a10(struct si476x_core *core, + struct si476x_power_down_args *pdargs) +{ + u8 resp[CMD_POWER_DOWN_A10_NRESP]; + + return si476x_core_send_command(core, CMD_POWER_DOWN, + NULL, 0, + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_power_down_a20(struct si476x_core *core, + struct si476x_power_down_args *pdargs) +{ + u8 resp[CMD_POWER_DOWN_A20_NRESP]; + const u8 args[CMD_POWER_DOWN_A20_NARGS] = { + pdargs->xosc, + }; + return si476x_core_send_command(core, CMD_POWER_DOWN, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); +} + +static int si476x_core_cmd_am_tune_freq_a10(struct si476x_core *core, + struct si476x_tune_freq_args *tuneargs) +{ + + const int am_freq = tuneargs->freq; + u8 resp[CMD_AM_TUNE_FREQ_NRESP]; + const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { + (tuneargs->hd << 6), + msb(am_freq), + lsb(am_freq), + }; + + return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, args, + sizeof(args), + resp, sizeof(resp)); +} + +static int si476x_core_cmd_am_tune_freq_a20(struct si476x_core *core, + struct si476x_tune_freq_args *tuneargs) +{ + const int am_freq = tuneargs->freq; + u8 resp[CMD_AM_TUNE_FREQ_NRESP]; + const u8 args[CMD_AM_TUNE_FREQ_NARGS] = { + (tuneargs->zifsr << 6) | (tuneargs->injside & 0b11), + msb(am_freq), + lsb(am_freq), + }; + + return si476x_cmd_tune_seek_freq(core, CMD_AM_TUNE_FREQ, + args, sizeof(args), + resp, sizeof(resp)); +} + +static int si476x_core_cmd_fm_rsq_status_a10(struct si476x_core *core, + struct si476x_rsq_status_args *rsqargs, + struct si476x_rsq_status_report *report) +{ + int err; + u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; + const u8 args[CMD_FM_RSQ_STATUS_A10_NARGS] = { + rsqargs->rsqack << 3 | rsqargs->attune << 2 | + rsqargs->cancel << 1 | rsqargs->stcack, + }; + + err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + /* + * Besides getting received signal quality information this + * command can be used to just acknowledge different interrupt + * flags in those cases it is useless to copy and parse + * received data so user can pass NULL, and thus avoid + * unnecessary copying. + */ + if (err < 0 || report == NULL) + return err; + + report->multhint = 0b10000000 & resp[1]; + report->multlint = 0b01000000 & resp[1]; + report->snrhint = 0b00001000 & resp[1]; + report->snrlint = 0b00000100 & resp[1]; + report->rssihint = 0b00000010 & resp[1]; + report->rssilint = 0b00000001 & resp[1]; + + report->bltf = 0b10000000 & resp[2]; + report->snr_ready = 0b00100000 & resp[2]; + report->rssiready = 0b00001000 & resp[2]; + report->afcrl = 0b00000010 & resp[2]; + report->valid = 0b00000001 & resp[2]; + + report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); + report->freqoff = resp[5]; + report->rssi = resp[6]; + report->snr = resp[7]; + report->lassi = resp[9]; + report->hassi = resp[10]; + report->mult = resp[11]; + report->dev = resp[12]; + report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); + report->assi = resp[15]; + report->usn = resp[16]; + + return err; +} + +static int si476x_core_cmd_fm_rsq_status_a20(struct si476x_core *core, + struct si476x_rsq_status_args *rsqargs, + struct si476x_rsq_status_report *report) +{ + int err; + u8 resp[CMD_FM_RSQ_STATUS_A10_NRESP]; + const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { + rsqargs->primary << 4 | rsqargs->rsqack << 3 | + rsqargs->attune << 2 | rsqargs->cancel << 1 | + rsqargs->stcack, + }; + + err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + /* + * Besides getting received signal quality information this + * command can be used to just acknowledge different interrupt + * flags in those cases it is useless to copy and parse + * received data so user can pass NULL, and thus avoid + * unnecessary copying. + */ + if (err < 0 || report == NULL) + return err; + + report->multhint = 0b10000000 & resp[1]; + report->multlint = 0b01000000 & resp[1]; + report->snrhint = 0b00001000 & resp[1]; + report->snrlint = 0b00000100 & resp[1]; + report->rssihint = 0b00000010 & resp[1]; + report->rssilint = 0b00000001 & resp[1]; + + report->bltf = 0b10000000 & resp[2]; + report->snr_ready = 0b00100000 & resp[2]; + report->rssiready = 0b00001000 & resp[2]; + report->afcrl = 0b00000010 & resp[2]; + report->valid = 0b00000001 & resp[2]; + + report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); + report->freqoff = resp[5]; + report->rssi = resp[6]; + report->snr = resp[7]; + report->lassi = resp[9]; + report->hassi = resp[10]; + report->mult = resp[11]; + report->dev = resp[12]; + report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); + report->assi = resp[15]; + report->usn = resp[16]; + + return err; +} + + +static int si476x_core_cmd_fm_rsq_status_a30(struct si476x_core *core, + struct si476x_rsq_status_args *rsqargs, + struct si476x_rsq_status_report *report) +{ + int err; + u8 resp[CMD_FM_RSQ_STATUS_A30_NRESP]; + const u8 args[CMD_FM_RSQ_STATUS_A30_NARGS] = { + rsqargs->primary << 4 | rsqargs->rsqack << 3 | + rsqargs->attune << 2 | rsqargs->cancel << 1 | + rsqargs->stcack, + }; + + err = si476x_core_send_command(core, CMD_FM_RSQ_STATUS, + args, ARRAY_SIZE(args), + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + /* + * Besides getting received signal quality information this + * command can be used to just acknowledge different interrupt + * flags in those cases it is useless to copy and parse + * received data so user can pass NULL, and thus avoid + * unnecessary copying. + */ + if (err < 0 || report == NULL) + return err; + + report->multhint = 0b10000000 & resp[1]; + report->multlint = 0b01000000 & resp[1]; + report->snrhint = 0b00001000 & resp[1]; + report->snrlint = 0b00000100 & resp[1]; + report->rssihint = 0b00000010 & resp[1]; + report->rssilint = 0b00000001 & resp[1]; + + report->bltf = 0b10000000 & resp[2]; + report->snr_ready = 0b00100000 & resp[2]; + report->rssiready = 0b00001000 & resp[2]; + report->injside = 0b00000100 & resp[2]; + report->afcrl = 0b00000010 & resp[2]; + report->valid = 0b00000001 & resp[2]; + + report->readfreq = be16_to_cpup((__be16 *)(resp + 3)); + report->freqoff = resp[5]; + report->rssi = resp[6]; + report->snr = resp[7]; + report->issi = resp[8]; + report->lassi = resp[9]; + report->hassi = resp[10]; + report->mult = resp[11]; + report->dev = resp[12]; + report->readantcap = be16_to_cpup((__be16 *)(resp + 13)); + report->assi = resp[15]; + report->usn = resp[16]; + + report->pilotdev = resp[17]; + report->rdsdev = resp[18]; + report->assidev = resp[19]; + report->strongdev = resp[20]; + report->rdspi = be16_to_cpup((__be16 *)(resp + 21)); + + return err; +} + +static int si476x_core_cmd_fm_tune_freq_a10(struct si476x_core *core, + struct si476x_tune_freq_args *tuneargs) +{ + u8 resp[CMD_FM_TUNE_FREQ_NRESP]; + const u8 args[CMD_FM_TUNE_FREQ_A10_NARGS] = { + (tuneargs->hd << 6) | (tuneargs->tunemode << 4) + | (tuneargs->smoothmetrics << 2), + msb(tuneargs->freq), + lsb(tuneargs->freq), + msb(tuneargs->antcap), + lsb(tuneargs->antcap) + }; + + return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, + args, sizeof(args), + resp, sizeof(resp)); +} + +static int si476x_core_cmd_fm_tune_freq_a20(struct si476x_core *core, + struct si476x_tune_freq_args *tuneargs) +{ + u8 resp[CMD_FM_TUNE_FREQ_NRESP]; + const u8 args[CMD_FM_TUNE_FREQ_A20_NARGS] = { + (tuneargs->hd << 6) | (tuneargs->tunemode << 4) + | (tuneargs->smoothmetrics << 2) | (tuneargs->injside), + msb(tuneargs->freq), + lsb(tuneargs->freq), + }; + + return si476x_cmd_tune_seek_freq(core, CMD_FM_TUNE_FREQ, + args, sizeof(args), + resp, sizeof(resp)); +} + +static int si476x_core_cmd_agc_status_a20(struct si476x_core *core, + struct si476x_agc_status_report *report) +{ + int err; + u8 resp[CMD_AGC_STATUS_NRESP_A20]; + + if (!report) + return -EINVAL; + + err = si476x_core_send_command(core, CMD_AGC_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + if (err < 0) + return err; + + report->mxhi = resp[1] & SI476X_AGC_MXHI; + report->mxlo = resp[1] & SI476X_AGC_MXLO; + report->lnahi = resp[1] & SI476X_AGC_LNAHI; + report->lnalo = resp[1] & SI476X_AGC_LNALO; + report->fmagc1 = resp[2]; + report->fmagc2 = resp[3]; + report->pgagain = resp[4]; + report->fmwblang = resp[5]; + + return err; +} + +static int si476x_core_cmd_agc_status_a10(struct si476x_core *core, + struct si476x_agc_status_report *report) +{ + int err; + u8 resp[CMD_AGC_STATUS_NRESP_A10]; + + if (!report) + return -EINVAL; + + err = si476x_core_send_command(core, CMD_AGC_STATUS, + NULL, 0, + resp, ARRAY_SIZE(resp), + SI476X_DEFAULT_TIMEOUT); + if (err < 0) + return err; + + report->mxhi = resp[1] & SI476X_AGC_MXHI; + report->mxlo = resp[1] & SI476X_AGC_MXLO; + report->lnahi = resp[1] & SI476X_AGC_LNAHI; + report->lnalo = resp[1] & SI476X_AGC_LNALO; + + return err; +} + +typedef int (*tune_freq_func_t) (struct si476x_core *core, + struct si476x_tune_freq_args *tuneargs); + +static struct { + int (*power_up) (struct si476x_core *, + struct si476x_power_up_args *); + int (*power_down) (struct si476x_core *, + struct si476x_power_down_args *); + + tune_freq_func_t fm_tune_freq; + tune_freq_func_t am_tune_freq; + + int (*fm_rsq_status)(struct si476x_core *, + struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); + + int (*agc_status)(struct si476x_core *, + struct si476x_agc_status_report *); + int (*intb_pin_cfg)(struct si476x_core *core, + enum si476x_intb_config intb, + enum si476x_a1_config a1); +} si476x_cmds_vtable[] = { + [SI476X_REVISION_A10] = { + .power_up = si476x_core_cmd_power_up_a10, + .power_down = si476x_core_cmd_power_down_a10, + .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a10, + .am_tune_freq = si476x_core_cmd_am_tune_freq_a10, + .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a10, + .agc_status = si476x_core_cmd_agc_status_a10, + .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a10, + }, + [SI476X_REVISION_A20] = { + .power_up = si476x_core_cmd_power_up_a20, + .power_down = si476x_core_cmd_power_down_a20, + .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, + .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, + .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a20, + .agc_status = si476x_core_cmd_agc_status_a20, + .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, + }, + [SI476X_REVISION_A30] = { + .power_up = si476x_core_cmd_power_up_a20, + .power_down = si476x_core_cmd_power_down_a20, + .fm_tune_freq = si476x_core_cmd_fm_tune_freq_a20, + .am_tune_freq = si476x_core_cmd_am_tune_freq_a20, + .fm_rsq_status = si476x_core_cmd_fm_rsq_status_a30, + .agc_status = si476x_core_cmd_agc_status_a20, + .intb_pin_cfg = si476x_core_cmd_intb_pin_cfg_a20, + }, +}; + +int si476x_core_cmd_power_up(struct si476x_core *core, + struct si476x_power_up_args *args) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].power_up(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_power_up); + +int si476x_core_cmd_power_down(struct si476x_core *core, + struct si476x_power_down_args *args) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].power_down(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_power_down); + +int si476x_core_cmd_fm_tune_freq(struct si476x_core *core, + struct si476x_tune_freq_args *args) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].fm_tune_freq(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_tune_freq); + +int si476x_core_cmd_am_tune_freq(struct si476x_core *core, + struct si476x_tune_freq_args *args) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].am_tune_freq(core, args); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_am_tune_freq); + +int si476x_core_cmd_fm_rsq_status(struct si476x_core *core, + struct si476x_rsq_status_args *args, + struct si476x_rsq_status_report *report) + +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].fm_rsq_status(core, args, + report); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_fm_rsq_status); + +int si476x_core_cmd_agc_status(struct si476x_core *core, + struct si476x_agc_status_report *report) + +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return si476x_cmds_vtable[core->revision].agc_status(core, report); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_agc_status); + +int si476x_core_cmd_intb_pin_cfg(struct si476x_core *core, + enum si476x_intb_config intb, + enum si476x_a1_config a1) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + + return si476x_cmds_vtable[core->revision].intb_pin_cfg(core, intb, a1); +} +EXPORT_SYMBOL_GPL(si476x_core_cmd_intb_pin_cfg); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("API for command exchange for si476x"); -- cgit v0.10.2 From 730a30ab7fba60c986e0e4a362bb21d2ed196a29 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 18 Apr 2013 09:58:28 -0700 Subject: mfd: si476x: Add the bulk of the core driver This patch adds main part(out of three) of the I2C driver for the "core" of MFD device. Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c new file mode 100644 index 0000000..118c6b1 --- /dev/null +++ b/drivers/mfd/si476x-i2c.c @@ -0,0 +1,886 @@ +/* + * drivers/mfd/si476x-i2c.c -- Core device driver for si476x MFD + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SI476X_MAX_IO_ERRORS 10 +#define SI476X_DRIVER_RDS_FIFO_DEPTH 128 + +/** + * si476x_core_config_pinmux() - pin function configuration function + * + * @core: Core device structure + * + * Configure the functions of the pins of the radio chip. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +static int si476x_core_config_pinmux(struct si476x_core *core) +{ + int err; + dev_dbg(&core->client->dev, "Configuring pinmux\n"); + err = si476x_core_cmd_dig_audio_pin_cfg(core, + core->pinmux.dclk, + core->pinmux.dfs, + core->pinmux.dout, + core->pinmux.xout); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure digital audio pins(err = %d)\n", + err); + return err; + } + + err = si476x_core_cmd_zif_pin_cfg(core, + core->pinmux.iqclk, + core->pinmux.iqfs, + core->pinmux.iout, + core->pinmux.qout); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure ZIF pins(err = %d)\n", + err); + return err; + } + + err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, + core->pinmux.icin, + core->pinmux.icip, + core->pinmux.icon, + core->pinmux.icop); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure IC-Link/GPO pins(err = %d)\n", + err); + return err; + } + + err = si476x_core_cmd_ana_audio_pin_cfg(core, + core->pinmux.lrout); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure analog audio pins(err = %d)\n", + err); + return err; + } + + err = si476x_core_cmd_intb_pin_cfg(core, + core->pinmux.intb, + core->pinmux.a1); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure interrupt pins(err = %d)\n", + err); + return err; + } + + return 0; +} + +static inline void si476x_core_schedule_polling_work(struct si476x_core *core) +{ + schedule_delayed_work(&core->status_monitor, + usecs_to_jiffies(SI476X_STATUS_POLL_US)); +} + +/** + * si476x_core_start() - early chip startup function + * @core: Core device structure + * @soft: When set, this flag forces "soft" startup, where "soft" + * power down is the one done by sending appropriate command instead + * of using reset pin of the tuner + * + * Perform required startup sequence to correctly power + * up the chip and perform initial configuration. It does the + * following sequence of actions: + * 1. Claims and enables the power supplies VD and VIO1 required + * for I2C interface of the chip operation. + * 2. Waits for 100us, pulls the reset line up, enables irq, + * waits for another 100us as it is specified by the + * datasheet. + * 3. Sends 'POWER_UP' command to the device with all provided + * information about power-up parameters. + * 4. Configures, pin multiplexor, disables digital audio and + * configures interrupt sources. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +int si476x_core_start(struct si476x_core *core, bool soft) +{ + struct i2c_client *client = core->client; + int err; + + if (!soft) { + if (gpio_is_valid(core->gpio_reset)) + gpio_set_value_cansleep(core->gpio_reset, 1); + + if (client->irq) + enable_irq(client->irq); + + udelay(100); + + if (!client->irq) { + atomic_set(&core->is_alive, 1); + si476x_core_schedule_polling_work(core); + } + } else { + if (client->irq) + enable_irq(client->irq); + else { + atomic_set(&core->is_alive, 1); + si476x_core_schedule_polling_work(core); + } + } + + err = si476x_core_cmd_power_up(core, + &core->power_up_parameters); + + if (err < 0) { + dev_err(&core->client->dev, + "Power up failure(err = %d)\n", + err); + goto disable_irq; + } + + if (client->irq) + atomic_set(&core->is_alive, 1); + + err = si476x_core_config_pinmux(core); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure pinmux(err = %d)\n", + err); + goto disable_irq; + } + + if (client->irq) { + err = regmap_write(core->regmap, + SI476X_PROP_INT_CTL_ENABLE, + SI476X_RDSIEN | + SI476X_STCIEN | + SI476X_CTSIEN); + if (err < 0) { + dev_err(&core->client->dev, + "Failed to configure interrupt sources" + "(err = %d)\n", err); + goto disable_irq; + } + } + + return 0; + +disable_irq: + if (err == -ENODEV) + atomic_set(&core->is_alive, 0); + + if (client->irq) + disable_irq(client->irq); + else + cancel_delayed_work_sync(&core->status_monitor); + + if (gpio_is_valid(core->gpio_reset)) + gpio_set_value_cansleep(core->gpio_reset, 0); + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_start); + +/** + * si476x_core_stop() - chip power-down function + * @core: Core device structure + * @soft: When set, function sends a POWER_DOWN command instead of + * bringing reset line low + * + * Power down the chip by performing following actions: + * 1. Disable IRQ or stop the polling worker + * 2. Send the POWER_DOWN command if the power down is soft or bring + * reset line low if not. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +int si476x_core_stop(struct si476x_core *core, bool soft) +{ + int err = 0; + atomic_set(&core->is_alive, 0); + + if (soft) { + /* TODO: This probably shoud be a configurable option, + * so it is possible to have the chips keep their + * oscillators running + */ + struct si476x_power_down_args args = { + .xosc = false, + }; + err = si476x_core_cmd_power_down(core, &args); + } + + /* We couldn't disable those before + * 'si476x_core_cmd_power_down' since we expect to get CTS + * interrupt */ + if (core->client->irq) + disable_irq(core->client->irq); + else + cancel_delayed_work_sync(&core->status_monitor); + + if (!soft) { + if (gpio_is_valid(core->gpio_reset)) + gpio_set_value_cansleep(core->gpio_reset, 0); + } + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_stop); + +/** + * si476x_core_set_power_state() - set the level at which the power is + * supplied for the chip. + * @core: Core device structure + * @next_state: enum si476x_power_state describing power state to + * switch to. + * + * Switch on all the required power supplies + * + * This function returns 0 in case of suvccess and negative error code + * otherwise. + */ +int si476x_core_set_power_state(struct si476x_core *core, + enum si476x_power_state next_state) +{ + /* + It is not clear form the datasheet if it is possible to + work with device if not all power domains are operational. + So for now the power-up policy is "power-up all the things!" + */ + int err = 0; + + if (core->power_state == SI476X_POWER_INCONSISTENT) { + dev_err(&core->client->dev, + "The device in inconsistent power state\n"); + return -EINVAL; + } + + if (next_state != core->power_state) { + switch (next_state) { + case SI476X_POWER_UP_FULL: + err = regulator_bulk_enable(ARRAY_SIZE(core->supplies), + core->supplies); + if (err < 0) { + core->power_state = SI476X_POWER_INCONSISTENT; + break; + } + /* + * Startup timing diagram recommends to have a + * 100 us delay between enabling of the power + * supplies and turning the tuner on. + */ + udelay(100); + + err = si476x_core_start(core, false); + if (err < 0) + goto disable_regulators; + + core->power_state = next_state; + break; + + case SI476X_POWER_DOWN: + core->power_state = next_state; + err = si476x_core_stop(core, false); + if (err < 0) + core->power_state = SI476X_POWER_INCONSISTENT; +disable_regulators: + err = regulator_bulk_disable(ARRAY_SIZE(core->supplies), + core->supplies); + if (err < 0) + core->power_state = SI476X_POWER_INCONSISTENT; + break; + default: + BUG(); + } + } + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_set_power_state); + +/** + * si476x_core_report_drainer_stop() - mark the completion of the RDS + * buffer drain porcess by the worker. + * + * @core: Core device structure + */ +static inline void si476x_core_report_drainer_stop(struct si476x_core *core) +{ + mutex_lock(&core->rds_drainer_status_lock); + core->rds_drainer_is_working = false; + mutex_unlock(&core->rds_drainer_status_lock); +} + +/** + * si476x_core_start_rds_drainer_once() - start RDS drainer worker if + * ther is none working, do nothing otherwise + * + * @core: Datastructure corresponding to the chip. + */ +static inline void si476x_core_start_rds_drainer_once(struct si476x_core *core) +{ + mutex_lock(&core->rds_drainer_status_lock); + if (!core->rds_drainer_is_working) { + core->rds_drainer_is_working = true; + schedule_work(&core->rds_fifo_drainer); + } + mutex_unlock(&core->rds_drainer_status_lock); +} +/** + * si476x_drain_rds_fifo() - RDS buffer drainer. + * @work: struct work_struct being ppassed to the function by the + * kernel. + * + * Drain the contents of the RDS FIFO of + */ +static void si476x_core_drain_rds_fifo(struct work_struct *work) +{ + int err; + + struct si476x_core *core = container_of(work, struct si476x_core, + rds_fifo_drainer); + + struct si476x_rds_status_report report; + + si476x_core_lock(core); + err = si476x_core_cmd_fm_rds_status(core, true, false, false, &report); + if (!err) { + int i = report.rdsfifoused; + dev_dbg(&core->client->dev, + "%d elements in RDS FIFO. Draining.\n", i); + for (; i > 0; --i) { + err = si476x_core_cmd_fm_rds_status(core, false, false, + (i == 1), &report); + if (err < 0) + goto unlock; + + kfifo_in(&core->rds_fifo, report.rds, + sizeof(report.rds)); + dev_dbg(&core->client->dev, "RDS data:\n %*ph\n", + sizeof(report.rds), report.rds); + } + dev_dbg(&core->client->dev, "Drrrrained!\n"); + wake_up_interruptible(&core->rds_read_queue); + } + +unlock: + si476x_core_unlock(core); + si476x_core_report_drainer_stop(core); +} + +/** + * si476x_core_pronounce_dead() + * + * @core: Core device structure + * + * Mark the device as being dead and wake up all potentially waiting + * threads of execution. + * + */ +static void si476x_core_pronounce_dead(struct si476x_core *core) +{ + dev_info(&core->client->dev, "Core device is dead.\n"); + + atomic_set(&core->is_alive, 0); + + /* Wake up al possible waiting processes */ + wake_up_interruptible(&core->rds_read_queue); + + atomic_set(&core->cts, 1); + wake_up(&core->command); + + atomic_set(&core->stc, 1); + wake_up(&core->tuning); +} + +/** + * si476x_core_i2c_xfer() + * + * @core: Core device structure + * @type: Transfer type + * @buf: Transfer buffer for/with data + * @count: Transfer buffer size + * + * Perfrom and I2C transfer(either read or write) and keep a counter + * of I/O errors. If the error counter rises above the threshold + * pronounce device dead. + * + * The function returns zero on succes or negative error code on + * failure. + */ +int si476x_core_i2c_xfer(struct si476x_core *core, + enum si476x_i2c_type type, + char *buf, int count) +{ + static int io_errors_count; + int err; + if (type == SI476X_I2C_SEND) + err = i2c_master_send(core->client, buf, count); + else + err = i2c_master_recv(core->client, buf, count); + + if (err < 0) { + if (io_errors_count++ > SI476X_MAX_IO_ERRORS) + si476x_core_pronounce_dead(core); + } else { + io_errors_count = 0; + } + + return err; +} +EXPORT_SYMBOL_GPL(si476x_core_i2c_xfer); + +/** + * si476x_get_status() + * @core: Core device structure + * + * Get the status byte of the core device by berforming one byte I2C + * read. + * + * The function returns a status value or a negative error code on + * error. + */ +static int si476x_core_get_status(struct si476x_core *core) +{ + u8 response; + int err = si476x_core_i2c_xfer(core, SI476X_I2C_RECV, + &response, sizeof(response)); + + return (err < 0) ? err : response; +} + +/** + * si476x_get_and_signal_status() - IRQ dispatcher + * @core: Core device structure + * + * Dispatch the arrived interrupt request based on the value of the + * status byte reported by the tuner. + * + */ +static void si476x_core_get_and_signal_status(struct si476x_core *core) +{ + int status = si476x_core_get_status(core); + if (status < 0) { + dev_err(&core->client->dev, "Failed to get status\n"); + return; + } + + if (status & SI476X_CTS) { + /* Unfortunately completions could not be used for + * signalling CTS since this flag cannot be cleared + * in status byte, and therefore once it becomes true + * multiple calls to 'complete' would cause the + * commands following the current one to be completed + * before they actually are */ + dev_dbg(&core->client->dev, "[interrupt] CTSINT\n"); + atomic_set(&core->cts, 1); + wake_up(&core->command); + } + + if (status & SI476X_FM_RDS_INT) { + dev_dbg(&core->client->dev, "[interrupt] RDSINT\n"); + si476x_core_start_rds_drainer_once(core); + } + + if (status & SI476X_STC_INT) { + dev_dbg(&core->client->dev, "[interrupt] STCINT\n"); + atomic_set(&core->stc, 1); + wake_up(&core->tuning); + } +} + +static void si476x_core_poll_loop(struct work_struct *work) +{ + struct si476x_core *core = SI476X_WORK_TO_CORE(work); + + si476x_core_get_and_signal_status(core); + + if (atomic_read(&core->is_alive)) + si476x_core_schedule_polling_work(core); +} + +static irqreturn_t si476x_core_interrupt(int irq, void *dev) +{ + struct si476x_core *core = dev; + + si476x_core_get_and_signal_status(core); + + return IRQ_HANDLED; +} + +/** + * si476x_firmware_version_to_revision() + * @core: Core device structure + * @major: Firmware major number + * @minor1: Firmware first minor number + * @minor2: Firmware second minor number + * + * Convert a chip's firmware version number into an offset that later + * will be used to as offset in "vtable" of tuner functions + * + * This function returns a positive offset in case of success and a -1 + * in case of failure. + */ +static int si476x_core_fwver_to_revision(struct si476x_core *core, + int func, int major, + int minor1, int minor2) +{ + switch (func) { + case SI476X_FUNC_FM_RECEIVER: + switch (major) { + case 5: + return SI476X_REVISION_A10; + case 8: + return SI476X_REVISION_A20; + case 10: + return SI476X_REVISION_A30; + default: + goto unknown_revision; + } + case SI476X_FUNC_AM_RECEIVER: + switch (major) { + case 5: + return SI476X_REVISION_A10; + case 7: + return SI476X_REVISION_A20; + case 9: + return SI476X_REVISION_A30; + default: + goto unknown_revision; + } + case SI476X_FUNC_WB_RECEIVER: + switch (major) { + case 3: + return SI476X_REVISION_A10; + case 5: + return SI476X_REVISION_A20; + case 7: + return SI476X_REVISION_A30; + default: + goto unknown_revision; + } + case SI476X_FUNC_BOOTLOADER: + default: /* FALLTHROUG */ + BUG(); + return -1; + } + +unknown_revision: + dev_err(&core->client->dev, + "Unsupported version of the firmware: %d.%d.%d, " + "reverting to A10 comptible functions\n", + major, minor1, minor2); + + return SI476X_REVISION_A10; +} + +/** + * si476x_get_revision_info() + * @core: Core device structure + * + * Get the firmware version number of the device. It is done in + * following three steps: + * 1. Power-up the device + * 2. Send the 'FUNC_INFO' command + * 3. Powering the device down. + * + * The function return zero on success and a negative error code on + * failure. + */ +static int si476x_core_get_revision_info(struct si476x_core *core) +{ + int rval; + struct si476x_func_info info; + + si476x_core_lock(core); + rval = si476x_core_set_power_state(core, SI476X_POWER_UP_FULL); + if (rval < 0) + goto exit; + + rval = si476x_core_cmd_func_info(core, &info); + if (rval < 0) + goto power_down; + + core->revision = si476x_core_fwver_to_revision(core, info.func, + info.firmware.major, + info.firmware.minor[0], + info.firmware.minor[1]); +power_down: + si476x_core_set_power_state(core, SI476X_POWER_DOWN); +exit: + si476x_core_unlock(core); + + return rval; +} + +bool si476x_core_has_am(struct si476x_core *core) +{ + return core->chip_id == SI476X_CHIP_SI4761 || + core->chip_id == SI476X_CHIP_SI4764; +} +EXPORT_SYMBOL_GPL(si476x_core_has_am); + +bool si476x_core_has_diversity(struct si476x_core *core) +{ + return core->chip_id == SI476X_CHIP_SI4764; +} +EXPORT_SYMBOL_GPL(si476x_core_has_diversity); + +bool si476x_core_is_a_secondary_tuner(struct si476x_core *core) +{ + return si476x_core_has_diversity(core) && + (core->diversity_mode == SI476X_PHDIV_SECONDARY_ANTENNA || + core->diversity_mode == SI476X_PHDIV_SECONDARY_COMBINING); +} +EXPORT_SYMBOL_GPL(si476x_core_is_a_secondary_tuner); + +bool si476x_core_is_a_primary_tuner(struct si476x_core *core) +{ + return si476x_core_has_diversity(core) && + (core->diversity_mode == SI476X_PHDIV_PRIMARY_ANTENNA || + core->diversity_mode == SI476X_PHDIV_PRIMARY_COMBINING); +} +EXPORT_SYMBOL_GPL(si476x_core_is_a_primary_tuner); + +bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core) +{ + return si476x_core_has_am(core) && + (core->power_up_parameters.func == SI476X_FUNC_AM_RECEIVER); +} +EXPORT_SYMBOL_GPL(si476x_core_is_in_am_receiver_mode); + +bool si476x_core_is_powered_up(struct si476x_core *core) +{ + return core->power_state == SI476X_POWER_UP_FULL; +} +EXPORT_SYMBOL_GPL(si476x_core_is_powered_up); + +static int si476x_core_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rval; + struct si476x_core *core; + struct si476x_platform_data *pdata; + struct mfd_cell *cell; + int cell_num; + + core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL); + if (!core) { + dev_err(&client->dev, + "failed to allocate 'struct si476x_core'\n"); + return -ENOMEM; + } + core->client = client; + + core->regmap = devm_regmap_init_si476x(core); + if (IS_ERR(core->regmap)) { + rval = PTR_ERR(core->regmap); + dev_err(&client->dev, + "Failed to allocate register map: %d\n", + rval); + return rval; + } + + i2c_set_clientdata(client, core); + + atomic_set(&core->is_alive, 0); + core->power_state = SI476X_POWER_DOWN; + + pdata = client->dev.platform_data; + if (pdata) { + memcpy(&core->power_up_parameters, + &pdata->power_up_parameters, + sizeof(core->power_up_parameters)); + + core->gpio_reset = -1; + if (gpio_is_valid(pdata->gpio_reset)) { + rval = gpio_request(pdata->gpio_reset, "si476x reset"); + if (rval) { + dev_err(&client->dev, + "Failed to request gpio: %d\n", rval); + return rval; + } + core->gpio_reset = pdata->gpio_reset; + gpio_direction_output(core->gpio_reset, 0); + } + + core->diversity_mode = pdata->diversity_mode; + memcpy(&core->pinmux, &pdata->pinmux, + sizeof(struct si476x_pinmux)); + } else { + dev_err(&client->dev, "No platform data provided\n"); + return -EINVAL; + } + + core->supplies[0].supply = "vd"; + core->supplies[1].supply = "va"; + core->supplies[2].supply = "vio1"; + core->supplies[3].supply = "vio2"; + + rval = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(core->supplies), + core->supplies); + if (rval) { + dev_err(&client->dev, "Failet to gett all of the regulators\n"); + goto free_gpio; + } + + mutex_init(&core->cmd_lock); + init_waitqueue_head(&core->command); + init_waitqueue_head(&core->tuning); + + rval = kfifo_alloc(&core->rds_fifo, + SI476X_DRIVER_RDS_FIFO_DEPTH * + sizeof(struct v4l2_rds_data), + GFP_KERNEL); + if (rval) { + dev_err(&client->dev, "Could not alloate the FIFO\n"); + goto free_gpio; + } + mutex_init(&core->rds_drainer_status_lock); + init_waitqueue_head(&core->rds_read_queue); + INIT_WORK(&core->rds_fifo_drainer, si476x_core_drain_rds_fifo); + + if (client->irq) { + rval = devm_request_threaded_irq(&client->dev, + client->irq, NULL, + si476x_core_interrupt, + IRQF_TRIGGER_FALLING, + client->name, core); + if (rval < 0) { + dev_err(&client->dev, "Could not request IRQ %d\n", + client->irq); + goto free_kfifo; + } + disable_irq(client->irq); + dev_dbg(&client->dev, "IRQ requested.\n"); + + core->rds_fifo_depth = 20; + } else { + INIT_DELAYED_WORK(&core->status_monitor, + si476x_core_poll_loop); + dev_info(&client->dev, + "No IRQ number specified, will use polling\n"); + + core->rds_fifo_depth = 5; + } + + core->chip_id = id->driver_data; + + rval = si476x_core_get_revision_info(core); + if (rval < 0) { + rval = -ENODEV; + goto free_kfifo; + } + + cell_num = 0; + + cell = &core->cells[SI476X_RADIO_CELL]; + cell->name = "si476x-radio"; + cell_num++; + +#ifdef CONFIG_SND_SOC_SI476X + if ((core->chip_id == SI476X_CHIP_SI4761 || + core->chip_id == SI476X_CHIP_SI4764) && + core->pinmux.dclk == SI476X_DCLK_DAUDIO && + core->pinmux.dfs == SI476X_DFS_DAUDIO && + core->pinmux.dout == SI476X_DOUT_I2S_OUTPUT && + core->pinmux.xout == SI476X_XOUT_TRISTATE) { + cell = &core->cells[SI476X_CODEC_CELL]; + cell->name = "si476x-codec"; + cell_num++; + } +#endif + rval = mfd_add_devices(&client->dev, + (client->adapter->nr << 8) + client->addr, + core->cells, cell_num, + NULL, 0, NULL); + if (!rval) + return 0; + +free_kfifo: + kfifo_free(&core->rds_fifo); + +free_gpio: + if (gpio_is_valid(core->gpio_reset)) + gpio_free(core->gpio_reset); + + return rval; +} + +static int si476x_core_remove(struct i2c_client *client) +{ + struct si476x_core *core = i2c_get_clientdata(client); + + si476x_core_pronounce_dead(core); + mfd_remove_devices(&client->dev); + + if (client->irq) + disable_irq(client->irq); + else + cancel_delayed_work_sync(&core->status_monitor); + + kfifo_free(&core->rds_fifo); + + if (gpio_is_valid(core->gpio_reset)) + gpio_free(core->gpio_reset); + + return 0; +} + + +static const struct i2c_device_id si476x_id[] = { + { "si4761", SI476X_CHIP_SI4761 }, + { "si4764", SI476X_CHIP_SI4764 }, + { "si4768", SI476X_CHIP_SI4768 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, si476x_id); + +static struct i2c_driver si476x_core_driver = { + .driver = { + .name = "si476x-core", + .owner = THIS_MODULE, + }, + .probe = si476x_core_probe, + .remove = si476x_core_remove, + .id_table = si476x_id, +}; +module_i2c_driver(si476x_core_driver); + + +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("Si4761/64/68 AM/FM MFD core device driver"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 379550698c1583db63ac62e62f465782f10418c2 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 18 Apr 2013 09:58:29 -0700 Subject: mfd: si476x: Add chip properties handling code This patch adds code related to manipulation of the properties of SI476X chips. Acked-by: Hans Verkuil Signed-off-by: Andrey Smirnov Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/si476x-prop.c b/drivers/mfd/si476x-prop.c new file mode 100644 index 0000000..cfeffa6 --- /dev/null +++ b/drivers/mfd/si476x-prop.c @@ -0,0 +1,241 @@ +/* + * drivers/mfd/si476x-prop.c -- Subroutines to access + * properties of si476x chips + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include + +#include + +struct si476x_property_range { + u16 low, high; +}; + +static bool si476x_core_element_is_in_array(u16 element, + const u16 array[], + size_t size) +{ + int i; + + for (i = 0; i < size; i++) + if (element == array[i]) + return true; + + return false; +} + +static bool si476x_core_element_is_in_range(u16 element, + const struct si476x_property_range range[], + size_t size) +{ + int i; + + for (i = 0; i < size; i++) + if (element <= range[i].high && element >= range[i].low) + return true; + + return false; +} + +static bool si476x_core_is_valid_property_a10(struct si476x_core *core, + u16 property) +{ + static const u16 valid_properties[] = { + 0x0000, + 0x0500, 0x0501, + 0x0600, + 0x0709, 0x070C, 0x070D, 0x70E, 0x710, + 0x0718, + 0x1207, 0x1208, + 0x2007, + 0x2300, + }; + + static const struct si476x_property_range valid_ranges[] = { + { 0x0200, 0x0203 }, + { 0x0300, 0x0303 }, + { 0x0400, 0x0404 }, + { 0x0700, 0x0707 }, + { 0x1100, 0x1102 }, + { 0x1200, 0x1204 }, + { 0x1300, 0x1306 }, + { 0x2000, 0x2005 }, + { 0x2100, 0x2104 }, + { 0x2106, 0x2106 }, + { 0x2200, 0x220E }, + { 0x3100, 0x3104 }, + { 0x3207, 0x320F }, + { 0x3300, 0x3304 }, + { 0x3500, 0x3517 }, + { 0x3600, 0x3617 }, + { 0x3700, 0x3717 }, + { 0x4000, 0x4003 }, + }; + + return si476x_core_element_is_in_range(property, valid_ranges, + ARRAY_SIZE(valid_ranges)) || + si476x_core_element_is_in_array(property, valid_properties, + ARRAY_SIZE(valid_properties)); +} + +static bool si476x_core_is_valid_property_a20(struct si476x_core *core, + u16 property) +{ + static const u16 valid_properties[] = { + 0x071B, + 0x1006, + 0x2210, + 0x3401, + }; + + static const struct si476x_property_range valid_ranges[] = { + { 0x2215, 0x2219 }, + }; + + return si476x_core_is_valid_property_a10(core, property) || + si476x_core_element_is_in_range(property, valid_ranges, + ARRAY_SIZE(valid_ranges)) || + si476x_core_element_is_in_array(property, valid_properties, + ARRAY_SIZE(valid_properties)); +} + +static bool si476x_core_is_valid_property_a30(struct si476x_core *core, + u16 property) +{ + static const u16 valid_properties[] = { + 0x071C, 0x071D, + 0x1007, 0x1008, + 0x220F, 0x2214, + 0x2301, + 0x3105, 0x3106, + 0x3402, + }; + + static const struct si476x_property_range valid_ranges[] = { + { 0x0405, 0x0411 }, + { 0x2008, 0x200B }, + { 0x2220, 0x2223 }, + { 0x3100, 0x3106 }, + }; + + return si476x_core_is_valid_property_a20(core, property) || + si476x_core_element_is_in_range(property, valid_ranges, + ARRAY_SIZE(valid_ranges)) || + si476x_core_element_is_in_array(property, valid_properties, + ARRAY_SIZE(valid_properties)); +} + +typedef bool (*valid_property_pred_t) (struct si476x_core *, u16); + +static bool si476x_core_is_valid_property(struct si476x_core *core, + u16 property) +{ + static const valid_property_pred_t is_valid_property[] = { + [SI476X_REVISION_A10] = si476x_core_is_valid_property_a10, + [SI476X_REVISION_A20] = si476x_core_is_valid_property_a20, + [SI476X_REVISION_A30] = si476x_core_is_valid_property_a30, + }; + + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + return is_valid_property[core->revision](core, property); +} + + +static bool si476x_core_is_readonly_property(struct si476x_core *core, + u16 property) +{ + BUG_ON(core->revision > SI476X_REVISION_A30 || + core->revision == -1); + + switch (core->revision) { + case SI476X_REVISION_A10: + return (property == 0x3200); + case SI476X_REVISION_A20: + return (property == 0x1006 || + property == 0x2210 || + property == 0x3200); + case SI476X_REVISION_A30: + return false; + } + + return false; +} + +static bool si476x_core_regmap_readable_register(struct device *dev, + unsigned int reg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct si476x_core *core = i2c_get_clientdata(client); + + return si476x_core_is_valid_property(core, (u16) reg); + +} + +static bool si476x_core_regmap_writable_register(struct device *dev, + unsigned int reg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct si476x_core *core = i2c_get_clientdata(client); + + return si476x_core_is_valid_property(core, (u16) reg) && + !si476x_core_is_readonly_property(core, (u16) reg); +} + + +static int si476x_core_regmap_write(void *context, unsigned int reg, + unsigned int val) +{ + return si476x_core_cmd_set_property(context, reg, val); +} + +static int si476x_core_regmap_read(void *context, unsigned int reg, + unsigned *val) +{ + struct si476x_core *core = context; + int err; + + err = si476x_core_cmd_get_property(core, reg); + if (err < 0) + return err; + + *val = err; + + return 0; +} + + +static const struct regmap_config si476x_regmap_config = { + .reg_bits = 16, + .val_bits = 16, + + .max_register = 0x4003, + + .writeable_reg = si476x_core_regmap_writable_register, + .readable_reg = si476x_core_regmap_readable_register, + + .reg_read = si476x_core_regmap_read, + .reg_write = si476x_core_regmap_write, + + .cache_type = REGCACHE_RBTREE, +}; + +struct regmap *devm_regmap_init_si476x(struct si476x_core *core) +{ + return devm_regmap_init(&core->client->dev, NULL, + core, &si476x_regmap_config); +} +EXPORT_SYMBOL_GPL(devm_regmap_init_si476x); -- cgit v0.10.2 From 9b8e1a5e4670b3b24c0e35c0884b4185f17fceab Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 18 Apr 2013 09:58:30 -0700 Subject: mfd: si476x: Add header files and Kbuild plumbing This patch adds all necessary header files and Kbuild plumbing for the core driver for Silicon Laboratories Si476x series of AM/FM tuner chips. The driver as a whole is implemented as an MFD device and this patch adds a core portion of it that provides all the necessary functionality to the two other drivers that represent radio and audio codec subsystems of the chip. Acked-by: Hans Verkuil Acked-by: Sam Ravnborg Signed-off-by: Andrey Smirnov Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 9de0502..7acd863 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -487,6 +487,19 @@ config MFD_SEC_CORE additional drivers must be enabled in order to use the functionality of the device +config MFD_SI476X_CORE + tristate "Silicon Laboratories 4761/64/68 AM/FM radio." + depends on I2C + select MFD_CORE + select REGMAP_I2C + help + This is the core driver for the SI476x series of AM/FM + radio. This MFD driver connects the radio-si476x V4L2 module + and the si476x audio codec. + + To compile this driver as a module, choose M here: the + module will be called si476x-core. + config MFD_SM501 tristate "Silicon Motion SM501" ---help--- diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 0676566..718e94a 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -134,6 +134,10 @@ obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o obj-$(CONFIG_MFD_VX855) += vx855.o obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o + +si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o +obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o + obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h new file mode 100644 index 0000000..ede3022 --- /dev/null +++ b/include/linux/mfd/si476x-core.h @@ -0,0 +1,533 @@ +/* + * include/media/si476x-core.h -- Common definitions for si476x core + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef SI476X_CORE_H +#define SI476X_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Command Timeouts */ +#define SI476X_DEFAULT_TIMEOUT 100000 +#define SI476X_TIMEOUT_TUNE 700000 +#define SI476X_TIMEOUT_POWER_UP 330000 +#define SI476X_STATUS_POLL_US 0 + +/* -------------------- si476x-i2c.c ----------------------- */ + +enum si476x_freq_supported_chips { + SI476X_CHIP_SI4761 = 1, + SI476X_CHIP_SI4764, + SI476X_CHIP_SI4768, +}; + +enum si476x_part_revisions { + SI476X_REVISION_A10 = 0, + SI476X_REVISION_A20 = 1, + SI476X_REVISION_A30 = 2, +}; + +enum si476x_mfd_cells { + SI476X_RADIO_CELL = 0, + SI476X_CODEC_CELL, + SI476X_MFD_CELLS, +}; + +/** + * enum si476x_power_state - possible power state of the si476x + * device. + * + * @SI476X_POWER_DOWN: In this state all regulators are turned off + * and the reset line is pulled low. The device is completely + * inactive. + * @SI476X_POWER_UP_FULL: In this state all the power regualtors are + * turned on, reset line pulled high, IRQ line is enabled(polling is + * active for polling use scenario) and device is turned on with + * POWER_UP command. The device is ready to be used. + * @SI476X_POWER_INCONSISTENT: This state indicates that previous + * power down was inconsistent, meaning some of the regulators were + * not turned down and thus use of the device, without power-cycling + * is impossible. + */ +enum si476x_power_state { + SI476X_POWER_DOWN = 0, + SI476X_POWER_UP_FULL = 1, + SI476X_POWER_INCONSISTENT = 2, +}; + +/** + * struct si476x_core - internal data structure representing the + * underlying "core" device which all the MFD cell-devices use. + * + * @client: Actual I2C client used to transfer commands to the chip. + * @chip_id: Last digit of the chip model(E.g. "1" for SI4761) + * @cells: MFD cell devices created by this driver. + * @cmd_lock: Mutex used to serialize all the requests to the core + * device. This filed should not be used directly. Instead + * si476x_core_lock()/si476x_core_unlock() should be used to get + * exclusive access to the "core" device. + * @users: Active users counter(Used by the radio cell) + * @rds_read_queue: Wait queue used to wait for RDS data. + * @rds_fifo: FIFO in which all the RDS data received from the chip is + * placed. + * @rds_fifo_drainer: Worker that drains on-chip RDS FIFO. + * @rds_drainer_is_working: Flag used for launching only one instance + * of the @rds_fifo_drainer. + * @rds_drainer_status_lock: Lock used to guard access to the + * @rds_drainer_is_working variable. + * @command: Wait queue for wainting on the command comapletion. + * @cts: Clear To Send flag set upon receiving first status with CTS + * set. + * @tuning: Wait queue used for wainting for tune/seek comand + * completion. + * @stc: Similar to @cts, but for the STC bit of the status value. + * @power_up_parameters: Parameters used as argument for POWER_UP + * command when the device is started. + * @state: Current power state of the device. + * @supplues: Structure containing handles to all power supplies used + * by the device (NULL ones are ignored). + * @gpio_reset: GPIO pin connectet to the RSTB pin of the chip. + * @pinmux: Chip's configurable pins configuration. + * @diversity_mode: Chips role when functioning in diversity mode. + * @status_monitor: Polling worker used in polling use case scenarion + * (when IRQ is not avalible). + * @revision: Chip's running firmware revision number(Used for correct + * command set support). + */ + +struct si476x_core { + struct i2c_client *client; + struct regmap *regmap; + int chip_id; + struct mfd_cell cells[SI476X_MFD_CELLS]; + + struct mutex cmd_lock; /* for serializing fm radio operations */ + atomic_t users; + + wait_queue_head_t rds_read_queue; + struct kfifo rds_fifo; + struct work_struct rds_fifo_drainer; + bool rds_drainer_is_working; + struct mutex rds_drainer_status_lock; + + wait_queue_head_t command; + atomic_t cts; + + wait_queue_head_t tuning; + atomic_t stc; + + struct si476x_power_up_args power_up_parameters; + + enum si476x_power_state power_state; + + struct regulator_bulk_data supplies[4]; + + int gpio_reset; + + struct si476x_pinmux pinmux; + enum si476x_phase_diversity_mode diversity_mode; + + atomic_t is_alive; + + struct delayed_work status_monitor; +#define SI476X_WORK_TO_CORE(w) container_of(to_delayed_work(w), \ + struct si476x_core, \ + status_monitor) + + int revision; + + int rds_fifo_depth; +}; + +static inline struct si476x_core *i2c_mfd_cell_to_core(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + return i2c_get_clientdata(client); +} + + +/** + * si476x_core_lock() - lock the core device to get an exclusive access + * to it. + */ +static inline void si476x_core_lock(struct si476x_core *core) +{ + mutex_lock(&core->cmd_lock); +} + +/** + * si476x_core_unlock() - unlock the core device to relinquish an + * exclusive access to it. + */ +static inline void si476x_core_unlock(struct si476x_core *core) +{ + mutex_unlock(&core->cmd_lock); +} + +/* *_TUNE_FREQ family of commands accept frequency in multiples of + 10kHz */ +static inline u16 hz_to_si476x(struct si476x_core *core, int freq) +{ + u16 result; + + switch (core->power_up_parameters.func) { + default: + case SI476X_FUNC_FM_RECEIVER: + result = freq / 10000; + break; + case SI476X_FUNC_AM_RECEIVER: + result = freq / 1000; + break; + } + + return result; +} + +static inline int si476x_to_hz(struct si476x_core *core, u16 freq) +{ + int result; + + switch (core->power_up_parameters.func) { + default: + case SI476X_FUNC_FM_RECEIVER: + result = freq * 10000; + break; + case SI476X_FUNC_AM_RECEIVER: + result = freq * 1000; + break; + } + + return result; +} + +/* Since the V4L2_TUNER_CAP_LOW flag is supplied, V4L2 subsystem + * mesures frequency in 62.5 Hz units */ + +static inline int hz_to_v4l2(int freq) +{ + return (freq * 10) / 625; +} + +static inline int v4l2_to_hz(int freq) +{ + return (freq * 625) / 10; +} + +static inline u16 v4l2_to_si476x(struct si476x_core *core, int freq) +{ + return hz_to_si476x(core, v4l2_to_hz(freq)); +} + +static inline int si476x_to_v4l2(struct si476x_core *core, u16 freq) +{ + return hz_to_v4l2(si476x_to_hz(core, freq)); +} + + + +/** + * struct si476x_func_info - structure containing result of the + * FUNC_INFO command. + * + * @firmware.major: Firmware major number. + * @firmware.minor[...]: Firmware minor numbers. + * @patch_id: + * @func: Mode tuner is working in. + */ +struct si476x_func_info { + struct { + u8 major, minor[2]; + } firmware; + u16 patch_id; + enum si476x_func func; +}; + +/** + * struct si476x_power_down_args - structure used to pass parameters + * to POWER_DOWN command + * + * @xosc: true - Power down, but leav oscillator running. + * false - Full power down. + */ +struct si476x_power_down_args { + bool xosc; +}; + +/** + * enum si476x_tunemode - enum representing possible tune modes for + * the chip. + * @SI476X_TM_VALIDATED_NORMAL_TUNE: Unconditionally stay on the new + * channel after tune, tune status is valid. + * @SI476X_TM_INVALIDATED_FAST_TUNE: Unconditionally stay in the new + * channel after tune, tune status invalid. + * @SI476X_TM_VALIDATED_AF_TUNE: Jump back to previous channel if + * metric thresholds are not met. + * @SI476X_TM_VALIDATED_AF_CHECK: Unconditionally jump back to the + * previous channel. + */ +enum si476x_tunemode { + SI476X_TM_VALIDATED_NORMAL_TUNE = 0, + SI476X_TM_INVALIDATED_FAST_TUNE = 1, + SI476X_TM_VALIDATED_AF_TUNE = 2, + SI476X_TM_VALIDATED_AF_CHECK = 3, +}; + +/** + * enum si476x_smoothmetrics - enum containing the possible setting fo + * audio transitioning of the chip + * @SI476X_SM_INITIALIZE_AUDIO: Initialize audio state to match this + * new channel + * @SI476X_SM_TRANSITION_AUDIO: Transition audio state from previous + * channel values to the new values + */ +enum si476x_smoothmetrics { + SI476X_SM_INITIALIZE_AUDIO = 0, + SI476X_SM_TRANSITION_AUDIO = 1, +}; + +/** + * struct si476x_rds_status_report - the structure representing the + * response to 'FM_RD_STATUS' command + * @rdstpptyint: Traffic program flag(TP) and/or program type(PTY) + * code has changed. + * @rdspiint: Program indentifiaction(PI) code has changed. + * @rdssyncint: RDS synchronization has changed. + * @rdsfifoint: RDS was received and the RDS FIFO has at least + * 'FM_RDS_INTERRUPT_FIFO_COUNT' elements in it. + * @tpptyvalid: TP flag and PTY code are valid falg. + * @pivalid: PI code is valid flag. + * @rdssync: RDS is currently synchronized. + * @rdsfifolost: On or more RDS groups have been lost/discarded flag. + * @tp: Current channel's TP flag. + * @pty: Current channel's PTY code. + * @pi: Current channel's PI code. + * @rdsfifoused: Number of blocks remaining in the RDS FIFO (0 if + * empty). + */ +struct si476x_rds_status_report { + bool rdstpptyint, rdspiint, rdssyncint, rdsfifoint; + bool tpptyvalid, pivalid, rdssync, rdsfifolost; + bool tp; + + u8 pty; + u16 pi; + + u8 rdsfifoused; + u8 ble[4]; + + struct v4l2_rds_data rds[4]; +}; + +struct si476x_rsq_status_args { + bool primary; + bool rsqack; + bool attune; + bool cancel; + bool stcack; +}; + +enum si476x_injside { + SI476X_INJSIDE_AUTO = 0, + SI476X_INJSIDE_LOW = 1, + SI476X_INJSIDE_HIGH = 2, +}; + +struct si476x_tune_freq_args { + bool zifsr; + bool hd; + enum si476x_injside injside; + int freq; + enum si476x_tunemode tunemode; + enum si476x_smoothmetrics smoothmetrics; + int antcap; +}; + +int si476x_core_stop(struct si476x_core *, bool); +int si476x_core_start(struct si476x_core *, bool); +int si476x_core_set_power_state(struct si476x_core *, enum si476x_power_state); +bool si476x_core_has_am(struct si476x_core *); +bool si476x_core_has_diversity(struct si476x_core *); +bool si476x_core_is_a_secondary_tuner(struct si476x_core *); +bool si476x_core_is_a_primary_tuner(struct si476x_core *); +bool si476x_core_is_in_am_receiver_mode(struct si476x_core *core); +bool si476x_core_is_powered_up(struct si476x_core *core); + +enum si476x_i2c_type { + SI476X_I2C_SEND, + SI476X_I2C_RECV +}; + +int si476x_core_i2c_xfer(struct si476x_core *, + enum si476x_i2c_type, + char *, int); + + +/* -------------------- si476x-cmd.c ----------------------- */ + +int si476x_core_cmd_func_info(struct si476x_core *, struct si476x_func_info *); +int si476x_core_cmd_set_property(struct si476x_core *, u16, u16); +int si476x_core_cmd_get_property(struct si476x_core *, u16); +int si476x_core_cmd_dig_audio_pin_cfg(struct si476x_core *, + enum si476x_dclk_config, + enum si476x_dfs_config, + enum si476x_dout_config, + enum si476x_xout_config); +int si476x_core_cmd_zif_pin_cfg(struct si476x_core *, + enum si476x_iqclk_config, + enum si476x_iqfs_config, + enum si476x_iout_config, + enum si476x_qout_config); +int si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(struct si476x_core *, + enum si476x_icin_config, + enum si476x_icip_config, + enum si476x_icon_config, + enum si476x_icop_config); +int si476x_core_cmd_ana_audio_pin_cfg(struct si476x_core *, + enum si476x_lrout_config); +int si476x_core_cmd_intb_pin_cfg(struct si476x_core *, enum si476x_intb_config, + enum si476x_a1_config); +int si476x_core_cmd_fm_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_am_seek_start(struct si476x_core *, bool, bool); +int si476x_core_cmd_fm_rds_status(struct si476x_core *, bool, bool, bool, + struct si476x_rds_status_report *); +int si476x_core_cmd_fm_rds_blockcount(struct si476x_core *, bool, + struct si476x_rds_blockcount_report *); +int si476x_core_cmd_fm_tune_freq(struct si476x_core *, + struct si476x_tune_freq_args *); +int si476x_core_cmd_am_tune_freq(struct si476x_core *, + struct si476x_tune_freq_args *); +int si476x_core_cmd_am_rsq_status(struct si476x_core *, + struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); +int si476x_core_cmd_fm_rsq_status(struct si476x_core *, + struct si476x_rsq_status_args *, + struct si476x_rsq_status_report *); +int si476x_core_cmd_power_up(struct si476x_core *, + struct si476x_power_up_args *); +int si476x_core_cmd_power_down(struct si476x_core *, + struct si476x_power_down_args *); +int si476x_core_cmd_fm_phase_div_status(struct si476x_core *); +int si476x_core_cmd_fm_phase_diversity(struct si476x_core *, + enum si476x_phase_diversity_mode); + +int si476x_core_cmd_fm_acf_status(struct si476x_core *, + struct si476x_acf_status_report *); +int si476x_core_cmd_am_acf_status(struct si476x_core *, + struct si476x_acf_status_report *); +int si476x_core_cmd_agc_status(struct si476x_core *, + struct si476x_agc_status_report *); + +enum si476x_power_grid_type { + SI476X_POWER_GRID_50HZ = 0, + SI476X_POWER_GRID_60HZ, +}; + +/* Properties */ + +enum si476x_interrupt_flags { + SI476X_STCIEN = (1 << 0), + SI476X_ACFIEN = (1 << 1), + SI476X_RDSIEN = (1 << 2), + SI476X_RSQIEN = (1 << 3), + + SI476X_ERRIEN = (1 << 6), + SI476X_CTSIEN = (1 << 7), + + SI476X_STCREP = (1 << 8), + SI476X_ACFREP = (1 << 9), + SI476X_RDSREP = (1 << 10), + SI476X_RSQREP = (1 << 11), +}; + +enum si476x_rdsint_sources { + SI476X_RDSTPPTY = (1 << 4), + SI476X_RDSPI = (1 << 3), + SI476X_RDSSYNC = (1 << 1), + SI476X_RDSRECV = (1 << 0), +}; + +enum si476x_status_response_bits { + SI476X_CTS = (1 << 7), + SI476X_ERR = (1 << 6), + /* Status response for WB receiver */ + SI476X_WB_ASQ_INT = (1 << 4), + SI476X_RSQ_INT = (1 << 3), + /* Status response for FM receiver */ + SI476X_FM_RDS_INT = (1 << 2), + SI476X_ACF_INT = (1 << 1), + SI476X_STC_INT = (1 << 0), +}; + +/* -------------------- si476x-prop.c ----------------------- */ + +enum si476x_common_receiver_properties { + SI476X_PROP_INT_CTL_ENABLE = 0x0000, + SI476X_PROP_DIGITAL_IO_INPUT_SAMPLE_RATE = 0x0200, + SI476X_PROP_DIGITAL_IO_INPUT_FORMAT = 0x0201, + SI476X_PROP_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202, + SI476X_PROP_DIGITAL_IO_OUTPUT_FORMAT = 0x0203, + + SI476X_PROP_SEEK_BAND_BOTTOM = 0x1100, + SI476X_PROP_SEEK_BAND_TOP = 0x1101, + SI476X_PROP_SEEK_FREQUENCY_SPACING = 0x1102, + + SI476X_PROP_VALID_MAX_TUNE_ERROR = 0x2000, + SI476X_PROP_VALID_SNR_THRESHOLD = 0x2003, + SI476X_PROP_VALID_RSSI_THRESHOLD = 0x2004, +}; + +enum si476x_am_receiver_properties { + SI476X_PROP_AUDIO_PWR_LINE_FILTER = 0x0303, +}; + +enum si476x_fm_receiver_properties { + SI476X_PROP_AUDIO_DEEMPHASIS = 0x0302, + + SI476X_PROP_FM_RDS_INTERRUPT_SOURCE = 0x4000, + SI476X_PROP_FM_RDS_INTERRUPT_FIFO_COUNT = 0x4001, + SI476X_PROP_FM_RDS_CONFIG = 0x4002, +}; + +enum si476x_prop_audio_pwr_line_filter_bits { + SI476X_PROP_PWR_HARMONICS_MASK = 0b0000000000011111, + SI476X_PROP_PWR_GRID_MASK = 0b0000000100000000, + SI476X_PROP_PWR_ENABLE_MASK = 0b0000001000000000, + SI476X_PROP_PWR_GRID_50HZ = 0b0000000000000000, + SI476X_PROP_PWR_GRID_60HZ = 0b0000000100000000, +}; + +enum si476x_prop_fm_rds_config_bits { + SI476X_PROP_RDSEN_MASK = 0x1, + SI476X_PROP_RDSEN = 0x1, +}; + + +struct regmap *devm_regmap_init_si476x(struct si476x_core *); + +#endif /* SI476X_CORE_H */ diff --git a/include/linux/mfd/si476x-platform.h b/include/linux/mfd/si476x-platform.h new file mode 100644 index 0000000..88bb93b --- /dev/null +++ b/include/linux/mfd/si476x-platform.h @@ -0,0 +1,267 @@ +/* + * include/media/si476x-platform.h -- Platform data specific definitions + * + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef __SI476X_PLATFORM_H__ +#define __SI476X_PLATFORM_H__ + +/* It is possible to select one of the four adresses using pins A0 + * and A1 on SI476x */ +#define SI476X_I2C_ADDR_1 0x60 +#define SI476X_I2C_ADDR_2 0x61 +#define SI476X_I2C_ADDR_3 0x62 +#define SI476X_I2C_ADDR_4 0x63 + +enum si476x_iqclk_config { + SI476X_IQCLK_NOOP = 0, + SI476X_IQCLK_TRISTATE = 1, + SI476X_IQCLK_IQ = 21, +}; +enum si476x_iqfs_config { + SI476X_IQFS_NOOP = 0, + SI476X_IQFS_TRISTATE = 1, + SI476X_IQFS_IQ = 21, +}; +enum si476x_iout_config { + SI476X_IOUT_NOOP = 0, + SI476X_IOUT_TRISTATE = 1, + SI476X_IOUT_OUTPUT = 22, +}; +enum si476x_qout_config { + SI476X_QOUT_NOOP = 0, + SI476X_QOUT_TRISTATE = 1, + SI476X_QOUT_OUTPUT = 22, +}; + +enum si476x_dclk_config { + SI476X_DCLK_NOOP = 0, + SI476X_DCLK_TRISTATE = 1, + SI476X_DCLK_DAUDIO = 10, +}; + +enum si476x_dfs_config { + SI476X_DFS_NOOP = 0, + SI476X_DFS_TRISTATE = 1, + SI476X_DFS_DAUDIO = 10, +}; + +enum si476x_dout_config { + SI476X_DOUT_NOOP = 0, + SI476X_DOUT_TRISTATE = 1, + SI476X_DOUT_I2S_OUTPUT = 12, + SI476X_DOUT_I2S_INPUT = 13, +}; + +enum si476x_xout_config { + SI476X_XOUT_NOOP = 0, + SI476X_XOUT_TRISTATE = 1, + SI476X_XOUT_I2S_INPUT = 13, + SI476X_XOUT_MODE_SELECT = 23, +}; + +enum si476x_icin_config { + SI476X_ICIN_NOOP = 0, + SI476X_ICIN_TRISTATE = 1, + SI476X_ICIN_GPO1_HIGH = 2, + SI476X_ICIN_GPO1_LOW = 3, + SI476X_ICIN_IC_LINK = 30, +}; + +enum si476x_icip_config { + SI476X_ICIP_NOOP = 0, + SI476X_ICIP_TRISTATE = 1, + SI476X_ICIP_GPO2_HIGH = 2, + SI476X_ICIP_GPO2_LOW = 3, + SI476X_ICIP_IC_LINK = 30, +}; + +enum si476x_icon_config { + SI476X_ICON_NOOP = 0, + SI476X_ICON_TRISTATE = 1, + SI476X_ICON_I2S = 10, + SI476X_ICON_IC_LINK = 30, +}; + +enum si476x_icop_config { + SI476X_ICOP_NOOP = 0, + SI476X_ICOP_TRISTATE = 1, + SI476X_ICOP_I2S = 10, + SI476X_ICOP_IC_LINK = 30, +}; + + +enum si476x_lrout_config { + SI476X_LROUT_NOOP = 0, + SI476X_LROUT_TRISTATE = 1, + SI476X_LROUT_AUDIO = 2, + SI476X_LROUT_MPX = 3, +}; + + +enum si476x_intb_config { + SI476X_INTB_NOOP = 0, + SI476X_INTB_TRISTATE = 1, + SI476X_INTB_DAUDIO = 10, + SI476X_INTB_IRQ = 40, +}; + +enum si476x_a1_config { + SI476X_A1_NOOP = 0, + SI476X_A1_TRISTATE = 1, + SI476X_A1_IRQ = 40, +}; + + +struct si476x_pinmux { + enum si476x_dclk_config dclk; + enum si476x_dfs_config dfs; + enum si476x_dout_config dout; + enum si476x_xout_config xout; + + enum si476x_iqclk_config iqclk; + enum si476x_iqfs_config iqfs; + enum si476x_iout_config iout; + enum si476x_qout_config qout; + + enum si476x_icin_config icin; + enum si476x_icip_config icip; + enum si476x_icon_config icon; + enum si476x_icop_config icop; + + enum si476x_lrout_config lrout; + + enum si476x_intb_config intb; + enum si476x_a1_config a1; +}; + +enum si476x_ibias6x { + SI476X_IBIAS6X_OTHER = 0, + SI476X_IBIAS6X_RCVR1_NON_4MHZ_CLK = 1, +}; + +enum si476x_xstart { + SI476X_XSTART_MULTIPLE_TUNER = 0x11, + SI476X_XSTART_NORMAL = 0x77, +}; + +enum si476x_freq { + SI476X_FREQ_4_MHZ = 0, + SI476X_FREQ_37P209375_MHZ = 1, + SI476X_FREQ_36P4_MHZ = 2, + SI476X_FREQ_37P8_MHZ = 3, +}; + +enum si476x_xmode { + SI476X_XMODE_CRYSTAL_RCVR1 = 1, + SI476X_XMODE_EXT_CLOCK = 2, + SI476X_XMODE_CRYSTAL_RCVR2_3 = 3, +}; + +enum si476x_xbiashc { + SI476X_XBIASHC_SINGLE_RECEIVER = 0, + SI476X_XBIASHC_MULTIPLE_RECEIVER = 1, +}; + +enum si476x_xbias { + SI476X_XBIAS_RCVR2_3 = 0, + SI476X_XBIAS_4MHZ_RCVR1 = 3, + SI476X_XBIAS_RCVR1 = 7, +}; + +enum si476x_func { + SI476X_FUNC_BOOTLOADER = 0, + SI476X_FUNC_FM_RECEIVER = 1, + SI476X_FUNC_AM_RECEIVER = 2, + SI476X_FUNC_WB_RECEIVER = 3, +}; + + +/** + * @xcload: Selects the amount of additional on-chip capacitance to + * be connected between XTAL1 and gnd and between XTAL2 and + * GND. One half of the capacitance value shown here is the + * additional load capacitance presented to the xtal. The + * minimum step size is 0.277 pF. Recommended value is 0x28 + * but it will be layout dependent. Range is 0–0x3F i.e. + * (0–16.33 pF) + * @ctsien: enable CTSINT(interrupt request when CTS condition + * arises) when set + * @intsel: when set A1 pin becomes the interrupt pin; otherwise, + * INTB is the interrupt pin + * @func: selects the boot function of the device. I.e. + * SI476X_BOOTLOADER - Boot loader + * SI476X_FM_RECEIVER - FM receiver + * SI476X_AM_RECEIVER - AM receiver + * SI476X_WB_RECEIVER - Weatherband receiver + * @freq: oscillator's crystal frequency: + * SI476X_XTAL_37P209375_MHZ - 37.209375 Mhz + * SI476X_XTAL_36P4_MHZ - 36.4 Mhz + * SI476X_XTAL_37P8_MHZ - 37.8 Mhz + */ +struct si476x_power_up_args { + enum si476x_ibias6x ibias6x; + enum si476x_xstart xstart; + u8 xcload; + bool fastboot; + enum si476x_xbiashc xbiashc; + enum si476x_xbias xbias; + enum si476x_func func; + enum si476x_freq freq; + enum si476x_xmode xmode; +}; + + +/** + * enum si476x_phase_diversity_mode - possbile phase diversity modes + * for SI4764/5/6/7 chips. + * + * @SI476X_PHDIV_DISABLED: Phase diversity feature is + * disabled. + * @SI476X_PHDIV_PRIMARY_COMBINING: Tuner works as a primary tuner + * in combination with a + * secondary one. + * @SI476X_PHDIV_PRIMARY_ANTENNA: Tuner works as a primary tuner + * using only its own antenna. + * @SI476X_PHDIV_SECONDARY_ANTENNA: Tuner works as a primary tuner + * usning seconary tuner's antenna. + * @SI476X_PHDIV_SECONDARY_COMBINING: Tuner works as a secondary + * tuner in combination with the + * primary one. + */ +enum si476x_phase_diversity_mode { + SI476X_PHDIV_DISABLED = 0, + SI476X_PHDIV_PRIMARY_COMBINING = 1, + SI476X_PHDIV_PRIMARY_ANTENNA = 2, + SI476X_PHDIV_SECONDARY_ANTENNA = 3, + SI476X_PHDIV_SECONDARY_COMBINING = 5, +}; + + +/* + * Platform dependent definition + */ +struct si476x_platform_data { + int gpio_reset; /* < 0 if not used */ + + struct si476x_power_up_args power_up_parameters; + enum si476x_phase_diversity_mode diversity_mode; + + struct si476x_pinmux pinmux; +}; + + +#endif /* __SI476X_PLATFORM_H__ */ diff --git a/include/linux/mfd/si476x-reports.h b/include/linux/mfd/si476x-reports.h new file mode 100644 index 0000000..e0b9455 --- /dev/null +++ b/include/linux/mfd/si476x-reports.h @@ -0,0 +1,163 @@ +/* + * include/media/si476x-platform.h -- Definitions of the data formats + * returned by debugfs hooks + * + * Copyright (C) 2013 Andrey Smirnov + * + * Author: Andrey Smirnov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef __SI476X_REPORTS_H__ +#define __SI476X_REPORTS_H__ + +/** + * struct si476x_rsq_status - structure containing received signal + * quality + * @multhint: Multipath Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_HIGH_THRESHOLD + * @multlint: Multipath Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_MULTIPATH_LOW_THRESHOLD + * @snrhint: SNR Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_HIGH_THRESHOLD + * @snrlint: SNR Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_SNR_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_SNR_LOW_THRESHOLD + * @rssihint: RSSI Detect High. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_HIGH_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_HIGH_THRESHOLD + * @rssilint: RSSI Detect Low. + * true - Indicatedes that the value is below + * FM_RSQ_RSSI_LOW_THRESHOLD + * false - Indicatedes that the value is above + * FM_RSQ_RSSI_LOW_THRESHOLD + * @bltf: Band Limit. + * Set if seek command hits the band limit or wrapped to + * the original frequency. + * @snr_ready: SNR measurement in progress. + * @rssiready: RSSI measurement in progress. + * @afcrl: Set if FREQOFF >= MAX_TUNE_ERROR + * @valid: Set if the channel is valid + * rssi < FM_VALID_RSSI_THRESHOLD + * snr < FM_VALID_SNR_THRESHOLD + * tune_error < FM_VALID_MAX_TUNE_ERROR + * @readfreq: Current tuned frequency. + * @freqoff: Signed frequency offset. + * @rssi: Received Signal Strength Indicator(dBuV). + * @snr: RF SNR Indicator(dB). + * @lassi: + * @hassi: Low/High side Adjacent(100 kHz) Channel Strength Indicator + * @mult: Multipath indicator + * @dev: Who knows? But values may vary. + * @readantcap: Antenna tuning capacity value. + * @assi: Adjacent Channel(+/- 200kHz) Strength Indicator + * @usn: Ultrasonic Noise Inticator in -DBFS + */ +struct si476x_rsq_status_report { + __u8 multhint, multlint; + __u8 snrhint, snrlint; + __u8 rssihint, rssilint; + __u8 bltf; + __u8 snr_ready; + __u8 rssiready; + __u8 injside; + __u8 afcrl; + __u8 valid; + + __u16 readfreq; + __s8 freqoff; + __s8 rssi; + __s8 snr; + __s8 issi; + __s8 lassi, hassi; + __s8 mult; + __u8 dev; + __u16 readantcap; + __s8 assi; + __s8 usn; + + __u8 pilotdev; + __u8 rdsdev; + __u8 assidev; + __u8 strongdev; + __u16 rdspi; +} __packed; + +/** + * si476x_acf_status_report - ACF report results + * + * @blend_int: If set, indicates that stereo separation has crossed + * below the blend threshold as set by FM_ACF_BLEND_THRESHOLD + * @hblend_int: If set, indicates that HiBlend cutoff frequency is + * lower than threshold as set by FM_ACF_HBLEND_THRESHOLD + * @hicut_int: If set, indicates that HiCut cutoff frequency is lower + * than the threshold set by ACF_ + + */ +struct si476x_acf_status_report { + __u8 blend_int; + __u8 hblend_int; + __u8 hicut_int; + __u8 chbw_int; + __u8 softmute_int; + __u8 smute; + __u8 smattn; + __u8 chbw; + __u8 hicut; + __u8 hiblend; + __u8 pilot; + __u8 stblend; +} __packed; + +enum si476x_fmagc { + SI476X_FMAGC_10K_OHM = 0, + SI476X_FMAGC_800_OHM = 1, + SI476X_FMAGC_400_OHM = 2, + SI476X_FMAGC_200_OHM = 4, + SI476X_FMAGC_100_OHM = 8, + SI476X_FMAGC_50_OHM = 16, + SI476X_FMAGC_25_OHM = 32, + SI476X_FMAGC_12P5_OHM = 64, + SI476X_FMAGC_6P25_OHM = 128, +}; + +struct si476x_agc_status_report { + __u8 mxhi; + __u8 mxlo; + __u8 lnahi; + __u8 lnalo; + __u8 fmagc1; + __u8 fmagc2; + __u8 pgagain; + __u8 fmwblang; +} __packed; + +struct si476x_rds_blockcount_report { + __u16 expected; + __u16 received; + __u16 uncorrectable; +} __packed; + +#endif /* __SI476X_REPORTS_H__ */ -- cgit v0.10.2 From 4adedc57e0fd2a6efa6a7d84405b8d5c2a956c3d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 19 Apr 2013 18:40:05 +0200 Subject: mfd: si476x: Fix i2c warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/mfd/si476x-i2c.c: In function ‘si476x_core_drain_rds_fifo’: drivers/mfd/si476x-i2c.c:391:4: warning: field width specifier ‘*’ expects +argument of type ‘int’, but argument 4 has type ‘long unsigned int’ [-Wformat] Acked-by: Andrey Smirnov Signed-off-by: Hans Verkuil Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c index 118c6b1..f5bc8e4 100644 --- a/drivers/mfd/si476x-i2c.c +++ b/drivers/mfd/si476x-i2c.c @@ -389,7 +389,7 @@ static void si476x_core_drain_rds_fifo(struct work_struct *work) kfifo_in(&core->rds_fifo, report.rds, sizeof(report.rds)); dev_dbg(&core->client->dev, "RDS data:\n %*ph\n", - sizeof(report.rds), report.rds); + (int)sizeof(report.rds), report.rds); } dev_dbg(&core->client->dev, "Drrrrained!\n"); wake_up_interruptible(&core->rds_read_queue); -- cgit v0.10.2 From 8bf874a474f17e2d0fca13456e773a11fa4715ca Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 23 Apr 2013 18:30:52 +0200 Subject: mfd: ab8500: Export ab8500_gpadc_sw_hw_convert properly Apparently the ab8500_gpadc_sw_hw_convert function got renamed from ab8500_gpadc_convert to ab8500_gpadc_sw_hw_convert in commit 734823462 "mfd: ab8500-gpadc: Add gpadc hw conversion", but the export for this function did not get changed at the same time, causing this allyesconfig error: ERROR: "ab8500_gpadc_sw_hw_convert" [drivers/hwmon/ab8500.ko] undefined! This patch fixes the export. Signed-off-by: Arnd Bergmann Cc: M'boumba Cedric Madianga Cc: Lee Jones Cc: Mattias WALLIN Cc: Michel JAOUEN Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 65f7228..5e65b28 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -332,7 +332,7 @@ if (ad_value < 0) { return voltage; } -EXPORT_SYMBOL(ab8500_gpadc_convert); +EXPORT_SYMBOL(ab8500_gpadc_sw_hw_convert); /** * ab8500_gpadc_read_raw() - gpadc read -- cgit v0.10.2 From 367764a4d1c8dfbf3e24bb0ffbc5eb8c5d5bf6eb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Thu, 25 Apr 2013 18:14:51 +0100 Subject: mfd: vexpress: Handle pending config transactions The config transactions "scheduler" was hopelessly broken, repeating completed transaction instead of picking up next pending one. Fixed now. Also improved debug messages. Signed-off-by: Pawel Moll Reviewed-by: Jon Medhurst Signed-off-by: Samuel Ortiz diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c index 3c1723aa..84ce6b9 100644 --- a/drivers/mfd/vexpress-config.c +++ b/drivers/mfd/vexpress-config.c @@ -184,13 +184,14 @@ static int vexpress_config_schedule(struct vexpress_config_trans *trans) spin_lock_irqsave(&bridge->transactions_lock, flags); - vexpress_config_dump_trans("Executing", trans); - - if (list_empty(&bridge->transactions)) + if (list_empty(&bridge->transactions)) { + vexpress_config_dump_trans("Executing", trans); status = bridge->info->func_exec(trans->func->func, trans->offset, trans->write, trans->data); - else + } else { + vexpress_config_dump_trans("Queuing", trans); status = VEXPRESS_CONFIG_STATUS_WAIT; + } switch (status) { case VEXPRESS_CONFIG_STATUS_DONE: @@ -212,25 +213,31 @@ void vexpress_config_complete(struct vexpress_config_bridge *bridge, { struct vexpress_config_trans *trans; unsigned long flags; + const char *message = "Completed"; spin_lock_irqsave(&bridge->transactions_lock, flags); trans = list_first_entry(&bridge->transactions, struct vexpress_config_trans, list); - vexpress_config_dump_trans("Completed", trans); - trans->status = status; - list_del(&trans->list); - if (!list_empty(&bridge->transactions)) { - vexpress_config_dump_trans("Pending", trans); + do { + vexpress_config_dump_trans(message, trans); + list_del(&trans->list); + complete(&trans->completion); - bridge->info->func_exec(trans->func->func, trans->offset, - trans->write, trans->data); - } - spin_unlock_irqrestore(&bridge->transactions_lock, flags); + if (list_empty(&bridge->transactions)) + break; + + trans = list_first_entry(&bridge->transactions, + struct vexpress_config_trans, list); + vexpress_config_dump_trans("Executing pending", trans); + trans->status = bridge->info->func_exec(trans->func->func, + trans->offset, trans->write, trans->data); + message = "Finished pending"; + } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE); - complete(&trans->completion); + spin_unlock_irqrestore(&bridge->transactions_lock, flags); } EXPORT_SYMBOL(vexpress_config_complete); -- cgit v0.10.2 From 99f4c6b66a9ae362d21e6df95d04bc74e04d285e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 26 Apr 2013 17:23:26 +0200 Subject: mfd: si476x: Don't use 0bNNN This doesn't compile with sparc64 gcc-3.4.5. Cc: Hans Verkuil Cc: Sam Ravnborg Cc: Andrey Smirnov Signed-off-by: Andrew Morton Signed-off-by: Samuel Ortiz diff --git a/include/linux/mfd/si476x-core.h b/include/linux/mfd/si476x-core.h index ede3022..ba89b94 100644 --- a/include/linux/mfd/si476x-core.h +++ b/include/linux/mfd/si476x-core.h @@ -515,11 +515,11 @@ enum si476x_fm_receiver_properties { }; enum si476x_prop_audio_pwr_line_filter_bits { - SI476X_PROP_PWR_HARMONICS_MASK = 0b0000000000011111, - SI476X_PROP_PWR_GRID_MASK = 0b0000000100000000, - SI476X_PROP_PWR_ENABLE_MASK = 0b0000001000000000, - SI476X_PROP_PWR_GRID_50HZ = 0b0000000000000000, - SI476X_PROP_PWR_GRID_60HZ = 0b0000000100000000, + SI476X_PROP_PWR_HARMONICS_MASK = 0x001f, + SI476X_PROP_PWR_GRID_MASK = 0x0100, + SI476X_PROP_PWR_ENABLE_MASK = 0x0200, + SI476X_PROP_PWR_GRID_50HZ = 0x0000, + SI476X_PROP_PWR_GRID_60HZ = 0x0100, }; enum si476x_prop_fm_rds_config_bits { -- cgit v0.10.2