code:
/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Host communication command constants for Chrome EC */

#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 */

/* 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
/* Was recovery requested via keyboard; now unused. */
#define EC_SWITCH_IGNORE1		 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__

/*
 * Define __packed if someone hasn't beat us to it.  Linux kernel style
 * checking prefers __packed over __attribute__((packed)).
 */
#ifndef __packed
#define __packed __attribute__((packed))
#endif

/* 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;

/*****************************************************************************/
/* CROS_EC - Matrix KeyBoard Protocol */

/*
 * Read key state
 *
 * Returns raw data for keyboard cols; see ec_response_cros_ec_info.cols for
 * expected response size.
 */
#define EC_CMD_CROS_EC_STATE 0x60

/* Provide information about the matrix : number of rows and columns */
#define EC_CMD_CROS_EC_INFO 0x61

struct ec_response_cros_ec_info {
	uint32_t rows;
	uint32_t cols;
	uint8_t switches;
} __packed;

/* Simulate key press */
#define EC_CMD_CROS_EC_SIMULATE_KEY 0x62

struct ec_params_cros_ec_simulate_key {
	uint8_t col;
	uint8_t row;
	uint8_t pressed;
} __packed;

/* Configure keyboard scanning */
#define EC_CMD_CROS_EC_SET_CONFIG 0x64
#define EC_CMD_CROS_EC_GET_CONFIG 0x65

/* flags */
enum cros_ec_config_flags {
	EC_CROS_EC_FLAGS_ENABLE = 1,	/* Enable keyboard scanning */
};

enum cros_ec_config_valid {
	EC_CROS_EC_VALID_SCAN_PERIOD		= 1 << 0,
	EC_CROS_EC_VALID_POLL_TIMEOUT		= 1 << 1,
	EC_CROS_EC_VALID_MIN_POST_SCAN_DELAY	= 1 << 3,
	EC_CROS_EC_VALID_OUTPUT_SETTLE		= 1 << 4,
	EC_CROS_EC_VALID_DEBOUNCE_DOWN		= 1 << 5,
	EC_CROS_EC_VALID_DEBOUNCE_UP		= 1 << 6,
	EC_CROS_EC_VALID_FIFO_MAX_DEPTH		= 1 << 7,
};

/* Configuration for our key scanning algorithm */
struct ec_cros_ec_config {
	uint32_t valid_mask;		/* valid fields */
	uint8_t flags;		/* some flags (enum cros_ec_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_cros_ec_set_config {
	struct ec_cros_ec_config config;
} __packed;

struct ec_response_cros_ec_get_config {
	struct ec_cros_ec_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; /* 8-bit address (7-bit shifted << 1) */
	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; /* 8-bit address (7-bit shifted << 1) */
	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

/*****************************************************************************/
/* USB port mux control. */

/*
 * Switch USB mux or return to automatic switching.
 */
#define EC_CMD_USB_MUX 0x9a

struct ec_params_usb_mux {
	uint8_t mux;
} __packed;

/*****************************************************************************/
/* LDOs / FETs control. */

enum ec_ldo_state {
	EC_LDO_STATE_OFF = 0,	/* the LDO / FET is shut down */
	EC_LDO_STATE_ON = 1,	/* the LDO / FET is ON / providing power */
};

/*
 * Switch on/off a LDO.
 */
#define EC_CMD_LDO_SET 0x9b

struct ec_params_ldo_set {
	uint8_t index;
	uint8_t state;
} __packed;

/*
 * Get LDO state.
 */
#define EC_CMD_LDO_GET 0x9c

struct ec_params_ldo_get {
	uint8_t index;
} __packed;

struct ec_response_ldo_get {
	uint8_t state;
} __packed;

/*****************************************************************************/
/* Power info. */

/*
 * Get power info.
 */
#define EC_CMD_POWER_INFO 0x9d

struct ec_response_power_info {
	uint32_t usb_dev_type;
	uint16_t voltage_ac;
	uint16_t voltage_system;
	uint16_t current_system;
	uint16_t usb_current_limit;
} __packed;

/*****************************************************************************/
/* I2C passthru command */

#define EC_CMD_I2C_PASSTHRU 0x9e

/* Slave address is 10 (not 7) bit */
#define EC_I2C_FLAG_10BIT	(1 << 16)

/* Read data; if not present, message is a write */
#define EC_I2C_FLAG_READ	(1 << 15)

/* Mask for address */
#define EC_I2C_ADDR_MASK	0x3ff

#define EC_I2C_STATUS_NAK	(1 << 0) /* Transfer was not acknowledged */
#define EC_I2C_STATUS_TIMEOUT	(1 << 1) /* Timeout during transfer */

/* Any error */
#define EC_I2C_STATUS_ERROR	(EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT)

struct ec_params_i2c_passthru_msg {
	uint16_t addr_flags;	/* I2C slave address (7 or 10 bits) and flags */
	uint16_t len;		/* Number of bytes to write*/
} __packed;

struct ec_params_i2c_passthru {
	uint8_t port;		/* I2C port number */
	uint8_t num_msgs;	/* Number of messages */
	struct ec_params_i2c_passthru_msg msg[];
	/* Data for all messages is concatenated here */
} __packed;

struct ec_response_i2c_passthru {
	uint8_t i2c_status;	/* Status flags (EC_I2C_STATUS_...) */
	uint8_t num_msgs;	/* Number of messages processed */
	uint8_t data[];		/* Data for all messages concatenated here */
} __packed;

/*****************************************************************************/
/* 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; /* in mA */
} __packed;

/*
 * Set maximum external power current.
 */
#define EC_CMD_EXT_POWER_CURRENT_LIMIT 0xa2

struct ec_params_ext_power_current_limit {
	uint32_t limit; /* in mA */
} __packed;

/*****************************************************************************/
/* Smart battery pass-through */

/* Get / Set 16-bit smart battery registers */
#define EC_CMD_SB_READ_WORD   0xb0
#define EC_CMD_SB_WRITE_WORD  0xb1

/* Get / Set string smart battery parameters
 * formatted as SMBUS "block".
 */
#define EC_CMD_SB_READ_BLOCK  0xb2
#define EC_CMD_SB_WRITE_BLOCK 0xb3

struct ec_params_sb_rd {
	uint8_t reg;
} __packed;

struct ec_response_sb_rd_word {
	uint16_t value;
} __packed;

struct ec_params_sb_wr_word {
	uint8_t reg;
	uint16_t value;
} __packed;

struct ec_response_sb_rd_block {
	uint8_t data[32];
} __packed;

struct ec_params_sb_wr_block {
	uint8_t reg;
	uint16_t data[32];
} __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 */


code:
/* $OpenBSD: exclock.c,v 1.2 2015/05/27 00:06:14 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "fdt.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <machine/intr.h>
#include <machine/bus.h>
#if NFDT > 0
#include <machine/fdt.h>
#endif
#include <armv7/armv7/armv7var.h>

/* registers */
#define CLOCK_APLL_CON0				0x0100
#define CLOCK_APLL_CON1				0x0104
#define CLOCK_BPLL_CON0				0x0110
#define CLOCK_BPLL_CON1				0x0114
#define CLOCK_EPLL_CON0				0x0130
#define CLOCK_EPLL_CON1				0x0134
#define CLOCK_EPLL_CON2				0x0138
#define CLOCK_VPLL_CON0				0x0140
#define CLOCK_VPLL_CON1				0x0144
#define CLOCK_VPLL_CON2				0x0148
#define CLOCK_CLK_DIV_CPU0			0x0500
#define CLOCK_CLK_DIV_CPU1			0x0504
#define CLOCK_CLK_DIV_TOP0			0x0510
#define CLOCK_CLK_DIV_TOP1			0x0514
#define CLOCK_PLL_DIV2_SEL			0x0A24
#define CLOCK_MPLL_CON0				0x4100
#define CLOCK_MPLL_CON1				0x4104

/* bits and bytes */
#define MPLL_FOUT_SEL_SHIFT			0x4
#define MPLL_FOUT_SEL_MASK			0x1
#define BPLL_FOUT_SEL_SHIFT			0x0
#define BPLL_FOUT_SEL_MASK			0x1

#define HCLK_FREQ				24000

#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

struct exclock_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

enum clocks {
	/* OSC */
	OSC,		/* 24 MHz OSC */

	/* PLLs */
	APLL,		/* ARM core clock */
	MPLL,		/* System bus clock for memory controller */
	BPLL,		/* Graphic 3D processor clock and 1066 MHz clock for memory controller if necessary */
	CPLL,		/* Multi Format Video Hardware Codec clock */
	GPLL,		/* Graphic 3D processor clock or other clocks for DVFS flexibility */
	EPLL,		/* Audio interface clocks and clocks for other external device interfaces */
	VPLL,		/* dithered PLL, helps to reduce the EMI of display and camera */
};

struct exclock_softc *exclock_sc;

int exclock_match(struct device *parent, void *v, void *aux);
void exclock_attach(struct device *parent, struct device *self, void *args);
int exclock_cpuspeed(int *);
unsigned int exclock_decode_pll_clk(enum clocks, unsigned int, unsigned int);
unsigned int exclock_get_pll_clk(enum clocks);
unsigned int exclock_get_armclk(void);
unsigned int exclock_get_i2cclk(void);

struct cfattach	exclock_ca = {
	sizeof (struct exclock_softc), NULL, exclock_attach
};
struct cfattach	exclock_fdt_ca = {
	sizeof (struct exclock_softc), exclock_match, exclock_attach
};

struct cfdriver exclock_cd = {
	NULL, "exclock", DV_DULL
};

int
exclock_match(struct device *parent, void *v, void *aux)
{
#if NFDT > 0
	struct armv7_attach_args *aa = aux;

	if (fdt_node_compatible("samsung,exynos5250-clock", aa->aa_node))
		return 1;
#endif

	return 0;
}

void
exclock_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct exclock_softc *sc = (struct exclock_softc *) self;
	struct armv7mem mem;

	exclock_sc = sc;
	sc->sc_iot = aa->aa_iot;
#if NFDT > 0
	if (aa->aa_node) {
		struct fdt_memory fdtmem;
		if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem))
			panic("%s: could not extract memory data from FDT",
			    __func__);
		mem.addr = fdtmem.addr;
		mem.size = fdtmem.size;
	} else
#endif
	{

		mem.addr = aa->aa_dev->mem[0].addr;
		mem.size = aa->aa_dev->mem[0].size;
	}
	if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!", __func__);

	printf(": Exynos 5 CPU freq: %d MHz",
	    exclock_get_armclk() / 1000);

	printf("\n");

	cpu_cpuspeed = exclock_cpuspeed;
}

int
exclock_cpuspeed(int *freq)
{
	*freq = exclock_get_armclk() / 1000;
	return (0);
}

unsigned int
exclock_decode_pll_clk(enum clocks pll, unsigned int r, unsigned int k)
{
	uint32_t m, p, s = 0, mask, fout, freq;
	/*
	 * APLL_CON: MIDV [25:16]
	 * MPLL_CON: MIDV [25:16]
	 * EPLL_CON: MIDV [24:16]
	 * VPLL_CON: MIDV [24:16]
	 * BPLL_CON: MIDV [25:16]: Exynos5
	 */

	switch (pll)
	{
	case APLL:
	case MPLL:
	case BPLL:
		mask = 0x3ff;
		break;
	default:
		mask = 0x1ff;
	}

	m = (r >> 16) & mask;

	/* PDIV [13:8] */
	p = (r >> 8) & 0x3f;
	/* SDIV [2:0] */
	s = r & 0x7;

	freq = HCLK_FREQ;

	if (pll == EPLL) {
		k = k & 0xffff;
		/* FOUT = (MDIV + K / 65536) * FIN / (PDIV * 2^SDIV) */
		fout = (m + k / 65536) * (freq / (p * (1 << s)));
	} else if (pll == VPLL) {
		k = k & 0xfff;
		/* FOUT = (MDIV + K / 1024) * FIN / (PDIV * 2^SDIV) */
		fout = (m + k / 1024) * (freq / (p * (1 << s)));
	} else {
		/* FOUT = MDIV * FIN / (PDIV * 2^(SDIV - 1)) */
		fout = m * (freq / (p * (1 << s)));
	}

	return fout;
}

unsigned int
exclock_get_pll_clk(enum clocks pll)
{
	struct exclock_softc *sc = exclock_sc;
	uint32_t freq;

	switch (pll) {
	case APLL:
		freq = exclock_decode_pll_clk(pll,
		    HREAD4(sc, CLOCK_APLL_CON0),
		    0);
		break;
	case MPLL:
		freq = exclock_decode_pll_clk(pll,
		    HREAD4(sc, CLOCK_MPLL_CON0),
		    0);
		break;
	case BPLL:
		freq = exclock_decode_pll_clk(pll,
		    HREAD4(sc, CLOCK_BPLL_CON0),
		    0);
		break;
	case EPLL:
		freq = exclock_decode_pll_clk(pll,
		    HREAD4(sc, CLOCK_EPLL_CON0),
		    HREAD4(sc, CLOCK_EPLL_CON1));
		break;
	case VPLL:
		freq = exclock_decode_pll_clk(pll,
		    HREAD4(sc, CLOCK_VPLL_CON0),
		    HREAD4(sc, CLOCK_VPLL_CON1));
		break;
	default:
		return 0;
	}

	/*
	 * According to the user manual, in EVT1 MPLL and BPLL always gives
	 * 1.6GHz clock, so divide by 2 to get 800MHz MPLL clock.
	 */
	if (pll == MPLL || pll == BPLL) {
		uint32_t freq_sel;
		uint32_t pll_div2_sel = HREAD4(sc, CLOCK_PLL_DIV2_SEL);

		switch (pll) {
		case MPLL:
			freq_sel = (pll_div2_sel >> MPLL_FOUT_SEL_SHIFT)
					& MPLL_FOUT_SEL_MASK;
			break;
		case BPLL:
			freq_sel = (pll_div2_sel >> BPLL_FOUT_SEL_SHIFT)
					& BPLL_FOUT_SEL_MASK;
			break;
		default:
			freq_sel = -1;
			break;
		}

		if (freq_sel == 0)
			freq /= 2;
	}

	return freq;
}

unsigned int
exclock_get_armclk()
{
	struct exclock_softc *sc = exclock_sc;
	uint32_t div, armclk, arm_ratio, arm2_ratio;

	div = HREAD4(sc, CLOCK_CLK_DIV_CPU0);

	/* ARM_RATIO: [2:0], ARM2_RATIO: [30:28] */
	arm_ratio = (div >> 0) & 0x7;
	arm2_ratio = (div >> 28) & 0x7;

	armclk = exclock_get_pll_clk(APLL) / (arm_ratio + 1);
	armclk /= (arm2_ratio + 1);

	return armclk;
}

unsigned int
exclock_get_i2cclk()
{
	struct exclock_softc *sc = exclock_sc;
	uint32_t aclk_66, aclk_66_pre, div, ratio;

	div = HREAD4(sc, CLOCK_CLK_DIV_TOP1);
	ratio = (div >> 24) & 0x7;
	aclk_66_pre = exclock_get_pll_clk(MPLL) / (ratio + 1);
	div = HREAD4(sc, CLOCK_CLK_DIV_TOP0);
	ratio = (div >> 0) & 0x7;
	aclk_66 = aclk_66_pre / (ratio + 1);

	return aclk_66;
}

license: unknown


code:
/* $OpenBSD: exclockvar.h,v 1.1 2015/01/26 02:48:24 bmercer Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef EXCLOCKVAR_H
#define EXCLOCKVAR_H

unsigned int exclock_get_i2cclk(void);

#endif /* EXCLOCKVAR_H */


code:
/* $OpenBSD: exdisplay.c,v 1.2 2015/05/27 00:06:14 jsg Exp $ */
/*
 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "fdt.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>

#include <dev/cons.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/wscons/wscons_callbacks.h>
#include <dev/wsfont/wsfont.h>
#include <dev/rasops/rasops.h>

#include <machine/intr.h>
#include <machine/bus.h>
#if NFDT > 0
#include <machine/fdt.h>
#endif
#include <armv7/armv7/armv7var.h>

/* registers */

struct exdisplay_softc {
	struct device		 sc_dev;
	bus_space_tag_t		 sc_iot;
	bus_space_handle_t	 sc_ioh;
	struct rasops_info	*ro;
};

int exdisplay_match(struct device *parent, void *v, void *aux);
void exdisplay_attach(struct device *parent, struct device *self, void *args);
int exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size);
void exdisplay_setup_rasops(struct rasops_info *rinfo, struct wsscreen_descr *descr);

struct cfattach	exdisplay_ca = {
	sizeof (struct exdisplay_softc), NULL, exdisplay_attach
};
struct cfattach	exdisplay_fdt_ca = {
	sizeof (struct exdisplay_softc), exdisplay_match, exdisplay_attach
};

struct cfdriver exdisplay_cd = {
	NULL, "exdisplay", DV_DULL
};

int exdisplay_wsioctl(void *, u_long, caddr_t, int, struct proc *);
paddr_t exdisplay_wsmmap(void *, off_t, int);
int exdisplay_alloc_screen(void *, const struct wsscreen_descr *,
    void **, int *, int *, long *);
void exdisplay_free_screen(void *, void *);
int exdisplay_show_screen(void *, void *, int,
    void (*)(void *, int, int), void *);
void exdisplay_doswitch(void *, void *);
int exdisplay_load_font(void *, void *, struct wsdisplay_font *);
int exdisplay_list_font(void *, struct wsdisplay_font *);
int exdisplay_getchar(void *, int, int, struct wsdisplay_charcell *);
void exdisplay_burner(void *, u_int, u_int);

struct rasops_info exdisplay_ri;
struct wsscreen_descr exdisplay_stdscreen = {
	"std"
};

const struct wsscreen_descr *exdisplay_scrlist[] = {
	&exdisplay_stdscreen,
};

struct wsscreen_list exdisplay_screenlist = {
	nitems(exdisplay_scrlist), exdisplay_scrlist
};

struct wsdisplay_accessops exdisplay_accessops = {
	.ioctl = exdisplay_wsioctl,
	.mmap = exdisplay_wsmmap,
	.alloc_screen = exdisplay_alloc_screen,
	.free_screen = exdisplay_free_screen,
	.show_screen = exdisplay_show_screen,
	.getchar = exdisplay_getchar,
	.load_font = exdisplay_load_font,
	.list_font = exdisplay_list_font,
	.burn_screen = exdisplay_burner
};

int
exdisplay_match(struct device *parent, void *v, void *aux)
{
#if NFDT > 0
	struct armv7_attach_args *aa = aux;

	if (fdt_node_compatible("samsung,exynos5250-fimd", aa->aa_node))
		return 1;
#endif

	return 0;
}

void
exdisplay_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct exdisplay_softc *sc = (struct exdisplay_softc *) self;
	struct wsemuldisplaydev_attach_args waa;
	struct rasops_info *ri = &exdisplay_ri;
	struct armv7mem mem;

	sc->sc_iot = aa->aa_iot;
#if NFDT > 0
	if (aa->aa_node) {
		struct fdt_memory fdtmem;
		if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem))
			panic("%s: could not extract memory data from FDT",
			    __func__);
		mem.addr = fdtmem.addr;
		mem.size = fdtmem.size;
	} else
#endif
	{
		mem.addr = aa->aa_dev->mem[0].addr;
		mem.size = aa->aa_dev->mem[0].size;
	}

	if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!", __func__);

	printf("\n");

#if notyet
	/* FIXME: Set up framebuffer instead of re-using. */
	if (!fdt_find_compatible("simple-framebuffer")) {
		long defattr;

		ri->ri_bits = (u_char *)sc->sc_fbioh;
		exdisplay_setup_rasops(ri, &exdisplay_stdscreen);

		ri->ri_ops.alloc_attr(ri->ri_active, 0, 0, 0, &defattr);
		wsdisplay_cnattach(&exdisplay_stdscreen, ri->ri_active,
		    0, 0, defattr);
	}
#endif

	sc->ro = ri;

	waa.console = 1;
	waa.scrdata = &exdisplay_screenlist;
	waa.accessops = &exdisplay_accessops;
	waa.accesscookie = sc;
	waa.defaultscreens = 0;

	printf("%s: %dx%d\n", sc->sc_dev.dv_xname, ri->ri_width, ri->ri_height);

	config_found(self, &waa, wsemuldisplaydevprint);
}

int
exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size)
{
	struct wsscreen_descr *descr = &exdisplay_stdscreen;
	struct rasops_info *ri = &exdisplay_ri;
	long defattr;

	if (bus_space_map(iot, iobase, size, 0, (bus_space_handle_t *)&ri->ri_bits))
		return ENOMEM;

	exdisplay_setup_rasops(ri, descr);

	/* assumes 16 bpp */
	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);

	wsdisplay_cnattach(descr, ri, ri->ri_ccol, ri->ri_crow, defattr);

	return 0;
}

void
exdisplay_setup_rasops(struct rasops_info *rinfo, struct wsscreen_descr *descr)
{
	rinfo->ri_flg = RI_CLEAR;
	rinfo->ri_depth = 16;
	rinfo->ri_width = 1366;
	rinfo->ri_height = 768;
	rinfo->ri_stride = rinfo->ri_width * rinfo->ri_depth / 8;

	/* swap B and R */
	if (rinfo->ri_depth == 16) {
		rinfo->ri_rnum = 5;
		rinfo->ri_rpos = 11;
		rinfo->ri_gnum = 6;
		rinfo->ri_gpos = 5;
		rinfo->ri_bnum = 5;
		rinfo->ri_bpos = 0;
	}

	wsfont_init();
	rinfo->ri_wsfcookie = wsfont_find(NULL, 8, 0, 0);
	wsfont_lock(rinfo->ri_wsfcookie, &rinfo->ri_font,
	    WSDISPLAY_FONTORDER_L2R, WSDISPLAY_FONTORDER_L2R);

	/* get rasops to compute screen size the first time */
	rasops_init(rinfo, 200, 200);

	descr->nrows = rinfo->ri_rows;
	descr->ncols = rinfo->ri_cols;
	descr->capabilities = rinfo->ri_caps;
	descr->textops = &rinfo->ri_ops;
}

int
exdisplay_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
{
	return (-1);
}

paddr_t
exdisplay_wsmmap(void *v, off_t off, int prot)
{
	return (-1);
}

int
exdisplay_alloc_screen(void *v, const struct wsscreen_descr *type,
    void **cookiep, int *curxp, int *curyp, long *attrp)
{
	struct exdisplay_softc *sc = v;
	struct rasops_info *ri = sc->ro;

	return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp);
}

void
exdisplay_free_screen(void *v, void *cookie)
{
	struct exdisplay_softc *sc = v;
	struct rasops_info *ri = sc->ro;

	return rasops_free_screen(ri, cookie);
}

int
exdisplay_show_screen(void *v, void *cookie, int waitok,
    void (*cb)(void *, int, int), void *cbarg)
{
	return (0);
}

void
exdisplay_doswitch(void *v, void *dummy)
{
}

int
exdisplay_getchar(void *v, int row, int col, struct wsdisplay_charcell *cell)
{
	struct exdisplay_softc *sc = v;
	struct rasops_info *ri = sc->ro;

	return rasops_getchar(ri, row, col, cell);
}

int
exdisplay_load_font(void *v, void *cookie, struct wsdisplay_font *font)
{
	struct exdisplay_softc *sc = v;
	struct rasops_info *ri = sc->ro;

	return rasops_load_font(ri, cookie, font);
}

int
exdisplay_list_font(void *v, struct wsdisplay_font *font)
{
	struct exdisplay_softc *sc = v;
	struct rasops_info *ri = sc->ro;

	return rasops_list_font(ri, font);
}

void
exdisplay_burner(void *v, u_int on, u_int flags)
{
}


code:
/*
 * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

int exdisplay_cnattach(bus_space_tag_t iot, bus_addr_t iobase, size_t size);


code:
/* $OpenBSD: exdog.c,v 1.2 2015/05/27 00:06:14 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "fdt.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/evcount.h>
#include <sys/socket.h>
#include <sys/timeout.h>
#include <machine/bus.h>
#if NFDT > 0
#include <machine/fdt.h>
#endif
#include <machine/intr.h>
#include <armv7/armv7/armv7var.h>

/* registers */
#define WTCON		0x00
#define WTDAT		0x04
#define WTCNT		0x08
#define WTCLRINT	0x0C

/* bits and bytes */
#define WTCON_RESET		(1 << 0)
#define WTCON_INT		(1 << 2)
#define WTCON_CLKSEL_16		(0x0 << 3)
#define WTCON_CLKSEL_32		(0x1 << 3)
#define WTCON_CLKSEL_64		(0x2 << 3)
#define WTCON_CLKSEL_128	(0x3 << 3)
#define WTCON_EN		(1 << 5)
#define WTCON_PRESCALER(x)	(((x) & 0xff) << 8)

struct exdog_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

struct exdog_softc *exdog_sc;

int exdog_match(struct device *parent, void *v, void *aux);
void exdog_attach(struct device *parent, struct device *self, void *args);
void exdog_stop(void);
void exdog_reset(void);

struct cfattach	exdog_ca = {
	sizeof (struct exdog_softc), NULL, exdog_attach
};
struct cfattach	exdog_fdt_ca = {
	sizeof (struct exdog_softc), exdog_match, exdog_attach
};

struct cfdriver exdog_cd = {
	NULL, "exdog", DV_DULL
};

int
exdog_match(struct device *parent, void *v, void *aux)
{
#if NFDT > 0
	struct armv7_attach_args *aa = aux;

	if (fdt_node_compatible("samsung,exynos5250-wdt", aa->aa_node))
		return 1;
#endif

	return 0;
}

void
exdog_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct exdog_softc *sc = (struct exdog_softc *) self;
	struct armv7mem mem;

	sc->sc_iot = aa->aa_iot;
#if NFDT > 0
	if (aa->aa_node) {
		struct fdt_memory fdtmem;
		if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem))
			panic("%s: could not extract memory data from FDT",
			    __func__);
		mem.addr = fdtmem.addr;
		mem.size = fdtmem.size;
	} else
#endif
	{
		mem.addr = aa->aa_dev->mem[0].addr;
		mem.size = aa->aa_dev->mem[0].size;
	}
	if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!", __func__);

	printf("\n");
	exdog_sc = sc;
}

void
exdog_stop()
{
	uint32_t wtcon;

	if (exdog_sc == NULL)
		return;

	wtcon = bus_space_read_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON);

	wtcon &= ~(WTCON_EN | WTCON_INT | WTCON_RESET);

	bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON, wtcon);
}

void
exdog_reset()
{
	uint32_t wtcon;

	if (exdog_sc == NULL)
		return;

	/* disable watchdog */
	exdog_stop();

	wtcon = bus_space_read_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON);

	wtcon |= WTCON_EN | WTCON_CLKSEL_128;
	wtcon &= ~WTCON_INT;
	wtcon |= WTCON_RESET;
	wtcon |= WTCON_PRESCALER(0xff);

	/* set timeout to 1 */
	bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTDAT, 1);
	bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCNT, 1);

	/* kick off the watchdog */
	bus_space_write_4(exdog_sc->sc_iot, exdog_sc->sc_ioh, WTCON, wtcon);

	delay(100000);
}


code:
/*	$OpenBSD: exehci.c,v 1.2 2015/05/27 00:06:14 jsg Exp $ */
/*
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include "fdt.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>
#include <sys/timeout.h>

#include <machine/intr.h>
#include <machine/bus.h>
#if NFDT > 0
#include <machine/fdt.h>
#endif

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/exynos/exsysregvar.h>
#include <armv7/exynos/expowervar.h>
#include <armv7/exynos/exgpiovar.h>

#include <dev/usb/ehcireg.h>
#include <dev/usb/ehcivar.h>

/* registers */
#define USBPHY_CTRL0			0x00
#define USBPHY_TUNE0			0x04
#define HSICPHY_CTRL1			0x10
#define HSICPHY_TUNE1			0x14
#define HSICPHY_CTRL2			0x20
#define HSICPHY_TUNE2			0x24
#define EHCI_CTRL			0x30
#define OHCI_CTRL			0x34
#define USBOTG_SYS			0x38
#define USBOTG_TUNE			0x40

/* bits and bytes */
#define CLK_24MHZ			5

#define HOST_CTRL0_PHYSWRSTALL		(1U << 31)
#define HOST_CTRL0_COMMONON_N		(1 << 9)
#define HOST_CTRL0_SIDDQ		(1 << 6)
#define HOST_CTRL0_FORCESLEEP		(1 << 5)
#define HOST_CTRL0_FORCESUSPEND		(1 << 4)
#define HOST_CTRL0_WORDINTERFACE	(1 << 3)
#define HOST_CTRL0_UTMISWRST		(1 << 2)
#define HOST_CTRL0_LINKSWRST		(1 << 1)
#define HOST_CTRL0_PHYSWRST		(1 << 0)

#define HOST_CTRL0_FSEL_MASK		(7 << 16)

#define EHCI_CTRL_ENAINCRXALIGN		(1 << 29)
#define EHCI_CTRL_ENAINCR4		(1 << 28)
#define EHCI_CTRL_ENAINCR8		(1 << 27)
#define EHCI_CTRL_ENAINCR16		(1 << 26)

int	exehci_match(struct device *, void *, void *);
void	exehci_attach(struct device *, struct device *, void *);
int	exehci_detach(struct device *, int);

struct exehci_softc {
	struct device		sc_dev;
	struct ehci_softc	*sc_ehci;
	void			*sc_ih;
	bus_dma_tag_t		sc_dmat;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	bus_size_t		sc_size;
	bus_space_handle_t	ph_ioh;
};

struct cfdriver exehci_cd = {
	NULL, "exehci", DV_DULL
};

struct cfattach exehci_ca = {
	sizeof (struct exehci_softc), NULL, exehci_attach,
	exehci_detach, NULL
};
struct cfattach exehci_fdt_ca = {
	sizeof (struct exehci_softc), exehci_match, exehci_attach,
	exehci_detach, NULL
};

void	exehci_setup(struct exehci_softc *);

int
exehci_match(struct device *parent, void *v, void *aux)
{
#if NFDT > 0
	struct armv7_attach_args *aa = aux;

	if (fdt_node_compatible("samsung,exynos4210-ehci", aa->aa_node))
		return 1;
#endif

	return 0;
}

void
exehci_attach(struct device *parent, struct device *self, void *aux)
{
	struct exehci_softc		*sc = (struct exehci_softc *)self;
	struct ehci_softc		*esc;
	struct armv7_attach_args	*aa = aux;
	struct armv7mem			 hmem, pmem;
	int				 irq;
	usbd_status			 r;

	sc->sc_iot = aa->aa_iot;
	sc->sc_dmat = aa->aa_dmat;

#if NFDT > 0
	if (aa->aa_node) {
		struct fdt_memory fhmem, fpmem;
		uint32_t ints[3];

		if (fdt_get_memory_address(aa->aa_node, 0, &fhmem))
			panic("%s: could not extract memory data from FDT",
			    __func__);

		/* XXX: In a different way, please. */
		void *node = fdt_find_compatible("samsung,exynos5250-usb2-phy");
		if (node == NULL || fdt_get_memory_address(node, 0, &fpmem))
			panic("%s: could not extract phy data from FDT",
			    __func__);

		/* TODO: Add interrupt FDT API. */
		if (fdt_node_property_ints(aa->aa_node, "interrupts",
		    ints, 3) != 3)
			panic("%s: could not extract interrupt data from FDT",
			    __func__);

		hmem.addr = fhmem.addr;
		hmem.size = fhmem.size;
		pmem.addr = fpmem.addr;
		pmem.size = fpmem.size;

		irq = ints[1];
	} else
#endif
	{
		hmem.addr = aa->aa_dev->mem[0].addr;
		hmem.size = aa->aa_dev->mem[0].size;
		pmem.addr = aa->aa_dev->mem[1].addr;
		pmem.size = aa->aa_dev->mem[1].size;
		irq = aa->aa_dev->irq[0];
	}

	/* Map I/O space */
	sc->sc_size = hmem.size;
	if (bus_space_map(sc->sc_iot, hmem.addr, hmem.size, 0, &sc->sc_ioh)) {
		printf(": cannot map mem space\n");
		goto out;
	}

	if (bus_space_map(sc->sc_iot, pmem.addr, pmem.size, 0, &sc->ph_ioh)) {
		printf(": cannot map mem space\n");
		goto pmem;
	}

	printf("\n");

	exehci_setup(sc);

	if ((esc = (struct ehci_softc *)config_found(self, NULL, NULL)) == NULL)
		goto hmem;

	sc->sc_ehci = esc;
	esc->iot = sc->sc_iot;
	esc->ioh = sc->sc_ioh;
	esc->sc_bus.dmatag = aa->aa_dmat;

	sc->sc_ih = arm_intr_establish(irq, IPL_USB,
	    ehci_intr, esc, esc->sc_bus.bdev.dv_xname);
	if (sc->sc_ih == NULL) {
		printf(": unable to establish interrupt\n");
		goto hmem;
	}

	strlcpy(esc->sc_vendor, "Exynos 5", sizeof(esc->sc_vendor));
	r = ehci_init(esc);
	if (r != USBD_NORMAL_COMPLETION) {
		printf("%s: init failed, error=%d\n",
		    esc->sc_bus.bdev.dv_xname, r);
		goto intr;
	}

	printf("\n");

	config_found((struct device *)esc, &esc->sc_bus, usbctlprint);

	goto out;

intr:
	arm_intr_disestablish(sc->sc_ih);
	sc->sc_ih = NULL;
hmem:
	bus_space_unmap(sc->sc_iot, sc->ph_ioh, hmem.size);
pmem:
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
	sc->sc_size = 0;
out:
	return;
}

int
exehci_detach(struct device *self, int flags)
{
	struct exehci_softc		*sc = (struct exehci_softc *)self;
	int				 rv = 0;

	rv = ehci_detach(self, flags);
	if (rv)
		return (rv);

	if (sc->sc_ih != NULL) {
		arm_intr_disestablish(sc->sc_ih);
		sc->sc_ih = NULL;
	}

	if (sc->sc_size) {
		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_size);
		sc->sc_size = 0;
	}

	return 0;
}

void
exehci_setup(struct exehci_softc *sc)
{
	uint32_t val;

	/* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */
	exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
	exgpio_set_bit(0xa9);
	delay(3000);

	exsysreg_usbhost_mode(1);
	expower_usbhost_phy_ctrl(1);

	delay(10000);

	/* Setting up host and device simultaneously */
	val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0);
	val &= ~(HOST_CTRL0_FSEL_MASK |
		 HOST_CTRL0_COMMONON_N |
		 /* HOST Phy setting */
		 HOST_CTRL0_PHYSWRST |
		 HOST_CTRL0_PHYSWRSTALL |
		 HOST_CTRL0_SIDDQ |
		 HOST_CTRL0_FORCESUSPEND |
		 HOST_CTRL0_FORCESLEEP);
	val |= (/* Setting up the ref freq */
		 CLK_24MHZ << 16 |
		 /* HOST Phy setting */
		 HOST_CTRL0_LINKSWRST |
		 HOST_CTRL0_UTMISWRST);
	bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0, val);
	delay(10000);
	bus_space_write_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0,
	    bus_space_read_4(sc->sc_iot, sc->ph_ioh, USBPHY_CTRL0) &
		~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
	delay(20000);

	/* EHCI Ctrl setting */
	bus_space_write_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL,
	    bus_space_read_4(sc->sc_iot, sc->ph_ioh, EHCI_CTRL) |
		EHCI_CTRL_ENAINCRXALIGN |
		EHCI_CTRL_ENAINCR4 |
		EHCI_CTRL_ENAINCR8 |
		EHCI_CTRL_ENAINCR16);

	/* HSIC USB Hub initialization. */
	if (1) {
		exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
		exgpio_clear_bit(0xc8);
		delay(1000);
		exgpio_set_bit(0xc8);
		delay(5000);

		val = bus_space_read_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1);
		val &= ~(HOST_CTRL0_SIDDQ |
			 HOST_CTRL0_FORCESLEEP |
			 HOST_CTRL0_FORCESUSPEND);
		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
		val |= HOST_CTRL0_PHYSWRST;
		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
		delay(1000);
		val &= ~HOST_CTRL0_PHYSWRST;
		bus_space_write_4(sc->sc_iot, sc->ph_ioh, HSICPHY_CTRL1, val);
	}

	/* PHY clock and power setup time */
	delay(50000);
}

int	ehci_ex_match(struct device *, void *, void *);
void	ehci_ex_attach(struct device *, struct device *, void *);

struct cfattach ehci_ex_ca = {
	sizeof (struct ehci_softc), ehci_ex_match, ehci_ex_attach
};

int
ehci_ex_match(struct device *parent, void *v, void *aux)
{
	return 1;
}

void
ehci_ex_attach(struct device *parent, struct device *self, void *aux)
{
}


code:
/*	$OpenBSD: exesdhc.c,v 1.3 2015/05/30 02:17:36 jsg Exp $	*/
/*
 * Copyright (c) 2009 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
 * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* i.MX SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */

#include "fdt.h"

#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/bus.h>
#if NFDT > 0
#include <machine/fdt.h>
#endif

#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcvar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/exynos/exclockvar.h>
#include <armv7/exynos/exgpiovar.h>

/* registers */
#define SDHC_DS_ADDR			0x00
#define SDHC_BLK_ATT			0x04
#define SDHC_CMD_ARG			0x08
#define SDHC_CMD_XFR_TYP		0x0c
#define SDHC_CMD_RSP0			0x10
#define SDHC_CMD_RSP1			0x14
#define SDHC_CMD_RSP2			0x18
#define SDHC_CMD_RSP3			0x1c
#define SDHC_DATA_BUFF_ACC_PORT		0x20
#define SDHC_PRES_STATE			0x24
#define SDHC_PROT_CTRL			0x28
#define SDHC_SYS_CTRL			0x2c
#define SDHC_INT_STATUS			0x30
#define SDHC_INT_STATUS_EN		0x34
#define SDHC_INT_SIGNAL_EN		0x38
#define SDHC_AUTOCMD12_ERR_STATUS	0x3c
#define SDHC_HOST_CTRL_CAP		0x40
#define SDHC_WTMK_LVL			0x44
#define SDHC_MIX_CTRL			0x48
#define SDHC_FORCE_EVENT		0x50
#define SDHC_ADMA_ERR_STATUS		0x54
#define SDHC_ADMA_SYS_ADDR		0x58
#define SDHC_DLL_CTRL			0x60
#define SDHC_DLL_STATUS			0x64
#define SDHC_CLK_TUNE_CTRL_STATUS	0x68
#define SDHC_VEND_SPEC			0xc0
#define SDHC_MMC_BOOT			0xc4
#define SDHC_VEND_SPEC2			0xc8
#define SDHC_HOST_CTRL_VER		0xfc

/* bits and bytes */
#define SDHC_BLK_ATT_BLKCNT_MAX			0xffff
#define SDHC_BLK_ATT_BLKCNT_SHIFT		16
#define SDHC_BLK_ATT_BLKSIZE_SHIFT		0
#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT		24
#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK	(0x3f << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT)
#define SDHC_CMD_XFR_TYP_CMDTYP_SHIFT		22
#define SDHC_CMD_XFR_TYP_DPSEL_SHIFT		21
#define SDHC_CMD_XFR_TYP_DPSEL			(1 << SDHC_CMD_XFR_TYP_DPSEL_SHIFT)
#define SDHC_CMD_XFR_TYP_CICEN_SHIFT		20
#define SDHC_CMD_XFR_TYP_CICEN			(1 << SDHC_CMD_XFR_TYP_CICEN_SHIFT)
#define SDHC_CMD_XFR_TYP_CCCEN_SHIFT		19
#define SDHC_CMD_XFR_TYP_CCCEN			(1 << SDHC_CMD_XFR_TYP_CCCEN_SHIFT)
#define SDHC_CMD_XFR_TYP_RSPTYP_SHIFT		16
#define SDHC_CMD_XFR_TYP_RSP_NONE		(0x0 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT)
#define SDHC_CMD_XFR_TYP_RSP136			(0x1 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT)
#define SDHC_CMD_XFR_TYP_RSP48			(0x2 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT)
#define SDHC_CMD_XFR_TYP_RSP48B			(0x3 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT)
#define SDHC_PRES_STATE_WPSPL			(1 << 19)
#define SDHC_PRES_STATE_BREN			(1 << 11)
#define SDHC_PRES_STATE_BWEN			(1 << 10)
#define SDHC_PRES_STATE_SDSTB			(1 << 3)
#define SDHC_PRES_STATE_DLA			(1 << 2)
#define SDHC_PRES_STATE_CDIHB			(1 << 1)
#define SDHC_PRES_STATE_CIHB			(1 << 0)
#define SDHC_SYS_CTRL_RSTA			(1 << 24)
#define SDHC_SYS_CTRL_RSTC			(1 << 25)
#define SDHC_SYS_CTRL_RSTD			(1 << 26)
#define SDHC_SYS_CTRL_CLOCK_MASK		(0xfff << 4)
#define SDHC_SYS_CTRL_CLOCK_DIV_SHIFT		4
#define SDHC_SYS_CTRL_CLOCK_PRE_SHIFT		8
#define SDHC_SYS_CTRL_DTOCV_SHIFT		16
#define SDHC_INT_STATUS_CC			(1 << 0)
#define SDHC_INT_STATUS_TC			(1 << 1)
#define SDHC_INT_STATUS_BGE			(1 << 2)
#define SDHC_INT_STATUS_DINT			(1 << 3)
#define SDHC_INT_STATUS_BWR			(1 << 4)
#define SDHC_INT_STATUS_BRR			(1 << 5)
#define SDHC_INT_STATUS_CINS			(1 << 6)
#define SDHC_INT_STATUS_CRM			(1 << 7)
#define SDHC_INT_STATUS_CINT			(1 << 8)
#define SDHC_INT_STATUS_CTOE			(1 << 16)
#define SDHC_INT_STATUS_CCE			(1 << 17)
#define SDHC_INT_STATUS_CEBE			(1 << 18)
#define SDHC_INT_STATUS_CIC			(1 << 19)
#define SDHC_INT_STATUS_DTOE			(1 << 20)
#define SDHC_INT_STATUS_DCE			(1 << 21)
#define SDHC_INT_STATUS_DEBE			(1 << 22)
#define SDHC_INT_STATUS_DMAE			(1 << 28)
#define SDHC_INT_STATUS_CMD_ERR			(SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CCE)
#define SDHC_INT_STATUS_ERR			(SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE | SDHC_INT_STATUS_CEBE | \
						 SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE | \
						 SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE)
#define SDHC_MIX_CTRL_DMAEN			(1 << 0)
#define SDHC_MIX_CTRL_BCEN			(1 << 1)
#define SDHC_MIX_CTRL_AC12EN			(1 << 2)
#define SDHC_MIX_CTRL_DTDSEL			(1 << 4)
#define SDHC_MIX_CTRL_MSBSEL			(1 << 5)
#define SDHC_PROT_CTRL_DMASEL_SDMA_MASK		(0x3 << 8)
#define SDHC_HOST_CTRL_CAP_MBL_SHIFT		16
#define SDHC_HOST_CTRL_CAP_MBL_MASK		0x7
#define SDHC_HOST_CTRL_CAP_VS33			(1 << 24)
#define SDHC_HOST_CTRL_CAP_VS30			(1 << 25)
#define SDHC_HOST_CTRL_CAP_VS18			(1 << 26)
#define SDHC_VEND_SPEC_FRC_SDCLK_ON		(1 << 8)
#define SDHC_WTMK_LVL_RD_WML_SHIFT		0
#define SDHC_WTMK_LVL_WR_WML_SHIFT		16

#define SDHC_COMMAND_TIMEOUT	hz
#define SDHC_BUFFER_TIMEOUT	hz
#define SDHC_TRANSFER_TIMEOUT	hz

int exesdhc_match(struct device *parent, void *v, void *aux);
void exesdhc_attach(struct device *parent, struct device *self, void *args);

#include <machine/bus.h>

struct exesdhc_softc {
	struct device sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	void			*sc_ih; /* Interrupt handler */
	u_int sc_flags;

	int unit;			/* unit id */
	struct device *sdmmc;		/* generic SD/MMC device */
	int clockbit;			/* clock control bit */
	u_int clkbase;			/* base clock frequency in KHz */
	int maxblklen;			/* maximum block length */
	int flags;			/* flags for this host */
	uint32_t ocr;			/* OCR value from capabilities */
//	u_int8_t regs[14];		/* host controller state */
	uint32_t intr_status;		/* soft interrupt status */
	uint32_t intr_error_status;	/*  */
};

/* Host controller functions called by the attachment driver. */
int	exesdhc_host_found(struct exesdhc_softc *, bus_space_tag_t,
	    bus_space_handle_t, bus_size_t, int);
void	exesdhc_power(int, void *);
void	exesdhc_shutdown(void *);
int	exesdhc_intr(void *);

/* RESET MODES */
#define MMC_RESET_DAT	1
#define MMC_RESET_CMD	2
#define MMC_RESET_ALL	(MMC_RESET_CMD|MMC_RESET_DAT)

#define HDEVNAME(sc)	((sc)->sc_dev.dv_xname)

/* flag values */
#define SHF_USE_DMA		0x0001

/* SDHC should only be accessed with 4 byte reads or writes. */
#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

int	exesdhc_host_reset(sdmmc_chipset_handle_t);
uint32_t exesdhc_host_ocr(sdmmc_chipset_handle_t);
int	exesdhc_host_maxblklen(sdmmc_chipset_handle_t);
int	exesdhc_card_detect(sdmmc_chipset_handle_t);
int	exesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t);
int	exesdhc_bus_clock(sdmmc_chipset_handle_t, int);
void	exesdhc_card_intr_mask(sdmmc_chipset_handle_t, int);
void	exesdhc_card_intr_ack(sdmmc_chipset_handle_t);
void	exesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
int	exesdhc_start_command(struct exesdhc_softc *, struct sdmmc_command *);
int	exesdhc_wait_state(struct exesdhc_softc *, uint32_t, uint32_t);
int	exesdhc_soft_reset(struct exesdhc_softc *, int);
int	exesdhc_wait_intr(struct exesdhc_softc *, int, int);
void	exesdhc_transfer_data(struct exesdhc_softc *, struct sdmmc_command *);
void	exesdhc_read_data(struct exesdhc_softc *, u_char *, int);
void	exesdhc_write_data(struct exesdhc_softc *, u_char *, int);

//#define SDHC_DEBUG
#ifdef SDHC_DEBUG
int exesdhcdebug = 20;
#define DPRINTF(n,s)	do { if ((n) <= exesdhcdebug) printf s; } while (0)
#else
#define DPRINTF(n,s)	do {} while(0)
#endif

struct sdmmc_chip_functions exesdhc_functions = {
	/* host controller reset */
	exesdhc_host_reset,
	/* host controller capabilities */
	exesdhc_host_ocr,
	exesdhc_host_maxblklen,
	/* card detection */
	exesdhc_card_detect,
	/* bus power and clock frequency */
	exesdhc_bus_power,
	exesdhc_bus_clock,
	/* command execution */
	exesdhc_exec_command,
	/* card interrupt */
	exesdhc_card_intr_mask,
	exesdhc_card_intr_ack
};

struct cfdriver exesdhc_cd = {
	NULL, "exesdhc", DV_DULL
};

struct cfattach exesdhc_ca = {
	sizeof(struct exesdhc_softc), NULL, exesdhc_attach
};
struct cfattach exesdhc_fdt_ca = {
	sizeof(struct exesdhc_softc), exesdhc_match, exesdhc_attach
};

int
exesdhc_match(struct device *parent, void *v, void *aux)
{
#if NFDT > 0
	struct armv7_attach_args *aa = aux;

	if (fdt_node_compatible("samsung,exynos5250-dw-mshc", aa->aa_node))
		return 1;
#endif

	return 0;
}

void
exesdhc_attach(struct device *parent, struct device *self, void *args)
{
	struct exesdhc_softc		*sc = (struct exesdhc_softc *) self;
	struct armv7_attach_args	*aa = args;
	struct sdmmcbus_attach_args	 saa;
	struct armv7mem			 mem;
	int				 error = 1, irq;
	uint32_t			 caps;

	sc->sc_iot = aa->aa_iot;
#if NFDT > 0
	if (aa->aa_node) {
		struct fdt_memory fdtmem;
		static int unit = 0;
		uint32_t ints[3];

		sc->unit = unit++;

		if (fdt_get_memory_address(aa->aa_node, 0, &fdtmem))
			panic("%s: could not extract memory data from FDT",
			    __func__);

		/* TODO: Add interrupt FDT API. */
		if (fdt_node_property_ints(aa->aa_node, "interrupts",
		    ints, 3) != 3)
			panic("%s: could not extract interrupt data from FDT",
			    __func__);

		mem.addr = fdtmem.addr;
		mem.size = fdtmem.size;

		irq = ints[1];
	} else
#endif
	{
		irq = aa->aa_dev->irq[0];
		mem.addr = aa->aa_dev->mem[0].addr;
		mem.size = aa->aa_dev->mem[0].size;
	}

	if (bus_space_map(sc->sc_iot, mem.addr, mem.size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!", __func__);

	printf("\n");

	/* XXX DMA channels? */

	sc->sc_ih = arm_intr_establish(irq, IPL_SDMMC,
	    exesdhc_intr, sc, sc->sc_dev.dv_xname);

	/*
	 * Reset the host controller and enable interrupts.
	 */
	if (exesdhc_host_reset(sc))
		goto err;

	/* Determine host capabilities. */
	caps = HREAD4(sc, SDHC_HOST_CTRL_CAP);

	/*
	 * Determine the base clock frequency. (2.2.24)
	 */
	//sc->clkbase = exccm_get_usdhx(aa->aa_dev->unit + 1);
	sc->clkbase = 0;

	/*
	 * Determine SD bus voltage levels supported by the controller.
	 */
	if (caps & SDHC_HOST_CTRL_CAP_VS18)
		SET(sc->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V);
	if (caps & SDHC_HOST_CTRL_CAP_VS30)
		SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V);
	if (caps & SDHC_HOST_CTRL_CAP_VS33)
		SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V);

	/*
	 * Determine max block size.
	 */
	switch ((caps >> SDHC_HOST_CTRL_CAP_MBL_SHIFT)
	    & SDHC_HOST_CTRL_CAP_MBL_MASK) {
	case 0:
		sc->maxblklen = 512;
		break;
	case 1:
		sc->maxblklen = 1024;
		break;
	case 2:
		sc->maxblklen = 2048;
		break;
	case 3:
		sc->maxblklen = 4096;
		break;
	default:
		sc->maxblklen = 512;
		printf("invalid capability blocksize in capa %08x,"
		    " trying 512\n", caps);
	}

	/* somewhere this blksize might be used instead of the device's */
	sc->maxblklen = 512;

	/*
	 * Attach the generic SD/MMC bus driver.  (The bus driver must
	 * not invoke any chipset functions before it is attached.)
	 */

	bzero(&saa, sizeof(saa));
	saa.saa_busname = "sdmmc";
	saa.sct = &exesdhc_functions;
	saa.sch = sc;

	sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
	if (sc->sdmmc == NULL) {
		error = 0;
		goto err;
	}

	return;

err:
	return;
}

/*
 * Power hook established by or called from attachment driver.
 */
void
exesdhc_power(int why, void *arg)
{
}

/*
 * Shutdown hook established by or called from attachment driver.
 */
void
exesdhc_shutdown(void *arg)
{
	struct exesdhc_softc *sc = arg;

	/* XXX chip locks up if we don't disable it before reboot. */
	(void)exesdhc_host_reset(sc);
}

/*
 * Reset the host controller.  Called during initialization, when
 * cards are removed, upon resume, and during error recovery.
 */
int
exesdhc_host_reset(sdmmc_chipset_handle_t sch)
{
	struct exesdhc_softc *sc = sch;
	u_int32_t imask;
	int error;
	int s;

	s = splsdmmc();

	/* Disable all interrupts. */
	HWRITE4(sc, SDHC_INT_STATUS_EN, 0);
	HWRITE4(sc, SDHC_INT_SIGNAL_EN, 0);

	/*
	 * Reset the entire host controller and wait up to 100ms for
	 * the controller to clear the reset bit.
	 */
	if ((error = exesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTA)) != 0) {
		splx(s);
		return (error);
	}

	/* Set data timeout counter value to max for now. */
	HSET4(sc, SDHC_SYS_CTRL, 0xe << SDHC_SYS_CTRL_DTOCV_SHIFT);

	/* Enable interrupts. */
	imask = SDHC_INT_STATUS_CC | SDHC_INT_STATUS_TC |
	    SDHC_INT_STATUS_BGE |
#ifdef SDHC_DMA
	    SHDC_INT_STATUS_DINT;
#else
	    SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR;
#endif

	imask |= SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE |
	    SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CIC |
	    SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE |
	    SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE;

	HWRITE4(sc, SDHC_INT_STATUS_EN, imask);
	HWRITE4(sc, SDHC_INT_SIGNAL_EN, imask);

	// Use no or simple DMA
	HWRITE4(sc, SDHC_PROT_CTRL,
	    HREAD4(sc, SDHC_PROT_CTRL) & ~SDHC_PROT_CTRL_DMASEL_SDMA_MASK);

	splx(s);
	return 0;
}

uint32_t
exesdhc_host_ocr(sdmmc_chipset_handle_t sch)
{
	struct exesdhc_softc *sc = sch;
	return sc->ocr;
}

int
exesdhc_host_maxblklen(sdmmc_chipset_handle_t sch)
{
	struct exesdhc_softc *sc = sch;
	return sc->maxblklen;
}

/*
 * Return non-zero if the card is currently inserted.
 */
int
exesdhc_card_detect(sdmmc_chipset_handle_t sch)
{
	struct exesdhc_softc *sc = sch;
	int gpio;

	switch (board_id)
	{
	case BOARD_ID_EXYNOS5_CHROMEBOOK:
		switch (sc->unit) {
			case 2:
				gpio = 6*32 + 0;
				break;
			case 3:
				gpio = 1*32 + 6;
				break;
			default:
				return 0;
		}
		return exgpio_get_bit(gpio) ? 0 : 1;
	default:
		return 1;
	}
}

/*
 * Set or change SD bus voltage and enable or disable SD bus power.
 * Return zero on success.
 */
int
exesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
{
	return 0;
}

/*
 * Set or change SDCLK frequency or disable the SD clock.
 * Return zero on success.
 */
int
exesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
{
	struct exesdhc_softc *sc = sch;
	int div, pre_div, cur_freq, s;
	int error = 0;

	s = splsdmmc();

	if (sc->clkbase / 16 > freq) {
		for (pre_div = 2; pre_div < 256; pre_div *= 2)
			if ((sc->clkbase / pre_div) <= (freq * 16))
				break;
	} else
		pre_div = 2;

	if (sc->clkbase == freq)
		pre_div = 1;

	for (div = 1; div <= 16; div++)
		if ((sc->clkbase / (div * pre_div)) <= freq)
			break;

	div -= 1;
	pre_div >>= 1;

	cur_freq = sc->clkbase / (pre_div * 2) / (div + 1);

	/* disable force CLK ouput active */
	HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON);

	/* wait while clock is unstable */
	if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0)
		goto ret;

	HCLR4(sc, SDHC_SYS_CTRL, SDHC_SYS_CTRL_CLOCK_MASK);
	HSET4(sc, SDHC_SYS_CTRL, (div << SDHC_SYS_CTRL_CLOCK_DIV_SHIFT) | (pre_div << SDHC_SYS_CTRL_CLOCK_PRE_SHIFT));

	/* wait while clock is unstable */
	if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0)
		goto ret;

ret:
	splx(s);
	return error;
}

void
exesdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
{
	printf("exesdhc_card_intr_mask\n");
	/* - this is SDIO card interrupt */
	struct exesdhc_softc *sc = sch;

	if (enable) {
		HSET4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT);
		HSET4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT);
	} else {
		HCLR4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT);
		HCLR4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT);
	}
}

void
exesdhc_card_intr_ack(sdmmc_chipset_handle_t sch)
{
	printf("exesdhc_card_intr_ack\n");
	struct exesdhc_softc *sc = sch;

	HWRITE4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT);
}

int
exesdhc_wait_state(struct exesdhc_softc *sc, uint32_t mask, uint32_t value)
{
	uint32_t state;
	int timeout;
	state = HREAD4(sc, SDHC_PRES_STATE);
	DPRINTF(3,("%s: wait_state %x %x %x)\n", HDEVNAME(sc),
	    mask, value, state));
	for (timeout = 1000; timeout > 0; timeout--) {
		if (((state = HREAD4(sc, SDHC_PRES_STATE)) & mask) == value)
			return 0;
		delay(10);
	}
	DPRINTF(0,("%s: timeout waiting for %x, state %x\n", HDEVNAME(sc),
	    value, state));
	return ETIMEDOUT;
}

void
exesdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
{
	struct exesdhc_softc *sc = sch;
	int error;

	/*
	 * Start the command, or mark `cmd' as failed and return.
	 */
	error = exesdhc_start_command(sc, cmd);
	if (error != 0) {
		cmd->c_error = error;
		SET(cmd->c_flags, SCF_ITSDONE);
		return;
	}

	/*
	 * Wait until the command phase is done, or until the command
	 * is marked done for any other reason.
	 */
	if (!exesdhc_wait_intr(sc, SDHC_INT_STATUS_CC, SDHC_COMMAND_TIMEOUT)) {
		cmd->c_error = ETIMEDOUT;
		SET(cmd->c_flags, SCF_ITSDONE);
		return;
	}

	/*
	 * The host controller removes bits [0:7] from the response
	 * data (CRC) and we pass the data up unchanged to the bus
	 * driver (without padding).
	 */
	if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
			cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0);
			cmd->c_resp[1] = HREAD4(sc, SDHC_CMD_RSP1);
			cmd->c_resp[2] = HREAD4(sc, SDHC_CMD_RSP2);
			cmd->c_resp[3] = HREAD4(sc, SDHC_CMD_RSP3);

#ifdef SDHC_DEBUG
			printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]);
#endif
		} else  {
			cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0);
#ifdef SDHC_DEBUG
			printf("resp[0] 0x%08x\n", cmd->c_resp[0]);
#endif
		}
	}

	/*
	 * If the command has data to transfer in any direction,
	 * execute the transfer now.
	 */
	if (cmd->c_error == 0 && cmd->c_data)
		exesdhc_transfer_data(sc, cmd);

	DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n",
	    HDEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error));
	SET(cmd->c_flags, SCF_ITSDONE);
}

int
exesdhc_start_command(struct exesdhc_softc *sc, struct sdmmc_command *cmd)
{
	u_int32_t blksize = 0;
	u_int32_t blkcount = 0;
	u_int32_t command;
	int error;
	int s;

	DPRINTF(1,("%s: start cmd %u arg=%#x data=%p dlen=%d flags=%#x "
	    "proc=\"%s\"\n", HDEVNAME(sc), cmd->c_opcode, cmd->c_arg,
	    cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ?
	    curproc->p_comm : ""));

	/*
	 * The maximum block length for commands should be the minimum
	 * of the host buffer size and the card buffer size. (1.7.2)
	 */

	/* Fragment the data into proper blocks. */
	if (cmd->c_datalen > 0) {
		blksize = MIN(cmd->c_datalen, cmd->c_blklen);
		blkcount = cmd->c_datalen / blksize;
		if (cmd->c_datalen % blksize > 0) {
			/* XXX: Split this command. (1.7.4) */
			printf("%s: data not a multiple of %d bytes\n",
			    HDEVNAME(sc), blksize);
			return EINVAL;
		}
	}

	/* Check limit imposed by 9-bit block count. (1.7.2) */
	if (blkcount > SDHC_BLK_ATT_BLKCNT_MAX) {
		printf("%s: too much data\n", HDEVNAME(sc));
		return EINVAL;
	}

	/* setup for PIO, check for write protection */
	if (!ISSET(cmd->c_flags, SCF_CMD_READ)) {
		if (!(HREAD4(sc, SDHC_PRES_STATE) & SDHC_PRES_STATE_WPSPL)) {
			printf("%s: card is write protected\n",
			    HDEVNAME(sc));
			return EINVAL;
		}
	}

#ifdef SDHC_DMA
	/* set watermark level */
	uint32_t wml = blksize / sizeof(uint32_t);
	if (ISSET(cmd->c_flags, SCF_CMD_READ)) {
		if (wml > 16)
			wml = 16;
		HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_RD_WML_SHIFT);
	} else {
		if (wml > 128)
			wml = 128;
		HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_WR_WML_SHIFT);
	}
#endif

	/* Prepare transfer mode register value. (2.2.5) */
	command = 0;

	if (ISSET(cmd->c_flags, SCF_CMD_READ))
		command |= SDHC_MIX_CTRL_DTDSEL;
	if (blkcount > 0) {
		command |= SDHC_MIX_CTRL_BCEN;
#ifdef SDHC_DMA
		command |= SDHC_MIX_CTRL_DMAEN;
#endif
		if (blkcount > 1) {
			command |= SDHC_MIX_CTRL_MSBSEL;
			command |= SDHC_MIX_CTRL_AC12EN;
		}
	}

	command |= (cmd->c_opcode << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT) &
	   SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK;

	if (ISSET(cmd->c_flags, SCF_RSP_CRC))
		command |= SDHC_CMD_XFR_TYP_CCCEN;
	if (ISSET(cmd->c_flags, SCF_RSP_IDX))
		command |= SDHC_CMD_XFR_TYP_CICEN;
	if (cmd->c_data != NULL)
		command |= SDHC_CMD_XFR_TYP_DPSEL;

	if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
		command |= SDHC_CMD_XFR_TYP_RSP_NONE;
	else if (ISSET(cmd->c_flags, SCF_RSP_136))
		command |= SDHC_CMD_XFR_TYP_RSP136;
	else if (ISSET(cmd->c_flags, SCF_RSP_BSY))
		command |= SDHC_CMD_XFR_TYP_RSP48B;
	else
		command |= SDHC_CMD_XFR_TYP_RSP48;

	/* Wait until command and data inhibit bits are clear. (1.5) */
	if ((error = exesdhc_wait_state(sc, SDHC_PRES_STATE_CIHB, 0)) != 0)
		return error;

	s = splsdmmc();

	/*
	 * Start a CPU data transfer.  Writing to the high order byte
	 * of the SDHC_COMMAND register triggers the SD command. (1.5)
	 */
#ifdef SDHC_DMA
	if (cmd->c_data)
		HWRITE4(sc, SDHC_DS_ADDR, (uint32_t)cmd->c_data);
#endif
	HWRITE4(sc, SDHC_BLK_ATT, blkcount << SDHC_BLK_ATT_BLKCNT_SHIFT |
	    blksize << SDHC_BLK_ATT_BLKSIZE_SHIFT);
	HWRITE4(sc, SDHC_CMD_ARG, cmd->c_arg);
	HWRITE4(sc, SDHC_MIX_CTRL,
	    (HREAD4(sc, SDHC_MIX_CTRL) & (0xf << 22)) | (command & 0xffff));
	HWRITE4(sc, SDHC_CMD_XFR_TYP, command);

	splx(s);
	return 0;
}

void
exesdhc_transfer_data(struct exesdhc_softc *sc, struct sdmmc_command *cmd)
{
#ifndef SDHC_DMA
	u_char *datap = cmd->c_data;
	int i;
#endif
	int datalen;
	int mask;
	int error;

	mask = ISSET(cmd->c_flags, SCF_CMD_READ) ?
	    SDHC_PRES_STATE_BREN : SDHC_PRES_STATE_BWEN;
	error = 0;
	datalen = cmd->c_datalen;

	DPRINTF(1,("%s: resp=%#x datalen=%d\n", HDEVNAME(sc),
	    MMC_R1(cmd->c_resp), datalen));

#ifndef SDHC_DMA
	while (datalen > 0) {
		if (!exesdhc_wait_intr(sc, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR,
		    SDHC_BUFFER_TIMEOUT)) {
			error = ETIMEDOUT;
			break;
		}

		if ((error = exesdhc_wait_state(sc, mask, mask)) != 0)
			break;

		/* FIXME: wait a bit, else it fails */
		delay(100);
		i = MIN(datalen, cmd->c_blklen);
		if (ISSET(cmd->c_flags, SCF_CMD_READ))
			exesdhc_read_data(sc, datap, i);
		else
			exesdhc_write_data(sc, datap, i);

		datap += i;
		datalen -= i;
	}
#endif

	if (error == 0 && !exesdhc_wait_intr(sc, SDHC_INT_STATUS_TC,
	    SDHC_TRANSFER_TIMEOUT))
		error = ETIMEDOUT;

	if (error != 0)
		cmd->c_error = error;
	SET(cmd->c_flags, SCF_ITSDONE);

	DPRINTF(1,("%s: data transfer done (error=%d)\n",
	    HDEVNAME(sc), cmd->c_error));
}

void
exesdhc_read_data(struct exesdhc_softc *sc, u_char *datap, int datalen)
{
	while (datalen > 3) {
		*(uint32_t *)datap = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT);
		datap += 4;
		datalen -= 4;
	}
	if (datalen > 0) {
		uint32_t rv = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT);
		do {
			*datap++ = rv & 0xff;
			rv = rv >> 8;
		} while (--datalen > 0);
	}
}

void
exesdhc_write_data(struct exesdhc_softc *sc, u_char *datap, int datalen)
{
	while (datalen > 3) {
		DPRINTF(3,("%08x\n", *(uint32_t *)datap));
		HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, *((uint32_t *)datap));
		datap += 4;
		datalen -= 4;
	}
	if (datalen > 0) {
		uint32_t rv = *datap++;
		if (datalen > 1)
			rv |= *datap++ << 8;
		if (datalen > 2)
			rv |= *datap++ << 16;
		DPRINTF(3,("rv %08x\n", rv));
		HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, rv);
	}
}

/* Prepare for another command. */
int
exesdhc_soft_reset(struct exesdhc_softc *sc, int mask)
{
	int timo;

	DPRINTF(1,("%s: software reset reg=%#x\n", HDEVNAME(sc), mask));

	/* disable force CLK ouput active */
	HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON);

	/* reset */
	HSET4(sc, SDHC_SYS_CTRL, mask);
	delay(10);

	for (timo = 1000; timo > 0; timo--) {
		if (!ISSET(HREAD4(sc, SDHC_SYS_CTRL), mask))
			break;
		delay(10);
	}
	if (timo == 0) {
		DPRINTF(1,("%s: timeout reg=%#x\n", HDEVNAME(sc),
		    HREAD4(sc, SDHC_SYS_CTRL)));
		return ETIMEDOUT;
	}

	return 0;
}

int
exesdhc_wait_intr(struct exesdhc_softc *sc, int mask, int timo)
{
	int status;
	int s;

	mask |= SDHC_INT_STATUS_ERR;
	s = splsdmmc();

	/* enable interrupts for brr and bwr */
	if (mask & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR))
		HSET4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR));

	status = sc->intr_status & mask;
	while (status == 0) {
		if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo)
		    == EWOULDBLOCK) {
			status |= SDHC_INT_STATUS_ERR;
			break;
		}
		status = sc->intr_status & mask;
	}
	sc->intr_status &= ~status;
	DPRINTF(2,("%s: intr status %#x error %#x\n", HDEVNAME(sc), status,
	    sc->intr_error_status));

	/* Command timeout has higher priority than command complete. */
	if (ISSET(status, SDHC_INT_STATUS_ERR)) {
		sc->intr_error_status = 0;
		(void)exesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTC | SDHC_SYS_CTRL_RSTD);
		status = 0;
	}

	splx(s);
	return status;
}

/*
 * Established by attachment driver at interrupt priority IPL_SDMMC.
 */
int
exesdhc_intr(void *arg)
{
	struct exesdhc_softc *sc = arg;

	u_int32_t status;

	/* Find out which interrupts are pending. */
	status = HREAD4(sc, SDHC_INT_STATUS);

#ifndef SDHC_DMA
	/* disable interrupts for brr and bwr, else we get flooded */
	if (status & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR))
		HCLR4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR));
#endif

	/* Acknowledge the interrupts we are about to handle. */
	HWRITE4(sc, SDHC_INT_STATUS, status);
	DPRINTF(2,("%s: interrupt status=0x%08x\n", HDEVNAME(sc),
	    status));

	/*
	 * Service error interrupts.
	 */
	if (ISSET(status, SDHC_INT_STATUS_CMD_ERR |
	    SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_DTOE)) {
		sc->intr_status |= status;
		sc->intr_error_status |= status & 0xffff0000;
		wakeup(&sc->intr_status);
	}

	/*
	 * Wake up the blocking process to service command
	 * related interrupt(s).
	 */
	if (ISSET(status, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR |
	    SDHC_INT_STATUS_TC | SDHC_INT_STATUS_CC)) {
		sc->intr_status |= status;
		wakeup(&sc->intr_status);
	}

	/*
	 * Service SD card interrupts.
	 */
	if (ISSET(status, SDHC_INT_STATUS_CINT)) {
		DPRINTF(0,("%s: card interrupt\n", HDEVNAME(sc)));
		HCLR4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT);
		sdmmc_card_intr(sc->sdmmc);
	}
	return 1;
}