r2540 - in developers/nbd: . ar6k ar6k/ar6000 ar6k/bmi ar6k/hif ar6k/htc ar6k/include ar6k/include/app ar6k/include/hw ar6k/wlan ar6k/wmi patches-2.6.22
nbd at sita.openmoko.org
nbd at sita.openmoko.org
Fri Jul 27 20:59:04 CEST 2007
Author: nbd
Date: 2007-07-27 20:59:01 +0200 (Fri, 27 Jul 2007)
New Revision: 2540
Added:
developers/nbd/ar6k/
developers/nbd/ar6k/Makefile
developers/nbd/ar6k/ar6000/
developers/nbd/ar6k/ar6000/ar6000_drv.c
developers/nbd/ar6k/ar6000/ar6000_drv.h
developers/nbd/ar6k/bmi/
developers/nbd/ar6k/bmi/Makefile
developers/nbd/ar6k/bmi/bmi.c
developers/nbd/ar6k/bmi/bmi_internal.h
developers/nbd/ar6k/hif/
developers/nbd/ar6k/hif/Makefile
developers/nbd/ar6k/hif/hif.c
developers/nbd/ar6k/hif/hif_internal.h
developers/nbd/ar6k/htc/
developers/nbd/ar6k/htc/Makefile
developers/nbd/ar6k/htc/htc.c
developers/nbd/ar6k/htc/htc_events.c
developers/nbd/ar6k/htc/htc_internal.h
developers/nbd/ar6k/htc/htc_recv.c
developers/nbd/ar6k/htc/htc_send.c
developers/nbd/ar6k/htc/htc_utils.c
developers/nbd/ar6k/include/
developers/nbd/ar6k/include/AR6000_bmi.h
developers/nbd/ar6k/include/AR6000_gpio.h
developers/nbd/ar6k/include/AR6000_version.h
developers/nbd/ar6k/include/app/
developers/nbd/ar6k/include/app/dset.h
developers/nbd/ar6k/include/ar6000_api.h
developers/nbd/ar6k/include/athdefs.h
developers/nbd/ar6k/include/athdrv.h
developers/nbd/ar6k/include/athtypes.h
developers/nbd/ar6k/include/bmi.h
developers/nbd/ar6k/include/dset_api.h
developers/nbd/ar6k/include/gpio_api.h
developers/nbd/ar6k/include/hif.h
developers/nbd/ar6k/include/host_version.h
developers/nbd/ar6k/include/htc.h
developers/nbd/ar6k/include/hw/
developers/nbd/ar6k/include/hw/mbox_host_reg.h
developers/nbd/ar6k/include/hw/mbox_reg.h
developers/nbd/ar6k/include/hw/mc_reg.h
developers/nbd/ar6k/include/hw/rtc_reg.h
developers/nbd/ar6k/include/ieee80211.h
developers/nbd/ar6k/include/ieee80211_ioctl.h
developers/nbd/ar6k/include/ieee80211_node.h
developers/nbd/ar6k/include/osapi.h
developers/nbd/ar6k/include/osapi_linux.h
developers/nbd/ar6k/include/queue.h
developers/nbd/ar6k/include/wlan_api.h
developers/nbd/ar6k/include/wmi.h
developers/nbd/ar6k/include/wmi_api.h
developers/nbd/ar6k/include/wmi_host.h
developers/nbd/ar6k/include/wmix.h
developers/nbd/ar6k/wlan/
developers/nbd/ar6k/wlan/Makefile
developers/nbd/ar6k/wlan/wlan_node.c
developers/nbd/ar6k/wlan/wlan_recv_beacon.c
developers/nbd/ar6k/wlan/wlan_utils.c
developers/nbd/ar6k/wmi/
developers/nbd/ar6k/wmi/wmi.c
developers/nbd/patches-2.6.22/
developers/nbd/patches-2.6.22/100-asoc.patch
developers/nbd/patches-2.6.22/110-asoc-asm_hardware_h.patch
developers/nbd/patches-2.6.22/120-asoc-platform-hw_init-pcm_emulation-fix.patch
developers/nbd/patches-2.6.22/130-i2c-permit_invalid_addrs.patch
developers/nbd/patches-2.6.22/140-pm-debug_less_verbose.patch
developers/nbd/patches-2.6.22/150-g_ether-highpower.patch
developers/nbd/patches-2.6.22/160-g_ether-vendor_product.patch
developers/nbd/patches-2.6.22/170-s3c2410_serial-nodebug.patch
developers/nbd/patches-2.6.22/180-s3c2410_udc.patch
developers/nbd/patches-2.6.22/190-s3c2410_touchscreen.patch
developers/nbd/patches-2.6.22/200-s3c2410-bbt.patch
developers/nbd/patches-2.6.22/210-udc-nomodule-misccr.patch
developers/nbd/patches-2.6.22/220-s3c2410_udc-vbus_draw_pdata.patch
developers/nbd/patches-2.6.22/280-qt2410-base.patch
developers/nbd/patches-2.6.22/290-qt2410-cs8900.patch
developers/nbd/patches-2.6.22/300-qt2410-s3c_mci-pdata.patch
developers/nbd/patches-2.6.22/310-gta01-pcf50606.patch
developers/nbd/patches-2.6.22/320-gta01-core.patch
developers/nbd/patches-2.6.22/330-gta01-jbt6k74.patch
developers/nbd/patches-2.6.22/340-gta01-backlight.patch
developers/nbd/patches-2.6.22/350-gta01-vibrator.patch
developers/nbd/patches-2.6.22/360-gta01-inputdevice.patch
developers/nbd/patches-2.6.22/370-gta01-power_control.patch
developers/nbd/patches-2.6.22/380-gta01-no_nand_partitions.patch
developers/nbd/patches-2.6.22/390-input-nots-mousedev.patch
developers/nbd/patches-2.6.22/400-ts0710.patch
developers/nbd/patches-2.6.22/410-s3c2410-qt2410-buttons.patch
developers/nbd/patches-2.6.22/420-config-nr-tty-devices.patch
developers/nbd/patches-2.6.22/430-hxd8-core.patch
developers/nbd/patches-2.6.22/440-s3c2410_fb-truecolor.patch
developers/nbd/patches-2.6.22/450-s3c2440-nand-disable-hwecc.patch
developers/nbd/patches-2.6.22/460-hxd8-tsl256x.patch
developers/nbd/patches-2.6.22/470-pcf50633.patch
developers/nbd/patches-2.6.22/480-smedia-glamo.patch
developers/nbd/patches-2.6.22/490-s3c24xx-nand-largepage.patch
developers/nbd/patches-2.6.22/500-gta02-core.patch
developers/nbd/patches-2.6.22/800-sdio_merge.patch
developers/nbd/patches-2.6.22/810-s3c_mci.patch
developers/nbd/patches-2.6.22/820-s3cmci_dbg.patch
developers/nbd/patches-2.6.22/830-s3cmci-dma-free.patch
developers/nbd/patches-2.6.22/840-s3cmci-stop-fix.patch
developers/nbd/patches-2.6.22/850-s3c_mci_platform.patch
developers/nbd/patches-2.6.22/860-missing_defs.patch
developers/nbd/patches-2.6.22/870-missing_export.patch
developers/nbd/patches-2.6.22/880-sdio_ops.patch
developers/nbd/patches-2.6.22/900-pcf50606_fix.patch
developers/nbd/patches-2.6.22/901-pcf50633_fix.patch
developers/nbd/patches-2.6.22/902-qt2410_lcd.patch
developers/nbd/patches-2.6.22/903-qt2410_nand.patch
developers/nbd/patches-2.6.22/904-partitions.patch
Log:
add my WiP ar6k tree and kernel patches
Added: developers/nbd/ar6k/Makefile
===================================================================
--- developers/nbd/ar6k/Makefile 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/Makefile 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,29 @@
+#
+# AR6K Kernel Module makefile.
+#
+REV ?= 2
+
+
+EXTRA_CFLAGS += -DLINUX -DDEBUG -D__KERNEL__ -DHTC_RAW_INTERFACE \
+ -DSEND_EVENT_TO_APP#\
+ -DMULTIPLE_FRAMES_PER_INTERRUPT -DAR6000REV$(REV) \
+ -DBLOCK_TX_PATH_FLAG \
+
+EXTRA_CFLAGS += -DKERNEL_2_6
+
+obj-dir := /bmi /htc /hif
+
+obj-m += ar6000.o
+ar6000-objs += htc/htc.o \
+ htc/htc_send.o \
+ htc/htc_recv.o \
+ htc/htc_utils.o \
+ htc/htc_events.o \
+ bmi/bmi.o \
+ hif/hif.o \
+ ar6000/ar6000_drv.o \
+ wmi/wmi.o \
+ wlan/wlan_node.o \
+ wlan/wlan_recv_beacon.o \
+ wlan/wlan_utils.o
+
Added: developers/nbd/ar6k/ar6000/ar6000_drv.c
===================================================================
--- developers/nbd/ar6k/ar6000/ar6000_drv.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/ar6000/ar6000_drv.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,5761 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+
+/*
+ * This driver is a pseudo ethernet driver to access the Atheros AR6000
+ * WLAN Device
+ */
+static const char athId[] __attribute__ ((unused)) = "$Id: //depot/sw/releases/etnaGPL1.1/host/os/linux/ar6000_drv.c#2 $";
+
+#include <linux/module.h>
+#include <linux/autoconf.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <net/iw_handler.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/rtnetlink.h>
+#include <linux/netdevice.h>
+#include <asm/semaphore.h>
+#include <linux/wireless.h>
+
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/htc.h"
+#include "../include/wmi.h"
+#include "../include/athdrv.h"
+#include "ar6000_drv.h"
+#include "../include/bmi.h"
+#include "../include/ieee80211.h"
+#include "../include/ieee80211_ioctl.h"
+#include "../include/wlan_api.h"
+#include "../include/wmi_api.h"
+#include "../include/dset_api.h"
+#include "../include/app/dset.h"
+#include "../include/gpio_api.h"
+#include "../include/AR6000_gpio.h"
+#include "../include/host_version.h"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+#include <asm/uaccess.h>
+#include <linux/rtnetlink.h>
+#endif
+#include "../include/hw/mbox_host_reg.h"
+#include "../include/hw/mc_reg.h"
+#include "../include/hw/mbox_reg.h"
+#include "../include/hw/rtc_reg.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+#if defined (SPI)
+#define CONTROL_PADCONF_AA12 0x00E8
+unsigned int awake_gpio = 17;
+unsigned int awake_gpio_pad_conf_offset = CONTROL_PADCONF_AA12;
+unsigned int awake_gpio_pad_conf_byte = 1;
+unsigned int awake_gpio_pad_mode_value = 0x3;
+#endif
+
+int bmienable = 0;
+unsigned int bypasswmi = 0;
+unsigned int debuglevel = 0;
+int tspecCompliance = CCX_V4_COMPLIANCE;
+unsigned int busspeedlow = 0;
+unsigned int onebitmode = 0;
+unsigned int skipflash = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+module_param(bmienable, int, 0644);
+module_param(bypasswmi, int, 0644);
+module_param(debuglevel, int, 0644);
+module_param(tspecCompliance, int, 0644);
+module_param(onebitmode, int, 0644);
+module_param(busspeedlow, int, 0644);
+module_param(skipflash, int, 0644);
+
+#else
+
+#define __user
+/* for linux 2.4 and lower */
+MODULE_PARM(bmienable,"i");
+MODULE_PARM(bypasswmi,"i");
+MODULE_PARM(debuglevel, "i");
+MODULE_PARM(onebitmode,"i");
+MODULE_PARM(busspeedlow, "i");
+MODULE_PARM(skipflash, "i");
+
+#if defined SPI
+MODULE_PARM(awake_gpio, "i");
+MODULE_PARM_DESC(awake_gpio, "Awake GPIO pin");
+MODULE_PARM(awake_gpio_pad_conf_offset, "i");
+MODULE_PARM_DESC(awake_gpio_pad_conf_offset,
+ "Awake GPIO pin pad config offset");
+MODULE_PARM(awake_gpio_pad_conf_byte, "i");
+MODULE_PARM_DESC(awake_gpio_pad_conf_byte,
+ "Awake GPIO pin pad byte number");
+MODULE_PARM(awake_gpio_pad_mode_value, "i");
+MODULE_PARM_DESC(awake_gpio_pad_mode_value,
+ "Awake GPIO pin mode value");
+#endif
+#endif
+
+#define MAX_ALLOWED_TXQ_DEPTH (HTC_DATA_REQUEST_RING_BUFFER_SIZE - 2)
+static int txFlowCtrlThresh[HTC_MAILBOX_NUM_MAX]
+ = {0 * (MAX_ALLOWED_TXQ_DEPTH - 1),
+ 1 * (MAX_ALLOWED_TXQ_DEPTH - 1),
+ 2 * (MAX_ALLOWED_TXQ_DEPTH - 1),
+ 3 * (MAX_ALLOWED_TXQ_DEPTH - 1)};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
+/* in 2.6.10 and later this is now a pointer to a uint */
+unsigned int _mboxnum = HTC_MAILBOX_NUM_MAX;
+#define mboxnum &_mboxnum
+#else
+unsigned int mboxnum = HTC_MAILBOX_NUM_MAX;
+#endif
+
+#ifdef DEBUG
+int debugdriver = 1;
+unsigned int debughtc = 128;
+unsigned int debugbmi = 1;
+unsigned int debughif = 1;
+unsigned int txcreditsavailable[HTC_MAILBOX_NUM_MAX] = {0};
+unsigned int txcreditsconsumed[HTC_MAILBOX_NUM_MAX] = {0};
+unsigned int txcreditintrenable[HTC_MAILBOX_NUM_MAX] = {0};
+unsigned int txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX] = {0};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+module_param(debugdriver, int, 0644);
+module_param(debughtc, int, 0644);
+module_param(debugbmi, int, 0644);
+module_param(debughif, int, 0644);
+module_param_array(txcreditsavailable, int, mboxnum, 0644);
+module_param_array(txcreditsconsumed, int, mboxnum, 0644);
+module_param_array(txcreditintrenable, int, mboxnum, 0644);
+module_param_array(txcreditintrenableaggregate, int, mboxnum, 0644);
+#else
+/* linux 2.4 and lower */
+MODULE_PARM(debugdriver, "i");
+MODULE_PARM(debughtc, "i");
+MODULE_PARM(debugbmi, "i");
+MODULE_PARM(debughif, "i");
+MODULE_PARM(txcreditsavailable, "0-3i");
+MODULE_PARM(txcreditsconsumed, "0-3i");
+MODULE_PARM(txcreditintrenable, "0-3i");
+MODULE_PARM(txcreditintrenableaggregate, "0-3i");
+#endif
+
+#endif /* DEBUG */
+
+unsigned int tx_attempt[HTC_MAILBOX_NUM_MAX] = {0};
+unsigned int tx_post[HTC_MAILBOX_NUM_MAX] = {0};
+unsigned int tx_complete[HTC_MAILBOX_NUM_MAX] = {0};
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+module_param_array(tx_attempt, int, mboxnum, 0644);
+module_param_array(tx_post, int, mboxnum, 0644);
+module_param_array(tx_complete, int, mboxnum, 0644);
+#else
+MODULE_PARM(tx_attempt, "0-3i");
+MODULE_PARM(tx_post, "0-3i");
+MODULE_PARM(tx_complete, "0-3i");
+#endif
+
+#ifdef BLOCK_TX_PATH_FLAG
+int blocktx = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+module_param(blocktx, int, 0644);
+#else
+MODULE_PARM(blocktx, "i");
+#endif
+#endif /* BLOCK_TX_PATH_FLAG */
+
+#ifdef DEBUG
+#define AR_DEBUG_PRINTF(args...) if (debugdriver) printk(args);
+#define AR_DEBUG2_PRINTF(args...) if (debugdriver >= 2) printk(args);
+#else
+#define AR_DEBUG_PRINTF(args...)
+#define AR_DEBUG2_PRINTF(args...)
+#endif
+
+/* Function declarations */
+static int ar6000_init_module(void);
+static void ar6000_cleanup_module(void);
+
+static int ar6000_init(struct net_device *dev);
+static int ar6000_open(struct net_device *dev);
+static int ar6000_close(struct net_device *dev);
+static void ar6000_init_control_info(AR_SOFTC_T *ar);
+static void ar6000_init_profile_info(AR_SOFTC_T *ar);
+static int ar6000_data_tx(struct sk_buff *skb, struct net_device *dev);
+static int ar6000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void ar6000_destroy(struct net_device *dev);
+static struct net_device_stats *ar6000_get_stats(struct net_device *dev);
+static void ar6000_ioctl_iwsetup(struct iw_handler_def *def);
+static int ar6000_ioctl_set_channelParams(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_ioctl_set_probedSsid(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_ioctl_set_badAp(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_get_qos_queue(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_delete_qos(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_create_qos(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_set_link_threshold(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_get_target_stats(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_set_error_report_bitmask(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_set_access_params(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_ioctl_set_disconnect_timeout(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_xioctl_set_voice_pkt_size(struct net_device *dev, char * userdata);
+static int ar6000_xioctl_set_max_sp_len(struct net_device *dev, char * userdata);
+static int ar6000_ioctl_get_roam_tbl(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_ioctl_get_roam_data(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_set_roam_ctrl(struct net_device *dev, char *userdata);
+static int ar6000_ioctl_set_powersave_timers(struct net_device *dev, char *userdata);
+static int ar6000_ioctl_get_power_mode(struct net_device *dev,
+ struct ifreq *rq);
+
+#ifdef HTC_RAW_INTERFACE
+static void ar6000_htc_raw_read_cb(HTC_TARGET *htcTarget,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID evId,
+ HTC_EVENT_INFO *evInfo,
+ void *arg);
+static void ar6000_htc_raw_unread_cb(HTC_TARGET *htcTarget,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID evId,
+ HTC_EVENT_INFO *evInfo,
+ void *arg);
+static void ar6000_htc_raw_write_cb(HTC_TARGET *htcTarget,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID evId,
+ HTC_EVENT_INFO *evInfo,
+ void *arg);
+static int ar6000_htc_raw_open(HTC_TARGET *htcTarget);
+static int ar6000_htc_raw_close(HTC_TARGET *htcTarget);
+static ssize_t ar6000_htc_raw_read(HTC_TARGET *htcTarget,
+ HTC_ENDPOINT_ID endPointId,
+ char __user *buffer, size_t count);
+static ssize_t ar6000_htc_raw_write(HTC_TARGET *htcTarget,
+ HTC_ENDPOINT_ID endPointId,
+ char __user *buffer, size_t count);
+#endif /* HTC_RAW_INTERFACE */
+static void ar6000_install_static_wep_keys(AR_SOFTC_T *ar);
+static int ar6000_ioctl_addpmkid(struct net_device *dev,
+ struct iw_request_info *info, void *w, char *extra);
+
+#if CONFIG_HOST_DSET_SUPPORT
+static void ar6000_dset_init(void);
+static int ar6000_ioctl_wait_dset_req(struct net_device *dev, struct ifreq *rq);
+static int ar6000_ioctl_dset_open_reply(struct net_device *dev,
+ struct ifreq *rq);
+static int ar6000_ioctl_dset_data_reply(struct net_device *dev,
+ struct ifreq *rq);
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+#if CONFIG_HOST_GPIO_SUPPORT
+static void ar6000_gpio_init(void);
+A_STATUS ar6000_gpio_output_set(struct net_device *dev,
+ A_UINT32 set_mask,
+ A_UINT32 clear_mask,
+ A_UINT32 enable_mask,
+ A_UINT32 disable_mask);
+static A_STATUS ar6000_gpio_input_get(struct net_device *dev);
+static A_STATUS ar6000_gpio_register_set(struct net_device *dev,
+ A_UINT32 gpioreg_id,
+ A_UINT32 value);
+static A_STATUS ar6000_gpio_register_get(struct net_device *dev,
+ A_UINT32 gpioreg_id);
+static A_STATUS ar6000_gpio_intr_ack(struct net_device *dev,
+ A_UINT32 ack_mask);
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+/*
+ * HTC event handlers
+ */
+static void ar6000_avail_ev(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID id, HTC_EVENT_INFO *evInfo, void *arg);
+
+static void ar6000_unavail_ev(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID id, HTC_EVENT_INFO *evInfo, void *arg);
+
+static void ar6000_rx(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID id, HTC_EVENT_INFO *evInfo, void *arg);
+
+static void ar6000_rx_refill(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID id, HTC_EVENT_INFO *evInfo, void *arg);
+
+static void ar6000_tx_complete(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID id, HTC_EVENT_INFO *evInfo, void *arg);
+
+/*
+ * Static variables
+ */
+static struct net_device *ar6000_devices[MAX_AR6000];
+static struct iw_handler_def ath_iw_handler_def;
+static DECLARE_WAIT_QUEUE_HEAD(arEvent);
+static A_UINT8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+static A_UINT8 null_mac[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+#if CONFIG_HOST_GPIO_SUPPORT
+struct ar6000_gpio_intr_wait_cmd_s gpio_intr_results;
+/* gpio_reg_results and gpio_data_available are protected by arSem */
+static struct ar6000_gpio_register_cmd_s gpio_reg_results;
+static A_BOOL gpio_data_available; /* Requested GPIO data available */
+static A_BOOL gpio_intr_available; /* GPIO interrupt info available */
+static A_BOOL gpio_ack_received; /* GPIO ack was received */
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+#if 0
+static struct iw_statistics * ar6000_get_iwstats(struct net_device *dev);
+#endif
+
+/*
+ * adhoc PS support
+ */
+static void ar6000_cookie_init(AR_SOFTC_T *ar);
+static void ar6000_cookie_cleanup(AR_SOFTC_T *ar);
+static void ar6000_free_cookie(AR_SOFTC_T *ar, struct ar_cookie * cookie);
+static struct ar_cookie *ar6000_alloc_cookie(AR_SOFTC_T *ar);
+
+static struct ar_cookie s_ar_cookie_mem[MAX_COOKIE_NUM];
+
+static int __init
+ar6000_init_module(void)
+{
+ static int probed = 0;
+ A_STATUS status;
+
+ if (probed) {
+ return -ENODEV;
+ }
+ probed++;
+
+#if CONFIG_HOST_DSET_SUPPORT
+ ar6000_dset_init();
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+#if CONFIG_HOST_GPIO_SUPPORT
+ ar6000_gpio_init();
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+ status = HTCInit();
+ if(status != A_OK)
+ return -ENODEV;
+
+ HTCEventReg(NULL, ENDPOINT_UNUSED, HTC_TARGET_AVAILABLE,
+ ar6000_avail_ev, NULL);
+ HTCEventReg(NULL, ENDPOINT_UNUSED, HTC_TARGET_UNAVAILABLE,
+ ar6000_unavail_ev, NULL);
+
+ return 0;
+}
+
+static void __exit
+ar6000_cleanup_module(void)
+{
+ int i = 0;
+ struct net_device *ar6000_netdev;
+
+ for (i=0; i < MAX_AR6000; i++) {
+ if (ar6000_devices[i] != NULL) {
+ ar6000_netdev = ar6000_devices[i];
+ ar6000_devices[i] = NULL;
+ ar6000_destroy(ar6000_netdev);
+ }
+ }
+
+ /* This will unregister with the SDIO bus driver */
+ HTCShutDown(NULL);
+
+ AR_DEBUG_PRINTF("ar6000_cleanup: success\n");
+}
+
+/*
+ * Write to the AR6000 through its diagnostic window.
+ * No cooperation from the Target is required for this.
+ */
+A_STATUS
+ar6000_WriteRegDiag(HIF_DEVICE *hifDevice, A_UINT32 *address, A_UINT32 *data)
+{
+ HIF_REQUEST request;
+ A_STATUS status;
+
+ HIF_FRAME_REQUEST(&request,
+ HIF_WRITE,
+ HIF_EXTENDED_IO,
+ HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS,
+ HIF_INCREMENTAL_ADDRESS);
+
+ status = HIFReadWrite(hifDevice,
+ WINDOW_DATA_ADDRESS,
+ (A_UCHAR *)data,
+ sizeof(A_UINT32),
+ &request,
+ NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Cannot write 0x%x to WINDOW_DATA_ADDRESS\n", *data);
+ return status;
+ }
+
+ status = HIFReadWrite(hifDevice,
+ WINDOW_WRITE_ADDR_ADDRESS+1,
+ ((A_UCHAR *)(address))+1,
+ sizeof(A_UINT32)-1,
+ &request,
+ NULL);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Cannot write initial bytes of 0x%x to WINDOW_WRITE_ADDR_ADDRESS\n", *address);
+ return status;
+ }
+
+ status = HIFReadWrite(hifDevice,
+ WINDOW_WRITE_ADDR_ADDRESS,
+ (A_UCHAR *)address,
+ sizeof(A_UINT8),
+ &request,
+ NULL);
+
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Cannot write 0x%x to WINDOW_WRITE_ADDR_ADDRESS\n", *address);
+ return status;
+ }
+
+ return status;
+}
+
+/*
+ * HTC Event handlers
+ */
+static void
+ar6000_avail_ev(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid, HTC_EVENT_ID event,
+ HTC_EVENT_INFO *evInfo,
+ void *arg)
+{
+ int i;
+ struct net_device *dev;
+ AR_SOFTC_T *ar;
+ int device_index = 0;
+
+ AR_DEBUG_PRINTF("ar6000_available\n");
+
+ for (i=0; i < MAX_AR6000; i++) {
+ if (ar6000_devices[i] == NULL) {
+ break;
+ }
+ }
+
+ if (i == MAX_AR6000) {
+ AR_DEBUG_PRINTF("ar6000_available: max devices reached\n");
+ return;
+ }
+
+ /* Save this. It gives a bit better readability especially since */
+ /* we use another local "i" variable below. */
+ device_index = i;
+
+ A_ASSERT(htcTarget != NULL);
+ A_ASSERT(event == HTC_TARGET_AVAILABLE);
+
+ dev = alloc_etherdev(sizeof(AR_SOFTC_T));
+ if (dev == NULL) {
+ AR_DEBUG_PRINTF("ar6000_available: can't alloc etherdev\n");
+ return;
+ }
+ SET_MODULE_OWNER(dev);
+
+ ether_setup(dev);
+
+ if (dev->priv == NULL) {
+ printk(KERN_CRIT "ar6000_available: Could not allocate memory\n");
+ return;
+ }
+
+ A_MEMZERO(dev->priv, sizeof(AR_SOFTC_T));
+
+ ar = (AR_SOFTC_T *)dev->priv;
+ ar->arNetDev = dev;
+ ar->arHtcTarget = htcTarget;
+ ar->arHifDevice = evInfo->buffer;
+ ar->arWlanState = WLAN_ENABLED;
+
+ ar6000_init_control_info(ar);
+ init_waitqueue_head(&arEvent);
+ sema_init(&ar->arSem, 1);
+
+ /*
+ * If requested, perform some magic which requires no cooperation from
+ * the Target. It causes the Target to ignore flash and execute the
+ * OS from ROM.
+ *
+ * This code uses the Diagnostic Window to remap instructions at
+ * the start of ROM in such a way that on the next CPU reset, the
+ * ROM code avoids using flash. Then it uses the Diagnostic
+ * Window to force a CPU Warm Reset.
+ *
+ * This is intended to support recovery from a corrupted flash.
+ */
+ if (skipflash)
+ {
+ int i;
+
+ static struct {
+ A_UINT32 addr;
+ A_UINT32 data;
+ } ForceROM[] = {
+ /* NB: This is ROM-version dependent. */
+ {0x00001ff0, 0x175b0027}, /* jump instruction at 0xa0001ff0 */
+ {0x00001ff4, 0x00000000}, /* nop instruction at 0xa0001ff4 */
+
+ {MC_REMAP_TARGET_ADDRESS, 0x00001ff0}, /* remap to 0xa0001ff0 */
+ {MC_REMAP_COMPARE_ADDRESS, 0x01000040},/* ...from 0xbfc00040 */
+ {MC_REMAP_SIZE_ADDRESS, 0x00000000}, /* ...1 cache line */
+ {MC_REMAP_VALID_ADDRESS, 0x00000001}, /* ...remap is valid */
+
+ {LOCAL_COUNT_ADDRESS+0x10, 0}, /* clear BMI credit counter */
+
+ {RESET_CONTROL_ADDRESS, RESET_CONTROL_WARM_RST_MASK},
+ };
+
+ AR_DEBUG_PRINTF("Force Target to execute from ROM....\n");
+ for (i=0; i<sizeof(ForceROM)/sizeof(*ForceROM); i++) {
+ if (ar6000_WriteRegDiag(ar->arHifDevice,
+ &ForceROM[i].addr,
+ &ForceROM[i].data) != A_OK)
+ {
+ AR_DEBUG_PRINTF("Cannot force Target to execute ROM!\n");
+ break;
+ }
+ }
+ }
+
+ BMIInit();
+ if ((BMIGetTargetId(ar->arHifDevice, &ar->arVersion.target_ver)) != A_OK) {
+ return;
+ }
+
+ spin_lock_init(&ar->arLock);
+
+ /* Don't install the init function if BMI is requested */
+ if(!bmienable)
+ {
+ dev->init = ar6000_init;
+ }
+ dev->open = &ar6000_open;
+ dev->stop = &ar6000_close;
+ dev->hard_start_xmit = &ar6000_data_tx;
+ dev->get_stats = &ar6000_get_stats;
+#if 0
+ dev->get_wireless_stats = ar6000_get_iwstats; /*Displayed via proc fs */
+#endif
+ /* dev->tx_timeout = ar6000_tx_timeout; */
+ dev->do_ioctl = &ar6000_ioctl;
+ dev->watchdog_timeo = AR6000_TX_TIMEOUT;
+ ar6000_ioctl_iwsetup(&ath_iw_handler_def);
+ dev->wireless_handlers = &ath_iw_handler_def;
+
+ /*
+ * We need the OS to provide us with more headroom in order to
+ * perform dix to 802.3, WMI header encap, and the HTC header
+ */
+ dev->hard_header_len = ETH_HLEN + sizeof(ATH_LLC_SNAP_HDR) +
+ sizeof(WMI_DATA_HDR) + HTC_HEADER_LEN;
+
+ /* This runs the init function */
+ if (register_netdev(dev)) {
+ AR_DEBUG_PRINTF("ar6000_avail: register_netdev failed\n");
+ ar6000_destroy(dev);
+ return;
+ }
+
+ /* We only register the device in the global list if we succeed. */
+ /* If the device is in the global list, it will be destroyed */
+ /* when the module is unloaded. */
+ ar6000_devices[device_index] = dev;
+
+ AR_DEBUG_PRINTF("ar6000_avail: name=%s htcTarget=0x%x, dev=0x%x (%d), ar=0x%x\n",
+ dev->name, (A_UINT32)htcTarget, (A_UINT32)dev, device_index,
+ (A_UINT32)ar);
+}
+
+static void
+ar6000_unavail_ev(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID event, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ int i;
+ AR_SOFTC_T *ar;
+ struct net_device *ar6000_netdev;
+
+ for (i=0; i < MAX_AR6000; i++) {
+ if (ar6000_devices[i] != NULL) {
+ ar = (AR_SOFTC_T *)ar6000_devices[i]->priv;
+ if (ar && ar->arHtcTarget == htcTarget) {
+ ar6000_netdev = ar6000_devices[i];
+ ar6000_devices[i] = NULL;
+ ar6000_destroy(ar6000_netdev);
+ }
+ }
+ }
+}
+
+static void
+ar6000_destroy(struct net_device *dev)
+{
+ AR_SOFTC_T *ar;
+
+ if((dev == NULL) || ((ar = netdev_priv(dev)) == NULL))
+ {
+ AR_DEBUG_PRINTF("%s(): Failed to get device structure.\n", __func__);
+ return;
+ }
+
+ /* Stop the transmit queues */
+ netif_stop_queue(dev);
+
+ /* Disable the target and the interrupts associated with it */
+ if (ar->arWmiReady == TRUE)
+ {
+ if (!bypasswmi)
+ {
+ if (ar->arConnected == TRUE || ar->arConnectPending == TRUE)
+ {
+ AR_DEBUG_PRINTF("%s(): Disconnect\n", __func__);
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar6000_init_profile_info(ar);
+ wmi_disconnect_cmd(ar->arWmi);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+
+ /* It's necessary to wait for the tx pending cleaning */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX]==0);
+
+ ar->arWmiReady = FALSE;
+ ar->arConnected = FALSE;
+ ar->arConnectPending = FALSE;
+ wmi_shutdown(ar->arWmi);
+ ar->arWmiEnabled = FALSE;
+ ar->arWmi = NULL;
+ ar->arWlanState = WLAN_ENABLED;
+ }
+
+ /* It's necessary to wait for the tx pending cleaning */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX]==0);
+
+ HTCStop(ar->arHtcTarget);
+
+ /* It's necessary to wait for the tx pending cleaning */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX]==0);
+
+ AR_DEBUG_PRINTF("%s(): WMI and HTC stopped\n", __func__);
+ }
+ else
+ {
+ AR_DEBUG_PRINTF("%s(): WMI not ready 0x%08x 0x%08x\n",
+ __func__, (unsigned int) ar, (unsigned int) ar->arWmi);
+
+ /* Shut down WMI if we have started it */
+ if(ar->arWmiEnabled == TRUE)
+ {
+ AR_DEBUG_PRINTF("%s(): Shut down WMI\n", __func__);
+ wmi_shutdown(ar->arWmi);
+ ar->arWmiEnabled = FALSE;
+ ar->arWmi = NULL;
+ }
+
+ /* It's necessary to wait for the tx pending cleaning */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX]==0);
+
+ HTCStop(ar->arHtcTarget);
+
+ /* It's necessary to wait for the tx pending cleaning */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX]==0);
+ }
+
+ BMIInit();
+
+ /* Done with cookies */
+ ar6000_cookie_cleanup(ar);
+
+ /* Free up the device data structure */
+ HTCShutDown(ar->arHtcTarget);
+
+ unregister_netdev(dev);
+#ifdef mvlcee31_2_4_20_omap2420_gsm_gprs
+ kfree(dev);
+#else
+ free_netdev(dev);
+#endif
+}
+
+static void
+ar6000_init_profile_info(AR_SOFTC_T *ar)
+{
+ ar->arSsidLen = 0;
+ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
+ ar->arNetworkType = INFRA_NETWORK;
+ ar->arDot11AuthMode = OPEN_AUTH;
+ ar->arAuthMode = NONE_AUTH;
+ ar->arPairwiseCrypto = NONE_CRYPT;
+ ar->arPairwiseCryptoLen = 0;
+ ar->arGroupCrypto = NONE_CRYPT;
+ ar->arGroupCryptoLen = 0;
+ A_MEMZERO(ar->arWepKeyList, sizeof(ar->arWepKeyList));
+ A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid));
+ A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
+ ar->arBssChannel = 0;
+}
+
+static void
+ar6000_init_control_info(AR_SOFTC_T *ar)
+{
+ ar->arWmiEnabled = FALSE;
+ ar6000_init_profile_info(ar);
+ ar->arDefTxKeyIndex = 0;
+ A_MEMZERO(ar->arWepKeyList, sizeof(ar->arWepKeyList));
+ ar->arChannelHint = 0;
+ ar->arListenInterval = MAX_LISTEN_INTERVAL;
+ ar->arVersion.host_ver = AR6000_SW_VERSION;
+ ar->arRssi = 0;
+ ar->arTxPwr = 0;
+ ar->arTxPwrSet = FALSE;
+ ar->arBitRate = 0;
+ ar->arMaxRetries = 0;
+}
+
+static int
+ar6000_open(struct net_device *dev)
+{
+ /* Wake up the queues */
+ netif_wake_queue(dev);
+
+ return 0;
+}
+
+static int
+ar6000_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+/* This function does one time initialization for the lifetime of the device */
+static int
+ar6000_init(struct net_device *dev)
+{
+ int i;
+ AR_SOFTC_T *ar;
+ int endpoint[] = { ENDPOINT1, ENDPOINT2, ENDPOINT3, ENDPOINT4 };
+ A_STATUS status;
+ A_INT32 timeleft;
+
+ if((ar = netdev_priv(dev)) == NULL)
+ {
+ return(-EIO);
+ }
+
+ /* Do we need to finish the BMI phase */
+ if(BMIDone(ar->arHifDevice) != A_OK)
+ {
+ return -EIO;
+ }
+
+ if (!bypasswmi)
+ {
+ if (ar->arVersion.host_ver != ar->arVersion.target_ver) {
+ A_PRINTF("WARNING: Host version 0x%x does not match Target "
+ " version 0x%x!\n",
+ ar->arVersion.host_ver, ar->arVersion.target_ver);
+ }
+
+ /* Indicate that WMI is enabled (although not ready yet) */
+ ar->arWmiEnabled = TRUE;
+ if ((ar->arWmi = wmi_init((void *) ar)) == NULL)
+ {
+ AR_DEBUG_PRINTF("%s() Failed to initialize WMI.\n", __func__);
+ return(-EIO);
+ }
+
+ AR_DEBUG_PRINTF("%s() Got WMI @ 0x%08x.\n", __func__,
+ (unsigned int) ar->arWmi);
+ }
+
+ /* Install event handlers for each end point */
+ for(i = 0; i < (sizeof(endpoint) / sizeof(int)); i++)
+ {
+ if(HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_BUFFER_RECEIVED,
+ ar6000_rx, ar) != A_OK)
+ {
+ return(-EIO);
+ }
+
+ if(HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_DATA_AVAILABLE,
+ ar6000_rx_refill, ar) != A_OK)
+ {
+ return(-EIO);
+ }
+
+ if(HTCEventReg(ar->arHtcTarget, endpoint[i], HTC_BUFFER_SENT,
+ ar6000_tx_complete, ar) != A_OK)
+ {
+ return(-EIO);
+ }
+ }
+
+ /*
+ * Provide HTC with receive buffers
+ */
+ ar6000_rx_refill(ar->arHtcTarget, ENDPOINT1, HTC_DATA_AVAILABLE,
+ NULL, ar);
+
+ ar6000_rx_refill(ar->arHtcTarget, ENDPOINT2, HTC_DATA_AVAILABLE,
+ NULL, ar);
+
+ /*
+ * We will post the receive buffers only for SPE testing and so we are
+ * making it conditional on the 'bypasswmi' flag.
+ */
+ if (bypasswmi) {
+ ar6000_rx_refill(ar->arHtcTarget, ENDPOINT3, HTC_DATA_AVAILABLE,
+ NULL, ar);
+
+ ar6000_rx_refill(ar->arHtcTarget, ENDPOINT4, HTC_DATA_AVAILABLE,
+ NULL, ar);
+ }
+
+ /* Since cookies are used for HTC transports, they should be */
+ /* initialized prior to enabling HTC. */
+ ar6000_cookie_init(ar);
+
+ /* Enable the target and the interrupts associated with it */
+ status = HTCStart(ar->arHtcTarget);
+
+ if (status != A_OK) {
+ if (ar->arWmiEnabled == TRUE) {
+ wmi_shutdown(ar->arWmi);
+ ar->arWmiEnabled = FALSE;
+ ar->arWmi = NULL;
+ }
+ ar6000_cookie_cleanup(ar);
+ return -EIO;
+ }
+
+ if (!bypasswmi) {
+ /*Wait for Wmi event to be ready */
+ timeleft = wait_event_interruptible_timeout(arEvent,
+ (ar->arWmiReady == TRUE), 1 * HZ);
+
+ if(!timeleft || signal_pending(current))
+ {
+ AR_DEBUG_PRINTF("WMI is not ready or wait was interrupted\n");
+ return -EIO;
+ }
+
+ AR_DEBUG_PRINTF("%s() WMI is ready\n", __func__);
+ }
+
+ ar->arNumDataEndPts = 1;
+
+ return(0);
+}
+
+/* This would basically hold all the private ioctls that are not related to
+ WLAN operation */
+static int
+ar6000_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ HIF_DEVICE *hifDevice = ar->arHifDevice;
+ HTC_TARGET *htcTarget = ar->arHtcTarget;
+ int ret, param;
+ unsigned int address = 0;
+ unsigned int length = 0;
+ unsigned char *buffer;
+ char *userdata;
+#ifdef HTC_RAW_INTERFACE
+ HTC_ENDPOINT_ID endPointId = 0;
+ static A_BOOL rawIfInit = FALSE;
+#endif /* HTC_RAW_INTERFACE */
+ static WMI_SCAN_PARAMS_CMD scParams = {0, 0, 0, 0, 0,
+ WMI_SHORTSCANRATIO_DEFAULT };
+
+ if (cmd == AR6000_IOCTL_EXTENDED)
+ {
+ /*
+ * This allows for many more wireless ioctls than would otherwise
+ * be available. Applications embed the actual ioctl command in
+ * the first word of the parameter block, and use the command
+ * AR6000_IOCTL_EXTENDED_CMD on the ioctl call.
+ */
+ get_user(cmd, (int *)rq->ifr_data);
+ userdata = (char *)(((unsigned int *)rq->ifr_data)+1);
+ }
+ else
+ {
+ userdata = (char *)rq->ifr_data;
+ }
+
+ if ((ar->arWlanState == WLAN_DISABLED) &&
+ (cmd != AR6000_XIOCTRL_WMI_SET_WLAN_STATE))
+ {
+ return -EIO;
+ }
+
+ ret = 0;
+ switch(cmd)
+ {
+ case AR6000_XIOCTL_BMI_DONE:
+ if(bmienable)
+ {
+ ret = ar6000_init(dev);
+ }
+ else
+ {
+ ret = BMIDone(hifDevice);
+ }
+ break;
+
+ case AR6000_XIOCTL_BMI_READ_MEMORY:
+ get_user(address, (unsigned int *)userdata);
+ get_user(length, (unsigned int *)userdata + 1);
+ AR_DEBUG_PRINTF("Read Memory (address: 0x%x, length: %d)\n",
+ address, length);
+ if ((buffer = (unsigned char *)A_MALLOC(length)) != NULL) {
+ A_MEMZERO(buffer, length);
+ ret = BMIReadMemory(hifDevice, address, buffer, length);
+ if (copy_to_user(rq->ifr_data, buffer, length)) {
+ ret = -EFAULT;
+ }
+ A_FREE(buffer);
+ } else {
+ ret = -ENOMEM;
+ }
+ break;
+
+ case AR6000_XIOCTL_BMI_WRITE_MEMORY:
+ get_user(address, (unsigned int *)userdata);
+ get_user(length, (unsigned int *)userdata + 1);
+ AR_DEBUG_PRINTF("Write Memory (address: 0x%x, length: %d)\n",
+ address, length);
+ if ((buffer = (unsigned char *)A_MALLOC(length)) != NULL) {
+ A_MEMZERO(buffer, length);
+ if (copy_from_user(buffer, &userdata[sizeof(address) +
+ sizeof(length)], length))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = BMIWriteMemory(hifDevice, address, buffer, length);
+ }
+ A_FREE(buffer);
+ } else {
+ ret = -ENOMEM;
+ }
+ break;
+
+ case AR6000_XIOCTL_BMI_TEST:
+ AR_DEBUG_PRINTF("No longer supported\n");
+ ret = -EOPNOTSUPP;
+ break;
+
+ case AR6000_XIOCTL_BMI_EXECUTE:
+ get_user(address, (unsigned int *)userdata);
+ get_user(param, (unsigned int *)userdata + 1);
+ AR_DEBUG_PRINTF("Execute (address: 0x%x, param: %d)\n",
+ address, param);
+ ret = BMIExecute(hifDevice, address, ¶m);
+ put_user(param, (unsigned int *)rq->ifr_data); /* return value */
+ break;
+
+ case AR6000_XIOCTL_BMI_SET_APP_START:
+ get_user(address, (unsigned int *)userdata);
+ AR_DEBUG_PRINTF("Set App Start (address: 0x%x)\n", address);
+ ret = BMISetAppStart(hifDevice, address);
+ break;
+
+ case AR6000_XIOCTL_BMI_READ_SOC_REGISTER:
+ get_user(address, (unsigned int *)userdata);
+ ret = BMIReadSOCRegister(hifDevice, address, ¶m);
+ put_user(param, (unsigned int *)rq->ifr_data); /* return value */
+ break;
+
+ case AR6000_XIOCTL_BMI_WRITE_SOC_REGISTER:
+ get_user(address, (unsigned int *)userdata);
+ get_user(param, (unsigned int *)userdata + 1);
+ ret = BMIWriteSOCRegister(hifDevice, address, param);
+ break;
+
+#ifdef HTC_RAW_INTERFACE
+ case AR6000_XIOCTL_HTC_RAW_OPEN:
+ ret = A_OK;
+ if (!rawIfInit) {
+ /* Terminate the BMI phase */
+ ret = BMIDone(hifDevice);
+ if (ret == A_OK) {
+ ret = ar6000_htc_raw_open(htcTarget);
+ }
+ if (ret == A_OK) {
+ rawIfInit = TRUE;
+ }
+ }
+ break;
+
+ case AR6000_XIOCTL_HTC_RAW_CLOSE:
+ if (rawIfInit) {
+ ret = ar6000_htc_raw_close(htcTarget);
+ rawIfInit = FALSE;
+ } else {
+ ret = A_ERROR;
+ }
+ break;
+
+ case AR6000_XIOCTL_HTC_RAW_READ:
+ if (rawIfInit) {
+ get_user(endPointId, (unsigned int *)userdata);
+ get_user(length, (unsigned int *)userdata + 1);
+ buffer = rq->ifr_data + sizeof(length);
+ ret = ar6000_htc_raw_read(htcTarget, endPointId,
+ buffer, length);
+ put_user(ret, (unsigned int *)rq->ifr_data);
+ } else {
+ ret = A_ERROR;
+ }
+ break;
+
+ case AR6000_XIOCTL_HTC_RAW_WRITE:
+ if (rawIfInit) {
+ get_user(endPointId, (unsigned int *)userdata);
+ get_user(length, (unsigned int *)userdata + 1);
+ buffer = userdata + sizeof(endPointId) + sizeof(length);
+ ret = ar6000_htc_raw_write(htcTarget, endPointId,
+ buffer, length);
+ put_user(ret, (unsigned int *)rq->ifr_data);
+ } else {
+ ret = A_ERROR;
+ }
+ break;
+#endif /* HTC_RAW_INTERFACE */
+
+ case AR6000_IOCTL_WMI_GETREV:
+ {
+ if (copy_to_user(rq->ifr_data, &ar->arVersion,
+ sizeof(ar->arVersion)))
+ {
+ ret = -EFAULT;
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SETPWR:
+ {
+ WMI_POWER_MODE_CMD pwrModeCmd;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&pwrModeCmd, userdata,
+ sizeof(pwrModeCmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_powermode_cmd(ar->arWmi, pwrModeCmd.powerMode)
+ != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_IBSS_PM_CAPS:
+ {
+ WMI_IBSS_PM_CAPS_CMD ibssPmCaps;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&ibssPmCaps, userdata,
+ sizeof(ibssPmCaps)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_ibsspmcaps_cmd(ar->arWmi, ibssPmCaps.power_saving, ibssPmCaps.ttl,
+ ibssPmCaps.atim_windows, ibssPmCaps.timeout_value) != A_OK)
+ {
+ ret = -EIO;
+ }
+ ar->arIbssPsEnable = ibssPmCaps.power_saving;
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_PMPARAMS:
+ {
+ WMI_POWER_PARAMS_CMD pmParams;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&pmParams, userdata,
+ sizeof(pmParams)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_pmparams_cmd(ar->arWmi, pmParams.idle_period,
+ pmParams.pspoll_number,
+ pmParams.dtim_policy) != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SETSCAN:
+ {
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&scParams, userdata,
+ sizeof(scParams)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_scanparams_cmd(ar->arWmi, scParams.fg_start_period,
+ scParams.fg_end_period,
+ scParams.bg_period,
+ scParams.act_chdwell_time,
+ scParams.pas_chdwell_time,
+ scParams.shortScanRatio) != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SETLISTENINT:
+ {
+ WMI_LISTEN_INT_CMD listenCmd;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&listenCmd, userdata,
+ sizeof(listenCmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_listeninterval_cmd(ar->arWmi, listenCmd.listenInterval, listenCmd.numBeacons) != A_OK) {
+ ret = -EIO;
+ } else {
+ ar->arListenInterval = param;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_BMISS_TIME:
+ {
+ WMI_BMISS_TIME_CMD bmissCmd;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&bmissCmd, userdata,
+ sizeof(bmissCmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_bmisstime_cmd(ar->arWmi, bmissCmd.bmissTime, bmissCmd.numBeacons) != A_OK) {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SETBSSFILTER:
+ {
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else {
+ get_user(param, (unsigned int *)userdata);
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_bssfilter_cmd(ar->arWmi, param) != A_OK) {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_LINKTHRESHOLD:
+ {
+ ret = ar6000_ioctl_set_link_threshold(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_CHANNELPARAMS:
+ {
+ ret = ar6000_ioctl_set_channelParams(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_PROBEDSSID:
+ {
+ ret = ar6000_ioctl_set_probedSsid(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_BADAP:
+ {
+ ret = ar6000_ioctl_set_badAp(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_CREATE_QOS:
+ {
+ ret = ar6000_ioctl_create_qos(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_DELETE_QOS:
+ {
+ ret = ar6000_ioctl_delete_qos(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_GET_QOS_QUEUE:
+ {
+ ret = ar6000_ioctl_get_qos_queue(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_GET_TARGET_STATS:
+ {
+ ret = ar6000_ioctl_get_target_stats(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_ERROR_REPORT_BITMASK:
+ {
+ ret = ar6000_ioctl_set_error_report_bitmask(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_ASSOC_INFO:
+ {
+ WMI_SET_ASSOC_INFO_CMD cmd;
+ A_UINT8 assocInfo[WMI_MAX_ASSOC_INFO_LEN];
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else {
+ get_user(cmd.ieType, userdata);
+ if (cmd.ieType >= WMI_MAX_ASSOC_INFO_TYPE) {
+ ret = -EIO;
+ } else {
+ get_user(cmd.bufferSize, userdata + 1);
+ if (cmd.bufferSize > WMI_MAX_ASSOC_INFO_LEN) {
+ ret = -EFAULT;
+ break;
+ }
+ if (copy_from_user(assocInfo, userdata + 2,
+ cmd.bufferSize))
+ {
+ ret = -EFAULT;
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_associnfo_cmd(ar->arWmi, cmd.ieType,
+ cmd.bufferSize,
+ assocInfo) != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ }
+ }
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_ACCESS_PARAMS:
+ {
+ ret = ar6000_ioctl_set_access_params(dev, rq);
+ break;
+ }
+ case AR6000_IOCTL_WMI_SET_DISC_TIMEOUT:
+ {
+ ret = ar6000_ioctl_set_disconnect_timeout(dev, rq);
+ break;
+ }
+#if CONFIG_HOST_DSET_SUPPORT
+ case AR6000_XIOCTL_WMI_DSET_WAIT_REQ:
+ {
+ ar6000_ioctl_wait_dset_req(dev, rq);
+ break;
+ }
+ case AR6000_XIOCTL_WMI_DSET_OPEN_REPLY:
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar6000_ioctl_dset_open_reply(dev, rq);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ break;
+ }
+ case AR6000_XIOCTL_WMI_DSET_DATA_REPLY:
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar6000_ioctl_dset_data_reply(dev, rq);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ break;
+ }
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+ case AR6000_XIOCTL_FORCE_TARGET_RESET:
+ {
+ if (htcTarget)
+ {
+// HTCForceReset(htcTarget);
+ }
+ else
+ {
+ AR_DEBUG_PRINTF("ar6000_ioctl cannot attempt reset.\n");
+ }
+ break;
+ }
+ case AR6000_XIOCTL_CHECK_TARGET_READY:
+ {
+ /* If we made it to here, then the Target exists and is ready. */
+ break;
+ }
+#if CONFIG_HOST_GPIO_SUPPORT
+ case AR6000_XIOCTL_GPIO_OUTPUT_SET:
+ {
+ struct ar6000_gpio_output_set_cmd_s gpio_output_set_cmd;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (copy_from_user(&gpio_output_set_cmd, userdata,
+ sizeof(gpio_output_set_cmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = ar6000_gpio_output_set(dev,
+ gpio_output_set_cmd.set_mask,
+ gpio_output_set_cmd.clear_mask,
+ gpio_output_set_cmd.enable_mask,
+ gpio_output_set_cmd.disable_mask);
+ if (ret != A_OK) {
+ ret = EIO;
+ }
+ }
+ up(&ar->arSem);
+ break;
+ }
+ case AR6000_XIOCTL_GPIO_INPUT_GET:
+ {
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ ret = ar6000_gpio_input_get(dev);
+ if (ret != A_OK) {
+ up(&ar->arSem);
+ return -EIO;
+ }
+
+ /* Wait for Target to respond. */
+ wait_event_interruptible(arEvent, gpio_data_available);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ } else {
+ A_ASSERT(gpio_reg_results.gpioreg_id == GPIO_ID_NONE);
+
+ if (copy_to_user(userdata, &gpio_reg_results.value,
+ sizeof(gpio_reg_results.value)))
+ {
+ ret = -EFAULT;
+ }
+ }
+ up(&ar->arSem);
+ break;
+ }
+ case AR6000_XIOCTL_GPIO_REGISTER_SET:
+ {
+ struct ar6000_gpio_register_cmd_s gpio_register_cmd;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (copy_from_user(&gpio_register_cmd, userdata,
+ sizeof(gpio_register_cmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = ar6000_gpio_register_set(dev,
+ gpio_register_cmd.gpioreg_id,
+ gpio_register_cmd.value);
+ if (ret != A_OK) {
+ ret = EIO;
+ }
+
+ /* Wait for acknowledgement from Target */
+ wait_event_interruptible(arEvent, gpio_ack_received);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ }
+ }
+ up(&ar->arSem);
+ break;
+ }
+ case AR6000_XIOCTL_GPIO_REGISTER_GET:
+ {
+ struct ar6000_gpio_register_cmd_s gpio_register_cmd;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (copy_from_user(&gpio_register_cmd, userdata,
+ sizeof(gpio_register_cmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = ar6000_gpio_register_get(dev, gpio_register_cmd.gpioreg_id);
+ if (ret != A_OK) {
+ up(&ar->arSem);
+ return -EIO;
+ }
+
+ /* Wait for Target to respond. */
+ wait_event_interruptible(arEvent, gpio_data_available);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ } else {
+ A_ASSERT(gpio_register_cmd.gpioreg_id == gpio_reg_results.gpioreg_id);
+ if (copy_to_user(userdata, &gpio_reg_results,
+ sizeof(gpio_reg_results)))
+ {
+ ret = -EFAULT;
+ }
+ }
+ }
+ up(&ar->arSem);
+ break;
+ }
+ case AR6000_XIOCTL_GPIO_INTR_ACK:
+ {
+ struct ar6000_gpio_intr_ack_cmd_s gpio_intr_ack_cmd;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (copy_from_user(&gpio_intr_ack_cmd, userdata,
+ sizeof(gpio_intr_ack_cmd)))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = ar6000_gpio_intr_ack(dev, gpio_intr_ack_cmd.ack_mask);
+ if (ret != A_OK) {
+ ret = EIO;
+ }
+ }
+ up(&ar->arSem);
+ break;
+ }
+ case AR6000_XIOCTL_GPIO_INTR_WAIT:
+ {
+ /* Wait for Target to report an interrupt. */
+ dev_hold(dev);
+ rtnl_unlock();
+ wait_event_interruptible(arEvent, gpio_intr_available);
+ rtnl_lock();
+ dev_put(dev);
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ } else {
+ if (copy_to_user(userdata, &gpio_intr_results,
+ sizeof(gpio_intr_results)))
+ {
+ ret = -EFAULT;
+ }
+ }
+ break;
+ }
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+ case AR6000_XIOCTL_SET_ADHOC_BSSID:
+ {
+ WMI_SET_ADHOC_BSSID_CMD adhocBssid;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&adhocBssid, userdata,
+ sizeof(adhocBssid)))
+ {
+ ret = -EFAULT;
+ } else if (A_MEMCMP(adhocBssid.bssid, bcast_mac,
+ AR6000_ETH_ADDR_LEN) == 0)
+ {
+ ret = -EFAULT;
+ } else {
+
+ A_MEMCPY(ar->arReqBssid, adhocBssid.bssid, sizeof(ar->arReqBssid));
+ }
+ break;
+ }
+
+ case AR6000_XIOCTL_SET_OPT_MODE:
+ {
+ WMI_SET_OPT_MODE_CMD optModeCmd;
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&optModeCmd, userdata,
+ sizeof(optModeCmd)))
+ {
+ ret = -EFAULT;
+ } else if (ar->arConnected && optModeCmd.optMode == SPECIAL_ON) {
+ ret = -EFAULT;
+
+ } else if (wmi_set_opt_mode_cmd(ar->arWmi, optModeCmd.optMode)
+ != A_OK)
+ {
+ ret = -EIO;
+ }
+ break;
+ }
+
+ case AR6000_XIOCTL_OPT_SEND_FRAME:
+ {
+ WMI_OPT_TX_FRAME_CMD optTxFrmCmd;
+ A_UINT8 data[MAX_OPT_DATA_LEN];
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&optTxFrmCmd, userdata,
+ sizeof(optTxFrmCmd)))
+ {
+ ret = -EFAULT;
+ } else if (copy_from_user(data,
+ optTxFrmCmd.optIEData,
+ optTxFrmCmd.optIEDataLen))
+ {
+ ret = -EFAULT;
+ } else {
+ ret = wmi_opt_tx_frame_cmd(ar->arWmi,
+ optTxFrmCmd.frmType,
+ optTxFrmCmd.dstAddr,
+ optTxFrmCmd.bssid,
+ optTxFrmCmd.optIEDataLen,
+ data);
+ }
+
+ break;
+ }
+
+ case AR6000_XIOCTL_SET_ADHOC_BEACON_INTVAL:
+ {
+ WMI_BEACON_INT_CMD bIntvlCmd;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&bIntvlCmd, userdata,
+ sizeof(bIntvlCmd)))
+ {
+ ret = -EFAULT;
+ } else if (wmi_set_adhoc_bconIntvl_cmd(ar->arWmi, bIntvlCmd.beaconInterval)
+ != A_OK)
+ {
+ ret = -EIO;
+ }
+ break;
+ }
+ case IEEE80211_IOCTL_SETAUTHALG:
+ {
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ieee80211req_authalg req;
+
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ } else if (copy_from_user(&req, userdata,
+ sizeof(struct ieee80211req_authalg)))
+ {
+ ret = -EFAULT;
+ } else if (req.auth_alg == AUTH_ALG_OPEN_SYSTEM) {
+ ar->arDot11AuthMode = OPEN_AUTH;
+ ar->arPairwiseCrypto = NONE_CRYPT;
+ ar->arGroupCrypto = NONE_CRYPT;
+ } else if (req.auth_alg == AUTH_ALG_LEAP) {
+ ar->arDot11AuthMode = LEAP_AUTH;
+ } else {
+ ret = -EIO;
+ }
+ break;
+ }
+
+ case AR6000_XIOCTL_SET_VOICE_PKT_SIZE:
+ ret = ar6000_xioctl_set_voice_pkt_size(dev, userdata);
+ break;
+
+ case AR6000_XIOCTL_SET_MAX_SP:
+ ret = ar6000_xioctl_set_max_sp_len(dev, userdata);
+ break;
+
+ case AR6000_XIOCTL_WMI_GET_ROAM_TBL:
+ ret = ar6000_ioctl_get_roam_tbl(dev, rq);
+ break;
+ case AR6000_XIOCTL_WMI_SET_ROAM_CTRL:
+ ret = ar6000_ioctl_set_roam_ctrl(dev, userdata);
+ break;
+ case AR6000_XIOCTRL_WMI_SET_POWERSAVE_TIMERS:
+ ret = ar6000_ioctl_set_powersave_timers(dev, userdata);
+ break;
+ case AR6000_XIOCTRL_WMI_GET_POWER_MODE:
+ ret = ar6000_ioctl_get_power_mode(dev, rq);
+ break;
+ case AR6000_XIOCTRL_WMI_SET_WLAN_STATE:
+ get_user(ar->arWlanState, (unsigned int *)userdata);
+ if (ar->arWmiReady == FALSE) {
+ ret = -EIO;
+ break;
+ }
+
+ if (ar->arWlanState == WLAN_ENABLED) {
+ /* Enable foreground scanning */
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_scanparams_cmd(ar->arWmi, scParams.fg_start_period,
+ scParams.fg_end_period,
+ scParams.bg_period,
+ scParams.act_chdwell_time,
+ scParams.pas_chdwell_time,
+ scParams.shortScanRatio) != A_OK)
+ {
+ ret = -EIO;
+ }
+ if (ar->arSsidLen) {
+ if (wmi_connect_cmd(ar->arWmi, ar->arNetworkType,
+ ar->arDot11AuthMode, ar->arAuthMode,
+ ar->arPairwiseCrypto,
+ ar->arPairwiseCryptoLen,
+ ar->arGroupCrypto, ar->arGroupCryptoLen,
+ ar->arSsidLen, ar->arSsid,
+ ar->arReqBssid, ar->arChannelHint) != A_OK)
+ {
+ ret = -EIO;
+ }
+ else
+ {
+ ar->arConnectPending = TRUE;
+ }
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ } else {
+ /* Disconnect from the AP and disable foreground scanning */
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (ar->arConnected == TRUE || ar->arConnectPending == TRUE) {
+ wmi_disconnect_cmd(ar->arWmi);
+ }
+ if (wmi_scanparams_cmd(ar->arWmi, 65535, 0, 0, 0, 0, 0) != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ break;
+ case AR6000_XIOCTL_WMI_GET_ROAM_DATA:
+ ret = ar6000_ioctl_get_roam_data(dev, rq);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+void
+ar6000_bitrate_rx(void *devt, A_INT32 rateKbps)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+
+ ar->arBitRate = rateKbps;
+ wake_up(&arEvent);
+}
+
+void
+ar6000_txPwr_rx(void *devt, A_UINT8 txPwr)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+
+ ar->arTxPwr = txPwr;
+ wake_up(&arEvent);
+}
+
+static int
+ar6000_ioctl_set_channelParams(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_CHANNEL_PARAMS_CMD cmd, *cmdp;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ if (cmd.numChannels > 1) {
+ cmdp = A_MALLOC(128);
+ if (copy_from_user(cmdp, rq->ifr_data,
+ sizeof (*cmdp) +
+ ((cmd.numChannels - 1) * sizeof(A_UINT16))))
+ {
+ kfree(cmdp);
+ return -EFAULT;
+ }
+ } else {
+ cmdp = &cmd;
+ }
+
+ if ((ar->arPhyCapability == WMI_11G_CAPABILITY) &&
+ ((cmdp->phyMode == WMI_11A_MODE) || (cmdp->phyMode == WMI_11AG_MODE)))
+ {
+ ret = -EINVAL;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (!ret &&
+ (wmi_set_channelParams_cmd(ar->arWmi, cmdp->phyMode, cmdp->numChannels,
+ cmdp->channelList) != A_OK))
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ if (cmd.numChannels > 1) {
+ kfree(cmdp);
+ }
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_set_link_threshold(struct net_device *dev, struct ifreq *rq)
+{
+
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_RSSI_THRESHOLD_PARAMS_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if( wmi_set_link_threshold_params(ar->arWmi,
+ cmd.highThreshold_upperVal,
+ cmd.highThreshold_lowerVal,
+ cmd.lowThreshold_upperVal,
+ cmd.lowThreshold_lowerVal,
+ cmd.pollTime) != A_OK ) {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return ret;
+}
+
+void
+ar6000_channelList_rx(void *devt, A_INT8 numChan, A_UINT16 *chanList)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+
+ A_MEMCPY(ar->arChannelList, chanList, numChan * sizeof (A_UINT16));
+ ar->arNumChannels = numChan;
+
+ wake_up(&arEvent);
+}
+
+static int
+ar6000_ioctl_set_probedSsid(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_PROBED_SSID_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_probedSsid_cmd(ar->arWmi, cmd.entryIndex, cmd.flag, cmd.ssidLength,
+ cmd.ssid) != A_OK)
+ {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_set_badAp(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_ADD_BAD_AP_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ if (cmd.badApIndex > WMI_MAX_BAD_AP_INDEX) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (A_MEMCMP(cmd.bssid, null_mac, AR6000_ETH_ADDR_LEN) == 0) {
+ /*
+ * This is a delete badAP.
+ */
+ if (wmi_deleteBadAp_cmd(ar->arWmi, cmd.badApIndex) != A_OK) {
+ ret = -EIO;
+ }
+ } else {
+ if (wmi_addBadAp_cmd(ar->arWmi, cmd.badApIndex, cmd.bssid) != A_OK) {
+ ret = -EIO;
+ }
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_create_qos(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_CREATE_PSTREAM_CMD cmd;
+ A_STATUS ret;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ cmd.rxQueueNum = 0xFF;
+ ret = wmi_verify_tspec_params(&cmd, tspecCompliance);
+ if (ret == A_OK)
+ ret = wmi_create_pstream_cmd(ar->arWmi, &cmd);
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ switch (ret) {
+ case A_OK:
+ return 0;
+ case A_EBUSY :
+ return -EBUSY;
+ case A_NO_MEMORY:
+ return -ENOMEM;
+ case A_EINVAL:
+ default:
+ return -EFAULT;
+ }
+}
+
+static int
+ar6000_ioctl_delete_qos(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_DELETE_PSTREAM_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ret = wmi_delete_pstream_cmd(ar->arWmi, cmd.txQueueNumber,
+ cmd.rxQueueNumber, cmd.trafficDirection);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ switch (ret) {
+ case A_OK:
+ return 0;
+ case A_EBUSY :
+ return -EBUSY;
+ case A_NO_MEMORY:
+ return -ENOMEM;
+ case A_EINVAL:
+ default:
+ return -EFAULT;
+ }
+}
+
+static int
+ar6000_ioctl_get_qos_queue(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ar6000_queuereq qreq;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ copy_from_user(&qreq, rq->ifr_data,
+ sizeof(struct ar6000_queuereq));
+
+ qreq.queueNumber = wmi_get_mapped_qos_queue(ar->arWmi,
+ qreq.trafficDirection,
+ qreq.trafficClass);
+
+ if (copy_to_user(rq->ifr_data, &qreq,
+ sizeof(struct ar6000_queuereq)))
+ {
+ ret = -EFAULT;
+ }
+ return ret;
+}
+
+#ifdef HTC_RAW_INTERFACE
+#define RAW_HTC_READ_BUFFERS_NUM 16
+#define RAW_HTC_WRITE_BUFFERS_NUM 16
+typedef struct {
+ int currPtr;
+ int length;
+ unsigned char data[AR6000_BUFFER_SIZE];
+} raw_htc_buffer;
+
+static struct semaphore raw_htc_read_sem[HTC_MAILBOX_NUM_MAX];
+static struct semaphore raw_htc_write_sem[HTC_MAILBOX_NUM_MAX];
+static wait_queue_head_t raw_htc_read_queue[HTC_MAILBOX_NUM_MAX];
+static wait_queue_head_t raw_htc_write_queue[HTC_MAILBOX_NUM_MAX];
+static raw_htc_buffer raw_htc_read_buffer[HTC_MAILBOX_NUM_MAX][RAW_HTC_READ_BUFFERS_NUM];
+static raw_htc_buffer raw_htc_write_buffer[HTC_MAILBOX_NUM_MAX][RAW_HTC_WRITE_BUFFERS_NUM];
+static A_BOOL write_buffer_available[HTC_MAILBOX_NUM_MAX];
+static A_BOOL read_buffer_available[HTC_MAILBOX_NUM_MAX];
+
+static void
+ar6000_htc_raw_read_cb(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ HTC_TARGET *target;
+ raw_htc_buffer *busy;
+
+ target = (HTC_TARGET *)arg;
+ A_ASSERT(target != NULL);
+ busy = (raw_htc_buffer *)evInfo->cookie;
+ A_ASSERT(busy != NULL);
+
+ if (evInfo->status == A_ECANCELED) {
+ /*
+ * HTC provides A_ECANCELED status when it doesn't want to be refilled
+ * (probably due to a shutdown)
+ */
+ memset(busy, 0, sizeof(raw_htc_buffer));
+ return;
+ }
+
+#ifdef CF
+ if (down_trylock(&raw_htc_read_sem[endPointId])) {
+#else
+ if (down_interruptible(&raw_htc_read_sem[endPointId])) {
+#endif /* CF */
+ AR_DEBUG2_PRINTF("Unable to down the semaphore\n");
+ }
+
+ A_ASSERT(evId == HTC_BUFFER_RECEIVED);
+ A_ASSERT((evInfo->status != A_OK) ||
+ (evInfo->buffer == (busy->data + HTC_HEADER_LEN)));
+
+ busy->length = evInfo->actualLength + HTC_HEADER_LEN;
+ busy->currPtr = HTC_HEADER_LEN;
+ read_buffer_available[endPointId] = TRUE;
+ up(&raw_htc_read_sem[endPointId]);
+
+ /* Signal the waiting process */
+ AR_DEBUG2_PRINTF("Waking up the endpoint(%d) read process\n", endPointId);
+ wake_up_interruptible(&raw_htc_read_queue[endPointId]);
+}
+
+static void
+ar6000_htc_raw_write_cb(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ HTC_TARGET *target;
+ raw_htc_buffer *free;
+
+ target = (HTC_TARGET *)arg;
+ A_ASSERT(target != NULL);
+ free = (raw_htc_buffer *)evInfo->cookie;
+ A_ASSERT(free != NULL);
+
+ if (evInfo->status == A_ECANCELED) {
+ /*
+ * HTC provides A_ECANCELED status when it doesn't want to be refilled
+ * (probably due to a shutdown)
+ */
+ memset(free, 0, sizeof(raw_htc_buffer));
+ return;
+ }
+
+#ifdef CF
+ if (down_trylock(&raw_htc_write_sem[endPointId])) {
+#else
+ if (down_interruptible(&raw_htc_write_sem[endPointId])) {
+#endif /* CF */
+ AR_DEBUG2_PRINTF("Unable to down the semaphore\n");
+ }
+
+ A_ASSERT(evId == HTC_BUFFER_SENT);
+ A_ASSERT(evInfo->buffer == (free->data + HTC_HEADER_LEN));
+
+ free->length = 0;
+ write_buffer_available[endPointId] = TRUE;
+ up(&raw_htc_write_sem[endPointId]);
+
+ /* Signal the waiting process */
+ AR_DEBUG2_PRINTF("Waking up the endpoint(%d) write process\n", endPointId);
+ wake_up_interruptible(&raw_htc_write_queue[endPointId]);
+}
+
+static void
+ar6000_htc_raw_unread_cb(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ HTC_TARGET *target;
+
+ target = (HTC_TARGET *)arg;
+ A_ASSERT(target != NULL);
+
+ AR_DEBUG_PRINTF("Not implemented\n");
+}
+
+static int
+ar6000_htc_raw_open(HTC_TARGET *htcTarget)
+{
+ A_STATUS status;
+ int count1, count2;
+ raw_htc_buffer *buffer;
+
+ for (count1 = 0; count1 < HTC_MAILBOX_NUM_MAX; count1 ++) {
+ /* Initialize the data structures */
+ init_MUTEX(&raw_htc_read_sem[count1]);
+ init_MUTEX(&raw_htc_write_sem[count1]);
+ init_waitqueue_head(&raw_htc_read_queue[count1]);
+ init_waitqueue_head(&raw_htc_write_queue[count1]);
+
+ /* Register the event handlers */
+ if ((status = HTCEventReg(htcTarget, count1, HTC_BUFFER_RECEIVED,
+ ar6000_htc_raw_read_cb, htcTarget)) != A_OK)
+ {
+ BMIInit();
+ return -EIO;
+ }
+ if ((status = HTCEventReg(htcTarget, count1, HTC_DATA_AVAILABLE,
+ ar6000_htc_raw_unread_cb, htcTarget)) != A_OK)
+ {
+ BMIInit();
+ return -EIO;
+ }
+ if ((status = HTCEventReg(htcTarget, count1, HTC_BUFFER_SENT,
+ ar6000_htc_raw_write_cb, htcTarget)) != A_OK)
+ {
+ BMIInit();
+ return -EIO;
+ }
+
+ for (count2 = 0; count2 < RAW_HTC_READ_BUFFERS_NUM; count2 ++) {
+ /* Initialize the receive buffers */
+ buffer = &raw_htc_write_buffer[count1][count2];
+ memset(buffer, 0, sizeof(raw_htc_buffer));
+ buffer = &raw_htc_read_buffer[count1][count2];
+ memset(buffer, 0, sizeof(raw_htc_buffer));
+
+ /* Queue buffers to HTC for receive */
+ if ((status = HTCBufferReceive(htcTarget, count1, buffer->data,
+ AR6000_BUFFER_SIZE, buffer)) != A_OK)
+ {
+ BMIInit();
+ return -EIO;
+ }
+ }
+
+ for (count2 = 0; count2 < RAW_HTC_WRITE_BUFFERS_NUM; count2 ++) {
+ /* Initialize the receive buffers */
+ buffer = &raw_htc_write_buffer[count1][count2];
+ memset(buffer, 0, sizeof(raw_htc_buffer));
+ }
+
+ read_buffer_available[count1] = FALSE;
+ write_buffer_available[count1] = TRUE;
+ }
+
+ /* Start the HTC component */
+ if ((status = HTCStart(htcTarget)) != A_OK) {
+ BMIInit();
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+ar6000_htc_raw_close(HTC_TARGET *htcTarget)
+{
+ int count;
+ A_STATUS status;
+
+ /* Stop the HTC */
+ HTCStop(htcTarget);
+
+ /* Unregister the event handlers */
+ for (count = 0; count < HTC_MAILBOX_NUM_MAX; count ++) {
+ status = HTCEventReg(htcTarget, count, HTC_BUFFER_RECEIVED,
+ NULL, htcTarget);
+
+ status = HTCEventReg(htcTarget, count, HTC_DATA_AVAILABLE,
+ NULL, htcTarget);
+
+ status = HTCEventReg(htcTarget, count, HTC_BUFFER_SENT,
+ NULL, htcTarget);
+ }
+
+ /* Initialize the BMI component */
+ BMIInit();
+
+ return 0;
+}
+
+raw_htc_buffer *
+get_filled_buffer(HTC_ENDPOINT_ID endPointId)
+{
+ int count;
+ raw_htc_buffer *busy;
+
+ /* Check for data */
+ for (count = 0; count < RAW_HTC_READ_BUFFERS_NUM; count ++) {
+ busy = &raw_htc_read_buffer[endPointId][count];
+ if (busy->length) {
+ break;
+ }
+ }
+ if (busy->length) {
+ read_buffer_available[endPointId] = TRUE;
+ } else {
+ read_buffer_available[endPointId] = FALSE;
+ }
+
+ return busy;
+}
+
+static ssize_t
+ar6000_htc_raw_read(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID endPointId,
+ char __user *buffer, size_t length)
+{
+ int readPtr;
+ raw_htc_buffer *busy;
+
+ if (down_interruptible(&raw_htc_read_sem[endPointId])) {
+ return -ERESTARTSYS;
+ }
+
+ busy = get_filled_buffer(endPointId);
+ while (!read_buffer_available[endPointId]) {
+ up(&raw_htc_read_sem[endPointId]);
+
+ /* Wait for the data */
+ AR_DEBUG2_PRINTF("Sleeping endpoint(%d) read process\n", endPointId);
+ if (wait_event_interruptible(raw_htc_read_queue[endPointId],
+ read_buffer_available[endPointId]))
+ {
+ return -EINTR;
+ }
+ if (down_interruptible(&raw_htc_read_sem[endPointId])) {
+ return -ERESTARTSYS;
+ }
+ busy = get_filled_buffer(endPointId);
+ }
+
+ /* Read the data */
+ readPtr = busy->currPtr;
+ if (length > busy->length - HTC_HEADER_LEN) {
+ length = busy->length - HTC_HEADER_LEN;
+ }
+ if (copy_to_user(buffer, &busy->data[readPtr], length)) {
+ up(&raw_htc_read_sem[endPointId]);
+ return -EFAULT;
+ }
+
+ busy->currPtr += length;
+ if (busy->currPtr == busy->length)
+ {
+ /* Packet has been completely read. Queue it with HTC */
+ memset(busy, 0, sizeof(raw_htc_buffer));
+ HTCBufferReceive(htcTarget, endPointId, busy->data,
+ AR6000_BUFFER_SIZE, busy);
+ }
+ read_buffer_available[endPointId] = FALSE;
+ up(&raw_htc_read_sem[endPointId]);
+
+ return length;
+}
+
+raw_htc_buffer *
+get_free_buffer(HTC_ENDPOINT_ID endPointId)
+{
+ int count;
+ raw_htc_buffer *free;
+
+ free = NULL;
+ for (count = 0; count < RAW_HTC_WRITE_BUFFERS_NUM; count ++) {
+ free = &raw_htc_write_buffer[endPointId][count];
+ if (free->length == 0) {
+ break;
+ }
+ }
+ if (!free->length) {
+ write_buffer_available[endPointId] = TRUE;
+ } else {
+ write_buffer_available[endPointId] = FALSE;
+ }
+
+ return free;
+}
+
+static ssize_t
+ar6000_htc_raw_write(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID endPointId,
+ char __user *buffer, size_t length)
+{
+ int writePtr;
+ raw_htc_buffer *free;
+
+ if (down_interruptible(&raw_htc_write_sem[endPointId])) {
+ return -ERESTARTSYS;
+ }
+
+ /* Search for a free buffer */
+ free = get_free_buffer(endPointId);
+
+ /* Check if there is space to write else wait */
+ while (!write_buffer_available[endPointId]) {
+ up(&raw_htc_write_sem[endPointId]);
+
+ /* Wait for buffer to become free */
+ AR_DEBUG2_PRINTF("Sleeping endpoint(%d) write process\n", endPointId);
+ if (wait_event_interruptible(raw_htc_write_queue[endPointId],
+ write_buffer_available[endPointId]))
+ {
+ return -EINTR;
+ }
+ if (down_interruptible(&raw_htc_write_sem[endPointId])) {
+ return -ERESTARTSYS;
+ }
+ free = get_free_buffer(endPointId);
+ }
+
+ /* Send the data */
+ writePtr = HTC_HEADER_LEN;
+ if (length > (AR6000_BUFFER_SIZE - HTC_HEADER_LEN)) {
+ length = AR6000_BUFFER_SIZE - HTC_HEADER_LEN;
+ }
+
+ if (copy_from_user(&free->data[writePtr], buffer, length)) {
+ up(&raw_htc_read_sem[endPointId]);
+ return -EFAULT;
+ }
+
+ free->length = length;
+ HTCBufferSend(htcTarget, endPointId, &free->data[writePtr], length, free);
+ write_buffer_available[endPointId] = FALSE;
+ up(&raw_htc_write_sem[endPointId]);
+
+ return length;
+}
+#endif /* HTC_RAW_INTERFACE */
+
+/*
+ * As soon as the DataSet Server application waits for a request,
+ * we know that the server is operational, and we try to process
+ * requests. If the Target sends any requests before the server
+ * has started, we reject the request. The model we use is that
+ * once started, the server must continue to service requests as
+ * long as they may arrive, and the server ought to be started
+ * before WMI is started (before the network interface is brought
+ * up.
+ */
+static A_BOOL dset_server_alive;
+
+#if CONFIG_HOST_DSET_SUPPORT
+/*
+ * For now, we allow for just one outstanding (unhandled) request
+ * at a time. The current Target doesn't need any more than that.
+ * If we ever need to handle many simultaneous requests, this
+ * pending_dset_request and pending_dset_request_valid mechanism
+ * can get more complicated. The use of dset_request_lock is
+ * also very simple; it assumes that a single well-behaved thread
+ * serves all requests.
+ */
+static A_BOOL pending_dset_request_valid;
+static dset_request_t pending_dset_request;
+
+static spinlock_t dset_request_lock;
+static DECLARE_WAIT_QUEUE_HEAD(dset_request);
+
+static int
+ar6000_ioctl_wait_dset_req(struct net_device *dev, struct ifreq *rq)
+{
+ int ret = 0;
+
+ dset_server_alive = TRUE;
+
+ /* Wait for a DataSet Request to arrive. */
+ dev_hold(dev);
+ rtnl_unlock();
+ wait_event_interruptible(dset_request, pending_dset_request_valid);
+ rtnl_lock();
+ dev_put(dev);
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ } else {
+ if (copy_to_user(rq->ifr_data,
+ &pending_dset_request,
+ sizeof(pending_dset_request))) {
+ ret = -EFAULT;
+ }
+
+ /* pending_dset_request is now available for use */
+ pending_dset_request_valid = FALSE;
+ spin_unlock(&dset_request_lock);
+ }
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_dset_open_reply(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int ret = 0;
+ dset_open_reply_t dset_reply;
+
+ if (copy_from_user(&dset_reply, rq->ifr_data, sizeof(dset_reply))) {
+ ret = -EFAULT;
+ } else {
+ A_ASSERT(dset_reply.cmd == AR6000_XIOCTL_WMI_DSET_OPEN_REPLY);
+ ret = wmi_dset_open_reply(ar->arWmi,
+ dset_reply.status,
+ dset_reply.access_cookie,
+ dset_reply.size,
+ dset_reply.version,
+ dset_reply.targ_handle,
+ dset_reply.targ_reply_fn,
+ dset_reply.targ_reply_arg);
+
+ if (ret == A_NO_MEMORY) {
+ ret = -ENOMEM;
+ } else if (ret == A_ERROR) {
+ ret = -EFAULT;
+ }
+ }
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_dset_data_reply(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int ret = 0;
+ dset_data_reply_t dset_reply;
+
+ if (copy_from_user(&dset_reply, rq->ifr_data, sizeof(dset_reply))) {
+ ret = -EFAULT;
+ } else {
+ A_ASSERT(dset_reply.cmd == AR6000_XIOCTL_WMI_DSET_DATA_REPLY);
+ ret = wmi_dset_data_reply(ar->arWmi,
+ dset_reply.status,
+ dset_reply.buf,
+ dset_reply.length,
+ dset_reply.targ_buf,
+ dset_reply.targ_reply_fn,
+ dset_reply.targ_reply_arg);
+
+ if (ret == A_NO_MEMORY) {
+ ret = -ENOMEM;
+ } else if (ret == A_ERROR) {
+ ret = -EFAULT;
+ }
+ }
+
+ return ret;
+}
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+A_UINT8 ar6000_ibss_map_epid(struct sk_buff *skb, struct net_device *dev, A_UINT32 * mapNo)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ A_UINT8 *datap;
+ ATH_MAC_HDR *macHdr;
+ A_UINT32 i, emptMap;
+
+ (*mapNo) = 0;
+ datap = a_netbuf_to_data(skb);
+ macHdr = (ATH_MAC_HDR *)(datap + sizeof(WMI_DATA_HDR));
+ if (IEEE80211_IS_MULTICAST(macHdr->dstMac)) {
+ return ENDPOINT3;
+ }
+
+ emptMap = -1;
+ for (i = 0; i < ar->arNodeNum; i ++) {
+ if (IEEE80211_ADDR_EQ(macHdr->dstMac, ar->arNodeMap[i].macAddress)) {
+ (*mapNo) = i + 1;
+ ar->arNodeMap[i].txPending ++;
+ return ar->arNodeMap[i].epId;
+ }
+
+ if ((emptMap == -1) && !ar->arNodeMap[i].txPending) {
+ emptMap = i;
+ }
+ }
+
+ if (emptMap == -1) {
+ emptMap = ar->arNodeNum;
+ ar->arNodeNum ++;
+ A_ASSERT(ar->arNodeNum <= MAX_NODE_NUM);
+ }
+
+ A_MEMCPY(ar->arNodeMap[emptMap].macAddress, macHdr->dstMac, IEEE80211_ADDR_LEN);
+
+ for (i = ENDPOINT2; i <= ENDPOINT4; i ++) {
+ if (!ar->arTxPending[i]) {
+ ar->arNodeMap[emptMap].epId = i;
+ break;
+ }
+ }
+
+ if (i > ENDPOINT4) {
+ ar->arNodeMap[emptMap].epId = ar->arNexEpId;
+ ar->arNexEpId ++;
+ if (ar->arNexEpId > ENDPOINT4) {
+ ar->arNexEpId = ENDPOINT2;
+ }
+ }
+
+ (*mapNo) = emptMap + 1;
+ ar->arNodeMap[emptMap].txPending ++;
+
+ return ar->arNodeMap[ar->arNodeNum - 1].epId;
+}
+
+static int
+ar6000_data_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ HTC_ENDPOINT_ID endPointId;
+ A_UINT32 mapNo = 0;
+ struct iphdr *ipHdr;
+ struct sk_buff *newbuf;
+ int len;
+ struct ar_cookie *cookie;
+
+ if (ar->arWmiReady == FALSE && bypasswmi == 0) {
+ a_netbuf_free(skb);
+ return 0;
+ }
+
+#ifdef BLOCK_TX_PATH_FLAG
+ if (blocktx) {
+ a_netbuf_free(skb);
+ return 0;
+ }
+#endif /* BLOCK_TX_PATH_FLAG */
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ /* If all data queues are full, notify upper layer to stop. */
+ if (ar->arTotalTxDataPending >= (txFlowCtrlThresh[ar->arNumDataEndPts])) {
+ netif_stop_queue(ar->arNetDev);
+ }
+
+ AR_DEBUG2_PRINTF("ar6000_data_tx start - skb=0x%x, data=0x%x, len=0x%x\n",
+ (A_UINT32)skb, (A_UINT32)a_netbuf_to_data(skb),
+ a_netbuf_to_len(skb));
+
+ if (ar->arWmiEnabled == TRUE) {
+ if (a_netbuf_headroom(skb) < dev->hard_header_len) {
+ /*
+ * We really should have gotten enough headroom but sometimes
+ * we still get packets with not enough headroom. Copy the packet.
+ */
+ len = a_netbuf_to_len(skb);
+ newbuf = a_netbuf_alloc(len);
+ if (newbuf == NULL) {
+ a_netbuf_free(skb);
+ AR6000_STAT_INC(ar, tx_dropped);
+ AR6000_STAT_INC(ar, tx_aborted_errors);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(newbuf, len);
+ A_MEMCPY(a_netbuf_to_data(newbuf), a_netbuf_to_data(skb), len);
+ a_netbuf_free(skb);
+ skb = newbuf;
+ }
+ if (wmi_dix_2_dot3(ar->arWmi, skb) != A_OK) {
+ AR_DEBUG_PRINTF("ar6000_data_tx - wmi_dix_2_dot3 failed\n");
+ AR6000_STAT_INC(ar, tx_dropped);
+ AR6000_STAT_INC(ar, tx_aborted_errors);
+ a_netbuf_free(skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return 0;
+ }
+
+ if (wmi_data_hdr_add(ar->arWmi, skb, DATA_MSGTYPE) != A_OK) {
+ AR_DEBUG_PRINTF("ar6000_data_tx - wmi_data_hdr_add failed\n");
+ AR6000_STAT_INC(ar, tx_dropped);
+ AR6000_STAT_INC(ar, tx_aborted_errors);
+ a_netbuf_free(skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return 0;
+ }
+
+ if ((ar->arNetworkType == ADHOC_NETWORK) &&
+ ar->arIbssPsEnable && ar->arConnected)
+ {
+ endPointId = ar6000_ibss_map_epid(skb, dev, &mapNo);
+ } else {
+ /* Extract the end point information */
+ endPointId = wmi_get_endpoint(ar->arWmi,
+ wmi_implicit_create_pstream(ar->arWmi, skb, UPLINK_TRAFFIC));
+ }
+ } else {
+ /*
+ * the endpoint is directly based on the TOS field in the IP
+ * header
+ */
+ ipHdr = a_netbuf_to_data(skb) + sizeof(ATH_MAC_HDR);
+ endPointId = ((ipHdr->tos >> 1) & 0x03);
+ }
+
+ AR_DEBUG2_PRINTF("ar6000_data_tx - ep=%d skb=0x%x, data=0x%x, len=0x%x\n",
+ endPointId, (A_UINT32)skb, (A_UINT32)a_netbuf_to_data(skb),
+ a_netbuf_to_len(skb));
+
+#ifdef DEBUG
+ if (debugdriver >= 3) {
+ u_char *ch;
+
+ for (ch = a_netbuf_to_data(skb);
+ (A_UINT32)ch < ((A_UINT32)a_netbuf_to_data(skb) +
+ a_netbuf_to_len(skb)); ch++)
+ {
+ AR_DEBUG_PRINTF("%2.2x ", *ch);
+ }
+ AR_DEBUG_PRINTF("\n");
+ }
+#endif /* DEBUG */
+ ar->arTxPending[endPointId]++;
+ ar->arTotalTxDataPending++;
+
+ /* If the particular data queue is full, silently drop the pkt. */
+ if (ar->arTxPending[endPointId] > MAX_ALLOWED_TXQ_DEPTH) {
+ ar->arTxPending[endPointId]--;
+ ar->arTotalTxDataPending--;
+ a_netbuf_free(skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return 0;
+ }
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ cookie = ar6000_alloc_cookie(ar);
+ if (cookie == NULL) {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar->arTxPending[endPointId]--;
+ ar->arTotalTxDataPending--;
+ a_netbuf_free(skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return 0;
+ }
+
+ cookie->arc_bp[0] = (A_UINT32)skb;
+ cookie->arc_bp[1] = mapNo;
+
+ if (HTCBufferSend(ar->arHtcTarget, endPointId, a_netbuf_to_data(skb),
+ a_netbuf_to_len(skb), cookie) != A_OK)
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar->arTxPending[endPointId]--;
+ ar->arTotalTxDataPending--;
+ AR_DEBUG_PRINTF("Dropping the frame\n");
+ ar6000_free_cookie(ar, cookie);
+ a_netbuf_free(skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+
+ return 0;
+}
+
+static void
+ar6000_tx_complete(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)arg;
+ void *cookie = (void *)evInfo->cookie;
+ struct sk_buff *skb = NULL;
+ A_UINT32 mapNo = 0;
+
+ if (ar->arWmiReady == TRUE || bypasswmi != 0)
+ {
+ struct ar_cookie * ar_cookie = (struct ar_cookie *)cookie;
+ skb = (struct sk_buff *)ar_cookie->arc_bp[0];
+ mapNo = ar_cookie->arc_bp[1];
+ } else {
+ skb = (struct sk_buff *)cookie;
+ AR_DEBUG_PRINTF("%s() WARNING Wierd cookie\n", __func__);
+ }
+
+ A_ASSERT(skb);
+ A_ASSERT(evId == HTC_BUFFER_SENT);
+ A_ASSERT(evInfo->buffer == a_netbuf_to_data(skb));
+ if (evInfo->status != A_ECANCELED) {
+ A_ASSERT(evInfo->actualLength == a_netbuf_to_len(skb));
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ ar->arTxPending[eid]--;
+ if (eid != WMI_CONTROL_MBOX || bypasswmi) {
+ ar->arTotalTxDataPending--;
+ }
+ AR_DEBUG2_PRINTF("ar6000_tx_complete skb=0x%x data=0x%x len=0x%x mbox=%d ",
+ (A_UINT32)skb, (A_UINT32)evInfo->buffer,
+ evInfo->actualLength,
+ eid);
+
+ if ((eid == WMI_CONTROL_MBOX) &&
+ (ar->arTxPending[WMI_CONTROL_MBOX] == 0))
+ {
+ wake_up(&arEvent);
+ }
+
+
+ if (evInfo->status != A_OK) {
+ AR_DEBUG_PRINTF("%s() -TX ERROR\n", __func__);
+ AR6000_STAT_INC(ar, tx_errors);
+ } else {
+ AR_DEBUG2_PRINTF("OK\n");
+ AR6000_STAT_INC(ar, tx_packets);
+ ar->arNetStats.tx_bytes += a_netbuf_to_len(skb);
+ }
+
+ if ((ar->arNetworkType == ADHOC_NETWORK) && ar->arIbssPsEnable
+ && (eid != WMI_CONTROL_MBOX) && mapNo)
+ {
+ mapNo --;
+ ar->arNodeMap[mapNo].txPending --;
+
+ if (!ar->arNodeMap[mapNo].txPending && (mapNo == (ar->arNodeNum - 1))) {
+ A_UINT32 i;
+ for (i = ar->arNodeNum; i > 0; i --) {
+ if (!ar->arNodeMap[i - 1].txPending) {
+ A_MEMZERO(&ar->arNodeMap[i - 1], sizeof(struct ar_node_mapping));
+ ar->arNodeNum --;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /* Freeing a cookie should not be contingent on either of */
+ /* these flags, just if we have a cookie or not. */
+ /* Can we even get here without a cookie? Fix later. */
+ if (ar->arWmiReady == TRUE || (bypasswmi))
+ {
+ ar6000_free_cookie(ar, cookie);
+ }
+
+ a_netbuf_free(skb);
+
+ if ((ar->arConnected == TRUE) || (bypasswmi)) {
+ netif_wake_queue(ar->arNetDev);
+ }
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+}
+
+/*
+ * Receive event handler. This is called by HTC when a packet is received
+ */
+int pktcount;
+static void
+ar6000_rx(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)arg;
+ struct sk_buff *skb = (struct sk_buff *)evInfo->cookie;
+ int minHdrLen;
+
+ A_ASSERT(evId == HTC_BUFFER_RECEIVED);
+ A_ASSERT((evInfo->status != A_OK) || (evInfo->buffer == (a_netbuf_to_data(skb) + HTC_HEADER_LEN)));
+
+#ifdef DEBUG
+ AR_DEBUG2_PRINTF("ar6000_rx ar=0x%x ep=%d, skb=0x%x, data=0x%x, len=0x%x ",
+ (A_UINT32)ar, eid, (A_UINT32)skb, (A_UINT32)evInfo->buffer,
+ evInfo->actualLength);
+ if (evInfo->status != A_OK) {
+ AR_DEBUG2_PRINTF("ERR\n");
+ } else {
+ AR_DEBUG2_PRINTF("OK\n");
+ }
+#endif /* DEBUG */
+
+ ar->arRxBuffers[eid]--;
+ AR6000_STAT_INC(ar, rx_packets);
+ ar->arNetStats.rx_bytes += evInfo->actualLength;
+
+ a_netbuf_put(skb, evInfo->actualLength + HTC_HEADER_LEN);
+
+ a_netbuf_pull(skb, HTC_HEADER_LEN);
+
+#ifdef DEBUG
+ if (debugdriver >= 2) {
+ u_char *ch;
+
+ for (ch = a_netbuf_to_data(skb);
+ (A_UINT32)ch < ((A_UINT32)a_netbuf_to_data(skb) +
+ a_netbuf_to_len(skb)); ch++)
+ {
+ AR_DEBUG_PRINTF("%2.2x ", *ch);
+ }
+ AR_DEBUG_PRINTF("\n");
+ }
+#endif /* DEBUG */
+
+ if (evInfo->status != A_OK) {
+ AR6000_STAT_INC(ar, rx_errors);
+ a_netbuf_free(skb);
+ } else if (ar->arWmiEnabled == TRUE) {
+ if (eid == WMI_CONTROL_MBOX) {
+ /*
+ * this is a wmi control msg
+ */
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ wmi_control_rx(ar->arWmi, skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ } else {
+ WMI_DATA_HDR *dhdr = (WMI_DATA_HDR *)a_netbuf_to_data(skb);
+ if (WMI_DATA_HDR_IS_MSG_TYPE(dhdr, CNTL_MSGTYPE)) {
+ /*
+ * this is a wmi control msg
+ */
+ /* strip off WMI hdr */
+ wmi_data_hdr_remove(ar->arWmi, skb);
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ wmi_control_rx(ar->arWmi, skb);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ } else {
+ /*
+ * this is a wmi data packet
+ */
+ minHdrLen = sizeof (WMI_DATA_HDR) + sizeof(ATH_MAC_HDR) +
+ sizeof(ATH_LLC_SNAP_HDR);
+
+ if ((evInfo->actualLength < minHdrLen) ||
+ (evInfo->actualLength > AR6000_BUFFER_SIZE))
+ {
+ /*
+ * packet is too short or too long
+ */
+ AR_DEBUG_PRINTF("TOO SHORT or TOO LONG\n");
+ AR6000_STAT_INC(ar, rx_errors);
+ AR6000_STAT_INC(ar, rx_length_errors);
+ a_netbuf_free(skb);
+ } else {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ wmi_implicit_create_pstream(ar->arWmi, skb, DNLINK_TRAFFIC);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+#if 0
+ /* Access RSSI values here */
+ AR_DEBUG_PRINTF("RSSI %d\n",
+ ((WMI_DATA_HDR *) a_netbuf_to_data(skb))->rssi);
+#endif
+ wmi_data_hdr_remove(ar->arWmi, skb);
+ wmi_dot3_2_dix(ar->arWmi, skb);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+ /*
+ * extra push and memcpy, for eth_type_trans() of 2.4 kernel
+ * will pull out hard_header_len bytes of the skb.
+ */
+ a_netbuf_push(skb, sizeof(WMI_DATA_HDR) + sizeof(ATH_LLC_SNAP_HDR) + HTC_HEADER_LEN);
+ A_MEMCPY(a_netbuf_to_data(skb), a_netbuf_to_data(skb) + sizeof(WMI_DATA_HDR) +
+ sizeof(ATH_LLC_SNAP_HDR) + HTC_HEADER_LEN, sizeof(ATH_MAC_HDR));
+#endif
+ if ((ar->arNetDev->flags & IFF_UP) == IFF_UP)
+ {
+ skb->dev = ar->arNetDev;
+ skb->protocol = eth_type_trans(skb, ar->arNetDev);
+ netif_rx(skb);
+ }
+ else
+ {
+ a_netbuf_free(skb);
+ }
+ }
+ }
+ }
+ } else {
+ if ((ar->arNetDev->flags & IFF_UP) == IFF_UP)
+ {
+ skb->dev = ar->arNetDev;
+ skb->protocol = eth_type_trans(skb, ar->arNetDev);
+ netif_rx(skb);
+ }
+ else
+ {
+ a_netbuf_free(skb);
+ }
+ }
+
+ if (evInfo->status != A_ECANCELED) {
+ /*
+ * HTC provides A_ECANCELED status when it doesn't want to be refilled
+ * (probably due to a shutdown)
+ */
+ ar6000_rx_refill(htcTarget, eid, HTC_DATA_AVAILABLE, NULL, ar);
+ }
+}
+
+static void
+ar6000_rx_refill(HTC_TARGET *htcTarget, HTC_ENDPOINT_ID eid,
+ HTC_EVENT_ID evId, HTC_EVENT_INFO *evInfo, void *arg)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)arg;
+ void *osBuf;
+ int arRxBuffers;
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ arRxBuffers = ar->arRxBuffers[eid];
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ AR_DEBUG2_PRINTF("ar6000_rx_refill: providing htc with %d buffers at eid=%d\n",
+ AR6000_MAX_RX_BUFFERS - arRxBuffers, eid);
+ while (arRxBuffers < AR6000_MAX_RX_BUFFERS) {
+ osBuf = a_netbuf_alloc(AR6000_BUFFER_SIZE);
+ HTCBufferReceive(htcTarget, eid, a_netbuf_to_data(osBuf),
+ AR6000_BUFFER_SIZE, osBuf);
+ arRxBuffers++;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ar->arRxBuffers[eid] = arRxBuffers;
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+}
+
+static struct net_device_stats *
+ar6000_get_stats(struct net_device *dev)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ return &ar->arNetStats;
+}
+#if 0
+static struct iw_statistics *
+ar6000_get_iwstats(struct net_device * dev)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ TARGET_STATS *pStats = &ar->arTargetStats;
+ struct iw_statistics * pIwStats = &ar->arIwStats;
+
+ if (ar->arWmiReady == FALSE) {
+ pIwStats->status = 0;
+ pIwStats->qual.qual = 0;
+ pIwStats->qual.level =0;
+ pIwStats->qual.noise = 0;
+ pIwStats->discard.code =0;
+ pIwStats->discard.retries=0;
+ pIwStats->miss.beacon =0;
+ return pIwStats;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ pIwStats->status = 0;
+ return pIwStats;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ ar->statsUpdatePending = TRUE;
+
+ if(wmi_get_stats_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ pIwStats->status = 0;
+ return pIwStats;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ wait_event_interruptible(arEvent, ar->statsUpdatePending == FALSE);
+
+ if (signal_pending(current)) {
+ pIwStats->status = 0;
+ return pIwStats;
+ }
+ pIwStats->status = 1 ;
+ pIwStats->qual.qual = pStats->cs_aveBeacon_rssi;
+ pIwStats->qual.level =pStats->cs_aveBeacon_rssi + 161; /* noise is -95 dBm */
+ pIwStats->qual.noise = pStats->noise_floor_calibation;
+ pIwStats->discard.code = pStats->rx_decrypt_err;
+ pIwStats->discard.retries = pStats->tx_retry_cnt;
+ pIwStats->miss.beacon = pStats->cs_bmiss_cnt;
+ up(&ar->arSem);
+ return pIwStats;
+}
+#endif
+
+void
+ar6000_ready_event(void *devt, A_UINT8 *datap, A_UINT8 phyCap)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+ struct net_device *dev = ar->arNetDev;
+
+ ar->arWmiReady = TRUE;
+ wake_up(&arEvent);
+ A_MEMCPY(dev->dev_addr, datap, AR6000_ETH_ADDR_LEN);
+ AR_DEBUG_PRINTF("mac address = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+ dev->dev_addr[0], dev->dev_addr[1],
+ dev->dev_addr[2], dev->dev_addr[3],
+ dev->dev_addr[4], dev->dev_addr[5]);
+
+ ar->arPhyCapability = phyCap;
+}
+
+A_UINT8
+ar6000_iptos_to_userPriority(A_UINT8 *pkt)
+{
+ struct iphdr *ipHdr = (struct iphdr *)pkt;
+ A_UINT8 userPriority;
+
+ /*
+ * IP Tos format :
+ * (Refer Pg 57 WMM-test-plan-v1.2)
+ * IP-TOS - 8bits
+ * : DSCP(6-bits) ECN(2-bits)
+ * : DSCP - P2 P1 P0 X X X
+ * where (P2 P1 P0) form 802.1D
+ */
+ userPriority = ipHdr->tos >> 5;
+ return (userPriority & 0x7);
+}
+
+void
+ar6000_connect_event(AR_SOFTC_T *ar, A_UINT16 channel, A_UINT8 *bssid,
+ A_UINT16 listenInterval, A_UINT8 beaconIeLen,
+ A_UINT8 assocReqLen, A_UINT8 assocRespLen,
+ A_UINT8 *assocInfo)
+{
+ union iwreq_data wrqu;
+ int i, beacon_ie_pos, assoc_resp_ie_pos, assoc_req_ie_pos;
+ static const char *tag1 = "ASSOCINFO(ReqIEs=";
+ static const char *tag2 = "ASSOCRESPIE=";
+ static const char *beaconIetag = "BEACONIE=";
+ char buf[WMI_CONTROL_MSG_MAX_LEN * 2 + sizeof(tag1)];
+ char *pos;
+
+ A_MEMCPY(ar->arBssid, bssid, sizeof(ar->arBssid));
+ ar->arBssChannel = channel;
+
+ A_PRINTF("AR6000 connected event on freq %d ", channel);
+ A_PRINTF("with bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+ " listenInterval=%d, beaconIeLen = %d assocReqLen=%d"
+ " assocRespLen =%d\n",
+ bssid[0], bssid[1], bssid[2],
+ bssid[3], bssid[4], bssid[5],
+ listenInterval,
+ beaconIeLen, assocReqLen, assocRespLen);
+
+ if (beaconIeLen && (sizeof(buf) > (9 + beaconIeLen * 2))) {
+ AR_DEBUG_PRINTF("\nBeaconIEs= ");
+
+ beacon_ie_pos = 0;
+ A_MEMZERO(buf, sizeof(buf));
+ sprintf(buf, "%s", beaconIetag);
+ pos = buf + 9;
+ for (i = beacon_ie_pos; i < beacon_ie_pos + beaconIeLen; i++) {
+ AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]);
+ sprintf(pos, "%2.2x", assocInfo[i]);
+ pos += 2;
+ }
+ AR_DEBUG_PRINTF("\n");
+
+ A_MEMZERO(&wrqu, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
+ }
+
+ if (assocRespLen && (sizeof(buf) > (12 + (assocRespLen * 2))))
+ {
+ assoc_resp_ie_pos = beaconIeLen + assocReqLen +
+ sizeof(A_UINT16) + /* capinfo*/
+ sizeof(A_UINT16) + /* status Code */
+ sizeof(A_UINT16) ; /* associd */
+ A_MEMZERO(buf, sizeof(buf));
+ sprintf(buf, "%s", tag2);
+ pos = buf + 12;
+ AR_DEBUG_PRINTF("\nAssocRespIEs= ");
+ /*
+ * The Association Response Frame w.o. the WLAN header is delivered to
+ * the host, so skip over to the IEs
+ */
+ for (i = assoc_resp_ie_pos; i < assoc_resp_ie_pos + assocRespLen; i++)
+ {
+ AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]);
+ sprintf(pos, "%2.2x", assocInfo[i]);
+ pos += 2;
+ }
+ AR_DEBUG_PRINTF("\n");
+
+ A_MEMZERO(&wrqu, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
+ }
+
+ if (assocReqLen && (sizeof(buf) > (17 + (assocReqLen * 2)))) {
+ /*
+ * assoc Request includes capability and listen interval. Skip these.
+ */
+ assoc_req_ie_pos = beaconIeLen +
+ sizeof(A_UINT16) + /* capinfo*/
+ sizeof(A_UINT16); /* listen interval */
+
+ A_MEMZERO(buf, sizeof(buf));
+ sprintf(buf, "%s", tag1);
+ pos = buf + 17;
+ AR_DEBUG_PRINTF("AssocReqIEs= ");
+ for (i = assoc_req_ie_pos; i < assoc_req_ie_pos + assocReqLen; i++) {
+ AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]);
+ sprintf(pos, "%2.2x", assocInfo[i]);
+ pos += 2;;
+ }
+ AR_DEBUG_PRINTF("\n");
+
+ A_MEMZERO(&wrqu, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
+ }
+
+ if (ar->arTotalTxDataPending < txFlowCtrlThresh[ar->arNumDataEndPts]) {
+ netif_wake_queue(ar->arNetDev);
+ }
+
+ if ((OPEN_AUTH == ar->arDot11AuthMode) &&
+ (NONE_AUTH == ar->arAuthMode) &&
+ (WEP_CRYPT == ar->arPairwiseCrypto))
+ {
+ if (!ar->arConnected) {
+ ar6000_install_static_wep_keys(ar);
+ }
+ }
+
+ ar->arConnected = TRUE;
+ ar->arConnectPending = FALSE;
+
+ A_MEMZERO(&wrqu, sizeof(wrqu));
+ A_MEMCPY(wrqu.addr.sa_data, bssid, IEEE80211_ADDR_LEN);
+ wrqu.addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(ar->arNetDev, SIOCGIWAP, &wrqu, NULL);
+
+ if ((ar->arNetworkType == ADHOC_NETWORK) && ar->arIbssPsEnable) {
+ A_MEMZERO(ar->arNodeMap, sizeof(ar->arNodeMap));
+ ar->arNodeNum = 0;
+ ar->arNexEpId = ENDPOINT2;
+ }
+}
+
+void ar6000_set_numdataendpts(AR_SOFTC_T *ar, A_UINT32 num)
+{
+ A_ASSERT(num <= (HTC_MAILBOX_NUM_MAX - 1));
+ ar->arNumDataEndPts = num;
+}
+
+void
+ar6000_disconnect_event(AR_SOFTC_T *ar, A_UINT8 reason, A_UINT8 *bssid,
+ A_UINT8 assocRespLen, A_UINT8 *assocInfo)
+{
+ A_UINT8 i;
+
+ A_PRINTF("AR6000 disconnected");
+ if (bssid[0] || bssid[1] || bssid[2] || bssid[3] || bssid[4] || bssid[5]) {
+ A_PRINTF(" from %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ",
+ bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+ }
+
+ AR_DEBUG_PRINTF("\nAssocResp Frame = %s",
+ assocRespLen ? " " : "NULL");
+ for (i = 0; i < assocRespLen; i++) {
+ if (!(i % 0x10)) {
+ AR_DEBUG_PRINTF("\n");
+ }
+ AR_DEBUG_PRINTF("%2.2x ", assocInfo[i]);
+ }
+ AR_DEBUG_PRINTF("\n");
+ /*
+ * If the event is due to disconnect cmd from the host, only they the target
+ * would stop trying to connect. Under any other condition, target would
+ * keep trying to connect.
+ *
+ */
+ if( reason == DISCONNECT_CMD)
+ {
+ ar->arConnectPending = FALSE;
+ }
+ ar->arConnected = FALSE;
+
+ netif_stop_queue(ar->arNetDev);
+ A_MEMZERO(ar->arBssid, sizeof(ar->arBssid));
+ ar->arBssChannel = 0;
+}
+
+void
+ar6000_regDomain_event(AR_SOFTC_T *ar, A_UINT32 regCode)
+{
+ A_PRINTF("AR6000 Reg Code = 0x%x\n", regCode);
+ ar->arRegCode = regCode;
+}
+
+void
+ar6000_neighborReport_event(AR_SOFTC_T *ar, int numAps, WMI_NEIGHBOR_INFO *info)
+{
+ static const char *tag = "PRE-AUTH";
+ char buf[128];
+ union iwreq_data wrqu;
+ int i;
+
+ A_PRINTF("AR6000 Neighbor Report Event\n");
+ for (i=0; i < numAps; info++, i++) {
+ A_PRINTF("bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ",
+ info->bssid[0], info->bssid[1], info->bssid[2],
+ info->bssid[3], info->bssid[4], info->bssid[5]);
+ if (info->bssFlags & WMI_PREAUTH_CAPABLE_BSS) {
+ A_PRINTF("preauth-cap");
+ }
+ if (info->bssFlags & WMI_PMKID_VALID_BSS) {
+ A_PRINTF(" pmkid-valid\n");
+ continue; /* we skip bss if the pmkid is already valid */
+ }
+ A_PRINTF("\n");
+ snprintf(buf, sizeof(buf), "%s%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
+ tag,
+ info->bssid[0], info->bssid[1], info->bssid[2],
+ info->bssid[3], info->bssid[4], info->bssid[5],
+ i, info->bssFlags);
+ A_MEMZERO(&wrqu, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
+ }
+}
+
+void
+ar6000_tkip_micerr_event(AR_SOFTC_T *ar, A_UINT8 keyid, A_BOOL ismcast)
+{
+ static const char *tag = "MLME-MICHAELMICFAILURE.indication";
+ char buf[128];
+ union iwreq_data wrqu;
+
+ A_PRINTF("AR6000 TKIP MIC error received for keyid %d %scast\n",
+ keyid, ismcast ? "multi": "uni");
+ snprintf(buf, sizeof(buf), "%s(keyid=%d %scat)", tag, keyid,
+ ismcast ? "multi" : "uni");
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = strlen(buf);
+ wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
+}
+
+void
+ar6000_scanComplete_event(AR_SOFTC_T *ar)
+{
+ A_PRINTF("AR6000 scan complete\n");
+}
+
+void
+ar6000_targetStats_event(AR_SOFTC_T *ar, WMI_TARGET_STATS *pTarget)
+{
+ TARGET_STATS *pStats = &ar->arTargetStats;
+ A_UINT8 ac;
+
+ A_PRINTF("AR6000 updating target stats\n");
+ pStats->tx_packets += pTarget->txrxStats.tx_stats.tx_packets;
+ pStats->tx_bytes += pTarget->txrxStats.tx_stats.tx_bytes;
+ pStats->tx_unicast_pkts += pTarget->txrxStats.tx_stats.tx_unicast_pkts;
+ pStats->tx_unicast_bytes += pTarget->txrxStats.tx_stats.tx_unicast_bytes;
+ pStats->tx_multicast_pkts += pTarget->txrxStats.tx_stats.tx_multicast_pkts;
+ pStats->tx_multicast_bytes += pTarget->txrxStats.tx_stats.tx_multicast_bytes;
+ pStats->tx_broadcast_pkts += pTarget->txrxStats.tx_stats.tx_broadcast_pkts;
+ pStats->tx_broadcast_bytes += pTarget->txrxStats.tx_stats.tx_broadcast_bytes;
+ pStats->tx_rts_success_cnt += pTarget->txrxStats.tx_stats.tx_rts_success_cnt;
+ for(ac = 0; ac < WMM_NUM_AC; ac++)
+ pStats->tx_packet_per_ac[ac] += pTarget->txrxStats.tx_stats.tx_packet_per_ac[ac];
+ pStats->tx_errors += pTarget->txrxStats.tx_stats.tx_errors;
+ pStats->tx_failed_cnt += pTarget->txrxStats.tx_stats.tx_failed_cnt;
+ pStats->tx_retry_cnt += pTarget->txrxStats.tx_stats.tx_retry_cnt;
+ pStats->tx_rts_fail_cnt += pTarget->txrxStats.tx_stats.tx_rts_fail_cnt;
+
+ pStats->rx_packets += pTarget->txrxStats.rx_stats.rx_packets;
+ pStats->rx_bytes += pTarget->txrxStats.rx_stats.rx_bytes;
+ pStats->rx_unicast_pkts += pTarget->txrxStats.rx_stats.rx_unicast_pkts;
+ pStats->rx_unicast_bytes += pTarget->txrxStats.rx_stats.rx_unicast_bytes;
+ pStats->rx_multicast_pkts += pTarget->txrxStats.rx_stats.rx_multicast_pkts;
+ pStats->rx_multicast_bytes += pTarget->txrxStats.rx_stats.rx_multicast_bytes;
+ pStats->rx_broadcast_pkts += pTarget->txrxStats.rx_stats.rx_broadcast_pkts;
+ pStats->rx_broadcast_bytes += pTarget->txrxStats.rx_stats.rx_broadcast_bytes;
+ pStats->rx_fragment_pkt += pTarget->txrxStats.rx_stats.rx_fragment_pkt;
+ pStats->rx_errors += pTarget->txrxStats.rx_stats.rx_errors;
+ pStats->rx_crcerr += pTarget->txrxStats.rx_stats.rx_crcerr;
+ pStats->rx_key_cache_miss += pTarget->txrxStats.rx_stats.rx_key_cache_miss;
+ pStats->rx_decrypt_err += pTarget->txrxStats.rx_stats.rx_decrypt_err;
+ pStats->rx_duplicate_frames += pTarget->txrxStats.rx_stats.rx_duplicate_frames;
+
+
+ pStats->tkip_local_mic_failure
+ += pTarget->txrxStats.tkipCcmpStats.tkip_local_mic_failure;
+ pStats->tkip_counter_measures_invoked
+ += pTarget->txrxStats.tkipCcmpStats.tkip_counter_measures_invoked;
+ pStats->tkip_replays += pTarget->txrxStats.tkipCcmpStats.tkip_replays;
+ pStats->tkip_format_errors += pTarget->txrxStats.tkipCcmpStats.tkip_format_errors;
+ pStats->ccmp_format_errors += pTarget->txrxStats.tkipCcmpStats.ccmp_format_errors;
+ pStats->ccmp_replays += pTarget->txrxStats.tkipCcmpStats.ccmp_replays;
+
+
+ pStats->power_save_failure_cnt += pTarget->pmStats.power_save_failure_cnt;
+ pStats->noise_floor_calibation = pTarget->noise_floor_calibation;
+
+ pStats->cs_bmiss_cnt += pTarget->cservStats.cs_bmiss_cnt;
+ pStats->cs_lowRssi_cnt += pTarget->cservStats.cs_lowRssi_cnt;
+ pStats->cs_connect_cnt += pTarget->cservStats.cs_connect_cnt;
+ pStats->cs_disconnect_cnt += pTarget->cservStats.cs_disconnect_cnt;
+ pStats->cs_aveBeacon_rssi = pTarget->cservStats.cs_aveBeacon_rssi;
+ pStats->cs_lastRoam_msec = pTarget->cservStats.cs_lastRoam_msec;
+
+ ar->statsUpdatePending = FALSE;
+ wake_up(&arEvent);
+}
+
+
+void
+ar6000_rssiThreshold_event(AR_SOFTC_T *ar, WMI_RSSI_THRESHOLD_VAL newThreshold)
+{
+ A_PRINTF("AR6000 Threshold val = %d \n", newThreshold);
+}
+
+void
+ar6000_reportError_event(AR_SOFTC_T *ar, WMI_TARGET_ERROR_VAL errorVal)
+{
+ char *errString[] = {
+ [WMI_TARGET_PM_ERR_FAIL] "WMI_TARGET_PM_ERR_FAIL",
+ [WMI_TARGET_KEY_NOT_FOUND] "WMI_TARGET_KEY_NOT_FOUND",
+ [WMI_TARGET_DECRYPTION_ERR] "WMI_TARGET_DECRYPTION_ERR",
+ [WMI_TARGET_BMISS] "WMI_TARGET_BMISS",
+ [WMI_PSDISABLE_NODE_JOIN] "WMI_PSDISABLE_NODE_JOIN"
+ };
+
+ A_PRINTF("AR6000 Error on Target. Error = 0x%x\n", errorVal);
+
+ /* One error is reported at a time, and errorval is a bitmask */
+ if(errorVal & (errorVal - 1))
+ return;
+
+ A_PRINTF("AR6000 Error type = ");
+ switch(errorVal)
+ {
+ case WMI_TARGET_PM_ERR_FAIL:
+ case WMI_TARGET_KEY_NOT_FOUND:
+ case WMI_TARGET_DECRYPTION_ERR:
+ case WMI_TARGET_BMISS:
+ case WMI_PSDISABLE_NODE_JOIN:
+ A_PRINTF("%s\n", errString[errorVal]);
+ break;
+ default:
+ A_PRINTF("INVALID\n");
+ break;
+ }
+
+}
+
+
+void
+ar6000_cac_event(AR_SOFTC_T *ar, A_UINT8 ac, A_UINT8 cacIndication,
+ A_UINT8 statusCode, A_UINT8 *tspecSuggestion)
+{
+ WMM_TSPEC_IE *tspecIe;
+
+ /*
+ * This is the TSPEC IE suggestion from AP.
+ * Suggestion provided by AP under some error
+ * cases, could be helpful for the host app.
+ * Check documentation.
+ */
+ tspecIe = (WMM_TSPEC_IE *)tspecSuggestion;
+
+ /*
+ * What do we do, if we get TSPEC rejection? One thought
+ * that comes to mind is implictly delete the pstream...
+ */
+ A_PRINTF("AR6000 CAC notification. "
+ "AC = %d, cacIndication = 0x%x, statusCode = 0x%x\n",
+ ac, cacIndication, statusCode);
+}
+
+#define AR6000_PRINT_BSSID(_pBss) do { \
+ A_PRINTF("%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ",\
+ (_pBss)[0],(_pBss)[1],(_pBss)[2],(_pBss)[3],\
+ (_pBss)[4],(_pBss)[5]); \
+} while(0)
+
+void
+ar6000_roam_tbl_event(AR_SOFTC_T *ar, WMI_TARGET_ROAM_TBL *pTbl)
+{
+ A_UINT8 i;
+
+ A_PRINTF("ROAM TABLE NO OF ENTRIES is %d ROAM MODE is %d\n",
+ pTbl->numEntries, pTbl->roamMode);
+ for (i= 0; i < pTbl->numEntries; i++) {
+ A_PRINTF("[%d]bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x ", i,
+ pTbl->bssRoamInfo[i].bssid[0], pTbl->bssRoamInfo[i].bssid[1],
+ pTbl->bssRoamInfo[i].bssid[2],
+ pTbl->bssRoamInfo[i].bssid[3],
+ pTbl->bssRoamInfo[i].bssid[4],
+ pTbl->bssRoamInfo[i].bssid[5]);
+ A_PRINTF("RSSI %d RSSIDT %d LAST RSSI %d UTIL %d ROAM_UTIL %d"
+ " BIAS %d\n",
+ pTbl->bssRoamInfo[i].rssi,
+ pTbl->bssRoamInfo[i].rssidt,
+ pTbl->bssRoamInfo[i].last_rssi,
+ pTbl->bssRoamInfo[i].util,
+ pTbl->bssRoamInfo[i].roam_util,
+ pTbl->bssRoamInfo[i].bias);
+ }
+}
+
+/*
+ * Report the Roaming related data collected on the target
+ */
+void
+ar6000_display_roam_time(WMI_TARGET_ROAM_TIME *p)
+{
+ A_PRINTF("Disconnect Data : BSSID: ");
+ AR6000_PRINT_BSSID(p->disassoc_bssid);
+ A_PRINTF(" RSSI %d DISASSOC Time %d NO_TXRX_TIME %d\n",
+ p->disassoc_bss_rssi,p->disassoc_time,
+ p->no_txrx_time);
+ A_PRINTF("Connect Data: BSSID: ");
+ AR6000_PRINT_BSSID(p->assoc_bssid);
+ A_PRINTF(" RSSI %d ASSOC Time %d TXRX_TIME %d\n",
+ p->assoc_bss_rssi,p->assoc_time,
+ p->allow_txrx_time);
+ A_PRINTF("Last Data Tx Time (b4 Disassoc) %d "\
+ "First Data Tx Time (after Assoc) %d\n",
+ p->last_data_txrx_time, p->first_data_txrx_time);
+}
+
+void
+ar6000_roam_data_event(AR_SOFTC_T *ar, WMI_TARGET_ROAM_DATA *p)
+{
+ switch (p->roamDataType) {
+ case ROAM_DATA_TIME:
+ ar6000_display_roam_time(&p->u.roamTime);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+ar6000_ioctl_set_error_report_bitmask(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_TARGET_ERROR_REPORT_BITMASK cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ ret = wmi_set_error_report_bitmask(ar->arWmi, cmd.bitmask);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return (ret==0 ? ret : -EINVAL);
+}
+
+
+static int
+ar6000_ioctl_get_target_stats(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ TARGET_STATS *pStats = &ar->arTargetStats;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ ar->statsUpdatePending = TRUE;
+
+ if(wmi_get_stats_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ wait_event_interruptible(arEvent, ar->statsUpdatePending == FALSE);
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ }
+
+ if (!ret && copy_to_user(rq->ifr_data, pStats, sizeof(*pStats))) {
+ ret = -EFAULT;
+ }
+
+ up(&ar->arSem);
+
+ return ret;
+}
+
+static int
+ar6000_ioctl_set_access_params(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_SET_ACCESS_PARAMS_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_set_access_params_cmd(ar->arWmi, cmd.txop, cmd.eCWmin, cmd.eCWmax,
+ cmd.aifsn) == A_OK)
+ {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return (ret);
+}
+
+static int
+ar6000_ioctl_set_disconnect_timeout(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_DISC_TIMEOUT_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, rq->ifr_data, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_disctimeout_cmd(ar->arWmi, cmd.disconnectTimeout) == A_OK)
+ {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return (ret);
+}
+
+static int
+ar6000_xioctl_set_voice_pkt_size(struct net_device *dev, char * userdata)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_SET_VOICE_PKT_SIZE_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, userdata, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_set_voice_pkt_size_cmd(ar->arWmi, cmd.voicePktSize) == A_OK)
+ {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return (ret);
+}
+
+static int
+ar6000_xioctl_set_max_sp_len(struct net_device *dev, char * userdata)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_SET_MAX_SP_LEN_CMD cmd;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, userdata, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_set_max_sp_len_cmd(ar->arWmi, cmd.maxSPLen) == A_OK)
+ {
+ ret = 0;
+ } else {
+ ret = -EINVAL;
+ }
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return (ret);
+}
+
+A_STATUS
+ar6000_control_tx(void *devt, void *osbuf, int endPt)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+ A_STATUS status;
+ struct ar_cookie *cookie;
+ WMI_DATA_HDR *dhdr;
+
+ A_ASSERT((endPt == WMI_CONTROL_MBOX) || (endPt == WMI_LOW_PRIORITY_MBOX) ||
+ (endPt == WMI_HIGH_PRIORITY_MBOX) || (endPt == WMI_BEST_EFFORT_MBOX));
+
+ ar->arTxPending[endPt]++;
+ if (endPt != WMI_CONTROL_MBOX) {
+ ar->arTotalTxDataPending++;
+ }
+
+ AR_DEBUG2_PRINTF("ar_control_tx: skb=0x%x, len=0x%x, mbox=%d\n",
+ (A_UINT32)osbuf, a_netbuf_to_len(osbuf), endPt);
+
+ cookie = ar6000_alloc_cookie(ar);
+
+ /* If the particular data queue is full, silently drop the pkt. */
+ dhdr = (WMI_DATA_HDR *)a_netbuf_to_data(osbuf);
+ if ((cookie == NULL) ||
+ ((ar->arTxPending[endPt] > MAX_ALLOWED_TXQ_DEPTH) &&
+ (!WMI_DATA_HDR_IS_MSG_TYPE(dhdr, SYNC_MSGTYPE))))
+ {
+ ar->arTxPending[endPt]--;
+ if (endPt != WMI_CONTROL_MBOX) {
+ ar->arTotalTxDataPending--;
+ }
+ a_netbuf_free(osbuf);
+ if (cookie != NULL) {
+ ar6000_free_cookie(ar, cookie);
+ }
+ return A_NO_MEMORY;
+ }
+
+ cookie->arc_bp[0] = (A_UINT32)osbuf;
+ cookie->arc_bp[1] = 0;
+ status = HTCBufferSend(ar->arHtcTarget, endPt, a_netbuf_to_data(osbuf),
+ a_netbuf_to_len(osbuf), cookie);
+ if (status != A_OK) {
+ /*
+ * XXX This is an error that requires us to reset chip
+ */
+ ar->arTxPending[endPt]--;
+ if (endPt != WMI_CONTROL_MBOX) {
+ ar->arTotalTxDataPending--;
+ }
+ AR_DEBUG_PRINTF("Dropping control frame!!\n");
+ ar6000_free_cookie(ar, cookie);
+ a_netbuf_free(osbuf);
+ return (status);
+ }
+
+ return A_OK;
+}
+
+module_init(ar6000_init_module);
+module_exit(ar6000_cleanup_module);
+
+/*
+ * SIOCGIWNAME
+ */
+int
+ar6000_ioctl_giwname(struct net_device *dev,
+ struct iw_request_info *info,
+ char *name, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ switch (ar->arPhyCapability) {
+ case (WMI_11A_CAPABILITY):
+ strncpy(name, "AR6000 802.11a", IFNAMSIZ);
+ break;
+ case (WMI_11G_CAPABILITY):
+ strncpy(name, "AR6000 802.11g", IFNAMSIZ);
+ break;
+ case (WMI_11AG_CAPABILITY):
+ strncpy(name, "AR6000 802.11ag", IFNAMSIZ);
+ break;
+ default:
+ strncpy(name, "AR6000 802.11", IFNAMSIZ);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * SIOCSIWFREQ
+ */
+int
+ar6000_ioctl_siwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ /*
+ * We support limiting the channels via wmiconfig.
+ *
+ * We use this command to configure the channel hint for the connect cmd
+ * so it is possible the target will end up connecting to a different
+ * channel.
+ */
+ if (freq->e > 1) {
+ return -EINVAL;
+ } else if (freq->e == 1) {
+ ar->arChannelHint = freq->m / 100000;
+ } else {
+ ar->arChannelHint = wlan_ieee2freq(freq->m);
+ }
+
+ A_PRINTF("channel hint set to %d\n", ar->arChannelHint);
+ return 0;
+}
+
+/*
+ * SIOCGIWFREQ
+ */
+int
+ar6000_ioctl_giwfreq(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_freq *freq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ar->arConnected != TRUE) {
+ return -EINVAL;
+ }
+
+ freq->m = ar->arBssChannel * 100000;
+ freq->e = 1;
+
+ return 0;
+}
+
+/*
+ * SIOCSIWMODE
+ */
+int
+ar6000_ioctl_siwmode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *mode, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ switch (*mode) {
+ case IW_MODE_INFRA:
+ ar->arNetworkType = INFRA_NETWORK;
+ break;
+ case IW_MODE_ADHOC:
+ ar->arNetworkType = ADHOC_NETWORK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * SIOCGIWMODE
+ */
+int
+ar6000_ioctl_giwmode(struct net_device *dev,
+ struct iw_request_info *info,
+ __u32 *mode, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ switch (ar->arNetworkType) {
+ case INFRA_NETWORK:
+ *mode = IW_MODE_INFRA;
+ break;
+ case ADHOC_NETWORK:
+ *mode = IW_MODE_ADHOC;
+ break;
+ default:
+ return -EIO;
+ }
+ return 0;
+}
+
+/*
+ * SIOCSIWSENS
+ */
+int
+ar6000_ioctl_siwsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *sens, char *extra)
+{
+ return 0;
+}
+
+/*
+ * SIOCGIWSENS
+ */
+int
+ar6000_ioctl_giwsens(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *sens, char *extra)
+{
+ sens->value = 0;
+ sens->fixed = 1;
+
+ return 0;
+}
+
+/*
+ * SIOCGIWRANGE
+ */
+int
+ar6000_ioctl_giwrange(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct iw_range *range = (struct iw_range *) extra;
+ int i, ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+ ar->arNumChannels = -1;
+ A_MEMZERO(ar->arChannelList, sizeof (ar->arChannelList));
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_get_channelList_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ wait_event_interruptible(arEvent, ar->arNumChannels != -1);
+
+ if (signal_pending(current)) {
+ up(&ar->arSem);
+ return -EINTR;
+ }
+
+ data->length = sizeof(struct iw_range);
+ A_MEMZERO(range, sizeof(struct iw_range));
+
+ range->txpower_capa = 0;
+
+ range->min_pmp = 1 * 1024;
+ range->max_pmp = 65535 * 1024;
+ range->min_pmt = 1 * 1024;
+ range->max_pmt = 1000 * 1024;
+ range->pmp_flags = IW_POWER_PERIOD;
+ range->pmt_flags = IW_POWER_TIMEOUT;
+ range->pm_capa = 0;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = 13;
+
+ range->retry_capa = IW_RETRY_LIMIT;
+ range->retry_flags = IW_RETRY_LIMIT;
+ range->min_retry = 0;
+ range->max_retry = 255;
+
+ range->num_frequency = range->num_channels = ar->arNumChannels;
+ for (i = 0; i < ar->arNumChannels; i++) {
+ range->freq[i].i = wlan_freq2ieee(ar->arChannelList[i]);
+ range->freq[i].m = ar->arChannelList[i] * 100000;
+ range->freq[i].e = 1;
+ }
+
+ /* Max quality is max field value minus noise floor */
+ range->max_qual.qual = 0xff - 161;
+
+ /*
+ * In order to use dBm measurements, 'level' must be lower
+ * than any possible measurement (see iw_print_stats() in
+ * wireless tools). It's unclear how this is meant to be
+ * done, but setting zero in these values forces dBm and
+ * the actual numbers are not used.
+ */
+ range->max_qual.level = 0;
+ range->max_qual.noise = 0;
+
+ range->sensitivity = 3;
+
+ range->max_encoding_tokens = 4;
+ /* XXX query driver to find out supported key sizes */
+ range->num_encoding_sizes = 3;
+ range->encoding_size[0] = 5; /* 40-bit */
+ range->encoding_size[1] = 13; /* 104-bit */
+ range->encoding_size[2] = 16; /* 128-bit */
+
+ range->num_bitrates = 0;
+
+ /* estimated maximum TCP throughput values (bps) */
+ range->throughput = 22000000;
+
+ range->min_rts = 0;
+ range->max_rts = 2347;
+ range->min_frag = 256;
+ range->max_frag = 2346;
+
+ up(&ar->arSem);
+
+ return ret;
+}
+
+/*
+ * SIOCSIWAP
+ * This ioctl is used to set the desired bssid for the connect command.
+ */
+int
+ar6000_ioctl_siwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ap_addr->sa_family != ARPHRD_ETHER) {
+ return -EIO;
+ }
+
+ if (A_MEMCMP(&ap_addr->sa_data, bcast_mac, AR6000_ETH_ADDR_LEN) == 0) {
+ A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid));
+ } else {
+ A_MEMCPY(ar->arReqBssid, &ap_addr->sa_data, sizeof(ar->arReqBssid));
+ }
+
+ return 0;
+}
+
+/*
+ * SIOCGIWAP
+ */
+int
+ar6000_ioctl_giwap(struct net_device *dev,
+ struct iw_request_info *info,
+ struct sockaddr *ap_addr, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ar->arConnected != TRUE) {
+ return -EINVAL;
+ }
+
+ A_MEMCPY(&ap_addr->sa_data, ar->arBssid, sizeof(ar->arBssid));
+ ap_addr->sa_family = ARPHRD_ETHER;
+
+ return 0;
+}
+
+/*
+ * SIOCGIWAPLIST
+ */
+int
+ar6000_ioctl_iwaplist(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ return -EIO; /* for now */
+}
+
+/*
+ * SIOCGIWSCAN
+ */
+int
+ar6000_ioctl_siwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_startscan_cmd(ar->arWmi, WMI_LONG_SCAN) != A_OK) {
+ ret = -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return ret;
+}
+
+#if WIRELESS_EXT > 14
+/*
+ * Encode a WPA or RSN information element as a custom
+ * element using the hostap format.
+ */
+static u_int
+encode_ie(void *buf, size_t bufsize,
+ const u_int8_t *ie, size_t ielen,
+ const char *leader, size_t leader_len)
+{
+ u_int8_t *p;
+ int i;
+
+ if (bufsize < leader_len)
+ return 0;
+ p = buf;
+ memcpy(p, leader, leader_len);
+ bufsize -= leader_len;
+ p += leader_len;
+ for (i = 0; i < ielen && bufsize > 2; i++)
+ p += sprintf(p, "%02x", ie[i]);
+ return (i == ielen ? p - (u_int8_t *)buf : 0);
+}
+#endif /* WIRELESS_EXT > 14 */
+/*
+ * Units are in db above the noise floor. That means the
+ * rssi values reported in the tx/rx descriptors in the
+ * driver are the SNR expressed in db.
+ *
+ * If you assume that the noise floor is -95, which is an
+ * excellent assumption 99.5 % of the time, then you can
+ * derive the absolute signal level (i.e. -95 + rssi).
+ * There are some other slight factors to take into account
+ * depending on whether the rssi measurement is from 11b,
+ * 11g, or 11a. These differences are at most 2db and
+ * can be documented.
+ *
+ * NB: various calculations are based on the orinoco/wavelan
+ * drivers for compatibility
+ */
+static void
+ar6000_set_quality(struct iw_quality *iq, A_INT8 rssi)
+{
+ if (rssi < 0) {
+ iq->qual = 0;
+ } else {
+ iq->qual = rssi;
+ }
+
+ /* NB: max is 94 because noise is hardcoded to 161 */
+ if (iq->qual > 94)
+ iq->qual = 94;
+
+ iq->noise = 161; /* -95dBm */
+ iq->level = iq->noise + iq->qual;
+ iq->updated = 7;
+}
+
+void
+ar6000_scan_node(void *arg, bss_t *ni)
+{
+ struct iw_event iwe;
+#if WIRELESS_EXT > 14
+ char buf[64*2 + 30];
+#endif
+ struct ar_giwscan_param *param;
+ A_CHAR *current_ev;
+ A_CHAR *end_buf;
+ struct ieee80211_common_ie *cie;
+
+ param = (struct ar_giwscan_param *)arg;
+
+ if (param->current_ev >= param->end_buf) {
+ return;
+ }
+ if ((param->firstPass == TRUE) && (ni->ni_cie.ie_wpa == NULL)) {
+ /*
+ * Only forward wpa bss's in first pass
+ */
+ return;
+ }
+ if ((param->firstPass == FALSE) && (ni->ni_cie.ie_wpa != NULL)) {
+ /*
+ * Only forward non-wpa bss's in 2nd pass
+ */
+ return;
+ }
+
+ current_ev = param->current_ev;
+ end_buf = param->end_buf;
+
+ cie = &ni->ni_cie;
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ A_MEMCPY(iwe.u.ap_addr.sa_data, ni->ni_macaddr, 6);
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_ADDR_LEN);
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ iwe.u.data.length = cie->ie_ssid[1];
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+ &cie->ie_ssid[2]);
+
+ if (cie->ie_capInfo & (IEEE80211_CAPINFO_ESS|IEEE80211_CAPINFO_IBSS)) {
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWMODE;
+ iwe.u.mode = cie->ie_capInfo & IEEE80211_CAPINFO_ESS ?
+ IW_MODE_MASTER : IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_UINT_LEN);
+ }
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = cie->ie_chan * 100000;
+ iwe.u.freq.e = 1;
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_FREQ_LEN);
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = IWEVQUAL;
+ ar6000_set_quality(&iwe.u.qual, ni->ni_rssi);
+ current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe,
+ IW_EV_QUAL_LEN);
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWENCODE;
+ if (cie->ie_capInfo & IEEE80211_CAPINFO_PRIVACY) {
+ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+ } else {
+ iwe.u.data.flags = IW_ENCODE_DISABLED;
+ }
+ iwe.u.data.length = 0;
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, "");
+
+#ifdef NOTYET
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = SIOCGIWRATE;
+ current_val = current_ev + IW_EV_LCP_LEN;
+ for (j = 0; j < ni->ni_rates.rs_nrates; j++) {
+ if (ni->ni_rates.rs_rates[j]) {
+ iwe.u.bitrate.value = ((ni->ni_rates.rs_rates[j] &
+ IEEE80211_RATE_VAL) / 2) * 1000000;
+ current_val = iwe_stream_add_value(current_ev,
+ current_val, end_buf, &iwe,
+ IW_EV_PARAM_LEN);
+ }
+ }
+ /* remove fixed header if no rates were added */
+ if ((current_val - current_ev) > IW_EV_LCP_LEN)
+ current_ev = current_val;
+#endif /* NOTYET */
+
+#if WIRELESS_EXT > 14
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ snprintf(buf, sizeof(buf), "bcn_int=%d", cie->ie_beaconInt);
+ iwe.u.data.length = strlen(buf);
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+
+ if (cie->ie_wpa != NULL) {
+ static const char rsn_leader[] = "rsn_ie=";
+ static const char wpa_leader[] = "wpa_ie=";
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ if (cie->ie_wpa[0] == IEEE80211_ELEMID_RSN) {
+ iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wpa,
+ cie->ie_wpa[1]+2,
+ rsn_leader, sizeof(rsn_leader)-1);
+ } else {
+ iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wpa,
+ cie->ie_wpa[1]+2,
+ wpa_leader, sizeof(wpa_leader)-1);
+ }
+ if (iwe.u.data.length != 0) {
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+ }
+ }
+
+ if (cie->ie_wmm != NULL) {
+ static const char wmm_leader[] = "wmm_ie=";
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_wmm,
+ cie->ie_wmm[1]+2,
+ wmm_leader, sizeof(wmm_leader)-1);
+ if (iwe.u.data.length != 0) {
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+ }
+ }
+
+ if (cie->ie_ath != NULL) {
+ static const char ath_leader[] = "ath_ie=";
+
+ A_MEMZERO(&iwe, sizeof(iwe));
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = encode_ie(buf, sizeof(buf), cie->ie_ath,
+ cie->ie_ath[1]+2,
+ ath_leader, sizeof(ath_leader)-1);
+ if (iwe.u.data.length != 0) {
+ current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf);
+ }
+ }
+#endif /* WIRELESS_EXT > 14 */
+
+ param->current_ev = current_ev;
+}
+
+int
+ar6000_ioctl_giwscan(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ar_giwscan_param param;
+ int i;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ param.current_ev = extra;
+ param.end_buf = extra + IW_SCAN_MAX_DATA;
+ param.firstPass = TRUE;
+
+ /*
+ * Do two passes to insure WPA scan candidates
+ * are sorted to the front. This is a hack to deal with
+ * the wireless extensions capping scan results at
+ * IW_SCAN_MAX_DATA bytes. In densely populated environments
+ * it's easy to overflow this buffer (especially with WPA/RSN
+ * information elements). Note this sorting hack does not
+ * guarantee we won't overflow anyway.
+ */
+ for (i = 0; i < 2; i++) {
+ /*
+ * Translate data to WE format.
+ */
+ wmi_iterate_nodes(ar->arWmi, ar6000_scan_node, ¶m);
+ param.firstPass = FALSE;
+ if (param.current_ev >= param.end_buf) {
+ break;
+ }
+ }
+
+ data->length = param.current_ev - extra;
+ return 0;
+}
+
+/* SIOCSIWESSID */
+static int
+ar6000_ioctl_siwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *ssid)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ A_STATUS status;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ /*
+ * iwconfig passes a null terminated string with length including this
+ * so we need to account for this
+ */
+ if (data->flags && (!data->length || (data->length == 1) ||
+ ((data->length - 1) > sizeof(ar->arSsid))))
+ {
+ /*
+ * ssid is invalid
+ */
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+
+ if (ar->arTxPending[WMI_CONTROL_MBOX]) {
+ /*
+ * sleep until the command queue drains
+ */
+ wait_event_interruptible(arEvent,
+ ar->arTxPending[WMI_CONTROL_MBOX] == 0);
+ if (signal_pending(current)) {
+ return -EINTR;
+ }
+ }
+
+ if ((ar->arSsidLen) || (!data->flags))
+ {
+ if ((!data->flags) ||
+ (A_MEMCMP(ar->arSsid, ssid, ar->arSsidLen) != 0) ||
+ (ar->arSsidLen != (data->length - 1)))
+ {
+ /*
+ * SSID set previously or essid off has been issued.
+ *
+ * Disconnect Command is issued in two cases after wmi is ready
+ * (1) ssid is different from the previous setting
+ * (2) essid off has been issued
+ *
+ */
+ if (ar->arWmiReady == TRUE) {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ status = wmi_disconnect_cmd(ar->arWmi);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
+ ar->arSsidLen = 0;
+ A_MEMZERO(ar->arReqBssid, sizeof(ar->arReqBssid));
+ if (!data->flags) {
+ up(&ar->arSem);
+ return 0;
+ }
+ } else {
+ up(&ar->arSem);
+ }
+ }
+ else
+ {
+ /*
+ * SSID is same, so we assume profile hasn't changed.
+ * If the interface is up and wmi is ready, we issue
+ * a reconnect cmd.
+ */
+ if((ar->arConnected == TRUE || ar->arConnectPending == TRUE) && (ar->arWmiReady == TRUE))
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ status = wmi_reconnect_cmd(ar->arWmi,ar->arReqBssid,
+ ar->arChannelHint);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ if (status != A_OK) {
+ return -EIO;
+ }
+ return 0;
+ }
+ else{
+ up(&ar->arSem);
+ return 0;
+ }
+ }
+ }
+
+ ar->arSsidLen = data->length - 1;
+ A_MEMCPY(ar->arSsid, ssid, ar->arSsidLen);
+
+ /* The ssid length check prevents second "essid off" from the user,
+ to be treated as a connect cmd. The second "essid off" is ignored.
+ */
+ if((ar->arWmiReady == TRUE) && (ar->arSsidLen > 0) )
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (SHARED_AUTH == ar->arDot11AuthMode) {
+ ar6000_install_static_wep_keys(ar);
+ }
+ AR_DEBUG_PRINTF("Connect called with authmode %d dot11 auth %d"\
+ " PW crypto %d PW crypto Len %d GRP crypto %d"\
+ " GRP crypto Len %d\n",
+ ar->arAuthMode, ar->arDot11AuthMode,
+ ar->arPairwiseCrypto, ar->arPairwiseCryptoLen,
+ ar->arGroupCrypto, ar->arGroupCryptoLen);
+
+ status = wmi_connect_cmd(ar->arWmi, ar->arNetworkType,
+ ar->arDot11AuthMode, ar->arAuthMode,
+ ar->arPairwiseCrypto, ar->arPairwiseCryptoLen,
+ ar->arGroupCrypto,ar->arGroupCryptoLen,
+ ar->arSsidLen, ar->arSsid,
+ ar->arReqBssid, ar->arChannelHint);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ up(&ar->arSem);
+
+ if (status != A_OK) {
+ return -EIO;
+ }
+ ar->arConnectPending = TRUE;
+ }else{
+ up(&ar->arSem);
+ }
+ return 0;
+}
+
+/* SIOCGIWESSID */
+static int
+ar6000_ioctl_giwessid(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *data, char *essid)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (!ar->arSsidLen) {
+ return -EINVAL;
+ }
+
+ data->flags = 1;
+ data->length = ar->arSsidLen;
+ A_MEMCPY(essid, ar->arSsid, ar->arSsidLen);
+
+ return 0;
+}
+
+/*
+ * SIOCSIWRATE
+ */
+int
+ar6000_ioctl_siwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ A_UINT32 kbps;
+
+ if (rrq->fixed) {
+ kbps = rrq->value / 1000; /* rrq->value is in bps */
+ } else {
+ kbps = -1; /* -1 indicates auto rate */
+ }
+ if(wmi_validate_bitrate(ar->arWmi, kbps) == A_EINVAL)
+ {
+ AR_DEBUG_PRINTF("BitRate is not Valid %d\n", kbps);
+ return -EINVAL;
+ }
+ ar->arBitRate = kbps;
+ if(ar->arWmiReady == TRUE)
+ {
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_set_bitrate_cmd(ar->arWmi, kbps) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return -EINVAL;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ return 0;
+}
+
+/*
+ * SIOCGIWRATE
+ */
+int
+ar6000_ioctl_giwrate(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int ret = 0;
+
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+ if(ar->arWmiReady == TRUE)
+ {
+ ar->arBitRate = 0xFFFF;
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_get_bitrate_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ wait_event_interruptible(arEvent, ar->arBitRate != 0xFFFF);
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ }
+ }
+ /* If the interface is down or wmi is not ready or the target is not
+ connected - return the value stored in the device structure */
+ if (!ret) {
+ if (ar->arBitRate == -1) {
+ rrq->fixed = TRUE;
+ rrq->value = 0;
+ } else {
+ rrq->value = ar->arBitRate * 1000;
+ }
+ }
+
+ up(&ar->arSem);
+
+ return ret;
+}
+
+/*
+ * SIOCSIWTXPOW
+ */
+static int
+ar6000_ioctl_siwtxpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ A_UINT8 dbM;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (rrq->disabled) {
+ return -EOPNOTSUPP;
+ }
+
+ if (rrq->fixed) {
+ if (rrq->flags != IW_TXPOW_DBM) {
+ return -EOPNOTSUPP;
+ }
+ ar->arTxPwr= dbM = rrq->value;
+ ar->arTxPwrSet = TRUE;
+ } else {
+ ar->arTxPwr = dbM = 0;
+ ar->arTxPwrSet = FALSE;
+ }
+ if(ar->arWmiReady == TRUE)
+ {
+ AR_DEBUG_PRINTF("Set tx pwr cmd %d dbM\n", dbM);
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ wmi_set_txPwr_cmd(ar->arWmi, dbM);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ }
+ return 0;
+}
+
+/*
+ * SIOCGIWTXPOW
+ */
+int
+ar6000_ioctl_giwtxpow(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int ret = 0;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (down_interruptible(&ar->arSem)) {
+ return -ERESTARTSYS;
+ }
+ if((ar->arWmiReady == TRUE) && (ar->arConnected == TRUE))
+ {
+ ar->arTxPwr = 0;
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ if (wmi_get_txPwr_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ up(&ar->arSem);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ wait_event_interruptible(arEvent, ar->arTxPwr != 0);
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ }
+ }
+ /* If the interace is down or wmi is not ready or target is not connected
+ then return value stored in the device structure */
+
+ if (!ret) {
+ if (ar->arTxPwrSet == TRUE) {
+ rrq->fixed = TRUE;
+ }
+ rrq->value = ar->arTxPwr;
+ rrq->flags = IW_TXPOW_DBM;
+ }
+
+ up(&ar->arSem);
+
+ return ret;
+}
+
+/*
+ * SIOCSIWRETRY
+ * since iwconfig only provides us with one max retry value, we use it
+ * to apply to data frames of the BE traffic class.
+ */
+static int
+ar6000_ioctl_siwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (rrq->disabled) {
+ return -EOPNOTSUPP;
+ }
+
+ if ((rrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) {
+ return -EOPNOTSUPP;
+ }
+
+ if ( !(rrq->value >= WMI_MIN_RETRIES) || !(rrq->value <= WMI_MAX_RETRIES)) {
+ return - EINVAL;
+ }
+ if(ar->arWmiReady == TRUE)
+ {
+ if (wmi_set_retry_limits_cmd(ar->arWmi, DATA_FRAMETYPE, WMM_AC_BE,
+ rrq->value) == A_OK){
+ return -EINVAL;
+ }
+ }
+ ar->arMaxRetries = rrq->value;
+ return 0;
+}
+
+/*
+ * SIOCGIWRETRY
+ */
+static int
+ar6000_ioctl_giwretry(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_param *rrq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ rrq->disabled = 0;
+ switch (rrq->flags & IW_RETRY_TYPE) {
+ case IW_RETRY_LIFETIME:
+ return -EOPNOTSUPP;
+ break;
+ case IW_RETRY_LIMIT:
+ rrq->flags = IW_RETRY_LIMIT;
+ switch (rrq->flags & IW_RETRY_MODIFIER) {
+ case IW_RETRY_MIN:
+ rrq->flags |= IW_RETRY_MIN;
+ rrq->value = WMI_MIN_RETRIES;
+ break;
+ case IW_RETRY_MAX:
+ rrq->flags |= IW_RETRY_MAX;
+ rrq->value = ar->arMaxRetries;
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * SIOCSIWENCODE
+ */
+static int
+ar6000_ioctl_siwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *keybuf)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int index;
+ DOT11_AUTH_MODE auth = ar->arDot11AuthMode;
+ /*
+ * Static WEP Keys should be configured before setting the SSID
+ */
+ if (ar->arSsidLen) {
+ return -EIO;
+ }
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ index = erq->flags & IW_ENCODE_INDEX;
+
+ if (index && (((index - 1) < WMI_MIN_KEY_INDEX) ||
+ ((index - 1) > WMI_MAX_KEY_INDEX)))
+ {
+ return -EIO;
+ }
+
+ if (erq->flags & IW_ENCODE_DISABLED) {
+ /*
+ * Encryption disabled
+ */
+ if (index) {
+ /*
+ * If key index was specified then clear the specified key
+ */
+ index--;
+ A_MEMZERO(ar->arWepKeyList[index].arKey,
+ sizeof(ar->arWepKeyList[index].arKey));
+ ar->arWepKeyList[index].arKeyLen = 0;
+ }
+ ar->arDot11AuthMode = OPEN_AUTH;
+ ar->arPairwiseCrypto = NONE_CRYPT;
+ ar->arGroupCrypto = NONE_CRYPT;
+ ar->arAuthMode = NONE_AUTH;
+ } else {
+ /*
+ * Enabling WEP encryption
+ */
+ if (index) {
+ index--; /* keyindex is off base 1 in iwconfig */
+ }
+
+ if (erq->flags & IW_ENCODE_OPEN) {
+ auth = OPEN_AUTH;
+ } else if (erq->flags & IW_ENCODE_RESTRICTED) {
+ auth = SHARED_AUTH;
+ }
+
+ if (erq->length) {
+ if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(erq->length)) {
+ return -EIO;
+ }
+
+ A_MEMZERO(ar->arWepKeyList[index].arKey,
+ sizeof(ar->arWepKeyList[index].arKey));
+ A_MEMCPY(ar->arWepKeyList[index].arKey, keybuf, erq->length);
+ ar->arWepKeyList[index].arKeyLen = erq->length;
+ } else {
+ if (ar->arWepKeyList[index].arKeyLen == 0) {
+ return -EIO;
+ }
+ ar->arDefTxKeyIndex = index;
+ }
+
+ ar->arPairwiseCrypto = WEP_CRYPT;
+ ar->arGroupCrypto = WEP_CRYPT;
+ ar->arDot11AuthMode = auth;
+ ar->arAuthMode = NONE_AUTH;
+ }
+
+ /*
+ * profile has changed. Erase ssid to signal change
+ */
+ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_giwencode(struct net_device *dev,
+ struct iw_request_info *info,
+ struct iw_point *erq, char *key)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ A_UINT8 keyIndex;
+ struct ar_wep_key *wk;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if (ar->arPairwiseCrypto == NONE_CRYPT) {
+ erq->length = 0;
+ erq->flags = IW_ENCODE_DISABLED;
+ } else {
+ /* get the keyIndex */
+ keyIndex = erq->flags & IW_ENCODE_INDEX;
+ if (0 == keyIndex) {
+ keyIndex = ar->arDefTxKeyIndex;
+ } else if ((keyIndex - 1 < WMI_MIN_KEY_INDEX) ||
+ (keyIndex - 1 > WMI_MAX_KEY_INDEX))
+ {
+ keyIndex = WMI_MIN_KEY_INDEX;
+ } else {
+ keyIndex--;
+ }
+ erq->flags = keyIndex + 1;
+ erq->flags |= IW_ENCODE_ENABLED;
+ wk = &ar->arWepKeyList[keyIndex];
+ if (erq->length > wk->arKeyLen) {
+ erq->length = wk->arKeyLen;
+ }
+ if (wk->arKeyLen) {
+ A_MEMCPY(key, wk->arKey, erq->length);
+ }
+ if (ar->arDot11AuthMode == OPEN_AUTH) {
+ erq->flags |= IW_ENCODE_OPEN;
+ } else if (ar->arDot11AuthMode == SHARED_AUTH) {
+ erq->flags |= IW_ENCODE_RESTRICTED;
+ }
+ }
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_setparam(struct net_device *dev,
+ struct iw_request_info *info,
+ void *erq, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ int *i = (int *)extra;
+ int param = i[0];
+ int value = i[1];
+ int ret = 0;
+ A_BOOL profChanged = FALSE;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ switch (param) {
+ case IEEE80211_PARAM_WPA:
+ switch (value) {
+ case WPA_MODE_WPA1:
+ ar->arAuthMode = WPA_AUTH;
+ profChanged = TRUE;
+ break;
+ case WPA_MODE_WPA2:
+ ar->arAuthMode = WPA2_AUTH;
+ profChanged = TRUE;
+ break;
+ case WPA_MODE_NONE:
+ ar->arAuthMode = NONE_AUTH;
+ profChanged = TRUE;
+ break;
+ }
+ break;
+ case IEEE80211_PARAM_AUTHMODE:
+ switch(value) {
+ case IEEE80211_AUTH_WPA_PSK:
+ if (WPA_AUTH == ar->arAuthMode) {
+ ar->arAuthMode = WPA_PSK_AUTH;
+ profChanged = TRUE;
+ } else if (WPA2_AUTH == ar->arAuthMode) {
+ ar->arAuthMode = WPA2_PSK_AUTH;
+ profChanged = TRUE;
+ } else {
+ AR_DEBUG_PRINTF("Error - Setting PSK mode when WPA "\
+ "param was set to %d\n",
+ ar->arAuthMode);
+ ret = -1;
+ }
+ break;
+ case IEEE80211_AUTH_WPA_CCKM:
+ if (WPA2_AUTH == ar->arAuthMode) {
+ ar->arAuthMode = WPA2_AUTH_CCKM;
+ } else {
+ ar->arAuthMode = WPA_AUTH_CCKM;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case IEEE80211_PARAM_UCASTCIPHER:
+ switch (value) {
+ case IEEE80211_CIPHER_AES_CCM:
+ ar->arPairwiseCrypto = AES_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ ar->arPairwiseCrypto = TKIP_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_WEP:
+ ar->arPairwiseCrypto = WEP_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_NONE:
+ ar->arPairwiseCrypto = NONE_CRYPT;
+ profChanged = TRUE;
+ break;
+ }
+ break;
+ case IEEE80211_PARAM_UCASTKEYLEN:
+ if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) {
+ ret = -EIO;
+ } else {
+ ar->arPairwiseCryptoLen = value;
+ }
+ break;
+ case IEEE80211_PARAM_MCASTCIPHER:
+ switch (value) {
+ case IEEE80211_CIPHER_AES_CCM:
+ ar->arGroupCrypto = AES_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ ar->arGroupCrypto = TKIP_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_WEP:
+ ar->arGroupCrypto = WEP_CRYPT;
+ profChanged = TRUE;
+ break;
+ case IEEE80211_CIPHER_NONE:
+ ar->arGroupCrypto = NONE_CRYPT;
+ profChanged = TRUE;
+ break;
+ }
+ break;
+ case IEEE80211_PARAM_MCASTKEYLEN:
+ if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(value)) {
+ ret = -EIO;
+ } else {
+ ar->arGroupCryptoLen = value;
+ }
+ break;
+ case IEEE80211_PARAM_COUNTERMEASURES:
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ wmi_set_tkip_countermeasures_cmd(ar->arWmi, value);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ break;
+ default:
+ break;
+ }
+
+ if (profChanged == TRUE) {
+ /*
+ * profile has changed. Erase ssid to signal change
+ */
+ A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
+ }
+
+ return ret;
+}
+
+int
+ar6000_ioctl_getparam(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ return -EIO; /* for now */
+}
+
+int
+ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ieee80211req_key *ik = (struct ieee80211req_key *)extra;
+ KEY_USAGE keyUsage;
+ A_STATUS status;
+ CRYPTO_TYPE keyType = NONE_CRYPT;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ if ( 0 == memcmp(ik->ik_macaddr, "\x00\x00\x00\x00\x00\x00",
+ IEEE80211_ADDR_LEN)) {
+ keyUsage = GROUP_USAGE;
+ } else {
+ keyUsage = PAIRWISE_USAGE;
+ }
+
+ switch (ik->ik_type) {
+ case IEEE80211_CIPHER_WEP:
+ keyType = WEP_CRYPT;
+ break;
+ case IEEE80211_CIPHER_TKIP:
+ keyType = TKIP_CRYPT;
+ break;
+ case IEEE80211_CIPHER_AES_CCM:
+ keyType = AES_CRYPT;
+ break;
+ default:
+ break;
+ }
+
+ if (IEEE80211_CIPHER_CCKM_KRK != ik->ik_type) {
+ if (NONE_CRYPT == keyType) {
+ return -EIO;
+ }
+
+ status = wmi_addKey_cmd(ar->arWmi, ik->ik_keyix, keyType, keyUsage,
+ ik->ik_keylen, (A_UINT8 *)&ik->ik_keyrsc,
+ ik->ik_keydata, SYNC_BEFORE_WMIFLAG);
+
+ if (status != A_OK) {
+ return -EIO;
+ }
+ } else {
+ status = wmi_add_krk_cmd(ar->arWmi, ik->ik_keydata);
+ }
+
+ return 0;
+}
+
+void
+ar6000_install_static_wep_keys(AR_SOFTC_T *ar)
+{
+ A_UINT8 index;
+ A_UINT8 keyUsage;
+
+ for (index = WMI_MIN_KEY_INDEX; index <= WMI_MAX_KEY_INDEX; index++) {
+ if (ar->arWepKeyList[index].arKeyLen) {
+ keyUsage = GROUP_USAGE;
+ if (index == ar->arDefTxKeyIndex) {
+ keyUsage |= TX_USAGE;
+ }
+ wmi_addKey_cmd(ar->arWmi,
+ index,
+ WEP_CRYPT,
+ keyUsage,
+ ar->arWepKeyList[index].arKeyLen,
+ NULL,
+ ar->arWepKeyList[index].arKey, NO_SYNC_WMIFLAG);
+ }
+ }
+}
+
+int
+ar6000_ioctl_delkey(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ return 0;
+}
+
+int
+ar6000_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra;
+
+ if ((ar->arWmiReady == FALSE) || (ar->arConnected != TRUE)) {
+ return -EIO;
+ }
+
+ switch (mlme->im_op) {
+ case IEEE80211_MLME_DISASSOC:
+ case IEEE80211_MLME_DEAUTH:
+ /* Not Supported */
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int
+ar6000_ioctl_setwmmparams(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ return -EIO; /* for now */
+}
+
+int
+ar6000_ioctl_getwmmparams(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ return -EIO; /* for now */
+}
+
+int
+ar6000_ioctl_setoptie(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ return 0;
+}
+
+int
+ar6000_ioctl_setauthalg(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ieee80211req_authalg *req = (struct ieee80211req_authalg *)extra;
+ int ret = 0;
+
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ if (req->auth_alg == AUTH_ALG_OPEN_SYSTEM) {
+ ar->arDot11AuthMode = OPEN_AUTH;
+ } else if (req->auth_alg == AUTH_ALG_LEAP) {
+ ar->arDot11AuthMode = LEAP_AUTH;
+ ar->arPairwiseCrypto = WEP_CRYPT;
+ ar->arGroupCrypto = WEP_CRYPT;
+ } else {
+ ret = -EIO;
+ }
+
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return ret;
+}
+static int
+ar6000_ioctl_addpmkid(struct net_device *dev, struct iw_request_info *info,
+ void *w, char *extra)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ struct ieee80211req_addpmkid *req = (struct ieee80211req_addpmkid *)extra;
+ A_STATUS status;
+
+ if (ar->arWlanState == WLAN_DISABLED) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+ AR_DEBUG_PRINTF("Add pmkid for %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x en=%d\n",
+ req->pi_bssid[0], req->pi_bssid[1], req->pi_bssid[2],
+ req->pi_bssid[3], req->pi_bssid[4], req->pi_bssid[5],
+ req->pi_enable);
+
+ status = wmi_setPmkid_cmd(ar->arWmi, req->pi_bssid, req->pi_pmkid,
+ req->pi_enable);
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ if (status != A_OK) {
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_get_roam_tbl(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ if(wmi_get_roam_tbl_cmd(ar->arWmi) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_get_roam_data(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ /* currently assume only roam times are required */
+ if(wmi_get_roam_data_cmd(ar->arWmi, ROAM_DATA_TIME) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_set_roam_ctrl(struct net_device *dev, char *userdata)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_SET_ROAM_CTRL_CMD cmd;
+ A_UINT8 size = sizeof(cmd);
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+
+ if (copy_from_user(&cmd, userdata, size)) {
+ return -EFAULT;
+ }
+
+ if (cmd.roamCtrlType == WMI_SET_HOST_BIAS) {
+ if (cmd.info.bssBiasInfo.numBss > 1) {
+ size += (cmd.info.bssBiasInfo.numBss - 1) * sizeof(WMI_BSS_BIAS);
+ }
+ }
+
+ if (copy_from_user(&cmd, userdata, size)) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ if(wmi_set_roam_ctrl_cmd(ar->arWmi, &cmd, size) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return 0;
+}
+
+static int
+ar6000_ioctl_set_powersave_timers(struct net_device *dev, char *userdata)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_POWERSAVE_TIMERS_CMD cmd;
+ A_UINT8 size = sizeof(cmd);
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ if (copy_from_user(&cmd, userdata, size)) {
+ return -EFAULT;
+ }
+
+ if (copy_from_user(&cmd, userdata, size)) {
+ return -EFAULT;
+ }
+
+ AR6000_SPIN_LOCK(&ar->arLock, 0);
+
+ if(wmi_set_powersave_timers_cmd(ar->arWmi, &cmd, size) != A_OK) {
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+ return -EIO;
+ }
+ AR6000_SPIN_UNLOCK(&ar->arLock, 0);
+
+ return 0;
+}
+
+
+#if defined(DEBUG)
+int dset_debug = 0;
+#define DSET_DEBUG_PRINTF(args...) if (dset_debug) printk(args);
+#else
+#define DSET_DEBUG_PRINTF(args...)
+#endif
+
+/* Called when a DataSet Open request is received from the Target. */
+void
+ar6000_dset_open_req(void *devt,
+ A_UINT32 id,
+ A_UINT32 targ_handle,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+
+ DSET_DEBUG_PRINTF("ar6000_dset_open_req id=0x%x\n", id);
+
+ if (!dset_server_alive) {
+ /*
+ * An attempt to open a Host DataSet before the DataSet Server
+ * has started. (Or this Host chooses not to serve DataSets.)
+ */
+ DSET_DEBUG_PRINTF("DataSet Server not running. Unable to handle open request for id=0x%x\n", id);
+ wmi_dset_open_reply(ar->arWmi, A_ENOTSUP, 0, 0, 0,
+ targ_handle, targ_reply_fn, targ_reply_arg);
+ return;
+ }
+
+#if CONFIG_HOST_DSET_SUPPORT
+ if (spin_trylock(&dset_request_lock)) {
+ pending_dset_request.cmd = AR6000_OPEN_REQ;
+ pending_dset_request.u.open_req.id = id;
+ pending_dset_request.u.open_req.targ_handle = targ_handle;
+ pending_dset_request.u.open_req.targ_reply_fn = targ_reply_fn;
+ pending_dset_request.u.open_req.targ_reply_arg = targ_reply_arg;
+ /* Wakeup waiting thread */
+ pending_dset_request_valid = TRUE;
+ wake_up(&dset_request);
+ } else {
+ wmi_dset_open_reply(ar->arWmi, A_EBUSY, 0, 0, 0,
+ targ_handle, targ_reply_fn, targ_reply_arg);
+ }
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+}
+
+#if CONFIG_HOST_DSET_SUPPORT
+/* Called when a DataSet Close is received from the Target. */
+void
+ar6000_dset_close(void *devt,
+ A_UINT32 access_cookie)
+{
+ DSET_DEBUG_PRINTF("ar6000_dset_close access_cookie=0x%x\n", access_cookie);
+}
+
+/* Called when a DataSet Data Request is received from the Target. */
+void
+ar6000_dset_data_req(void *devt,
+ A_UINT32 access_cookie,
+ A_UINT32 offset,
+ A_UINT32 length,
+ A_UINT32 targ_buf,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)devt;
+
+ DSET_DEBUG_PRINTF("ar6000_dset_data_req: Cookie=0x%x offset=%d length=%d\n",
+ access_cookie, offset, length);
+
+ if (spin_trylock(&dset_request_lock)) {
+ pending_dset_request.cmd = AR6000_DATA_REQ;
+ pending_dset_request.u.data_req.access_cookie = access_cookie;
+ pending_dset_request.u.data_req.offset = offset;
+ pending_dset_request.u.data_req.length = length;
+ pending_dset_request.u.data_req.targ_buf = targ_buf;
+ pending_dset_request.u.data_req.targ_reply_fn = targ_reply_fn;
+ pending_dset_request.u.data_req.targ_reply_arg = targ_reply_arg;
+
+ /* Wakeup waiting thread */
+ pending_dset_request_valid = TRUE;
+ wake_up(&dset_request);
+ } else {
+ wmi_dset_data_reply(ar->arWmi, A_EBUSY, NULL, length,
+ targ_buf, targ_reply_fn, targ_reply_arg);
+ }
+}
+
+/* Init cookie queue */
+static void
+ar6000_cookie_init(AR_SOFTC_T *ar)
+{
+ A_UINT32 i;
+
+ ar->arCookieList = NULL;
+ A_MEMZERO(s_ar_cookie_mem, sizeof(s_ar_cookie_mem));
+
+ for (i = 0; i < MAX_COOKIE_NUM; i++) {
+ ar6000_free_cookie(ar, &s_ar_cookie_mem[i]);
+ }
+}
+
+/* cleanup cookie queue */
+static void
+ar6000_cookie_cleanup(AR_SOFTC_T *ar)
+{
+ /* It is gone .... */
+ ar->arCookieList = NULL;
+}
+
+/* Init cookie queue */
+static void
+ar6000_free_cookie(AR_SOFTC_T *ar, struct ar_cookie * cookie)
+{
+ /* Insert first */
+ cookie->arc_list_next = ar->arCookieList;
+ ar->arCookieList = cookie;
+}
+
+/* cleanup cookie queue */
+static struct ar_cookie *
+ar6000_alloc_cookie(AR_SOFTC_T *ar)
+{
+ struct ar_cookie *cookie;
+
+ cookie = ar->arCookieList;
+ if(cookie != NULL)
+ {
+ ar->arCookieList = cookie->arc_list_next;
+ }
+
+ return cookie;
+}
+
+/* Host-side initialization for DataSets */
+static void
+ar6000_dset_init(void)
+{
+ spin_lock_init(&dset_request_lock);
+ pending_dset_request_valid = FALSE;
+}
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+#if CONFIG_HOST_GPIO_SUPPORT
+/* Host-side initialization for General Purpose I/O support */
+static void
+ar6000_gpio_init(void)
+{
+ gpio_intr_available = FALSE;
+ gpio_data_available = FALSE;
+ gpio_ack_received = FALSE;
+}
+
+/*
+ * Called when a GPIO interrupt is received from the Target.
+ * intr_values shows which GPIO pins have interrupted.
+ * input_values shows a recent value of GPIO pins.
+ */
+void
+ar6000_gpio_intr_rx(A_UINT32 intr_mask, A_UINT32 input_values)
+{
+ gpio_intr_results.intr_mask = intr_mask;
+ gpio_intr_results.input_values = input_values;
+ *((volatile A_BOOL *)&gpio_intr_available) = TRUE;
+ wake_up(&arEvent);
+}
+
+/*
+ * This is called when a response is received from the Target
+ * for a previous or ar6000_gpio_input_get or ar6000_gpio_register_get
+ * call.
+ */
+void
+ar6000_gpio_data_rx(A_UINT32 reg_id, A_UINT32 value)
+{
+ gpio_reg_results.gpioreg_id = reg_id;
+ gpio_reg_results.value = value;
+ *((volatile A_BOOL *)&gpio_data_available) = TRUE;
+ wake_up(&arEvent);
+}
+
+/*
+ * This is called when an acknowledgement is received from the Target
+ * for a previous or ar6000_gpio_output_set or ar6000_gpio_register_set
+ * call.
+ */
+void
+ar6000_gpio_ack_rx(void)
+{
+ gpio_ack_received = TRUE;
+ wake_up(&arEvent);
+}
+
+A_STATUS
+ar6000_gpio_output_set(struct net_device *dev,
+ A_UINT32 set_mask,
+ A_UINT32 clear_mask,
+ A_UINT32 enable_mask,
+ A_UINT32 disable_mask)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ gpio_ack_received = FALSE;
+ return wmi_gpio_output_set(ar->arWmi,
+ set_mask, clear_mask, enable_mask, disable_mask);
+}
+
+static A_STATUS
+ar6000_gpio_input_get(struct net_device *dev)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ *((volatile A_BOOL *)&gpio_data_available) = FALSE;
+ return wmi_gpio_input_get(ar->arWmi);
+}
+
+static A_STATUS
+ar6000_gpio_register_set(struct net_device *dev,
+ A_UINT32 gpioreg_id,
+ A_UINT32 value)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ gpio_ack_received = FALSE;
+ return wmi_gpio_register_set(ar->arWmi, gpioreg_id, value);
+}
+
+static A_STATUS
+ar6000_gpio_register_get(struct net_device *dev,
+ A_UINT32 gpioreg_id)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ *((volatile A_BOOL *)&gpio_data_available) = FALSE;
+ return wmi_gpio_register_get(ar->arWmi, gpioreg_id);
+}
+
+static A_STATUS
+ar6000_gpio_intr_ack(struct net_device *dev,
+ A_UINT32 ack_mask)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+
+ gpio_intr_available = FALSE;
+ return wmi_gpio_intr_ack(ar->arWmi, ack_mask);
+}
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+/* Structures to export the Wireless Handlers */
+static const iw_handler ath_handlers[] = {
+ (iw_handler) NULL, /* SIOCSIWCOMMIT */
+ (iw_handler) ar6000_ioctl_giwname, /* SIOCGIWNAME */
+ (iw_handler) NULL, /* SIOCSIWNWID */
+ (iw_handler) NULL, /* SIOCGIWNWID */
+ (iw_handler) ar6000_ioctl_siwfreq, /* SIOCSIWFREQ */
+ (iw_handler) ar6000_ioctl_giwfreq, /* SIOCGIWFREQ */
+ (iw_handler) ar6000_ioctl_siwmode, /* SIOCSIWMODE */
+ (iw_handler) ar6000_ioctl_giwmode, /* SIOCGIWMODE */
+ (iw_handler) ar6000_ioctl_siwsens, /* SIOCSIWSENS */
+ (iw_handler) ar6000_ioctl_giwsens, /* SIOCGIWSENS */
+ (iw_handler) NULL /* not _used */, /* SIOCSIWRANGE */
+ (iw_handler) ar6000_ioctl_giwrange, /* SIOCGIWRANGE */
+ (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */
+ (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */
+ (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */
+ (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */
+ (iw_handler) NULL, /* SIOCSIWSPY */
+ (iw_handler) NULL, /* SIOCGIWSPY */
+ (iw_handler) NULL, /* SIOCSIWTHRSPY */
+ (iw_handler) NULL, /* SIOCGIWTHRSPY */
+ (iw_handler) ar6000_ioctl_siwap, /* SIOCSIWAP */
+ (iw_handler) ar6000_ioctl_giwap, /* SIOCGIWAP */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) ar6000_ioctl_iwaplist, /* SIOCGIWAPLIST */
+ (iw_handler) ar6000_ioctl_siwscan, /* SIOCSIWSCAN */
+ (iw_handler) ar6000_ioctl_giwscan, /* SIOCGIWSCAN */
+ (iw_handler) ar6000_ioctl_siwessid, /* SIOCSIWESSID */
+ (iw_handler) ar6000_ioctl_giwessid, /* SIOCGIWESSID */
+ (iw_handler) NULL, /* SIOCSIWNICKN */
+ (iw_handler) NULL, /* SIOCGIWNICKN */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) NULL, /* -- hole -- */
+ (iw_handler) ar6000_ioctl_siwrate, /* SIOCSIWRATE */
+ (iw_handler) ar6000_ioctl_giwrate, /* SIOCGIWRATE */
+#ifdef NOTYET
+ (iw_handler) ar6000_ioctl_siwrts, /* SIOCSIWRTS */
+ (iw_handler) ar6000_ioctl_giwrts, /* SIOCGIWRTS */
+ (iw_handler) ar6000_ioctl_siwfrag, /* SIOCSIWFRAG */
+ (iw_handler) ar6000_ioctl_giwfrag, /* SIOCGIWFRAG */
+ (iw_handler) ar6000_ioctl_siwtxpow, /* SIOCSIWTXPOW */
+ (iw_handler) ar6000_ioctl_giwtxpow, /* SIOCGIWTXPOW */
+ (iw_handler) ar6000_ioctl_siwretry, /* SIOCSIWRETRY */
+ (iw_handler) ar6000_ioctl_giwretry, /* SIOCGIWRETRY */
+ (iw_handler) ar6000_ioctl_siwencode, /* SIOCSIWENCODE */
+ (iw_handler) ar6000_ioctl_giwencode, /* SIOCGIWENCODE */
+ (iw_handler) ar6000_ioctl_siwpower, /* SIOCSIWPOWER */
+ (iw_handler) ar6000_ioctl_giwpower, /* SIOCGIWPOWER */
+#else
+ (iw_handler) NULL, /* SIOCSIWRTS */
+ (iw_handler) NULL, /* SIOCGIWRTS */
+ (iw_handler) NULL, /* SIOCSIWFRAG */
+ (iw_handler) NULL, /* SIOCGIWFRAG */
+ (iw_handler) ar6000_ioctl_siwtxpow, /* SIOCSIWTXPOW */
+ (iw_handler) ar6000_ioctl_giwtxpow, /* SIOCGIWTXPOW */
+ (iw_handler) ar6000_ioctl_siwretry, /* SIOCSIWRETRY */
+ (iw_handler) ar6000_ioctl_giwretry, /* SIOCGIWRETRY */
+ (iw_handler) ar6000_ioctl_siwencode, /* SIOCSIWENCODE */
+ (iw_handler) ar6000_ioctl_giwencode, /* SIOCGIWENCODE */
+ (iw_handler) NULL, /* SIOCSIWPOWER */
+ (iw_handler) NULL, /* SIOCGIWPOWER */
+#endif /* NOTYET */
+};
+
+static const iw_handler ath_priv_handlers[] = {
+ (iw_handler) ar6000_ioctl_setparam, /* SIOCWFIRSTPRIV+0 */
+ (iw_handler) ar6000_ioctl_getparam, /* SIOCWFIRSTPRIV+1 */
+ (iw_handler) ar6000_ioctl_setkey, /* SIOCWFIRSTPRIV+2 */
+ (iw_handler) ar6000_ioctl_setwmmparams, /* SIOCWFIRSTPRIV+3 */
+ (iw_handler) ar6000_ioctl_delkey, /* SIOCWFIRSTPRIV+4 */
+ (iw_handler) ar6000_ioctl_getwmmparams, /* SIOCWFIRSTPRIV+5 */
+ (iw_handler) NULL, /* SIOCWFIRSTPRIV+6 */
+ (iw_handler) NULL, /* SIOCWFIRSTPRIV+7 */
+ (iw_handler) ar6000_ioctl_addpmkid, /* SIOCWFIRSTPRIV+8 */
+ (iw_handler) NULL, /* SIOCWFIRSTPRIV+9 */
+#ifdef NOT_YET
+ (iw_handler) ar6000_ioctl_setauthalg, /* SIOCWFIRSTPRIV+10 */
+#endif
+};
+
+#define IW_PRIV_TYPE_KEY \
+ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_key))
+#define IW_PRIV_TYPE_DELKEY \
+ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_del_key))
+#define IW_PRIV_TYPE_MLME \
+ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_mlme))
+#define IW_PRIV_TYPE_ADDPMKID \
+ (IW_PRIV_TYPE_BYTE | sizeof(struct ieee80211req_addpmkid))
+
+static const struct iw_priv_args ar6000_priv_args[] = {
+ { IEEE80211_IOCTL_SETKEY,
+ IW_PRIV_TYPE_KEY | IW_PRIV_SIZE_FIXED, 0, "setkey"},
+ { IEEE80211_IOCTL_DELKEY,
+ IW_PRIV_TYPE_DELKEY | IW_PRIV_SIZE_FIXED, 0, "delkey"},
+ { IEEE80211_IOCTL_SETPARAM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "setparam"},
+ { IEEE80211_IOCTL_GETPARAM,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getparam"},
+ { IEEE80211_IOCTL_SETWMMPARAMS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, 0, "setwmmparams"},
+ { IEEE80211_IOCTL_GETWMMPARAMS,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3,
+ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwmmparams"},
+ { IEEE80211_IOCTL_ADDPMKID,
+ IW_PRIV_TYPE_ADDPMKID | IW_PRIV_SIZE_FIXED, 0, "addpmkid"},
+};
+
+static void
+ar6000_ioctl_iwsetup(struct iw_handler_def *def)
+{
+#define N(a) (sizeof(a) / sizeof(a[0]))
+ def->private_args = (struct iw_priv_args *)ar6000_priv_args;
+ def->num_private_args = N(ar6000_priv_args);
+#undef N
+}
+
+static struct iw_handler_def ath_iw_handler_def = {
+#define N(a) (sizeof (a) / sizeof (a[0]))
+ .standard = (iw_handler *)ath_handlers,
+ .num_standard = N(ath_handlers),
+ .private = (iw_handler *)ath_priv_handlers,
+ .num_private = N(ath_priv_handlers),
+#undef N
+};
+
+void *
+a_netbuf_alloc(int size)
+{
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(AR6000_DATA_OFFSET + size);
+ skb_reserve(skb, AR6000_DATA_OFFSET);
+
+ return ((void *)skb);
+}
+
+void
+a_netbuf_free(void *bufPtr)
+{
+ struct sk_buff *skb = (struct sk_buff *)bufPtr;
+
+ dev_kfree_skb(skb);
+}
+
+A_UINT32
+a_netbuf_to_len(void *bufPtr)
+{
+ return (((struct sk_buff *)bufPtr)->len);
+}
+
+void *
+a_netbuf_to_data(void *bufPtr)
+{
+ return (((struct sk_buff *)bufPtr)->data);
+}
+
+/*
+ * Add len # of bytes to the beginning of the network buffer
+ * pointed to by bufPtr
+ */
+A_STATUS
+a_netbuf_push(void *bufPtr, A_INT32 len)
+{
+ skb_push((struct sk_buff *)bufPtr, len);
+
+ return A_OK;
+}
+
+/*
+ * Add len # of bytes to the end of the network buffer
+ * pointed to by bufPtr
+ */
+A_STATUS
+a_netbuf_put(void *bufPtr, A_INT32 len)
+{
+ skb_put((struct sk_buff *)bufPtr, len);
+
+ return A_OK;
+}
+
+/*
+ * Trim the network buffer pointed to by bufPtr to len # of bytes
+ */
+A_STATUS
+a_netbuf_trim(void *bufPtr, A_INT32 len)
+{
+ skb_trim((struct sk_buff *)bufPtr, len);
+
+ return A_OK;
+}
+
+/*
+ * Returns the number of bytes available to a a_netbuf_push()
+ */
+A_INT32
+a_netbuf_headroom(void *bufPtr)
+{
+ return (skb_headroom((struct sk_buff *)bufPtr));
+}
+
+/*
+ * Removes specified number of bytes from the beginning of the buffer
+ */
+A_STATUS
+a_netbuf_pull(void *bufPtr, A_INT32 len)
+{
+ skb_pull((struct sk_buff *)bufPtr, len);
+
+ return A_OK;
+}
+
+A_UINT32
+a_copy_to_user(void *to, const void *from, A_UINT32 n)
+{
+ return(copy_to_user(to, from, n));
+}
+
+A_UINT32
+a_copy_from_user(void *to, const void *from, A_UINT32 n)
+{
+ return(copy_from_user(to, from, n));
+}
+
+/* Get power mode command */
+static int
+ar6000_ioctl_get_power_mode(struct net_device *dev, struct ifreq *rq)
+{
+ AR_SOFTC_T *ar = (AR_SOFTC_T *)dev->priv;
+ WMI_POWER_MODE_CMD power_mode;
+ int ret = 0;
+
+ if (ar->arWmiReady == FALSE) {
+ return -EIO;
+ }
+
+ power_mode.powerMode = wmi_get_power_mode_cmd(ar->arWmi);
+ if (copy_to_user(rq->ifr_data, &power_mode, sizeof(WMI_POWER_MODE_CMD))) {
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
Added: developers/nbd/ar6k/ar6000/ar6000_drv.h
===================================================================
--- developers/nbd/ar6k/ar6000/ar6000_drv.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/ar6000/ar6000_drv.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions for the AR6000 linux driver.
+ *
+ */
+
+#ifndef _AR6000_H_
+#define _AR6000_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_AR6000 1
+#define AR6000_MAX_RX_BUFFERS 16
+#define AR6000_BUFFER_SIZE 1552
+#define AR6000_DATA_OFFSET 64
+#define AR6000_TX_TIMEOUT 10
+#define AR6000_ETH_ADDR_LEN 6
+#define AR6000_MAX_ENDPOINTS 4
+#define MAX_NODE_NUM 15
+#define MAX_COOKIE_NUM 150
+
+struct ar_wep_key {
+ A_UINT8 arKeyIndex;
+ A_UINT8 arKeyLen;
+ A_UINT8 arKey[64];
+} ;
+
+struct ar_node_mapping {
+ A_UINT8 macAddress[6];
+ A_UINT8 epId;
+ A_UINT8 txPending;
+};
+
+struct ar_cookie {
+ A_UINT32 arc_bp[2]; /* Must be first field */
+ struct ar_cookie *arc_list_next;
+};
+
+typedef struct ar6_softc {
+ struct net_device *arNetDev; /* net_device pointer */
+ void *arWmi;
+ int arTxPending[AR6000_MAX_ENDPOINTS];
+ int arTotalTxDataPending;
+ A_UINT8 arNumDataEndPts;
+ A_BOOL arWmiEnabled;
+ A_BOOL arWmiReady;
+ A_BOOL arConnected;
+ void *arHtcTarget;
+ void *arHifDevice;
+ spinlock_t arLock;
+ struct semaphore arSem;
+ int arRxBuffers[AR6000_MAX_ENDPOINTS];
+ int arSsidLen;
+ u_char arSsid[32];
+ A_UINT8 arNetworkType;
+ A_UINT8 arDot11AuthMode;
+ A_UINT8 arAuthMode;
+ A_UINT8 arPairwiseCrypto;
+ A_UINT8 arPairwiseCryptoLen;
+ A_UINT8 arGroupCrypto;
+ A_UINT8 arGroupCryptoLen;
+ A_UINT8 arDefTxKeyIndex;
+ struct ar_wep_key arWepKeyList[WMI_MAX_KEY_INDEX + 1];
+ A_UINT8 arBssid[6];
+ A_UINT8 arReqBssid[6];
+ A_UINT16 arChannelHint;
+ A_UINT16 arBssChannel;
+ A_UINT16 arListenInterval;
+ struct ar6000_version arVersion;
+ A_INT8 arRssi;
+ A_UINT8 arTxPwr;
+ A_BOOL arTxPwrSet;
+ A_INT32 arBitRate;
+ struct net_device_stats arNetStats;
+ struct iw_statistics arIwStats;
+ A_INT8 arNumChannels;
+ A_UINT16 arChannelList[32];
+ A_UINT32 arRegCode;
+ A_BOOL statsUpdatePending;
+ TARGET_STATS arTargetStats;
+ A_INT8 arMaxRetries;
+ A_UINT8 arPhyCapability;
+ AR6000_WLAN_STATE arWlanState;
+ struct ar_node_mapping arNodeMap[MAX_NODE_NUM];
+ A_UINT8 arIbssPsEnable;
+ A_UINT8 arNodeNum;
+ A_UINT8 arNexEpId;
+ struct ar_cookie *arCookieList;
+ A_BOOL arConnectPending;
+} AR_SOFTC_T;
+
+struct ar_giwscan_param {
+ char *current_ev;
+ char *end_buf;
+ A_BOOL firstPass;
+};
+
+#define AR6000_STAT_INC(ar, stat) (ar->arNetStats.stat++)
+
+#define AR6000_SPIN_LOCK(lock, param) do { \
+ if (irqs_disabled()) { \
+ AR_DEBUG_PRINTF("IRQs disabled:AR6000_LOCK\n"); \
+ } \
+ spin_lock_bh(lock); \
+} while (0)
+
+#define AR6000_SPIN_UNLOCK(lock, param) do { \
+ if (irqs_disabled()) { \
+ AR_DEBUG_PRINTF("IRQs disabled: AR6000_UNLOCK\n"); \
+ } \
+ spin_unlock_bh(lock); \
+} while (0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AR6000_H_ */
Added: developers/nbd/ar6k/bmi/Makefile
===================================================================
--- developers/nbd/ar6k/bmi/Makefile 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/bmi/Makefile 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1 @@
+obj-$(CONFIG_SDIO_AR6000_WLAN) += bmi.o
Added: developers/nbd/ar6k/bmi/bmi.c
===================================================================
--- developers/nbd/ar6k/bmi/bmi.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/bmi/bmi.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the routines that implement the Boot loader messaging
+ * interface
+ */
+
+#include "../include/hif.h"
+#include "../include/bmi.h"
+#include "../include/htc.h"
+#include "bmi_internal.h"
+
+/*
+Although we had envisioned BMI to run on top of HTC, this is not what the
+final implementation boiled down to on dragon. Its a part of BSP and does
+not use the HTC protocol either. On the host side, however, we were still
+living with the original idea. I think the time has come to accept the truth
+and separate it from HTC which has been carrying BMI's burden all this while.
+It shall make HTC state machine relatively simpler
+*/
+
+/* ------ Static Variables ------ */
+
+/* ------ Global Variable Declarations ------- */
+A_BOOL bmiDone;
+extern A_UINT32 debugbmi;
+
+#ifdef DEBUG
+#define AR_DEBUG_PRINTF(...) if (debugbmi) A_PRINTF(__VA_ARGS__);
+#else
+#define AR_DEBUG_PRINTF(...)
+#endif
+
+/* APIs visible to the driver */
+void
+BMIInit(void)
+{
+ bmiDone = FALSE;
+}
+
+A_STATUS
+BMIDone(HIF_DEVICE *device)
+{
+ A_STATUS status;
+ A_UINT32 cid;
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF("BMI Done: Enter (device: 0x%p)\n", device);
+ bmiDone = TRUE;
+ cid = BMI_DONE;
+
+ status = bmiBufferSend(device, (A_UCHAR *)&cid, sizeof(cid));
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+ AR_DEBUG_PRINTF("BMI Done: Exit\n");
+
+ return A_OK;
+}
+
+A_STATUS
+BMIGetTargetId(HIF_DEVICE *device, A_UINT32 *id)
+{
+ A_STATUS status;
+ A_UINT32 cid;
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF("BMI Get Target ID: Enter (device: 0x%p)\n", device);
+ cid = BMI_GET_TARGET_ID;
+
+ status = bmiBufferSend(device, (A_UCHAR *)&cid, sizeof(cid));
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+
+ status = bmiBufferReceive(device, (A_UCHAR *)id, sizeof(*id));
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to read from the device\n");
+ return A_ERROR;
+ }
+ AR_DEBUG_PRINTF("BMI Get Target ID: Exit (ID: 0x%x)\n", *id);
+
+ return A_OK;
+}
+
+A_STATUS
+BMIReadMemory(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ A_UINT32 remaining, rxlen;
+ const A_UINT32 header = sizeof(cid) + sizeof(address) + sizeof(length);
+ A_UCHAR data[BMI_DATASZ_MAX + header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Read Memory: Enter (device: 0x%p, address: 0x%x, length: %d)\n",
+ device, address, length);
+
+ cid = BMI_READ_MEMORY;
+
+ remaining = length;
+ while (remaining)
+ {
+ rxlen = (remaining < BMI_DATASZ_MAX) ? remaining : BMI_DATASZ_MAX;
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ A_MEMCPY(&data[offset], &rxlen, sizeof(rxlen));
+ offset += sizeof(length);
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+ status = bmiBufferReceive(device, data, rxlen);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to read from the device\n");
+ return A_ERROR;
+ }
+ A_MEMCPY(&buffer[length - remaining], data, rxlen);
+ remaining -= rxlen; address += rxlen;
+ }
+
+ AR_DEBUG_PRINTF("BMI Read Memory: Exit\n");
+ return A_OK;
+}
+
+A_STATUS
+BMIWriteMemory(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ A_UINT32 remaining, txlen;
+ const A_UINT32 header = sizeof(cid) + sizeof(address) + sizeof(length);
+ A_UCHAR data[BMI_DATASZ_MAX + header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Write Memory: Enter (device: 0x%p, address: 0x%x, length: %d)\n",
+ device, address, length);
+
+ cid = BMI_WRITE_MEMORY;
+
+ remaining = length;
+ while (remaining)
+ {
+ txlen = (remaining < (BMI_DATASZ_MAX - header)) ?
+ remaining : (BMI_DATASZ_MAX - header);
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ A_MEMCPY(&data[offset], &txlen, sizeof(txlen));
+ offset += sizeof(txlen);
+ A_MEMCPY(&data[offset], &buffer[length - remaining], txlen);
+ offset += txlen;
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+ remaining -= txlen; address += txlen;
+ }
+
+ AR_DEBUG_PRINTF("BMI Write Memory: Exit\n");
+
+ return A_OK;
+}
+
+A_STATUS
+BMIExecute(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 *param)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ const A_UINT32 header = sizeof(cid) + sizeof(address) + sizeof(*param);
+ A_UCHAR data[header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Execute: Enter (device: 0x%p, address: 0x%x, param: %d)\n",
+ device, address, *param);
+
+ cid = BMI_EXECUTE;
+
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ A_MEMCPY(&data[offset], param, sizeof(*param));
+ offset += sizeof(*param);
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+
+ status = bmiBufferReceive(device, data, sizeof(*param));
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to read from the device\n");
+ return A_ERROR;
+ }
+
+ A_MEMCPY(param, data, sizeof(*param));
+
+ AR_DEBUG_PRINTF("BMI Execute: Exit (param: %d)\n", *param);
+ return A_OK;
+}
+
+A_STATUS
+BMISetAppStart(HIF_DEVICE *device,
+ A_UINT32 address)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ const A_UINT32 header = sizeof(cid) + sizeof(address);
+ A_UCHAR data[header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Set App Start: Enter (device: 0x%p, address: 0x%x)\n",
+ device, address);
+
+ cid = BMI_SET_APP_START;
+
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF("BMI Set App Start: Exit\n");
+ return A_OK;
+}
+
+A_STATUS
+BMIReadSOCRegister(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 *param)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ const A_UINT32 header = sizeof(cid) + sizeof(address);
+ A_UCHAR data[header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Read SOC Register: Enter (device: 0x%p, address: 0x%x)\n",
+ device, address);
+
+ cid = BMI_READ_SOC_REGISTER;
+
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+
+ status = bmiBufferReceive(device, data, sizeof(*param));
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to read from the device\n");
+ return A_ERROR;
+ }
+ A_MEMCPY(param, data, sizeof(*param));
+
+ AR_DEBUG_PRINTF("BMI Read SOC Register: Exit (value: %d)\n", *param);
+ return A_OK;
+}
+
+A_STATUS
+BMIWriteSOCRegister(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 param)
+{
+ A_UINT32 cid;
+ A_STATUS status;
+ A_UINT32 offset;
+ const A_UINT32 header = sizeof(cid) + sizeof(address) + sizeof(param);
+ A_UCHAR data[header];
+
+ if (bmiDone) {
+ AR_DEBUG_PRINTF("Command disallowed\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF(
+ "BMI Write SOC Register: Enter (device: 0x%p, address: 0x%x, param: %d)\n",
+ device, address, param);
+
+ cid = BMI_WRITE_SOC_REGISTER;
+
+ offset = 0;
+ A_MEMCPY(&data[offset], &cid, sizeof(cid));
+ offset += sizeof(cid);
+ A_MEMCPY(&data[offset], &address, sizeof(address));
+ offset += sizeof(address);
+ A_MEMCPY(&data[offset], ¶m, sizeof(param));
+ offset += sizeof(param);
+ status = bmiBufferSend(device, data, offset);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to write to the device\n");
+ return A_ERROR;
+ }
+
+ AR_DEBUG_PRINTF("BMI Read SOC Register: Exit\n");
+ return A_OK;
+}
+
+/* BMI Access routines */
+A_STATUS
+bmiBufferSend(HIF_DEVICE *device,
+ A_UCHAR *buffer,
+ A_UINT32 length)
+{
+ A_STATUS status;
+ A_UINT32 timeout;
+ A_UINT32 address;
+ A_UCHAR cmdCredits;
+ HIF_REQUEST request;
+ A_UINT32 mboxAddress[HTC_MAILBOX_NUM_MAX];
+
+ HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_ADDR,
+ &mboxAddress, sizeof(mboxAddress));
+
+ cmdCredits = 0;
+ timeout = BMI_COMMUNICATION_TIMEOUT;
+ while(timeout-- && !cmdCredits) {
+ /* Read the counter register to get the command credits */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4;
+ status = HIFReadWrite(device, address, &cmdCredits, 1,
+ &request, NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to decrement the command credit count register\n");
+ return A_ERROR;
+ }
+ }
+
+ if (cmdCredits) {
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
+ HIF_SYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_INCREMENTAL_ADDRESS);
+ address = mboxAddress[ENDPOINT1];
+ status = HIFReadWrite(device, address, buffer, length, &request, NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to send the BMI data to the device\n");
+ return A_ERROR;
+ }
+ } else {
+ AR_DEBUG_PRINTF("BMI Communication timeout\n");
+ return A_ERROR;
+ }
+
+ return status;
+}
+
+A_STATUS
+bmiBufferReceive(HIF_DEVICE *device,
+ A_UCHAR *buffer,
+ A_UINT32 length)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ A_UINT32 timeout;
+ A_UCHAR cmdCredits;
+ HIF_REQUEST request;
+ A_UINT32 mboxAddress[HTC_MAILBOX_NUM_MAX];
+
+ HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_ADDR,
+ &mboxAddress, sizeof(mboxAddress));
+
+ cmdCredits = 0;
+ timeout = BMI_COMMUNICATION_TIMEOUT;
+ while(timeout-- && !cmdCredits) {
+ /* Read the counter register to get the command credits */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = COUNT_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 1;
+ status = HIFReadWrite(device, address, &cmdCredits, sizeof(cmdCredits),
+ &request, NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to decrement the command credit count register\n");
+ return A_ERROR;
+ }
+ status = A_ERROR;
+ }
+
+ if (cmdCredits) {
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
+ HIF_SYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_INCREMENTAL_ADDRESS);
+ address = mboxAddress[ENDPOINT1];
+ status = HIFReadWrite(device, address, buffer, length, &request, NULL);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF("Unable to read the BMI data from the device\n");
+ return A_ERROR;
+ }
+ } else {
+ AR_DEBUG_PRINTF("BMI Communication timeout\n");
+ return A_ERROR;
+ }
+
+ return status;
+}
Added: developers/nbd/ar6k/bmi/bmi_internal.h
===================================================================
--- developers/nbd/ar6k/bmi/bmi_internal.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/bmi/bmi_internal.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ */
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/hw/mbox_host_reg.h"
+#include "../include/AR6000_bmi.h"
+
+#define BMI_COMMUNICATION_TIMEOUT 100000
+
+A_STATUS
+bmiBufferSend(HIF_DEVICE *device,
+ A_UCHAR *buffer,
+ A_UINT32 length);
+
+A_STATUS
+bmiBufferReceive(HIF_DEVICE *device,
+ A_UCHAR *buffer,
+ A_UINT32 length);
Added: developers/nbd/ar6k/hif/Makefile
===================================================================
--- developers/nbd/ar6k/hif/Makefile 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/hif/Makefile 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SDIO_AR6000_WLAN) += hif.o
+
Added: developers/nbd/ar6k/hif/hif.c
===================================================================
--- developers/nbd/ar6k/hif/hif.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/hif/hif.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2004-2005 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the routines handling the interaction with the SDIO
+ * driver
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ops.h>
+#include "hif_internal.h"
+
+/* ------ Static Variables ------ */
+
+/* ------ Global Variable Declarations ------- */
+
+
+const struct sdio_device_id ar6k_id_table[] = {
+ { SDIO_DEVICE(SDIO_ANY_ID, 0x10b) },
+ { SDIO_DEVICE(SDIO_ANY_ID, 0x10a) },
+ { SDIO_DEVICE(SDIO_ANY_ID, 0x109) },
+ { SDIO_DEVICE(SDIO_ANY_ID, 0x108) },
+ {},
+};
+
+struct sdio_driver ar6k_driver = {
+ .name = "sdio_wlan",
+ .id_table = ar6k_id_table,
+ .probe = hifDeviceInserted,
+ .remove = hifDeviceRemoved,
+};
+
+HTC_CALLBACKS htcCallbacks;
+#if 0
+extern A_UINT32 onebitmode;
+extern A_UINT32 busspeedlow;
+extern A_UINT32 debughif;
+#endif
+
+#define PFX "ar6k_hif: "
+#ifdef DEBUG
+#define ATH_DEBUG_ERROR 1
+#define ATH_DEBUG_WARN 2
+#define ATH_DEBUG_TRACE 3
+#define _AR_DEBUG_PRINTX_ARG(arg...) arg
+#define AR_DEBUG_PRINTF(lvl, args) \
+ A_PRINTF(KERN_ALERT _AR_DEBUG_PRINTX_ARG args);
+#else
+#define AR_DEBUG_PRINTF(lvl, args)
+#endif
+
+/* ------ Functions ------ */
+void
+HIFRegisterCallbacks(HTC_CALLBACKS *callbacks)
+{
+ /* Store the callback and event handlers */
+ htcCallbacks.deviceInsertedHandler = callbacks->deviceInsertedHandler;
+ htcCallbacks.deviceRemovedHandler = callbacks->deviceRemovedHandler;
+ htcCallbacks.deviceSuspendHandler = callbacks->deviceSuspendHandler;
+ htcCallbacks.deviceResumeHandler = callbacks->deviceResumeHandler;
+ htcCallbacks.deviceWakeupHandler = callbacks->deviceWakeupHandler;
+ htcCallbacks.rwCompletionHandler = callbacks->rwCompletionHandler;
+ htcCallbacks.dsrHandler = callbacks->dsrHandler;
+
+ /* Register with bus driver core */
+ if (sdio_register_driver(&ar6k_driver))
+ printk(PFX "failed to register SDIO driver\n");
+}
+
+A_STATUS
+HIFReadWrite(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length,
+ HIF_REQUEST *request,
+ void *context)
+{
+ int ret;
+ u8 write, bang;
+
+ /* XXX: handle bigger block sizes */
+#if 0
+ if (request->dmode == HIF_BLOCK_BASIS) {
+ data->blocksz = HIF_MBOX_BLOCK_SIZE;
+ data->blocks = length / HIF_MBOX_BLOCK_SIZE;
+ count = data->blocks;
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("Block mode (BlockLen: %d, BlockCount: %d)\n",
+ data.blocksz, data.blocks));
+ } else if (request->dmode == HIF_BYTE_BASIS) {
+ data->blocksz = length;
+ data->blocks = 1;
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("Byte mode (BlockLen: %d, BlockCount: %d)\n",
+ data.blocksz, data.blocks));
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Invalid data mode: %d\n", request->dmode));
+ goto err3;
+ }
+#endif
+
+ if ((address >= HIF_MBOX_START_ADDR(0)) &&
+ (address <= HIF_MBOX_END_ADDR(3)))
+ {
+ /*
+ * Mailbox write. Adjust the address so that the last byte
+ * falls on the EOM address.
+ */
+ address += (HIF_MBOX_WIDTH - length);
+ }
+
+ write = (request->direction == HIF_WRITE);
+ bang = !(request->amode == HIF_INCREMENTAL_ADDRESS);
+
+ printk("rwreq: %s at 0x%x [bang=%d, len=%d, async=%d]\n", (write ? "write" : "read"), address, bang, length, (request->emode == HIF_ASYNCHRONOUS));
+ /* XXX: does not handle block mode with blocksz != 1 properly yet */
+ /* Send the command out */
+ if (request->emode == HIF_ASYNCHRONOUS) {
+ mmc_async_io_rw_extended(device->card, write, device->num, address, bang, buffer, length, hifRWCompletionHandler, context);
+ return A_OK;
+ }
+
+ ret = mmc_io_rw_extended(device->card, write, device->num, address, bang, buffer, length);
+ printk("buf: 0x%02x\n", *((u8*)buffer));
+ A_MDELAY(100);
+ return (ret == MMC_ERR_NONE ? A_OK : A_ERROR);
+}
+
+A_STATUS
+HIFConfigureDevice(struct sdio_func *func, HIF_DEVICE_CONFIG_OPCODE opcode,
+ void *config, A_UINT32 configLen)
+{
+ A_UINT32 count;
+
+ switch(opcode) {
+ case HIF_DEVICE_GET_MBOX_BLOCK_SIZE:
+ ((A_UINT32 *)config)[0] = HIF_MBOX0_BLOCK_SIZE;
+ ((A_UINT32 *)config)[1] = HIF_MBOX1_BLOCK_SIZE;
+ ((A_UINT32 *)config)[2] = HIF_MBOX2_BLOCK_SIZE;
+ ((A_UINT32 *)config)[3] = HIF_MBOX3_BLOCK_SIZE;
+ break;
+
+ case HIF_DEVICE_GET_MBOX_ADDR:
+ for (count = 0; count < 4; count ++) {
+ ((A_UINT32 *)config)[count] = HIF_MBOX_START_ADDR(count);
+ }
+ break;
+
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Invalid configuration opcode: %d\n", opcode));
+ return A_ERROR;
+ }
+
+ return A_OK;
+}
+
+void
+HIFShutDownDevice(struct sdio_func *func)
+{
+ sdio_disable_func(func);
+#if 0
+ A_UINT8 data;
+ A_UINT32 count;
+ SDIO_STATUS status;
+ SDCONFIG_BUS_MODE_DATA busSettings;
+ SDCONFIG_FUNC_ENABLE_DISABLE_DATA fData;
+
+ if (device != NULL) {
+ /* Remove the allocated current if any */
+ status = SDLIB_IssueConfig(device, SDCONFIG_FUNC_FREE_SLOT_CURRENT, NULL, 0);
+
+ /* Disable the card */
+ fData.EnableFlags = SDCONFIG_DISABLE_FUNC;
+ fData.TimeOut = 1;
+ status = SDLIB_IssueConfig(device, SDCONFIG_FUNC_ENABLE_DISABLE,
+ &fData, sizeof(fData));
+
+ /* Perform a soft I/O reset */
+ data = SDIO_IO_RESET;
+ status = SDLIB_IssueCMD52(device, 0, SDIO_IO_ABORT_REG,
+ &data, 1, 1);
+
+ /*
+ * WAR - Codetelligence driver does not seem to shutdown correctly in 1
+ * bit mode. By default it configures the HC in the 4 bit. Its later in
+ * our driver that we switch to 1 bit mode. If we try to shutdown, the
+ * driver hangs so we revert to 4 bit mode, to be transparent to the
+ * underlying bus driver.
+ */
+ if (onebitmode) {
+ ZERO_OBJECT(busSettings);
+ busSettings.BusModeFlags = device->pHcd->CardProperties.BusMode;
+ SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
+ SDCONFIG_BUS_WIDTH_4_BIT);
+
+ /* Issue config request to change the bus width to 4 bit */
+ status = SDLIB_IssueConfig(device->handle, SDCONFIG_BUS_MODE_CTRL,
+ &busSettings,
+ sizeof(SDCONFIG_BUS_MODE_DATA));
+ }
+
+ } else {
+ /* Unregister with bus driver core */
+ status = SDIO_UnregisterFunction(&FunctionContext.function);
+ }
+#endif
+}
+
+void
+hifRWCompletionHandler(struct mmc_request *mrq)
+{
+ A_STATUS status;
+ void *context;
+
+ if (mrq->cmd->error == MMC_ERR_NONE) {
+ status = A_OK;
+ } else {
+ status = A_ERROR;
+ }
+ context = (void *)mrq->done_data;
+
+ htcCallbacks.rwCompletionHandler(context, status);
+ kfree(mrq->data->sg);
+ kfree(mrq->data);
+ kfree(mrq->cmd);
+ kfree(mrq);
+}
+
+void
+hifIRQHandler(struct sdio_func *func)
+{
+ printk("DEBUG: %s\n", __func__);
+ htcCallbacks.dsrHandler(func);
+}
+
+
+int hifDeviceInserted(struct sdio_func *func, const struct sdio_device_id *id)
+{
+ int ret;
+
+ sdio_claim_host(func);
+ ret = sdio_enable_func(func);
+ if (ret)
+ return ret;
+
+/* XXX: do we need 1-bit mode? */
+#if 0
+ BOOL enabled;
+ A_UINT8 data;
+ A_UINT32 count;
+ int status;
+
+ /* Configure the SDIO Bus Width */
+ if (onebitmode) {
+ data = SD_BUS_WIDTH_1;
+ status = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IF, 1, &data)
+ if (status != MMC_ERR_NONE) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Unable to set the bus width to 1 bit\n"));
+ return -EINVAL;
+ }
+ }
+
+ /* Get current bus flags */
+ ZERO_OBJECT(busSettings);
+ busSettings.BusModeFlags = handle->pHcd->CardProperties.BusMode;
+ if (onebitmode) {
+ SDCONFIG_SET_BUS_WIDTH(busSettings.BusModeFlags,
+ SDCONFIG_BUS_WIDTH_1_BIT);
+ }
+ busSettings.ClockRate = (busspeedlow ? SDIO_CLOCK_FREQUENCY_REDUCED :
+ SDIO_CLOCK_FREQUENCY_DEFAULT);
+
+ /* Issue config request to override clock rate */
+ status = SDLIB_IssueConfig(handle, SDCONFIG_BUS_MODE_CTRL, &busSettings,
+ sizeof(SDCONFIG_BUS_MODE_DATA));
+ if (!SDIO_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Unable to configure the host clock\n"));
+ return FALSE;
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("Configured clock: %d, Maximum clock: %d\n",
+ busSettings.ActualClockRate,
+ HIF_DEVICE_GET_MAX_CLOCK(handle)));
+ }
+#endif
+
+ /*
+ * Check if the target supports block mode. This result of this check
+ * can be used to implement the HIFReadWrite API.
+ * XXX: probably unnecessary, stack currently only does 1 byte blocks
+ */
+#if 0
+ if (HIF_DEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle)) {
+ /* Limit block size to operational block limit or card function
+ capability */
+ maxBlockSize = min(HIF_DEVICE_GET_OPER_BLOCK_LEN(handle),
+ HIF_DEVICE_GET_SDIO_FUNC_MAXBLKSIZE(handle));
+
+ /* check if the card support multi-block transfers */
+ if (!(HIF_DEVICE_GET_SDIOCARD_CAPS(handle) & SDIO_CAPS_MULTI_BLOCK)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Byte basis only\n"));
+
+ /* Limit block size to max byte basis */
+ maxBlockSize = min(maxBlockSize,
+ (A_UINT16)SDIO_MAX_LENGTH_BYTE_BASIS);
+ maxBlocks = 1;
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Multi-block capable\n"));
+ maxBlocks = HIF_DEVICE_GET_OPER_BLOCKS(handle);
+ status = SDLIB_SetFunctionBlockSize(handle, HIF_MBOX_BLOCK_SIZE);
+ if (!SDIO_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Failed to set block size. Err:%d\n", status));
+ return FALSE;
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE,
+ ("Bytes Per Block: %d bytes, Block Count:%d \n",
+ maxBlockSize, maxBlocks));
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Function does not support Block Mode!\n"));
+ return FALSE;
+ }
+
+ /* Allocate the slot current */
+ status = SDLIB_GetDefaultOpCurrent(handle, &slotCurrent.SlotCurrent);
+ if (SDIO_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Allocating Slot current: %d mA\n",
+ slotCurrent.SlotCurrent));
+ status = SDLIB_IssueConfig(handle, SDCONFIG_FUNC_ALLOC_SLOT_CURRENT,
+ &slotCurrent, sizeof(slotCurrent));
+ if (!SDIO_SUCCESS(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERROR,
+ ("Failed to allocate slot current %d\n", status));
+ return FALSE;
+ }
+ }
+#endif
+
+ /*
+ * Adding a wait of around a second before we issue the very first
+ * command to dragon. During the process of loading/unloading the
+ * driver repeatedly it was observed that we get a data timeout
+ * while accessing function 1 registers in the chip. The theory at
+ * this point is that some initialization delay in dragon is
+ * causing the SDIO state in dragon core to be not ready even after
+ * the ready bit indicates that function 1 is ready. Accomodating
+ * for this behavior by adding some delay in the driver before it
+ * issues the first command after switching on dragon. Need to
+ * investigate this a bit more - TODO
+ */
+ A_MDELAY(1000);
+ /* Inform HTC */
+ if ((htcCallbacks.deviceInsertedHandler(func)) != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRACE, ("Device rejected\n"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+HIFAckInterrupt(struct sdio_func *func)
+{
+ /* XXX; should done by the stack already, not sure */
+#if 0
+ SDIO_STATUS status;
+ /* Acknowledge our function IRQ */
+ status = SDLIB_IssueConfig(device->handle, SDCONFIG_FUNC_ACK_IRQ,
+ NULL, 0);
+#endif
+}
+
+void
+HIFUnMaskInterrupt(struct sdio_func *func)
+{
+ printk("DEBUG: %s\n", __func__);
+ sdio_claim_irq(func, hifIRQHandler);
+}
+
+void HIFMaskInterrupt(struct sdio_func *func)
+{
+ printk("DEBUG: %s\n", __func__);
+ sdio_release_irq(func);
+}
+
+void
+hifDeviceRemoved(struct sdio_func *func)
+{
+ printk("DEBUG: %s\n", __func__);
+ sdio_release_host(func);
+ htcCallbacks.deviceRemovedHandler(func);
+}
+
Added: developers/nbd/ar6k/hif/hif_internal.h
===================================================================
--- developers/nbd/ar6k/hif/hif_internal.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/hif/hif_internal.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ $ATH_LICENSE_AR6K0$
+ *
+ */
+
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/hif.h"
+
+#define MANUFACTURER_ID_BASE 0x100
+#define FUNCTION_CLASS 0x0
+#define MANUFACTURER_CODE 0x271
+
+#define BUS_REQUEST_MAX_NUM 32
+
+#define SDIO_CLOCK_FREQUENCY_DEFAULT 24000000
+#define SDIO_CLOCK_FREQUENCY_REDUCED 12000000
+
+#define SDWLAN_ENABLE_DISABLE_TIMEOUT 20
+#define FLAGS_CARD_ENAB 0x02
+#define FLAGS_CARD_IRQ_UNMSK 0x04
+
+#define HIF_MBOX_BLOCK_SIZE 128
+#define HIF_MBOX_BASE_ADDR 0x800
+#define HIF_MBOX_WIDTH 0x800
+#define HIF_MBOX0_BLOCK_SIZE 1
+#define HIF_MBOX1_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+#define HIF_MBOX2_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+#define HIF_MBOX3_BLOCK_SIZE HIF_MBOX_BLOCK_SIZE
+
+#define HIF_MBOX_START_ADDR(mbox) \
+ HIF_MBOX_BASE_ADDR + mbox * HIF_MBOX_WIDTH
+
+#define HIF_MBOX_END_ADDR(mbox) \
+ HIF_MBOX_START_ADDR(mbox) + HIF_MBOX_WIDTH - 1
+
+#if 0
+typedef struct target_function_context {
+ struct sdio_func function; /* function description of the bus driver */
+ OS_SEMAPHORE instanceSem; /* instance lock. Unused */
+ SDLIST instanceList; /* list of instances. Unused */
+} TARGET_FUNCTION_CONTEXT;
+#endif
+
+int
+hifDeviceInserted(struct sdio_func *func, const struct sdio_device_id *id);
+
+void
+hifDeviceRemoved(struct sdio_func *func);
+
+void
+hifRWCompletionHandler(struct mmc_request *mrq);
+
+void
+hifIRQHandler(struct sdio_func *func);
+
+
Added: developers/nbd/ar6k/htc/Makefile
===================================================================
--- developers/nbd/ar6k/htc/Makefile 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/Makefile 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,5 @@
+#
+# SDIO ar6000 wlan htc function driver
+#
+obj-$(CONFIG_SDIO_AR6000_WLAN) += htc.o htc_events.o htc_recv.o htc_send.o htc_utils.o
+
Added: developers/nbd/ar6k/htc/htc.c
===================================================================
--- developers/nbd/ar6k/htc/htc.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the HTC APIs that are exposed to higher layers.
+ */
+
+#include "htc_internal.h"
+
+/* ------ Global Variable Declarations ------- */
+HTC_TARGET *AtherosTargetList[HIF_MAX_DEVICES];
+HTC_GLOBAL_EVENT_TABLE AtherosEventTable;
+A_MUTEX_T creditCS, counterCS, instanceCS;
+A_WAITQUEUE_HEAD htcEvent;
+
+#ifdef DEBUG
+extern A_UINT32 debughtc;
+extern A_UINT32 txcreditsavailable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditsconsumed[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditintrenable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX];
+#endif
+
+extern int tx_attempt[HTC_MAILBOX_NUM_MAX]; /* Num of attempts to add */
+extern int tx_post[HTC_MAILBOX_NUM_MAX]; /* Num of attemps succeded */
+extern int tx_complete[HTC_MAILBOX_NUM_MAX]; /* Num of tx complete */
+
+/* Initializes the HTC module */
+A_STATUS
+HTCInit(void)
+{
+ HTC_CALLBACKS htcCallbacks;
+ static A_BOOL HTCInitialized = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Enter\n"));
+ if (HTCInitialized) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Exit\n"));
+ return A_OK;
+ }
+
+ A_MEMZERO(&AtherosEventTable, sizeof(HTC_GLOBAL_EVENT_TABLE));
+ A_MEMZERO(&htcCallbacks, sizeof(HTC_CALLBACKS));
+ A_INIT_WAITQUEUE_HEAD(&htcEvent);
+
+ htcCallbacks.deviceInsertedHandler = htcTargetInsertedHandler;
+ htcCallbacks.deviceRemovedHandler = htcTargetRemovedHandler;
+ htcCallbacks.rwCompletionHandler = htcRWCompletionHandler;
+#ifdef CF
+ htcCallbacks.deviceInterruptEnabler = htcInterruptEnabler;
+ htcCallbacks.deviceInterruptDisabler = htcInterruptDisabler;
+#endif /* CF */
+
+ htcCallbacks.dsrHandler = htcDSRHandler;
+ HIFRegisterCallbacks(&htcCallbacks);
+ HTCInitialized = TRUE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCInit: Exit\n"));
+ return A_OK;
+}
+
+/* Enables Dragon interrupts */
+A_STATUS
+HTCStart(HTC_TARGET *target)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Enter\n"));
+
+ /* Unmask the host controller interrupts */
+ HIFUnMaskInterrupt(target->device);
+
+ /* Enable all the interrupts except for the dragon interrupt */
+ target->table.int_status_enable = INT_STATUS_ENABLE_ERROR_SET(0x01) |
+ INT_STATUS_ENABLE_CPU_SET(0x01) |
+ INT_STATUS_ENABLE_COUNTER_SET(0x01) |
+ INT_STATUS_ENABLE_MBOX_DATA_SET(0x0F);
+
+ /* Set up the CPU Interrupt Status Register */
+ target->table.cpu_int_status_enable = CPU_INT_STATUS_ENABLE_BIT_SET(0x00);
+
+ /* Set up the Error Interrupt Status Register */
+ target->table.error_status_enable =
+ ERROR_STATUS_ENABLE_RX_UNDERFLOW_SET(0x01) |
+ ERROR_STATUS_ENABLE_TX_OVERFLOW_SET(0x01);
+
+ /* Set up the Counter Interrupt Status Register */
+ target->table.counter_int_status_enable =
+ COUNTER_INT_STATUS_ENABLE_BIT_SET(0xFF);
+
+ /* Write to the register */
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_status_enable, 4, &request, NULL);
+ if (status != A_OK) {
+ /* Can't write it for some reason */
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Failed to enable INT_STATUS_ENABLE | CPU_INT_STATUS_ENABLE | ERROR_STATUS_ENABLE | COUNTER_INT_STATUS_ENABLE, err: %d\n", status));
+ HTCStop(target);
+ return status;
+ }
+
+#ifdef DEBUG
+ txcreditintrenable[ENDPOINT1] += 1;
+ txcreditintrenable[ENDPOINT2] += 1;
+ txcreditintrenable[ENDPOINT3] += 1;
+ txcreditintrenable[ENDPOINT4] += 1;
+ txcreditintrenableaggregate[ENDPOINT1] += 1;
+ txcreditintrenableaggregate[ENDPOINT2] += 1;
+ txcreditintrenableaggregate[ENDPOINT3] += 1;
+ txcreditintrenableaggregate[ENDPOINT4] += 1;
+#endif /* DEBUG */
+
+ /* Wait on a timed semaphore that will get signalled once the block
+ size negotiation with the target has completed. Furthermore, we have
+ to do it only once during the lifetime of the target detection */
+ if (!target->ready) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Waiting for the block size negotiation to finish\n"));
+ A_WAIT_EVENT_INTERRUPTIBLE_TIMEOUT(htcEvent, (target->ready == TRUE),
+ HTC_TARGET_RESPONSE_TIMEOUT);
+
+ if (target->ready) {
+ status = A_OK;
+ } else {
+ status = A_ERROR;
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Failed to negotiate the block sizes\n"));
+ HTCStop(target);
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStart Exit\n"));
+ return status;
+}
+
+/*
+ * Provides an interface for the higher layer module to register for
+ * different events supported by the HTC module
+ */
+A_STATUS
+HTCEventReg(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId, HTC_EVENT_HANDLER eventHandler,
+ void *param)
+{
+ /*
+ * Add the event handler against the specified event and store it in
+ * the event table
+ */
+ A_STATUS status;
+ HTC_ENDPOINT *endPoint;
+ HTC_EVENT_INFO eventInfo;
+ HTC_DATA_REQUEST_QUEUE *sendQueue, *recvQueue;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("HTCEventReg: Enter (eventId: 0x%x, endPointId: %d)\n",
+ eventId, endPointId));
+
+ if (eventHandler) {
+ if ((status = addToEventTable(target, endPointId, eventId,
+ eventHandler, param)) != A_OK)
+ {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Could not add the event 0x%x to the event table\n", eventId));
+ return status;
+ }
+ }
+
+ switch(eventId) {
+ case HTC_TARGET_AVAILABLE:
+ if (eventHandler != NULL) {
+ /*
+ * Dispatch a Target Available event for all the targets
+ * present. Iterate through the global list of targets but
+ * currently we shall simply look for the first instance
+ */
+ target = AtherosTargetList[0];
+ if (target != NULL) {
+ FRAME_EVENT(eventInfo, (A_UCHAR *)target->device,
+ sizeof(HIF_DEVICE *), sizeof(HIF_DEVICE *),
+ A_OK, NULL);
+ dispatchEvent(target, ENDPOINT_UNUSED, eventId, &eventInfo);
+ }
+ } else {
+ /* Initiate a shut down procedure */
+ }
+ break;
+
+ case HTC_TARGET_UNAVAILABLE:
+ break;
+
+ case HTC_BUFFER_RECEIVED:
+ if (eventHandler == NULL) {
+ /* Flush the queue before unregistering the event handler */
+ endPoint = &target->endPoint[endPointId];
+ recvQueue = &endPoint->recvQueue;
+ flushMboxQueue(endPoint, recvQueue, HTC_BUFFER_RECEIVED);
+ }
+ break;
+
+ case HTC_SKB_RECEIVED:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("skb not handled currently\n"));
+ break;
+
+ case HTC_BUFFER_SENT:
+ if (eventHandler == NULL) {
+ /* Flush the queue before unregistering the event handler */
+ endPoint = &target->endPoint[endPointId];
+ sendQueue = &endPoint->sendQueue;
+ flushMboxQueue(endPoint, sendQueue, HTC_BUFFER_SENT);
+ }
+ break;
+
+ case HTC_SKB_SENT:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("skb not handled currently\n"));
+ break;
+
+ case HTC_DATA_AVAILABLE:
+ /*
+ * Dispatch a data available event with the length. We are
+ * not handling this specific case currently because registering
+ * for HTC_DATA_AVAILABLE event is a part of the discipline
+ * that is imposed before one starts using HTC
+ */
+ break;
+
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Unknown Event ID: 0x%x\n", eventId));
+ return A_EINVAL;
+ }
+
+ /* Check if its a call for registering the event or unregistering it */
+ if (eventHandler == NULL) {
+ if ((status = removeFromEventTable(target, endPointId,
+ eventId)) != A_OK)
+ {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Could not remove the event 0x%x from the event table\n", eventId));
+ return status;
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCEventReg: Exit\n"));
+ return A_OK;
+}
+
+/*
+ * Commit an address to either WINDOW_WRITE_ADDR_REG or to
+ * WINDOW_READ_ADDR_REG. We write the least significan byte (LSB)
+ * last, since it triggers the read/write.
+ */
+static void
+_WRITE_WINDOW_ADDR(HTC_TARGET *target, A_UINT32 whichreg, A_UINT32 value)
+{
+ A_UINT32 window_addr;
+ HIF_REQUEST request;
+ A_STATUS status;
+ A_UINT32 address;
+
+ window_addr = value;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+
+ address = getRegAddr(whichreg, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address+1,
+ (A_UCHAR *)&window_addr+1, 3, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ status = HIFReadWrite(target->device, address,
+ (A_UCHAR *)&window_addr, 1, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+}
+
+void
+HTCStop(HTC_TARGET *target)
+{
+ A_UINT32 count;
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ A_UINT32 window_data;
+ HTC_ENDPOINT *endPoint;
+ HTC_REG_REQUEST_LIST *regList;
+ HTC_REG_REQUEST_ELEMENT *element;
+ HTC_DATA_REQUEST_QUEUE *sendQueue;
+ HTC_DATA_REQUEST_QUEUE *recvQueue;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStop: Enter"));
+
+ /* Disable all the dragon interrupts */
+ target->table.int_status_enable = 0;
+ target->table.cpu_int_status_enable = 0;
+ target->table.error_status_enable = 0;
+ target->table.counter_int_status_enable = 0;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_status_enable, 4, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ /* Disable the host controller interrupts */
+ HIFMaskInterrupt(target->device);
+
+ /* Flush all the queues and return the buffers to their owner */
+ for (count = ENDPOINT1; count <= ENDPOINT4; count ++) {
+ endPoint = &target->endPoint[count];
+
+ /* Decrement the number of credits consumed */
+ if (endPoint->txCreditsConsumed) {
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
+ HIF_SYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_FIXED_ADDRESS);
+ address = getRegAddr(TX_CREDIT_COUNTER_DECREMENT_REG, count);
+ status = HIFReadWrite(target->device, address,
+ endPoint->txCreditsAvailable,
+ endPoint->txCreditsConsumed, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+ }
+
+ SET_TX_CREDITS_AVAILABLE(endPoint, 0);
+ SET_TX_CREDITS_CONSUMED(endPoint, 0);
+
+#ifdef DEBUG
+ txcreditsavailable[count] = GET_TX_CREDITS_AVAILABLE(endPoint);
+ txcreditsconsumed[count] = GET_TX_CREDITS_CONSUMED(endPoint);
+#endif
+
+ endPoint->txCreditsIntrEnable = FALSE;
+ endPoint->rxLengthPending = 0;
+ endPoint->enabled = FALSE;
+
+ /* Flush the Pending Receive Queue */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Flushing the recv queue & returning the buffers\n"));
+
+ recvQueue = &endPoint->recvQueue;
+ flushMboxQueue(endPoint, recvQueue, HTC_BUFFER_RECEIVED);
+
+ /* Flush the Pending Send Queue */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Flushing the send queue & returning the buffers\n"));
+ sendQueue = &endPoint->sendQueue;
+ flushMboxQueue(endPoint, sendQueue, HTC_BUFFER_SENT);
+ }
+
+ /* Clear the tx counters */
+ memset(tx_attempt, 0, sizeof(tx_attempt));
+ memset(tx_post, 0, sizeof(tx_post));
+ memset(tx_complete, 0, sizeof(tx_complete));
+
+ /* Attempting a force reset of the target */
+ window_data = RESET_CONTROL_COLD_RST_MASK;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(WINDOW_DATA_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address, (A_UCHAR *)&window_data,
+ 4, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ _WRITE_WINDOW_ADDR(target, WINDOW_WRITE_ADDR_REG, RESET_CONTROL_ADDRESS);
+
+ /*
+ * Read back the RESET CAUSE register to ensure that the cold reset
+ * went through.
+ */
+ A_MDELAY(2000); /* 2 second delay to allow dragon to settle down */
+ _WRITE_WINDOW_ADDR(target, WINDOW_READ_ADDR_REG, RESET_CAUSE_ADDRESS);
+
+ window_data = 0;
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(WINDOW_DATA_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address, (A_UCHAR *)&window_data,
+ 4, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("window data: %d\n", window_data));
+ window_data &= RESET_CAUSE_LAST_MASK;
+
+ if (window_data != 2) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to cold reset the target\n"));
+ }
+
+ /*
+ * Ensure that all the pending asynchronous register read/writes have
+ * been finished.
+ */
+ regList = &target->regList;
+ for (count = 0; count < HTC_REG_REQUEST_LIST_SIZE; count ++) {
+ element = ®List->element[count];
+ AR_DEBUG_ASSERT(IS_ELEMENT_FREE(element));
+ }
+
+ /* Initialize the shadow copy of the target register table */
+ A_MEMZERO(&target->table, sizeof(HTC_REGISTER_TABLE));
+ target->ready = FALSE;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCStop: Exit"));
+}
+
+
+void
+HTCShutDown(HTC_TARGET *target)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCShutDown: Enter\n"));
+
+ if (target != NULL) {
+ HIFShutDownDevice(target->device);
+ delTargetInstance(target);
+ A_MEMZERO(target, sizeof(HTC_TARGET));
+ A_FREE(target);
+ } else {
+ HIFShutDownDevice(NULL);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HTCShutDown: Exit\n"));
+}
Added: developers/nbd/ar6k/htc/htc_events.c
===================================================================
--- developers/nbd/ar6k/htc/htc_events.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc_events.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the routines handling the different events and callbacks
+ * from the hardware interface layer.
+ */
+
+#include "htc_internal.h"
+
+/* ------ Global Variable Declarations ------- */
+extern A_MUTEX_T instanceCS, counterCS, creditCS;
+extern A_WAITQUEUE_HEAD htcEvent;
+
+#ifdef DEBUG
+extern A_UINT32 debughtc;
+extern A_UINT32 txcreditsavailable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditsconsumed[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditintrenable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX];
+#endif
+
+extern A_UINT32 tx_complete[HTC_MAILBOX_NUM_MAX]; /* Num of tx complete */
+
+/* ------ Static Variables ------ */
+
+
+/* ------ Functions ------ */
+#ifdef CF
+A_STATUS htcInterruptEnabler(HIF_DEVICE *device) {
+
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ HTC_TARGET *target;
+
+ target = getTargetInstance(device);
+ AR_DEBUG_ASSERT(target != NULL);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("htcInterruptEnabler Enter target: 0x%p\n", target));
+
+ target->table.int_status_enable = INT_STATUS_ENABLE_ERROR_SET(0x01) |
+ INT_STATUS_ENABLE_CPU_SET(0x01) |
+ INT_STATUS_ENABLE_COUNTER_SET(0x01) |
+ INT_STATUS_ENABLE_MBOX_DATA_SET(0x0F);
+ /* Reenable Dragon Interrupts */
+
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_status_enable, 1,
+ &request, NULL);
+
+ AR_DEBUG_ASSERT(status == A_OK);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,("htcInterruptEnabler Exit\n"));
+
+ return A_OK;
+}
+#endif /* CF */
+
+
+A_STATUS
+htcRWCompletionHandler(void *context,
+ A_STATUS status)
+{
+ HTC_QUEUE_ELEMENT *element;
+
+ element = (HTC_QUEUE_ELEMENT *)context;
+ AR_DEBUG_ASSERT(element != NULL);
+
+ return(element->completionCB(element, status));
+}
+
+A_STATUS
+htcTxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status)
+{
+ HTC_TARGET *target;
+ HTC_ENDPOINT_ID endPointId;
+ HTC_ENDPOINT *endPoint;
+ HTC_EVENT_INFO eventInfo;
+ HTC_MBOX_BUFFER *mboxBuffer;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcTxCompletionCB - Enter\n"));
+
+ /* Get the context */
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ AR_DEBUG_ASSERT(mboxBuffer != NULL);
+ endPoint = mboxBuffer->endPoint;
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("mboxBuffer: 0x%p, buffer: 0x%p, endPoint(%d): 0x%p, target: 0x%p\n", mboxBuffer, mboxBuffer->buffer, endPointId, endPoint, target));
+
+ /* Return the buffer to the user if the transmission was not successful */
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
+ ("Frame transmission failed\n"));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
+ ("EndPoint: %d, Tx credits available: %d\n",
+ endPointId, GET_TX_CREDITS_AVAILABLE(endPoint)));
+ /*
+ * In the failure case it is possible that while queueing of the
+ * request itself it returned an error status in which case we
+ * would have dispatched an event and freed the element there
+ * itself. Ideally if it failed to queue the request then it
+ * should not generate a callback but we are being a little
+ * conservative.
+ */
+ if (!(IS_ELEMENT_FREE(element))) {
+ mboxBuffer->buffer += HTC_HEADER_LEN;
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer,
+ mboxBuffer->bufferLength, mboxBuffer->actualLength,
+ A_ECANCELED, mboxBuffer->cookie);
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+ dispatchEvent(target, endPointId, HTC_BUFFER_SENT, &eventInfo);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcTxCompletionCB - Exit\n"));
+ }
+ return A_OK;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Frame transmission complete\n"));
+
+ /*
+ * The user should see the actual length and buffer length
+ * to be the same. In case of block mode, we use the actual length
+ * parameter to reflect the total number of bytes transmitted after
+ * padding.
+ */
+ mboxBuffer->actualLength = mboxBuffer->bufferLength;
+ mboxBuffer->buffer += HTC_HEADER_LEN;
+
+ /*
+ * Return the transmit buffer to the user through the HTC_BUFFER_SENT
+ * event indicating that the frame was transmitted successfully.
+ */
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer, mboxBuffer->bufferLength,
+ mboxBuffer->actualLength, A_OK, mboxBuffer->cookie);
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+
+ tx_complete[endPointId] += 1;
+
+ dispatchEvent(target, endPointId, HTC_BUFFER_SENT, &eventInfo);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcTxCompletionCB - Exit\n"));
+
+ return A_OK;
+}
+
+A_STATUS
+htcBlkSzNegCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status)
+{
+ HTC_TARGET *target;
+ HTC_ENDPOINT *endPoint;
+ HIF_REQUEST request;
+ HTC_MBOX_BUFFER *mboxBuffer;
+ HTC_REG_REQUEST_ELEMENT *regElement;
+ A_UINT32 address;
+
+ /* Get the context */
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ AR_DEBUG_ASSERT(mboxBuffer != NULL);
+ endPoint = mboxBuffer->endPoint;
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+
+ /* Recycle the request element */
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+ element->completionCB = htcTxCompletionCB;
+
+ if (status == A_OK) {
+ /* Mark the state to be ready */
+ endPoint->enabled = TRUE;
+
+ /* Set the state of the target as ready */
+ if (target->endPoint[ENDPOINT1].enabled &&
+ target->endPoint[ENDPOINT2].enabled &&
+ target->endPoint[ENDPOINT3].enabled &&
+ target->endPoint[ENDPOINT4].enabled )
+ {
+ /* Send the INT_WLAN interrupt to the target */
+ target->table.int_wlan = 1;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_FIXED_ADDRESS);
+ address = getRegAddr(INT_WLAN_REG, ENDPOINT_UNUSED);
+ regElement = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(regElement != NULL);
+ FILL_REG_BUFFER(regElement, &target->table.int_wlan, 1,
+ INT_WLAN_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_wlan,
+ 1, &request, regElement);
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if(status == A_OK) {
+ regElement->completionCB(regElement, status);
+ }
+#endif
+ }
+ }
+
+ return A_OK;
+}
+
+A_STATUS
+htcRxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status)
+{
+ HTC_TARGET *target;
+ HTC_ENDPOINT *endPoint;
+ HTC_EVENT_INFO eventInfo;
+ HTC_ENDPOINT_ID endPointId;
+ HTC_MBOX_BUFFER *mboxBuffer;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcRxCompletionCB - Enter\n"));
+
+ /* Get the context */
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ AR_DEBUG_ASSERT(mboxBuffer != NULL);
+ endPoint = mboxBuffer->endPoint;
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV,
+ ("mboxBuffer: 0x%p, buffer: 0x%p, endPoint(%d): 0x%p, target: 0x%p\n", mboxBuffer, mboxBuffer->buffer, endPointId, endPoint, target));
+
+ /* Return the buffer to the user if the reception was not successful */
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_RECV,
+ ("Frame reception failed\n"));
+ /*
+ * In the failure case it is possible that while queueing of the
+ * request itself it returned an error status in which case we
+ * would have dispatched an event and freed the element there
+ * itself. Ideally if it failed to queue the request then it
+ * should not generate a callback but we are being a little
+ * conservative.
+ */
+ if (!(IS_ELEMENT_FREE(element))) {
+ mboxBuffer->actualLength = 0;
+ mboxBuffer->buffer += HTC_HEADER_LEN;
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer,
+ mboxBuffer->bufferLength, mboxBuffer->actualLength,
+ A_ECANCELED, mboxBuffer->cookie);
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+ dispatchEvent(target, endPointId, HTC_BUFFER_RECEIVED, &eventInfo);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("htcRxCompletionCB - Exit\n"));
+ }
+ return A_OK;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV,
+ ("Frame reception complete\n"));
+
+ AR_DEBUG_PRINTBUF(mboxBuffer->buffer, mboxBuffer->actualLength);
+
+ /*
+ * Advance the pointer by the size of HTC header and pass the payload
+ * pointer to the upper layer.
+ */
+ mboxBuffer->actualLength = ((mboxBuffer->buffer[0] << 0) |
+ (mboxBuffer->buffer[1] << 8));
+ mboxBuffer->buffer += HTC_HEADER_LEN;
+
+ /*
+ * Frame the HTC_BUFFER_RECEIVED to the upper layer indicating that the
+ * packet has been succesfully received.
+ */
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer, mboxBuffer->bufferLength,
+ mboxBuffer->actualLength, A_OK, mboxBuffer->cookie);
+
+ /* Recycle the bufferElement structure */
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+
+ /* Dispatch the event */
+ dispatchEvent(target, endPointId, HTC_BUFFER_RECEIVED, &eventInfo);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("htcRxCompletion - Exit\n"));
+
+ return A_OK;
+}
+
+A_STATUS
+htcRegCompletionCB(HTC_REG_REQUEST_ELEMENT *element,
+ A_STATUS status)
+{
+ A_STATUS ret;
+ HTC_TARGET *target;
+ HTC_ENDPOINT *endPoint;
+ HTC_REG_BUFFER *regBuffer;
+ A_UINT8 txCreditsConsumed;
+ A_UINT8 txCreditsAvailable;
+ HTC_ENDPOINT_ID endPointId;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV | ATH_DEBUG_SEND,
+ ("htcRegCompletion - Enter\n"));
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ /* Get the context */
+ AR_DEBUG_ASSERT(element != NULL);
+ regBuffer = GET_REG_BUFFER(element);
+ AR_DEBUG_ASSERT(regBuffer != NULL);
+ target = regBuffer->target;
+ AR_DEBUG_ASSERT(target != NULL);
+
+ /* Identify the register and the operation responsible for the callback */
+ ret = A_OK;
+ switch(regBuffer->base) {
+ case TX_CREDIT_COUNTER_DECREMENT_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("TX_CREDIT_COUNTER_DECREMENT_REG\n"));
+ endPointId = regBuffer->offset;
+ endPoint = &target->endPoint[endPointId];
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&creditCS);
+
+ /* Calculate the number of credits available */
+ AR_DEBUG_ASSERT(GET_TX_CREDITS_CONSUMED(endPoint) == regBuffer->length);
+ AR_DEBUG_ASSERT(regBuffer->buffer[0] >=
+ GET_TX_CREDITS_CONSUMED(endPoint));
+ SET_TX_CREDITS_AVAILABLE(endPoint, regBuffer->buffer[0] -
+ GET_TX_CREDITS_CONSUMED(endPoint));
+ SET_TX_CREDITS_CONSUMED(endPoint, 0);
+ txCreditsAvailable = GET_TX_CREDITS_AVAILABLE(endPoint);
+ txCreditsConsumed = GET_TX_CREDITS_CONSUMED(endPoint);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&creditCS);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Pulling %d tx credits from the target\n",
+ txCreditsAvailable));
+
+#ifdef DEBUG
+ txcreditsavailable[endPointId] = txCreditsAvailable;
+ txcreditsconsumed[endPointId] = txCreditsConsumed;
+#endif /* DEBUG */
+
+ if (txCreditsAvailable) {
+ htcSendFrame(endPoint);
+ } else {
+ /*
+ * Enable the Tx credit counter interrupt so that we can get the
+ * credits posted by the target.
+ */
+ htcEnableCreditCounterInterrupt(target, endPointId);
+
+#ifdef DEBUG
+ txcreditintrenable[endPointId] += 1;
+ txcreditintrenableaggregate[endPointId] += 1;
+#endif /* DEBUG */
+ }
+ break;
+
+ case TX_CREDIT_COUNTER_RESET_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("TX_CREDIT_COUNTER_RESET_REG\n"));
+ endPointId = regBuffer->offset;
+
+ /*
+ * Enable the Tx credit counter interrupt so that we can get the
+ * credits posted by the target.
+ */
+ htcEnableCreditCounterInterrupt(target, endPointId);
+
+#ifdef DEBUG
+ txcreditintrenable[endPointId] += 1;
+ txcreditintrenableaggregate[endPointId] += 1;
+#endif /* DEBUG */
+ break;
+
+ case COUNTER_INT_STATUS_ENABLE_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("COUNTER_INT_STATUS_ENABLE: 0x%x\n",
+ target->table.counter_int_status_enable));
+ break;
+
+ case COUNTER_INT_STATUS_DISABLE_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("COUNTER_INT_STATUS_DISABLE:0x%x\n",
+ target->table.counter_int_status_enable));
+ HIFAckInterrupt(target->device);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler - ACK\n"));
+ break;
+
+ case INT_WLAN_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("INT_WLAN: 0x%x\n",
+ target->table.int_wlan));
+ target->table.int_wlan = 0;
+
+ /* Mark the target state as ready and signal the waiting sem */
+ target->ready = TRUE;
+ A_WAKE_UP(&htcEvent);
+ break;
+
+ case INT_STATUS_ENABLE_REG:
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,("INT_STATUS_ENABLE: 0x%x\n",
+ target->table.int_status_enable));
+ break;
+
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("Invalid register address: %d\n", regBuffer->base));
+ }
+
+ /* Free the register request structure */
+ freeRegRequestElement(element);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcRegCompletion - Exit\n"));
+
+ return ret;
+}
+
+
+A_STATUS
+htcTargetInsertedHandler(HIF_DEVICE *device)
+{
+ HTC_TARGET *target;
+ HTC_ENDPOINT *endPoint;
+ A_UINT8 count1, count2;
+ HTC_EVENT_INFO eventInfo;
+ HTC_REG_BUFFER *regBuffer;
+ HTC_QUEUE_ELEMENT *element;
+ HTC_MBOX_BUFFER *mboxBuffer;
+ HTC_REG_REQUEST_LIST *regList;
+ HTC_DATA_REQUEST_QUEUE *sendQueue, *recvQueue;
+ A_UINT32 mboxAddress[HTC_MAILBOX_NUM_MAX];
+ A_UINT32 blockSize[HTC_MAILBOX_NUM_MAX];
+#ifdef CF
+ HIF_REQUEST request;
+ A_STATUS status;
+ A_UINT32 address;
+#endif /* CF */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Enter\n"));
+
+ /* Initialize the locks */
+ A_MUTEX_INIT(&instanceCS);
+ A_MUTEX_INIT(&creditCS);
+ A_MUTEX_INIT(&counterCS);
+
+ /* Allocate target memory */
+ if ((target = (HTC_TARGET *)A_MALLOC(sizeof(HTC_TARGET))) == NULL) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unable to allocate memory\n"));
+ return A_ERROR;
+ }
+ A_MEMZERO(target, sizeof(HTC_TARGET));
+ target->device = device;
+ target->ready = FALSE;
+
+ /* Initialize the endpoints, mbox queues, event table */
+ for (count1 = ENDPOINT1; count1 <= ENDPOINT4; count1 ++) {
+ endPoint = &target->endPoint[count1];
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("endPoint[%d]: %p\n", count1, endPoint));
+ A_MEMZERO(endPoint->txCreditsAvailable, HTC_TX_CREDITS_NUM_MAX);
+ endPoint->txCreditsConsumed = 0;
+ endPoint->txCreditsIntrEnable = FALSE;
+ endPoint->rxLengthPending = 0;
+ endPoint->target = target;
+ endPoint->enabled = FALSE;
+ for (count2 = 0; count2<HTC_DATA_REQUEST_RING_BUFFER_SIZE; count2 ++) {
+ /* Send Queue */
+ sendQueue = &endPoint->sendQueue;
+ sendQueue->head = sendQueue->size = 0;
+ element = &sendQueue->element[count2];
+ A_MEMZERO(element, sizeof(HTC_DATA_REQUEST_ELEMENT));
+ element->buffer.free = TRUE;
+ element->completionCB = htcTxCompletionCB;
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ mboxBuffer->endPoint = endPoint;
+
+ /* Receive Queue */
+ recvQueue = &endPoint->recvQueue;
+ recvQueue->head = recvQueue->size = 0;
+ element = &recvQueue->element[count2];
+ A_MEMZERO(element, sizeof(HTC_DATA_REQUEST_ELEMENT));
+ element->buffer.free = TRUE;
+ element->completionCB = htcRxCompletionCB;
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ mboxBuffer->endPoint = endPoint;
+ }
+ A_MEMZERO(&target->endPoint[count1].eventTable,
+ sizeof(HTC_ENDPOINT_EVENT_TABLE));
+ }
+
+ /* Populate the block size for each of the end points */
+ HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ &blockSize, sizeof(blockSize));
+ HIFConfigureDevice(device, HIF_DEVICE_GET_MBOX_ADDR,
+ &mboxAddress, sizeof(mboxAddress));
+ for (count1 = ENDPOINT1; count1 <= ENDPOINT4; count1 ++) {
+ endPoint = &target->endPoint[count1];
+ endPoint->blockSize = blockSize[count1];
+ endPoint->address = mboxAddress[count1];
+ }
+
+ /* Initialize the shadow copy of the target register table */
+ A_MEMZERO(&target->table, sizeof(HTC_REGISTER_TABLE));
+
+ /* Initialize the register request list */
+ regList = &target->regList;
+ for (count1 = 0; count1 < HTC_REG_REQUEST_LIST_SIZE; count1 ++) {
+ element = ®List->element[count1];
+ A_MEMZERO(element, sizeof(HTC_REG_REQUEST_ELEMENT));
+ element->buffer.free = TRUE;
+ element->completionCB = htcRegCompletionCB;
+ regBuffer = GET_REG_BUFFER(element);
+ regBuffer->target = target;
+ }
+
+ /* Add the target instance to the global list */
+ addTargetInstance(target);
+#ifdef CF
+ /* Disable all the dragon interrupts */
+ target->table.int_status_enable = 0;
+ target->table.cpu_int_status_enable = 0;
+ target->table.error_status_enable = 0;
+ target->table.counter_int_status_enable = 0;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_status_enable, 4, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+#endif /* CF */
+
+ /*
+ * Frame a TARGET_AVAILABLE event and send it to the host. Return the
+ * HIF_DEVICE handle as a parameter with the event.
+ */
+ FRAME_EVENT(eventInfo, (A_UCHAR *)device, sizeof(HIF_DEVICE *),
+ sizeof(HIF_DEVICE *), A_OK, NULL);
+ dispatchEvent(target, ENDPOINT_UNUSED, HTC_TARGET_AVAILABLE, &eventInfo);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcTargetInserted - Exit\n"));
+
+ return A_OK;
+}
+
+A_STATUS
+htcTargetRemovedHandler(HIF_DEVICE *device)
+{
+ HTC_TARGET *target;
+ HTC_EVENT_INFO eventInfo;
+
+ /* Get the target instance bound to this device */
+ target = getTargetInstance(device);
+
+ if (target != NULL) {
+ /* Frame a TARGET_UNAVAILABLE event and send it to the host */
+ FRAME_EVENT(eventInfo, NULL, 0, 0, A_OK, NULL);
+ dispatchEvent(target, ENDPOINT_UNUSED, HTC_TARGET_UNAVAILABLE,
+ &eventInfo);
+ }
+
+ return A_OK;
+}
+
+
+#ifdef CF
+A_STATUS
+htcInterruptDisabler(HIF_DEVICE *device,A_BOOL *callDsr)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HTC_TARGET *target;
+ HIF_REQUEST request;
+
+ target = getTargetInstance(device);
+ AR_DEBUG_ASSERT(target != NULL);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("htcInterruptDisabler Enter target: 0x%p\n", target));
+
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.host_int_status, 1, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+ /* Handle Suprise removal of CF card. Upon removal of the card the
+ * host_int_status reads 0xFF
+ */
+ if (target->table.host_int_status == 0xFF) {
+ *callDsr=FALSE;
+ return A_OK;
+ }
+
+ if ((target->table.int_status_enable & target->table.host_int_status) == 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("htcInterruptDisabler: MisRouted / Spurious interrupt : 0x%p\n", target));
+ *callDsr=FALSE;
+ } else {
+ /*
+ * Disable the interrupts from Dragon.
+ * We do the interrupt servicing in the bottom half and reenable the
+ * Dragon interrupts at the end of the bottom-half
+ */
+
+ target->table.int_status_enable = 0;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(INT_STATUS_ENABLE_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.int_status_enable, 1, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+ *callDsr=TRUE;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcInterruptDisabler: Exit\n"));
+ return A_OK;
+}
+#endif /* CF */
+
+A_STATUS
+htcDSRHandler(HIF_DEVICE *device)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HTC_TARGET *target;
+ HIF_REQUEST request;
+ A_UCHAR host_int_status;
+
+ target = getTargetInstance(device);
+ AR_DEBUG_ASSERT(target != NULL);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC,
+ ("htcDsrHandler: Enter (target: 0x%p\n", target));
+
+ /*
+ * Read the first 28 bytes of the HTC register table. This will yield us
+ * the value of different int status registers and the lookahead
+ * registers.
+ * length = sizeof(int_status) + sizeof(cpu_int_status) +
+ * sizeof(error_int_status) + sizeof(counter_int_status) +
+ * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) +
+ * sizeof(hole) + sizeof(rx_lookahead) +
+ * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) +
+ * sizeof(error_status_enable) +
+ * sizeof(counter_int_status_enable);
+ */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.host_int_status, 28,
+ &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+
+#ifdef DEBUG
+ dumpRegisters(target);
+#endif /* DEBUG */
+#ifdef CF
+ /* Update only those registers that are enabled */
+ /* This is not required as we have already checked for spurious interrupt
+ * in htcInterruptDisabler
+ */
+
+ host_int_status = target->table.host_int_status;
+#else
+ /* Update only those registers that are enabled */
+ host_int_status = target->table.host_int_status &
+ target->table.int_status_enable;
+#endif /* CF */
+
+ AR_DEBUG_ASSERT(host_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Valid interrupt source(s) in INT_STATUS: 0x%x\n",
+ host_int_status));
+ if (HOST_INT_STATUS_CPU_GET(host_int_status)) {
+ /* CPU Interrupt */
+ htcServiceCPUInterrupt(target);
+ }
+
+ if (HOST_INT_STATUS_ERROR_GET(host_int_status)) {
+ /* Error Interrupt */
+ htcServiceErrorInterrupt(target);
+ }
+
+ if (HOST_INT_STATUS_MBOX_DATA_GET(host_int_status)) {
+ /* Mailbox Interrupt */
+ htcServiceMailboxInterrupt(target);
+ }
+
+ if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) {
+ /* Counter Interrupt */
+ htcServiceCounterInterrupt(target);
+ } else {
+ /* Ack the interrupt */
+ HIFAckInterrupt(target->device);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler - ACK\n"));
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("htcDSRHandler: Exit\n"));
+ return A_OK;
+}
+
+void
+htcServiceCPUInterrupt(HTC_TARGET *target)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ A_UINT8 cpu_int_status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("CPU Interrupt\n"));
+ cpu_int_status = target->table.cpu_int_status &
+ target->table.cpu_int_status_enable;
+ AR_DEBUG_ASSERT(cpu_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
+ cpu_int_status));
+
+ /* Figure out the interrupt number */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Interrupt Number: 0x%x\n",
+ htcGetBitNumSet(cpu_int_status)));
+
+ /* Clear the interrupt */
+ target->table.cpu_int_status = cpu_int_status; /* W1C */
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(CPU_INT_STATUS_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.cpu_int_status, 1, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+}
+
+
+void
+htcServiceErrorInterrupt(HTC_TARGET *target)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ A_UINT8 error_int_status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Error Interrupt\n"));
+ error_int_status = target->table.error_int_status &
+ target->table.error_status_enable;
+ AR_DEBUG_ASSERT(error_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
+ error_int_status));
+
+ if (ERROR_INT_STATUS_WAKEUP_GET(error_int_status)) {
+ /* Wakeup */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Wakeup\n"));
+ }
+
+ if (ERROR_INT_STATUS_RX_UNDERFLOW_GET(error_int_status)) {
+ /* Rx Underflow */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Rx Underflow\n"));
+ }
+
+ if (ERROR_INT_STATUS_TX_OVERFLOW_GET(error_int_status)) {
+ /* Tx Overflow */
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Tx Overflow\n"));
+ }
+
+ /* Clear the interrupt */
+ target->table.error_int_status = error_int_status; /* W1C */
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_SYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(ERROR_INT_STATUS_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.error_int_status, 1,
+ &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+}
+
+void
+htcServiceCounterInterrupt(HTC_TARGET *target)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ HTC_ENDPOINT *endPoint;
+ HTC_ENDPOINT_ID endPointId;
+ A_UINT8 counter_int_status;
+ A_UINT8 reset_credit_int_status;
+ A_UINT8 update_credit_int_status;
+ HTC_REG_REQUEST_ELEMENT *element;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Counter Interrupt\n"));
+
+ counter_int_status = target->table.counter_int_status &
+ target->table.counter_int_status_enable;
+ AR_DEBUG_ASSERT(counter_int_status);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
+ counter_int_status));
+
+ /* Service the reset credit counter interrupt */
+ reset_credit_int_status = (counter_int_status & 0x0F);
+ while(reset_credit_int_status) {
+ endPointId = htcGetBitNumSet(reset_credit_int_status);
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("endPoint(%d): %p\n", endPointId, endPoint));
+
+ /* Initialize the number of credits available to zero */
+ SET_TX_CREDITS_AVAILABLE(endPoint, 0);
+
+ /* Clear the interrupt */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(TX_CREDIT_COUNTER_RESET_REG, endPointId);
+ element = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(element != NULL);
+ FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1], 1,
+ TX_CREDIT_COUNTER_RESET_REG, endPointId);
+ status = HIFReadWrite(target->device, address,
+ &endPoint->txCreditsAvailable[1],
+ 1, &request, element);
+
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if (status == A_OK) {
+ /* Enable the Tx credit counter interrupt so that we can get the
+ * credits posted by the target */
+ htcEnableCreditCounterInterrupt(target, endPointId);
+ }
+#endif
+ reset_credit_int_status &=
+ ~(1 << htcGetBitNumSet(reset_credit_int_status));
+ }
+
+ /* Disable the credit counter interrupt */
+ htcDisableCreditCounterInterrupt(target, ENDPOINT_UNUSED);
+
+ /* Service the credit counter interrupt */
+ update_credit_int_status = counter_int_status & 0xF0;
+ while(update_credit_int_status) {
+ endPointId = htcGetBitNumSet(update_credit_int_status) -
+ HTC_MAILBOX_NUM_MAX;
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ /* This is the minimum number of credits that we would have got */
+ AR_DEBUG_ASSERT(GET_TX_CREDITS_AVAILABLE(endPoint) == 0);
+ SET_TX_CREDITS_AVAILABLE(endPoint, 1);
+
+#ifdef DEBUG
+ txcreditsavailable[endPointId] = GET_TX_CREDITS_AVAILABLE(endPoint);
+ txcreditintrenable[endPointId] -= 1;
+#endif /* DEBUG */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Tx Credits Available: %d\n",
+ GET_TX_CREDITS_AVAILABLE(endPoint)));
+
+ if (!target->ready) {
+ htcSendBlkSize(endPoint);
+ } else {
+ htcSendFrame(endPoint);
+ }
+
+ update_credit_int_status &=
+ ~(1 << htcGetBitNumSet(update_credit_int_status));
+ }
+}
+
+void
+htcEnableCreditCounterInterrupt(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ HTC_ENDPOINT *endPoint;
+ HTC_REG_REQUEST_ELEMENT *element;
+
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ A_MUTEX_LOCK(&counterCS);
+
+ endPoint->txCreditsIntrEnable = TRUE;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_FIXED_ADDRESS);
+
+ address = getRegAddr(COUNTER_INT_STATUS_ENABLE_REG,
+ ENDPOINT_UNUSED);
+ element = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(element != NULL);
+ FILL_REG_BUFFER(element, NULL, 1, COUNTER_INT_STATUS_ENABLE_REG,
+ (target->endPoint[0].txCreditsIntrEnable << (4)) |
+ (target->endPoint[1].txCreditsIntrEnable << (5)) |
+ (target->endPoint[2].txCreditsIntrEnable << (6)) |
+ (target->endPoint[3].txCreditsIntrEnable << (7)) | 0x0F);
+ status = HIFReadWrite(target->device, address,
+ (A_UCHAR *)&((GET_REG_BUFFER(element))->offset),
+ 1, &request, element);
+
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if(status == A_OK) {
+ element->completionCB(element, status);
+ }
+#endif
+
+ A_MUTEX_UNLOCK(&counterCS);
+}
+
+void
+htcDisableCreditCounterInterrupt(HTC_TARGET *target,
+ HTC_ENDPOINT_ID unused)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ HTC_ENDPOINT *endPoint;
+ HTC_ENDPOINT_ID endPointId;
+ A_UINT8 counter_int_status;
+ A_UINT8 update_credit_int_status;
+ HTC_REG_REQUEST_ELEMENT *element;
+
+ A_MUTEX_LOCK(&counterCS);
+
+ /* The Tx credit counter update bits are reflected in the upper nibble */
+ counter_int_status = target->table.counter_int_status &
+ target->table.counter_int_status_enable;
+ update_credit_int_status = counter_int_status & 0xF0;
+ while(update_credit_int_status) {
+ endPointId = htcGetBitNumSet(update_credit_int_status) -
+ HTC_MAILBOX_NUM_MAX;
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("endPoint(%d): %p\n", endPointId, endPoint));
+
+ /* Disable the tx credit interrupt */
+ endPoint->txCreditsIntrEnable = FALSE;
+
+ update_credit_int_status &=
+ ~(1 << htcGetBitNumSet(update_credit_int_status));
+ }
+
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(COUNTER_INT_STATUS_DISABLE_REG, ENDPOINT_UNUSED);
+ element = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(element != NULL);
+ FILL_REG_BUFFER(element, NULL, 1,
+ COUNTER_INT_STATUS_DISABLE_REG,
+ (target->endPoint[0].txCreditsIntrEnable << (4)) |
+ (target->endPoint[1].txCreditsIntrEnable << (5)) |
+ (target->endPoint[2].txCreditsIntrEnable << (6)) |
+ (target->endPoint[3].txCreditsIntrEnable << (7)) | 0x0F);
+ status = HIFReadWrite(target->device, address,
+ (A_UCHAR *)&((GET_REG_BUFFER(element))->offset),
+ 1, &request, element);
+
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if ( status == A_OK ) {
+ element->completionCB(element, status);
+ }
+#endif
+
+ A_MUTEX_UNLOCK(&counterCS);
+}
+
+void
+htcServiceMailboxInterrupt(HTC_TARGET *target)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_REQUEST request;
+ HTC_ENDPOINT *endPoint;
+ HTC_ENDPOINT_ID endPointId;
+ A_UINT8 mailbox_int_status;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF, ("Mailbox Interrupt\n"));
+
+ /* The Rx interrupt bits are reflected in the lower nibble */
+ mailbox_int_status = target->table.host_int_status &
+ HOST_INT_STATUS_MBOX_DATA_MASK;
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("Valid mailbox interrupt source(s) in INT_STATUS: 0x%x\n",
+ mailbox_int_status));
+
+ /* Disable the receive interrupt for all four mailboxes */
+ target->table.int_status_enable &= ~(HOST_INT_STATUS_MBOX_DATA_MASK);
+
+ do {
+ while(mailbox_int_status) {
+ endPointId = htcGetBitNumSet(mailbox_int_status);
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("endPoint(%d): %p\n", endPointId, endPoint));
+
+ /* Service the Rx interrupt */
+ htcReceiveFrame(endPoint);
+ mailbox_int_status &= ~(1 << htcGetBitNumSet(mailbox_int_status));
+ }
+
+ /*
+ * Read the register table again. Repeat the process until there are
+ * no more valid packets queued up on receive. It is assumed that
+ * the following request will be serialized along with the request
+ * above and will be completed in the order in which it is received
+ * by the bus driver.
+ */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
+ HIF_SYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_INCREMENTAL_ADDRESS);
+ address = getRegAddr(INT_STATUS_REG, ENDPOINT_UNUSED);
+ status = HIFReadWrite(target->device, address,
+ &target->table.host_int_status,
+ 24, &request, NULL);
+ AR_DEBUG_ASSERT(status == A_OK);
+ mailbox_int_status = target->table.host_int_status &
+ HOST_INT_STATUS_MBOX_DATA_MASK;
+ } while (mailbox_int_status);
+
+ target->table.int_status_enable |= HOST_INT_STATUS_MBOX_DATA_MASK;
+}
Added: developers/nbd/ar6k/htc/htc_internal.h
===================================================================
--- developers/nbd/ar6k/htc/htc_internal.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc_internal.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2004-2005 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * HTC internal specific declarations and prototypes
+ */
+
+#ifndef _HTC_INTERNAL_H_
+#define _HTC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Header files */
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/hw/mbox_host_reg.h"
+#include "../include/hw/rtc_reg.h"
+#include "../include/htc.h"
+#include "../include/AR6000_bmi.h"
+#include "../include/osapi.h"
+#include "../include/hif.h"
+
+/* HTC operational parameters */
+#define HTC_GLOBAL_EVENT_NUM_MAX 2 /* Target available/unavailable */
+#define HTC_ENDPOINT_EVENT_NUM_MAX 5 /* Endpoint specific */
+#define HTC_REG_REQUEST_LIST_SIZE 16
+#define HTC_MESSAGE_SIZE_MAX 1536 - HTC_HEADER_LEN /* Default maximum message size for each mailbox */
+#define HTC_TX_CREDITS_NUM_MAX 64
+#define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */
+
+/* Useful macros */
+#define GET_ENDPOINT_ID(endPoint) (endPoint - endPoint->target->endPoint)
+
+/* ------- Debug related stuff ------- */
+enum {
+ ATH_DEBUG_SEND = 0x0001,
+ ATH_DEBUG_RECV = 0x0002,
+ ATH_DEBUG_SYNC = 0x0004,
+ ATH_DEBUG_DUMP = 0x0008,
+ ATH_DEBUG_INF = 0x0010,
+ ATH_DEBUG_TRC = 0x0020,
+ ATH_DEBUG_WARN = 0x0040,
+ ATH_DEBUG_ERR = 0x0080,
+ ATH_DEBUG_ANY = 0xFFFF,
+};
+
+#ifdef DEBUG
+#define AR_DEBUG_PRINTBUF(buffer, length) do { \
+ if (debughtc & ATH_DEBUG_DUMP) { \
+ dumpBytes(buffer, length); \
+ } \
+} while(0)
+#define PRINTX_ARG(arg...) arg
+#define AR_DEBUG_PRINTF(flags, args) do { \
+ if (debughtc & (flags)) { \
+ A_PRINTF(KERN_ALERT PRINTX_ARG args); \
+ } \
+} while (0)
+#define AR_DEBUG_ASSERT(test) do { \
+ if (!(test)) { \
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Debug Assert Caught, File %s, Line: %d, Test:%s \n",__FILE__, __LINE__,#test)); \
+ } \
+} while(0)
+#else
+#define AR_DEBUG_PRINTF(flags, args)
+#define AR_DEBUG_PRINTBUF(buffer, length)
+#define AR_DEBUG_ASSERT(test)
+#endif
+
+/* ------- Event Related Data Structures ------- */
+typedef struct htc_event_map HTC_EVENT_MAP;
+typedef struct event_table_element EVENT_TABLE_ELEMENT;
+typedef struct htc_endpoint_event_table HTC_ENDPOINT_EVENT_TABLE;
+typedef struct htc_global_event_table HTC_GLOBAL_EVENT_TABLE;
+
+#define FRAME_EVENT(_eventInfo, _buffer, _bufferLength, \
+ _actualLength, _status, _cookie) do { \
+ _eventInfo.buffer = _buffer; \
+ _eventInfo.bufferLength = _bufferLength; \
+ _eventInfo.actualLength = _actualLength; \
+ _eventInfo.status = _status; \
+ _eventInfo.cookie = _cookie; \
+} while (0)
+
+struct event_table_element {
+ HTC_EVENT_ID id;
+ HTC_EVENT_HANDLER handler;
+ void *param;
+};
+
+struct htc_endpoint_event_table {
+ EVENT_TABLE_ELEMENT element[HTC_ENDPOINT_EVENT_NUM_MAX];
+};
+
+struct htc_global_event_table {
+ EVENT_TABLE_ELEMENT element[HTC_GLOBAL_EVENT_NUM_MAX];
+
+};
+
+/* ------ Mailbox Related Data Structures ------ */
+typedef struct htc_queue_element HTC_QUEUE_ELEMENT, HTC_REG_REQUEST_ELEMENT, HTC_DATA_REQUEST_ELEMENT;
+typedef struct htc_mbox_buffer HTC_MBOX_BUFFER;
+typedef struct htc_reg_buffer HTC_REG_BUFFER;
+typedef struct htc_data_request_queue HTC_DATA_REQUEST_QUEUE;
+typedef struct htc_reg_request_list HTC_REG_REQUEST_LIST;
+typedef struct htc_endpoint HTC_ENDPOINT;
+
+typedef enum {
+ INT_STATUS_REG,
+ ERROR_INT_STATUS_REG,
+ CPU_INT_STATUS_REG,
+ RX_LOOKAHEAD_VALID_REG,
+ RX_LOOKAHEAD0_REG,
+ RX_LOOKAHEAD1_REG,
+ RX_LOOKAHEAD2_REG,
+ RX_LOOKAHEAD3_REG,
+ TX_CREDIT_COUNTER_REG,
+ TX_CREDIT_COUNTER_RESET_REG,
+ TX_CREDIT_COUNTER_DECREMENT_REG,
+ SCRATCH_REG,
+ INT_STATUS_ENABLE_REG,
+ CPU_INT_STATUS_ENABLE_REG,
+ ERROR_STATUS_ENABLE_REG,
+ COUNTER_INT_STATUS_READ_REG,
+ COUNTER_INT_STATUS_ENABLE_REG,
+ COUNTER_INT_STATUS_DISABLE_REG,
+ INT_WLAN_REG,
+ WINDOW_DATA_REG,
+ WINDOW_WRITE_ADDR_REG,
+ WINDOW_READ_ADDR_REG
+} TARGET_REGISTERS;
+
+#define SET_TX_CREDITS_AVAILABLE(endPoint, credits) \
+ endPoint->txCreditsAvailable[0] = (credits)
+#define SET_TX_CREDITS_CONSUMED(endPoint, credits) \
+ endPoint->txCreditsConsumed = (credits)
+#define GET_TX_CREDITS_AVAILABLE(endPoint) \
+ endPoint->txCreditsAvailable[0]
+#define GET_TX_CREDITS_CONSUMED(endPoint) \
+ endPoint->txCreditsConsumed
+
+#define IS_ELEMENT_FREE(element) element->buffer.free
+#define GET_MBOX_BUFFER(element) &((element)->buffer.u.mboxBuffer)
+#define GET_REG_BUFFER(element) &((element)->buffer.u.regBuffer)
+#define GET_QUEUE_TAIL(queue) &queue->element[(queue->head + queue->size) % HTC_DATA_REQUEST_RING_BUFFER_SIZE]
+#define GET_QUEUE_HEAD(queue) &queue->element[queue->head]
+#define IS_DATA_QUEUE_EMPTY(queue) (!queue->size)
+#define IS_DATA_QUEUE_FULL(queue) (!(HTC_DATA_REQUEST_RING_BUFFER_SIZE - queue->size))
+
+#define RECYCLE_DATA_REQUEST_ELEMENT(element) do { \
+ FILL_MBOX_BUFFER(element, NULL, 0, 0, NULL); \
+ (element)->buffer.free = TRUE; \
+} while (0)
+
+#define FILL_MBOX_BUFFER(element, _buffer, _bufferLength, _actualLength, _cookie) do { \
+ (GET_MBOX_BUFFER(element))->buffer = _buffer; \
+ (GET_MBOX_BUFFER(element))->bufferLength = _bufferLength; \
+ (GET_MBOX_BUFFER(element))->actualLength = _actualLength; \
+ (GET_MBOX_BUFFER(element))->cookie = _cookie; \
+} while (0)
+
+#define FILL_REG_BUFFER(element, _buffer, _length, _base, _offset) do { \
+ (GET_REG_BUFFER(element))->buffer = _buffer; \
+ (GET_REG_BUFFER(element))->length = _length; \
+ (GET_REG_BUFFER(element))->base = _base; \
+ (GET_REG_BUFFER(element))->offset = _offset; \
+} while (0)
+
+struct htc_queue_element {
+ A_STATUS (*completionCB)(HTC_QUEUE_ELEMENT *element, A_STATUS status);
+ struct htc_buffer {
+ /* In use or available */
+ A_BOOL free;
+ union {
+ struct htc_mbox_buffer {
+ /*
+ * Given by the caller and is associated with the buffer being
+ * queued up.
+ */
+ void *cookie;
+
+ /*
+ * Pointer to the start of the buffer. In the transmit
+ * direction this points to the start of the payload. In the
+ * receive direction, however, the buffer when queued up
+ * points to the start of the HTC header but when returned
+ * to the caller points to the start of the payload
+ */
+ A_UCHAR *buffer;
+
+ /* Holds the length of the buffer */
+ A_UINT32 bufferLength;
+
+ /* Holds the length of the payload */
+ A_UINT32 actualLength;
+
+ HTC_ENDPOINT *endPoint;
+ } mboxBuffer;
+ struct htc_reg_buffer {
+ HTC_TARGET *target;
+ A_UCHAR *buffer;
+ A_UINT32 length;
+ TARGET_REGISTERS base;
+ A_UINT32 offset;
+ } regBuffer;
+ } u;
+ } buffer;
+};
+
+/* This is a FIFO queue of the pending data read/write requests. When a request
+has to be issued, the element at the head of the queue is dequeued and
+processed. New requests are added at the tail of the queue. The queue can only
+support a fixed number of requests and stops adding new requests once the total
+number of requests that are pending to be processed and the ones that are still
+under process reach the queue capacity */
+struct htc_data_request_queue {
+ A_UINT32 head;
+ A_UINT32 size;
+ HTC_DATA_REQUEST_ELEMENT element[HTC_DATA_REQUEST_RING_BUFFER_SIZE];
+};
+
+/* This is a list of 'free' register read/write requests. When a request has to
+be issued an element is taken from this list and after the completion of the
+request is added back to the list */
+struct htc_reg_request_list {
+ HTC_REG_REQUEST_ELEMENT element[HTC_REG_REQUEST_LIST_SIZE];
+};
+
+struct htc_endpoint {
+ /* Enabled or Disabled */
+ A_BOOL enabled;
+
+ /*
+ * Used to hold the length of the frame received from the target in
+ * case there are no buffers that have been queued up to receive the
+ * data.
+ */
+ A_UINT32 rxLengthPending;
+
+ /* Number of frames for which the target has space for at any time */
+ A_UINT8 txCreditsAvailable[1 + HTC_TX_CREDITS_NUM_MAX];
+
+ /*
+ * Number of frames that have been sent since the transmit credits
+ * were last updated.
+ */
+ A_UINT8 txCreditsConsumed;
+
+ A_BOOL txCreditsIntrEnable;
+
+ /* Pending Send Queue */
+ HTC_DATA_REQUEST_QUEUE sendQueue;
+
+ /* Pending Receive Queue */
+ HTC_DATA_REQUEST_QUEUE recvQueue;
+
+ /* Inverse reference to the target */
+ HTC_TARGET *target;
+
+ /* Block size configured for the endpoint */
+ A_UINT32 blockSize;
+
+ /* Event Table */
+ HTC_ENDPOINT_EVENT_TABLE eventTable;
+
+ /* Stating address of the endpoint */
+ A_UINT32 address;
+};
+
+/* ------- Target Related Data structures ------- */
+typedef struct htc_register_table HTC_REGISTER_TABLE;
+
+/*
+ * The following Register table only contain those registers that are used
+ * in HTC. It does not reflect the actual register layout in the hardware
+ */
+struct htc_register_table {
+ A_UINT8 host_int_status;
+ A_UINT8 cpu_int_status;
+ A_UINT8 error_int_status;
+ A_UINT8 counter_int_status;
+ A_UINT8 mbox_frame;
+ A_UINT8 rx_lookahead_valid;
+ A_UINT8 hole[2];
+ A_UINT32 rx_lookahead[HTC_MAILBOX_NUM_MAX];
+ A_UINT8 int_status_enable;
+ A_UINT8 cpu_int_status_enable;
+ A_UINT8 error_status_enable;
+ A_UINT8 counter_int_status_enable;
+ A_UINT8 int_wlan;
+};
+
+struct htc_target {
+ A_BOOL ready;
+ void *device; /* Handle to the device instance
+ reported by the bus driver */
+ HTC_ENDPOINT endPoint[HTC_MAILBOX_NUM_MAX];
+ HTC_REGISTER_TABLE table;
+ HTC_REG_REQUEST_LIST regList;
+};
+
+
+/* ------- Function Prototypes for Receive -------- */
+void
+htcReceiveFrame(HTC_ENDPOINT *endPoint);
+
+A_UINT32
+htcGetFrameLength(HTC_ENDPOINT *endPoint);
+
+
+/* ------- Function Prototypes for Transmit -------- */
+void
+htcSendFrame(HTC_ENDPOINT *endPoint);
+
+void
+htcSendBlkSize(HTC_ENDPOINT *endPoint);
+
+
+/* ------- Function Prototypes for Events and Callbacks ------- */
+A_STATUS
+htcRWCompletionHandler(void *element,
+ A_STATUS status);
+
+A_STATUS
+htcTxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status);
+
+A_STATUS
+htcBlkSzNegCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status);
+
+A_STATUS
+htcRxCompletionCB(HTC_DATA_REQUEST_ELEMENT *element,
+ A_STATUS status);
+
+A_STATUS
+htcRegCompletionCB(HTC_REG_REQUEST_ELEMENT *element,
+ A_STATUS status);
+
+A_STATUS
+htcTargetInsertedHandler(HIF_DEVICE *device);
+
+A_STATUS
+htcTargetRemovedHandler(HIF_DEVICE *device);
+
+A_STATUS
+htcDSRHandler(HIF_DEVICE *device);
+
+#ifdef CF
+A_STATUS
+htcInterruptDisabler(HIF_DEVICE *device,A_BOOL *callDsr);
+
+A_STATUS
+htcInterruptEnabler(HIF_DEVICE *device);
+#endif /* CF */
+
+void
+htcServiceCPUInterrupt(HTC_TARGET *target);
+
+void
+htcServiceErrorInterrupt(HTC_TARGET *target);
+
+void
+htcServiceCounterInterrupt(HTC_TARGET *target);
+
+void
+htcServiceMailboxInterrupt(HTC_TARGET *target);
+
+void
+htcEnableCreditCounterInterrupt(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId);
+
+void
+htcDisableCreditCounterInterrupt(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId);
+
+/* ------- Function Prototypes for Utility routines ------- */
+A_STATUS
+addToMboxQueue(HTC_DATA_REQUEST_QUEUE *queue,
+ A_UCHAR *buffer,
+ A_UINT32 bufferLength,
+ A_UINT32 actualLength,
+ void *cookie);
+
+HTC_DATA_REQUEST_ELEMENT *
+removeFromMboxQueue(HTC_DATA_REQUEST_QUEUE *queue);
+
+void
+flushMboxQueue(HTC_ENDPOINT *endPoint,
+ HTC_DATA_REQUEST_QUEUE *queue,
+ HTC_EVENT_ID eventId);
+
+HTC_REG_REQUEST_ELEMENT *
+allocateRegRequestElement(HTC_TARGET *target);
+
+void
+freeRegRequestElement(HTC_REG_REQUEST_ELEMENT *element);
+
+A_STATUS
+addToEventTable(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId,
+ HTC_EVENT_HANDLER handler,
+ void *param);
+
+A_STATUS
+removeFromEventTable(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId);
+
+void
+dispatchEvent(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId,
+ HTC_EVENT_INFO *eventInfo);
+
+HTC_TARGET *
+getTargetInstance(void *device);
+
+void
+addTargetInstance(HTC_TARGET *target);
+
+void
+delTargetInstance(HTC_TARGET *target);
+
+A_UINT32
+getRegAddr(TARGET_REGISTERS base,
+ HTC_ENDPOINT_ID endPointId);
+
+A_UINT8
+htcGetBitNumSet(A_UINT32 data);
+
+void
+dumpBytes(A_UCHAR *buffer, A_UINT16 length);
+
+void
+dumpRegisters(HTC_TARGET *target);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTC_INTERNAL_H_ */
Added: developers/nbd/ar6k/htc/htc_recv.c
===================================================================
--- developers/nbd/ar6k/htc/htc_recv.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc_recv.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the routines handling the receive path.
+ */
+
+#include "htc_internal.h"
+
+/* ------ Global Variable Declarations ------- */
+#ifdef DEBUG
+extern A_UINT32 debughtc;
+#endif
+
+/* ------ Static Variables ------ */
+
+
+/* ------ Functions ------ */
+/* Makes a buffer available to the HTC module */
+A_STATUS
+HTCBufferReceive(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ A_UCHAR *buffer,
+ A_UINT32 length,
+ void *cookie)
+{
+ A_STATUS status;
+ HTC_ENDPOINT *endPoint;
+ HTC_DATA_REQUEST_QUEUE *recvQueue;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("HTCBufferReceive: Enter (endPointId: %d, buffer: 0x%p, length: %d, cookie: 0x%p)\n", endPointId, buffer, length, cookie));
+
+ AR_DEBUG_ASSERT((endPointId >= ENDPOINT1) && (endPointId <= ENDPOINT4));
+
+ /* Extract the end point instance */
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+
+ recvQueue = &endPoint->recvQueue;
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV, ("recvQueue: %p\n",
+ recvQueue));
+
+ /* Add this posted buffer to the pending receive queue */
+ status = addToMboxQueue(recvQueue, buffer, length, 0, cookie);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_RECV,
+ ("Mailbox (%d) Send queue full. Unable to add buffer\n",
+ GET_ENDPOINT_ID(endPoint)));
+ return A_ERROR;
+ }
+
+ /*
+ * If this API was called as a result of a HTC_DATA_AVAILABLE event to
+ * the upper layer, indicating that HTC is out of buffers, then we should
+ * receive the frame in the buffer supplied otherwise we simply add the
+ * buffer to the Pending Receive Queue
+ */
+ if (endPoint->rxLengthPending) {
+ htcReceiveFrame(endPoint);
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("HTCBufferReceive: Exit\n"));
+ return A_OK;
+}
+
+void
+htcReceiveFrame(HTC_ENDPOINT *endPoint)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ A_UINT32 paddedLength;
+ A_UINT32 frameLength;
+ HIF_REQUEST request;
+ HTC_ENDPOINT_ID endPointId;
+ HTC_QUEUE_ELEMENT *element;
+ HTC_MBOX_BUFFER *mboxBuffer;
+ HTC_DATA_REQUEST_QUEUE *recvQueue;
+ HTC_TARGET *target;
+ HTC_EVENT_INFO eventInfo;
+ HIF_DATAMODE dmode;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("htcReceiveFrame - Enter\n"));
+
+ /* Get the context */
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ recvQueue = &endPoint->recvQueue;
+ AR_DEBUG_ASSERT(recvQueue != NULL);
+
+ /*
+ * Receive the frame if we have any pending frames and a buffer to
+ * receive it into.
+ */
+ if (IS_DATA_QUEUE_EMPTY(recvQueue)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN | ATH_DEBUG_RECV,
+ ("Mailbox (%d) recv queue empty. Unable to remove buffer\n", endPointId));
+
+ /*
+ * Communicate this situation to the host via the HTC_DATA_AVAILABLE
+ * event to request some buffers in the queue.
+ */
+ endPoint->rxLengthPending = htcGetFrameLength(endPoint);
+ AR_DEBUG_ASSERT(endPoint->rxLengthPending);
+ FRAME_EVENT(eventInfo, NULL, endPoint->rxLengthPending,
+ 0, A_OK, NULL);
+ dispatchEvent(target, endPointId, HTC_DATA_AVAILABLE, &eventInfo);
+ return;
+ }
+
+ /*
+ * Get the length from the lookahead register if there is nothing
+ * pending.
+ */
+ if (endPoint->rxLengthPending) {
+ frameLength = endPoint->rxLengthPending;
+ endPoint->rxLengthPending = 0;
+ } else {
+ frameLength = htcGetFrameLength(endPoint);
+ }
+ AR_DEBUG_ASSERT((frameLength > 0) &&
+ (frameLength <= HTC_MESSAGE_SIZE_MAX));
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_RECV, ("Frame Length: %d\n",
+ frameLength));
+
+ /* Adjust the length to be a multiple of block size if appropriate */
+ paddedLength = (frameLength + (endPoint->blockSize - 1)) &
+ (~(endPoint->blockSize - 1));
+
+ /*
+ * Receive the frame(s). Pull an empty buffer from the head of the
+ * Pending Receive Queue.
+ */
+ element = removeFromMboxQueue(recvQueue);
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ mboxBuffer->actualLength = paddedLength;
+ dmode = (endPoint->blockSize > 1) ? HIF_BLOCK_BASIS : HIF_BYTE_BASIS;
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, dmode, HIF_INCREMENTAL_ADDRESS);
+ address = endPoint->address;
+ status = HIFReadWrite(target->device, address, mboxBuffer->buffer,
+ mboxBuffer->actualLength, &request, element);
+#ifndef HTC_SYNC
+ if (status != A_OK) {
+#else
+ if (status != A_OK && status != A_PENDING) {
+#endif
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_RECV,
+ ("Frame reception failed\n"));
+ if (!IS_ELEMENT_FREE(element)) {
+ mboxBuffer->actualLength = 0;
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer,
+ mboxBuffer->bufferLength, mboxBuffer->actualLength,
+ A_ECANCELED, mboxBuffer->cookie);
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+ dispatchEvent(target, endPointId, HTC_BUFFER_RECEIVED,
+ &eventInfo);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("htcReceiveFrame - Exit\n"));
+ return;
+ }
+ }
+#ifdef HTC_SYNC
+ else if (status == A_OK) {
+ element->completionCB(element, status);
+ }
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_RECV,
+ ("htcReceiveFrame - Exit\n"));
+}
+
+A_UINT32
+htcGetFrameLength(HTC_ENDPOINT *endPoint)
+{
+ HTC_TARGET *target;
+ A_UINT32 frameLength;
+ HTC_ENDPOINT_ID endPointId;
+
+ /* Get the context */
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+
+ AR_DEBUG_ASSERT(target->table.rx_lookahead_valid & (1 << endPointId));
+
+ /* The length is contained in the first two bytes - HTC_HEADER_LEN */
+ frameLength = (target->table.rx_lookahead[endPointId] & 0xFFFF) +
+ HTC_HEADER_LEN;
+ AR_DEBUG_ASSERT(frameLength);
+
+ return frameLength;
+}
Added: developers/nbd/ar6k/htc/htc_send.c
===================================================================
--- developers/nbd/ar6k/htc/htc_send.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc_send.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the routines handling the transmit path.
+ */
+
+#include "htc_internal.h"
+
+/* ------ Global Variable Declarations ------- */
+extern A_MUTEX_T creditCS;
+
+#ifdef DEBUG
+extern A_UINT32 debughtc;
+extern A_UINT32 txcreditsavailable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditsconsumed[HTC_MAILBOX_NUM_MAX];
+#ifdef HTC_SYNC
+extern A_UINT32 txcreditintrenable[HTC_MAILBOX_NUM_MAX];
+extern A_UINT32 txcreditintrenableaggregate[HTC_MAILBOX_NUM_MAX];
+#endif
+#endif
+
+extern A_UINT32 tx_attempt[HTC_MAILBOX_NUM_MAX]; /* Num of attempts to add */
+extern A_UINT32 tx_post[HTC_MAILBOX_NUM_MAX]; /* Num of attemps succeded */
+extern A_UINT32 tx_complete[HTC_MAILBOX_NUM_MAX]; /* Num of tx complete */
+
+/* ------ Functions ------ */
+A_STATUS
+HTCBufferSend(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ A_UCHAR *buffer,
+ A_UINT32 length,
+ void *cookie)
+{
+ A_STATUS status;
+ HTC_ENDPOINT *endPoint;
+ HTC_DATA_REQUEST_QUEUE *sendQueue;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("HTCBufferSend: Enter (endPointId: %d, buffer: 0x%p, length: %d, cookie: 0x%p)\n", endPointId, buffer, length, cookie));
+
+ AR_DEBUG_ASSERT((endPointId >= ENDPOINT1) && (endPointId <= ENDPOINT4));
+ AR_DEBUG_ASSERT(length);
+
+ /* Extract the end point instance */
+ endPoint = &target->endPoint[endPointId];
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ sendQueue = &endPoint->sendQueue;
+ AR_DEBUG_ASSERT(sendQueue != NULL);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("mboxQueue: %p\n", sendQueue));
+
+ /*
+ * Add this posted buffer to the pending send queue. We need to allocate
+ * a bufferElement to store the packet information and we borrow that
+ * buffer from the pending send queue. If circumstances allow us to
+ * transmit it right away then we dequeue it otherwise we let it remain
+ * to be picked in the interrupt handler context.
+ */
+ tx_attempt[endPointId] += 1;
+
+ if (!endPoint->enabled) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Endpoint not enabled: %d\n",
+ GET_ENDPOINT_ID(endPoint)));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("tx_attempt[%d] = %d, tx_post[%d] = %d, tx_complete[%d] = %d\n", endPointId, tx_attempt[endPointId], endPointId, tx_post[endPointId], endPointId, tx_complete[endPointId]));
+ return A_ERROR;
+ }
+
+ status = addToMboxQueue(sendQueue, buffer, length, 0, cookie);
+ if (status != A_OK) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
+ ("Mailbox (%d) PSQ full. Unable to add buffer\n",
+ endPointId));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
+ ("tx_attempt[%d] = %d, tx_post[%d] = %d, tx_complete[%d] = %d\n", endPointId, tx_attempt[endPointId], endPointId, tx_post[endPointId], endPointId, tx_complete[endPointId]));
+ return A_ERROR;
+ }
+
+ tx_post[endPointId] += 1;
+
+ /*
+ * The frame shall be dequeued and sent if there are any credits
+ * available.
+ */
+ htcSendFrame(endPoint);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("HTCBufferSend: Exit\n"));
+ return A_OK;
+}
+
+
+void
+htcSendFrame(HTC_ENDPOINT *endPoint)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HIF_DATAMODE dmode;
+ HTC_TARGET *target;
+ HIF_REQUEST request;
+ A_UINT32 frameLength;
+ A_UINT32 paddedLength;
+ HTC_EVENT_INFO eventInfo;
+ A_UINT8 txCreditsConsumed;
+ A_UINT8 txCreditsAvailable;
+ HTC_ENDPOINT_ID endPointId;
+ HTC_QUEUE_ELEMENT *element;
+ HTC_MBOX_BUFFER *mboxBuffer;
+ HTC_REG_REQUEST_LIST *regList;
+ HTC_DATA_REQUEST_QUEUE *sendQueue;
+#ifdef HTC_SYNC
+ HTC_REG_BUFFER *regBuffer;
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("htcSendFrame - Enter\n"));
+
+ /* Get the context */
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ sendQueue = &endPoint->sendQueue;
+ AR_DEBUG_ASSERT(sendQueue != NULL);
+ regList = &target->regList;
+ AR_DEBUG_ASSERT(regList != NULL);
+
+ /*
+ * Transmit the frames as long as we have the credits available and
+ * the queue is not out of them
+ */
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&creditCS);
+ txCreditsAvailable = GET_TX_CREDITS_AVAILABLE(endPoint);
+ txCreditsConsumed = GET_TX_CREDITS_CONSUMED(endPoint);
+ SET_TX_CREDITS_AVAILABLE(endPoint, 0);
+ SET_TX_CREDITS_CONSUMED(endPoint, txCreditsConsumed + txCreditsAvailable);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&creditCS);
+
+ /*
+ * Send the packet only when there are packets to be sent and there
+ * are positive number of credits available.
+ */
+ while((!IS_DATA_QUEUE_EMPTY(sendQueue)) && txCreditsAvailable)
+ {
+ /* Get the request buffer from the Pending Send Queue */
+ element = removeFromMboxQueue(sendQueue);
+ mboxBuffer = GET_MBOX_BUFFER(element);
+
+ /*
+ * Prepend the actual length in the first 2 bytes of the outgoing
+ * packet.
+ */
+ mboxBuffer->buffer -= HTC_HEADER_LEN;
+ A_MEMCPY(mboxBuffer->buffer, &mboxBuffer->bufferLength, HTC_HEADER_LEN);
+
+ /*
+ * Adjust the length in the block mode only when its not an integral
+ * multiple of the block size. Assumption is that the block size is
+ * a power of 2.
+ */
+ frameLength = mboxBuffer->bufferLength + HTC_HEADER_LEN;
+ paddedLength = (frameLength + (endPoint->blockSize - 1)) &
+ (~(endPoint->blockSize - 1));
+ mboxBuffer->actualLength = paddedLength;
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Original frame length: %d, Padded frame length: %d\n", frameLength, paddedLength));
+
+ AR_DEBUG_PRINTBUF(mboxBuffer->buffer, mboxBuffer->actualLength);
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Available Tx credits: %d\n", txCreditsAvailable));
+
+ /* Frame the interface request */
+ dmode = (endPoint->blockSize > 1) ? HIF_BLOCK_BASIS : HIF_BYTE_BASIS;
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, dmode, HIF_INCREMENTAL_ADDRESS);
+ address = endPoint->address;
+
+ /* Send the data to the bus driver */
+ status = HIFReadWrite(target->device, address, mboxBuffer->buffer,
+ mboxBuffer->actualLength, &request, element);
+#ifndef HTC_SYNC
+ if (status != A_OK) {
+#else
+ if (status != A_OK && status != A_PENDING) {
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
+ ("Frame transmission failed\n"));
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR | ATH_DEBUG_SEND,
+ ("EndPoint: %d, Tx credits available: %d\n",
+ endPointId, GET_TX_CREDITS_AVAILABLE(endPoint)));
+ /*
+ * We need to check just in case the callback routine was called
+ * with the error status before we reach this point and in that
+ * context we fee up the buffer so its just a conservative design.
+ */
+ if (!IS_ELEMENT_FREE(element)) {
+ mboxBuffer->buffer += HTC_HEADER_LEN;
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer,
+ mboxBuffer->bufferLength,
+ mboxBuffer->actualLength,
+ A_ECANCELED, mboxBuffer->cookie);
+ RECYCLE_DATA_REQUEST_ELEMENT(element);
+ dispatchEvent(target, endPointId, HTC_BUFFER_SENT, &eventInfo);
+ }
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcSendFrame - Exit\n"));
+ return;
+ }
+#ifdef HTC_SYNC
+ else if (status == A_OK) {
+ element->completionCB(element, status);
+ }
+#endif
+ txCreditsAvailable -= 1;
+ txCreditsConsumed += 1;
+
+#ifdef DEBUG
+ txcreditsavailable[endPointId] = txCreditsAvailable;
+ txcreditsconsumed[endPointId] = txCreditsConsumed;
+#endif /* DEBUG */
+
+ if (!txCreditsAvailable) {
+
+ AR_DEBUG_ASSERT(txCreditsConsumed);
+
+ /*
+ * Instead of taking an interrupt we can just poll for more
+ * credits that might have been queued up by now.
+ */
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO,
+ HIF_ASYNCHRONOUS, HIF_BYTE_BASIS,
+ HIF_FIXED_ADDRESS);
+ address = getRegAddr(TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
+ element = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(element != NULL);
+ FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1],
+ txCreditsConsumed, TX_CREDIT_COUNTER_DECREMENT_REG,
+ endPointId);
+ status = HIFReadWrite(target->device, address,
+ &endPoint->txCreditsAvailable[1],
+ txCreditsConsumed, &request, element);
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND,
+ ("htcSendFrame - Exit\n"));
+ return;
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if ( status == A_OK ) {
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): LOCK at line %d in file %s \n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&creditCS);
+
+ regBuffer = GET_REG_BUFFER(element);
+ /* Calculate the number of credits available */
+ AR_DEBUG_ASSERT(GET_TX_CREDITS_CONSUMED(endPoint) == \
+ regBuffer->length);
+ SET_TX_CREDITS_AVAILABLE(endPoint, regBuffer->buffer[0] -
+ GET_TX_CREDITS_CONSUMED(endPoint));
+ SET_TX_CREDITS_CONSUMED(endPoint, 0);
+ txCreditsAvailable = GET_TX_CREDITS_AVAILABLE(endPoint);
+ txCreditsConsumed = GET_TX_CREDITS_CONSUMED(endPoint);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&creditCS);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Pulling %d tx credits from the target\n",
+ txCreditsAvailable));
+
+ #ifdef DEBUG
+ txcreditsavailable[endPointId] = txCreditsAvailable;
+ txcreditsconsumed[endPointId] = txCreditsConsumed;
+ #endif /* DEBUG */
+
+ freeRegRequestElement(element);
+
+ if (!txCreditsAvailable) {
+
+ /* Enable the Tx credit counter interrupt so that we can get
+ * the credits posted by the target */
+
+ htcEnableCreditCounterInterrupt(target, endPointId);
+
+ /* Counter Interrupts have been enabled if
+ * txCreditsAvailable is still 0 after polling. We need to
+ * return here as there is nothing we can send till we get
+ * a Counter Interrupt.
+ */
+ return;
+ }
+ }
+#endif
+ }
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): LOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&creditCS);
+ SET_TX_CREDITS_AVAILABLE(endPoint, txCreditsAvailable);
+ SET_TX_CREDITS_CONSUMED(endPoint, txCreditsConsumed);
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (credit): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&creditCS);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC | ATH_DEBUG_SEND, ("htcSendFrame - Exit\n"));
+}
+
+void
+htcSendBlkSize(HTC_ENDPOINT *endPoint)
+{
+ A_STATUS status;
+ A_UINT32 address;
+ HTC_TARGET *target;
+ HIF_REQUEST request;
+ HTC_ENDPOINT_ID endPointId;
+ HTC_QUEUE_ELEMENT *element;
+ HTC_MBOX_BUFFER *mboxBuffer;
+ HTC_DATA_REQUEST_QUEUE *sendQueue;
+ HTC_REG_REQUEST_LIST *regList;
+
+ /* Get the context */
+ AR_DEBUG_ASSERT(endPoint != NULL);
+ target = endPoint->target;
+ AR_DEBUG_ASSERT(target != NULL);
+ regList = &target->regList;
+ AR_DEBUG_ASSERT(regList != NULL);
+ sendQueue = &endPoint->sendQueue;
+ AR_DEBUG_ASSERT(sendQueue != NULL);
+ endPointId = GET_ENDPOINT_ID(endPoint);
+
+ /* Decrement the tx credit count */
+ AR_DEBUG_ASSERT(endPoint->txCreditsConsumed == 0);
+ endPoint->txCreditsConsumed = 1;
+ HIF_FRAME_REQUEST(&request, HIF_READ, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_FIXED_ADDRESS);
+ address = getRegAddr(TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
+ element = allocateRegRequestElement(target);
+ AR_DEBUG_ASSERT(element != NULL);
+ FILL_REG_BUFFER(element, &endPoint->txCreditsAvailable[1],
+ endPoint->txCreditsConsumed,
+ TX_CREDIT_COUNTER_DECREMENT_REG, endPointId);
+ status = HIFReadWrite(target->device, address,
+ &endPoint->txCreditsAvailable[1],
+ endPoint->txCreditsConsumed, &request, element);
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if (status == A_OK) {
+ element->completionCB(element, status);
+ }
+#endif
+
+ /* Negotiate the maximum block size for the endpoint */
+ addToMboxQueue(sendQueue, (A_UCHAR *)&endPoint->blockSize,
+ sizeof(endPoint->blockSize), sizeof(endPoint->blockSize),
+ NULL);
+ element = removeFromMboxQueue(sendQueue);
+ element->completionCB = htcBlkSzNegCompletionCB;
+ mboxBuffer = GET_MBOX_BUFFER(element);
+ HIF_FRAME_REQUEST(&request, HIF_WRITE, HIF_EXTENDED_IO, HIF_ASYNCHRONOUS,
+ HIF_BYTE_BASIS, HIF_INCREMENTAL_ADDRESS);
+ address = endPoint->address;
+ status = HIFReadWrite(target->device, address, mboxBuffer->buffer,
+ mboxBuffer->bufferLength, &request, element);
+#ifndef HTC_SYNC
+ AR_DEBUG_ASSERT(status == A_OK);
+#else
+ AR_DEBUG_ASSERT(status == A_OK || status == A_PENDING);
+ if (status == A_OK) {
+ element->completionCB(element, status);
+ }
+#endif
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF | ATH_DEBUG_SEND,
+ ("Mailbox(%d), Block size: %d\n",
+ endPointId, endPoint->blockSize));
+}
Added: developers/nbd/ar6k/htc/htc_utils.c
===================================================================
--- developers/nbd/ar6k/htc/htc_utils.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/htc/htc_utils.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the utility routines used across the entire HTC module.
+ */
+
+#include "htc_internal.h"
+
+/* ------ Global Variable Declarations ------- */
+extern HTC_TARGET *AtherosTargetList[HIF_MAX_DEVICES];
+extern HTC_GLOBAL_EVENT_TABLE AtherosEventTable;
+extern A_MUTEX_T instanceCS;
+
+#ifdef DEBUG
+extern A_UINT32 debughtc;
+#endif
+
+/* ------ Static Variables ------ */
+
+/* ------ Functions ------ */
+void
+dispatchEvent(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId,
+ HTC_EVENT_INFO *eventInfo)
+{
+ EVENT_TABLE_ELEMENT *eventElement;
+
+ if (eventId == HTC_TARGET_AVAILABLE) {
+ eventElement = &AtherosEventTable.element[0];
+ } else if (eventId == HTC_TARGET_UNAVAILABLE) {
+ eventElement = &AtherosEventTable.element[1];
+ } else {
+ eventElement =
+ &target->endPoint[endPointId].eventTable.element[eventId];
+ }
+ AR_DEBUG_ASSERT(eventElement != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("dispatchEvent(endpoint: %d, eventId: 0x%d, handler: 0x%p)\n", endPointId, eventElement->id, eventElement->handler));
+ if (eventElement->handler) {
+ eventElement->handler(target, endPointId, eventId, eventInfo,
+ eventElement->param);
+ }
+}
+
+
+A_STATUS
+addToEventTable(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId,
+ HTC_EVENT_HANDLER handler,
+ void *param)
+{
+ EVENT_TABLE_ELEMENT *new;
+
+ if (eventId == HTC_TARGET_AVAILABLE) {
+ new = &AtherosEventTable.element[0];
+ } else if (eventId == HTC_TARGET_UNAVAILABLE) {
+ new = &AtherosEventTable.element[1];
+ } else {
+ new = &target->endPoint[endPointId].eventTable.element[eventId];
+ }
+
+ /* Store the event id, the corresponding handler and the param passed */
+ new->id = eventId;
+ new->handler = handler;
+ new->param = param;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("addToEventTable(endpoint: %d, eventId: 0x%d, handler: 0x%p)\n", endPointId, new->id, new->handler));
+
+ return A_OK;
+}
+
+
+A_STATUS
+removeFromEventTable(HTC_TARGET *target,
+ HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId)
+{
+ EVENT_TABLE_ELEMENT *remove;
+
+ if (eventId == HTC_TARGET_AVAILABLE) {
+ remove = &AtherosEventTable.element[0];
+ } else if (eventId == HTC_TARGET_UNAVAILABLE) {
+ remove = &AtherosEventTable.element[1];
+ } else {
+ remove = &target->endPoint[endPointId].eventTable.element[eventId];
+ }
+
+ /* Remove the event handler */
+ A_MEMZERO(remove, sizeof(EVENT_TABLE_ELEMENT));
+
+ return A_OK;
+}
+
+A_STATUS
+addToMboxQueue(HTC_DATA_REQUEST_QUEUE *queue,
+ A_UCHAR *buffer,
+ A_UINT32 bufferLength,
+ A_UINT32 actualLength,
+ void *cookie)
+{
+ A_STATUS status;
+ HTC_DATA_REQUEST_ELEMENT *element;
+
+ AR_DEBUG_ASSERT(queue != NULL);
+ AR_DEBUG_ASSERT(bufferLength);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (queue): LOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&instanceCS);
+ element = GET_QUEUE_TAIL(queue);
+ if (!(IS_DATA_QUEUE_FULL(queue)) && IS_ELEMENT_FREE(element)) {
+ element->buffer.free = FALSE;
+ FILL_MBOX_BUFFER(element, buffer, bufferLength, actualLength, cookie);
+ queue->size += 1;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("addToMboxQueue (index: %d, size: %d, bufferElement: 0x%p, bufferElement->buffer: 0x%p, bufferElement->cookie: 0x%p)\n", (queue->head + queue->size - 1) % HTC_DATA_REQUEST_RING_BUFFER_SIZE, queue->size, element, (GET_MBOX_BUFFER(element))->buffer, (GET_MBOX_BUFFER(element))->cookie));
+ status = A_OK;
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Queue size: %d\n", queue->size));
+ status = A_ERROR;
+ }
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (queue): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&instanceCS);
+
+ return status;
+}
+
+HTC_DATA_REQUEST_ELEMENT *
+removeFromMboxQueue(HTC_DATA_REQUEST_QUEUE *queue) {
+ HTC_DATA_REQUEST_ELEMENT *element;
+ AR_DEBUG_ASSERT(queue != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (queue): LOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_LOCK(&instanceCS);
+ if (!(IS_DATA_QUEUE_EMPTY(queue))) {
+ element = GET_QUEUE_HEAD(queue);
+ queue->head = (queue->head + 1) % HTC_DATA_REQUEST_RING_BUFFER_SIZE;
+ queue->size -= 1;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_INF,
+ ("removeFromMboxQueue (index: %d, size: %d, bufferElement: 0x%p, bufferElement->buffer: 0x%p, bufferElement->cookie: 0x%p)\n", queue->head, queue->size, element, (GET_MBOX_BUFFER(element))->buffer, (GET_MBOX_BUFFER(element))->cookie));
+ } else {
+ element = NULL;
+ }
+ AR_DEBUG_PRINTF(ATH_DEBUG_SYNC,
+ ("Critical Section (queue): UNLOCK at line %d in file %s\n", __LINE__, __FILE__));
+ A_MUTEX_UNLOCK(&instanceCS);
+
+ return element;
+}
+
+void
+flushMboxQueue(HTC_ENDPOINT *endPoint,
+ HTC_DATA_REQUEST_QUEUE *queue,
+ HTC_EVENT_ID eventId)
+{
+ HTC_DATA_REQUEST_ELEMENT *curr;
+ HTC_EVENT_INFO eventInfo;
+ HTC_ENDPOINT_EVENT_TABLE *eventTable;
+ HTC_ENDPOINT_ID endPointId;
+ EVENT_TABLE_ELEMENT *eventElement;
+ HTC_MBOX_BUFFER *mboxBuffer;
+
+ eventTable = &endPoint->eventTable;
+ endPointId = GET_ENDPOINT_ID(endPoint);
+
+ /*
+ * Release the buffer to WMI using the registered event handler. If WMI
+ * has not registered any callbacks for a particular endpoint then it
+ * means that its queues will not have any buffers so we skip that
+ * endpoint.
+ */
+ if ((eventElement = &eventTable->element[eventId]) != NULL) {
+ while ((curr = removeFromMboxQueue(queue)) != NULL) {
+ /* Frame the event and dispatch it */
+ mboxBuffer = GET_MBOX_BUFFER(curr);
+ FRAME_EVENT(eventInfo, mboxBuffer->buffer,
+ mboxBuffer->bufferLength, mboxBuffer->actualLength,
+ A_ECANCELED, mboxBuffer->cookie);
+ if (eventElement->handler) {
+ eventElement->handler(endPoint->target, endPointId, eventId,
+ &eventInfo, eventElement->param);
+ }
+ RECYCLE_DATA_REQUEST_ELEMENT(curr);
+ }
+ }
+
+ /* Initialize the head and tail pointer */
+ queue->head = 0;
+ queue->size = 0;
+}
+
+HTC_REG_REQUEST_ELEMENT *
+allocateRegRequestElement(HTC_TARGET *target) {
+ A_UINT32 count;
+ HTC_REG_REQUEST_ELEMENT *element;
+
+ A_MUTEX_LOCK(&instanceCS);
+ element = NULL;
+ for (count = 0; count < HTC_REG_REQUEST_LIST_SIZE; count ++) {
+ element = &target->regList.element[count];
+ if (IS_ELEMENT_FREE(element)) {
+ element->buffer.free = FALSE;
+ break;
+ }
+ }
+ A_MUTEX_UNLOCK(&instanceCS);
+
+ return element;
+}
+
+void
+freeRegRequestElement(HTC_REG_REQUEST_ELEMENT *element) {
+ A_MUTEX_LOCK(&instanceCS);
+ FILL_REG_BUFFER(element, NULL, 0, 0, 0);
+ element->buffer.free = TRUE;
+ A_MUTEX_UNLOCK(&instanceCS);
+}
+
+HTC_TARGET *
+getTargetInstance(void *device)
+{
+ return AtherosTargetList[0];
+}
+
+void
+addTargetInstance(HTC_TARGET *target)
+{
+ AtherosTargetList[0] = target;
+}
+
+void
+delTargetInstance(HTC_TARGET *target)
+{
+ AtherosTargetList[0] = NULL;
+}
+
+A_UINT32
+getRegAddr(TARGET_REGISTERS base,
+ HTC_ENDPOINT_ID endPointId)
+{
+ A_UINT32 address;
+
+ switch(base) {
+ case TX_CREDIT_COUNTER_RESET_REG:
+ address = COUNT_DEC_ADDRESS + endPointId * 4;
+ break;
+
+ case TX_CREDIT_COUNTER_DECREMENT_REG:
+ address = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + endPointId) * 4;
+ break;
+
+ case TX_CREDIT_COUNTER_REG:
+ address = COUNT_ADDRESS + (HTC_MAILBOX_NUM_MAX + endPointId) * 4;
+ break;
+
+ case INT_STATUS_ENABLE_REG:
+ address = INT_STATUS_ENABLE_ADDRESS;
+ break;
+
+ case COUNTER_INT_STATUS_ENABLE_REG:
+ case COUNTER_INT_STATUS_DISABLE_REG:
+ address = COUNTER_INT_STATUS_ENABLE_ADDRESS;
+ break;
+
+ case INT_STATUS_REG:
+ address = HOST_INT_STATUS_ADDRESS;
+ break;
+
+ case CPU_INT_STATUS_REG:
+ address = CPU_INT_STATUS_ADDRESS;
+ break;
+
+ case ERROR_INT_STATUS_REG:
+ address = ERROR_INT_STATUS_ADDRESS;
+ break;
+
+ case INT_WLAN_REG:
+ address = INT_WLAN_ADDRESS;
+ break;
+
+ case WINDOW_DATA_REG:
+ address = WINDOW_DATA_ADDRESS;
+ break;
+
+ case WINDOW_WRITE_ADDR_REG:
+ address = WINDOW_WRITE_ADDR_ADDRESS;
+ break;
+
+ case WINDOW_READ_ADDR_REG:
+ address = WINDOW_READ_ADDR_ADDRESS;
+ break;
+
+ default:
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Invalid register: %d\n", base));
+ AR_DEBUG_ASSERT(0);
+ address = 0;
+ break;
+ }
+
+ return address;
+}
+
+void
+dumpBytes(A_UCHAR *buffer, A_UINT16 length)
+{
+ A_CHAR stream[60];
+ A_UINT32 i;
+ A_UINT16 offset, count;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("Dumping %d Bytes : ------>\n", length));
+
+ count = 0;
+ offset = 0;
+ for(i = 0; i < length; i++) {
+ sprintf(stream + offset, "%2x ", buffer[i]);
+ count ++;
+ offset += 3;
+
+ if(count == 16) {
+ count = 0;
+ offset = 0;
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("[H]: %s\n", stream));
+ A_MEMZERO(stream, 60);
+ }
+ }
+
+ if(offset != 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("[H]: %s\n", stream));
+ }
+}
+
+void
+dumpRegisters(HTC_TARGET *target)
+{
+ HTC_REGISTER_TABLE *reg;
+
+ reg = &target->table;
+ AR_DEBUG_PRINTF(ATH_DEBUG_DUMP, ("\n<------- Register Table -------->\nInt Status: 0x%x\nCPU Int Status: 0x%x\nError Int Status: 0x%x\nCounter Int Status: 0x%x\nMbox Frame: 0x%x\nRx Lookahead Valid: 0x%x\nRx Lookahead 0: 0x%x\nRx Lookahead 1: 0x%x\nRx Lookahead 2: 0x%x\nRx Lookahead 3: 0x%x\nInt Status Enable: 0x%x\nCounter Int Status Enable: 0x%x\n<------------------------------->\n", reg->host_int_status, reg->cpu_int_status, reg->error_int_status, reg->counter_int_status, reg->mbox_frame, reg->rx_lookahead_valid, reg->rx_lookahead[ENDPOINT1], reg->rx_lookahead[ENDPOINT2], reg->rx_lookahead[ENDPOINT3], reg->rx_lookahead[ENDPOINT4], reg->int_status_enable, reg->counter_int_status_enable));
+}
+
+A_UINT8
+htcGetBitNumSet(A_UINT32 data)
+{
+ A_UINT8 count;
+
+ count = 0;
+ while(!(data & 0x1)) {
+ count += 1;
+ data >>= 1;
+ }
+
+ return count;
+}
+
Added: developers/nbd/ar6k/include/AR6000_bmi.h
===================================================================
--- developers/nbd/ar6k/include/AR6000_bmi.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/AR6000_bmi.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+#ifndef __AR6000_BMI_H__
+#define __AR6000_BMI_H__
+
+/*
+ * Bootloader Messaging Interface (BMI)
+ *
+ * BMI is a very simple messaging interface used during initialization
+ * to read memory, write memory, execute code, and to define an
+ * application entry PC.
+ *
+ * It is used to download an application to AR6000, to provide
+ * patches to code that is already resident on AR6000, and generally
+ * to examine and modify state. The Host has an opportunity to use
+ * BMI only once during bootup. Once the Host issues a BMI_DONE
+ * command, this opportunity ends.
+ *
+ * The Host writes BMI requests to mailbox0, and reads BMI responses
+ * from mailbox0. BMI requests all begin with a command
+ * (see below for specific commands), and are followed by
+ * command-specific data.
+ *
+ * Flow control:
+ * The Host can only issue a command once the Target gives it a
+ * "BMI Command Credit", using AR6000 Counter #4. As soon as the
+ * Target has completed a command, it issues another BMI Command
+ * Credit (so the Host can issue the next command).
+ *
+ * BMI handles all required Target-side cache flushing.
+ */
+
+
+/* Maximum data size used for BMI transfers */
+#define BMI_DATASZ_MAX 32
+
+/* BMI Commands */
+
+#define BMI_NO_COMMAND 0
+
+#define BMI_DONE 1
+ /*
+ * Semantics: Host is done using BMI
+ * Request format:
+ * A_UINT32 command (BMI_DONE)
+ * Response format: none
+ */
+
+#define BMI_READ_MEMORY 2
+ /*
+ * Semantics: Host reads AR6000 memory
+ * Request format:
+ * A_UINT32 command (BMI_READ_MEMORY)
+ * A_UINT32 address
+ * A_UINT32 length, at most BMI_DATASZ_MAX
+ * Response format:
+ * A_UINT8 data[length]
+ */
+
+#define BMI_WRITE_MEMORY 3
+ /*
+ * Semantics: Host writes AR6000 memory
+ * Request format:
+ * A_UINT32 command (BMI_WRITE_MEMORY)
+ * A_UINT32 address
+ * A_UINT32 length, at most BMI_DATASZ_MAX
+ * A_UINT8 data[length]
+ * Response format: none
+ */
+
+#define BMI_EXECUTE 4
+ /*
+ * Semantics: Causes AR6000 to execute code
+ * Request format:
+ * A_UINT32 command (BMI_EXECUTE)
+ * A_UINT32 address
+ * A_UINT32 parameter
+ * Response format:
+ * A_UINT32 return value
+ */
+
+#define BMI_SET_APP_START 5
+ /*
+ * Semantics: Set Target application starting address
+ * Request format:
+ * A_UINT32 command (BMI_SET_APP_START)
+ * A_UINT32 address
+ * Response format: none
+ */
+
+#define BMI_READ_SOC_REGISTER 6
+ /*
+ * Semantics: Read a 32-bit Target SOC register.
+ * Request format:
+ * A_UINT32 command (BMI_READ_REGISTER)
+ * A_UINT32 address
+ * Response format:
+ * A_UINT32 value
+ */
+
+#define BMI_WRITE_SOC_REGISTER 7
+ /*
+ * Semantics: Write a 32-bit Target SOC register.
+ * Request format:
+ * A_UINT32 command (BMI_WRITE_REGISTER)
+ * A_UINT32 address
+ * A_UINT32 value
+ *
+ * Response format: none
+ */
+
+#define BMI_GET_TARGET_ID 8
+ /*
+ * Semantics: Fetch the 4-byte Target ID.
+ * Request format:
+ * A_UINT32 command (BMI_GET_TARGET_ID)
+ *
+ * Response format:
+ * A_UINT32 TargetID
+ */
+
+#endif /* __AR6000_BMI_H__ */
Added: developers/nbd/ar6k/include/AR6000_gpio.h
===================================================================
--- developers/nbd/ar6k/include/AR6000_gpio.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/AR6000_gpio.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2005-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+
+#define GPIO_PIN_COUNT 18
+
+/*
+ * Possible values for WMIX_GPIO_SET_REGISTER_CMDID.
+ * NB: These match hardware order, so that addresses can
+ * easily be computed.
+ */
+#define GPIO_ID_OUT 0x00000000
+#define GPIO_ID_OUT_W1TS 0x00000001
+#define GPIO_ID_OUT_W1TC 0x00000002
+#define GPIO_ID_ENABLE 0x00000003
+#define GPIO_ID_ENABLE_W1TS 0x00000004
+#define GPIO_ID_ENABLE_W1TC 0x00000005
+#define GPIO_ID_IN 0x00000006
+#define GPIO_ID_STATUS 0x00000007
+#define GPIO_ID_STATUS_W1TS 0x00000008
+#define GPIO_ID_STATUS_W1TC 0x00000009
+#define GPIO_ID_PIN0 0x0000000a
+#define GPIO_ID_PIN(n) (GPIO_ID_PIN0+(n))
+
+#define GPIO_LAST_REGISTER_ID GPIO_ID_PIN(17)
+#define GPIO_ID_NONE 0xffffffff
Added: developers/nbd/ar6k/include/AR6000_version.h
===================================================================
--- developers/nbd/ar6k/include/AR6000_version.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/AR6000_version.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+
+#define __VER_MAJOR_ 1
+#define __VER_MINOR_ 1
+#define __VER_PATCH_ 0
+
+
+/* The makear6ksdk script (used for release builds) modifies the following line. */
+#define __BUILD_NUMBER_ 2
+
+
+/* Format of the version number. */
+#define VER_MAJOR_BIT_OFFSET 28
+#define VER_MINOR_BIT_OFFSET 24
+#define VER_PATCH_BIT_OFFSET 16
+#define VER_BUILD_NUM_BIT_OFFSET 0
+
+
+/*
+ * The version has the following format:
+ * Bits 28-31: Major version
+ * Bits 24-27: Minor version
+ * Bits 16-23: Patch version
+ * Bits 0-15: Build number (automatically generated during build process )
+ * E.g. Build 1.1.3.7 would be represented as 0x11030007.
+ *
+ * DO NOT split the following macro into multiple lines as this may confuse the build scripts.
+ */
+#define AR6000_SW_VERSION ( ( __VER_MAJOR_ << VER_MAJOR_BIT_OFFSET ) + ( __VER_MINOR_ << VER_MINOR_BIT_OFFSET ) + ( __VER_PATCH_ << VER_PATCH_BIT_OFFSET ) + ( __BUILD_NUMBER_ << VER_BUILD_NUM_BIT_OFFSET ) )
+
+
Added: developers/nbd/ar6k/include/app/dset.h
===================================================================
--- developers/nbd/ar6k/include/app/dset.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/app/dset.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,77 @@
+#ifndef _DSET_H_
+#define _DSET_H_
+
+/*
+ * Structures used to communicate between an application and the kernel for
+ * DataSet ioctls.
+ *
+ * Pass a dset_request_t with cmd=AR6000_XIOCTL_WMI_WAITDSETREQ in order to
+ * wait for the next DataSet Request to arrive. Once a request is received
+ * from the Target, the kernel fills in information about the Request and
+ * returns from the ioctl to the application. When the ioctl returns,
+ * cmd is set to either AR6000_OPEN_REQ or AR6000_DATA_REQ.
+ *
+ * Pass a dset_open_reply_t with cmd=AR6000_IOCTL_WMI_DSET_OPEN_REPLY in order
+ * to send a DataSet Open reply to the Target. The targ_* fields should simply
+ * be copied from the original Open Request. The status field should be set
+ * to 0 for success or non-zero for failure.
+ *
+ * Pass a dset_data_reply_t with cmd=AR6000_IOCTL_WMI_DSET_DATA_REPLY in order
+ * to send a DataSet Data reply to the Target. The targ_* fields should simply
+ * be copied from the original Data Request. The status field should be set
+ * to 0 for success or non-zero for failure. The buf field is a pointer
+ * to a buffer that contains the requested data.
+ */
+
+/* A DataSet Request, either Open Request or Data Request */
+typedef struct dset_request_s {
+ unsigned int cmd;
+ union {
+ struct open_req_s {
+ unsigned int id;
+ unsigned int targ_handle;
+ unsigned int targ_reply_fn;
+ unsigned int targ_reply_arg;
+ } open_req;
+ struct data_req_s {
+ unsigned int access_cookie;
+ unsigned int offset;
+ unsigned int length;
+ unsigned int targ_buf;
+ unsigned int targ_reply_fn;
+ unsigned int targ_reply_arg;
+ } data_req;
+ } u;
+} dset_request_t;
+
+/*
+ * Values in cmd on return from an AR6000_IOCTL_EXTENDED ioctl that had
+ * cmd=AR6000_XIOCTL_WMI_WAITDSETREQ.
+ */
+#define AR6000_OPEN_REQ 1
+#define AR6000_DATA_REQ 2
+
+/* Open Reply from Application to Kernel (to be sent to Target) */
+typedef struct dset_open_reply {
+ unsigned int cmd;
+ unsigned int status;
+ unsigned int targ_handle;
+ unsigned int targ_reply_fn;
+ unsigned int targ_reply_arg;
+ unsigned int access_cookie;
+ unsigned int size;
+ unsigned int version;
+} dset_open_reply_t;
+
+/* Data Reply from Application to Kernel (to be sent to Target) */
+typedef struct dset_data_reply {
+ unsigned int cmd;
+ unsigned int status;
+ char *buf;
+ unsigned int length;
+ unsigned int targ_buf;
+ unsigned int targ_reply_fn;
+ unsigned int targ_reply_arg;
+} dset_data_reply_t;
+
+#endif /* _DSET_H_ */
Added: developers/nbd/ar6k/include/ar6000_api.h
===================================================================
--- developers/nbd/ar6k/include/ar6000_api.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/ar6000_api.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the API to access the OS dependent atheros host driver
+ * by the WMI or WLAN generic modules.
+ *
+ */
+
+#ifndef _AR6000_API_H_
+#define _AR6000_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ar6_softc;
+
+void ar6000_ready_event(void *devt, A_UINT8 *datap, A_UINT8 phyCap);
+A_UINT8 ar6000_iptos_to_userPriority(A_UINT8 *pkt);
+A_STATUS ar6000_control_tx(void *devt, void *osbuf, int endPt);
+void ar6000_connect_event(struct ar6_softc *ar, A_UINT16 channel,
+ A_UINT8 *bssid, A_UINT16 listenInterval,
+ A_UINT8 beaconIeLen, A_UINT8 assocReqLen,
+ A_UINT8 assocRespLen,A_UINT8 *assocInfo);
+void ar6000_disconnect_event(struct ar6_softc *ar, A_UINT8 reason,
+ A_UINT8 *bssid, A_UINT8 assocRespLen,
+ A_UINT8 *assocInfo);
+void ar6000_tkip_micerr_event(struct ar6_softc *ar, A_UINT8 keyid,
+ A_BOOL ismcast);
+void ar6000_bitrate_rx(void *devt, A_INT32 rateKbps);
+void ar6000_channelList_rx(void *devt, A_INT8 numChan, A_UINT16 *chanList);
+void ar6000_regDomain_event(struct ar6_softc *ar, A_UINT32 regCode);
+void ar6000_txPwr_rx(void *devt, A_UINT8 txPwr);
+void ar6000_neighborReport_event(struct ar6_softc *ar, int numAps,
+ WMI_NEIGHBOR_INFO *info);
+void ar6000_set_numdataendpts(struct ar6_softc *ar, A_UINT32 num);
+void ar6000_scanComplete_event(struct ar6_softc *ar);
+void ar6000_targetStats_event(struct ar6_softc *ar, WMI_TARGET_STATS *pStats);
+void ar6000_rssiThreshold_event(struct ar6_softc *, WMI_RSSI_THRESHOLD_VAL);
+void ar6000_reportError_event(struct ar6_softc *, WMI_TARGET_ERROR_VAL errorVal);
+void ar6000_cac_event(struct ar6_softc *ar, A_UINT8 ac, A_UINT8 cac_indication,
+ A_UINT8 statusCode, A_UINT8 *tspecSuggestion);
+
+void
+ar6000_roam_tbl_event(struct ar6_softc *ar, WMI_TARGET_ROAM_TBL *pTbl);
+
+void
+ar6000_roam_data_event(struct ar6_softc **ar, WMI_TARGET_ROAM_DATA *p);
+
+void ar6000_dset_open_req(void *devt,
+ A_UINT32 id,
+ A_UINT32 targ_handle,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg);
+void ar6000_dset_close(void *devt, A_UINT32 access_cookie);
+void ar6000_dset_data_req(void *devt,
+ A_UINT32 access_cookie,
+ A_UINT32 offset,
+ A_UINT32 length,
+ A_UINT32 targ_buf,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg);
+
+void ar6000_gpio_intr_rx(A_UINT32 intr_mask, A_UINT32 input_values);
+void ar6000_gpio_data_rx(A_UINT32 reg_id, A_UINT32 value);
+void ar6000_gpio_ack_rx(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AR6000_API_H_ */
Added: developers/nbd/ar6k/include/athdefs.h
===================================================================
--- developers/nbd/ar6k/include/athdefs.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/athdefs.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,82 @@
+
+#ifndef __ATHDEFS_H__
+#define __ATHDEFS_H__
+
+/*
+ * This file contains definitions that may be used across both
+ * Host and Target software. Nothing here is module-dependent
+ * or platform-dependent.
+ */
+
+/*
+ * Generic error codes that can be used by hw, sta, ap, sim, dk
+ * and any other environments. Since these are enums, feel free to
+ * add any more codes that you need.
+ */
+
+typedef enum {
+ A_ERROR = -1, /* Generic error return */
+ A_OK = 0, /* success */
+ /* Following values start at 1 */
+ A_DEVICE_NOT_FOUND, /* not able to find PCI device */
+ A_NO_MEMORY, /* not able to allocate memory, not available */
+ A_MEMORY_NOT_AVAIL, /* memory region is not free for mapping */
+ A_NO_FREE_DESC, /* no free descriptors available */
+ A_BAD_ADDRESS, /* address does not match descriptor */
+ A_WIN_DRIVER_ERROR, /* used in NT_HW version, if problem at init */
+ A_REGS_NOT_MAPPED, /* registers not correctly mapped */
+ A_EPERM, /* Not superuser */
+ A_EACCES, /* Access denied */
+ A_ENOENT, /* No such entry, search failed, etc. */
+ A_EEXIST, /* The object already exists (can't create) */
+ A_EFAULT, /* Bad address fault */
+ A_EBUSY, /* Object is busy */
+ A_EINVAL, /* Invalid parameter */
+ A_EMSGSIZE, /* Inappropriate message buffer length */
+ A_ECANCELED, /* Operation canceled */
+ A_ENOTSUP, /* Operation not supported */
+ A_ECOMM, /* Communication error on send */
+ A_EPROTO, /* Protocol error */
+ A_ENODEV, /* No such device */
+ A_EDEVNOTUP, /* device is not UP */
+ A_NO_RESOURCE, /* No resources for requested operation */
+ A_HARDWARE, /* Hardware failure */
+ A_PENDING, /* Asynchronous routine; will send up results la
+ter (typically in callback) */
+ A_EBADCHANNEL, /* The channel cannot be used */
+ A_DECRYPT_ERROR, /* Decryption error */
+ A_PHY_ERROR, /* RX PHY error */
+ A_CONSUMED /* Object was consumed */
+} A_STATUS;
+
+#define A_SUCCESS(x) (x == A_OK)
+#define A_FAILED(x) (!A_SUCCESS(x))
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * The following definition is WLAN specific definition
+ */
+typedef enum {
+ MODE_11A = 0, /* 11a Mode */
+ MODE_11G = 1, /* 11g + 11b Mode */
+ MODE_11B = 2, /* 11b Mode */
+ MODE_11GONLY = 3, /* 11g only Mode */
+ MODE_UNKNOWN = 4,
+
+ MODE_MAX = 4
+} WLAN_PHY_MODE;
+
+typedef enum {
+ WLAN_11A_CAPABILITY = 1,
+ WLAN_11G_CAPABILITY = 2,
+ WLAN_11AG_CAPABILITY = 3,
+}WALN_CAPABILITY;
+
+#endif /* __ATHDEFS_H__ */
Added: developers/nbd/ar6k/include/athdrv.h
===================================================================
--- developers/nbd/ar6k/include/athdrv.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/athdrv.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions for the AR6000 linux driver.
+ *
+ */
+
+#ifndef _ATHDRV_H_
+#define _ATHDRV_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * There are two types of ioctl's here: Standard ioctls and
+ * eXtended ioctls. All extended ioctls (XIOCTL) are multiplexed
+ * off of the single ioctl command, AR6000_IOCTL_EXTENDED. The
+ * arguments for every XIOCTL starts with a 32-bit command word
+ * that is used to select which extended ioctl is in use. After
+ * the command word are command-specific arguments.
+ */
+
+/* Linux standard Wireless Extensions, private ioctl interfaces */
+#define IEEE80211_IOCTL_SETPARAM (SIOCIWFIRSTPRIV+0)
+#define IEEE80211_IOCTL_GETPARAM (SIOCIWFIRSTPRIV+1)
+#define IEEE80211_IOCTL_SETKEY (SIOCIWFIRSTPRIV+2)
+#define IEEE80211_IOCTL_SETWMMPARAMS (SIOCIWFIRSTPRIV+3)
+#define IEEE80211_IOCTL_DELKEY (SIOCIWFIRSTPRIV+4)
+#define IEEE80211_IOCTL_GETWMMPARAMS (SIOCIWFIRSTPRIV+5)
+#define IEEE80211_IOCTL_SETMLME (SIOCIWFIRSTPRIV+6)
+#define IEEE80211_IOCTL_SETOPTIE (SIOCIWFIRSTPRIV+6)
+#define IEEE80211_IOCTL_GETOPTIE (SIOCIWFIRSTPRIV+7)
+#define IEEE80211_IOCTL_ADDPMKID (SIOCIWFIRSTPRIV+8)
+//#define IEEE80211_IOCTL_SETAUTHALG (SIOCIWFIRSTPRIV+10)
+#define IEEE80211_IOCTL_LASTONE (SIOCIWFIRSTPRIV+9)
+
+
+
+/* ====WMI Ioctls==== */
+/*
+ *
+ * Many ioctls simply provide WMI services to application code:
+ * an application makes such an ioctl call with a set of arguments
+ * that are packaged into the corresponding WMI message, and sent
+ * to the Target.
+ */
+
+#define AR6000_IOCTL_WMI_GETREV (SIOCIWFIRSTPRIV+10)
+/*
+ * arguments:
+ * ar6000_version *revision
+ */
+
+#define AR6000_IOCTL_WMI_SETPWR (SIOCIWFIRSTPRIV+11)
+/*
+ * arguments:
+ * WMI_POWER_MODE_CMD pwrModeCmd (see include/wmi.h)
+ * uses: WMI_SET_POWER_MODE_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SETSCAN (SIOCIWFIRSTPRIV+12)
+/*
+ * arguments:
+ * WMI_SCAN_PARAMS_CMD scanParams (see include/wmi.h)
+ * uses: WMI_SET_SCAN_PARAMS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SETLISTENINT (SIOCIWFIRSTPRIV+13)
+/*
+ * arguments:
+ * UINT32 listenInterval
+ * uses: WMI_SET_LISTEN_INT_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SETBSSFILTER (SIOCIWFIRSTPRIV+14)
+/*
+ * arguments:
+ * WMI_BSS_FILTER filter (see include/wmi.h)
+ * uses: WMI_SET_BSS_FILTER_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_CHANNELPARAMS (SIOCIWFIRSTPRIV+16)
+/*
+ * arguments:
+ * WMI_CHANNEL_PARAMS_CMD chParams
+ * uses: WMI_SET_CHANNEL_PARAMS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_PROBEDSSID (SIOCIWFIRSTPRIV+17)
+/*
+ * arguments:
+ * WMI_PROBED_SSID_CMD probedSsids (see include/wmi.h)
+ * uses: WMI_SETPROBED_SSID_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_PMPARAMS (SIOCIWFIRSTPRIV+18)
+/*
+ * arguments:
+ * WMI_POWER_PARAMS_CMD powerParams (see include/wmi.h)
+ * uses: WMI_SET_POWER_PARAMS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_BADAP (SIOCIWFIRSTPRIV+19)
+/*
+ * arguments:
+ * WMI_ADD_BAD_AP_CMD badAPs (see include/wmi.h)
+ * uses: WMI_ADD_BAD_AP_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_GET_QOS_QUEUE (SIOCIWFIRSTPRIV+20)
+/*
+ * arguments:
+ * ar6000_queuereq queueRequest (see below)
+ */
+
+#define AR6000_IOCTL_WMI_CREATE_QOS (SIOCIWFIRSTPRIV+21)
+/*
+ * arguments:
+ * WMI_CREATE_PSTREAM createPstreamCmd (see include/wmi.h)
+ * uses: WMI_CREATE_PSTREAM_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_DELETE_QOS (SIOCIWFIRSTPRIV+22)
+/*
+ * arguments:
+ * WMI_DELETE_PSTREAM_CMD deletePstreamCmd (see include/wmi.h)
+ * uses: WMI_DELETE_PSTREAM_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_LINKTHRESHOLD (SIOCIWFIRSTPRIV+23)
+/*
+ * arguments:
+ * WMI_RSSI_THRESHOLD_PARAMS_CMD thresholdParams (see include/wmi.h)
+ * uses: WMI_RSSI_THRESHOLD_PARAMS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_ERROR_REPORT_BITMASK (SIOCIWFIRSTPRIV+24)
+/*
+ * arguments:
+ * WMI_TARGET_ERROR_REPORT_BITMASK errorReportBitMask (see include/wmi.h)
+ * uses: WMI_TARGET_ERROR_REPORT_BITMASK_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_GET_TARGET_STATS (SIOCIWFIRSTPRIV+25)
+/*
+ * arguments:
+ * TARGET_STATS *targetStats (see below)
+ * uses: WMI_GET_STATISTICS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_ASSOC_INFO (SIOCIWFIRSTPRIV+26)
+/*
+ * arguments:
+ * WMI_SET_ASSOC_INFO_CMD setAssocInfoCmd
+ * uses: WMI_SET_ASSOC_INFO_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_ACCESS_PARAMS (SIOCIWFIRSTPRIV+27)
+/*
+ * arguments:
+ * WMI_SET_ACCESS_PARAMS_CMD setAccessParams (see include/wmi.h)
+ * uses: WMI_SET_ACCESS_PARAMS_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_BMISS_TIME (SIOCIWFIRSTPRIV+28)
+/*
+ * arguments:
+ * UINT32 beaconMissTime
+ * uses: WMI_SET_BMISS_TIME_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_DISC_TIMEOUT (SIOCIWFIRSTPRIV+29)
+/*
+ * arguments:
+ * WMI_DISC_TIMEOUT_CMD disconnectTimeoutCmd (see include/wmi.h)
+ * uses: WMI_SET_DISC_TIMEOUT_CMDID
+ */
+
+#define AR6000_IOCTL_WMI_SET_IBSS_PM_CAPS (SIOCIWFIRSTPRIV+30)
+/*
+ * arguments:
+ * WMI_IBSS_PM_CAPS_CMD ibssPowerMgmtCapsCmd
+ * uses: WMI_SET_IBSS_PM_CAPS_CMDID
+ */
+
+/*
+ * There is a very small space available for driver-private
+ * wireless ioctls. In order to circumvent this limitation,
+ * we multiplex a bunch of ioctls (XIOCTLs) on top of a
+ * single AR6000_IOCTL_EXTENDED ioctl.
+ */
+#define AR6000_IOCTL_EXTENDED (SIOCIWFIRSTPRIV+31)
+
+
+/* ====BMI Extended Ioctls==== */
+
+#define AR6000_XIOCTL_BMI_DONE 1
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_BMI_DONE)
+ * uses: BMI_DONE
+ */
+
+#define AR6000_XIOCTL_BMI_READ_MEMORY 2
+/*
+ * arguments:
+ * union {
+ * struct {
+ * UINT32 cmd (AR6000_XIOCTL_BMI_READ_MEMORY)
+ * UINT32 address
+ * UINT32 length
+ * }
+ * char results[length]
+ * }
+ * uses: BMI_READ_MEMORY
+ */
+
+#define AR6000_XIOCTL_BMI_WRITE_MEMORY 3
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_BMI_WRITE_MEMORY)
+ * UINT32 address
+ * UINT32 length
+ * char data[length]
+ * uses: BMI_WRITE_MEMORY
+ */
+
+#define AR6000_XIOCTL_BMI_EXECUTE 4
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_BMI_EXECUTE)
+ * UINT32 TargetAddress
+ * UINT32 parameter
+ * uses: BMI_EXECUTE
+ */
+
+#define AR6000_XIOCTL_BMI_SET_APP_START 5
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_BMI_SET_APP_START)
+ * UINT32 TargetAddress
+ * uses: BMI_SET_APP_START
+ */
+
+#define AR6000_XIOCTL_BMI_READ_SOC_REGISTER 6
+/*
+ * arguments:
+ * union {
+ * struct {
+ * UINT32 cmd (AR6000_XIOCTL_BMI_READ_SOC_REGISTER)
+ * UINT32 TargetAddress, 32-bit aligned
+ * }
+ * UINT32 result
+ * }
+ * uses: BMI_READ_SOC_REGISTER
+ */
+
+#define AR6000_XIOCTL_BMI_WRITE_SOC_REGISTER 7
+/*
+ * arguments:
+ * struct {
+ * UINT32 cmd (AR6000_XIOCTL_BMI_WRITE_SOC_REGISTER)
+ * UINT32 TargetAddress, 32-bit aligned
+ * UINT32 newValue
+ * }
+ * uses: BMI_WRITE_SOC_REGISTER
+ */
+
+#define AR6000_XIOCTL_BMI_TEST 8
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_BMI_TEST)
+ * UINT32 address
+ * UINT32 length
+ * UINT32 count
+ */
+
+
+
+/* ====DataSet Extended Ioctls==== */
+
+#define AR6000_XIOCTL_WMI_DSET_WAIT_REQ 9
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_WMI_DSET_WAIT_REQ)
+ */
+
+#define AR6000_XIOCTL_WMI_DSET_OPEN_REPLY 10
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_WMI_DSET_OPEN_REPLY)
+ * dset_open_reply_t (see host/include/app/dset.h)
+ * uses: WMIX_DSETOPEN_REPLY_CMDID
+ */
+
+#define AR6000_XIOCTL_WMI_DSET_DATA_REPLY 11
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_WMI_DSET_DATA_REPLY)
+ * dset_data_reply_t (see host/include/app/dset.h)
+ * uses: WMIX_DSETDATA_REPLY_CMDID
+ */
+
+#define AR6000_XIOCTL_FORCE_TARGET_RESET 12
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_FORCE_TARGET_RESET)
+ */
+
+
+#ifdef HTC_RAW_INTERFACE
+/* HTC Raw Interface Ioctls */
+#define AR6000_XIOCTL_HTC_RAW_OPEN 13
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_HTC_RAW_OPEN)
+ */
+
+#define AR6000_XIOCTL_HTC_RAW_CLOSE 14
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_HTC_RAW_CLOSE)
+ */
+
+#define AR6000_XIOCTL_HTC_RAW_READ 15
+/*
+ * arguments:
+ * union {
+ * struct {
+ * UINT32 cmd (AR6000_XIOCTL_HTC_RAW_READ)
+ * UINT32 mailboxID
+ * UINT32 length
+ * }
+ * results[length]
+ * }
+ */
+
+#define AR6000_XIOCTL_HTC_RAW_WRITE 16
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_HTC_RAW_WRITE)
+ * UINT32 mailboxID
+ * UINT32 length
+ * char buffer[length]
+ */
+#endif /* HTC_RAW_INTERFACE */
+
+#define AR6000_XIOCTL_CHECK_TARGET_READY 17
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_CHECK_TARGET_READY)
+ */
+
+
+
+/* ====GPIO (General Purpose I/O) Extended Ioctls==== */
+
+#define AR6000_XIOCTL_GPIO_OUTPUT_SET 18
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_OUTPUT_SET)
+ * ar6000_gpio_output_set_cmd_s (see below)
+ * uses: WMIX_GPIO_OUTPUT_SET_CMDID
+ */
+
+#define AR6000_XIOCTL_GPIO_INPUT_GET 19
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_INPUT_GET)
+ * uses: WMIX_GPIO_INPUT_GET_CMDID
+ */
+
+#define AR6000_XIOCTL_GPIO_REGISTER_SET 20
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_REGISTER_SET)
+ * ar6000_gpio_register_cmd_s (see below)
+ * uses: WMIX_GPIO_REGISTER_SET_CMDID
+ */
+
+#define AR6000_XIOCTL_GPIO_REGISTER_GET 21
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_REGISTER_GET)
+ * ar6000_gpio_register_cmd_s (see below)
+ * uses: WMIX_GPIO_REGISTER_GET_CMDID
+ */
+
+#define AR6000_XIOCTL_GPIO_INTR_ACK 22
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_INTR_ACK)
+ * ar6000_cpio_intr_ack_cmd_s (see below)
+ * uses: WMIX_GPIO_INTR_ACK_CMDID
+ */
+
+#define AR6000_XIOCTL_GPIO_INTR_WAIT 23
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_GPIO_INTR_WAIT)
+ */
+
+
+
+/* ====more wireless commands==== */
+
+#define AR6000_XIOCTL_SET_ADHOC_BSSID 24
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_SET_ADHOC_BSSID)
+ * WMI_SET_ADHOC_BSSID_CMD setAdHocBssidCmd (see include/wmi.h)
+ */
+
+#define AR6000_XIOCTL_SET_OPT_MODE 25
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_SET_OPT_MODE)
+ * WMI_SET_OPT_MODE_CMD setOptModeCmd (see include/wmi.h)
+ * uses: WMI_SET_OPT_MODE_CMDID
+ */
+
+#define AR6000_XIOCTL_OPT_SEND_FRAME 26
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_OPT_SEND_FRAME)
+ * WMI_OPT_TX_FRAME_CMD optTxFrameCmd (see include/wmi.h)
+ * uses: WMI_OPT_TX_FRAME_CMDID
+ */
+
+#define AR6000_XIOCTL_SET_ADHOC_BEACON_INTVAL 27
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_SET_ADHOC_BEACON_INTVAL)
+ * WMI_BEACON_INT_CMD beaconIntCmd (see include/wmi.h)
+ * uses: WMI_SET_BEACON_INT_CMDID
+ */
+
+
+#define IEEE80211_IOCTL_SETAUTHALG 28
+
+
+#define AR6000_XIOCTL_SET_VOICE_PKT_SIZE 29
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_SET_VOICE_PKT_SIZE)
+ * WMI_SET_VOICE_PKT_SIZE_CMD setVoicePktSizeCmd (see include/wmi.h)
+ * uses: WMI_SET_VOICE_PKT_SIZE_CMDID
+ */
+
+
+#define AR6000_XIOCTL_SET_MAX_SP 30
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTL_SET_MAX_SP)
+ * WMI_SET_MAX_SP_LEN_CMD maxSPLen(see include/wmi.h)
+ * uses: WMI_SET_MAX_SP_LEN_CMDID
+ */
+
+#define AR6000_XIOCTL_WMI_GET_ROAM_TBL 31
+
+#define AR6000_XIOCTL_WMI_SET_ROAM_CTRL 32
+
+#define AR6000_XIOCTRL_WMI_SET_POWERSAVE_TIMERS 33
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTRL_WMI_SET_POWERSAVE_TIMERS)
+ * WMI_SET_POWERSAVE_TIMERS_CMD powerSaveTimers(see include/wmi.h)
+ * WMI_SET_POWERSAVE_TIMERS_CMDID
+ */
+
+#define AR6000_XIOCTRL_WMI_GET_POWER_MODE 34
+/*
+ * arguments:
+ * UINT32 cmd (AR6000_XIOCTRL_WMI_GET_POWER_MODE)
+ */
+
+#define AR6000_XIOCTRL_WMI_SET_WLAN_STATE 35
+typedef enum {
+ WLAN_DISABLED,
+ WLAN_ENABLED
+} AR6000_WLAN_STATE;
+/*
+ * arguments:
+ * enable/disable
+ */
+
+#define AR6000_XIOCTL_WMI_GET_ROAM_DATA 36
+
+/* used by AR6000_IOCTL_WMI_GETREV */
+struct ar6000_version {
+ A_UINT32 host_ver;
+ A_UINT32 target_ver;
+};
+
+/* used by AR6000_IOCTL_WMI_GET_QOS_QUEUE */
+struct ar6000_queuereq{
+ A_UINT8 trafficDirection;
+ A_UINT8 trafficClass;
+ A_INT8 queueNumber;
+};
+
+/* used by AR6000_IOCTL_WMI_GET_TARGET_STATS */
+typedef struct targetStats_t {
+ A_UINT64 tx_packets;
+ A_UINT64 tx_bytes;
+ A_UINT64 tx_unicast_pkts;
+ A_UINT64 tx_unicast_bytes;
+ A_UINT64 tx_multicast_pkts;
+ A_UINT64 tx_multicast_bytes;
+ A_UINT64 tx_broadcast_pkts;
+ A_UINT64 tx_broadcast_bytes;
+ A_UINT64 tx_rts_success_cnt;
+ A_UINT64 tx_packet_per_ac[4];
+
+ A_UINT64 tx_errors;
+ A_UINT64 tx_failed_cnt;
+ A_UINT64 tx_retry_cnt;
+ A_UINT64 tx_rts_fail_cnt;
+ A_UINT64 rx_packets;
+ A_UINT64 rx_bytes;
+ A_UINT64 rx_unicast_pkts;
+ A_UINT64 rx_unicast_bytes;
+ A_UINT64 rx_multicast_pkts;
+ A_UINT64 rx_multicast_bytes;
+ A_UINT64 rx_broadcast_pkts;
+ A_UINT64 rx_broadcast_bytes;
+ A_UINT64 rx_fragment_pkt;
+
+ A_UINT64 rx_errors;
+ A_UINT64 rx_crcerr;
+ A_UINT64 rx_key_cache_miss;
+ A_UINT64 rx_decrypt_err;
+ A_UINT64 rx_duplicate_frames;
+
+ A_UINT64 tkip_local_mic_failure;
+ A_UINT64 tkip_counter_measures_invoked;
+ A_UINT64 tkip_replays;
+ A_UINT64 tkip_format_errors;
+ A_UINT64 ccmp_format_errors;
+ A_UINT64 ccmp_replays;
+
+ A_UINT64 power_save_failure_cnt;
+ A_INT16 noise_floor_calibation;
+
+ A_UINT64 cs_bmiss_cnt;
+ A_UINT64 cs_lowRssi_cnt;
+ A_UINT64 cs_connect_cnt;
+ A_UINT64 cs_disconnect_cnt;
+ A_UINT8 cs_aveBeacon_rssi;
+ A_UINT8 cs_lastRoam_msec;
+}TARGET_STATS;
+
+/* used by AR6000_XIOCTL_GPIO_OUTPUT_SET */
+struct ar6000_gpio_output_set_cmd_s {
+ A_UINT32 set_mask;
+ A_UINT32 clear_mask;
+ A_UINT32 enable_mask;
+ A_UINT32 disable_mask;
+};
+
+/*
+ * used by AR6000_XIOCTL_GPIO_REGISTER_GET and AR6000_XIOCTL_GPIO_REGISTER_SET
+ */
+struct ar6000_gpio_register_cmd_s {
+ A_UINT32 gpioreg_id;
+ A_UINT32 value;
+};
+
+/* used by AR6000_XIOCTL_GPIO_INTR_ACK */
+struct ar6000_gpio_intr_ack_cmd_s {
+ A_UINT32 ack_mask;
+};
+
+/* used by AR6000_XIOCTL_GPIO_INTR_WAIT */
+struct ar6000_gpio_intr_wait_cmd_s {
+ A_UINT32 intr_mask;
+ A_UINT32 input_values;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ATHDRV_H_ */
Added: developers/nbd/ar6k/include/athtypes.h
===================================================================
--- developers/nbd/ar6k/include/athtypes.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/athtypes.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions of the basic atheros data types.
+ * It is used to map the data types in atheros files to a platform specific
+ * type.
+ *
+ */
+
+#ifndef _ATHTYPES_LINUX_H_
+#define _ATHTYPES_LINUX_H_
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#endif
+
+typedef int8_t A_INT8;
+typedef int16_t A_INT16;
+typedef int32_t A_INT32;
+typedef int64_t A_INT64;
+
+typedef u_int8_t A_UINT8;
+typedef u_int16_t A_UINT16;
+typedef u_int32_t A_UINT32;
+typedef u_int64_t A_UINT64;
+
+typedef int A_BOOL;
+typedef char A_CHAR;
+typedef unsigned char A_UCHAR;
+
+#endif /* _ATHTYPES_LINUX_H_ */
Added: developers/nbd/ar6k/include/bmi.h
===================================================================
--- developers/nbd/ar6k/include/bmi.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/bmi.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * BMI declarations and prototypes
+ */
+
+#ifndef _BMI_H_
+#define _BMI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Header files */
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/hif.h"
+#include "../include/osapi.h"
+
+void
+BMIInit(void);
+
+A_STATUS
+BMIDone(HIF_DEVICE *device);
+
+A_STATUS
+BMIGetTargetId(HIF_DEVICE *device, A_UINT32 *id);
+
+A_STATUS
+BMIReadMemory(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length);
+
+A_STATUS
+BMIWriteMemory(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length);
+
+A_STATUS
+BMIExecute(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 *param);
+
+A_STATUS
+BMISetAppStart(HIF_DEVICE *device,
+ A_UINT32 address);
+
+A_STATUS
+BMIReadSOCRegister(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 *param);
+
+A_STATUS
+BMIWriteSOCRegister(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UINT32 param);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BMI_H_ */
Added: developers/nbd/ar6k/include/dset_api.h
===================================================================
--- developers/nbd/ar6k/include/dset_api.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/dset_api.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2005-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * Host-side DataSet API.
+ *
+ */
+
+#ifndef _DSET_API_H_
+#define _DSET_API_H_
+
+/*
+ * Host-side DataSet support is optional, and is not
+ * currently required for correct operation. To disable
+ * Host-side DataSet support, set this to 0.
+ */
+#define CONFIG_HOST_DSET_SUPPORT 1
+
+/* Called to send a DataSet Open Reply back to the Target. */
+A_STATUS wmi_dset_open_reply(struct wmi_t *wmip,
+ A_UINT32 status,
+ A_UINT32 access_cookie,
+ A_UINT32 size,
+ A_UINT32 version,
+ A_UINT32 targ_handle,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg);
+
+/* Called to send a DataSet Data Reply back to the Target. */
+A_STATUS wmi_dset_data_reply(struct wmi_t *wmip,
+ A_UINT32 status,
+ A_UINT8 *host_buf,
+ A_UINT32 length,
+ A_UINT32 targ_buf,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg);
+
+#endif /* _DSET_API_H_ */
Added: developers/nbd/ar6k/include/gpio_api.h
===================================================================
--- developers/nbd/ar6k/include/gpio_api.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/gpio_api.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2005-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ */
+
+#ifndef _GPIO_API_H_
+#define _GPIO_API_H_
+
+/*
+ * Host-side GPIO support is optional.
+ * If run-time access to GPIO pins is not required, then
+ * this should be changed to 0.
+ */
+#define CONFIG_HOST_GPIO_SUPPORT 1
+
+/*
+ * Host-side General Purpose I/O API.
+ *
+ * $Id: //depot/sw/releases/etnaGPL1.1/host/include/gpio_api.h#2 $
+ */
+
+/*
+ * Send a command to the Target in order to change output on GPIO pins.
+ */
+A_STATUS wmi_gpio_output_set(struct wmi_t *wmip,
+ A_UINT32 set_mask,
+ A_UINT32 clear_mask,
+ A_UINT32 enable_mask,
+ A_UINT32 disable_mask);
+
+/*
+ * Send a command to the Target requesting input state of GPIO pins.
+ */
+A_STATUS wmi_gpio_input_get(struct wmi_t *wmip);
+
+/*
+ * Send a command to the Target to change the value of a GPIO register.
+ */
+A_STATUS wmi_gpio_register_set(struct wmi_t *wmip,
+ A_UINT32 gpioreg_id,
+ A_UINT32 value);
+
+/*
+ * Send a command to the Target to fetch the value of a GPIO register.
+ */
+A_STATUS wmi_gpio_register_get(struct wmi_t *wmip, A_UINT32 gpioreg_id);
+
+/*
+ * Send a command to the Target, acknowledging some GPIO interrupts.
+ */
+A_STATUS wmi_gpio_intr_ack(struct wmi_t *wmip, A_UINT32 ack_mask);
+
+#endif /* _GPIO_API_H_ */
Added: developers/nbd/ar6k/include/hif.h
===================================================================
--- developers/nbd/ar6k/include/hif.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/hif.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * HIF specific declarations and prototypes
+ */
+
+#ifndef _HIF_H_
+#define _HIF_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Header files */
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+
+typedef struct htc_callbacks HTC_CALLBACKS;
+typedef struct hif_request HIF_REQUEST;
+typedef struct sdio_func HIF_DEVICE;
+
+typedef enum {
+ HIF_READ,
+ HIF_WRITE
+} HIF_DIRECTION;
+
+typedef enum {
+ HIF_BASIC_IO,
+ HIF_EXTENDED_IO
+} HIF_CMDTYPE;
+
+typedef enum {
+ HIF_SYNCHRONOUS,
+ HIF_ASYNCHRONOUS
+} HIF_EXECMODE;
+
+typedef enum {
+ HIF_BYTE_BASIS,
+ HIF_BLOCK_BASIS
+} HIF_DATAMODE;
+
+typedef enum {
+ HIF_FIXED_ADDRESS,
+ HIF_INCREMENTAL_ADDRESS
+} HIF_ADDRMODE;
+
+typedef enum {
+ HIF_DEVICE_POWER_STATE,
+ HIF_DEVICE_GET_MBOX_BLOCK_SIZE,
+ HIF_DEVICE_GET_MBOX_ADDR,
+} HIF_DEVICE_CONFIG_OPCODE;
+
+#define HIF_MAX_DEVICES 1
+
+#define HIF_FRAME_REQUEST(request, _direction, _type, \
+ _emode, _dmode, _amode) do { \
+ (request)->direction = _direction; \
+ (request)->type = _type; \
+ (request)->emode = _emode; \
+ (request)->dmode = _dmode; \
+ (request)->amode = _amode; \
+} while(0)
+
+struct htc_callbacks {
+ A_UCHAR *name;
+ A_UINT32 id;
+ A_STATUS (* deviceInsertedHandler)(HIF_DEVICE *device);
+ A_STATUS (* deviceRemovedHandler)(HIF_DEVICE *device);
+ A_STATUS (* deviceSuspendHandler)(HIF_DEVICE *device);
+ A_STATUS (* deviceResumeHandler)(HIF_DEVICE *device);
+ A_STATUS (* deviceWakeupHandler)(HIF_DEVICE *device);
+ A_STATUS (* rwCompletionHandler)(void *context, A_STATUS status);
+#ifdef CF
+ A_STATUS (* deviceInterruptDisabler)(HIF_DEVICE *device,A_BOOL *callDsr);
+ A_STATUS (* deviceInterruptEnabler)(HIF_DEVICE *device);
+#endif /* CF */
+ A_STATUS (* dsrHandler)(HIF_DEVICE *device);
+};
+
+/*
+ * The request structure captures different attributes that can precisely
+ * characterize a command and its behavior for different physical interfaces.
+ */
+struct hif_request {
+ HIF_DIRECTION direction; /* HIF_READ/HIF_WRITE */
+ HIF_CMDTYPE type; /* HIF_BASIC_IO/HIF_EXTENDED_IO */
+ HIF_EXECMODE emode; /* HIF_SYNCHRONOUS/HIF_ASYNCHRONOUS */
+ HIF_DATAMODE dmode; /* HIF_BYTE_BASIS/HIF_BLOCK_BASIS */
+ HIF_ADDRMODE amode; /* HIF_FIXED_ADDRESS/HIF_INCREMENTAL_ADDRESS */
+};
+
+/*
+ * This API is used by the HTC layer to register different callback routines
+ * with the HIF layer. Support for following events has been captured - DSR,
+ * Read/Write completion, Device insertion/removal, Device suspension/
+ * resumption/wakeup. In addition to this, the API is also used to register
+ * the name and the revision of the chip. The latter can be used to verify
+ * the revision of the chip read from the device before reporting it to HTC.
+ */
+void
+HIFRegisterCallbacks(HTC_CALLBACKS *callbacks);
+
+/*
+ * This API is used to provide the read/write interface over the specific bus
+ * interface.
+ * address - Starting address in the dragon's address space. For mailbox
+ * writes, it refers to the start of the mbox boundary. It should
+ * be ensured that the last byte falls on the mailbox's EOM. For
+ * mailbox reads, it refers to the end of the mbox boundary.
+ * buffer - Pointer to the buffer containg the data to be transmitted or
+ * received.
+ * length - Amount of data to be transmitted or received.
+ * request - Characterizes the attributes of the command.
+ * direction - Direction of transfer (HIF_READ/HIF_WRITE).
+ * type - An interface may support different kind of read/write commands.
+ * For example: SDIO supports CMD52/CMD53s. The command type
+ * is thus divided into a basic and an extended command and can
+ * be specified using HIF_BASIC_IO/HIF_EXTENDED_IO.
+ * emode - This indicates the whether the command is to be executed in a
+ * blocking or non-blocking fashion (HIF_SYNCHRONOUS/
+ * HIF_ASYNCHRONOUS). The read/write data paths in HTC have been
+ * implemented using the asynchronous mode allowing the the bus
+ * driver to indicate the completion of operation through the
+ * registered callback routine. The requirement primarily comes
+ * from the contexts these operations get called from (a driver's
+ * transmit context or the ISR context in case of receive).
+ * Support for both of these modes is essential.
+ * dmode - An interface may support different kinds of commands based on
+ * the tradeoff between the amount of data it can carry and the
+ * setup time. Byte and Block modes are supported (HIF_BYTE_BASIS/
+ * HIF_BLOCK_BASIS). In case of latter, the data is rounded off
+ * to the nearest block size by padding. The size of the block is
+ * configurable at compile time using the HIF_BLOCK_SIZE and is
+ * negotiated with the target during initialization after the
+ * dragon interrupts are enabled.
+ * amode - This indicates if the address has to be incremented on dragon
+ * after every read/write operation (HIF?FIXED_ADDRESS/
+ * HIF_INCREMENTAL_ADDRESS).
+ */
+A_STATUS
+HIFReadWrite(HIF_DEVICE *device,
+ A_UINT32 address,
+ A_UCHAR *buffer,
+ A_UINT32 length,
+ HIF_REQUEST *request,
+ void *context);
+
+#if 0
+Not required since the hif layer can automatically do all this once the device
+is discovered and then report the instance to the HTC layer only after
+everything is successfully finished.
+/*
+ * This is called right after the device insertion event is reported to HTC.
+ * It is expected to perform atleast the following functions:
+ * i) Configure the interface - bus width, clock rate, operational current.
+ * ii) Enable the interrupts and unmask any IRQs.
+ * iii) Enable dragon by writing to the IO Enable bit if the interface supports
+ * one.
+ */
+A_STATUS
+HIFInitializeInterface(void *device);
+#endif
+
+/*
+ * This can be initiated from the unload driver context ie when the HTCShutdown
+ * routine is called.
+ */
+void
+HIFShutDownDevice(HIF_DEVICE *device);
+
+/*
+ * This should translate to an acknowledgment to the bus driver indicating that
+ * the previous interrupt request has been serviced and the all the relevant
+ * sources have been cleared. HTC is ready to process more interrupts.
+ * This should prevent the bus driver from raising an interrupt unless the
+ * previous one has been serviced and acknowledged using the previous API.
+ */
+void
+HIFAckInterrupt(HIF_DEVICE *device);
+
+void
+HIFMaskInterrupt(HIF_DEVICE *device);
+
+void
+HIFUnMaskInterrupt(HIF_DEVICE *device);
+
+A_STATUS
+HIFConfigureDevice(HIF_DEVICE *device, HIF_DEVICE_CONFIG_OPCODE opcode,
+ void *config, A_UINT32 configLen);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HIF_H_ */
Added: developers/nbd/ar6k/include/host_version.h
===================================================================
--- developers/nbd/ar6k/include/host_version.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/host_version.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains version information for the sample host driver for the
+ * AR6000 chip
+ *
+ *
+ */
+
+#ifndef _HOST_VERSION_H_
+#define _HOST_VERSION_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../include/AR6000_version.h"
+
+/*
+ * The version number is made up of major, minor, patch and build
+ * numbers. These are 16 bit numbers. The build and release script will
+ * set the build number using a Perforce counter. Here the build number is
+ * set to 9999 so that builds done without the build-release script are easily
+ * identifiable.
+ */
+
+#define ATH_SW_VER_MAJOR __VER_MAJOR_
+#define ATH_SW_VER_MINOR __VER_MINOR_
+#define ATH_SW_VER_PATCH __VER_PATCH_
+#define ATH_SW_VER_BUILD 2
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HOST_VERSION_H_ */
Added: developers/nbd/ar6k/include/htc.h
===================================================================
--- developers/nbd/ar6k/include/htc.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/htc.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * htc.h - HTC Module specific declarations and prototypes
+ */
+
+#ifndef _HTC_H_
+#define _HTC_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* ------ MBOX ID ------ */
+typedef enum
+{
+ ENDPOINT_UNUSED = -1,
+ ENDPOINT1 = 0,
+ ENDPOINT2,
+ ENDPOINT3,
+ ENDPOINT4,
+} HTC_ENDPOINT_ID;
+
+/* ------ Event Types ------ */
+typedef enum
+{
+ HTC_BUFFER_RECEIVED = 0,
+ HTC_SKB_RECEIVED,
+ HTC_BUFFER_SENT,
+ HTC_SKB_SENT,
+ HTC_DATA_AVAILABLE,
+ HTC_TARGET_AVAILABLE,
+ HTC_TARGET_UNAVAILABLE,
+} HTC_EVENT_ID;
+
+#define HTC_MAILBOX_NUM_MAX 4
+#define HTC_HEADER_LEN 2
+#define HTC_DATA_REQUEST_RING_BUFFER_SIZE 30
+
+/* ------- Target Specific Data structures ------- */
+typedef struct htc_target HTC_TARGET;
+
+/* ------- Event Specific Data Structures ------- */
+typedef struct htc_event_info HTC_EVENT_INFO;
+typedef void (*HTC_EVENT_HANDLER) (HTC_TARGET *,
+ HTC_ENDPOINT_ID,
+ HTC_EVENT_ID,
+ HTC_EVENT_INFO *,
+ void *);
+
+/* WMI layer extracts the relevant information from this data structure */
+struct htc_event_info
+{
+ A_UCHAR *buffer;
+ void *cookie;
+ A_UINT32 bufferLength;
+ A_UINT32 actualLength;
+ A_STATUS status;
+};
+
+/* ------ Function Prototypes ------ */
+A_STATUS
+HTCInit(void);
+
+A_STATUS
+HTCStart(HTC_TARGET *target);
+/* target - IN */
+
+A_STATUS
+HTCEventReg(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ HTC_EVENT_ID eventId, HTC_EVENT_HANDLER eventHandler,
+ void *param);
+/* target - IN, endPointId - IN, eventId - IN, eventHandler - IN, param - IN */
+
+A_STATUS
+HTCBufferReceive(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ A_UCHAR *buffer, A_UINT32 length, void *cookie);
+/* target - IN, endPointId - IN, buffer - IN, length - IN, cookie - IN */
+
+#if 0
+A_STATUS
+HTCSkbReceive(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ struct sk_buff *skb, void *cookie);
+#endif
+
+A_STATUS
+HTCBufferSend(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ A_UCHAR *buffer, A_UINT32 length, void *cookie);
+/* target - IN, endPointId - IN, buffer - IN, length - IN, cookie - IN */
+
+#if 0
+A_STATUS
+HTCSkbSend(HTC_TARGET *target, HTC_ENDPOINT_ID endPointId,
+ struct sk_buff *skb, void *cookie);
+#endif
+
+void
+HTCStop(HTC_TARGET *target);
+/* target - IN */
+
+void
+HTCShutDown(HTC_TARGET *target);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HTC_H_ */
Added: developers/nbd/ar6k/include/hw/mbox_host_reg.h
===================================================================
--- developers/nbd/ar6k/include/hw/mbox_host_reg.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/hw/mbox_host_reg.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+
+#ifndef _MBOX_HOST_REG_REG_H_
+#define _MBOX_HOST_REG_REG_H_
+
+#define HOST_INT_STATUS_ADDRESS 0x00000400
+#define HOST_INT_STATUS_OFFSET 0x00000400
+#define HOST_INT_STATUS_ERROR_MSB 7
+#define HOST_INT_STATUS_ERROR_LSB 7
+#define HOST_INT_STATUS_ERROR_MASK 0x00000080
+#define HOST_INT_STATUS_ERROR_GET(x) (((x) & HOST_INT_STATUS_ERROR_MASK) >> HOST_INT_STATUS_ERROR_LSB)
+#define HOST_INT_STATUS_ERROR_SET(x) (((x) << HOST_INT_STATUS_ERROR_LSB) & HOST_INT_STATUS_ERROR_MASK)
+#define HOST_INT_STATUS_CPU_MSB 6
+#define HOST_INT_STATUS_CPU_LSB 6
+#define HOST_INT_STATUS_CPU_MASK 0x00000040
+#define HOST_INT_STATUS_CPU_GET(x) (((x) & HOST_INT_STATUS_CPU_MASK) >> HOST_INT_STATUS_CPU_LSB)
+#define HOST_INT_STATUS_CPU_SET(x) (((x) << HOST_INT_STATUS_CPU_LSB) & HOST_INT_STATUS_CPU_MASK)
+#define HOST_INT_STATUS_DRAGON_INT_MSB 5
+#define HOST_INT_STATUS_DRAGON_INT_LSB 5
+#define HOST_INT_STATUS_DRAGON_INT_MASK 0x00000020
+#define HOST_INT_STATUS_DRAGON_INT_GET(x) (((x) & HOST_INT_STATUS_DRAGON_INT_MASK) >> HOST_INT_STATUS_DRAGON_INT_LSB)
+#define HOST_INT_STATUS_DRAGON_INT_SET(x) (((x) << HOST_INT_STATUS_DRAGON_INT_LSB) & HOST_INT_STATUS_DRAGON_INT_MASK)
+#define HOST_INT_STATUS_COUNTER_MSB 4
+#define HOST_INT_STATUS_COUNTER_LSB 4
+#define HOST_INT_STATUS_COUNTER_MASK 0x00000010
+#define HOST_INT_STATUS_COUNTER_GET(x) (((x) & HOST_INT_STATUS_COUNTER_MASK) >> HOST_INT_STATUS_COUNTER_LSB)
+#define HOST_INT_STATUS_COUNTER_SET(x) (((x) << HOST_INT_STATUS_COUNTER_LSB) & HOST_INT_STATUS_COUNTER_MASK)
+#define HOST_INT_STATUS_MBOX_DATA_MSB 3
+#define HOST_INT_STATUS_MBOX_DATA_LSB 0
+#define HOST_INT_STATUS_MBOX_DATA_MASK 0x0000000f
+#define HOST_INT_STATUS_MBOX_DATA_GET(x) (((x) & HOST_INT_STATUS_MBOX_DATA_MASK) >> HOST_INT_STATUS_MBOX_DATA_LSB)
+#define HOST_INT_STATUS_MBOX_DATA_SET(x) (((x) << HOST_INT_STATUS_MBOX_DATA_LSB) & HOST_INT_STATUS_MBOX_DATA_MASK)
+
+#define CPU_INT_STATUS_ADDRESS 0x00000401
+#define CPU_INT_STATUS_OFFSET 0x00000401
+#define CPU_INT_STATUS_BIT_MSB 7
+#define CPU_INT_STATUS_BIT_LSB 0
+#define CPU_INT_STATUS_BIT_MASK 0x000000ff
+#define CPU_INT_STATUS_BIT_GET(x) (((x) & CPU_INT_STATUS_BIT_MASK) >> CPU_INT_STATUS_BIT_LSB)
+#define CPU_INT_STATUS_BIT_SET(x) (((x) << CPU_INT_STATUS_BIT_LSB) & CPU_INT_STATUS_BIT_MASK)
+
+#define ERROR_INT_STATUS_ADDRESS 0x00000402
+#define ERROR_INT_STATUS_OFFSET 0x00000402
+#define ERROR_INT_STATUS_SPI_MSB 3
+#define ERROR_INT_STATUS_SPI_LSB 3
+#define ERROR_INT_STATUS_SPI_MASK 0x00000008
+#define ERROR_INT_STATUS_SPI_GET(x) (((x) & ERROR_INT_STATUS_SPI_MASK) >> ERROR_INT_STATUS_SPI_LSB)
+#define ERROR_INT_STATUS_SPI_SET(x) (((x) << ERROR_INT_STATUS_SPI_LSB) & ERROR_INT_STATUS_SPI_MASK)
+#define ERROR_INT_STATUS_WAKEUP_MSB 2
+#define ERROR_INT_STATUS_WAKEUP_LSB 2
+#define ERROR_INT_STATUS_WAKEUP_MASK 0x00000004
+#define ERROR_INT_STATUS_WAKEUP_GET(x) (((x) & ERROR_INT_STATUS_WAKEUP_MASK) >> ERROR_INT_STATUS_WAKEUP_LSB)
+#define ERROR_INT_STATUS_WAKEUP_SET(x) (((x) << ERROR_INT_STATUS_WAKEUP_LSB) & ERROR_INT_STATUS_WAKEUP_MASK)
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MSB 1
+#define ERROR_INT_STATUS_RX_UNDERFLOW_LSB 1
+#define ERROR_INT_STATUS_RX_UNDERFLOW_MASK 0x00000002
+#define ERROR_INT_STATUS_RX_UNDERFLOW_GET(x) (((x) & ERROR_INT_STATUS_RX_UNDERFLOW_MASK) >> ERROR_INT_STATUS_RX_UNDERFLOW_LSB)
+#define ERROR_INT_STATUS_RX_UNDERFLOW_SET(x) (((x) << ERROR_INT_STATUS_RX_UNDERFLOW_LSB) & ERROR_INT_STATUS_RX_UNDERFLOW_MASK)
+#define ERROR_INT_STATUS_TX_OVERFLOW_MSB 0
+#define ERROR_INT_STATUS_TX_OVERFLOW_LSB 0
+#define ERROR_INT_STATUS_TX_OVERFLOW_MASK 0x00000001
+#define ERROR_INT_STATUS_TX_OVERFLOW_GET(x) (((x) & ERROR_INT_STATUS_TX_OVERFLOW_MASK) >> ERROR_INT_STATUS_TX_OVERFLOW_LSB)
+#define ERROR_INT_STATUS_TX_OVERFLOW_SET(x) (((x) << ERROR_INT_STATUS_TX_OVERFLOW_LSB) & ERROR_INT_STATUS_TX_OVERFLOW_MASK)
+
+#define COUNTER_INT_STATUS_ADDRESS 0x00000403
+#define COUNTER_INT_STATUS_OFFSET 0x00000403
+#define COUNTER_INT_STATUS_COUNTER_MSB 7
+#define COUNTER_INT_STATUS_COUNTER_LSB 0
+#define COUNTER_INT_STATUS_COUNTER_MASK 0x000000ff
+#define COUNTER_INT_STATUS_COUNTER_GET(x) (((x) & COUNTER_INT_STATUS_COUNTER_MASK) >> COUNTER_INT_STATUS_COUNTER_LSB)
+#define COUNTER_INT_STATUS_COUNTER_SET(x) (((x) << COUNTER_INT_STATUS_COUNTER_LSB) & COUNTER_INT_STATUS_COUNTER_MASK)
+
+#define MBOX_FRAME_ADDRESS 0x00000404
+#define MBOX_FRAME_OFFSET 0x00000404
+#define MBOX_FRAME_RX_EOM_MSB 7
+#define MBOX_FRAME_RX_EOM_LSB 4
+#define MBOX_FRAME_RX_EOM_MASK 0x000000f0
+#define MBOX_FRAME_RX_EOM_GET(x) (((x) & MBOX_FRAME_RX_EOM_MASK) >> MBOX_FRAME_RX_EOM_LSB)
+#define MBOX_FRAME_RX_EOM_SET(x) (((x) << MBOX_FRAME_RX_EOM_LSB) & MBOX_FRAME_RX_EOM_MASK)
+#define MBOX_FRAME_RX_SOM_MSB 3
+#define MBOX_FRAME_RX_SOM_LSB 0
+#define MBOX_FRAME_RX_SOM_MASK 0x0000000f
+#define MBOX_FRAME_RX_SOM_GET(x) (((x) & MBOX_FRAME_RX_SOM_MASK) >> MBOX_FRAME_RX_SOM_LSB)
+#define MBOX_FRAME_RX_SOM_SET(x) (((x) << MBOX_FRAME_RX_SOM_LSB) & MBOX_FRAME_RX_SOM_MASK)
+
+#define RX_LOOKAHEAD_VALID_ADDRESS 0x00000405
+#define RX_LOOKAHEAD_VALID_OFFSET 0x00000405
+#define RX_LOOKAHEAD_VALID_MBOX_MSB 3
+#define RX_LOOKAHEAD_VALID_MBOX_LSB 0
+#define RX_LOOKAHEAD_VALID_MBOX_MASK 0x0000000f
+#define RX_LOOKAHEAD_VALID_MBOX_GET(x) (((x) & RX_LOOKAHEAD_VALID_MBOX_MASK) >> RX_LOOKAHEAD_VALID_MBOX_LSB)
+#define RX_LOOKAHEAD_VALID_MBOX_SET(x) (((x) << RX_LOOKAHEAD_VALID_MBOX_LSB) & RX_LOOKAHEAD_VALID_MBOX_MASK)
+
+#define RX_LOOKAHEAD0_ADDRESS 0x00000408
+#define RX_LOOKAHEAD0_OFFSET 0x00000408
+#define RX_LOOKAHEAD0_DATA_MSB 7
+#define RX_LOOKAHEAD0_DATA_LSB 0
+#define RX_LOOKAHEAD0_DATA_MASK 0x000000ff
+#define RX_LOOKAHEAD0_DATA_GET(x) (((x) & RX_LOOKAHEAD0_DATA_MASK) >> RX_LOOKAHEAD0_DATA_LSB)
+#define RX_LOOKAHEAD0_DATA_SET(x) (((x) << RX_LOOKAHEAD0_DATA_LSB) & RX_LOOKAHEAD0_DATA_MASK)
+
+#define RX_LOOKAHEAD1_ADDRESS 0x0000040c
+#define RX_LOOKAHEAD1_OFFSET 0x0000040c
+#define RX_LOOKAHEAD1_DATA_MSB 7
+#define RX_LOOKAHEAD1_DATA_LSB 0
+#define RX_LOOKAHEAD1_DATA_MASK 0x000000ff
+#define RX_LOOKAHEAD1_DATA_GET(x) (((x) & RX_LOOKAHEAD1_DATA_MASK) >> RX_LOOKAHEAD1_DATA_LSB)
+#define RX_LOOKAHEAD1_DATA_SET(x) (((x) << RX_LOOKAHEAD1_DATA_LSB) & RX_LOOKAHEAD1_DATA_MASK)
+
+#define RX_LOOKAHEAD2_ADDRESS 0x00000410
+#define RX_LOOKAHEAD2_OFFSET 0x00000410
+#define RX_LOOKAHEAD2_DATA_MSB 7
+#define RX_LOOKAHEAD2_DATA_LSB 0
+#define RX_LOOKAHEAD2_DATA_MASK 0x000000ff
+#define RX_LOOKAHEAD2_DATA_GET(x) (((x) & RX_LOOKAHEAD2_DATA_MASK) >> RX_LOOKAHEAD2_DATA_LSB)
+#define RX_LOOKAHEAD2_DATA_SET(x) (((x) << RX_LOOKAHEAD2_DATA_LSB) & RX_LOOKAHEAD2_DATA_MASK)
+
+#define RX_LOOKAHEAD3_ADDRESS 0x00000414
+#define RX_LOOKAHEAD3_OFFSET 0x00000414
+#define RX_LOOKAHEAD3_DATA_MSB 7
+#define RX_LOOKAHEAD3_DATA_LSB 0
+#define RX_LOOKAHEAD3_DATA_MASK 0x000000ff
+#define RX_LOOKAHEAD3_DATA_GET(x) (((x) & RX_LOOKAHEAD3_DATA_MASK) >> RX_LOOKAHEAD3_DATA_LSB)
+#define RX_LOOKAHEAD3_DATA_SET(x) (((x) << RX_LOOKAHEAD3_DATA_LSB) & RX_LOOKAHEAD3_DATA_MASK)
+
+#define INT_STATUS_ENABLE_ADDRESS 0x00000418
+#define INT_STATUS_ENABLE_OFFSET 0x00000418
+#define INT_STATUS_ENABLE_ERROR_MSB 7
+#define INT_STATUS_ENABLE_ERROR_LSB 7
+#define INT_STATUS_ENABLE_ERROR_MASK 0x00000080
+#define INT_STATUS_ENABLE_ERROR_GET(x) (((x) & INT_STATUS_ENABLE_ERROR_MASK) >> INT_STATUS_ENABLE_ERROR_LSB)
+#define INT_STATUS_ENABLE_ERROR_SET(x) (((x) << INT_STATUS_ENABLE_ERROR_LSB) & INT_STATUS_ENABLE_ERROR_MASK)
+#define INT_STATUS_ENABLE_CPU_MSB 6
+#define INT_STATUS_ENABLE_CPU_LSB 6
+#define INT_STATUS_ENABLE_CPU_MASK 0x00000040
+#define INT_STATUS_ENABLE_CPU_GET(x) (((x) & INT_STATUS_ENABLE_CPU_MASK) >> INT_STATUS_ENABLE_CPU_LSB)
+#define INT_STATUS_ENABLE_CPU_SET(x) (((x) << INT_STATUS_ENABLE_CPU_LSB) & INT_STATUS_ENABLE_CPU_MASK)
+#define INT_STATUS_ENABLE_DRAGON_INT_MSB 5
+#define INT_STATUS_ENABLE_DRAGON_INT_LSB 5
+#define INT_STATUS_ENABLE_DRAGON_INT_MASK 0x00000020
+#define INT_STATUS_ENABLE_DRAGON_INT_GET(x) (((x) & INT_STATUS_ENABLE_DRAGON_INT_MASK) >> INT_STATUS_ENABLE_DRAGON_INT_LSB)
+#define INT_STATUS_ENABLE_DRAGON_INT_SET(x) (((x) << INT_STATUS_ENABLE_DRAGON_INT_LSB) & INT_STATUS_ENABLE_DRAGON_INT_MASK)
+#define INT_STATUS_ENABLE_COUNTER_MSB 4
+#define INT_STATUS_ENABLE_COUNTER_LSB 4
+#define INT_STATUS_ENABLE_COUNTER_MASK 0x00000010
+#define INT_STATUS_ENABLE_COUNTER_GET(x) (((x) & INT_STATUS_ENABLE_COUNTER_MASK) >> INT_STATUS_ENABLE_COUNTER_LSB)
+#define INT_STATUS_ENABLE_COUNTER_SET(x) (((x) << INT_STATUS_ENABLE_COUNTER_LSB) & INT_STATUS_ENABLE_COUNTER_MASK)
+#define INT_STATUS_ENABLE_MBOX_DATA_MSB 3
+#define INT_STATUS_ENABLE_MBOX_DATA_LSB 0
+#define INT_STATUS_ENABLE_MBOX_DATA_MASK 0x0000000f
+#define INT_STATUS_ENABLE_MBOX_DATA_GET(x) (((x) & INT_STATUS_ENABLE_MBOX_DATA_MASK) >> INT_STATUS_ENABLE_MBOX_DATA_LSB)
+#define INT_STATUS_ENABLE_MBOX_DATA_SET(x) (((x) << INT_STATUS_ENABLE_MBOX_DATA_LSB) & INT_STATUS_ENABLE_MBOX_DATA_MASK)
+
+#define CPU_INT_STATUS_ENABLE_ADDRESS 0x00000419
+#define CPU_INT_STATUS_ENABLE_OFFSET 0x00000419
+#define CPU_INT_STATUS_ENABLE_BIT_MSB 7
+#define CPU_INT_STATUS_ENABLE_BIT_LSB 0
+#define CPU_INT_STATUS_ENABLE_BIT_MASK 0x000000ff
+#define CPU_INT_STATUS_ENABLE_BIT_GET(x) (((x) & CPU_INT_STATUS_ENABLE_BIT_MASK) >> CPU_INT_STATUS_ENABLE_BIT_LSB)
+#define CPU_INT_STATUS_ENABLE_BIT_SET(x) (((x) << CPU_INT_STATUS_ENABLE_BIT_LSB) & CPU_INT_STATUS_ENABLE_BIT_MASK)
+
+#define ERROR_STATUS_ENABLE_ADDRESS 0x0000041a
+#define ERROR_STATUS_ENABLE_OFFSET 0x0000041a
+#define ERROR_STATUS_ENABLE_WAKEUP_MSB 2
+#define ERROR_STATUS_ENABLE_WAKEUP_LSB 2
+#define ERROR_STATUS_ENABLE_WAKEUP_MASK 0x00000004
+#define ERROR_STATUS_ENABLE_WAKEUP_GET(x) (((x) & ERROR_STATUS_ENABLE_WAKEUP_MASK) >> ERROR_STATUS_ENABLE_WAKEUP_LSB)
+#define ERROR_STATUS_ENABLE_WAKEUP_SET(x) (((x) << ERROR_STATUS_ENABLE_WAKEUP_LSB) & ERROR_STATUS_ENABLE_WAKEUP_MASK)
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MSB 1
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB 1
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK 0x00000002
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_GET(x) (((x) & ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK) >> ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB)
+#define ERROR_STATUS_ENABLE_RX_UNDERFLOW_SET(x) (((x) << ERROR_STATUS_ENABLE_RX_UNDERFLOW_LSB) & ERROR_STATUS_ENABLE_RX_UNDERFLOW_MASK)
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MSB 0
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB 0
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK 0x00000001
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_GET(x) (((x) & ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK) >> ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB)
+#define ERROR_STATUS_ENABLE_TX_OVERFLOW_SET(x) (((x) << ERROR_STATUS_ENABLE_TX_OVERFLOW_LSB) & ERROR_STATUS_ENABLE_TX_OVERFLOW_MASK)
+
+#define COUNTER_INT_STATUS_ENABLE_ADDRESS 0x0000041b
+#define COUNTER_INT_STATUS_ENABLE_OFFSET 0x0000041b
+#define COUNTER_INT_STATUS_ENABLE_BIT_MSB 7
+#define COUNTER_INT_STATUS_ENABLE_BIT_LSB 0
+#define COUNTER_INT_STATUS_ENABLE_BIT_MASK 0x000000ff
+#define COUNTER_INT_STATUS_ENABLE_BIT_GET(x) (((x) & COUNTER_INT_STATUS_ENABLE_BIT_MASK) >> COUNTER_INT_STATUS_ENABLE_BIT_LSB)
+#define COUNTER_INT_STATUS_ENABLE_BIT_SET(x) (((x) << COUNTER_INT_STATUS_ENABLE_BIT_LSB) & COUNTER_INT_STATUS_ENABLE_BIT_MASK)
+
+#define COUNT_ADDRESS 0x00000420
+#define COUNT_OFFSET 0x00000420
+#define COUNT_VALUE_MSB 7
+#define COUNT_VALUE_LSB 0
+#define COUNT_VALUE_MASK 0x000000ff
+#define COUNT_VALUE_GET(x) (((x) & COUNT_VALUE_MASK) >> COUNT_VALUE_LSB)
+#define COUNT_VALUE_SET(x) (((x) << COUNT_VALUE_LSB) & COUNT_VALUE_MASK)
+
+#define COUNT_DEC_ADDRESS 0x00000440
+#define COUNT_DEC_OFFSET 0x00000440
+#define COUNT_DEC_VALUE_MSB 7
+#define COUNT_DEC_VALUE_LSB 0
+#define COUNT_DEC_VALUE_MASK 0x000000ff
+#define COUNT_DEC_VALUE_GET(x) (((x) & COUNT_DEC_VALUE_MASK) >> COUNT_DEC_VALUE_LSB)
+#define COUNT_DEC_VALUE_SET(x) (((x) << COUNT_DEC_VALUE_LSB) & COUNT_DEC_VALUE_MASK)
+
+#define SCRATCH_ADDRESS 0x00000460
+#define SCRATCH_OFFSET 0x00000460
+#define SCRATCH_VALUE_MSB 7
+#define SCRATCH_VALUE_LSB 0
+#define SCRATCH_VALUE_MASK 0x000000ff
+#define SCRATCH_VALUE_GET(x) (((x) & SCRATCH_VALUE_MASK) >> SCRATCH_VALUE_LSB)
+#define SCRATCH_VALUE_SET(x) (((x) << SCRATCH_VALUE_LSB) & SCRATCH_VALUE_MASK)
+
+#define FIFO_TIMEOUT_ADDRESS 0x00000468
+#define FIFO_TIMEOUT_OFFSET 0x00000468
+#define FIFO_TIMEOUT_VALUE_MSB 7
+#define FIFO_TIMEOUT_VALUE_LSB 0
+#define FIFO_TIMEOUT_VALUE_MASK 0x000000ff
+#define FIFO_TIMEOUT_VALUE_GET(x) (((x) & FIFO_TIMEOUT_VALUE_MASK) >> FIFO_TIMEOUT_VALUE_LSB)
+#define FIFO_TIMEOUT_VALUE_SET(x) (((x) << FIFO_TIMEOUT_VALUE_LSB) & FIFO_TIMEOUT_VALUE_MASK)
+
+#define FIFO_TIMEOUT_ENABLE_ADDRESS 0x00000469
+#define FIFO_TIMEOUT_ENABLE_OFFSET 0x00000469
+#define FIFO_TIMEOUT_ENABLE_SET_MSB 0
+#define FIFO_TIMEOUT_ENABLE_SET_LSB 0
+#define FIFO_TIMEOUT_ENABLE_SET_MASK 0x00000001
+#define FIFO_TIMEOUT_ENABLE_SET_GET(x) (((x) & FIFO_TIMEOUT_ENABLE_SET_MASK) >> FIFO_TIMEOUT_ENABLE_SET_LSB)
+#define FIFO_TIMEOUT_ENABLE_SET_SET(x) (((x) << FIFO_TIMEOUT_ENABLE_SET_LSB) & FIFO_TIMEOUT_ENABLE_SET_MASK)
+
+#define DISABLE_SLEEP_ADDRESS 0x0000046a
+#define DISABLE_SLEEP_OFFSET 0x0000046a
+#define DISABLE_SLEEP_FOR_INT_MSB 1
+#define DISABLE_SLEEP_FOR_INT_LSB 1
+#define DISABLE_SLEEP_FOR_INT_MASK 0x00000002
+#define DISABLE_SLEEP_FOR_INT_GET(x) (((x) & DISABLE_SLEEP_FOR_INT_MASK) >> DISABLE_SLEEP_FOR_INT_LSB)
+#define DISABLE_SLEEP_FOR_INT_SET(x) (((x) << DISABLE_SLEEP_FOR_INT_LSB) & DISABLE_SLEEP_FOR_INT_MASK)
+#define DISABLE_SLEEP_ON_MSB 0
+#define DISABLE_SLEEP_ON_LSB 0
+#define DISABLE_SLEEP_ON_MASK 0x00000001
+#define DISABLE_SLEEP_ON_GET(x) (((x) & DISABLE_SLEEP_ON_MASK) >> DISABLE_SLEEP_ON_LSB)
+#define DISABLE_SLEEP_ON_SET(x) (((x) << DISABLE_SLEEP_ON_LSB) & DISABLE_SLEEP_ON_MASK)
+
+#define LOCAL_BUS_ENDIAN_ADDRESS 0x0000046e
+#define LOCAL_BUS_ENDIAN_OFFSET 0x0000046e
+#define LOCAL_BUS_ENDIAN_BIG_MSB 0
+#define LOCAL_BUS_ENDIAN_BIG_LSB 0
+#define LOCAL_BUS_ENDIAN_BIG_MASK 0x00000001
+#define LOCAL_BUS_ENDIAN_BIG_GET(x) (((x) & LOCAL_BUS_ENDIAN_BIG_MASK) >> LOCAL_BUS_ENDIAN_BIG_LSB)
+#define LOCAL_BUS_ENDIAN_BIG_SET(x) (((x) << LOCAL_BUS_ENDIAN_BIG_LSB) & LOCAL_BUS_ENDIAN_BIG_MASK)
+
+#define LOCAL_BUS_ADDRESS 0x00000470
+#define LOCAL_BUS_OFFSET 0x00000470
+#define LOCAL_BUS_SOFT_RESET_MSB 4
+#define LOCAL_BUS_SOFT_RESET_LSB 4
+#define LOCAL_BUS_SOFT_RESET_MASK 0x00000010
+#define LOCAL_BUS_SOFT_RESET_GET(x) (((x) & LOCAL_BUS_SOFT_RESET_MASK) >> LOCAL_BUS_SOFT_RESET_LSB)
+#define LOCAL_BUS_SOFT_RESET_SET(x) (((x) << LOCAL_BUS_SOFT_RESET_LSB) & LOCAL_BUS_SOFT_RESET_MASK)
+#define LOCAL_BUS_IO_ENABLE_MSB 3
+#define LOCAL_BUS_IO_ENABLE_LSB 3
+#define LOCAL_BUS_IO_ENABLE_MASK 0x00000008
+#define LOCAL_BUS_IO_ENABLE_GET(x) (((x) & LOCAL_BUS_IO_ENABLE_MASK) >> LOCAL_BUS_IO_ENABLE_LSB)
+#define LOCAL_BUS_IO_ENABLE_SET(x) (((x) << LOCAL_BUS_IO_ENABLE_LSB) & LOCAL_BUS_IO_ENABLE_MASK)
+#define LOCAL_BUS_KEEP_AWAKE_MSB 2
+#define LOCAL_BUS_KEEP_AWAKE_LSB 2
+#define LOCAL_BUS_KEEP_AWAKE_MASK 0x00000004
+#define LOCAL_BUS_KEEP_AWAKE_GET(x) (((x) & LOCAL_BUS_KEEP_AWAKE_MASK) >> LOCAL_BUS_KEEP_AWAKE_LSB)
+#define LOCAL_BUS_KEEP_AWAKE_SET(x) (((x) << LOCAL_BUS_KEEP_AWAKE_LSB) & LOCAL_BUS_KEEP_AWAKE_MASK)
+#define LOCAL_BUS_STATE_MSB 1
+#define LOCAL_BUS_STATE_LSB 0
+#define LOCAL_BUS_STATE_MASK 0x00000003
+#define LOCAL_BUS_STATE_GET(x) (((x) & LOCAL_BUS_STATE_MASK) >> LOCAL_BUS_STATE_LSB)
+#define LOCAL_BUS_STATE_SET(x) (((x) << LOCAL_BUS_STATE_LSB) & LOCAL_BUS_STATE_MASK)
+
+#define INT_WLAN_ADDRESS 0x00000472
+#define INT_WLAN_OFFSET 0x00000472
+#define INT_WLAN_VECTOR_MSB 7
+#define INT_WLAN_VECTOR_LSB 0
+#define INT_WLAN_VECTOR_MASK 0x000000ff
+#define INT_WLAN_VECTOR_GET(x) (((x) & INT_WLAN_VECTOR_MASK) >> INT_WLAN_VECTOR_LSB)
+#define INT_WLAN_VECTOR_SET(x) (((x) << INT_WLAN_VECTOR_LSB) & INT_WLAN_VECTOR_MASK)
+
+#define WINDOW_DATA_ADDRESS 0x00000474
+#define WINDOW_DATA_OFFSET 0x00000474
+#define WINDOW_DATA_DATA_MSB 7
+#define WINDOW_DATA_DATA_LSB 0
+#define WINDOW_DATA_DATA_MASK 0x000000ff
+#define WINDOW_DATA_DATA_GET(x) (((x) & WINDOW_DATA_DATA_MASK) >> WINDOW_DATA_DATA_LSB)
+#define WINDOW_DATA_DATA_SET(x) (((x) << WINDOW_DATA_DATA_LSB) & WINDOW_DATA_DATA_MASK)
+
+#define WINDOW_WRITE_ADDR_ADDRESS 0x00000478
+#define WINDOW_WRITE_ADDR_OFFSET 0x00000478
+#define WINDOW_WRITE_ADDR_ADDR_MSB 7
+#define WINDOW_WRITE_ADDR_ADDR_LSB 0
+#define WINDOW_WRITE_ADDR_ADDR_MASK 0x000000ff
+#define WINDOW_WRITE_ADDR_ADDR_GET(x) (((x) & WINDOW_WRITE_ADDR_ADDR_MASK) >> WINDOW_WRITE_ADDR_ADDR_LSB)
+#define WINDOW_WRITE_ADDR_ADDR_SET(x) (((x) << WINDOW_WRITE_ADDR_ADDR_LSB) & WINDOW_WRITE_ADDR_ADDR_MASK)
+
+#define WINDOW_READ_ADDR_ADDRESS 0x0000047c
+#define WINDOW_READ_ADDR_OFFSET 0x0000047c
+#define WINDOW_READ_ADDR_ADDR_MSB 7
+#define WINDOW_READ_ADDR_ADDR_LSB 0
+#define WINDOW_READ_ADDR_ADDR_MASK 0x000000ff
+#define WINDOW_READ_ADDR_ADDR_GET(x) (((x) & WINDOW_READ_ADDR_ADDR_MASK) >> WINDOW_READ_ADDR_ADDR_LSB)
+#define WINDOW_READ_ADDR_ADDR_SET(x) (((x) << WINDOW_READ_ADDR_ADDR_LSB) & WINDOW_READ_ADDR_ADDR_MASK)
+
+#define SPI_CONFIG_ADDRESS 0x00000480
+#define SPI_CONFIG_OFFSET 0x00000480
+#define SPI_CONFIG_SPI_RESET_MSB 4
+#define SPI_CONFIG_SPI_RESET_LSB 4
+#define SPI_CONFIG_SPI_RESET_MASK 0x00000010
+#define SPI_CONFIG_SPI_RESET_GET(x) (((x) & SPI_CONFIG_SPI_RESET_MASK) >> SPI_CONFIG_SPI_RESET_LSB)
+#define SPI_CONFIG_SPI_RESET_SET(x) (((x) << SPI_CONFIG_SPI_RESET_LSB) & SPI_CONFIG_SPI_RESET_MASK)
+#define SPI_CONFIG_INTERRUPT_ENABLE_MSB 3
+#define SPI_CONFIG_INTERRUPT_ENABLE_LSB 3
+#define SPI_CONFIG_INTERRUPT_ENABLE_MASK 0x00000008
+#define SPI_CONFIG_INTERRUPT_ENABLE_GET(x) (((x) & SPI_CONFIG_INTERRUPT_ENABLE_MASK) >> SPI_CONFIG_INTERRUPT_ENABLE_LSB)
+#define SPI_CONFIG_INTERRUPT_ENABLE_SET(x) (((x) << SPI_CONFIG_INTERRUPT_ENABLE_LSB) & SPI_CONFIG_INTERRUPT_ENABLE_MASK)
+#define SPI_CONFIG_TEST_MODE_MSB 2
+#define SPI_CONFIG_TEST_MODE_LSB 2
+#define SPI_CONFIG_TEST_MODE_MASK 0x00000004
+#define SPI_CONFIG_TEST_MODE_GET(x) (((x) & SPI_CONFIG_TEST_MODE_MASK) >> SPI_CONFIG_TEST_MODE_LSB)
+#define SPI_CONFIG_TEST_MODE_SET(x) (((x) << SPI_CONFIG_TEST_MODE_LSB) & SPI_CONFIG_TEST_MODE_MASK)
+#define SPI_CONFIG_DATA_SIZE_MSB 1
+#define SPI_CONFIG_DATA_SIZE_LSB 0
+#define SPI_CONFIG_DATA_SIZE_MASK 0x00000003
+#define SPI_CONFIG_DATA_SIZE_GET(x) (((x) & SPI_CONFIG_DATA_SIZE_MASK) >> SPI_CONFIG_DATA_SIZE_LSB)
+#define SPI_CONFIG_DATA_SIZE_SET(x) (((x) << SPI_CONFIG_DATA_SIZE_LSB) & SPI_CONFIG_DATA_SIZE_MASK)
+
+#define SPI_STATUS_ADDRESS 0x00000481
+#define SPI_STATUS_OFFSET 0x00000481
+#define SPI_STATUS_ADDR_ERR_MSB 3
+#define SPI_STATUS_ADDR_ERR_LSB 3
+#define SPI_STATUS_ADDR_ERR_MASK 0x00000008
+#define SPI_STATUS_ADDR_ERR_GET(x) (((x) & SPI_STATUS_ADDR_ERR_MASK) >> SPI_STATUS_ADDR_ERR_LSB)
+#define SPI_STATUS_ADDR_ERR_SET(x) (((x) << SPI_STATUS_ADDR_ERR_LSB) & SPI_STATUS_ADDR_ERR_MASK)
+#define SPI_STATUS_RD_ERR_MSB 2
+#define SPI_STATUS_RD_ERR_LSB 2
+#define SPI_STATUS_RD_ERR_MASK 0x00000004
+#define SPI_STATUS_RD_ERR_GET(x) (((x) & SPI_STATUS_RD_ERR_MASK) >> SPI_STATUS_RD_ERR_LSB)
+#define SPI_STATUS_RD_ERR_SET(x) (((x) << SPI_STATUS_RD_ERR_LSB) & SPI_STATUS_RD_ERR_MASK)
+#define SPI_STATUS_WR_ERR_MSB 1
+#define SPI_STATUS_WR_ERR_LSB 1
+#define SPI_STATUS_WR_ERR_MASK 0x00000002
+#define SPI_STATUS_WR_ERR_GET(x) (((x) & SPI_STATUS_WR_ERR_MASK) >> SPI_STATUS_WR_ERR_LSB)
+#define SPI_STATUS_WR_ERR_SET(x) (((x) << SPI_STATUS_WR_ERR_LSB) & SPI_STATUS_WR_ERR_MASK)
+#define SPI_STATUS_READY_MSB 0
+#define SPI_STATUS_READY_LSB 0
+#define SPI_STATUS_READY_MASK 0x00000001
+#define SPI_STATUS_READY_GET(x) (((x) & SPI_STATUS_READY_MASK) >> SPI_STATUS_READY_LSB)
+#define SPI_STATUS_READY_SET(x) (((x) << SPI_STATUS_READY_LSB) & SPI_STATUS_READY_MASK)
+
+#define CIS_WINDOW_ADDRESS 0x00000600
+#define CIS_WINDOW_OFFSET 0x00000600
+#define CIS_WINDOW_DATA_MSB 7
+#define CIS_WINDOW_DATA_LSB 0
+#define CIS_WINDOW_DATA_MASK 0x000000ff
+#define CIS_WINDOW_DATA_GET(x) (((x) & CIS_WINDOW_DATA_MASK) >> CIS_WINDOW_DATA_LSB)
+#define CIS_WINDOW_DATA_SET(x) (((x) << CIS_WINDOW_DATA_LSB) & CIS_WINDOW_DATA_MASK)
+
+#ifndef __ASSEMBLER__
+typedef struct mbox_host_reg_reg_s {
+ unsigned char pad0[1024]; /* pad to 0x400 */
+ volatile unsigned char host_int_status;
+ volatile unsigned char cpu_int_status;
+ volatile unsigned char error_int_status;
+ volatile unsigned char counter_int_status;
+ volatile unsigned char mbox_frame;
+ volatile unsigned char rx_lookahead_valid;
+ unsigned char pad1[2]; /* pad to 0x408 */
+ volatile unsigned char rx_lookahead0[4];
+ volatile unsigned char rx_lookahead1[4];
+ volatile unsigned char rx_lookahead2[4];
+ volatile unsigned char rx_lookahead3[4];
+ volatile unsigned char int_status_enable;
+ volatile unsigned char cpu_int_status_enable;
+ volatile unsigned char error_status_enable;
+ volatile unsigned char counter_int_status_enable;
+ unsigned char pad2[4]; /* pad to 0x420 */
+ volatile unsigned char count[8];
+ unsigned char pad3[24]; /* pad to 0x440 */
+ volatile unsigned char count_dec[32];
+ volatile unsigned char scratch[8];
+ volatile unsigned char fifo_timeout;
+ volatile unsigned char fifo_timeout_enable;
+ volatile unsigned char disable_sleep;
+ unsigned char pad4[3]; /* pad to 0x46e */
+ volatile unsigned char local_bus_endian;
+ unsigned char pad5[1]; /* pad to 0x470 */
+ volatile unsigned char local_bus;
+ unsigned char pad6[1]; /* pad to 0x472 */
+ volatile unsigned char int_wlan;
+ unsigned char pad7[1]; /* pad to 0x474 */
+ volatile unsigned char window_data[4];
+ volatile unsigned char window_write_addr[4];
+ volatile unsigned char window_read_addr[4];
+ volatile unsigned char spi_config;
+ volatile unsigned char spi_status;
+ unsigned char pad8[382]; /* pad to 0x600 */
+ volatile unsigned char cis_window[512];
+} mbox_host_reg_reg_t;
+#endif /* __ASSEMBLER__ */
+
+#endif /* _MBOX_HOST_REG_H_ */
Added: developers/nbd/ar6k/include/hw/mbox_reg.h
===================================================================
--- developers/nbd/ar6k/include/hw/mbox_reg.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/hw/mbox_reg.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,550 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+#ifndef _MBOX_REG_H_
+#define _MBOX_REG_H_
+
+#define MBOX_FIFO_ADDRESS 0x0c014000
+#define MBOX_FIFO_OFFSET 0x00000000
+#define MBOX_FIFO_DATA_MSB 19
+#define MBOX_FIFO_DATA_LSB 0
+#define MBOX_FIFO_DATA_MASK 0x000fffff
+#define MBOX_FIFO_DATA_GET(x) (((x) & MBOX_FIFO_DATA_MASK) >> MBOX_FIFO_DATA_LSB)
+#define MBOX_FIFO_DATA_SET(x) (((x) << MBOX_FIFO_DATA_LSB) & MBOX_FIFO_DATA_MASK)
+
+#define MBOX_FIFO_STATUS_ADDRESS 0x0c014010
+#define MBOX_FIFO_STATUS_OFFSET 0x00000010
+#define MBOX_FIFO_STATUS_EMPTY_MSB 19
+#define MBOX_FIFO_STATUS_EMPTY_LSB 16
+#define MBOX_FIFO_STATUS_EMPTY_MASK 0x000f0000
+#define MBOX_FIFO_STATUS_EMPTY_GET(x) (((x) & MBOX_FIFO_STATUS_EMPTY_MASK) >> MBOX_FIFO_STATUS_EMPTY_LSB)
+#define MBOX_FIFO_STATUS_EMPTY_SET(x) (((x) << MBOX_FIFO_STATUS_EMPTY_LSB) & MBOX_FIFO_STATUS_EMPTY_MASK)
+#define MBOX_FIFO_STATUS_FULL_MSB 15
+#define MBOX_FIFO_STATUS_FULL_LSB 12
+#define MBOX_FIFO_STATUS_FULL_MASK 0x0000f000
+#define MBOX_FIFO_STATUS_FULL_GET(x) (((x) & MBOX_FIFO_STATUS_FULL_MASK) >> MBOX_FIFO_STATUS_FULL_LSB)
+#define MBOX_FIFO_STATUS_FULL_SET(x) (((x) << MBOX_FIFO_STATUS_FULL_LSB) & MBOX_FIFO_STATUS_FULL_MASK)
+
+#define MBOX_DMA_POLICY_ADDRESS 0x0c014014
+#define MBOX_DMA_POLICY_OFFSET 0x00000014
+#define MBOX_DMA_POLICY_TX_QUANTUM_MSB 3
+#define MBOX_DMA_POLICY_TX_QUANTUM_LSB 3
+#define MBOX_DMA_POLICY_TX_QUANTUM_MASK 0x00000008
+#define MBOX_DMA_POLICY_TX_QUANTUM_GET(x) (((x) & MBOX_DMA_POLICY_TX_QUANTUM_MASK) >> MBOX_DMA_POLICY_TX_QUANTUM_LSB)
+#define MBOX_DMA_POLICY_TX_QUANTUM_SET(x) (((x) << MBOX_DMA_POLICY_TX_QUANTUM_LSB) & MBOX_DMA_POLICY_TX_QUANTUM_MASK)
+#define MBOX_DMA_POLICY_TX_ORDER_MSB 2
+#define MBOX_DMA_POLICY_TX_ORDER_LSB 2
+#define MBOX_DMA_POLICY_TX_ORDER_MASK 0x00000004
+#define MBOX_DMA_POLICY_TX_ORDER_GET(x) (((x) & MBOX_DMA_POLICY_TX_ORDER_MASK) >> MBOX_DMA_POLICY_TX_ORDER_LSB)
+#define MBOX_DMA_POLICY_TX_ORDER_SET(x) (((x) << MBOX_DMA_POLICY_TX_ORDER_LSB) & MBOX_DMA_POLICY_TX_ORDER_MASK)
+#define MBOX_DMA_POLICY_RX_QUANTUM_MSB 1
+#define MBOX_DMA_POLICY_RX_QUANTUM_LSB 1
+#define MBOX_DMA_POLICY_RX_QUANTUM_MASK 0x00000002
+#define MBOX_DMA_POLICY_RX_QUANTUM_GET(x) (((x) & MBOX_DMA_POLICY_RX_QUANTUM_MASK) >> MBOX_DMA_POLICY_RX_QUANTUM_LSB)
+#define MBOX_DMA_POLICY_RX_QUANTUM_SET(x) (((x) << MBOX_DMA_POLICY_RX_QUANTUM_LSB) & MBOX_DMA_POLICY_RX_QUANTUM_MASK)
+#define MBOX_DMA_POLICY_RX_ORDER_MSB 0
+#define MBOX_DMA_POLICY_RX_ORDER_LSB 0
+#define MBOX_DMA_POLICY_RX_ORDER_MASK 0x00000001
+#define MBOX_DMA_POLICY_RX_ORDER_GET(x) (((x) & MBOX_DMA_POLICY_RX_ORDER_MASK) >> MBOX_DMA_POLICY_RX_ORDER_LSB)
+#define MBOX_DMA_POLICY_RX_ORDER_SET(x) (((x) << MBOX_DMA_POLICY_RX_ORDER_LSB) & MBOX_DMA_POLICY_RX_ORDER_MASK)
+
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS 0x0c014018
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_OFFSET 0x00000018
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX0_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX0_DMA_RX_CONTROL_ADDRESS 0x0c01401c
+#define MBOX0_DMA_RX_CONTROL_OFFSET 0x0000001c
+#define MBOX0_DMA_RX_CONTROL_RESUME_MSB 2
+#define MBOX0_DMA_RX_CONTROL_RESUME_LSB 2
+#define MBOX0_DMA_RX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX0_DMA_RX_CONTROL_RESUME_GET(x) (((x) & MBOX0_DMA_RX_CONTROL_RESUME_MASK) >> MBOX0_DMA_RX_CONTROL_RESUME_LSB)
+#define MBOX0_DMA_RX_CONTROL_RESUME_SET(x) (((x) << MBOX0_DMA_RX_CONTROL_RESUME_LSB) & MBOX0_DMA_RX_CONTROL_RESUME_MASK)
+#define MBOX0_DMA_RX_CONTROL_START_MSB 1
+#define MBOX0_DMA_RX_CONTROL_START_LSB 1
+#define MBOX0_DMA_RX_CONTROL_START_MASK 0x00000002
+#define MBOX0_DMA_RX_CONTROL_START_GET(x) (((x) & MBOX0_DMA_RX_CONTROL_START_MASK) >> MBOX0_DMA_RX_CONTROL_START_LSB)
+#define MBOX0_DMA_RX_CONTROL_START_SET(x) (((x) << MBOX0_DMA_RX_CONTROL_START_LSB) & MBOX0_DMA_RX_CONTROL_START_MASK)
+#define MBOX0_DMA_RX_CONTROL_STOP_MSB 0
+#define MBOX0_DMA_RX_CONTROL_STOP_LSB 0
+#define MBOX0_DMA_RX_CONTROL_STOP_MASK 0x00000001
+#define MBOX0_DMA_RX_CONTROL_STOP_GET(x) (((x) & MBOX0_DMA_RX_CONTROL_STOP_MASK) >> MBOX0_DMA_RX_CONTROL_STOP_LSB)
+#define MBOX0_DMA_RX_CONTROL_STOP_SET(x) (((x) << MBOX0_DMA_RX_CONTROL_STOP_LSB) & MBOX0_DMA_RX_CONTROL_STOP_MASK)
+
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS 0x0c014020
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_OFFSET 0x00000020
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX0_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX0_DMA_TX_CONTROL_ADDRESS 0x0c014024
+#define MBOX0_DMA_TX_CONTROL_OFFSET 0x00000024
+#define MBOX0_DMA_TX_CONTROL_RESUME_MSB 2
+#define MBOX0_DMA_TX_CONTROL_RESUME_LSB 2
+#define MBOX0_DMA_TX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX0_DMA_TX_CONTROL_RESUME_GET(x) (((x) & MBOX0_DMA_TX_CONTROL_RESUME_MASK) >> MBOX0_DMA_TX_CONTROL_RESUME_LSB)
+#define MBOX0_DMA_TX_CONTROL_RESUME_SET(x) (((x) << MBOX0_DMA_TX_CONTROL_RESUME_LSB) & MBOX0_DMA_TX_CONTROL_RESUME_MASK)
+#define MBOX0_DMA_TX_CONTROL_START_MSB 1
+#define MBOX0_DMA_TX_CONTROL_START_LSB 1
+#define MBOX0_DMA_TX_CONTROL_START_MASK 0x00000002
+#define MBOX0_DMA_TX_CONTROL_START_GET(x) (((x) & MBOX0_DMA_TX_CONTROL_START_MASK) >> MBOX0_DMA_TX_CONTROL_START_LSB)
+#define MBOX0_DMA_TX_CONTROL_START_SET(x) (((x) << MBOX0_DMA_TX_CONTROL_START_LSB) & MBOX0_DMA_TX_CONTROL_START_MASK)
+#define MBOX0_DMA_TX_CONTROL_STOP_MSB 0
+#define MBOX0_DMA_TX_CONTROL_STOP_LSB 0
+#define MBOX0_DMA_TX_CONTROL_STOP_MASK 0x00000001
+#define MBOX0_DMA_TX_CONTROL_STOP_GET(x) (((x) & MBOX0_DMA_TX_CONTROL_STOP_MASK) >> MBOX0_DMA_TX_CONTROL_STOP_LSB)
+#define MBOX0_DMA_TX_CONTROL_STOP_SET(x) (((x) << MBOX0_DMA_TX_CONTROL_STOP_LSB) & MBOX0_DMA_TX_CONTROL_STOP_MASK)
+
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS 0x0c014028
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_OFFSET 0x00000028
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX1_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX1_DMA_RX_CONTROL_ADDRESS 0x0c01402c
+#define MBOX1_DMA_RX_CONTROL_OFFSET 0x0000002c
+#define MBOX1_DMA_RX_CONTROL_RESUME_MSB 2
+#define MBOX1_DMA_RX_CONTROL_RESUME_LSB 2
+#define MBOX1_DMA_RX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX1_DMA_RX_CONTROL_RESUME_GET(x) (((x) & MBOX1_DMA_RX_CONTROL_RESUME_MASK) >> MBOX1_DMA_RX_CONTROL_RESUME_LSB)
+#define MBOX1_DMA_RX_CONTROL_RESUME_SET(x) (((x) << MBOX1_DMA_RX_CONTROL_RESUME_LSB) & MBOX1_DMA_RX_CONTROL_RESUME_MASK)
+#define MBOX1_DMA_RX_CONTROL_START_MSB 1
+#define MBOX1_DMA_RX_CONTROL_START_LSB 1
+#define MBOX1_DMA_RX_CONTROL_START_MASK 0x00000002
+#define MBOX1_DMA_RX_CONTROL_START_GET(x) (((x) & MBOX1_DMA_RX_CONTROL_START_MASK) >> MBOX1_DMA_RX_CONTROL_START_LSB)
+#define MBOX1_DMA_RX_CONTROL_START_SET(x) (((x) << MBOX1_DMA_RX_CONTROL_START_LSB) & MBOX1_DMA_RX_CONTROL_START_MASK)
+#define MBOX1_DMA_RX_CONTROL_STOP_MSB 0
+#define MBOX1_DMA_RX_CONTROL_STOP_LSB 0
+#define MBOX1_DMA_RX_CONTROL_STOP_MASK 0x00000001
+#define MBOX1_DMA_RX_CONTROL_STOP_GET(x) (((x) & MBOX1_DMA_RX_CONTROL_STOP_MASK) >> MBOX1_DMA_RX_CONTROL_STOP_LSB)
+#define MBOX1_DMA_RX_CONTROL_STOP_SET(x) (((x) << MBOX1_DMA_RX_CONTROL_STOP_LSB) & MBOX1_DMA_RX_CONTROL_STOP_MASK)
+
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS 0x0c014030
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_OFFSET 0x00000030
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX1_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX1_DMA_TX_CONTROL_ADDRESS 0x0c014034
+#define MBOX1_DMA_TX_CONTROL_OFFSET 0x00000034
+#define MBOX1_DMA_TX_CONTROL_RESUME_MSB 2
+#define MBOX1_DMA_TX_CONTROL_RESUME_LSB 2
+#define MBOX1_DMA_TX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX1_DMA_TX_CONTROL_RESUME_GET(x) (((x) & MBOX1_DMA_TX_CONTROL_RESUME_MASK) >> MBOX1_DMA_TX_CONTROL_RESUME_LSB)
+#define MBOX1_DMA_TX_CONTROL_RESUME_SET(x) (((x) << MBOX1_DMA_TX_CONTROL_RESUME_LSB) & MBOX1_DMA_TX_CONTROL_RESUME_MASK)
+#define MBOX1_DMA_TX_CONTROL_START_MSB 1
+#define MBOX1_DMA_TX_CONTROL_START_LSB 1
+#define MBOX1_DMA_TX_CONTROL_START_MASK 0x00000002
+#define MBOX1_DMA_TX_CONTROL_START_GET(x) (((x) & MBOX1_DMA_TX_CONTROL_START_MASK) >> MBOX1_DMA_TX_CONTROL_START_LSB)
+#define MBOX1_DMA_TX_CONTROL_START_SET(x) (((x) << MBOX1_DMA_TX_CONTROL_START_LSB) & MBOX1_DMA_TX_CONTROL_START_MASK)
+#define MBOX1_DMA_TX_CONTROL_STOP_MSB 0
+#define MBOX1_DMA_TX_CONTROL_STOP_LSB 0
+#define MBOX1_DMA_TX_CONTROL_STOP_MASK 0x00000001
+#define MBOX1_DMA_TX_CONTROL_STOP_GET(x) (((x) & MBOX1_DMA_TX_CONTROL_STOP_MASK) >> MBOX1_DMA_TX_CONTROL_STOP_LSB)
+#define MBOX1_DMA_TX_CONTROL_STOP_SET(x) (((x) << MBOX1_DMA_TX_CONTROL_STOP_LSB) & MBOX1_DMA_TX_CONTROL_STOP_MASK)
+
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS 0x0c014038
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_OFFSET 0x00000038
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX2_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX2_DMA_RX_CONTROL_ADDRESS 0x0c01403c
+#define MBOX2_DMA_RX_CONTROL_OFFSET 0x0000003c
+#define MBOX2_DMA_RX_CONTROL_RESUME_MSB 2
+#define MBOX2_DMA_RX_CONTROL_RESUME_LSB 2
+#define MBOX2_DMA_RX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX2_DMA_RX_CONTROL_RESUME_GET(x) (((x) & MBOX2_DMA_RX_CONTROL_RESUME_MASK) >> MBOX2_DMA_RX_CONTROL_RESUME_LSB)
+#define MBOX2_DMA_RX_CONTROL_RESUME_SET(x) (((x) << MBOX2_DMA_RX_CONTROL_RESUME_LSB) & MBOX2_DMA_RX_CONTROL_RESUME_MASK)
+#define MBOX2_DMA_RX_CONTROL_START_MSB 1
+#define MBOX2_DMA_RX_CONTROL_START_LSB 1
+#define MBOX2_DMA_RX_CONTROL_START_MASK 0x00000002
+#define MBOX2_DMA_RX_CONTROL_START_GET(x) (((x) & MBOX2_DMA_RX_CONTROL_START_MASK) >> MBOX2_DMA_RX_CONTROL_START_LSB)
+#define MBOX2_DMA_RX_CONTROL_START_SET(x) (((x) << MBOX2_DMA_RX_CONTROL_START_LSB) & MBOX2_DMA_RX_CONTROL_START_MASK)
+#define MBOX2_DMA_RX_CONTROL_STOP_MSB 0
+#define MBOX2_DMA_RX_CONTROL_STOP_LSB 0
+#define MBOX2_DMA_RX_CONTROL_STOP_MASK 0x00000001
+#define MBOX2_DMA_RX_CONTROL_STOP_GET(x) (((x) & MBOX2_DMA_RX_CONTROL_STOP_MASK) >> MBOX2_DMA_RX_CONTROL_STOP_LSB)
+#define MBOX2_DMA_RX_CONTROL_STOP_SET(x) (((x) << MBOX2_DMA_RX_CONTROL_STOP_LSB) & MBOX2_DMA_RX_CONTROL_STOP_MASK)
+
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS 0x0c014040
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_OFFSET 0x00000040
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX2_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX2_DMA_TX_CONTROL_ADDRESS 0x0c014044
+#define MBOX2_DMA_TX_CONTROL_OFFSET 0x00000044
+#define MBOX2_DMA_TX_CONTROL_RESUME_MSB 2
+#define MBOX2_DMA_TX_CONTROL_RESUME_LSB 2
+#define MBOX2_DMA_TX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX2_DMA_TX_CONTROL_RESUME_GET(x) (((x) & MBOX2_DMA_TX_CONTROL_RESUME_MASK) >> MBOX2_DMA_TX_CONTROL_RESUME_LSB)
+#define MBOX2_DMA_TX_CONTROL_RESUME_SET(x) (((x) << MBOX2_DMA_TX_CONTROL_RESUME_LSB) & MBOX2_DMA_TX_CONTROL_RESUME_MASK)
+#define MBOX2_DMA_TX_CONTROL_START_MSB 1
+#define MBOX2_DMA_TX_CONTROL_START_LSB 1
+#define MBOX2_DMA_TX_CONTROL_START_MASK 0x00000002
+#define MBOX2_DMA_TX_CONTROL_START_GET(x) (((x) & MBOX2_DMA_TX_CONTROL_START_MASK) >> MBOX2_DMA_TX_CONTROL_START_LSB)
+#define MBOX2_DMA_TX_CONTROL_START_SET(x) (((x) << MBOX2_DMA_TX_CONTROL_START_LSB) & MBOX2_DMA_TX_CONTROL_START_MASK)
+#define MBOX2_DMA_TX_CONTROL_STOP_MSB 0
+#define MBOX2_DMA_TX_CONTROL_STOP_LSB 0
+#define MBOX2_DMA_TX_CONTROL_STOP_MASK 0x00000001
+#define MBOX2_DMA_TX_CONTROL_STOP_GET(x) (((x) & MBOX2_DMA_TX_CONTROL_STOP_MASK) >> MBOX2_DMA_TX_CONTROL_STOP_LSB)
+#define MBOX2_DMA_TX_CONTROL_STOP_SET(x) (((x) << MBOX2_DMA_TX_CONTROL_STOP_LSB) & MBOX2_DMA_TX_CONTROL_STOP_MASK)
+
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS 0x0c014048
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_OFFSET 0x00000048
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX3_DMA_RX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX3_DMA_RX_CONTROL_ADDRESS 0x0c01404c
+#define MBOX3_DMA_RX_CONTROL_OFFSET 0x0000004c
+#define MBOX3_DMA_RX_CONTROL_RESUME_MSB 2
+#define MBOX3_DMA_RX_CONTROL_RESUME_LSB 2
+#define MBOX3_DMA_RX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX3_DMA_RX_CONTROL_RESUME_GET(x) (((x) & MBOX3_DMA_RX_CONTROL_RESUME_MASK) >> MBOX3_DMA_RX_CONTROL_RESUME_LSB)
+#define MBOX3_DMA_RX_CONTROL_RESUME_SET(x) (((x) << MBOX3_DMA_RX_CONTROL_RESUME_LSB) & MBOX3_DMA_RX_CONTROL_RESUME_MASK)
+#define MBOX3_DMA_RX_CONTROL_START_MSB 1
+#define MBOX3_DMA_RX_CONTROL_START_LSB 1
+#define MBOX3_DMA_RX_CONTROL_START_MASK 0x00000002
+#define MBOX3_DMA_RX_CONTROL_START_GET(x) (((x) & MBOX3_DMA_RX_CONTROL_START_MASK) >> MBOX3_DMA_RX_CONTROL_START_LSB)
+#define MBOX3_DMA_RX_CONTROL_START_SET(x) (((x) << MBOX3_DMA_RX_CONTROL_START_LSB) & MBOX3_DMA_RX_CONTROL_START_MASK)
+#define MBOX3_DMA_RX_CONTROL_STOP_MSB 0
+#define MBOX3_DMA_RX_CONTROL_STOP_LSB 0
+#define MBOX3_DMA_RX_CONTROL_STOP_MASK 0x00000001
+#define MBOX3_DMA_RX_CONTROL_STOP_GET(x) (((x) & MBOX3_DMA_RX_CONTROL_STOP_MASK) >> MBOX3_DMA_RX_CONTROL_STOP_LSB)
+#define MBOX3_DMA_RX_CONTROL_STOP_SET(x) (((x) << MBOX3_DMA_RX_CONTROL_STOP_LSB) & MBOX3_DMA_RX_CONTROL_STOP_MASK)
+
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS 0x0c014050
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_OFFSET 0x00000050
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MSB 27
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB 2
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK 0x0ffffffc
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_GET(x) (((x) & MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK) >> MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB)
+#define MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_SET(x) (((x) << MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_LSB) & MBOX3_DMA_TX_DESCRIPTOR_BASE_ADDRESS_MASK)
+
+#define MBOX3_DMA_TX_CONTROL_ADDRESS 0x0c014054
+#define MBOX3_DMA_TX_CONTROL_OFFSET 0x00000054
+#define MBOX3_DMA_TX_CONTROL_RESUME_MSB 2
+#define MBOX3_DMA_TX_CONTROL_RESUME_LSB 2
+#define MBOX3_DMA_TX_CONTROL_RESUME_MASK 0x00000004
+#define MBOX3_DMA_TX_CONTROL_RESUME_GET(x) (((x) & MBOX3_DMA_TX_CONTROL_RESUME_MASK) >> MBOX3_DMA_TX_CONTROL_RESUME_LSB)
+#define MBOX3_DMA_TX_CONTROL_RESUME_SET(x) (((x) << MBOX3_DMA_TX_CONTROL_RESUME_LSB) & MBOX3_DMA_TX_CONTROL_RESUME_MASK)
+#define MBOX3_DMA_TX_CONTROL_START_MSB 1
+#define MBOX3_DMA_TX_CONTROL_START_LSB 1
+#define MBOX3_DMA_TX_CONTROL_START_MASK 0x00000002
+#define MBOX3_DMA_TX_CONTROL_START_GET(x) (((x) & MBOX3_DMA_TX_CONTROL_START_MASK) >> MBOX3_DMA_TX_CONTROL_START_LSB)
+#define MBOX3_DMA_TX_CONTROL_START_SET(x) (((x) << MBOX3_DMA_TX_CONTROL_START_LSB) & MBOX3_DMA_TX_CONTROL_START_MASK)
+#define MBOX3_DMA_TX_CONTROL_STOP_MSB 0
+#define MBOX3_DMA_TX_CONTROL_STOP_LSB 0
+#define MBOX3_DMA_TX_CONTROL_STOP_MASK 0x00000001
+#define MBOX3_DMA_TX_CONTROL_STOP_GET(x) (((x) & MBOX3_DMA_TX_CONTROL_STOP_MASK) >> MBOX3_DMA_TX_CONTROL_STOP_LSB)
+#define MBOX3_DMA_TX_CONTROL_STOP_SET(x) (((x) << MBOX3_DMA_TX_CONTROL_STOP_LSB) & MBOX3_DMA_TX_CONTROL_STOP_MASK)
+
+#define MBOX_INT_STATUS_ADDRESS 0x0c014058
+#define MBOX_INT_STATUS_OFFSET 0x00000058
+#define MBOX_INT_STATUS_RX_DMA_COMPLETE_MSB 31
+#define MBOX_INT_STATUS_RX_DMA_COMPLETE_LSB 28
+#define MBOX_INT_STATUS_RX_DMA_COMPLETE_MASK 0xf0000000
+#define MBOX_INT_STATUS_RX_DMA_COMPLETE_GET(x) (((x) & MBOX_INT_STATUS_RX_DMA_COMPLETE_MASK) >> MBOX_INT_STATUS_RX_DMA_COMPLETE_LSB)
+#define MBOX_INT_STATUS_RX_DMA_COMPLETE_SET(x) (((x) << MBOX_INT_STATUS_RX_DMA_COMPLETE_LSB) & MBOX_INT_STATUS_RX_DMA_COMPLETE_MASK)
+#define MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_MSB 27
+#define MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_LSB 24
+#define MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_MASK 0x0f000000
+#define MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_GET(x) (((x) & MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_MASK) >> MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_LSB)
+#define MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_SET(x) (((x) << MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_LSB) & MBOX_INT_STATUS_TX_DMA_EOM_COMPLETE_MASK)
+#define MBOX_INT_STATUS_TX_DMA_COMPLETE_MSB 23
+#define MBOX_INT_STATUS_TX_DMA_COMPLETE_LSB 20
+#define MBOX_INT_STATUS_TX_DMA_COMPLETE_MASK 0x00f00000
+#define MBOX_INT_STATUS_TX_DMA_COMPLETE_GET(x) (((x) & MBOX_INT_STATUS_TX_DMA_COMPLETE_MASK) >> MBOX_INT_STATUS_TX_DMA_COMPLETE_LSB)
+#define MBOX_INT_STATUS_TX_DMA_COMPLETE_SET(x) (((x) << MBOX_INT_STATUS_TX_DMA_COMPLETE_LSB) & MBOX_INT_STATUS_TX_DMA_COMPLETE_MASK)
+#define MBOX_INT_STATUS_TX_OVERFLOW_MSB 17
+#define MBOX_INT_STATUS_TX_OVERFLOW_LSB 17
+#define MBOX_INT_STATUS_TX_OVERFLOW_MASK 0x00020000
+#define MBOX_INT_STATUS_TX_OVERFLOW_GET(x) (((x) & MBOX_INT_STATUS_TX_OVERFLOW_MASK) >> MBOX_INT_STATUS_TX_OVERFLOW_LSB)
+#define MBOX_INT_STATUS_TX_OVERFLOW_SET(x) (((x) << MBOX_INT_STATUS_TX_OVERFLOW_LSB) & MBOX_INT_STATUS_TX_OVERFLOW_MASK)
+#define MBOX_INT_STATUS_RX_UNDERFLOW_MSB 16
+#define MBOX_INT_STATUS_RX_UNDERFLOW_LSB 16
+#define MBOX_INT_STATUS_RX_UNDERFLOW_MASK 0x00010000
+#define MBOX_INT_STATUS_RX_UNDERFLOW_GET(x) (((x) & MBOX_INT_STATUS_RX_UNDERFLOW_MASK) >> MBOX_INT_STATUS_RX_UNDERFLOW_LSB)
+#define MBOX_INT_STATUS_RX_UNDERFLOW_SET(x) (((x) << MBOX_INT_STATUS_RX_UNDERFLOW_LSB) & MBOX_INT_STATUS_RX_UNDERFLOW_MASK)
+#define MBOX_INT_STATUS_TX_NOT_EMPTY_MSB 15
+#define MBOX_INT_STATUS_TX_NOT_EMPTY_LSB 12
+#define MBOX_INT_STATUS_TX_NOT_EMPTY_MASK 0x0000f000
+#define MBOX_INT_STATUS_TX_NOT_EMPTY_GET(x) (((x) & MBOX_INT_STATUS_TX_NOT_EMPTY_MASK) >> MBOX_INT_STATUS_TX_NOT_EMPTY_LSB)
+#define MBOX_INT_STATUS_TX_NOT_EMPTY_SET(x) (((x) << MBOX_INT_STATUS_TX_NOT_EMPTY_LSB) & MBOX_INT_STATUS_TX_NOT_EMPTY_MASK)
+#define MBOX_INT_STATUS_RX_NOT_FULL_MSB 11
+#define MBOX_INT_STATUS_RX_NOT_FULL_LSB 8
+#define MBOX_INT_STATUS_RX_NOT_FULL_MASK 0x00000f00
+#define MBOX_INT_STATUS_RX_NOT_FULL_GET(x) (((x) & MBOX_INT_STATUS_RX_NOT_FULL_MASK) >> MBOX_INT_STATUS_RX_NOT_FULL_LSB)
+#define MBOX_INT_STATUS_RX_NOT_FULL_SET(x) (((x) << MBOX_INT_STATUS_RX_NOT_FULL_LSB) & MBOX_INT_STATUS_RX_NOT_FULL_MASK)
+#define MBOX_INT_STATUS_HOST_MSB 7
+#define MBOX_INT_STATUS_HOST_LSB 0
+#define MBOX_INT_STATUS_HOST_MASK 0x000000ff
+#define MBOX_INT_STATUS_HOST_GET(x) (((x) & MBOX_INT_STATUS_HOST_MASK) >> MBOX_INT_STATUS_HOST_LSB)
+#define MBOX_INT_STATUS_HOST_SET(x) (((x) << MBOX_INT_STATUS_HOST_LSB) & MBOX_INT_STATUS_HOST_MASK)
+
+#define MBOX_INT_ENABLE_ADDRESS 0x0c01405c
+#define MBOX_INT_ENABLE_OFFSET 0x0000005c
+#define MBOX_INT_ENABLE_RX_DMA_COMPLETE_MSB 31
+#define MBOX_INT_ENABLE_RX_DMA_COMPLETE_LSB 28
+#define MBOX_INT_ENABLE_RX_DMA_COMPLETE_MASK 0xf0000000
+#define MBOX_INT_ENABLE_RX_DMA_COMPLETE_GET(x) (((x) & MBOX_INT_ENABLE_RX_DMA_COMPLETE_MASK) >> MBOX_INT_ENABLE_RX_DMA_COMPLETE_LSB)
+#define MBOX_INT_ENABLE_RX_DMA_COMPLETE_SET(x) (((x) << MBOX_INT_ENABLE_RX_DMA_COMPLETE_LSB) & MBOX_INT_ENABLE_RX_DMA_COMPLETE_MASK)
+#define MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_MSB 27
+#define MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_LSB 24
+#define MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_MASK 0x0f000000
+#define MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_GET(x) (((x) & MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_MASK) >> MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_LSB)
+#define MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_SET(x) (((x) << MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_LSB) & MBOX_INT_ENABLE_TX_DMA_EOM_COMPLETE_MASK)
+#define MBOX_INT_ENABLE_TX_DMA_COMPLETE_MSB 23
+#define MBOX_INT_ENABLE_TX_DMA_COMPLETE_LSB 20
+#define MBOX_INT_ENABLE_TX_DMA_COMPLETE_MASK 0x00f00000
+#define MBOX_INT_ENABLE_TX_DMA_COMPLETE_GET(x) (((x) & MBOX_INT_ENABLE_TX_DMA_COMPLETE_MASK) >> MBOX_INT_ENABLE_TX_DMA_COMPLETE_LSB)
+#define MBOX_INT_ENABLE_TX_DMA_COMPLETE_SET(x) (((x) << MBOX_INT_ENABLE_TX_DMA_COMPLETE_LSB) & MBOX_INT_ENABLE_TX_DMA_COMPLETE_MASK)
+#define MBOX_INT_ENABLE_TX_OVERFLOW_MSB 17
+#define MBOX_INT_ENABLE_TX_OVERFLOW_LSB 17
+#define MBOX_INT_ENABLE_TX_OVERFLOW_MASK 0x00020000
+#define MBOX_INT_ENABLE_TX_OVERFLOW_GET(x) (((x) & MBOX_INT_ENABLE_TX_OVERFLOW_MASK) >> MBOX_INT_ENABLE_TX_OVERFLOW_LSB)
+#define MBOX_INT_ENABLE_TX_OVERFLOW_SET(x) (((x) << MBOX_INT_ENABLE_TX_OVERFLOW_LSB) & MBOX_INT_ENABLE_TX_OVERFLOW_MASK)
+#define MBOX_INT_ENABLE_RX_UNDERFLOW_MSB 16
+#define MBOX_INT_ENABLE_RX_UNDERFLOW_LSB 16
+#define MBOX_INT_ENABLE_RX_UNDERFLOW_MASK 0x00010000
+#define MBOX_INT_ENABLE_RX_UNDERFLOW_GET(x) (((x) & MBOX_INT_ENABLE_RX_UNDERFLOW_MASK) >> MBOX_INT_ENABLE_RX_UNDERFLOW_LSB)
+#define MBOX_INT_ENABLE_RX_UNDERFLOW_SET(x) (((x) << MBOX_INT_ENABLE_RX_UNDERFLOW_LSB) & MBOX_INT_ENABLE_RX_UNDERFLOW_MASK)
+#define MBOX_INT_ENABLE_TX_NOT_EMPTY_MSB 15
+#define MBOX_INT_ENABLE_TX_NOT_EMPTY_LSB 12
+#define MBOX_INT_ENABLE_TX_NOT_EMPTY_MASK 0x0000f000
+#define MBOX_INT_ENABLE_TX_NOT_EMPTY_GET(x) (((x) & MBOX_INT_ENABLE_TX_NOT_EMPTY_MASK) >> MBOX_INT_ENABLE_TX_NOT_EMPTY_LSB)
+#define MBOX_INT_ENABLE_TX_NOT_EMPTY_SET(x) (((x) << MBOX_INT_ENABLE_TX_NOT_EMPTY_LSB) & MBOX_INT_ENABLE_TX_NOT_EMPTY_MASK)
+#define MBOX_INT_ENABLE_RX_NOT_FULL_MSB 11
+#define MBOX_INT_ENABLE_RX_NOT_FULL_LSB 8
+#define MBOX_INT_ENABLE_RX_NOT_FULL_MASK 0x00000f00
+#define MBOX_INT_ENABLE_RX_NOT_FULL_GET(x) (((x) & MBOX_INT_ENABLE_RX_NOT_FULL_MASK) >> MBOX_INT_ENABLE_RX_NOT_FULL_LSB)
+#define MBOX_INT_ENABLE_RX_NOT_FULL_SET(x) (((x) << MBOX_INT_ENABLE_RX_NOT_FULL_LSB) & MBOX_INT_ENABLE_RX_NOT_FULL_MASK)
+#define MBOX_INT_ENABLE_HOST_MSB 7
+#define MBOX_INT_ENABLE_HOST_LSB 0
+#define MBOX_INT_ENABLE_HOST_MASK 0x000000ff
+#define MBOX_INT_ENABLE_HOST_GET(x) (((x) & MBOX_INT_ENABLE_HOST_MASK) >> MBOX_INT_ENABLE_HOST_LSB)
+#define MBOX_INT_ENABLE_HOST_SET(x) (((x) << MBOX_INT_ENABLE_HOST_LSB) & MBOX_INT_ENABLE_HOST_MASK)
+
+#define INT_HOST_ADDRESS 0x0c014060
+#define INT_HOST_OFFSET 0x00000060
+#define INT_HOST_VECTOR_MSB 7
+#define INT_HOST_VECTOR_LSB 0
+#define INT_HOST_VECTOR_MASK 0x000000ff
+#define INT_HOST_VECTOR_GET(x) (((x) & INT_HOST_VECTOR_MASK) >> INT_HOST_VECTOR_LSB)
+#define INT_HOST_VECTOR_SET(x) (((x) << INT_HOST_VECTOR_LSB) & INT_HOST_VECTOR_MASK)
+
+#define LOCAL_COUNT_ADDRESS 0x0c014080
+#define LOCAL_COUNT_OFFSET 0x00000080
+#define LOCAL_COUNT_VALUE_MSB 7
+#define LOCAL_COUNT_VALUE_LSB 0
+#define LOCAL_COUNT_VALUE_MASK 0x000000ff
+#define LOCAL_COUNT_VALUE_GET(x) (((x) & LOCAL_COUNT_VALUE_MASK) >> LOCAL_COUNT_VALUE_LSB)
+#define LOCAL_COUNT_VALUE_SET(x) (((x) << LOCAL_COUNT_VALUE_LSB) & LOCAL_COUNT_VALUE_MASK)
+
+#define COUNT_INC_ADDRESS 0x0c0140a0
+#define COUNT_INC_OFFSET 0x000000a0
+#define COUNT_INC_VALUE_MSB 7
+#define COUNT_INC_VALUE_LSB 0
+#define COUNT_INC_VALUE_MASK 0x000000ff
+#define COUNT_INC_VALUE_GET(x) (((x) & COUNT_INC_VALUE_MASK) >> COUNT_INC_VALUE_LSB)
+#define COUNT_INC_VALUE_SET(x) (((x) << COUNT_INC_VALUE_LSB) & COUNT_INC_VALUE_MASK)
+
+#define LOCAL_SCRATCH_ADDRESS 0x0c0140c0
+#define LOCAL_SCRATCH_OFFSET 0x000000c0
+#define LOCAL_SCRATCH_VALUE_MSB 7
+#define LOCAL_SCRATCH_VALUE_LSB 0
+#define LOCAL_SCRATCH_VALUE_MASK 0x000000ff
+#define LOCAL_SCRATCH_VALUE_GET(x) (((x) & LOCAL_SCRATCH_VALUE_MASK) >> LOCAL_SCRATCH_VALUE_LSB)
+#define LOCAL_SCRATCH_VALUE_SET(x) (((x) << LOCAL_SCRATCH_VALUE_LSB) & LOCAL_SCRATCH_VALUE_MASK)
+
+#define USE_LOCAL_BUS_ADDRESS 0x0c0140e0
+#define USE_LOCAL_BUS_OFFSET 0x000000e0
+#define USE_LOCAL_BUS_PIN_INIT_MSB 0
+#define USE_LOCAL_BUS_PIN_INIT_LSB 0
+#define USE_LOCAL_BUS_PIN_INIT_MASK 0x00000001
+#define USE_LOCAL_BUS_PIN_INIT_GET(x) (((x) & USE_LOCAL_BUS_PIN_INIT_MASK) >> USE_LOCAL_BUS_PIN_INIT_LSB)
+#define USE_LOCAL_BUS_PIN_INIT_SET(x) (((x) << USE_LOCAL_BUS_PIN_INIT_LSB) & USE_LOCAL_BUS_PIN_INIT_MASK)
+
+#define SDIO_CONFIG_ADDRESS 0x0c0140e4
+#define SDIO_CONFIG_OFFSET 0x000000e4
+#define SDIO_CONFIG_CCCR_IOR1_MSB 0
+#define SDIO_CONFIG_CCCR_IOR1_LSB 0
+#define SDIO_CONFIG_CCCR_IOR1_MASK 0x00000001
+#define SDIO_CONFIG_CCCR_IOR1_GET(x) (((x) & SDIO_CONFIG_CCCR_IOR1_MASK) >> SDIO_CONFIG_CCCR_IOR1_LSB)
+#define SDIO_CONFIG_CCCR_IOR1_SET(x) (((x) << SDIO_CONFIG_CCCR_IOR1_LSB) & SDIO_CONFIG_CCCR_IOR1_MASK)
+
+#define MBOX_DEBUG_ADDRESS 0x0c0140e8
+#define MBOX_DEBUG_OFFSET 0x000000e8
+#define MBOX_DEBUG_SEL_MSB 2
+#define MBOX_DEBUG_SEL_LSB 0
+#define MBOX_DEBUG_SEL_MASK 0x00000007
+#define MBOX_DEBUG_SEL_GET(x) (((x) & MBOX_DEBUG_SEL_MASK) >> MBOX_DEBUG_SEL_LSB)
+#define MBOX_DEBUG_SEL_SET(x) (((x) << MBOX_DEBUG_SEL_LSB) & MBOX_DEBUG_SEL_MASK)
+
+#define MBOX_FIFO_RESET_ADDRESS 0x0c0140ec
+#define MBOX_FIFO_RESET_OFFSET 0x000000ec
+#define MBOX_FIFO_RESET_INIT_MSB 0
+#define MBOX_FIFO_RESET_INIT_LSB 0
+#define MBOX_FIFO_RESET_INIT_MASK 0x00000001
+#define MBOX_FIFO_RESET_INIT_GET(x) (((x) & MBOX_FIFO_RESET_INIT_MASK) >> MBOX_FIFO_RESET_INIT_LSB)
+#define MBOX_FIFO_RESET_INIT_SET(x) (((x) << MBOX_FIFO_RESET_INIT_LSB) & MBOX_FIFO_RESET_INIT_MASK)
+
+#define STEREO_CONFIG_ADDRESS 0x0c0140f0
+#define STEREO_CONFIG_OFFSET 0x000000f0
+#define STEREO_CONFIG_ENABLE_MSB 24
+#define STEREO_CONFIG_ENABLE_LSB 24
+#define STEREO_CONFIG_ENABLE_MASK 0x01000000
+#define STEREO_CONFIG_ENABLE_GET(x) (((x) & STEREO_CONFIG_ENABLE_MASK) >> STEREO_CONFIG_ENABLE_LSB)
+#define STEREO_CONFIG_ENABLE_SET(x) (((x) << STEREO_CONFIG_ENABLE_LSB) & STEREO_CONFIG_ENABLE_MASK)
+#define STEREO_CONFIG_RESET_MSB 23
+#define STEREO_CONFIG_RESET_LSB 23
+#define STEREO_CONFIG_RESET_MASK 0x00800000
+#define STEREO_CONFIG_RESET_GET(x) (((x) & STEREO_CONFIG_RESET_MASK) >> STEREO_CONFIG_RESET_LSB)
+#define STEREO_CONFIG_RESET_SET(x) (((x) << STEREO_CONFIG_RESET_LSB) & STEREO_CONFIG_RESET_MASK)
+#define STEREO_CONFIG_I2S_DELAY_MSB 22
+#define STEREO_CONFIG_I2S_DELAY_LSB 22
+#define STEREO_CONFIG_I2S_DELAY_MASK 0x00400000
+#define STEREO_CONFIG_I2S_DELAY_GET(x) (((x) & STEREO_CONFIG_I2S_DELAY_MASK) >> STEREO_CONFIG_I2S_DELAY_LSB)
+#define STEREO_CONFIG_I2S_DELAY_SET(x) (((x) << STEREO_CONFIG_I2S_DELAY_LSB) & STEREO_CONFIG_I2S_DELAY_MASK)
+#define STEREO_CONFIG_MIC_MASTER_MSB 21
+#define STEREO_CONFIG_MIC_MASTER_LSB 21
+#define STEREO_CONFIG_MIC_MASTER_MASK 0x00200000
+#define STEREO_CONFIG_MIC_MASTER_GET(x) (((x) & STEREO_CONFIG_MIC_MASTER_MASK) >> STEREO_CONFIG_MIC_MASTER_LSB)
+#define STEREO_CONFIG_MIC_MASTER_SET(x) (((x) << STEREO_CONFIG_MIC_MASTER_LSB) & STEREO_CONFIG_MIC_MASTER_MASK)
+#define STEREO_CONFIG_MIC_WORD_SIZE_MSB 20
+#define STEREO_CONFIG_MIC_WORD_SIZE_LSB 20
+#define STEREO_CONFIG_MIC_WORD_SIZE_MASK 0x00100000
+#define STEREO_CONFIG_MIC_WORD_SIZE_GET(x) (((x) & STEREO_CONFIG_MIC_WORD_SIZE_MASK) >> STEREO_CONFIG_MIC_WORD_SIZE_LSB)
+#define STEREO_CONFIG_MIC_WORD_SIZE_SET(x) (((x) << STEREO_CONFIG_MIC_WORD_SIZE_LSB) & STEREO_CONFIG_MIC_WORD_SIZE_MASK)
+#define STEREO_CONFIG_STEREO_MONO_MSB 19
+#define STEREO_CONFIG_STEREO_MONO_LSB 18
+#define STEREO_CONFIG_STEREO_MONO_MASK 0x000c0000
+#define STEREO_CONFIG_STEREO_MONO_GET(x) (((x) & STEREO_CONFIG_STEREO_MONO_MASK) >> STEREO_CONFIG_STEREO_MONO_LSB)
+#define STEREO_CONFIG_STEREO_MONO_SET(x) (((x) << STEREO_CONFIG_STEREO_MONO_LSB) & STEREO_CONFIG_STEREO_MONO_MASK)
+#define STEREO_CONFIG_DATA_WORD_SIZE_MSB 17
+#define STEREO_CONFIG_DATA_WORD_SIZE_LSB 16
+#define STEREO_CONFIG_DATA_WORD_SIZE_MASK 0x00030000
+#define STEREO_CONFIG_DATA_WORD_SIZE_GET(x) (((x) & STEREO_CONFIG_DATA_WORD_SIZE_MASK) >> STEREO_CONFIG_DATA_WORD_SIZE_LSB)
+#define STEREO_CONFIG_DATA_WORD_SIZE_SET(x) (((x) << STEREO_CONFIG_DATA_WORD_SIZE_LSB) & STEREO_CONFIG_DATA_WORD_SIZE_MASK)
+#define STEREO_CONFIG_I2S_WORD_SIZE_MSB 15
+#define STEREO_CONFIG_I2S_WORD_SIZE_LSB 15
+#define STEREO_CONFIG_I2S_WORD_SIZE_MASK 0x00008000
+#define STEREO_CONFIG_I2S_WORD_SIZE_GET(x) (((x) & STEREO_CONFIG_I2S_WORD_SIZE_MASK) >> STEREO_CONFIG_I2S_WORD_SIZE_LSB)
+#define STEREO_CONFIG_I2S_WORD_SIZE_SET(x) (((x) << STEREO_CONFIG_I2S_WORD_SIZE_LSB) & STEREO_CONFIG_I2S_WORD_SIZE_MASK)
+#define STEREO_CONFIG_MCK_SEL_MSB 14
+#define STEREO_CONFIG_MCK_SEL_LSB 14
+#define STEREO_CONFIG_MCK_SEL_MASK 0x00004000
+#define STEREO_CONFIG_MCK_SEL_GET(x) (((x) & STEREO_CONFIG_MCK_SEL_MASK) >> STEREO_CONFIG_MCK_SEL_LSB)
+#define STEREO_CONFIG_MCK_SEL_SET(x) (((x) << STEREO_CONFIG_MCK_SEL_LSB) & STEREO_CONFIG_MCK_SEL_MASK)
+#define STEREO_CONFIG_MCK_CNT_MSB 13
+#define STEREO_CONFIG_MCK_CNT_LSB 10
+#define STEREO_CONFIG_MCK_CNT_MASK 0x00003c00
+#define STEREO_CONFIG_MCK_CNT_GET(x) (((x) & STEREO_CONFIG_MCK_CNT_MASK) >> STEREO_CONFIG_MCK_CNT_LSB)
+#define STEREO_CONFIG_MCK_CNT_SET(x) (((x) << STEREO_CONFIG_MCK_CNT_LSB) & STEREO_CONFIG_MCK_CNT_MASK)
+#define STEREO_CONFIG_MCK_RAW_MSB 9
+#define STEREO_CONFIG_MCK_RAW_LSB 9
+#define STEREO_CONFIG_MCK_RAW_MASK 0x00000200
+#define STEREO_CONFIG_MCK_RAW_GET(x) (((x) & STEREO_CONFIG_MCK_RAW_MASK) >> STEREO_CONFIG_MCK_RAW_LSB)
+#define STEREO_CONFIG_MCK_RAW_SET(x) (((x) << STEREO_CONFIG_MCK_RAW_LSB) & STEREO_CONFIG_MCK_RAW_MASK)
+#define STEREO_CONFIG_MASTER_MSB 8
+#define STEREO_CONFIG_MASTER_LSB 8
+#define STEREO_CONFIG_MASTER_MASK 0x00000100
+#define STEREO_CONFIG_MASTER_GET(x) (((x) & STEREO_CONFIG_MASTER_MASK) >> STEREO_CONFIG_MASTER_LSB)
+#define STEREO_CONFIG_MASTER_SET(x) (((x) << STEREO_CONFIG_MASTER_LSB) & STEREO_CONFIG_MASTER_MASK)
+#define STEREO_CONFIG_POSEDGE_MSB 7
+#define STEREO_CONFIG_POSEDGE_LSB 0
+#define STEREO_CONFIG_POSEDGE_MASK 0x000000ff
+#define STEREO_CONFIG_POSEDGE_GET(x) (((x) & STEREO_CONFIG_POSEDGE_MASK) >> STEREO_CONFIG_POSEDGE_LSB)
+#define STEREO_CONFIG_POSEDGE_SET(x) (((x) << STEREO_CONFIG_POSEDGE_LSB) & STEREO_CONFIG_POSEDGE_MASK)
+
+#define STEREO_VOLUME_ADDRESS 0x0c0140f4
+#define STEREO_VOLUME_OFFSET 0x000000f4
+#define STEREO_VOLUME_CHANNEL1_MSB 12
+#define STEREO_VOLUME_CHANNEL1_LSB 8
+#define STEREO_VOLUME_CHANNEL1_MASK 0x00001f00
+#define STEREO_VOLUME_CHANNEL1_GET(x) (((x) & STEREO_VOLUME_CHANNEL1_MASK) >> STEREO_VOLUME_CHANNEL1_LSB)
+#define STEREO_VOLUME_CHANNEL1_SET(x) (((x) << STEREO_VOLUME_CHANNEL1_LSB) & STEREO_VOLUME_CHANNEL1_MASK)
+#define STEREO_VOLUME_CHANNEL0_MSB 4
+#define STEREO_VOLUME_CHANNEL0_LSB 0
+#define STEREO_VOLUME_CHANNEL0_MASK 0x0000001f
+#define STEREO_VOLUME_CHANNEL0_GET(x) (((x) & STEREO_VOLUME_CHANNEL0_MASK) >> STEREO_VOLUME_CHANNEL0_LSB)
+#define STEREO_VOLUME_CHANNEL0_SET(x) (((x) << STEREO_VOLUME_CHANNEL0_LSB) & STEREO_VOLUME_CHANNEL0_MASK)
+
+#define HOST_IF_WINDOW_ADDRESS 0x0c016000
+#define HOST_IF_WINDOW_OFFSET 0x00002000
+#define HOST_IF_WINDOW_DATA_MSB 7
+#define HOST_IF_WINDOW_DATA_LSB 0
+#define HOST_IF_WINDOW_DATA_MASK 0x000000ff
+#define HOST_IF_WINDOW_DATA_GET(x) (((x) & HOST_IF_WINDOW_DATA_MASK) >> HOST_IF_WINDOW_DATA_LSB)
+#define HOST_IF_WINDOW_DATA_SET(x) (((x) << HOST_IF_WINDOW_DATA_LSB) & HOST_IF_WINDOW_DATA_MASK)
+
+#ifndef __ASSEMBLER__
+typedef struct mbox_reg_s {
+ volatile unsigned int mbox_fifo[4];
+ volatile unsigned int mbox_fifo_status;
+ volatile unsigned int mbox_dma_policy;
+ volatile unsigned int mbox0_dma_rx_descriptor_base;
+ volatile unsigned int mbox0_dma_rx_control;
+ volatile unsigned int mbox0_dma_tx_descriptor_base;
+ volatile unsigned int mbox0_dma_tx_control;
+ volatile unsigned int mbox1_dma_rx_descriptor_base;
+ volatile unsigned int mbox1_dma_rx_control;
+ volatile unsigned int mbox1_dma_tx_descriptor_base;
+ volatile unsigned int mbox1_dma_tx_control;
+ volatile unsigned int mbox2_dma_rx_descriptor_base;
+ volatile unsigned int mbox2_dma_rx_control;
+ volatile unsigned int mbox2_dma_tx_descriptor_base;
+ volatile unsigned int mbox2_dma_tx_control;
+ volatile unsigned int mbox3_dma_rx_descriptor_base;
+ volatile unsigned int mbox3_dma_rx_control;
+ volatile unsigned int mbox3_dma_tx_descriptor_base;
+ volatile unsigned int mbox3_dma_tx_control;
+ volatile unsigned int mbox_int_status;
+ volatile unsigned int mbox_int_enable;
+ volatile unsigned int int_host;
+ unsigned char pad0[28]; /* pad to 0x80 */
+ volatile unsigned int local_count[8];
+ volatile unsigned int count_inc[8];
+ volatile unsigned int local_scratch[8];
+ volatile unsigned int use_local_bus;
+ volatile unsigned int sdio_config;
+ volatile unsigned int mbox_debug;
+ volatile unsigned int mbox_fifo_reset;
+ volatile unsigned int stereo_config;
+ volatile unsigned int stereo_volume;
+ unsigned char pad1[7944]; /* pad to 0x2000 */
+ volatile unsigned int host_if_window[2048];
+} mbox_reg_t;
+#endif /* __ASSEMBLER__ */
+
+#endif /* _MBOX_H_ */
Added: developers/nbd/ar6k/include/hw/mc_reg.h
===================================================================
--- developers/nbd/ar6k/include/hw/mc_reg.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/hw/mc_reg.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,1129 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+#ifndef _MC_REG_H_
+#define _MC_REG_H_
+
+#define BANK0_ADDR_ADDRESS 0x0c004000
+#define BANK0_ADDR_OFFSET 0x00000000
+#define BANK0_ADDR_SIZE_MSB 31
+#define BANK0_ADDR_SIZE_LSB 28
+#define BANK0_ADDR_SIZE_MASK 0xf0000000
+#define BANK0_ADDR_SIZE_GET(x) (((x) & BANK0_ADDR_SIZE_MASK) >> BANK0_ADDR_SIZE_LSB)
+#define BANK0_ADDR_SIZE_SET(x) (((x) << BANK0_ADDR_SIZE_LSB) & BANK0_ADDR_SIZE_MASK)
+#define BANK0_ADDR_BASE_MSB 27
+#define BANK0_ADDR_BASE_LSB 10
+#define BANK0_ADDR_BASE_MASK 0x0ffffc00
+#define BANK0_ADDR_BASE_GET(x) (((x) & BANK0_ADDR_BASE_MASK) >> BANK0_ADDR_BASE_LSB)
+#define BANK0_ADDR_BASE_SET(x) (((x) << BANK0_ADDR_BASE_LSB) & BANK0_ADDR_BASE_MASK)
+
+#define BANK0_CONFIG_ADDRESS 0x0c004004
+#define BANK0_CONFIG_OFFSET 0x00000004
+#define BANK0_CONFIG_ENABLE_MSB 31
+#define BANK0_CONFIG_ENABLE_LSB 31
+#define BANK0_CONFIG_ENABLE_MASK 0x80000000
+#define BANK0_CONFIG_ENABLE_GET(x) (((x) & BANK0_CONFIG_ENABLE_MASK) >> BANK0_CONFIG_ENABLE_LSB)
+#define BANK0_CONFIG_ENABLE_SET(x) (((x) << BANK0_CONFIG_ENABLE_LSB) & BANK0_CONFIG_ENABLE_MASK)
+#define BANK0_CONFIG_WIDTH_MSB 28
+#define BANK0_CONFIG_WIDTH_LSB 28
+#define BANK0_CONFIG_WIDTH_MASK 0x10000000
+#define BANK0_CONFIG_WIDTH_GET(x) (((x) & BANK0_CONFIG_WIDTH_MASK) >> BANK0_CONFIG_WIDTH_LSB)
+#define BANK0_CONFIG_WIDTH_SET(x) (((x) << BANK0_CONFIG_WIDTH_LSB) & BANK0_CONFIG_WIDTH_MASK)
+#define BANK0_CONFIG_PROTECT_MSB 26
+#define BANK0_CONFIG_PROTECT_LSB 26
+#define BANK0_CONFIG_PROTECT_MASK 0x04000000
+#define BANK0_CONFIG_PROTECT_GET(x) (((x) & BANK0_CONFIG_PROTECT_MASK) >> BANK0_CONFIG_PROTECT_LSB)
+#define BANK0_CONFIG_PROTECT_SET(x) (((x) << BANK0_CONFIG_PROTECT_LSB) & BANK0_CONFIG_PROTECT_MASK)
+#define BANK0_CONFIG_WB_ENABLE_MSB 25
+#define BANK0_CONFIG_WB_ENABLE_LSB 25
+#define BANK0_CONFIG_WB_ENABLE_MASK 0x02000000
+#define BANK0_CONFIG_WB_ENABLE_GET(x) (((x) & BANK0_CONFIG_WB_ENABLE_MASK) >> BANK0_CONFIG_WB_ENABLE_LSB)
+#define BANK0_CONFIG_WB_ENABLE_SET(x) (((x) << BANK0_CONFIG_WB_ENABLE_LSB) & BANK0_CONFIG_WB_ENABLE_MASK)
+#define BANK0_CONFIG_WB_FLUSH_MSB 24
+#define BANK0_CONFIG_WB_FLUSH_LSB 24
+#define BANK0_CONFIG_WB_FLUSH_MASK 0x01000000
+#define BANK0_CONFIG_WB_FLUSH_GET(x) (((x) & BANK0_CONFIG_WB_FLUSH_MASK) >> BANK0_CONFIG_WB_FLUSH_LSB)
+#define BANK0_CONFIG_WB_FLUSH_SET(x) (((x) << BANK0_CONFIG_WB_FLUSH_LSB) & BANK0_CONFIG_WB_FLUSH_MASK)
+#define BANK0_CONFIG_SCALE_MSB 21
+#define BANK0_CONFIG_SCALE_LSB 20
+#define BANK0_CONFIG_SCALE_MASK 0x00300000
+#define BANK0_CONFIG_SCALE_GET(x) (((x) & BANK0_CONFIG_SCALE_MASK) >> BANK0_CONFIG_SCALE_LSB)
+#define BANK0_CONFIG_SCALE_SET(x) (((x) << BANK0_CONFIG_SCALE_LSB) & BANK0_CONFIG_SCALE_MASK)
+#define BANK0_CONFIG_HOLDOFF_MSB 19
+#define BANK0_CONFIG_HOLDOFF_LSB 16
+#define BANK0_CONFIG_HOLDOFF_MASK 0x000f0000
+#define BANK0_CONFIG_HOLDOFF_GET(x) (((x) & BANK0_CONFIG_HOLDOFF_MASK) >> BANK0_CONFIG_HOLDOFF_LSB)
+#define BANK0_CONFIG_HOLDOFF_SET(x) (((x) << BANK0_CONFIG_HOLDOFF_LSB) & BANK0_CONFIG_HOLDOFF_MASK)
+#define BANK0_CONFIG_TIMER3_MSB 15
+#define BANK0_CONFIG_TIMER3_LSB 12
+#define BANK0_CONFIG_TIMER3_MASK 0x0000f000
+#define BANK0_CONFIG_TIMER3_GET(x) (((x) & BANK0_CONFIG_TIMER3_MASK) >> BANK0_CONFIG_TIMER3_LSB)
+#define BANK0_CONFIG_TIMER3_SET(x) (((x) << BANK0_CONFIG_TIMER3_LSB) & BANK0_CONFIG_TIMER3_MASK)
+#define BANK0_CONFIG_TIMER2_MSB 11
+#define BANK0_CONFIG_TIMER2_LSB 8
+#define BANK0_CONFIG_TIMER2_MASK 0x00000f00
+#define BANK0_CONFIG_TIMER2_GET(x) (((x) & BANK0_CONFIG_TIMER2_MASK) >> BANK0_CONFIG_TIMER2_LSB)
+#define BANK0_CONFIG_TIMER2_SET(x) (((x) << BANK0_CONFIG_TIMER2_LSB) & BANK0_CONFIG_TIMER2_MASK)
+#define BANK0_CONFIG_TIMER1_MSB 7
+#define BANK0_CONFIG_TIMER1_LSB 4
+#define BANK0_CONFIG_TIMER1_MASK 0x000000f0
+#define BANK0_CONFIG_TIMER1_GET(x) (((x) & BANK0_CONFIG_TIMER1_MASK) >> BANK0_CONFIG_TIMER1_LSB)
+#define BANK0_CONFIG_TIMER1_SET(x) (((x) << BANK0_CONFIG_TIMER1_LSB) & BANK0_CONFIG_TIMER1_MASK)
+#define BANK0_CONFIG_TIMER0_MSB 3
+#define BANK0_CONFIG_TIMER0_LSB 0
+#define BANK0_CONFIG_TIMER0_MASK 0x0000000f
+#define BANK0_CONFIG_TIMER0_GET(x) (((x) & BANK0_CONFIG_TIMER0_MASK) >> BANK0_CONFIG_TIMER0_LSB)
+#define BANK0_CONFIG_TIMER0_SET(x) (((x) << BANK0_CONFIG_TIMER0_LSB) & BANK0_CONFIG_TIMER0_MASK)
+
+#define BANK0_READ_ADDRESS 0x0c004008
+#define BANK0_READ_OFFSET 0x00000008
+#define BANK0_READ_ENABLE_WAIT_MSB 31
+#define BANK0_READ_ENABLE_WAIT_LSB 31
+#define BANK0_READ_ENABLE_WAIT_MASK 0x80000000
+#define BANK0_READ_ENABLE_WAIT_GET(x) (((x) & BANK0_READ_ENABLE_WAIT_MASK) >> BANK0_READ_ENABLE_WAIT_LSB)
+#define BANK0_READ_ENABLE_WAIT_SET(x) (((x) << BANK0_READ_ENABLE_WAIT_LSB) & BANK0_READ_ENABLE_WAIT_MASK)
+#define BANK0_READ_WAIT_EVENT_MSB 30
+#define BANK0_READ_WAIT_EVENT_LSB 28
+#define BANK0_READ_WAIT_EVENT_MASK 0x70000000
+#define BANK0_READ_WAIT_EVENT_GET(x) (((x) & BANK0_READ_WAIT_EVENT_MASK) >> BANK0_READ_WAIT_EVENT_LSB)
+#define BANK0_READ_WAIT_EVENT_SET(x) (((x) << BANK0_READ_WAIT_EVENT_LSB) & BANK0_READ_WAIT_EVENT_MASK)
+#define BANK0_READ_END_EVENT_MSB 26
+#define BANK0_READ_END_EVENT_LSB 24
+#define BANK0_READ_END_EVENT_MASK 0x07000000
+#define BANK0_READ_END_EVENT_GET(x) (((x) & BANK0_READ_END_EVENT_MASK) >> BANK0_READ_END_EVENT_LSB)
+#define BANK0_READ_END_EVENT_SET(x) (((x) << BANK0_READ_END_EVENT_LSB) & BANK0_READ_END_EVENT_MASK)
+#define BANK0_READ_BURST_END_EVENT_MSB 22
+#define BANK0_READ_BURST_END_EVENT_LSB 20
+#define BANK0_READ_BURST_END_EVENT_MASK 0x00700000
+#define BANK0_READ_BURST_END_EVENT_GET(x) (((x) & BANK0_READ_BURST_END_EVENT_MASK) >> BANK0_READ_BURST_END_EVENT_LSB)
+#define BANK0_READ_BURST_END_EVENT_SET(x) (((x) << BANK0_READ_BURST_END_EVENT_LSB) & BANK0_READ_BURST_END_EVENT_MASK)
+#define BANK0_READ_BURST_START_EVENT_MSB 18
+#define BANK0_READ_BURST_START_EVENT_LSB 16
+#define BANK0_READ_BURST_START_EVENT_MASK 0x00070000
+#define BANK0_READ_BURST_START_EVENT_GET(x) (((x) & BANK0_READ_BURST_START_EVENT_MASK) >> BANK0_READ_BURST_START_EVENT_LSB)
+#define BANK0_READ_BURST_START_EVENT_SET(x) (((x) << BANK0_READ_BURST_START_EVENT_LSB) & BANK0_READ_BURST_START_EVENT_MASK)
+#define BANK0_READ_EVENT3_DC_MSB 15
+#define BANK0_READ_EVENT3_DC_LSB 15
+#define BANK0_READ_EVENT3_DC_MASK 0x00008000
+#define BANK0_READ_EVENT3_DC_GET(x) (((x) & BANK0_READ_EVENT3_DC_MASK) >> BANK0_READ_EVENT3_DC_LSB)
+#define BANK0_READ_EVENT3_DC_SET(x) (((x) << BANK0_READ_EVENT3_DC_LSB) & BANK0_READ_EVENT3_DC_MASK)
+#define BANK0_READ_EVENT3_BE_MSB 14
+#define BANK0_READ_EVENT3_BE_LSB 14
+#define BANK0_READ_EVENT3_BE_MASK 0x00004000
+#define BANK0_READ_EVENT3_BE_GET(x) (((x) & BANK0_READ_EVENT3_BE_MASK) >> BANK0_READ_EVENT3_BE_LSB)
+#define BANK0_READ_EVENT3_BE_SET(x) (((x) << BANK0_READ_EVENT3_BE_LSB) & BANK0_READ_EVENT3_BE_MASK)
+#define BANK0_READ_EVENT3_OE_MSB 13
+#define BANK0_READ_EVENT3_OE_LSB 13
+#define BANK0_READ_EVENT3_OE_MASK 0x00002000
+#define BANK0_READ_EVENT3_OE_GET(x) (((x) & BANK0_READ_EVENT3_OE_MASK) >> BANK0_READ_EVENT3_OE_LSB)
+#define BANK0_READ_EVENT3_OE_SET(x) (((x) << BANK0_READ_EVENT3_OE_LSB) & BANK0_READ_EVENT3_OE_MASK)
+#define BANK0_READ_EVENT3_CS_MSB 12
+#define BANK0_READ_EVENT3_CS_LSB 12
+#define BANK0_READ_EVENT3_CS_MASK 0x00001000
+#define BANK0_READ_EVENT3_CS_GET(x) (((x) & BANK0_READ_EVENT3_CS_MASK) >> BANK0_READ_EVENT3_CS_LSB)
+#define BANK0_READ_EVENT3_CS_SET(x) (((x) << BANK0_READ_EVENT3_CS_LSB) & BANK0_READ_EVENT3_CS_MASK)
+#define BANK0_READ_EVENT2_DC_MSB 11
+#define BANK0_READ_EVENT2_DC_LSB 11
+#define BANK0_READ_EVENT2_DC_MASK 0x00000800
+#define BANK0_READ_EVENT2_DC_GET(x) (((x) & BANK0_READ_EVENT2_DC_MASK) >> BANK0_READ_EVENT2_DC_LSB)
+#define BANK0_READ_EVENT2_DC_SET(x) (((x) << BANK0_READ_EVENT2_DC_LSB) & BANK0_READ_EVENT2_DC_MASK)
+#define BANK0_READ_EVENT2_BE_MSB 10
+#define BANK0_READ_EVENT2_BE_LSB 10
+#define BANK0_READ_EVENT2_BE_MASK 0x00000400
+#define BANK0_READ_EVENT2_BE_GET(x) (((x) & BANK0_READ_EVENT2_BE_MASK) >> BANK0_READ_EVENT2_BE_LSB)
+#define BANK0_READ_EVENT2_BE_SET(x) (((x) << BANK0_READ_EVENT2_BE_LSB) & BANK0_READ_EVENT2_BE_MASK)
+#define BANK0_READ_EVENT2_OE_MSB 9
+#define BANK0_READ_EVENT2_OE_LSB 9
+#define BANK0_READ_EVENT2_OE_MASK 0x00000200
+#define BANK0_READ_EVENT2_OE_GET(x) (((x) & BANK0_READ_EVENT2_OE_MASK) >> BANK0_READ_EVENT2_OE_LSB)
+#define BANK0_READ_EVENT2_OE_SET(x) (((x) << BANK0_READ_EVENT2_OE_LSB) & BANK0_READ_EVENT2_OE_MASK)
+#define BANK0_READ_EVENT2_CS_MSB 8
+#define BANK0_READ_EVENT2_CS_LSB 8
+#define BANK0_READ_EVENT2_CS_MASK 0x00000100
+#define BANK0_READ_EVENT2_CS_GET(x) (((x) & BANK0_READ_EVENT2_CS_MASK) >> BANK0_READ_EVENT2_CS_LSB)
+#define BANK0_READ_EVENT2_CS_SET(x) (((x) << BANK0_READ_EVENT2_CS_LSB) & BANK0_READ_EVENT2_CS_MASK)
+#define BANK0_READ_EVENT1_DC_MSB 7
+#define BANK0_READ_EVENT1_DC_LSB 7
+#define BANK0_READ_EVENT1_DC_MASK 0x00000080
+#define BANK0_READ_EVENT1_DC_GET(x) (((x) & BANK0_READ_EVENT1_DC_MASK) >> BANK0_READ_EVENT1_DC_LSB)
+#define BANK0_READ_EVENT1_DC_SET(x) (((x) << BANK0_READ_EVENT1_DC_LSB) & BANK0_READ_EVENT1_DC_MASK)
+#define BANK0_READ_EVENT1_BE_MSB 6
+#define BANK0_READ_EVENT1_BE_LSB 6
+#define BANK0_READ_EVENT1_BE_MASK 0x00000040
+#define BANK0_READ_EVENT1_BE_GET(x) (((x) & BANK0_READ_EVENT1_BE_MASK) >> BANK0_READ_EVENT1_BE_LSB)
+#define BANK0_READ_EVENT1_BE_SET(x) (((x) << BANK0_READ_EVENT1_BE_LSB) & BANK0_READ_EVENT1_BE_MASK)
+#define BANK0_READ_EVENT1_OE_MSB 5
+#define BANK0_READ_EVENT1_OE_LSB 5
+#define BANK0_READ_EVENT1_OE_MASK 0x00000020
+#define BANK0_READ_EVENT1_OE_GET(x) (((x) & BANK0_READ_EVENT1_OE_MASK) >> BANK0_READ_EVENT1_OE_LSB)
+#define BANK0_READ_EVENT1_OE_SET(x) (((x) << BANK0_READ_EVENT1_OE_LSB) & BANK0_READ_EVENT1_OE_MASK)
+#define BANK0_READ_EVENT1_CS_MSB 4
+#define BANK0_READ_EVENT1_CS_LSB 4
+#define BANK0_READ_EVENT1_CS_MASK 0x00000010
+#define BANK0_READ_EVENT1_CS_GET(x) (((x) & BANK0_READ_EVENT1_CS_MASK) >> BANK0_READ_EVENT1_CS_LSB)
+#define BANK0_READ_EVENT1_CS_SET(x) (((x) << BANK0_READ_EVENT1_CS_LSB) & BANK0_READ_EVENT1_CS_MASK)
+#define BANK0_READ_EVENT0_DC_MSB 3
+#define BANK0_READ_EVENT0_DC_LSB 3
+#define BANK0_READ_EVENT0_DC_MASK 0x00000008
+#define BANK0_READ_EVENT0_DC_GET(x) (((x) & BANK0_READ_EVENT0_DC_MASK) >> BANK0_READ_EVENT0_DC_LSB)
+#define BANK0_READ_EVENT0_DC_SET(x) (((x) << BANK0_READ_EVENT0_DC_LSB) & BANK0_READ_EVENT0_DC_MASK)
+#define BANK0_READ_EVENT0_BE_MSB 2
+#define BANK0_READ_EVENT0_BE_LSB 2
+#define BANK0_READ_EVENT0_BE_MASK 0x00000004
+#define BANK0_READ_EVENT0_BE_GET(x) (((x) & BANK0_READ_EVENT0_BE_MASK) >> BANK0_READ_EVENT0_BE_LSB)
+#define BANK0_READ_EVENT0_BE_SET(x) (((x) << BANK0_READ_EVENT0_BE_LSB) & BANK0_READ_EVENT0_BE_MASK)
+#define BANK0_READ_EVENT0_OE_MSB 1
+#define BANK0_READ_EVENT0_OE_LSB 1
+#define BANK0_READ_EVENT0_OE_MASK 0x00000002
+#define BANK0_READ_EVENT0_OE_GET(x) (((x) & BANK0_READ_EVENT0_OE_MASK) >> BANK0_READ_EVENT0_OE_LSB)
+#define BANK0_READ_EVENT0_OE_SET(x) (((x) << BANK0_READ_EVENT0_OE_LSB) & BANK0_READ_EVENT0_OE_MASK)
+#define BANK0_READ_EVENT0_CS_MSB 0
+#define BANK0_READ_EVENT0_CS_LSB 0
+#define BANK0_READ_EVENT0_CS_MASK 0x00000001
+#define BANK0_READ_EVENT0_CS_GET(x) (((x) & BANK0_READ_EVENT0_CS_MASK) >> BANK0_READ_EVENT0_CS_LSB)
+#define BANK0_READ_EVENT0_CS_SET(x) (((x) << BANK0_READ_EVENT0_CS_LSB) & BANK0_READ_EVENT0_CS_MASK)
+
+#define BANK0_WRITE_ADDRESS 0x0c00400c
+#define BANK0_WRITE_OFFSET 0x0000000c
+#define BANK0_WRITE_ENABLE_WAIT_MSB 31
+#define BANK0_WRITE_ENABLE_WAIT_LSB 31
+#define BANK0_WRITE_ENABLE_WAIT_MASK 0x80000000
+#define BANK0_WRITE_ENABLE_WAIT_GET(x) (((x) & BANK0_WRITE_ENABLE_WAIT_MASK) >> BANK0_WRITE_ENABLE_WAIT_LSB)
+#define BANK0_WRITE_ENABLE_WAIT_SET(x) (((x) << BANK0_WRITE_ENABLE_WAIT_LSB) & BANK0_WRITE_ENABLE_WAIT_MASK)
+#define BANK0_WRITE_WAIT_EVENT_MSB 30
+#define BANK0_WRITE_WAIT_EVENT_LSB 28
+#define BANK0_WRITE_WAIT_EVENT_MASK 0x70000000
+#define BANK0_WRITE_WAIT_EVENT_GET(x) (((x) & BANK0_WRITE_WAIT_EVENT_MASK) >> BANK0_WRITE_WAIT_EVENT_LSB)
+#define BANK0_WRITE_WAIT_EVENT_SET(x) (((x) << BANK0_WRITE_WAIT_EVENT_LSB) & BANK0_WRITE_WAIT_EVENT_MASK)
+#define BANK0_WRITE_END_EVENT_MSB 26
+#define BANK0_WRITE_END_EVENT_LSB 24
+#define BANK0_WRITE_END_EVENT_MASK 0x07000000
+#define BANK0_WRITE_END_EVENT_GET(x) (((x) & BANK0_WRITE_END_EVENT_MASK) >> BANK0_WRITE_END_EVENT_LSB)
+#define BANK0_WRITE_END_EVENT_SET(x) (((x) << BANK0_WRITE_END_EVENT_LSB) & BANK0_WRITE_END_EVENT_MASK)
+#define BANK0_WRITE_BURST_END_EVENT_MSB 22
+#define BANK0_WRITE_BURST_END_EVENT_LSB 20
+#define BANK0_WRITE_BURST_END_EVENT_MASK 0x00700000
+#define BANK0_WRITE_BURST_END_EVENT_GET(x) (((x) & BANK0_WRITE_BURST_END_EVENT_MASK) >> BANK0_WRITE_BURST_END_EVENT_LSB)
+#define BANK0_WRITE_BURST_END_EVENT_SET(x) (((x) << BANK0_WRITE_BURST_END_EVENT_LSB) & BANK0_WRITE_BURST_END_EVENT_MASK)
+#define BANK0_WRITE_BURST_START_EVENT_MSB 18
+#define BANK0_WRITE_BURST_START_EVENT_LSB 16
+#define BANK0_WRITE_BURST_START_EVENT_MASK 0x00070000
+#define BANK0_WRITE_BURST_START_EVENT_GET(x) (((x) & BANK0_WRITE_BURST_START_EVENT_MASK) >> BANK0_WRITE_BURST_START_EVENT_LSB)
+#define BANK0_WRITE_BURST_START_EVENT_SET(x) (((x) << BANK0_WRITE_BURST_START_EVENT_LSB) & BANK0_WRITE_BURST_START_EVENT_MASK)
+#define BANK0_WRITE_EVENT3_BE_MSB 14
+#define BANK0_WRITE_EVENT3_BE_LSB 14
+#define BANK0_WRITE_EVENT3_BE_MASK 0x00004000
+#define BANK0_WRITE_EVENT3_BE_GET(x) (((x) & BANK0_WRITE_EVENT3_BE_MASK) >> BANK0_WRITE_EVENT3_BE_LSB)
+#define BANK0_WRITE_EVENT3_BE_SET(x) (((x) << BANK0_WRITE_EVENT3_BE_LSB) & BANK0_WRITE_EVENT3_BE_MASK)
+#define BANK0_WRITE_EVENT3_WE_MSB 13
+#define BANK0_WRITE_EVENT3_WE_LSB 13
+#define BANK0_WRITE_EVENT3_WE_MASK 0x00002000
+#define BANK0_WRITE_EVENT3_WE_GET(x) (((x) & BANK0_WRITE_EVENT3_WE_MASK) >> BANK0_WRITE_EVENT3_WE_LSB)
+#define BANK0_WRITE_EVENT3_WE_SET(x) (((x) << BANK0_WRITE_EVENT3_WE_LSB) & BANK0_WRITE_EVENT3_WE_MASK)
+#define BANK0_WRITE_EVENT3_CS_MSB 12
+#define BANK0_WRITE_EVENT3_CS_LSB 12
+#define BANK0_WRITE_EVENT3_CS_MASK 0x00001000
+#define BANK0_WRITE_EVENT3_CS_GET(x) (((x) & BANK0_WRITE_EVENT3_CS_MASK) >> BANK0_WRITE_EVENT3_CS_LSB)
+#define BANK0_WRITE_EVENT3_CS_SET(x) (((x) << BANK0_WRITE_EVENT3_CS_LSB) & BANK0_WRITE_EVENT3_CS_MASK)
+#define BANK0_WRITE_EVENT2_BE_MSB 10
+#define BANK0_WRITE_EVENT2_BE_LSB 10
+#define BANK0_WRITE_EVENT2_BE_MASK 0x00000400
+#define BANK0_WRITE_EVENT2_BE_GET(x) (((x) & BANK0_WRITE_EVENT2_BE_MASK) >> BANK0_WRITE_EVENT2_BE_LSB)
+#define BANK0_WRITE_EVENT2_BE_SET(x) (((x) << BANK0_WRITE_EVENT2_BE_LSB) & BANK0_WRITE_EVENT2_BE_MASK)
+#define BANK0_WRITE_EVENT2_WE_MSB 9
+#define BANK0_WRITE_EVENT2_WE_LSB 9
+#define BANK0_WRITE_EVENT2_WE_MASK 0x00000200
+#define BANK0_WRITE_EVENT2_WE_GET(x) (((x) & BANK0_WRITE_EVENT2_WE_MASK) >> BANK0_WRITE_EVENT2_WE_LSB)
+#define BANK0_WRITE_EVENT2_WE_SET(x) (((x) << BANK0_WRITE_EVENT2_WE_LSB) & BANK0_WRITE_EVENT2_WE_MASK)
+#define BANK0_WRITE_EVENT2_CS_MSB 8
+#define BANK0_WRITE_EVENT2_CS_LSB 8
+#define BANK0_WRITE_EVENT2_CS_MASK 0x00000100
+#define BANK0_WRITE_EVENT2_CS_GET(x) (((x) & BANK0_WRITE_EVENT2_CS_MASK) >> BANK0_WRITE_EVENT2_CS_LSB)
+#define BANK0_WRITE_EVENT2_CS_SET(x) (((x) << BANK0_WRITE_EVENT2_CS_LSB) & BANK0_WRITE_EVENT2_CS_MASK)
+#define BANK0_WRITE_EVENT1_BE_MSB 6
+#define BANK0_WRITE_EVENT1_BE_LSB 6
+#define BANK0_WRITE_EVENT1_BE_MASK 0x00000040
+#define BANK0_WRITE_EVENT1_BE_GET(x) (((x) & BANK0_WRITE_EVENT1_BE_MASK) >> BANK0_WRITE_EVENT1_BE_LSB)
+#define BANK0_WRITE_EVENT1_BE_SET(x) (((x) << BANK0_WRITE_EVENT1_BE_LSB) & BANK0_WRITE_EVENT1_BE_MASK)
+#define BANK0_WRITE_EVENT1_WE_MSB 5
+#define BANK0_WRITE_EVENT1_WE_LSB 5
+#define BANK0_WRITE_EVENT1_WE_MASK 0x00000020
+#define BANK0_WRITE_EVENT1_WE_GET(x) (((x) & BANK0_WRITE_EVENT1_WE_MASK) >> BANK0_WRITE_EVENT1_WE_LSB)
+#define BANK0_WRITE_EVENT1_WE_SET(x) (((x) << BANK0_WRITE_EVENT1_WE_LSB) & BANK0_WRITE_EVENT1_WE_MASK)
+#define BANK0_WRITE_EVENT1_CS_MSB 4
+#define BANK0_WRITE_EVENT1_CS_LSB 4
+#define BANK0_WRITE_EVENT1_CS_MASK 0x00000010
+#define BANK0_WRITE_EVENT1_CS_GET(x) (((x) & BANK0_WRITE_EVENT1_CS_MASK) >> BANK0_WRITE_EVENT1_CS_LSB)
+#define BANK0_WRITE_EVENT1_CS_SET(x) (((x) << BANK0_WRITE_EVENT1_CS_LSB) & BANK0_WRITE_EVENT1_CS_MASK)
+#define BANK0_WRITE_EVENT0_BE_MSB 2
+#define BANK0_WRITE_EVENT0_BE_LSB 2
+#define BANK0_WRITE_EVENT0_BE_MASK 0x00000004
+#define BANK0_WRITE_EVENT0_BE_GET(x) (((x) & BANK0_WRITE_EVENT0_BE_MASK) >> BANK0_WRITE_EVENT0_BE_LSB)
+#define BANK0_WRITE_EVENT0_BE_SET(x) (((x) << BANK0_WRITE_EVENT0_BE_LSB) & BANK0_WRITE_EVENT0_BE_MASK)
+#define BANK0_WRITE_EVENT0_WE_MSB 1
+#define BANK0_WRITE_EVENT0_WE_LSB 1
+#define BANK0_WRITE_EVENT0_WE_MASK 0x00000002
+#define BANK0_WRITE_EVENT0_WE_GET(x) (((x) & BANK0_WRITE_EVENT0_WE_MASK) >> BANK0_WRITE_EVENT0_WE_LSB)
+#define BANK0_WRITE_EVENT0_WE_SET(x) (((x) << BANK0_WRITE_EVENT0_WE_LSB) & BANK0_WRITE_EVENT0_WE_MASK)
+#define BANK0_WRITE_EVENT0_CS_MSB 0
+#define BANK0_WRITE_EVENT0_CS_LSB 0
+#define BANK0_WRITE_EVENT0_CS_MASK 0x00000001
+#define BANK0_WRITE_EVENT0_CS_GET(x) (((x) & BANK0_WRITE_EVENT0_CS_MASK) >> BANK0_WRITE_EVENT0_CS_LSB)
+#define BANK0_WRITE_EVENT0_CS_SET(x) (((x) << BANK0_WRITE_EVENT0_CS_LSB) & BANK0_WRITE_EVENT0_CS_MASK)
+
+#define BANK1_ADDR_ADDRESS 0x0c004010
+#define BANK1_ADDR_OFFSET 0x00000010
+#define BANK1_ADDR_SIZE_MSB 31
+#define BANK1_ADDR_SIZE_LSB 28
+#define BANK1_ADDR_SIZE_MASK 0xf0000000
+#define BANK1_ADDR_SIZE_GET(x) (((x) & BANK1_ADDR_SIZE_MASK) >> BANK1_ADDR_SIZE_LSB)
+#define BANK1_ADDR_SIZE_SET(x) (((x) << BANK1_ADDR_SIZE_LSB) & BANK1_ADDR_SIZE_MASK)
+#define BANK1_ADDR_BASE_MSB 27
+#define BANK1_ADDR_BASE_LSB 10
+#define BANK1_ADDR_BASE_MASK 0x0ffffc00
+#define BANK1_ADDR_BASE_GET(x) (((x) & BANK1_ADDR_BASE_MASK) >> BANK1_ADDR_BASE_LSB)
+#define BANK1_ADDR_BASE_SET(x) (((x) << BANK1_ADDR_BASE_LSB) & BANK1_ADDR_BASE_MASK)
+
+#define BANK1_CONFIG_ADDRESS 0x0c004014
+#define BANK1_CONFIG_OFFSET 0x00000014
+#define BANK1_CONFIG_ENABLE_MSB 31
+#define BANK1_CONFIG_ENABLE_LSB 31
+#define BANK1_CONFIG_ENABLE_MASK 0x80000000
+#define BANK1_CONFIG_ENABLE_GET(x) (((x) & BANK1_CONFIG_ENABLE_MASK) >> BANK1_CONFIG_ENABLE_LSB)
+#define BANK1_CONFIG_ENABLE_SET(x) (((x) << BANK1_CONFIG_ENABLE_LSB) & BANK1_CONFIG_ENABLE_MASK)
+#define BANK1_CONFIG_WIDTH_MSB 28
+#define BANK1_CONFIG_WIDTH_LSB 28
+#define BANK1_CONFIG_WIDTH_MASK 0x10000000
+#define BANK1_CONFIG_WIDTH_GET(x) (((x) & BANK1_CONFIG_WIDTH_MASK) >> BANK1_CONFIG_WIDTH_LSB)
+#define BANK1_CONFIG_WIDTH_SET(x) (((x) << BANK1_CONFIG_WIDTH_LSB) & BANK1_CONFIG_WIDTH_MASK)
+#define BANK1_CONFIG_PROTECT_MSB 26
+#define BANK1_CONFIG_PROTECT_LSB 26
+#define BANK1_CONFIG_PROTECT_MASK 0x04000000
+#define BANK1_CONFIG_PROTECT_GET(x) (((x) & BANK1_CONFIG_PROTECT_MASK) >> BANK1_CONFIG_PROTECT_LSB)
+#define BANK1_CONFIG_PROTECT_SET(x) (((x) << BANK1_CONFIG_PROTECT_LSB) & BANK1_CONFIG_PROTECT_MASK)
+#define BANK1_CONFIG_WB_ENABLE_MSB 25
+#define BANK1_CONFIG_WB_ENABLE_LSB 25
+#define BANK1_CONFIG_WB_ENABLE_MASK 0x02000000
+#define BANK1_CONFIG_WB_ENABLE_GET(x) (((x) & BANK1_CONFIG_WB_ENABLE_MASK) >> BANK1_CONFIG_WB_ENABLE_LSB)
+#define BANK1_CONFIG_WB_ENABLE_SET(x) (((x) << BANK1_CONFIG_WB_ENABLE_LSB) & BANK1_CONFIG_WB_ENABLE_MASK)
+#define BANK1_CONFIG_WB_FLUSH_MSB 24
+#define BANK1_CONFIG_WB_FLUSH_LSB 24
+#define BANK1_CONFIG_WB_FLUSH_MASK 0x01000000
+#define BANK1_CONFIG_WB_FLUSH_GET(x) (((x) & BANK1_CONFIG_WB_FLUSH_MASK) >> BANK1_CONFIG_WB_FLUSH_LSB)
+#define BANK1_CONFIG_WB_FLUSH_SET(x) (((x) << BANK1_CONFIG_WB_FLUSH_LSB) & BANK1_CONFIG_WB_FLUSH_MASK)
+#define BANK1_CONFIG_SCALE_MSB 21
+#define BANK1_CONFIG_SCALE_LSB 20
+#define BANK1_CONFIG_SCALE_MASK 0x00300000
+#define BANK1_CONFIG_SCALE_GET(x) (((x) & BANK1_CONFIG_SCALE_MASK) >> BANK1_CONFIG_SCALE_LSB)
+#define BANK1_CONFIG_SCALE_SET(x) (((x) << BANK1_CONFIG_SCALE_LSB) & BANK1_CONFIG_SCALE_MASK)
+#define BANK1_CONFIG_HOLDOFF_MSB 19
+#define BANK1_CONFIG_HOLDOFF_LSB 16
+#define BANK1_CONFIG_HOLDOFF_MASK 0x000f0000
+#define BANK1_CONFIG_HOLDOFF_GET(x) (((x) & BANK1_CONFIG_HOLDOFF_MASK) >> BANK1_CONFIG_HOLDOFF_LSB)
+#define BANK1_CONFIG_HOLDOFF_SET(x) (((x) << BANK1_CONFIG_HOLDOFF_LSB) & BANK1_CONFIG_HOLDOFF_MASK)
+#define BANK1_CONFIG_TIMER3_MSB 15
+#define BANK1_CONFIG_TIMER3_LSB 12
+#define BANK1_CONFIG_TIMER3_MASK 0x0000f000
+#define BANK1_CONFIG_TIMER3_GET(x) (((x) & BANK1_CONFIG_TIMER3_MASK) >> BANK1_CONFIG_TIMER3_LSB)
+#define BANK1_CONFIG_TIMER3_SET(x) (((x) << BANK1_CONFIG_TIMER3_LSB) & BANK1_CONFIG_TIMER3_MASK)
+#define BANK1_CONFIG_TIMER2_MSB 11
+#define BANK1_CONFIG_TIMER2_LSB 8
+#define BANK1_CONFIG_TIMER2_MASK 0x00000f00
+#define BANK1_CONFIG_TIMER2_GET(x) (((x) & BANK1_CONFIG_TIMER2_MASK) >> BANK1_CONFIG_TIMER2_LSB)
+#define BANK1_CONFIG_TIMER2_SET(x) (((x) << BANK1_CONFIG_TIMER2_LSB) & BANK1_CONFIG_TIMER2_MASK)
+#define BANK1_CONFIG_TIMER1_MSB 7
+#define BANK1_CONFIG_TIMER1_LSB 4
+#define BANK1_CONFIG_TIMER1_MASK 0x000000f0
+#define BANK1_CONFIG_TIMER1_GET(x) (((x) & BANK1_CONFIG_TIMER1_MASK) >> BANK1_CONFIG_TIMER1_LSB)
+#define BANK1_CONFIG_TIMER1_SET(x) (((x) << BANK1_CONFIG_TIMER1_LSB) & BANK1_CONFIG_TIMER1_MASK)
+#define BANK1_CONFIG_TIMER0_MSB 3
+#define BANK1_CONFIG_TIMER0_LSB 0
+#define BANK1_CONFIG_TIMER0_MASK 0x0000000f
+#define BANK1_CONFIG_TIMER0_GET(x) (((x) & BANK1_CONFIG_TIMER0_MASK) >> BANK1_CONFIG_TIMER0_LSB)
+#define BANK1_CONFIG_TIMER0_SET(x) (((x) << BANK1_CONFIG_TIMER0_LSB) & BANK1_CONFIG_TIMER0_MASK)
+
+#define BANK1_READ_ADDRESS 0x0c004018
+#define BANK1_READ_OFFSET 0x00000018
+#define BANK1_READ_ENABLE_WAIT_MSB 31
+#define BANK1_READ_ENABLE_WAIT_LSB 31
+#define BANK1_READ_ENABLE_WAIT_MASK 0x80000000
+#define BANK1_READ_ENABLE_WAIT_GET(x) (((x) & BANK1_READ_ENABLE_WAIT_MASK) >> BANK1_READ_ENABLE_WAIT_LSB)
+#define BANK1_READ_ENABLE_WAIT_SET(x) (((x) << BANK1_READ_ENABLE_WAIT_LSB) & BANK1_READ_ENABLE_WAIT_MASK)
+#define BANK1_READ_WAIT_EVENT_MSB 30
+#define BANK1_READ_WAIT_EVENT_LSB 28
+#define BANK1_READ_WAIT_EVENT_MASK 0x70000000
+#define BANK1_READ_WAIT_EVENT_GET(x) (((x) & BANK1_READ_WAIT_EVENT_MASK) >> BANK1_READ_WAIT_EVENT_LSB)
+#define BANK1_READ_WAIT_EVENT_SET(x) (((x) << BANK1_READ_WAIT_EVENT_LSB) & BANK1_READ_WAIT_EVENT_MASK)
+#define BANK1_READ_END_EVENT_MSB 26
+#define BANK1_READ_END_EVENT_LSB 24
+#define BANK1_READ_END_EVENT_MASK 0x07000000
+#define BANK1_READ_END_EVENT_GET(x) (((x) & BANK1_READ_END_EVENT_MASK) >> BANK1_READ_END_EVENT_LSB)
+#define BANK1_READ_END_EVENT_SET(x) (((x) << BANK1_READ_END_EVENT_LSB) & BANK1_READ_END_EVENT_MASK)
+#define BANK1_READ_BURST_END_EVENT_MSB 22
+#define BANK1_READ_BURST_END_EVENT_LSB 20
+#define BANK1_READ_BURST_END_EVENT_MASK 0x00700000
+#define BANK1_READ_BURST_END_EVENT_GET(x) (((x) & BANK1_READ_BURST_END_EVENT_MASK) >> BANK1_READ_BURST_END_EVENT_LSB)
+#define BANK1_READ_BURST_END_EVENT_SET(x) (((x) << BANK1_READ_BURST_END_EVENT_LSB) & BANK1_READ_BURST_END_EVENT_MASK)
+#define BANK1_READ_BURST_START_EVENT_MSB 18
+#define BANK1_READ_BURST_START_EVENT_LSB 16
+#define BANK1_READ_BURST_START_EVENT_MASK 0x00070000
+#define BANK1_READ_BURST_START_EVENT_GET(x) (((x) & BANK1_READ_BURST_START_EVENT_MASK) >> BANK1_READ_BURST_START_EVENT_LSB)
+#define BANK1_READ_BURST_START_EVENT_SET(x) (((x) << BANK1_READ_BURST_START_EVENT_LSB) & BANK1_READ_BURST_START_EVENT_MASK)
+#define BANK1_READ_EVENT3_DC_MSB 15
+#define BANK1_READ_EVENT3_DC_LSB 15
+#define BANK1_READ_EVENT3_DC_MASK 0x00008000
+#define BANK1_READ_EVENT3_DC_GET(x) (((x) & BANK1_READ_EVENT3_DC_MASK) >> BANK1_READ_EVENT3_DC_LSB)
+#define BANK1_READ_EVENT3_DC_SET(x) (((x) << BANK1_READ_EVENT3_DC_LSB) & BANK1_READ_EVENT3_DC_MASK)
+#define BANK1_READ_EVENT3_BE_MSB 14
+#define BANK1_READ_EVENT3_BE_LSB 14
+#define BANK1_READ_EVENT3_BE_MASK 0x00004000
+#define BANK1_READ_EVENT3_BE_GET(x) (((x) & BANK1_READ_EVENT3_BE_MASK) >> BANK1_READ_EVENT3_BE_LSB)
+#define BANK1_READ_EVENT3_BE_SET(x) (((x) << BANK1_READ_EVENT3_BE_LSB) & BANK1_READ_EVENT3_BE_MASK)
+#define BANK1_READ_EVENT3_OE_MSB 13
+#define BANK1_READ_EVENT3_OE_LSB 13
+#define BANK1_READ_EVENT3_OE_MASK 0x00002000
+#define BANK1_READ_EVENT3_OE_GET(x) (((x) & BANK1_READ_EVENT3_OE_MASK) >> BANK1_READ_EVENT3_OE_LSB)
+#define BANK1_READ_EVENT3_OE_SET(x) (((x) << BANK1_READ_EVENT3_OE_LSB) & BANK1_READ_EVENT3_OE_MASK)
+#define BANK1_READ_EVENT3_CS_MSB 12
+#define BANK1_READ_EVENT3_CS_LSB 12
+#define BANK1_READ_EVENT3_CS_MASK 0x00001000
+#define BANK1_READ_EVENT3_CS_GET(x) (((x) & BANK1_READ_EVENT3_CS_MASK) >> BANK1_READ_EVENT3_CS_LSB)
+#define BANK1_READ_EVENT3_CS_SET(x) (((x) << BANK1_READ_EVENT3_CS_LSB) & BANK1_READ_EVENT3_CS_MASK)
+#define BANK1_READ_EVENT2_DC_MSB 11
+#define BANK1_READ_EVENT2_DC_LSB 11
+#define BANK1_READ_EVENT2_DC_MASK 0x00000800
+#define BANK1_READ_EVENT2_DC_GET(x) (((x) & BANK1_READ_EVENT2_DC_MASK) >> BANK1_READ_EVENT2_DC_LSB)
+#define BANK1_READ_EVENT2_DC_SET(x) (((x) << BANK1_READ_EVENT2_DC_LSB) & BANK1_READ_EVENT2_DC_MASK)
+#define BANK1_READ_EVENT2_BE_MSB 10
+#define BANK1_READ_EVENT2_BE_LSB 10
+#define BANK1_READ_EVENT2_BE_MASK 0x00000400
+#define BANK1_READ_EVENT2_BE_GET(x) (((x) & BANK1_READ_EVENT2_BE_MASK) >> BANK1_READ_EVENT2_BE_LSB)
+#define BANK1_READ_EVENT2_BE_SET(x) (((x) << BANK1_READ_EVENT2_BE_LSB) & BANK1_READ_EVENT2_BE_MASK)
+#define BANK1_READ_EVENT2_OE_MSB 9
+#define BANK1_READ_EVENT2_OE_LSB 9
+#define BANK1_READ_EVENT2_OE_MASK 0x00000200
+#define BANK1_READ_EVENT2_OE_GET(x) (((x) & BANK1_READ_EVENT2_OE_MASK) >> BANK1_READ_EVENT2_OE_LSB)
+#define BANK1_READ_EVENT2_OE_SET(x) (((x) << BANK1_READ_EVENT2_OE_LSB) & BANK1_READ_EVENT2_OE_MASK)
+#define BANK1_READ_EVENT2_CS_MSB 8
+#define BANK1_READ_EVENT2_CS_LSB 8
+#define BANK1_READ_EVENT2_CS_MASK 0x00000100
+#define BANK1_READ_EVENT2_CS_GET(x) (((x) & BANK1_READ_EVENT2_CS_MASK) >> BANK1_READ_EVENT2_CS_LSB)
+#define BANK1_READ_EVENT2_CS_SET(x) (((x) << BANK1_READ_EVENT2_CS_LSB) & BANK1_READ_EVENT2_CS_MASK)
+#define BANK1_READ_EVENT1_DC_MSB 7
+#define BANK1_READ_EVENT1_DC_LSB 7
+#define BANK1_READ_EVENT1_DC_MASK 0x00000080
+#define BANK1_READ_EVENT1_DC_GET(x) (((x) & BANK1_READ_EVENT1_DC_MASK) >> BANK1_READ_EVENT1_DC_LSB)
+#define BANK1_READ_EVENT1_DC_SET(x) (((x) << BANK1_READ_EVENT1_DC_LSB) & BANK1_READ_EVENT1_DC_MASK)
+#define BANK1_READ_EVENT1_BE_MSB 6
+#define BANK1_READ_EVENT1_BE_LSB 6
+#define BANK1_READ_EVENT1_BE_MASK 0x00000040
+#define BANK1_READ_EVENT1_BE_GET(x) (((x) & BANK1_READ_EVENT1_BE_MASK) >> BANK1_READ_EVENT1_BE_LSB)
+#define BANK1_READ_EVENT1_BE_SET(x) (((x) << BANK1_READ_EVENT1_BE_LSB) & BANK1_READ_EVENT1_BE_MASK)
+#define BANK1_READ_EVENT1_OE_MSB 5
+#define BANK1_READ_EVENT1_OE_LSB 5
+#define BANK1_READ_EVENT1_OE_MASK 0x00000020
+#define BANK1_READ_EVENT1_OE_GET(x) (((x) & BANK1_READ_EVENT1_OE_MASK) >> BANK1_READ_EVENT1_OE_LSB)
+#define BANK1_READ_EVENT1_OE_SET(x) (((x) << BANK1_READ_EVENT1_OE_LSB) & BANK1_READ_EVENT1_OE_MASK)
+#define BANK1_READ_EVENT1_CS_MSB 4
+#define BANK1_READ_EVENT1_CS_LSB 4
+#define BANK1_READ_EVENT1_CS_MASK 0x00000010
+#define BANK1_READ_EVENT1_CS_GET(x) (((x) & BANK1_READ_EVENT1_CS_MASK) >> BANK1_READ_EVENT1_CS_LSB)
+#define BANK1_READ_EVENT1_CS_SET(x) (((x) << BANK1_READ_EVENT1_CS_LSB) & BANK1_READ_EVENT1_CS_MASK)
+#define BANK1_READ_EVENT0_DC_MSB 3
+#define BANK1_READ_EVENT0_DC_LSB 3
+#define BANK1_READ_EVENT0_DC_MASK 0x00000008
+#define BANK1_READ_EVENT0_DC_GET(x) (((x) & BANK1_READ_EVENT0_DC_MASK) >> BANK1_READ_EVENT0_DC_LSB)
+#define BANK1_READ_EVENT0_DC_SET(x) (((x) << BANK1_READ_EVENT0_DC_LSB) & BANK1_READ_EVENT0_DC_MASK)
+#define BANK1_READ_EVENT0_BE_MSB 2
+#define BANK1_READ_EVENT0_BE_LSB 2
+#define BANK1_READ_EVENT0_BE_MASK 0x00000004
+#define BANK1_READ_EVENT0_BE_GET(x) (((x) & BANK1_READ_EVENT0_BE_MASK) >> BANK1_READ_EVENT0_BE_LSB)
+#define BANK1_READ_EVENT0_BE_SET(x) (((x) << BANK1_READ_EVENT0_BE_LSB) & BANK1_READ_EVENT0_BE_MASK)
+#define BANK1_READ_EVENT0_OE_MSB 1
+#define BANK1_READ_EVENT0_OE_LSB 1
+#define BANK1_READ_EVENT0_OE_MASK 0x00000002
+#define BANK1_READ_EVENT0_OE_GET(x) (((x) & BANK1_READ_EVENT0_OE_MASK) >> BANK1_READ_EVENT0_OE_LSB)
+#define BANK1_READ_EVENT0_OE_SET(x) (((x) << BANK1_READ_EVENT0_OE_LSB) & BANK1_READ_EVENT0_OE_MASK)
+#define BANK1_READ_EVENT0_CS_MSB 0
+#define BANK1_READ_EVENT0_CS_LSB 0
+#define BANK1_READ_EVENT0_CS_MASK 0x00000001
+#define BANK1_READ_EVENT0_CS_GET(x) (((x) & BANK1_READ_EVENT0_CS_MASK) >> BANK1_READ_EVENT0_CS_LSB)
+#define BANK1_READ_EVENT0_CS_SET(x) (((x) << BANK1_READ_EVENT0_CS_LSB) & BANK1_READ_EVENT0_CS_MASK)
+
+#define BANK1_WRITE_ADDRESS 0x0c00401c
+#define BANK1_WRITE_OFFSET 0x0000001c
+#define BANK1_WRITE_ENABLE_WAIT_MSB 31
+#define BANK1_WRITE_ENABLE_WAIT_LSB 31
+#define BANK1_WRITE_ENABLE_WAIT_MASK 0x80000000
+#define BANK1_WRITE_ENABLE_WAIT_GET(x) (((x) & BANK1_WRITE_ENABLE_WAIT_MASK) >> BANK1_WRITE_ENABLE_WAIT_LSB)
+#define BANK1_WRITE_ENABLE_WAIT_SET(x) (((x) << BANK1_WRITE_ENABLE_WAIT_LSB) & BANK1_WRITE_ENABLE_WAIT_MASK)
+#define BANK1_WRITE_WAIT_EVENT_MSB 30
+#define BANK1_WRITE_WAIT_EVENT_LSB 28
+#define BANK1_WRITE_WAIT_EVENT_MASK 0x70000000
+#define BANK1_WRITE_WAIT_EVENT_GET(x) (((x) & BANK1_WRITE_WAIT_EVENT_MASK) >> BANK1_WRITE_WAIT_EVENT_LSB)
+#define BANK1_WRITE_WAIT_EVENT_SET(x) (((x) << BANK1_WRITE_WAIT_EVENT_LSB) & BANK1_WRITE_WAIT_EVENT_MASK)
+#define BANK1_WRITE_END_EVENT_MSB 26
+#define BANK1_WRITE_END_EVENT_LSB 24
+#define BANK1_WRITE_END_EVENT_MASK 0x07000000
+#define BANK1_WRITE_END_EVENT_GET(x) (((x) & BANK1_WRITE_END_EVENT_MASK) >> BANK1_WRITE_END_EVENT_LSB)
+#define BANK1_WRITE_END_EVENT_SET(x) (((x) << BANK1_WRITE_END_EVENT_LSB) & BANK1_WRITE_END_EVENT_MASK)
+#define BANK1_WRITE_BURST_END_EVENT_MSB 22
+#define BANK1_WRITE_BURST_END_EVENT_LSB 20
+#define BANK1_WRITE_BURST_END_EVENT_MASK 0x00700000
+#define BANK1_WRITE_BURST_END_EVENT_GET(x) (((x) & BANK1_WRITE_BURST_END_EVENT_MASK) >> BANK1_WRITE_BURST_END_EVENT_LSB)
+#define BANK1_WRITE_BURST_END_EVENT_SET(x) (((x) << BANK1_WRITE_BURST_END_EVENT_LSB) & BANK1_WRITE_BURST_END_EVENT_MASK)
+#define BANK1_WRITE_BURST_START_EVENT_MSB 18
+#define BANK1_WRITE_BURST_START_EVENT_LSB 16
+#define BANK1_WRITE_BURST_START_EVENT_MASK 0x00070000
+#define BANK1_WRITE_BURST_START_EVENT_GET(x) (((x) & BANK1_WRITE_BURST_START_EVENT_MASK) >> BANK1_WRITE_BURST_START_EVENT_LSB)
+#define BANK1_WRITE_BURST_START_EVENT_SET(x) (((x) << BANK1_WRITE_BURST_START_EVENT_LSB) & BANK1_WRITE_BURST_START_EVENT_MASK)
+#define BANK1_WRITE_EVENT3_BE_MSB 14
+#define BANK1_WRITE_EVENT3_BE_LSB 14
+#define BANK1_WRITE_EVENT3_BE_MASK 0x00004000
+#define BANK1_WRITE_EVENT3_BE_GET(x) (((x) & BANK1_WRITE_EVENT3_BE_MASK) >> BANK1_WRITE_EVENT3_BE_LSB)
+#define BANK1_WRITE_EVENT3_BE_SET(x) (((x) << BANK1_WRITE_EVENT3_BE_LSB) & BANK1_WRITE_EVENT3_BE_MASK)
+#define BANK1_WRITE_EVENT3_WE_MSB 13
+#define BANK1_WRITE_EVENT3_WE_LSB 13
+#define BANK1_WRITE_EVENT3_WE_MASK 0x00002000
+#define BANK1_WRITE_EVENT3_WE_GET(x) (((x) & BANK1_WRITE_EVENT3_WE_MASK) >> BANK1_WRITE_EVENT3_WE_LSB)
+#define BANK1_WRITE_EVENT3_WE_SET(x) (((x) << BANK1_WRITE_EVENT3_WE_LSB) & BANK1_WRITE_EVENT3_WE_MASK)
+#define BANK1_WRITE_EVENT3_CS_MSB 12
+#define BANK1_WRITE_EVENT3_CS_LSB 12
+#define BANK1_WRITE_EVENT3_CS_MASK 0x00001000
+#define BANK1_WRITE_EVENT3_CS_GET(x) (((x) & BANK1_WRITE_EVENT3_CS_MASK) >> BANK1_WRITE_EVENT3_CS_LSB)
+#define BANK1_WRITE_EVENT3_CS_SET(x) (((x) << BANK1_WRITE_EVENT3_CS_LSB) & BANK1_WRITE_EVENT3_CS_MASK)
+#define BANK1_WRITE_EVENT2_BE_MSB 10
+#define BANK1_WRITE_EVENT2_BE_LSB 10
+#define BANK1_WRITE_EVENT2_BE_MASK 0x00000400
+#define BANK1_WRITE_EVENT2_BE_GET(x) (((x) & BANK1_WRITE_EVENT2_BE_MASK) >> BANK1_WRITE_EVENT2_BE_LSB)
+#define BANK1_WRITE_EVENT2_BE_SET(x) (((x) << BANK1_WRITE_EVENT2_BE_LSB) & BANK1_WRITE_EVENT2_BE_MASK)
+#define BANK1_WRITE_EVENT2_WE_MSB 9
+#define BANK1_WRITE_EVENT2_WE_LSB 9
+#define BANK1_WRITE_EVENT2_WE_MASK 0x00000200
+#define BANK1_WRITE_EVENT2_WE_GET(x) (((x) & BANK1_WRITE_EVENT2_WE_MASK) >> BANK1_WRITE_EVENT2_WE_LSB)
+#define BANK1_WRITE_EVENT2_WE_SET(x) (((x) << BANK1_WRITE_EVENT2_WE_LSB) & BANK1_WRITE_EVENT2_WE_MASK)
+#define BANK1_WRITE_EVENT2_CS_MSB 8
+#define BANK1_WRITE_EVENT2_CS_LSB 8
+#define BANK1_WRITE_EVENT2_CS_MASK 0x00000100
+#define BANK1_WRITE_EVENT2_CS_GET(x) (((x) & BANK1_WRITE_EVENT2_CS_MASK) >> BANK1_WRITE_EVENT2_CS_LSB)
+#define BANK1_WRITE_EVENT2_CS_SET(x) (((x) << BANK1_WRITE_EVENT2_CS_LSB) & BANK1_WRITE_EVENT2_CS_MASK)
+#define BANK1_WRITE_EVENT1_BE_MSB 6
+#define BANK1_WRITE_EVENT1_BE_LSB 6
+#define BANK1_WRITE_EVENT1_BE_MASK 0x00000040
+#define BANK1_WRITE_EVENT1_BE_GET(x) (((x) & BANK1_WRITE_EVENT1_BE_MASK) >> BANK1_WRITE_EVENT1_BE_LSB)
+#define BANK1_WRITE_EVENT1_BE_SET(x) (((x) << BANK1_WRITE_EVENT1_BE_LSB) & BANK1_WRITE_EVENT1_BE_MASK)
+#define BANK1_WRITE_EVENT1_WE_MSB 5
+#define BANK1_WRITE_EVENT1_WE_LSB 5
+#define BANK1_WRITE_EVENT1_WE_MASK 0x00000020
+#define BANK1_WRITE_EVENT1_WE_GET(x) (((x) & BANK1_WRITE_EVENT1_WE_MASK) >> BANK1_WRITE_EVENT1_WE_LSB)
+#define BANK1_WRITE_EVENT1_WE_SET(x) (((x) << BANK1_WRITE_EVENT1_WE_LSB) & BANK1_WRITE_EVENT1_WE_MASK)
+#define BANK1_WRITE_EVENT1_CS_MSB 4
+#define BANK1_WRITE_EVENT1_CS_LSB 4
+#define BANK1_WRITE_EVENT1_CS_MASK 0x00000010
+#define BANK1_WRITE_EVENT1_CS_GET(x) (((x) & BANK1_WRITE_EVENT1_CS_MASK) >> BANK1_WRITE_EVENT1_CS_LSB)
+#define BANK1_WRITE_EVENT1_CS_SET(x) (((x) << BANK1_WRITE_EVENT1_CS_LSB) & BANK1_WRITE_EVENT1_CS_MASK)
+#define BANK1_WRITE_EVENT0_BE_MSB 2
+#define BANK1_WRITE_EVENT0_BE_LSB 2
+#define BANK1_WRITE_EVENT0_BE_MASK 0x00000004
+#define BANK1_WRITE_EVENT0_BE_GET(x) (((x) & BANK1_WRITE_EVENT0_BE_MASK) >> BANK1_WRITE_EVENT0_BE_LSB)
+#define BANK1_WRITE_EVENT0_BE_SET(x) (((x) << BANK1_WRITE_EVENT0_BE_LSB) & BANK1_WRITE_EVENT0_BE_MASK)
+#define BANK1_WRITE_EVENT0_WE_MSB 1
+#define BANK1_WRITE_EVENT0_WE_LSB 1
+#define BANK1_WRITE_EVENT0_WE_MASK 0x00000002
+#define BANK1_WRITE_EVENT0_WE_GET(x) (((x) & BANK1_WRITE_EVENT0_WE_MASK) >> BANK1_WRITE_EVENT0_WE_LSB)
+#define BANK1_WRITE_EVENT0_WE_SET(x) (((x) << BANK1_WRITE_EVENT0_WE_LSB) & BANK1_WRITE_EVENT0_WE_MASK)
+#define BANK1_WRITE_EVENT0_CS_MSB 0
+#define BANK1_WRITE_EVENT0_CS_LSB 0
+#define BANK1_WRITE_EVENT0_CS_MASK 0x00000001
+#define BANK1_WRITE_EVENT0_CS_GET(x) (((x) & BANK1_WRITE_EVENT0_CS_MASK) >> BANK1_WRITE_EVENT0_CS_LSB)
+#define BANK1_WRITE_EVENT0_CS_SET(x) (((x) << BANK1_WRITE_EVENT0_CS_LSB) & BANK1_WRITE_EVENT0_CS_MASK)
+
+#define BANK2_ADDR_ADDRESS 0x0c004020
+#define BANK2_ADDR_OFFSET 0x00000020
+#define BANK2_ADDR_SIZE_MSB 31
+#define BANK2_ADDR_SIZE_LSB 28
+#define BANK2_ADDR_SIZE_MASK 0xf0000000
+#define BANK2_ADDR_SIZE_GET(x) (((x) & BANK2_ADDR_SIZE_MASK) >> BANK2_ADDR_SIZE_LSB)
+#define BANK2_ADDR_SIZE_SET(x) (((x) << BANK2_ADDR_SIZE_LSB) & BANK2_ADDR_SIZE_MASK)
+#define BANK2_ADDR_BASE_MSB 27
+#define BANK2_ADDR_BASE_LSB 10
+#define BANK2_ADDR_BASE_MASK 0x0ffffc00
+#define BANK2_ADDR_BASE_GET(x) (((x) & BANK2_ADDR_BASE_MASK) >> BANK2_ADDR_BASE_LSB)
+#define BANK2_ADDR_BASE_SET(x) (((x) << BANK2_ADDR_BASE_LSB) & BANK2_ADDR_BASE_MASK)
+
+#define BANK2_CONFIG_ADDRESS 0x0c004024
+#define BANK2_CONFIG_OFFSET 0x00000024
+#define BANK2_CONFIG_ENABLE_MSB 31
+#define BANK2_CONFIG_ENABLE_LSB 31
+#define BANK2_CONFIG_ENABLE_MASK 0x80000000
+#define BANK2_CONFIG_ENABLE_GET(x) (((x) & BANK2_CONFIG_ENABLE_MASK) >> BANK2_CONFIG_ENABLE_LSB)
+#define BANK2_CONFIG_ENABLE_SET(x) (((x) << BANK2_CONFIG_ENABLE_LSB) & BANK2_CONFIG_ENABLE_MASK)
+#define BANK2_CONFIG_WIDTH_MSB 28
+#define BANK2_CONFIG_WIDTH_LSB 28
+#define BANK2_CONFIG_WIDTH_MASK 0x10000000
+#define BANK2_CONFIG_WIDTH_GET(x) (((x) & BANK2_CONFIG_WIDTH_MASK) >> BANK2_CONFIG_WIDTH_LSB)
+#define BANK2_CONFIG_WIDTH_SET(x) (((x) << BANK2_CONFIG_WIDTH_LSB) & BANK2_CONFIG_WIDTH_MASK)
+#define BANK2_CONFIG_PROTECT_MSB 26
+#define BANK2_CONFIG_PROTECT_LSB 26
+#define BANK2_CONFIG_PROTECT_MASK 0x04000000
+#define BANK2_CONFIG_PROTECT_GET(x) (((x) & BANK2_CONFIG_PROTECT_MASK) >> BANK2_CONFIG_PROTECT_LSB)
+#define BANK2_CONFIG_PROTECT_SET(x) (((x) << BANK2_CONFIG_PROTECT_LSB) & BANK2_CONFIG_PROTECT_MASK)
+#define BANK2_CONFIG_WB_ENABLE_MSB 25
+#define BANK2_CONFIG_WB_ENABLE_LSB 25
+#define BANK2_CONFIG_WB_ENABLE_MASK 0x02000000
+#define BANK2_CONFIG_WB_ENABLE_GET(x) (((x) & BANK2_CONFIG_WB_ENABLE_MASK) >> BANK2_CONFIG_WB_ENABLE_LSB)
+#define BANK2_CONFIG_WB_ENABLE_SET(x) (((x) << BANK2_CONFIG_WB_ENABLE_LSB) & BANK2_CONFIG_WB_ENABLE_MASK)
+#define BANK2_CONFIG_WB_FLUSH_MSB 24
+#define BANK2_CONFIG_WB_FLUSH_LSB 24
+#define BANK2_CONFIG_WB_FLUSH_MASK 0x01000000
+#define BANK2_CONFIG_WB_FLUSH_GET(x) (((x) & BANK2_CONFIG_WB_FLUSH_MASK) >> BANK2_CONFIG_WB_FLUSH_LSB)
+#define BANK2_CONFIG_WB_FLUSH_SET(x) (((x) << BANK2_CONFIG_WB_FLUSH_LSB) & BANK2_CONFIG_WB_FLUSH_MASK)
+#define BANK2_CONFIG_SCALE_MSB 21
+#define BANK2_CONFIG_SCALE_LSB 20
+#define BANK2_CONFIG_SCALE_MASK 0x00300000
+#define BANK2_CONFIG_SCALE_GET(x) (((x) & BANK2_CONFIG_SCALE_MASK) >> BANK2_CONFIG_SCALE_LSB)
+#define BANK2_CONFIG_SCALE_SET(x) (((x) << BANK2_CONFIG_SCALE_LSB) & BANK2_CONFIG_SCALE_MASK)
+#define BANK2_CONFIG_HOLDOFF_MSB 19
+#define BANK2_CONFIG_HOLDOFF_LSB 16
+#define BANK2_CONFIG_HOLDOFF_MASK 0x000f0000
+#define BANK2_CONFIG_HOLDOFF_GET(x) (((x) & BANK2_CONFIG_HOLDOFF_MASK) >> BANK2_CONFIG_HOLDOFF_LSB)
+#define BANK2_CONFIG_HOLDOFF_SET(x) (((x) << BANK2_CONFIG_HOLDOFF_LSB) & BANK2_CONFIG_HOLDOFF_MASK)
+#define BANK2_CONFIG_TIMER3_MSB 15
+#define BANK2_CONFIG_TIMER3_LSB 12
+#define BANK2_CONFIG_TIMER3_MASK 0x0000f000
+#define BANK2_CONFIG_TIMER3_GET(x) (((x) & BANK2_CONFIG_TIMER3_MASK) >> BANK2_CONFIG_TIMER3_LSB)
+#define BANK2_CONFIG_TIMER3_SET(x) (((x) << BANK2_CONFIG_TIMER3_LSB) & BANK2_CONFIG_TIMER3_MASK)
+#define BANK2_CONFIG_TIMER2_MSB 11
+#define BANK2_CONFIG_TIMER2_LSB 8
+#define BANK2_CONFIG_TIMER2_MASK 0x00000f00
+#define BANK2_CONFIG_TIMER2_GET(x) (((x) & BANK2_CONFIG_TIMER2_MASK) >> BANK2_CONFIG_TIMER2_LSB)
+#define BANK2_CONFIG_TIMER2_SET(x) (((x) << BANK2_CONFIG_TIMER2_LSB) & BANK2_CONFIG_TIMER2_MASK)
+#define BANK2_CONFIG_TIMER1_MSB 7
+#define BANK2_CONFIG_TIMER1_LSB 4
+#define BANK2_CONFIG_TIMER1_MASK 0x000000f0
+#define BANK2_CONFIG_TIMER1_GET(x) (((x) & BANK2_CONFIG_TIMER1_MASK) >> BANK2_CONFIG_TIMER1_LSB)
+#define BANK2_CONFIG_TIMER1_SET(x) (((x) << BANK2_CONFIG_TIMER1_LSB) & BANK2_CONFIG_TIMER1_MASK)
+#define BANK2_CONFIG_TIMER0_MSB 3
+#define BANK2_CONFIG_TIMER0_LSB 0
+#define BANK2_CONFIG_TIMER0_MASK 0x0000000f
+#define BANK2_CONFIG_TIMER0_GET(x) (((x) & BANK2_CONFIG_TIMER0_MASK) >> BANK2_CONFIG_TIMER0_LSB)
+#define BANK2_CONFIG_TIMER0_SET(x) (((x) << BANK2_CONFIG_TIMER0_LSB) & BANK2_CONFIG_TIMER0_MASK)
+
+#define BANK2_READ_ADDRESS 0x0c004028
+#define BANK2_READ_OFFSET 0x00000028
+#define BANK2_READ_ENABLE_WAIT_MSB 31
+#define BANK2_READ_ENABLE_WAIT_LSB 31
+#define BANK2_READ_ENABLE_WAIT_MASK 0x80000000
+#define BANK2_READ_ENABLE_WAIT_GET(x) (((x) & BANK2_READ_ENABLE_WAIT_MASK) >> BANK2_READ_ENABLE_WAIT_LSB)
+#define BANK2_READ_ENABLE_WAIT_SET(x) (((x) << BANK2_READ_ENABLE_WAIT_LSB) & BANK2_READ_ENABLE_WAIT_MASK)
+#define BANK2_READ_WAIT_EVENT_MSB 30
+#define BANK2_READ_WAIT_EVENT_LSB 28
+#define BANK2_READ_WAIT_EVENT_MASK 0x70000000
+#define BANK2_READ_WAIT_EVENT_GET(x) (((x) & BANK2_READ_WAIT_EVENT_MASK) >> BANK2_READ_WAIT_EVENT_LSB)
+#define BANK2_READ_WAIT_EVENT_SET(x) (((x) << BANK2_READ_WAIT_EVENT_LSB) & BANK2_READ_WAIT_EVENT_MASK)
+#define BANK2_READ_END_EVENT_MSB 26
+#define BANK2_READ_END_EVENT_LSB 24
+#define BANK2_READ_END_EVENT_MASK 0x07000000
+#define BANK2_READ_END_EVENT_GET(x) (((x) & BANK2_READ_END_EVENT_MASK) >> BANK2_READ_END_EVENT_LSB)
+#define BANK2_READ_END_EVENT_SET(x) (((x) << BANK2_READ_END_EVENT_LSB) & BANK2_READ_END_EVENT_MASK)
+#define BANK2_READ_BURST_END_EVENT_MSB 22
+#define BANK2_READ_BURST_END_EVENT_LSB 20
+#define BANK2_READ_BURST_END_EVENT_MASK 0x00700000
+#define BANK2_READ_BURST_END_EVENT_GET(x) (((x) & BANK2_READ_BURST_END_EVENT_MASK) >> BANK2_READ_BURST_END_EVENT_LSB)
+#define BANK2_READ_BURST_END_EVENT_SET(x) (((x) << BANK2_READ_BURST_END_EVENT_LSB) & BANK2_READ_BURST_END_EVENT_MASK)
+#define BANK2_READ_BURST_START_EVENT_MSB 18
+#define BANK2_READ_BURST_START_EVENT_LSB 16
+#define BANK2_READ_BURST_START_EVENT_MASK 0x00070000
+#define BANK2_READ_BURST_START_EVENT_GET(x) (((x) & BANK2_READ_BURST_START_EVENT_MASK) >> BANK2_READ_BURST_START_EVENT_LSB)
+#define BANK2_READ_BURST_START_EVENT_SET(x) (((x) << BANK2_READ_BURST_START_EVENT_LSB) & BANK2_READ_BURST_START_EVENT_MASK)
+#define BANK2_READ_EVENT3_DC_MSB 15
+#define BANK2_READ_EVENT3_DC_LSB 15
+#define BANK2_READ_EVENT3_DC_MASK 0x00008000
+#define BANK2_READ_EVENT3_DC_GET(x) (((x) & BANK2_READ_EVENT3_DC_MASK) >> BANK2_READ_EVENT3_DC_LSB)
+#define BANK2_READ_EVENT3_DC_SET(x) (((x) << BANK2_READ_EVENT3_DC_LSB) & BANK2_READ_EVENT3_DC_MASK)
+#define BANK2_READ_EVENT3_BE_MSB 14
+#define BANK2_READ_EVENT3_BE_LSB 14
+#define BANK2_READ_EVENT3_BE_MASK 0x00004000
+#define BANK2_READ_EVENT3_BE_GET(x) (((x) & BANK2_READ_EVENT3_BE_MASK) >> BANK2_READ_EVENT3_BE_LSB)
+#define BANK2_READ_EVENT3_BE_SET(x) (((x) << BANK2_READ_EVENT3_BE_LSB) & BANK2_READ_EVENT3_BE_MASK)
+#define BANK2_READ_EVENT3_OE_MSB 13
+#define BANK2_READ_EVENT3_OE_LSB 13
+#define BANK2_READ_EVENT3_OE_MASK 0x00002000
+#define BANK2_READ_EVENT3_OE_GET(x) (((x) & BANK2_READ_EVENT3_OE_MASK) >> BANK2_READ_EVENT3_OE_LSB)
+#define BANK2_READ_EVENT3_OE_SET(x) (((x) << BANK2_READ_EVENT3_OE_LSB) & BANK2_READ_EVENT3_OE_MASK)
+#define BANK2_READ_EVENT3_CS_MSB 12
+#define BANK2_READ_EVENT3_CS_LSB 12
+#define BANK2_READ_EVENT3_CS_MASK 0x00001000
+#define BANK2_READ_EVENT3_CS_GET(x) (((x) & BANK2_READ_EVENT3_CS_MASK) >> BANK2_READ_EVENT3_CS_LSB)
+#define BANK2_READ_EVENT3_CS_SET(x) (((x) << BANK2_READ_EVENT3_CS_LSB) & BANK2_READ_EVENT3_CS_MASK)
+#define BANK2_READ_EVENT2_DC_MSB 11
+#define BANK2_READ_EVENT2_DC_LSB 11
+#define BANK2_READ_EVENT2_DC_MASK 0x00000800
+#define BANK2_READ_EVENT2_DC_GET(x) (((x) & BANK2_READ_EVENT2_DC_MASK) >> BANK2_READ_EVENT2_DC_LSB)
+#define BANK2_READ_EVENT2_DC_SET(x) (((x) << BANK2_READ_EVENT2_DC_LSB) & BANK2_READ_EVENT2_DC_MASK)
+#define BANK2_READ_EVENT2_BE_MSB 10
+#define BANK2_READ_EVENT2_BE_LSB 10
+#define BANK2_READ_EVENT2_BE_MASK 0x00000400
+#define BANK2_READ_EVENT2_BE_GET(x) (((x) & BANK2_READ_EVENT2_BE_MASK) >> BANK2_READ_EVENT2_BE_LSB)
+#define BANK2_READ_EVENT2_BE_SET(x) (((x) << BANK2_READ_EVENT2_BE_LSB) & BANK2_READ_EVENT2_BE_MASK)
+#define BANK2_READ_EVENT2_OE_MSB 9
+#define BANK2_READ_EVENT2_OE_LSB 9
+#define BANK2_READ_EVENT2_OE_MASK 0x00000200
+#define BANK2_READ_EVENT2_OE_GET(x) (((x) & BANK2_READ_EVENT2_OE_MASK) >> BANK2_READ_EVENT2_OE_LSB)
+#define BANK2_READ_EVENT2_OE_SET(x) (((x) << BANK2_READ_EVENT2_OE_LSB) & BANK2_READ_EVENT2_OE_MASK)
+#define BANK2_READ_EVENT2_CS_MSB 8
+#define BANK2_READ_EVENT2_CS_LSB 8
+#define BANK2_READ_EVENT2_CS_MASK 0x00000100
+#define BANK2_READ_EVENT2_CS_GET(x) (((x) & BANK2_READ_EVENT2_CS_MASK) >> BANK2_READ_EVENT2_CS_LSB)
+#define BANK2_READ_EVENT2_CS_SET(x) (((x) << BANK2_READ_EVENT2_CS_LSB) & BANK2_READ_EVENT2_CS_MASK)
+#define BANK2_READ_EVENT1_DC_MSB 7
+#define BANK2_READ_EVENT1_DC_LSB 7
+#define BANK2_READ_EVENT1_DC_MASK 0x00000080
+#define BANK2_READ_EVENT1_DC_GET(x) (((x) & BANK2_READ_EVENT1_DC_MASK) >> BANK2_READ_EVENT1_DC_LSB)
+#define BANK2_READ_EVENT1_DC_SET(x) (((x) << BANK2_READ_EVENT1_DC_LSB) & BANK2_READ_EVENT1_DC_MASK)
+#define BANK2_READ_EVENT1_BE_MSB 6
+#define BANK2_READ_EVENT1_BE_LSB 6
+#define BANK2_READ_EVENT1_BE_MASK 0x00000040
+#define BANK2_READ_EVENT1_BE_GET(x) (((x) & BANK2_READ_EVENT1_BE_MASK) >> BANK2_READ_EVENT1_BE_LSB)
+#define BANK2_READ_EVENT1_BE_SET(x) (((x) << BANK2_READ_EVENT1_BE_LSB) & BANK2_READ_EVENT1_BE_MASK)
+#define BANK2_READ_EVENT1_OE_MSB 5
+#define BANK2_READ_EVENT1_OE_LSB 5
+#define BANK2_READ_EVENT1_OE_MASK 0x00000020
+#define BANK2_READ_EVENT1_OE_GET(x) (((x) & BANK2_READ_EVENT1_OE_MASK) >> BANK2_READ_EVENT1_OE_LSB)
+#define BANK2_READ_EVENT1_OE_SET(x) (((x) << BANK2_READ_EVENT1_OE_LSB) & BANK2_READ_EVENT1_OE_MASK)
+#define BANK2_READ_EVENT1_CS_MSB 4
+#define BANK2_READ_EVENT1_CS_LSB 4
+#define BANK2_READ_EVENT1_CS_MASK 0x00000010
+#define BANK2_READ_EVENT1_CS_GET(x) (((x) & BANK2_READ_EVENT1_CS_MASK) >> BANK2_READ_EVENT1_CS_LSB)
+#define BANK2_READ_EVENT1_CS_SET(x) (((x) << BANK2_READ_EVENT1_CS_LSB) & BANK2_READ_EVENT1_CS_MASK)
+#define BANK2_READ_EVENT0_DC_MSB 3
+#define BANK2_READ_EVENT0_DC_LSB 3
+#define BANK2_READ_EVENT0_DC_MASK 0x00000008
+#define BANK2_READ_EVENT0_DC_GET(x) (((x) & BANK2_READ_EVENT0_DC_MASK) >> BANK2_READ_EVENT0_DC_LSB)
+#define BANK2_READ_EVENT0_DC_SET(x) (((x) << BANK2_READ_EVENT0_DC_LSB) & BANK2_READ_EVENT0_DC_MASK)
+#define BANK2_READ_EVENT0_BE_MSB 2
+#define BANK2_READ_EVENT0_BE_LSB 2
+#define BANK2_READ_EVENT0_BE_MASK 0x00000004
+#define BANK2_READ_EVENT0_BE_GET(x) (((x) & BANK2_READ_EVENT0_BE_MASK) >> BANK2_READ_EVENT0_BE_LSB)
+#define BANK2_READ_EVENT0_BE_SET(x) (((x) << BANK2_READ_EVENT0_BE_LSB) & BANK2_READ_EVENT0_BE_MASK)
+#define BANK2_READ_EVENT0_OE_MSB 1
+#define BANK2_READ_EVENT0_OE_LSB 1
+#define BANK2_READ_EVENT0_OE_MASK 0x00000002
+#define BANK2_READ_EVENT0_OE_GET(x) (((x) & BANK2_READ_EVENT0_OE_MASK) >> BANK2_READ_EVENT0_OE_LSB)
+#define BANK2_READ_EVENT0_OE_SET(x) (((x) << BANK2_READ_EVENT0_OE_LSB) & BANK2_READ_EVENT0_OE_MASK)
+#define BANK2_READ_EVENT0_CS_MSB 0
+#define BANK2_READ_EVENT0_CS_LSB 0
+#define BANK2_READ_EVENT0_CS_MASK 0x00000001
+#define BANK2_READ_EVENT0_CS_GET(x) (((x) & BANK2_READ_EVENT0_CS_MASK) >> BANK2_READ_EVENT0_CS_LSB)
+#define BANK2_READ_EVENT0_CS_SET(x) (((x) << BANK2_READ_EVENT0_CS_LSB) & BANK2_READ_EVENT0_CS_MASK)
+
+#define BANK2_WRITE_ADDRESS 0x0c00402c
+#define BANK2_WRITE_OFFSET 0x0000002c
+#define BANK2_WRITE_ENABLE_WAIT_MSB 31
+#define BANK2_WRITE_ENABLE_WAIT_LSB 31
+#define BANK2_WRITE_ENABLE_WAIT_MASK 0x80000000
+#define BANK2_WRITE_ENABLE_WAIT_GET(x) (((x) & BANK2_WRITE_ENABLE_WAIT_MASK) >> BANK2_WRITE_ENABLE_WAIT_LSB)
+#define BANK2_WRITE_ENABLE_WAIT_SET(x) (((x) << BANK2_WRITE_ENABLE_WAIT_LSB) & BANK2_WRITE_ENABLE_WAIT_MASK)
+#define BANK2_WRITE_WAIT_EVENT_MSB 30
+#define BANK2_WRITE_WAIT_EVENT_LSB 28
+#define BANK2_WRITE_WAIT_EVENT_MASK 0x70000000
+#define BANK2_WRITE_WAIT_EVENT_GET(x) (((x) & BANK2_WRITE_WAIT_EVENT_MASK) >> BANK2_WRITE_WAIT_EVENT_LSB)
+#define BANK2_WRITE_WAIT_EVENT_SET(x) (((x) << BANK2_WRITE_WAIT_EVENT_LSB) & BANK2_WRITE_WAIT_EVENT_MASK)
+#define BANK2_WRITE_END_EVENT_MSB 26
+#define BANK2_WRITE_END_EVENT_LSB 24
+#define BANK2_WRITE_END_EVENT_MASK 0x07000000
+#define BANK2_WRITE_END_EVENT_GET(x) (((x) & BANK2_WRITE_END_EVENT_MASK) >> BANK2_WRITE_END_EVENT_LSB)
+#define BANK2_WRITE_END_EVENT_SET(x) (((x) << BANK2_WRITE_END_EVENT_LSB) & BANK2_WRITE_END_EVENT_MASK)
+#define BANK2_WRITE_BURST_END_EVENT_MSB 22
+#define BANK2_WRITE_BURST_END_EVENT_LSB 20
+#define BANK2_WRITE_BURST_END_EVENT_MASK 0x00700000
+#define BANK2_WRITE_BURST_END_EVENT_GET(x) (((x) & BANK2_WRITE_BURST_END_EVENT_MASK) >> BANK2_WRITE_BURST_END_EVENT_LSB)
+#define BANK2_WRITE_BURST_END_EVENT_SET(x) (((x) << BANK2_WRITE_BURST_END_EVENT_LSB) & BANK2_WRITE_BURST_END_EVENT_MASK)
+#define BANK2_WRITE_BURST_START_EVENT_MSB 18
+#define BANK2_WRITE_BURST_START_EVENT_LSB 16
+#define BANK2_WRITE_BURST_START_EVENT_MASK 0x00070000
+#define BANK2_WRITE_BURST_START_EVENT_GET(x) (((x) & BANK2_WRITE_BURST_START_EVENT_MASK) >> BANK2_WRITE_BURST_START_EVENT_LSB)
+#define BANK2_WRITE_BURST_START_EVENT_SET(x) (((x) << BANK2_WRITE_BURST_START_EVENT_LSB) & BANK2_WRITE_BURST_START_EVENT_MASK)
+#define BANK2_WRITE_EVENT3_BE_MSB 14
+#define BANK2_WRITE_EVENT3_BE_LSB 14
+#define BANK2_WRITE_EVENT3_BE_MASK 0x00004000
+#define BANK2_WRITE_EVENT3_BE_GET(x) (((x) & BANK2_WRITE_EVENT3_BE_MASK) >> BANK2_WRITE_EVENT3_BE_LSB)
+#define BANK2_WRITE_EVENT3_BE_SET(x) (((x) << BANK2_WRITE_EVENT3_BE_LSB) & BANK2_WRITE_EVENT3_BE_MASK)
+#define BANK2_WRITE_EVENT3_WE_MSB 13
+#define BANK2_WRITE_EVENT3_WE_LSB 13
+#define BANK2_WRITE_EVENT3_WE_MASK 0x00002000
+#define BANK2_WRITE_EVENT3_WE_GET(x) (((x) & BANK2_WRITE_EVENT3_WE_MASK) >> BANK2_WRITE_EVENT3_WE_LSB)
+#define BANK2_WRITE_EVENT3_WE_SET(x) (((x) << BANK2_WRITE_EVENT3_WE_LSB) & BANK2_WRITE_EVENT3_WE_MASK)
+#define BANK2_WRITE_EVENT3_CS_MSB 12
+#define BANK2_WRITE_EVENT3_CS_LSB 12
+#define BANK2_WRITE_EVENT3_CS_MASK 0x00001000
+#define BANK2_WRITE_EVENT3_CS_GET(x) (((x) & BANK2_WRITE_EVENT3_CS_MASK) >> BANK2_WRITE_EVENT3_CS_LSB)
+#define BANK2_WRITE_EVENT3_CS_SET(x) (((x) << BANK2_WRITE_EVENT3_CS_LSB) & BANK2_WRITE_EVENT3_CS_MASK)
+#define BANK2_WRITE_EVENT2_BE_MSB 10
+#define BANK2_WRITE_EVENT2_BE_LSB 10
+#define BANK2_WRITE_EVENT2_BE_MASK 0x00000400
+#define BANK2_WRITE_EVENT2_BE_GET(x) (((x) & BANK2_WRITE_EVENT2_BE_MASK) >> BANK2_WRITE_EVENT2_BE_LSB)
+#define BANK2_WRITE_EVENT2_BE_SET(x) (((x) << BANK2_WRITE_EVENT2_BE_LSB) & BANK2_WRITE_EVENT2_BE_MASK)
+#define BANK2_WRITE_EVENT2_WE_MSB 9
+#define BANK2_WRITE_EVENT2_WE_LSB 9
+#define BANK2_WRITE_EVENT2_WE_MASK 0x00000200
+#define BANK2_WRITE_EVENT2_WE_GET(x) (((x) & BANK2_WRITE_EVENT2_WE_MASK) >> BANK2_WRITE_EVENT2_WE_LSB)
+#define BANK2_WRITE_EVENT2_WE_SET(x) (((x) << BANK2_WRITE_EVENT2_WE_LSB) & BANK2_WRITE_EVENT2_WE_MASK)
+#define BANK2_WRITE_EVENT2_CS_MSB 8
+#define BANK2_WRITE_EVENT2_CS_LSB 8
+#define BANK2_WRITE_EVENT2_CS_MASK 0x00000100
+#define BANK2_WRITE_EVENT2_CS_GET(x) (((x) & BANK2_WRITE_EVENT2_CS_MASK) >> BANK2_WRITE_EVENT2_CS_LSB)
+#define BANK2_WRITE_EVENT2_CS_SET(x) (((x) << BANK2_WRITE_EVENT2_CS_LSB) & BANK2_WRITE_EVENT2_CS_MASK)
+#define BANK2_WRITE_EVENT1_BE_MSB 6
+#define BANK2_WRITE_EVENT1_BE_LSB 6
+#define BANK2_WRITE_EVENT1_BE_MASK 0x00000040
+#define BANK2_WRITE_EVENT1_BE_GET(x) (((x) & BANK2_WRITE_EVENT1_BE_MASK) >> BANK2_WRITE_EVENT1_BE_LSB)
+#define BANK2_WRITE_EVENT1_BE_SET(x) (((x) << BANK2_WRITE_EVENT1_BE_LSB) & BANK2_WRITE_EVENT1_BE_MASK)
+#define BANK2_WRITE_EVENT1_WE_MSB 5
+#define BANK2_WRITE_EVENT1_WE_LSB 5
+#define BANK2_WRITE_EVENT1_WE_MASK 0x00000020
+#define BANK2_WRITE_EVENT1_WE_GET(x) (((x) & BANK2_WRITE_EVENT1_WE_MASK) >> BANK2_WRITE_EVENT1_WE_LSB)
+#define BANK2_WRITE_EVENT1_WE_SET(x) (((x) << BANK2_WRITE_EVENT1_WE_LSB) & BANK2_WRITE_EVENT1_WE_MASK)
+#define BANK2_WRITE_EVENT1_CS_MSB 4
+#define BANK2_WRITE_EVENT1_CS_LSB 4
+#define BANK2_WRITE_EVENT1_CS_MASK 0x00000010
+#define BANK2_WRITE_EVENT1_CS_GET(x) (((x) & BANK2_WRITE_EVENT1_CS_MASK) >> BANK2_WRITE_EVENT1_CS_LSB)
+#define BANK2_WRITE_EVENT1_CS_SET(x) (((x) << BANK2_WRITE_EVENT1_CS_LSB) & BANK2_WRITE_EVENT1_CS_MASK)
+#define BANK2_WRITE_EVENT0_BE_MSB 2
+#define BANK2_WRITE_EVENT0_BE_LSB 2
+#define BANK2_WRITE_EVENT0_BE_MASK 0x00000004
+#define BANK2_WRITE_EVENT0_BE_GET(x) (((x) & BANK2_WRITE_EVENT0_BE_MASK) >> BANK2_WRITE_EVENT0_BE_LSB)
+#define BANK2_WRITE_EVENT0_BE_SET(x) (((x) << BANK2_WRITE_EVENT0_BE_LSB) & BANK2_WRITE_EVENT0_BE_MASK)
+#define BANK2_WRITE_EVENT0_WE_MSB 1
+#define BANK2_WRITE_EVENT0_WE_LSB 1
+#define BANK2_WRITE_EVENT0_WE_MASK 0x00000002
+#define BANK2_WRITE_EVENT0_WE_GET(x) (((x) & BANK2_WRITE_EVENT0_WE_MASK) >> BANK2_WRITE_EVENT0_WE_LSB)
+#define BANK2_WRITE_EVENT0_WE_SET(x) (((x) << BANK2_WRITE_EVENT0_WE_LSB) & BANK2_WRITE_EVENT0_WE_MASK)
+#define BANK2_WRITE_EVENT0_CS_MSB 0
+#define BANK2_WRITE_EVENT0_CS_LSB 0
+#define BANK2_WRITE_EVENT0_CS_MASK 0x00000001
+#define BANK2_WRITE_EVENT0_CS_GET(x) (((x) & BANK2_WRITE_EVENT0_CS_MASK) >> BANK2_WRITE_EVENT0_CS_LSB)
+#define BANK2_WRITE_EVENT0_CS_SET(x) (((x) << BANK2_WRITE_EVENT0_CS_LSB) & BANK2_WRITE_EVENT0_CS_MASK)
+
+#define MC_REMAP_VALID_ADDRESS 0x0c004080
+#define MC_REMAP_VALID_OFFSET 0x00000080
+#define MC_REMAP_VALID_BIT_MSB 0
+#define MC_REMAP_VALID_BIT_LSB 0
+#define MC_REMAP_VALID_BIT_MASK 0x00000001
+#define MC_REMAP_VALID_BIT_GET(x) (((x) & MC_REMAP_VALID_BIT_MASK) >> MC_REMAP_VALID_BIT_LSB)
+#define MC_REMAP_VALID_BIT_SET(x) (((x) << MC_REMAP_VALID_BIT_LSB) & MC_REMAP_VALID_BIT_MASK)
+
+#define MC_REMAP_SIZE_ADDRESS 0x0c004100
+#define MC_REMAP_SIZE_OFFSET 0x00000100
+#define MC_REMAP_SIZE_VALUE_MSB 2
+#define MC_REMAP_SIZE_VALUE_LSB 0
+#define MC_REMAP_SIZE_VALUE_MASK 0x00000007
+#define MC_REMAP_SIZE_VALUE_GET(x) (((x) & MC_REMAP_SIZE_VALUE_MASK) >> MC_REMAP_SIZE_VALUE_LSB)
+#define MC_REMAP_SIZE_VALUE_SET(x) (((x) << MC_REMAP_SIZE_VALUE_LSB) & MC_REMAP_SIZE_VALUE_MASK)
+
+#define MC_REMAP_COMPARE_ADDRESS 0x0c004180
+#define MC_REMAP_COMPARE_OFFSET 0x00000180
+#define MC_REMAP_COMPARE_ADDRESS_MSB 17
+#define MC_REMAP_COMPARE_ADDRESS_LSB 4
+#define MC_REMAP_COMPARE_ADDRESS_MASK 0x0003fff0
+#define MC_REMAP_COMPARE_ADDRESS_GET(x) (((x) & MC_REMAP_COMPARE_ADDRESS_MASK) >> MC_REMAP_COMPARE_ADDRESS_LSB)
+#define MC_REMAP_COMPARE_ADDRESS_SET(x) (((x) << MC_REMAP_COMPARE_ADDRESS_LSB) & MC_REMAP_COMPARE_ADDRESS_MASK)
+
+#define MC_REMAP_TARGET_ADDRESS 0x0c004200
+#define MC_REMAP_TARGET_OFFSET 0x00000200
+#define MC_REMAP_TARGET_ADDRESS_MSB 16
+#define MC_REMAP_TARGET_ADDRESS_LSB 4
+#define MC_REMAP_TARGET_ADDRESS_MASK 0x0001fff0
+#define MC_REMAP_TARGET_ADDRESS_GET(x) (((x) & MC_REMAP_TARGET_ADDRESS_MASK) >> MC_REMAP_TARGET_ADDRESS_LSB)
+#define MC_REMAP_TARGET_ADDRESS_SET(x) (((x) << MC_REMAP_TARGET_ADDRESS_LSB) & MC_REMAP_TARGET_ADDRESS_MASK)
+
+#define G729_ROM_ADDRESS 0x0c004280
+#define G729_ROM_OFFSET 0x00000280
+#define G729_ROM_ENABLE_MSB 0
+#define G729_ROM_ENABLE_LSB 0
+#define G729_ROM_ENABLE_MASK 0x00000001
+#define G729_ROM_ENABLE_GET(x) (((x) & G729_ROM_ENABLE_MASK) >> G729_ROM_ENABLE_LSB)
+#define G729_ROM_ENABLE_SET(x) (((x) << G729_ROM_ENABLE_LSB) & G729_ROM_ENABLE_MASK)
+
+#define ERROR_VALID_ADDRESS 0x0c004284
+#define ERROR_VALID_OFFSET 0x00000284
+#define ERROR_VALID_ERROR_CAPTURE_ENABLE_MSB 8
+#define ERROR_VALID_ERROR_CAPTURE_ENABLE_LSB 8
+#define ERROR_VALID_ERROR_CAPTURE_ENABLE_MASK 0x00000100
+#define ERROR_VALID_ERROR_CAPTURE_ENABLE_GET(x) (((x) & ERROR_VALID_ERROR_CAPTURE_ENABLE_MASK) >> ERROR_VALID_ERROR_CAPTURE_ENABLE_LSB)
+#define ERROR_VALID_ERROR_CAPTURE_ENABLE_SET(x) (((x) << ERROR_VALID_ERROR_CAPTURE_ENABLE_LSB) & ERROR_VALID_ERROR_CAPTURE_ENABLE_MASK)
+#define ERROR_VALID_APB_ERROR_OVERFLOW_MSB 5
+#define ERROR_VALID_APB_ERROR_OVERFLOW_LSB 5
+#define ERROR_VALID_APB_ERROR_OVERFLOW_MASK 0x00000020
+#define ERROR_VALID_APB_ERROR_OVERFLOW_GET(x) (((x) & ERROR_VALID_APB_ERROR_OVERFLOW_MASK) >> ERROR_VALID_APB_ERROR_OVERFLOW_LSB)
+#define ERROR_VALID_APB_ERROR_OVERFLOW_SET(x) (((x) << ERROR_VALID_APB_ERROR_OVERFLOW_LSB) & ERROR_VALID_APB_ERROR_OVERFLOW_MASK)
+#define ERROR_VALID_APB_ERROR_VALID_MSB 4
+#define ERROR_VALID_APB_ERROR_VALID_LSB 4
+#define ERROR_VALID_APB_ERROR_VALID_MASK 0x00000010
+#define ERROR_VALID_APB_ERROR_VALID_GET(x) (((x) & ERROR_VALID_APB_ERROR_VALID_MASK) >> ERROR_VALID_APB_ERROR_VALID_LSB)
+#define ERROR_VALID_APB_ERROR_VALID_SET(x) (((x) << ERROR_VALID_APB_ERROR_VALID_LSB) & ERROR_VALID_APB_ERROR_VALID_MASK)
+#define ERROR_VALID_AHB_ERROR_OVERFLOW_MSB 1
+#define ERROR_VALID_AHB_ERROR_OVERFLOW_LSB 1
+#define ERROR_VALID_AHB_ERROR_OVERFLOW_MASK 0x00000002
+#define ERROR_VALID_AHB_ERROR_OVERFLOW_GET(x) (((x) & ERROR_VALID_AHB_ERROR_OVERFLOW_MASK) >> ERROR_VALID_AHB_ERROR_OVERFLOW_LSB)
+#define ERROR_VALID_AHB_ERROR_OVERFLOW_SET(x) (((x) << ERROR_VALID_AHB_ERROR_OVERFLOW_LSB) & ERROR_VALID_AHB_ERROR_OVERFLOW_MASK)
+#define ERROR_VALID_AHB_ERROR_VALID_MSB 0
+#define ERROR_VALID_AHB_ERROR_VALID_LSB 0
+#define ERROR_VALID_AHB_ERROR_VALID_MASK 0x00000001
+#define ERROR_VALID_AHB_ERROR_VALID_GET(x) (((x) & ERROR_VALID_AHB_ERROR_VALID_MASK) >> ERROR_VALID_AHB_ERROR_VALID_LSB)
+#define ERROR_VALID_AHB_ERROR_VALID_SET(x) (((x) << ERROR_VALID_AHB_ERROR_VALID_LSB) & ERROR_VALID_AHB_ERROR_VALID_MASK)
+
+#define AHB_ERROR_ADDRESS 0x0c004288
+#define AHB_ERROR_OFFSET 0x00000288
+#define AHB_ERROR_HMASTER_MSB 31
+#define AHB_ERROR_HMASTER_LSB 30
+#define AHB_ERROR_HMASTER_MASK 0xc0000000
+#define AHB_ERROR_HMASTER_GET(x) (((x) & AHB_ERROR_HMASTER_MASK) >> AHB_ERROR_HMASTER_LSB)
+#define AHB_ERROR_HMASTER_SET(x) (((x) << AHB_ERROR_HMASTER_LSB) & AHB_ERROR_HMASTER_MASK)
+#define AHB_ERROR_HTRANS_MSB 29
+#define AHB_ERROR_HTRANS_LSB 28
+#define AHB_ERROR_HTRANS_MASK 0x30000000
+#define AHB_ERROR_HTRANS_GET(x) (((x) & AHB_ERROR_HTRANS_MASK) >> AHB_ERROR_HTRANS_LSB)
+#define AHB_ERROR_HTRANS_SET(x) (((x) << AHB_ERROR_HTRANS_LSB) & AHB_ERROR_HTRANS_MASK)
+#define AHB_ERROR_HWRITE_MSB 27
+#define AHB_ERROR_HWRITE_LSB 27
+#define AHB_ERROR_HWRITE_MASK 0x08000000
+#define AHB_ERROR_HWRITE_GET(x) (((x) & AHB_ERROR_HWRITE_MASK) >> AHB_ERROR_HWRITE_LSB)
+#define AHB_ERROR_HWRITE_SET(x) (((x) << AHB_ERROR_HWRITE_LSB) & AHB_ERROR_HWRITE_MASK)
+#define AHB_ERROR_HBURST_MSB 26
+#define AHB_ERROR_HBURST_LSB 24
+#define AHB_ERROR_HBURST_MASK 0x07000000
+#define AHB_ERROR_HBURST_GET(x) (((x) & AHB_ERROR_HBURST_MASK) >> AHB_ERROR_HBURST_LSB)
+#define AHB_ERROR_HBURST_SET(x) (((x) << AHB_ERROR_HBURST_LSB) & AHB_ERROR_HBURST_MASK)
+#define AHB_ERROR_HADDR_MSB 23
+#define AHB_ERROR_HADDR_LSB 0
+#define AHB_ERROR_HADDR_MASK 0x00ffffff
+#define AHB_ERROR_HADDR_GET(x) (((x) & AHB_ERROR_HADDR_MASK) >> AHB_ERROR_HADDR_LSB)
+#define AHB_ERROR_HADDR_SET(x) (((x) << AHB_ERROR_HADDR_LSB) & AHB_ERROR_HADDR_MASK)
+
+#define APB_ERROR_ADDRESS 0x0c00428c
+#define APB_ERROR_OFFSET 0x0000028c
+#define APB_ERROR_PADDR_MSB 31
+#define APB_ERROR_PADDR_LSB 2
+#define APB_ERROR_PADDR_MASK 0xfffffffc
+#define APB_ERROR_PADDR_GET(x) (((x) & APB_ERROR_PADDR_MASK) >> APB_ERROR_PADDR_LSB)
+#define APB_ERROR_PADDR_SET(x) (((x) << APB_ERROR_PADDR_LSB) & APB_ERROR_PADDR_MASK)
+#define APB_ERROR_PVALID_MSB 1
+#define APB_ERROR_PVALID_LSB 1
+#define APB_ERROR_PVALID_MASK 0x00000002
+#define APB_ERROR_PVALID_GET(x) (((x) & APB_ERROR_PVALID_MASK) >> APB_ERROR_PVALID_LSB)
+#define APB_ERROR_PVALID_SET(x) (((x) << APB_ERROR_PVALID_LSB) & APB_ERROR_PVALID_MASK)
+#define APB_ERROR_PWRITE_MSB 0
+#define APB_ERROR_PWRITE_LSB 0
+#define APB_ERROR_PWRITE_MASK 0x00000001
+#define APB_ERROR_PWRITE_GET(x) (((x) & APB_ERROR_PWRITE_MASK) >> APB_ERROR_PWRITE_LSB)
+#define APB_ERROR_PWRITE_SET(x) (((x) << APB_ERROR_PWRITE_LSB) & APB_ERROR_PWRITE_MASK)
+
+#define PERF_CONFIG_ADDRESS 0x0c004290
+#define PERF_CONFIG_OFFSET 0x00000290
+#define PERF_CONFIG_ENABLE_MSB 20
+#define PERF_CONFIG_ENABLE_LSB 20
+#define PERF_CONFIG_ENABLE_MASK 0x00100000
+#define PERF_CONFIG_ENABLE_GET(x) (((x) & PERF_CONFIG_ENABLE_MASK) >> PERF_CONFIG_ENABLE_LSB)
+#define PERF_CONFIG_ENABLE_SET(x) (((x) << PERF_CONFIG_ENABLE_LSB) & PERF_CONFIG_ENABLE_MASK)
+#define PERF_CONFIG_RESET_MSB 19
+#define PERF_CONFIG_RESET_LSB 16
+#define PERF_CONFIG_RESET_MASK 0x000f0000
+#define PERF_CONFIG_RESET_GET(x) (((x) & PERF_CONFIG_RESET_MASK) >> PERF_CONFIG_RESET_LSB)
+#define PERF_CONFIG_RESET_SET(x) (((x) << PERF_CONFIG_RESET_LSB) & PERF_CONFIG_RESET_MASK)
+#define PERF_CONFIG_COUNTER3_MSB 15
+#define PERF_CONFIG_COUNTER3_LSB 12
+#define PERF_CONFIG_COUNTER3_MASK 0x0000f000
+#define PERF_CONFIG_COUNTER3_GET(x) (((x) & PERF_CONFIG_COUNTER3_MASK) >> PERF_CONFIG_COUNTER3_LSB)
+#define PERF_CONFIG_COUNTER3_SET(x) (((x) << PERF_CONFIG_COUNTER3_LSB) & PERF_CONFIG_COUNTER3_MASK)
+#define PERF_CONFIG_COUNTER2_MSB 11
+#define PERF_CONFIG_COUNTER2_LSB 8
+#define PERF_CONFIG_COUNTER2_MASK 0x00000f00
+#define PERF_CONFIG_COUNTER2_GET(x) (((x) & PERF_CONFIG_COUNTER2_MASK) >> PERF_CONFIG_COUNTER2_LSB)
+#define PERF_CONFIG_COUNTER2_SET(x) (((x) << PERF_CONFIG_COUNTER2_LSB) & PERF_CONFIG_COUNTER2_MASK)
+#define PERF_CONFIG_COUNTER1_MSB 7
+#define PERF_CONFIG_COUNTER1_LSB 4
+#define PERF_CONFIG_COUNTER1_MASK 0x000000f0
+#define PERF_CONFIG_COUNTER1_GET(x) (((x) & PERF_CONFIG_COUNTER1_MASK) >> PERF_CONFIG_COUNTER1_LSB)
+#define PERF_CONFIG_COUNTER1_SET(x) (((x) << PERF_CONFIG_COUNTER1_LSB) & PERF_CONFIG_COUNTER1_MASK)
+#define PERF_CONFIG_COUNTER0_MSB 3
+#define PERF_CONFIG_COUNTER0_LSB 0
+#define PERF_CONFIG_COUNTER0_MASK 0x0000000f
+#define PERF_CONFIG_COUNTER0_GET(x) (((x) & PERF_CONFIG_COUNTER0_MASK) >> PERF_CONFIG_COUNTER0_LSB)
+#define PERF_CONFIG_COUNTER0_SET(x) (((x) << PERF_CONFIG_COUNTER0_LSB) & PERF_CONFIG_COUNTER0_MASK)
+
+#define PERF_COUNTER_ADDRESS 0x0c0042a0
+#define PERF_COUNTER_OFFSET 0x000002a0
+#define PERF_COUNTER_VALUE_MSB 19
+#define PERF_COUNTER_VALUE_LSB 0
+#define PERF_COUNTER_VALUE_MASK 0x000fffff
+#define PERF_COUNTER_VALUE_GET(x) (((x) & PERF_COUNTER_VALUE_MASK) >> PERF_COUNTER_VALUE_LSB)
+#define PERF_COUNTER_VALUE_SET(x) (((x) << PERF_COUNTER_VALUE_LSB) & PERF_COUNTER_VALUE_MASK)
+
+#define CPU_SETUP_CONFIG_ADDRESS 0x0c0042b0
+#define CPU_SETUP_CONFIG_OFFSET 0x000002b0
+#define CPU_SETUP_CONFIG_ENABLE_MSB 1
+#define CPU_SETUP_CONFIG_ENABLE_LSB 1
+#define CPU_SETUP_CONFIG_ENABLE_MASK 0x00000002
+#define CPU_SETUP_CONFIG_ENABLE_GET(x) (((x) & CPU_SETUP_CONFIG_ENABLE_MASK) >> CPU_SETUP_CONFIG_ENABLE_LSB)
+#define CPU_SETUP_CONFIG_ENABLE_SET(x) (((x) << CPU_SETUP_CONFIG_ENABLE_LSB) & CPU_SETUP_CONFIG_ENABLE_MASK)
+#define CPU_SETUP_CONFIG_CLEAR_MSB 0
+#define CPU_SETUP_CONFIG_CLEAR_LSB 0
+#define CPU_SETUP_CONFIG_CLEAR_MASK 0x00000001
+#define CPU_SETUP_CONFIG_CLEAR_GET(x) (((x) & CPU_SETUP_CONFIG_CLEAR_MASK) >> CPU_SETUP_CONFIG_CLEAR_LSB)
+#define CPU_SETUP_CONFIG_CLEAR_SET(x) (((x) << CPU_SETUP_CONFIG_CLEAR_LSB) & CPU_SETUP_CONFIG_CLEAR_MASK)
+
+#define MC_SETUP_CONFIG_ADDRESS 0x0c0042b4
+#define MC_SETUP_CONFIG_OFFSET 0x000002b4
+#define MC_SETUP_CONFIG_ENABLE_MSB 1
+#define MC_SETUP_CONFIG_ENABLE_LSB 1
+#define MC_SETUP_CONFIG_ENABLE_MASK 0x00000002
+#define MC_SETUP_CONFIG_ENABLE_GET(x) (((x) & MC_SETUP_CONFIG_ENABLE_MASK) >> MC_SETUP_CONFIG_ENABLE_LSB)
+#define MC_SETUP_CONFIG_ENABLE_SET(x) (((x) << MC_SETUP_CONFIG_ENABLE_LSB) & MC_SETUP_CONFIG_ENABLE_MASK)
+#define MC_SETUP_CONFIG_CLEAR_MSB 0
+#define MC_SETUP_CONFIG_CLEAR_LSB 0
+#define MC_SETUP_CONFIG_CLEAR_MASK 0x00000001
+#define MC_SETUP_CONFIG_CLEAR_GET(x) (((x) & MC_SETUP_CONFIG_CLEAR_MASK) >> MC_SETUP_CONFIG_CLEAR_LSB)
+#define MC_SETUP_CONFIG_CLEAR_SET(x) (((x) << MC_SETUP_CONFIG_CLEAR_LSB) & MC_SETUP_CONFIG_CLEAR_MASK)
+
+#define BB_SETUP_CONFIG_ADDRESS 0x0c0042b8
+#define BB_SETUP_CONFIG_OFFSET 0x000002b8
+#define BB_SETUP_CONFIG_ENABLE_MSB 1
+#define BB_SETUP_CONFIG_ENABLE_LSB 1
+#define BB_SETUP_CONFIG_ENABLE_MASK 0x00000002
+#define BB_SETUP_CONFIG_ENABLE_GET(x) (((x) & BB_SETUP_CONFIG_ENABLE_MASK) >> BB_SETUP_CONFIG_ENABLE_LSB)
+#define BB_SETUP_CONFIG_ENABLE_SET(x) (((x) << BB_SETUP_CONFIG_ENABLE_LSB) & BB_SETUP_CONFIG_ENABLE_MASK)
+#define BB_SETUP_CONFIG_CLEAR_MSB 0
+#define BB_SETUP_CONFIG_CLEAR_LSB 0
+#define BB_SETUP_CONFIG_CLEAR_MASK 0x00000001
+#define BB_SETUP_CONFIG_CLEAR_GET(x) (((x) & BB_SETUP_CONFIG_CLEAR_MASK) >> BB_SETUP_CONFIG_CLEAR_LSB)
+#define BB_SETUP_CONFIG_CLEAR_SET(x) (((x) << BB_SETUP_CONFIG_CLEAR_LSB) & BB_SETUP_CONFIG_CLEAR_MASK)
+
+#define SDIO_SETUP_CONFIG_ADDRESS 0x0c0042bc
+#define SDIO_SETUP_CONFIG_OFFSET 0x000002bc
+#define SDIO_SETUP_CONFIG_ENABLE_MSB 1
+#define SDIO_SETUP_CONFIG_ENABLE_LSB 1
+#define SDIO_SETUP_CONFIG_ENABLE_MASK 0x00000002
+#define SDIO_SETUP_CONFIG_ENABLE_GET(x) (((x) & SDIO_SETUP_CONFIG_ENABLE_MASK) >> SDIO_SETUP_CONFIG_ENABLE_LSB)
+#define SDIO_SETUP_CONFIG_ENABLE_SET(x) (((x) << SDIO_SETUP_CONFIG_ENABLE_LSB) & SDIO_SETUP_CONFIG_ENABLE_MASK)
+#define SDIO_SETUP_CONFIG_CLEAR_MSB 0
+#define SDIO_SETUP_CONFIG_CLEAR_LSB 0
+#define SDIO_SETUP_CONFIG_CLEAR_MASK 0x00000001
+#define SDIO_SETUP_CONFIG_CLEAR_GET(x) (((x) & SDIO_SETUP_CONFIG_CLEAR_MASK) >> SDIO_SETUP_CONFIG_CLEAR_LSB)
+#define SDIO_SETUP_CONFIG_CLEAR_SET(x) (((x) << SDIO_SETUP_CONFIG_CLEAR_LSB) & SDIO_SETUP_CONFIG_CLEAR_MASK)
+
+#define CPU_SETUP_CIRCUIT_ADDRESS 0x0c0042c0
+#define CPU_SETUP_CIRCUIT_OFFSET 0x000002c0
+#define CPU_SETUP_CIRCUIT_VECTOR_MSB 7
+#define CPU_SETUP_CIRCUIT_VECTOR_LSB 0
+#define CPU_SETUP_CIRCUIT_VECTOR_MASK 0x000000ff
+#define CPU_SETUP_CIRCUIT_VECTOR_GET(x) (((x) & CPU_SETUP_CIRCUIT_VECTOR_MASK) >> CPU_SETUP_CIRCUIT_VECTOR_LSB)
+#define CPU_SETUP_CIRCUIT_VECTOR_SET(x) (((x) << CPU_SETUP_CIRCUIT_VECTOR_LSB) & CPU_SETUP_CIRCUIT_VECTOR_MASK)
+
+#define MC_SETUP_CIRCUIT_ADDRESS 0x0c0042e0
+#define MC_SETUP_CIRCUIT_OFFSET 0x000002e0
+#define MC_SETUP_CIRCUIT_VECTOR_MSB 7
+#define MC_SETUP_CIRCUIT_VECTOR_LSB 0
+#define MC_SETUP_CIRCUIT_VECTOR_MASK 0x000000ff
+#define MC_SETUP_CIRCUIT_VECTOR_GET(x) (((x) & MC_SETUP_CIRCUIT_VECTOR_MASK) >> MC_SETUP_CIRCUIT_VECTOR_LSB)
+#define MC_SETUP_CIRCUIT_VECTOR_SET(x) (((x) << MC_SETUP_CIRCUIT_VECTOR_LSB) & MC_SETUP_CIRCUIT_VECTOR_MASK)
+
+#define BB_SETUP_CIRCUIT_ADDRESS 0x0c004300
+#define BB_SETUP_CIRCUIT_OFFSET 0x00000300
+#define BB_SETUP_CIRCUIT_VECTOR_MSB 7
+#define BB_SETUP_CIRCUIT_VECTOR_LSB 0
+#define BB_SETUP_CIRCUIT_VECTOR_MASK 0x000000ff
+#define BB_SETUP_CIRCUIT_VECTOR_GET(x) (((x) & BB_SETUP_CIRCUIT_VECTOR_MASK) >> BB_SETUP_CIRCUIT_VECTOR_LSB)
+#define BB_SETUP_CIRCUIT_VECTOR_SET(x) (((x) << BB_SETUP_CIRCUIT_VECTOR_LSB) & BB_SETUP_CIRCUIT_VECTOR_MASK)
+
+#define SDIO_SETUP_CIRCUIT_ADDRESS 0x0c004320
+#define SDIO_SETUP_CIRCUIT_OFFSET 0x00000320
+#define SDIO_SETUP_CIRCUIT_VECTOR_MSB 7
+#define SDIO_SETUP_CIRCUIT_VECTOR_LSB 0
+#define SDIO_SETUP_CIRCUIT_VECTOR_MASK 0x000000ff
+#define SDIO_SETUP_CIRCUIT_VECTOR_GET(x) (((x) & SDIO_SETUP_CIRCUIT_VECTOR_MASK) >> SDIO_SETUP_CIRCUIT_VECTOR_LSB)
+#define SDIO_SETUP_CIRCUIT_VECTOR_SET(x) (((x) << SDIO_SETUP_CIRCUIT_VECTOR_LSB) & SDIO_SETUP_CIRCUIT_VECTOR_MASK)
+
+#define TIMING_SUMMARY_ADDRESS 0x0c004340
+#define TIMING_SUMMARY_OFFSET 0x00000340
+#define TIMING_SUMMARY_VECTOR_MSB 7
+#define TIMING_SUMMARY_VECTOR_LSB 0
+#define TIMING_SUMMARY_VECTOR_MASK 0x000000ff
+#define TIMING_SUMMARY_VECTOR_GET(x) (((x) & TIMING_SUMMARY_VECTOR_MASK) >> TIMING_SUMMARY_VECTOR_LSB)
+#define TIMING_SUMMARY_VECTOR_SET(x) (((x) << TIMING_SUMMARY_VECTOR_LSB) & TIMING_SUMMARY_VECTOR_MASK)
+
+#define TIMING_INT_ENABLE_ADDRESS 0x0c004344
+#define TIMING_INT_ENABLE_OFFSET 0x00000344
+#define TIMING_INT_ENABLE_VECTOR_MSB 7
+#define TIMING_INT_ENABLE_VECTOR_LSB 0
+#define TIMING_INT_ENABLE_VECTOR_MASK 0x000000ff
+#define TIMING_INT_ENABLE_VECTOR_GET(x) (((x) & TIMING_INT_ENABLE_VECTOR_MASK) >> TIMING_INT_ENABLE_VECTOR_LSB)
+#define TIMING_INT_ENABLE_VECTOR_SET(x) (((x) << TIMING_INT_ENABLE_VECTOR_LSB) & TIMING_INT_ENABLE_VECTOR_MASK)
+
+#define MC_ERROR_STATUS_ADDRESS 0x0c004348
+#define MC_ERROR_STATUS_OFFSET 0x00000348
+#define MC_ERROR_STATUS_AHB_MSB 1
+#define MC_ERROR_STATUS_AHB_LSB 1
+#define MC_ERROR_STATUS_AHB_MASK 0x00000002
+#define MC_ERROR_STATUS_AHB_GET(x) (((x) & MC_ERROR_STATUS_AHB_MASK) >> MC_ERROR_STATUS_AHB_LSB)
+#define MC_ERROR_STATUS_AHB_SET(x) (((x) << MC_ERROR_STATUS_AHB_LSB) & MC_ERROR_STATUS_AHB_MASK)
+#define MC_ERROR_STATUS_TIMING_MSB 0
+#define MC_ERROR_STATUS_TIMING_LSB 0
+#define MC_ERROR_STATUS_TIMING_MASK 0x00000001
+#define MC_ERROR_STATUS_TIMING_GET(x) (((x) & MC_ERROR_STATUS_TIMING_MASK) >> MC_ERROR_STATUS_TIMING_LSB)
+#define MC_ERROR_STATUS_TIMING_SET(x) (((x) << MC_ERROR_STATUS_TIMING_LSB) & MC_ERROR_STATUS_TIMING_MASK)
+
+#ifndef __ASSEMBLER__
+typedef struct mc_reg_s {
+ volatile unsigned int bank0_addr;
+ volatile unsigned int bank0_config;
+ volatile unsigned int bank0_read;
+ volatile unsigned int bank0_write;
+ volatile unsigned int bank1_addr;
+ volatile unsigned int bank1_config;
+ volatile unsigned int bank1_read;
+ volatile unsigned int bank1_write;
+ volatile unsigned int bank2_addr;
+ volatile unsigned int bank2_config;
+ volatile unsigned int bank2_read;
+ volatile unsigned int bank2_write;
+ unsigned char pad0[80]; /* pad to 0x80 */
+ volatile unsigned int mc_remap_valid[32];
+ volatile unsigned int mc_remap_size[32];
+ volatile unsigned int mc_remap_compare[32];
+ volatile unsigned int mc_remap_target[32];
+ volatile unsigned int g729_rom;
+ volatile unsigned int error_valid;
+ volatile unsigned int ahb_error;
+ volatile unsigned int apb_error;
+ volatile unsigned int perf_config;
+ unsigned char pad1[12]; /* pad to 0x2a0 */
+ volatile unsigned int perf_counter[4];
+ volatile unsigned int cpu_setup_config;
+ volatile unsigned int mc_setup_config;
+ volatile unsigned int bb_setup_config;
+ volatile unsigned int sdio_setup_config;
+ volatile unsigned int cpu_setup_circuit[8];
+ volatile unsigned int mc_setup_circuit[8];
+ volatile unsigned int bb_setup_circuit[8];
+ volatile unsigned int sdio_setup_circuit[8];
+ volatile unsigned int timing_summary;
+ volatile unsigned int timing_int_enable;
+ volatile unsigned int mc_error_status;
+} mc_reg_t;
+#endif /* __ASSEMBLER__ */
+
+#endif /* _MC_H_ */
Added: developers/nbd/ar6k/include/hw/rtc_reg.h
===================================================================
--- developers/nbd/ar6k/include/hw/rtc_reg.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/hw/rtc_reg.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,1204 @@
+/*
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+#ifndef _RTC_REG_H_
+#define _RTC_REG_H_
+
+#define RESET_CONTROL_ADDRESS 0x0c000000
+#define RESET_CONTROL_OFFSET 0x00000000
+#define RESET_CONTROL_RST_OUT_MSB 9
+#define RESET_CONTROL_RST_OUT_LSB 9
+#define RESET_CONTROL_RST_OUT_MASK 0x00000200
+#define RESET_CONTROL_RST_OUT_GET(x) (((x) & RESET_CONTROL_RST_OUT_MASK) >> RESET_CONTROL_RST_OUT_LSB)
+#define RESET_CONTROL_RST_OUT_SET(x) (((x) << RESET_CONTROL_RST_OUT_LSB) & RESET_CONTROL_RST_OUT_MASK)
+#define RESET_CONTROL_COLD_RST_MSB 8
+#define RESET_CONTROL_COLD_RST_LSB 8
+#define RESET_CONTROL_COLD_RST_MASK 0x00000100
+#define RESET_CONTROL_COLD_RST_GET(x) (((x) & RESET_CONTROL_COLD_RST_MASK) >> RESET_CONTROL_COLD_RST_LSB)
+#define RESET_CONTROL_COLD_RST_SET(x) (((x) << RESET_CONTROL_COLD_RST_LSB) & RESET_CONTROL_COLD_RST_MASK)
+#define RESET_CONTROL_WARM_RST_MSB 7
+#define RESET_CONTROL_WARM_RST_LSB 7
+#define RESET_CONTROL_WARM_RST_MASK 0x00000080
+#define RESET_CONTROL_WARM_RST_GET(x) (((x) & RESET_CONTROL_WARM_RST_MASK) >> RESET_CONTROL_WARM_RST_LSB)
+#define RESET_CONTROL_WARM_RST_SET(x) (((x) << RESET_CONTROL_WARM_RST_LSB) & RESET_CONTROL_WARM_RST_MASK)
+#define RESET_CONTROL_CPU_WARM_RST_MSB 6
+#define RESET_CONTROL_CPU_WARM_RST_LSB 6
+#define RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040
+#define RESET_CONTROL_CPU_WARM_RST_GET(x) (((x) & RESET_CONTROL_CPU_WARM_RST_MASK) >> RESET_CONTROL_CPU_WARM_RST_LSB)
+#define RESET_CONTROL_CPU_WARM_RST_SET(x) (((x) << RESET_CONTROL_CPU_WARM_RST_LSB) & RESET_CONTROL_CPU_WARM_RST_MASK)
+#define RESET_CONTROL_MAC_COLD_RST_MSB 5
+#define RESET_CONTROL_MAC_COLD_RST_LSB 5
+#define RESET_CONTROL_MAC_COLD_RST_MASK 0x00000020
+#define RESET_CONTROL_MAC_COLD_RST_GET(x) (((x) & RESET_CONTROL_MAC_COLD_RST_MASK) >> RESET_CONTROL_MAC_COLD_RST_LSB)
+#define RESET_CONTROL_MAC_COLD_RST_SET(x) (((x) << RESET_CONTROL_MAC_COLD_RST_LSB) & RESET_CONTROL_MAC_COLD_RST_MASK)
+#define RESET_CONTROL_MAC_WARM_RST_MSB 4
+#define RESET_CONTROL_MAC_WARM_RST_LSB 4
+#define RESET_CONTROL_MAC_WARM_RST_MASK 0x00000010
+#define RESET_CONTROL_MAC_WARM_RST_GET(x) (((x) & RESET_CONTROL_MAC_WARM_RST_MASK) >> RESET_CONTROL_MAC_WARM_RST_LSB)
+#define RESET_CONTROL_MAC_WARM_RST_SET(x) (((x) << RESET_CONTROL_MAC_WARM_RST_LSB) & RESET_CONTROL_MAC_WARM_RST_MASK)
+#define RESET_CONTROL_MBOX_RST_MSB 2
+#define RESET_CONTROL_MBOX_RST_LSB 2
+#define RESET_CONTROL_MBOX_RST_MASK 0x00000004
+#define RESET_CONTROL_MBOX_RST_GET(x) (((x) & RESET_CONTROL_MBOX_RST_MASK) >> RESET_CONTROL_MBOX_RST_LSB)
+#define RESET_CONTROL_MBOX_RST_SET(x) (((x) << RESET_CONTROL_MBOX_RST_LSB) & RESET_CONTROL_MBOX_RST_MASK)
+#define RESET_CONTROL_UART_RST_MSB 1
+#define RESET_CONTROL_UART_RST_LSB 1
+#define RESET_CONTROL_UART_RST_MASK 0x00000002
+#define RESET_CONTROL_UART_RST_GET(x) (((x) & RESET_CONTROL_UART_RST_MASK) >> RESET_CONTROL_UART_RST_LSB)
+#define RESET_CONTROL_UART_RST_SET(x) (((x) << RESET_CONTROL_UART_RST_LSB) & RESET_CONTROL_UART_RST_MASK)
+#define RESET_CONTROL_SI0_RST_MSB 0
+#define RESET_CONTROL_SI0_RST_LSB 0
+#define RESET_CONTROL_SI0_RST_MASK 0x00000001
+#define RESET_CONTROL_SI0_RST_GET(x) (((x) & RESET_CONTROL_SI0_RST_MASK) >> RESET_CONTROL_SI0_RST_LSB)
+#define RESET_CONTROL_SI0_RST_SET(x) (((x) << RESET_CONTROL_SI0_RST_LSB) & RESET_CONTROL_SI0_RST_MASK)
+
+#define XTAL_CONTROL_ADDRESS 0x0c000004
+#define XTAL_CONTROL_OFFSET 0x00000004
+#define XTAL_CONTROL_TCXO_MSB 0
+#define XTAL_CONTROL_TCXO_LSB 0
+#define XTAL_CONTROL_TCXO_MASK 0x00000001
+#define XTAL_CONTROL_TCXO_GET(x) (((x) & XTAL_CONTROL_TCXO_MASK) >> XTAL_CONTROL_TCXO_LSB)
+#define XTAL_CONTROL_TCXO_SET(x) (((x) << XTAL_CONTROL_TCXO_LSB) & XTAL_CONTROL_TCXO_MASK)
+
+#define TCXO_DETECT_ADDRESS 0x0c000008
+#define TCXO_DETECT_OFFSET 0x00000008
+#define TCXO_DETECT_PRESENT_MSB 0
+#define TCXO_DETECT_PRESENT_LSB 0
+#define TCXO_DETECT_PRESENT_MASK 0x00000001
+#define TCXO_DETECT_PRESENT_GET(x) (((x) & TCXO_DETECT_PRESENT_MASK) >> TCXO_DETECT_PRESENT_LSB)
+#define TCXO_DETECT_PRESENT_SET(x) (((x) << TCXO_DETECT_PRESENT_LSB) & TCXO_DETECT_PRESENT_MASK)
+
+#define XTAL_TEST_ADDRESS 0x0c00000c
+#define XTAL_TEST_OFFSET 0x0000000c
+#define XTAL_TEST_NOTCXODET_MSB 0
+#define XTAL_TEST_NOTCXODET_LSB 0
+#define XTAL_TEST_NOTCXODET_MASK 0x00000001
+#define XTAL_TEST_NOTCXODET_GET(x) (((x) & XTAL_TEST_NOTCXODET_MASK) >> XTAL_TEST_NOTCXODET_LSB)
+#define XTAL_TEST_NOTCXODET_SET(x) (((x) << XTAL_TEST_NOTCXODET_LSB) & XTAL_TEST_NOTCXODET_MASK)
+
+#define QUADRATURE_ADDRESS 0x0c000010
+#define QUADRATURE_OFFSET 0x00000010
+#define QUADRATURE_ADC_MSB 5
+#define QUADRATURE_ADC_LSB 4
+#define QUADRATURE_ADC_MASK 0x00000030
+#define QUADRATURE_ADC_GET(x) (((x) & QUADRATURE_ADC_MASK) >> QUADRATURE_ADC_LSB)
+#define QUADRATURE_ADC_SET(x) (((x) << QUADRATURE_ADC_LSB) & QUADRATURE_ADC_MASK)
+#define QUADRATURE_SEL_MSB 2
+#define QUADRATURE_SEL_LSB 2
+#define QUADRATURE_SEL_MASK 0x00000004
+#define QUADRATURE_SEL_GET(x) (((x) & QUADRATURE_SEL_MASK) >> QUADRATURE_SEL_LSB)
+#define QUADRATURE_SEL_SET(x) (((x) << QUADRATURE_SEL_LSB) & QUADRATURE_SEL_MASK)
+#define QUADRATURE_DAC_MSB 1
+#define QUADRATURE_DAC_LSB 0
+#define QUADRATURE_DAC_MASK 0x00000003
+#define QUADRATURE_DAC_GET(x) (((x) & QUADRATURE_DAC_MASK) >> QUADRATURE_DAC_LSB)
+#define QUADRATURE_DAC_SET(x) (((x) << QUADRATURE_DAC_LSB) & QUADRATURE_DAC_MASK)
+
+#define PLL_CONTROL_ADDRESS 0x0c000014
+#define PLL_CONTROL_OFFSET 0x00000014
+#define PLL_CONTROL_DIG_TEST_CLK_MSB 20
+#define PLL_CONTROL_DIG_TEST_CLK_LSB 20
+#define PLL_CONTROL_DIG_TEST_CLK_MASK 0x00100000
+#define PLL_CONTROL_DIG_TEST_CLK_GET(x) (((x) & PLL_CONTROL_DIG_TEST_CLK_MASK) >> PLL_CONTROL_DIG_TEST_CLK_LSB)
+#define PLL_CONTROL_DIG_TEST_CLK_SET(x) (((x) << PLL_CONTROL_DIG_TEST_CLK_LSB) & PLL_CONTROL_DIG_TEST_CLK_MASK)
+#define PLL_CONTROL_MAC_OVERRIDE_MSB 19
+#define PLL_CONTROL_MAC_OVERRIDE_LSB 19
+#define PLL_CONTROL_MAC_OVERRIDE_MASK 0x00080000
+#define PLL_CONTROL_MAC_OVERRIDE_GET(x) (((x) & PLL_CONTROL_MAC_OVERRIDE_MASK) >> PLL_CONTROL_MAC_OVERRIDE_LSB)
+#define PLL_CONTROL_MAC_OVERRIDE_SET(x) (((x) << PLL_CONTROL_MAC_OVERRIDE_LSB) & PLL_CONTROL_MAC_OVERRIDE_MASK)
+#define PLL_CONTROL_NOPWD_MSB 18
+#define PLL_CONTROL_NOPWD_LSB 18
+#define PLL_CONTROL_NOPWD_MASK 0x00040000
+#define PLL_CONTROL_NOPWD_GET(x) (((x) & PLL_CONTROL_NOPWD_MASK) >> PLL_CONTROL_NOPWD_LSB)
+#define PLL_CONTROL_NOPWD_SET(x) (((x) << PLL_CONTROL_NOPWD_LSB) & PLL_CONTROL_NOPWD_MASK)
+#define PLL_CONTROL_UPDATING_MSB 17
+#define PLL_CONTROL_UPDATING_LSB 17
+#define PLL_CONTROL_UPDATING_MASK 0x00020000
+#define PLL_CONTROL_UPDATING_GET(x) (((x) & PLL_CONTROL_UPDATING_MASK) >> PLL_CONTROL_UPDATING_LSB)
+#define PLL_CONTROL_UPDATING_SET(x) (((x) << PLL_CONTROL_UPDATING_LSB) & PLL_CONTROL_UPDATING_MASK)
+#define PLL_CONTROL_BYPASS_MSB 16
+#define PLL_CONTROL_BYPASS_LSB 16
+#define PLL_CONTROL_BYPASS_MASK 0x00010000
+#define PLL_CONTROL_BYPASS_GET(x) (((x) & PLL_CONTROL_BYPASS_MASK) >> PLL_CONTROL_BYPASS_LSB)
+#define PLL_CONTROL_BYPASS_SET(x) (((x) << PLL_CONTROL_BYPASS_LSB) & PLL_CONTROL_BYPASS_MASK)
+#define PLL_CONTROL_REFDIV_MSB 15
+#define PLL_CONTROL_REFDIV_LSB 12
+#define PLL_CONTROL_REFDIV_MASK 0x0000f000
+#define PLL_CONTROL_REFDIV_GET(x) (((x) & PLL_CONTROL_REFDIV_MASK) >> PLL_CONTROL_REFDIV_LSB)
+#define PLL_CONTROL_REFDIV_SET(x) (((x) << PLL_CONTROL_REFDIV_LSB) & PLL_CONTROL_REFDIV_MASK)
+#define PLL_CONTROL_DIV_MSB 9
+#define PLL_CONTROL_DIV_LSB 0
+#define PLL_CONTROL_DIV_MASK 0x000003ff
+#define PLL_CONTROL_DIV_GET(x) (((x) & PLL_CONTROL_DIV_MASK) >> PLL_CONTROL_DIV_LSB)
+#define PLL_CONTROL_DIV_SET(x) (((x) << PLL_CONTROL_DIV_LSB) & PLL_CONTROL_DIV_MASK)
+
+#define PLL_SETTLE_ADDRESS 0x0c000018
+#define PLL_SETTLE_OFFSET 0x00000018
+#define PLL_SETTLE_TIME_MSB 10
+#define PLL_SETTLE_TIME_LSB 0
+#define PLL_SETTLE_TIME_MASK 0x000007ff
+#define PLL_SETTLE_TIME_GET(x) (((x) & PLL_SETTLE_TIME_MASK) >> PLL_SETTLE_TIME_LSB)
+#define PLL_SETTLE_TIME_SET(x) (((x) << PLL_SETTLE_TIME_LSB) & PLL_SETTLE_TIME_MASK)
+
+#define XTAL_SETTLE_ADDRESS 0x0c00001c
+#define XTAL_SETTLE_OFFSET 0x0000001c
+#define XTAL_SETTLE_TIME_MSB 6
+#define XTAL_SETTLE_TIME_LSB 0
+#define XTAL_SETTLE_TIME_MASK 0x0000007f
+#define XTAL_SETTLE_TIME_GET(x) (((x) & XTAL_SETTLE_TIME_MASK) >> XTAL_SETTLE_TIME_LSB)
+#define XTAL_SETTLE_TIME_SET(x) (((x) << XTAL_SETTLE_TIME_LSB) & XTAL_SETTLE_TIME_MASK)
+
+#define CORE_CLOCK_ADDRESS 0x0c000020
+#define CORE_CLOCK_OFFSET 0x00000020
+#define CORE_CLOCK_DIG_TEST_MSB 12
+#define CORE_CLOCK_DIG_TEST_LSB 12
+#define CORE_CLOCK_DIG_TEST_MASK 0x00001000
+#define CORE_CLOCK_DIG_TEST_GET(x) (((x) & CORE_CLOCK_DIG_TEST_MASK) >> CORE_CLOCK_DIG_TEST_LSB)
+#define CORE_CLOCK_DIG_TEST_SET(x) (((x) << CORE_CLOCK_DIG_TEST_LSB) & CORE_CLOCK_DIG_TEST_MASK)
+#define CORE_CLOCK_STANDARD_MSB 9
+#define CORE_CLOCK_STANDARD_LSB 8
+#define CORE_CLOCK_STANDARD_MASK 0x00000300
+#define CORE_CLOCK_STANDARD_GET(x) (((x) & CORE_CLOCK_STANDARD_MASK) >> CORE_CLOCK_STANDARD_LSB)
+#define CORE_CLOCK_STANDARD_SET(x) (((x) << CORE_CLOCK_STANDARD_LSB) & CORE_CLOCK_STANDARD_MASK)
+#define CORE_CLOCK_REDUCED_MSB 1
+#define CORE_CLOCK_REDUCED_LSB 0
+#define CORE_CLOCK_REDUCED_MASK 0x00000003
+#define CORE_CLOCK_REDUCED_GET(x) (((x) & CORE_CLOCK_REDUCED_MASK) >> CORE_CLOCK_REDUCED_LSB)
+#define CORE_CLOCK_REDUCED_SET(x) (((x) << CORE_CLOCK_REDUCED_LSB) & CORE_CLOCK_REDUCED_MASK)
+
+#define CPU_CLOCK_ADDRESS 0x0c000024
+#define CPU_CLOCK_OFFSET 0x00000024
+#define CPU_CLOCK_DISABLE_SYNC_MSB 12
+#define CPU_CLOCK_DISABLE_SYNC_LSB 12
+#define CPU_CLOCK_DISABLE_SYNC_MASK 0x00001000
+#define CPU_CLOCK_DISABLE_SYNC_GET(x) (((x) & CPU_CLOCK_DISABLE_SYNC_MASK) >> CPU_CLOCK_DISABLE_SYNC_LSB)
+#define CPU_CLOCK_DISABLE_SYNC_SET(x) (((x) << CPU_CLOCK_DISABLE_SYNC_LSB) & CPU_CLOCK_DISABLE_SYNC_MASK)
+#define CPU_CLOCK_STANDARD_MSB 9
+#define CPU_CLOCK_STANDARD_LSB 8
+#define CPU_CLOCK_STANDARD_MASK 0x00000300
+#define CPU_CLOCK_STANDARD_GET(x) (((x) & CPU_CLOCK_STANDARD_MASK) >> CPU_CLOCK_STANDARD_LSB)
+#define CPU_CLOCK_STANDARD_SET(x) (((x) << CPU_CLOCK_STANDARD_LSB) & CPU_CLOCK_STANDARD_MASK)
+#define CPU_CLOCK_REDUCED_MSB 1
+#define CPU_CLOCK_REDUCED_LSB 0
+#define CPU_CLOCK_REDUCED_MASK 0x00000003
+#define CPU_CLOCK_REDUCED_GET(x) (((x) & CPU_CLOCK_REDUCED_MASK) >> CPU_CLOCK_REDUCED_LSB)
+#define CPU_CLOCK_REDUCED_SET(x) (((x) << CPU_CLOCK_REDUCED_LSB) & CPU_CLOCK_REDUCED_MASK)
+
+#define CLOCK_OUT_ADDRESS 0x0c000028
+#define CLOCK_OUT_OFFSET 0x00000028
+#define CLOCK_OUT_SELECT_MSB 3
+#define CLOCK_OUT_SELECT_LSB 0
+#define CLOCK_OUT_SELECT_MASK 0x0000000f
+#define CLOCK_OUT_SELECT_GET(x) (((x) & CLOCK_OUT_SELECT_MASK) >> CLOCK_OUT_SELECT_LSB)
+#define CLOCK_OUT_SELECT_SET(x) (((x) << CLOCK_OUT_SELECT_LSB) & CLOCK_OUT_SELECT_MASK)
+
+#define CLOCK_CONTROL_ADDRESS 0x0c00002c
+#define CLOCK_CONTROL_OFFSET 0x0000002c
+#define CLOCK_CONTROL_UART_CLK_MSB 1
+#define CLOCK_CONTROL_UART_CLK_LSB 1
+#define CLOCK_CONTROL_UART_CLK_MASK 0x00000002
+#define CLOCK_CONTROL_UART_CLK_GET(x) (((x) & CLOCK_CONTROL_UART_CLK_MASK) >> CLOCK_CONTROL_UART_CLK_LSB)
+#define CLOCK_CONTROL_UART_CLK_SET(x) (((x) << CLOCK_CONTROL_UART_CLK_LSB) & CLOCK_CONTROL_UART_CLK_MASK)
+#define CLOCK_CONTROL_SI0_CLK_MSB 0
+#define CLOCK_CONTROL_SI0_CLK_LSB 0
+#define CLOCK_CONTROL_SI0_CLK_MASK 0x00000001
+#define CLOCK_CONTROL_SI0_CLK_GET(x) (((x) & CLOCK_CONTROL_SI0_CLK_MASK) >> CLOCK_CONTROL_SI0_CLK_LSB)
+#define CLOCK_CONTROL_SI0_CLK_SET(x) (((x) << CLOCK_CONTROL_SI0_CLK_LSB) & CLOCK_CONTROL_SI0_CLK_MASK)
+
+#define BIAS_OVERRIDE_ADDRESS 0x0c000030
+#define BIAS_OVERRIDE_OFFSET 0x00000030
+#define BIAS_OVERRIDE_ON_MSB 0
+#define BIAS_OVERRIDE_ON_LSB 0
+#define BIAS_OVERRIDE_ON_MASK 0x00000001
+#define BIAS_OVERRIDE_ON_GET(x) (((x) & BIAS_OVERRIDE_ON_MASK) >> BIAS_OVERRIDE_ON_LSB)
+#define BIAS_OVERRIDE_ON_SET(x) (((x) << BIAS_OVERRIDE_ON_LSB) & BIAS_OVERRIDE_ON_MASK)
+
+#define REF_VOLTAGE_TRIM_ADDRESS 0x0c000034
+#define REF_VOLTAGE_TRIM_OFFSET 0x00000034
+#define REF_VOLTAGE_TRIM_REFSEL_MSB 3
+#define REF_VOLTAGE_TRIM_REFSEL_LSB 0
+#define REF_VOLTAGE_TRIM_REFSEL_MASK 0x0000000f
+#define REF_VOLTAGE_TRIM_REFSEL_GET(x) (((x) & REF_VOLTAGE_TRIM_REFSEL_MASK) >> REF_VOLTAGE_TRIM_REFSEL_LSB)
+#define REF_VOLTAGE_TRIM_REFSEL_SET(x) (((x) << REF_VOLTAGE_TRIM_REFSEL_LSB) & REF_VOLTAGE_TRIM_REFSEL_MASK)
+
+#define LDO_CONTROL_ADDRESS 0x0c000038
+#define LDO_CONTROL_OFFSET 0x00000038
+#define LDO_CONTROL_CORE_LIMIT_OFF_MSB 14
+#define LDO_CONTROL_CORE_LIMIT_OFF_LSB 14
+#define LDO_CONTROL_CORE_LIMIT_OFF_MASK 0x00004000
+#define LDO_CONTROL_CORE_LIMIT_OFF_GET(x) (((x) & LDO_CONTROL_CORE_LIMIT_OFF_MASK) >> LDO_CONTROL_CORE_LIMIT_OFF_LSB)
+#define LDO_CONTROL_CORE_LIMIT_OFF_SET(x) (((x) << LDO_CONTROL_CORE_LIMIT_OFF_LSB) & LDO_CONTROL_CORE_LIMIT_OFF_MASK)
+#define LDO_CONTROL_CORE_LIMIT_MSB 13
+#define LDO_CONTROL_CORE_LIMIT_LSB 11
+#define LDO_CONTROL_CORE_LIMIT_MASK 0x00003800
+#define LDO_CONTROL_CORE_LIMIT_GET(x) (((x) & LDO_CONTROL_CORE_LIMIT_MASK) >> LDO_CONTROL_CORE_LIMIT_LSB)
+#define LDO_CONTROL_CORE_LIMIT_SET(x) (((x) << LDO_CONTROL_CORE_LIMIT_LSB) & LDO_CONTROL_CORE_LIMIT_MASK)
+#define LDO_CONTROL_CORE_REG_Z_MSB 10
+#define LDO_CONTROL_CORE_REG_Z_LSB 8
+#define LDO_CONTROL_CORE_REG_Z_MASK 0x00000700
+#define LDO_CONTROL_CORE_REG_Z_GET(x) (((x) & LDO_CONTROL_CORE_REG_Z_MASK) >> LDO_CONTROL_CORE_REG_Z_LSB)
+#define LDO_CONTROL_CORE_REG_Z_SET(x) (((x) << LDO_CONTROL_CORE_REG_Z_LSB) & LDO_CONTROL_CORE_REG_Z_MASK)
+#define LDO_CONTROL_RADIO_LIMIT_OFF_MSB 6
+#define LDO_CONTROL_RADIO_LIMIT_OFF_LSB 6
+#define LDO_CONTROL_RADIO_LIMIT_OFF_MASK 0x00000040
+#define LDO_CONTROL_RADIO_LIMIT_OFF_GET(x) (((x) & LDO_CONTROL_RADIO_LIMIT_OFF_MASK) >> LDO_CONTROL_RADIO_LIMIT_OFF_LSB)
+#define LDO_CONTROL_RADIO_LIMIT_OFF_SET(x) (((x) << LDO_CONTROL_RADIO_LIMIT_OFF_LSB) & LDO_CONTROL_RADIO_LIMIT_OFF_MASK)
+#define LDO_CONTROL_RADIO_LIMIT_MSB 5
+#define LDO_CONTROL_RADIO_LIMIT_LSB 3
+#define LDO_CONTROL_RADIO_LIMIT_MASK 0x00000038
+#define LDO_CONTROL_RADIO_LIMIT_GET(x) (((x) & LDO_CONTROL_RADIO_LIMIT_MASK) >> LDO_CONTROL_RADIO_LIMIT_LSB)
+#define LDO_CONTROL_RADIO_LIMIT_SET(x) (((x) << LDO_CONTROL_RADIO_LIMIT_LSB) & LDO_CONTROL_RADIO_LIMIT_MASK)
+#define LDO_CONTROL_RADIO_REG_Z_MSB 2
+#define LDO_CONTROL_RADIO_REG_Z_LSB 0
+#define LDO_CONTROL_RADIO_REG_Z_MASK 0x00000007
+#define LDO_CONTROL_RADIO_REG_Z_GET(x) (((x) & LDO_CONTROL_RADIO_REG_Z_MASK) >> LDO_CONTROL_RADIO_REG_Z_LSB)
+#define LDO_CONTROL_RADIO_REG_Z_SET(x) (((x) << LDO_CONTROL_RADIO_REG_Z_LSB) & LDO_CONTROL_RADIO_REG_Z_MASK)
+
+#define WDT_CONTROL_ADDRESS 0x0c00003c
+#define WDT_CONTROL_OFFSET 0x0000003c
+#define WDT_CONTROL_ACTION_MSB 2
+#define WDT_CONTROL_ACTION_LSB 0
+#define WDT_CONTROL_ACTION_MASK 0x00000007
+#define WDT_CONTROL_ACTION_GET(x) (((x) & WDT_CONTROL_ACTION_MASK) >> WDT_CONTROL_ACTION_LSB)
+#define WDT_CONTROL_ACTION_SET(x) (((x) << WDT_CONTROL_ACTION_LSB) & WDT_CONTROL_ACTION_MASK)
+
+#define WDT_STATUS_ADDRESS 0x0c000040
+#define WDT_STATUS_OFFSET 0x00000040
+#define WDT_STATUS_INTERRUPT_MSB 0
+#define WDT_STATUS_INTERRUPT_LSB 0
+#define WDT_STATUS_INTERRUPT_MASK 0x00000001
+#define WDT_STATUS_INTERRUPT_GET(x) (((x) & WDT_STATUS_INTERRUPT_MASK) >> WDT_STATUS_INTERRUPT_LSB)
+#define WDT_STATUS_INTERRUPT_SET(x) (((x) << WDT_STATUS_INTERRUPT_LSB) & WDT_STATUS_INTERRUPT_MASK)
+
+#define WDT_ADDRESS 0x0c000044
+#define WDT_OFFSET 0x00000044
+#define WDT_TARGET_MSB 21
+#define WDT_TARGET_LSB 0
+#define WDT_TARGET_MASK 0x003fffff
+#define WDT_TARGET_GET(x) (((x) & WDT_TARGET_MASK) >> WDT_TARGET_LSB)
+#define WDT_TARGET_SET(x) (((x) << WDT_TARGET_LSB) & WDT_TARGET_MASK)
+
+#define WDT_COUNT_ADDRESS 0x0c000048
+#define WDT_COUNT_OFFSET 0x00000048
+#define WDT_COUNT_VALUE_MSB 21
+#define WDT_COUNT_VALUE_LSB 0
+#define WDT_COUNT_VALUE_MASK 0x003fffff
+#define WDT_COUNT_VALUE_GET(x) (((x) & WDT_COUNT_VALUE_MASK) >> WDT_COUNT_VALUE_LSB)
+#define WDT_COUNT_VALUE_SET(x) (((x) << WDT_COUNT_VALUE_LSB) & WDT_COUNT_VALUE_MASK)
+
+#define WDT_RESET_ADDRESS 0x0c00004c
+#define WDT_RESET_OFFSET 0x0000004c
+#define WDT_RESET_VALUE_MSB 0
+#define WDT_RESET_VALUE_LSB 0
+#define WDT_RESET_VALUE_MASK 0x00000001
+#define WDT_RESET_VALUE_GET(x) (((x) & WDT_RESET_VALUE_MASK) >> WDT_RESET_VALUE_LSB)
+#define WDT_RESET_VALUE_SET(x) (((x) << WDT_RESET_VALUE_LSB) & WDT_RESET_VALUE_MASK)
+
+#define INT_STATUS_ADDRESS 0x0c000050
+#define INT_STATUS_OFFSET 0x00000050
+#define INT_STATUS_TIMER_MSB 14
+#define INT_STATUS_TIMER_LSB 14
+#define INT_STATUS_TIMER_MASK 0x00004000
+#define INT_STATUS_TIMER_GET(x) (((x) & INT_STATUS_TIMER_MASK) >> INT_STATUS_TIMER_LSB)
+#define INT_STATUS_TIMER_SET(x) (((x) << INT_STATUS_TIMER_LSB) & INT_STATUS_TIMER_MASK)
+#define INT_STATUS_MAC_MSB 13
+#define INT_STATUS_MAC_LSB 13
+#define INT_STATUS_MAC_MASK 0x00002000
+#define INT_STATUS_MAC_GET(x) (((x) & INT_STATUS_MAC_MASK) >> INT_STATUS_MAC_LSB)
+#define INT_STATUS_MAC_SET(x) (((x) << INT_STATUS_MAC_LSB) & INT_STATUS_MAC_MASK)
+#define INT_STATUS_MAILBOX_MSB 12
+#define INT_STATUS_MAILBOX_LSB 12
+#define INT_STATUS_MAILBOX_MASK 0x00001000
+#define INT_STATUS_MAILBOX_GET(x) (((x) & INT_STATUS_MAILBOX_MASK) >> INT_STATUS_MAILBOX_LSB)
+#define INT_STATUS_MAILBOX_SET(x) (((x) << INT_STATUS_MAILBOX_LSB) & INT_STATUS_MAILBOX_MASK)
+#define INT_STATUS_RTC_ALARM_MSB 11
+#define INT_STATUS_RTC_ALARM_LSB 11
+#define INT_STATUS_RTC_ALARM_MASK 0x00000800
+#define INT_STATUS_RTC_ALARM_GET(x) (((x) & INT_STATUS_RTC_ALARM_MASK) >> INT_STATUS_RTC_ALARM_LSB)
+#define INT_STATUS_RTC_ALARM_SET(x) (((x) << INT_STATUS_RTC_ALARM_LSB) & INT_STATUS_RTC_ALARM_MASK)
+#define INT_STATUS_HF_TIMER_MSB 10
+#define INT_STATUS_HF_TIMER_LSB 10
+#define INT_STATUS_HF_TIMER_MASK 0x00000400
+#define INT_STATUS_HF_TIMER_GET(x) (((x) & INT_STATUS_HF_TIMER_MASK) >> INT_STATUS_HF_TIMER_LSB)
+#define INT_STATUS_HF_TIMER_SET(x) (((x) << INT_STATUS_HF_TIMER_LSB) & INT_STATUS_HF_TIMER_MASK)
+#define INT_STATUS_LF_TIMER3_MSB 9
+#define INT_STATUS_LF_TIMER3_LSB 9
+#define INT_STATUS_LF_TIMER3_MASK 0x00000200
+#define INT_STATUS_LF_TIMER3_GET(x) (((x) & INT_STATUS_LF_TIMER3_MASK) >> INT_STATUS_LF_TIMER3_LSB)
+#define INT_STATUS_LF_TIMER3_SET(x) (((x) << INT_STATUS_LF_TIMER3_LSB) & INT_STATUS_LF_TIMER3_MASK)
+#define INT_STATUS_LF_TIMER2_MSB 8
+#define INT_STATUS_LF_TIMER2_LSB 8
+#define INT_STATUS_LF_TIMER2_MASK 0x00000100
+#define INT_STATUS_LF_TIMER2_GET(x) (((x) & INT_STATUS_LF_TIMER2_MASK) >> INT_STATUS_LF_TIMER2_LSB)
+#define INT_STATUS_LF_TIMER2_SET(x) (((x) << INT_STATUS_LF_TIMER2_LSB) & INT_STATUS_LF_TIMER2_MASK)
+#define INT_STATUS_LF_TIMER1_MSB 7
+#define INT_STATUS_LF_TIMER1_LSB 7
+#define INT_STATUS_LF_TIMER1_MASK 0x00000080
+#define INT_STATUS_LF_TIMER1_GET(x) (((x) & INT_STATUS_LF_TIMER1_MASK) >> INT_STATUS_LF_TIMER1_LSB)
+#define INT_STATUS_LF_TIMER1_SET(x) (((x) << INT_STATUS_LF_TIMER1_LSB) & INT_STATUS_LF_TIMER1_MASK)
+#define INT_STATUS_LF_TIMER0_MSB 6
+#define INT_STATUS_LF_TIMER0_LSB 6
+#define INT_STATUS_LF_TIMER0_MASK 0x00000040
+#define INT_STATUS_LF_TIMER0_GET(x) (((x) & INT_STATUS_LF_TIMER0_MASK) >> INT_STATUS_LF_TIMER0_LSB)
+#define INT_STATUS_LF_TIMER0_SET(x) (((x) << INT_STATUS_LF_TIMER0_LSB) & INT_STATUS_LF_TIMER0_MASK)
+#define INT_STATUS_KEYPAD_MSB 5
+#define INT_STATUS_KEYPAD_LSB 5
+#define INT_STATUS_KEYPAD_MASK 0x00000020
+#define INT_STATUS_KEYPAD_GET(x) (((x) & INT_STATUS_KEYPAD_MASK) >> INT_STATUS_KEYPAD_LSB)
+#define INT_STATUS_KEYPAD_SET(x) (((x) << INT_STATUS_KEYPAD_LSB) & INT_STATUS_KEYPAD_MASK)
+#define INT_STATUS_SI_MSB 4
+#define INT_STATUS_SI_LSB 4
+#define INT_STATUS_SI_MASK 0x00000010
+#define INT_STATUS_SI_GET(x) (((x) & INT_STATUS_SI_MASK) >> INT_STATUS_SI_LSB)
+#define INT_STATUS_SI_SET(x) (((x) << INT_STATUS_SI_LSB) & INT_STATUS_SI_MASK)
+#define INT_STATUS_GPIO_MSB 3
+#define INT_STATUS_GPIO_LSB 3
+#define INT_STATUS_GPIO_MASK 0x00000008
+#define INT_STATUS_GPIO_GET(x) (((x) & INT_STATUS_GPIO_MASK) >> INT_STATUS_GPIO_LSB)
+#define INT_STATUS_GPIO_SET(x) (((x) << INT_STATUS_GPIO_LSB) & INT_STATUS_GPIO_MASK)
+#define INT_STATUS_UART_MSB 2
+#define INT_STATUS_UART_LSB 2
+#define INT_STATUS_UART_MASK 0x00000004
+#define INT_STATUS_UART_GET(x) (((x) & INT_STATUS_UART_MASK) >> INT_STATUS_UART_LSB)
+#define INT_STATUS_UART_SET(x) (((x) << INT_STATUS_UART_LSB) & INT_STATUS_UART_MASK)
+#define INT_STATUS_ERROR_MSB 1
+#define INT_STATUS_ERROR_LSB 1
+#define INT_STATUS_ERROR_MASK 0x00000002
+#define INT_STATUS_ERROR_GET(x) (((x) & INT_STATUS_ERROR_MASK) >> INT_STATUS_ERROR_LSB)
+#define INT_STATUS_ERROR_SET(x) (((x) << INT_STATUS_ERROR_LSB) & INT_STATUS_ERROR_MASK)
+#define INT_STATUS_WDT_INT_MSB 0
+#define INT_STATUS_WDT_INT_LSB 0
+#define INT_STATUS_WDT_INT_MASK 0x00000001
+#define INT_STATUS_WDT_INT_GET(x) (((x) & INT_STATUS_WDT_INT_MASK) >> INT_STATUS_WDT_INT_LSB)
+#define INT_STATUS_WDT_INT_SET(x) (((x) << INT_STATUS_WDT_INT_LSB) & INT_STATUS_WDT_INT_MASK)
+
+#define LF_TIMER0_ADDRESS 0x0c000054
+#define LF_TIMER0_OFFSET 0x00000054
+#define LF_TIMER0_TARGET_MSB 31
+#define LF_TIMER0_TARGET_LSB 0
+#define LF_TIMER0_TARGET_MASK 0xffffffff
+#define LF_TIMER0_TARGET_GET(x) (((x) & LF_TIMER0_TARGET_MASK) >> LF_TIMER0_TARGET_LSB)
+#define LF_TIMER0_TARGET_SET(x) (((x) << LF_TIMER0_TARGET_LSB) & LF_TIMER0_TARGET_MASK)
+
+#define LF_TIMER_COUNT0_ADDRESS 0x0c000058
+#define LF_TIMER_COUNT0_OFFSET 0x00000058
+#define LF_TIMER_COUNT0_VALUE_MSB 31
+#define LF_TIMER_COUNT0_VALUE_LSB 0
+#define LF_TIMER_COUNT0_VALUE_MASK 0xffffffff
+#define LF_TIMER_COUNT0_VALUE_GET(x) (((x) & LF_TIMER_COUNT0_VALUE_MASK) >> LF_TIMER_COUNT0_VALUE_LSB)
+#define LF_TIMER_COUNT0_VALUE_SET(x) (((x) << LF_TIMER_COUNT0_VALUE_LSB) & LF_TIMER_COUNT0_VALUE_MASK)
+
+#define LF_TIMER_CONTROL0_ADDRESS 0x0c00005c
+#define LF_TIMER_CONTROL0_OFFSET 0x0000005c
+#define LF_TIMER_CONTROL0_ENABLE_MSB 2
+#define LF_TIMER_CONTROL0_ENABLE_LSB 2
+#define LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004
+#define LF_TIMER_CONTROL0_ENABLE_GET(x) (((x) & LF_TIMER_CONTROL0_ENABLE_MASK) >> LF_TIMER_CONTROL0_ENABLE_LSB)
+#define LF_TIMER_CONTROL0_ENABLE_SET(x) (((x) << LF_TIMER_CONTROL0_ENABLE_LSB) & LF_TIMER_CONTROL0_ENABLE_MASK)
+#define LF_TIMER_CONTROL0_AUTO_RESTART_MSB 1
+#define LF_TIMER_CONTROL0_AUTO_RESTART_LSB 1
+#define LF_TIMER_CONTROL0_AUTO_RESTART_MASK 0x00000002
+#define LF_TIMER_CONTROL0_AUTO_RESTART_GET(x) (((x) & LF_TIMER_CONTROL0_AUTO_RESTART_MASK) >> LF_TIMER_CONTROL0_AUTO_RESTART_LSB)
+#define LF_TIMER_CONTROL0_AUTO_RESTART_SET(x) (((x) << LF_TIMER_CONTROL0_AUTO_RESTART_LSB) & LF_TIMER_CONTROL0_AUTO_RESTART_MASK)
+#define LF_TIMER_CONTROL0_RESET_MSB 0
+#define LF_TIMER_CONTROL0_RESET_LSB 0
+#define LF_TIMER_CONTROL0_RESET_MASK 0x00000001
+#define LF_TIMER_CONTROL0_RESET_GET(x) (((x) & LF_TIMER_CONTROL0_RESET_MASK) >> LF_TIMER_CONTROL0_RESET_LSB)
+#define LF_TIMER_CONTROL0_RESET_SET(x) (((x) << LF_TIMER_CONTROL0_RESET_LSB) & LF_TIMER_CONTROL0_RESET_MASK)
+
+#define LF_TIMER_STATUS0_ADDRESS 0x0c000060
+#define LF_TIMER_STATUS0_OFFSET 0x00000060
+#define LF_TIMER_STATUS0_INTERRUPT_MSB 0
+#define LF_TIMER_STATUS0_INTERRUPT_LSB 0
+#define LF_TIMER_STATUS0_INTERRUPT_MASK 0x00000001
+#define LF_TIMER_STATUS0_INTERRUPT_GET(x) (((x) & LF_TIMER_STATUS0_INTERRUPT_MASK) >> LF_TIMER_STATUS0_INTERRUPT_LSB)
+#define LF_TIMER_STATUS0_INTERRUPT_SET(x) (((x) << LF_TIMER_STATUS0_INTERRUPT_LSB) & LF_TIMER_STATUS0_INTERRUPT_MASK)
+
+#define LF_TIMER1_ADDRESS 0x0c000064
+#define LF_TIMER1_OFFSET 0x00000064
+#define LF_TIMER1_TARGET_MSB 31
+#define LF_TIMER1_TARGET_LSB 0
+#define LF_TIMER1_TARGET_MASK 0xffffffff
+#define LF_TIMER1_TARGET_GET(x) (((x) & LF_TIMER1_TARGET_MASK) >> LF_TIMER1_TARGET_LSB)
+#define LF_TIMER1_TARGET_SET(x) (((x) << LF_TIMER1_TARGET_LSB) & LF_TIMER1_TARGET_MASK)
+
+#define LF_TIMER_COUNT1_ADDRESS 0x0c000068
+#define LF_TIMER_COUNT1_OFFSET 0x00000068
+#define LF_TIMER_COUNT1_VALUE_MSB 31
+#define LF_TIMER_COUNT1_VALUE_LSB 0
+#define LF_TIMER_COUNT1_VALUE_MASK 0xffffffff
+#define LF_TIMER_COUNT1_VALUE_GET(x) (((x) & LF_TIMER_COUNT1_VALUE_MASK) >> LF_TIMER_COUNT1_VALUE_LSB)
+#define LF_TIMER_COUNT1_VALUE_SET(x) (((x) << LF_TIMER_COUNT1_VALUE_LSB) & LF_TIMER_COUNT1_VALUE_MASK)
+
+#define LF_TIMER_CONTROL1_ADDRESS 0x0c00006c
+#define LF_TIMER_CONTROL1_OFFSET 0x0000006c
+#define LF_TIMER_CONTROL1_ENABLE_MSB 2
+#define LF_TIMER_CONTROL1_ENABLE_LSB 2
+#define LF_TIMER_CONTROL1_ENABLE_MASK 0x00000004
+#define LF_TIMER_CONTROL1_ENABLE_GET(x) (((x) & LF_TIMER_CONTROL1_ENABLE_MASK) >> LF_TIMER_CONTROL1_ENABLE_LSB)
+#define LF_TIMER_CONTROL1_ENABLE_SET(x) (((x) << LF_TIMER_CONTROL1_ENABLE_LSB) & LF_TIMER_CONTROL1_ENABLE_MASK)
+#define LF_TIMER_CONTROL1_AUTO_RESTART_MSB 1
+#define LF_TIMER_CONTROL1_AUTO_RESTART_LSB 1
+#define LF_TIMER_CONTROL1_AUTO_RESTART_MASK 0x00000002
+#define LF_TIMER_CONTROL1_AUTO_RESTART_GET(x) (((x) & LF_TIMER_CONTROL1_AUTO_RESTART_MASK) >> LF_TIMER_CONTROL1_AUTO_RESTART_LSB)
+#define LF_TIMER_CONTROL1_AUTO_RESTART_SET(x) (((x) << LF_TIMER_CONTROL1_AUTO_RESTART_LSB) & LF_TIMER_CONTROL1_AUTO_RESTART_MASK)
+#define LF_TIMER_CONTROL1_RESET_MSB 0
+#define LF_TIMER_CONTROL1_RESET_LSB 0
+#define LF_TIMER_CONTROL1_RESET_MASK 0x00000001
+#define LF_TIMER_CONTROL1_RESET_GET(x) (((x) & LF_TIMER_CONTROL1_RESET_MASK) >> LF_TIMER_CONTROL1_RESET_LSB)
+#define LF_TIMER_CONTROL1_RESET_SET(x) (((x) << LF_TIMER_CONTROL1_RESET_LSB) & LF_TIMER_CONTROL1_RESET_MASK)
+
+#define LF_TIMER_STATUS1_ADDRESS 0x0c000070
+#define LF_TIMER_STATUS1_OFFSET 0x00000070
+#define LF_TIMER_STATUS1_INTERRUPT_MSB 0
+#define LF_TIMER_STATUS1_INTERRUPT_LSB 0
+#define LF_TIMER_STATUS1_INTERRUPT_MASK 0x00000001
+#define LF_TIMER_STATUS1_INTERRUPT_GET(x) (((x) & LF_TIMER_STATUS1_INTERRUPT_MASK) >> LF_TIMER_STATUS1_INTERRUPT_LSB)
+#define LF_TIMER_STATUS1_INTERRUPT_SET(x) (((x) << LF_TIMER_STATUS1_INTERRUPT_LSB) & LF_TIMER_STATUS1_INTERRUPT_MASK)
+
+#define LF_TIMER2_ADDRESS 0x0c000074
+#define LF_TIMER2_OFFSET 0x00000074
+#define LF_TIMER2_TARGET_MSB 31
+#define LF_TIMER2_TARGET_LSB 0
+#define LF_TIMER2_TARGET_MASK 0xffffffff
+#define LF_TIMER2_TARGET_GET(x) (((x) & LF_TIMER2_TARGET_MASK) >> LF_TIMER2_TARGET_LSB)
+#define LF_TIMER2_TARGET_SET(x) (((x) << LF_TIMER2_TARGET_LSB) & LF_TIMER2_TARGET_MASK)
+
+#define LF_TIMER_COUNT2_ADDRESS 0x0c000078
+#define LF_TIMER_COUNT2_OFFSET 0x00000078
+#define LF_TIMER_COUNT2_VALUE_MSB 31
+#define LF_TIMER_COUNT2_VALUE_LSB 0
+#define LF_TIMER_COUNT2_VALUE_MASK 0xffffffff
+#define LF_TIMER_COUNT2_VALUE_GET(x) (((x) & LF_TIMER_COUNT2_VALUE_MASK) >> LF_TIMER_COUNT2_VALUE_LSB)
+#define LF_TIMER_COUNT2_VALUE_SET(x) (((x) << LF_TIMER_COUNT2_VALUE_LSB) & LF_TIMER_COUNT2_VALUE_MASK)
+
+#define LF_TIMER_CONTROL2_ADDRESS 0x0c00007c
+#define LF_TIMER_CONTROL2_OFFSET 0x0000007c
+#define LF_TIMER_CONTROL2_ENABLE_MSB 2
+#define LF_TIMER_CONTROL2_ENABLE_LSB 2
+#define LF_TIMER_CONTROL2_ENABLE_MASK 0x00000004
+#define LF_TIMER_CONTROL2_ENABLE_GET(x) (((x) & LF_TIMER_CONTROL2_ENABLE_MASK) >> LF_TIMER_CONTROL2_ENABLE_LSB)
+#define LF_TIMER_CONTROL2_ENABLE_SET(x) (((x) << LF_TIMER_CONTROL2_ENABLE_LSB) & LF_TIMER_CONTROL2_ENABLE_MASK)
+#define LF_TIMER_CONTROL2_AUTO_RESTART_MSB 1
+#define LF_TIMER_CONTROL2_AUTO_RESTART_LSB 1
+#define LF_TIMER_CONTROL2_AUTO_RESTART_MASK 0x00000002
+#define LF_TIMER_CONTROL2_AUTO_RESTART_GET(x) (((x) & LF_TIMER_CONTROL2_AUTO_RESTART_MASK) >> LF_TIMER_CONTROL2_AUTO_RESTART_LSB)
+#define LF_TIMER_CONTROL2_AUTO_RESTART_SET(x) (((x) << LF_TIMER_CONTROL2_AUTO_RESTART_LSB) & LF_TIMER_CONTROL2_AUTO_RESTART_MASK)
+#define LF_TIMER_CONTROL2_RESET_MSB 0
+#define LF_TIMER_CONTROL2_RESET_LSB 0
+#define LF_TIMER_CONTROL2_RESET_MASK 0x00000001
+#define LF_TIMER_CONTROL2_RESET_GET(x) (((x) & LF_TIMER_CONTROL2_RESET_MASK) >> LF_TIMER_CONTROL2_RESET_LSB)
+#define LF_TIMER_CONTROL2_RESET_SET(x) (((x) << LF_TIMER_CONTROL2_RESET_LSB) & LF_TIMER_CONTROL2_RESET_MASK)
+
+#define LF_TIMER_STATUS2_ADDRESS 0x0c000080
+#define LF_TIMER_STATUS2_OFFSET 0x00000080
+#define LF_TIMER_STATUS2_INTERRUPT_MSB 0
+#define LF_TIMER_STATUS2_INTERRUPT_LSB 0
+#define LF_TIMER_STATUS2_INTERRUPT_MASK 0x00000001
+#define LF_TIMER_STATUS2_INTERRUPT_GET(x) (((x) & LF_TIMER_STATUS2_INTERRUPT_MASK) >> LF_TIMER_STATUS2_INTERRUPT_LSB)
+#define LF_TIMER_STATUS2_INTERRUPT_SET(x) (((x) << LF_TIMER_STATUS2_INTERRUPT_LSB) & LF_TIMER_STATUS2_INTERRUPT_MASK)
+
+#define LF_TIMER3_ADDRESS 0x0c000084
+#define LF_TIMER3_OFFSET 0x00000084
+#define LF_TIMER3_TARGET_MSB 31
+#define LF_TIMER3_TARGET_LSB 0
+#define LF_TIMER3_TARGET_MASK 0xffffffff
+#define LF_TIMER3_TARGET_GET(x) (((x) & LF_TIMER3_TARGET_MASK) >> LF_TIMER3_TARGET_LSB)
+#define LF_TIMER3_TARGET_SET(x) (((x) << LF_TIMER3_TARGET_LSB) & LF_TIMER3_TARGET_MASK)
+
+#define LF_TIMER_COUNT3_ADDRESS 0x0c000088
+#define LF_TIMER_COUNT3_OFFSET 0x00000088
+#define LF_TIMER_COUNT3_VALUE_MSB 31
+#define LF_TIMER_COUNT3_VALUE_LSB 0
+#define LF_TIMER_COUNT3_VALUE_MASK 0xffffffff
+#define LF_TIMER_COUNT3_VALUE_GET(x) (((x) & LF_TIMER_COUNT3_VALUE_MASK) >> LF_TIMER_COUNT3_VALUE_LSB)
+#define LF_TIMER_COUNT3_VALUE_SET(x) (((x) << LF_TIMER_COUNT3_VALUE_LSB) & LF_TIMER_COUNT3_VALUE_MASK)
+
+#define LF_TIMER_CONTROL3_ADDRESS 0x0c00008c
+#define LF_TIMER_CONTROL3_OFFSET 0x0000008c
+#define LF_TIMER_CONTROL3_ENABLE_MSB 2
+#define LF_TIMER_CONTROL3_ENABLE_LSB 2
+#define LF_TIMER_CONTROL3_ENABLE_MASK 0x00000004
+#define LF_TIMER_CONTROL3_ENABLE_GET(x) (((x) & LF_TIMER_CONTROL3_ENABLE_MASK) >> LF_TIMER_CONTROL3_ENABLE_LSB)
+#define LF_TIMER_CONTROL3_ENABLE_SET(x) (((x) << LF_TIMER_CONTROL3_ENABLE_LSB) & LF_TIMER_CONTROL3_ENABLE_MASK)
+#define LF_TIMER_CONTROL3_AUTO_RESTART_MSB 1
+#define LF_TIMER_CONTROL3_AUTO_RESTART_LSB 1
+#define LF_TIMER_CONTROL3_AUTO_RESTART_MASK 0x00000002
+#define LF_TIMER_CONTROL3_AUTO_RESTART_GET(x) (((x) & LF_TIMER_CONTROL3_AUTO_RESTART_MASK) >> LF_TIMER_CONTROL3_AUTO_RESTART_LSB)
+#define LF_TIMER_CONTROL3_AUTO_RESTART_SET(x) (((x) << LF_TIMER_CONTROL3_AUTO_RESTART_LSB) & LF_TIMER_CONTROL3_AUTO_RESTART_MASK)
+#define LF_TIMER_CONTROL3_RESET_MSB 0
+#define LF_TIMER_CONTROL3_RESET_LSB 0
+#define LF_TIMER_CONTROL3_RESET_MASK 0x00000001
+#define LF_TIMER_CONTROL3_RESET_GET(x) (((x) & LF_TIMER_CONTROL3_RESET_MASK) >> LF_TIMER_CONTROL3_RESET_LSB)
+#define LF_TIMER_CONTROL3_RESET_SET(x) (((x) << LF_TIMER_CONTROL3_RESET_LSB) & LF_TIMER_CONTROL3_RESET_MASK)
+
+#define LF_TIMER_STATUS3_ADDRESS 0x0c000090
+#define LF_TIMER_STATUS3_OFFSET 0x00000090
+#define LF_TIMER_STATUS3_INTERRUPT_MSB 0
+#define LF_TIMER_STATUS3_INTERRUPT_LSB 0
+#define LF_TIMER_STATUS3_INTERRUPT_MASK 0x00000001
+#define LF_TIMER_STATUS3_INTERRUPT_GET(x) (((x) & LF_TIMER_STATUS3_INTERRUPT_MASK) >> LF_TIMER_STATUS3_INTERRUPT_LSB)
+#define LF_TIMER_STATUS3_INTERRUPT_SET(x) (((x) << LF_TIMER_STATUS3_INTERRUPT_LSB) & LF_TIMER_STATUS3_INTERRUPT_MASK)
+
+#define HF_TIMER_ADDRESS 0x0c000094
+#define HF_TIMER_OFFSET 0x00000094
+#define HF_TIMER_TARGET_MSB 31
+#define HF_TIMER_TARGET_LSB 12
+#define HF_TIMER_TARGET_MASK 0xfffff000
+#define HF_TIMER_TARGET_GET(x) (((x) & HF_TIMER_TARGET_MASK) >> HF_TIMER_TARGET_LSB)
+#define HF_TIMER_TARGET_SET(x) (((x) << HF_TIMER_TARGET_LSB) & HF_TIMER_TARGET_MASK)
+
+#define HF_TIMER_COUNT_ADDRESS 0x0c000098
+#define HF_TIMER_COUNT_OFFSET 0x00000098
+#define HF_TIMER_COUNT_VALUE_MSB 31
+#define HF_TIMER_COUNT_VALUE_LSB 12
+#define HF_TIMER_COUNT_VALUE_MASK 0xfffff000
+#define HF_TIMER_COUNT_VALUE_GET(x) (((x) & HF_TIMER_COUNT_VALUE_MASK) >> HF_TIMER_COUNT_VALUE_LSB)
+#define HF_TIMER_COUNT_VALUE_SET(x) (((x) << HF_TIMER_COUNT_VALUE_LSB) & HF_TIMER_COUNT_VALUE_MASK)
+
+#define HF_LF_COUNT_ADDRESS 0x0c00009c
+#define HF_LF_COUNT_OFFSET 0x0000009c
+#define HF_LF_COUNT_VALUE_MSB 31
+#define HF_LF_COUNT_VALUE_LSB 0
+#define HF_LF_COUNT_VALUE_MASK 0xffffffff
+#define HF_LF_COUNT_VALUE_GET(x) (((x) & HF_LF_COUNT_VALUE_MASK) >> HF_LF_COUNT_VALUE_LSB)
+#define HF_LF_COUNT_VALUE_SET(x) (((x) << HF_LF_COUNT_VALUE_LSB) & HF_LF_COUNT_VALUE_MASK)
+
+#define HF_TIMER_CONTROL_ADDRESS 0x0c0000a0
+#define HF_TIMER_CONTROL_OFFSET 0x000000a0
+#define HF_TIMER_CONTROL_ENABLE_MSB 3
+#define HF_TIMER_CONTROL_ENABLE_LSB 3
+#define HF_TIMER_CONTROL_ENABLE_MASK 0x00000008
+#define HF_TIMER_CONTROL_ENABLE_GET(x) (((x) & HF_TIMER_CONTROL_ENABLE_MASK) >> HF_TIMER_CONTROL_ENABLE_LSB)
+#define HF_TIMER_CONTROL_ENABLE_SET(x) (((x) << HF_TIMER_CONTROL_ENABLE_LSB) & HF_TIMER_CONTROL_ENABLE_MASK)
+#define HF_TIMER_CONTROL_ON_MSB 2
+#define HF_TIMER_CONTROL_ON_LSB 2
+#define HF_TIMER_CONTROL_ON_MASK 0x00000004
+#define HF_TIMER_CONTROL_ON_GET(x) (((x) & HF_TIMER_CONTROL_ON_MASK) >> HF_TIMER_CONTROL_ON_LSB)
+#define HF_TIMER_CONTROL_ON_SET(x) (((x) << HF_TIMER_CONTROL_ON_LSB) & HF_TIMER_CONTROL_ON_MASK)
+#define HF_TIMER_CONTROL_AUTO_RESTART_MSB 1
+#define HF_TIMER_CONTROL_AUTO_RESTART_LSB 1
+#define HF_TIMER_CONTROL_AUTO_RESTART_MASK 0x00000002
+#define HF_TIMER_CONTROL_AUTO_RESTART_GET(x) (((x) & HF_TIMER_CONTROL_AUTO_RESTART_MASK) >> HF_TIMER_CONTROL_AUTO_RESTART_LSB)
+#define HF_TIMER_CONTROL_AUTO_RESTART_SET(x) (((x) << HF_TIMER_CONTROL_AUTO_RESTART_LSB) & HF_TIMER_CONTROL_AUTO_RESTART_MASK)
+#define HF_TIMER_CONTROL_RESET_MSB 0
+#define HF_TIMER_CONTROL_RESET_LSB 0
+#define HF_TIMER_CONTROL_RESET_MASK 0x00000001
+#define HF_TIMER_CONTROL_RESET_GET(x) (((x) & HF_TIMER_CONTROL_RESET_MASK) >> HF_TIMER_CONTROL_RESET_LSB)
+#define HF_TIMER_CONTROL_RESET_SET(x) (((x) << HF_TIMER_CONTROL_RESET_LSB) & HF_TIMER_CONTROL_RESET_MASK)
+
+#define HF_TIMER_STATUS_ADDRESS 0x0c0000a4
+#define HF_TIMER_STATUS_OFFSET 0x000000a4
+#define HF_TIMER_STATUS_INTERRUPT_MSB 0
+#define HF_TIMER_STATUS_INTERRUPT_LSB 0
+#define HF_TIMER_STATUS_INTERRUPT_MASK 0x00000001
+#define HF_TIMER_STATUS_INTERRUPT_GET(x) (((x) & HF_TIMER_STATUS_INTERRUPT_MASK) >> HF_TIMER_STATUS_INTERRUPT_LSB)
+#define HF_TIMER_STATUS_INTERRUPT_SET(x) (((x) << HF_TIMER_STATUS_INTERRUPT_LSB) & HF_TIMER_STATUS_INTERRUPT_MASK)
+
+#define RTC_CONTROL_ADDRESS 0x0c0000a8
+#define RTC_CONTROL_OFFSET 0x000000a8
+#define RTC_CONTROL_ENABLE_MSB 2
+#define RTC_CONTROL_ENABLE_LSB 2
+#define RTC_CONTROL_ENABLE_MASK 0x00000004
+#define RTC_CONTROL_ENABLE_GET(x) (((x) & RTC_CONTROL_ENABLE_MASK) >> RTC_CONTROL_ENABLE_LSB)
+#define RTC_CONTROL_ENABLE_SET(x) (((x) << RTC_CONTROL_ENABLE_LSB) & RTC_CONTROL_ENABLE_MASK)
+#define RTC_CONTROL_LOAD_RTC_MSB 1
+#define RTC_CONTROL_LOAD_RTC_LSB 1
+#define RTC_CONTROL_LOAD_RTC_MASK 0x00000002
+#define RTC_CONTROL_LOAD_RTC_GET(x) (((x) & RTC_CONTROL_LOAD_RTC_MASK) >> RTC_CONTROL_LOAD_RTC_LSB)
+#define RTC_CONTROL_LOAD_RTC_SET(x) (((x) << RTC_CONTROL_LOAD_RTC_LSB) & RTC_CONTROL_LOAD_RTC_MASK)
+#define RTC_CONTROL_LOAD_ALARM_MSB 0
+#define RTC_CONTROL_LOAD_ALARM_LSB 0
+#define RTC_CONTROL_LOAD_ALARM_MASK 0x00000001
+#define RTC_CONTROL_LOAD_ALARM_GET(x) (((x) & RTC_CONTROL_LOAD_ALARM_MASK) >> RTC_CONTROL_LOAD_ALARM_LSB)
+#define RTC_CONTROL_LOAD_ALARM_SET(x) (((x) << RTC_CONTROL_LOAD_ALARM_LSB) & RTC_CONTROL_LOAD_ALARM_MASK)
+
+#define RTC_TIME_ADDRESS 0x0c0000ac
+#define RTC_TIME_OFFSET 0x000000ac
+#define RTC_TIME_WEEK_DAY_MSB 26
+#define RTC_TIME_WEEK_DAY_LSB 24
+#define RTC_TIME_WEEK_DAY_MASK 0x07000000
+#define RTC_TIME_WEEK_DAY_GET(x) (((x) & RTC_TIME_WEEK_DAY_MASK) >> RTC_TIME_WEEK_DAY_LSB)
+#define RTC_TIME_WEEK_DAY_SET(x) (((x) << RTC_TIME_WEEK_DAY_LSB) & RTC_TIME_WEEK_DAY_MASK)
+#define RTC_TIME_HOUR_MSB 21
+#define RTC_TIME_HOUR_LSB 16
+#define RTC_TIME_HOUR_MASK 0x003f0000
+#define RTC_TIME_HOUR_GET(x) (((x) & RTC_TIME_HOUR_MASK) >> RTC_TIME_HOUR_LSB)
+#define RTC_TIME_HOUR_SET(x) (((x) << RTC_TIME_HOUR_LSB) & RTC_TIME_HOUR_MASK)
+#define RTC_TIME_MINUTE_MSB 14
+#define RTC_TIME_MINUTE_LSB 8
+#define RTC_TIME_MINUTE_MASK 0x00007f00
+#define RTC_TIME_MINUTE_GET(x) (((x) & RTC_TIME_MINUTE_MASK) >> RTC_TIME_MINUTE_LSB)
+#define RTC_TIME_MINUTE_SET(x) (((x) << RTC_TIME_MINUTE_LSB) & RTC_TIME_MINUTE_MASK)
+#define RTC_TIME_SECOND_MSB 6
+#define RTC_TIME_SECOND_LSB 0
+#define RTC_TIME_SECOND_MASK 0x0000007f
+#define RTC_TIME_SECOND_GET(x) (((x) & RTC_TIME_SECOND_MASK) >> RTC_TIME_SECOND_LSB)
+#define RTC_TIME_SECOND_SET(x) (((x) << RTC_TIME_SECOND_LSB) & RTC_TIME_SECOND_MASK)
+
+#define RTC_DATE_ADDRESS 0x0c0000b0
+#define RTC_DATE_OFFSET 0x000000b0
+#define RTC_DATE_YEAR_MSB 23
+#define RTC_DATE_YEAR_LSB 16
+#define RTC_DATE_YEAR_MASK 0x00ff0000
+#define RTC_DATE_YEAR_GET(x) (((x) & RTC_DATE_YEAR_MASK) >> RTC_DATE_YEAR_LSB)
+#define RTC_DATE_YEAR_SET(x) (((x) << RTC_DATE_YEAR_LSB) & RTC_DATE_YEAR_MASK)
+#define RTC_DATE_MONTH_MSB 12
+#define RTC_DATE_MONTH_LSB 8
+#define RTC_DATE_MONTH_MASK 0x00001f00
+#define RTC_DATE_MONTH_GET(x) (((x) & RTC_DATE_MONTH_MASK) >> RTC_DATE_MONTH_LSB)
+#define RTC_DATE_MONTH_SET(x) (((x) << RTC_DATE_MONTH_LSB) & RTC_DATE_MONTH_MASK)
+#define RTC_DATE_MONTH_DAY_MSB 5
+#define RTC_DATE_MONTH_DAY_LSB 0
+#define RTC_DATE_MONTH_DAY_MASK 0x0000003f
+#define RTC_DATE_MONTH_DAY_GET(x) (((x) & RTC_DATE_MONTH_DAY_MASK) >> RTC_DATE_MONTH_DAY_LSB)
+#define RTC_DATE_MONTH_DAY_SET(x) (((x) << RTC_DATE_MONTH_DAY_LSB) & RTC_DATE_MONTH_DAY_MASK)
+
+#define RTC_SET_TIME_ADDRESS 0x0c0000b4
+#define RTC_SET_TIME_OFFSET 0x000000b4
+#define RTC_SET_TIME_WEEK_DAY_MSB 26
+#define RTC_SET_TIME_WEEK_DAY_LSB 24
+#define RTC_SET_TIME_WEEK_DAY_MASK 0x07000000
+#define RTC_SET_TIME_WEEK_DAY_GET(x) (((x) & RTC_SET_TIME_WEEK_DAY_MASK) >> RTC_SET_TIME_WEEK_DAY_LSB)
+#define RTC_SET_TIME_WEEK_DAY_SET(x) (((x) << RTC_SET_TIME_WEEK_DAY_LSB) & RTC_SET_TIME_WEEK_DAY_MASK)
+#define RTC_SET_TIME_HOUR_MSB 21
+#define RTC_SET_TIME_HOUR_LSB 16
+#define RTC_SET_TIME_HOUR_MASK 0x003f0000
+#define RTC_SET_TIME_HOUR_GET(x) (((x) & RTC_SET_TIME_HOUR_MASK) >> RTC_SET_TIME_HOUR_LSB)
+#define RTC_SET_TIME_HOUR_SET(x) (((x) << RTC_SET_TIME_HOUR_LSB) & RTC_SET_TIME_HOUR_MASK)
+#define RTC_SET_TIME_MINUTE_MSB 14
+#define RTC_SET_TIME_MINUTE_LSB 8
+#define RTC_SET_TIME_MINUTE_MASK 0x00007f00
+#define RTC_SET_TIME_MINUTE_GET(x) (((x) & RTC_SET_TIME_MINUTE_MASK) >> RTC_SET_TIME_MINUTE_LSB)
+#define RTC_SET_TIME_MINUTE_SET(x) (((x) << RTC_SET_TIME_MINUTE_LSB) & RTC_SET_TIME_MINUTE_MASK)
+#define RTC_SET_TIME_SECOND_MSB 6
+#define RTC_SET_TIME_SECOND_LSB 0
+#define RTC_SET_TIME_SECOND_MASK 0x0000007f
+#define RTC_SET_TIME_SECOND_GET(x) (((x) & RTC_SET_TIME_SECOND_MASK) >> RTC_SET_TIME_SECOND_LSB)
+#define RTC_SET_TIME_SECOND_SET(x) (((x) << RTC_SET_TIME_SECOND_LSB) & RTC_SET_TIME_SECOND_MASK)
+
+#define RTC_SET_DATE_ADDRESS 0x0c0000b8
+#define RTC_SET_DATE_OFFSET 0x000000b8
+#define RTC_SET_DATE_YEAR_MSB 23
+#define RTC_SET_DATE_YEAR_LSB 16
+#define RTC_SET_DATE_YEAR_MASK 0x00ff0000
+#define RTC_SET_DATE_YEAR_GET(x) (((x) & RTC_SET_DATE_YEAR_MASK) >> RTC_SET_DATE_YEAR_LSB)
+#define RTC_SET_DATE_YEAR_SET(x) (((x) << RTC_SET_DATE_YEAR_LSB) & RTC_SET_DATE_YEAR_MASK)
+#define RTC_SET_DATE_MONTH_MSB 12
+#define RTC_SET_DATE_MONTH_LSB 8
+#define RTC_SET_DATE_MONTH_MASK 0x00001f00
+#define RTC_SET_DATE_MONTH_GET(x) (((x) & RTC_SET_DATE_MONTH_MASK) >> RTC_SET_DATE_MONTH_LSB)
+#define RTC_SET_DATE_MONTH_SET(x) (((x) << RTC_SET_DATE_MONTH_LSB) & RTC_SET_DATE_MONTH_MASK)
+#define RTC_SET_DATE_MONTH_DAY_MSB 5
+#define RTC_SET_DATE_MONTH_DAY_LSB 0
+#define RTC_SET_DATE_MONTH_DAY_MASK 0x0000003f
+#define RTC_SET_DATE_MONTH_DAY_GET(x) (((x) & RTC_SET_DATE_MONTH_DAY_MASK) >> RTC_SET_DATE_MONTH_DAY_LSB)
+#define RTC_SET_DATE_MONTH_DAY_SET(x) (((x) << RTC_SET_DATE_MONTH_DAY_LSB) & RTC_SET_DATE_MONTH_DAY_MASK)
+
+#define RTC_SET_ALARM_ADDRESS 0x0c0000bc
+#define RTC_SET_ALARM_OFFSET 0x000000bc
+#define RTC_SET_ALARM_HOUR_MSB 21
+#define RTC_SET_ALARM_HOUR_LSB 16
+#define RTC_SET_ALARM_HOUR_MASK 0x003f0000
+#define RTC_SET_ALARM_HOUR_GET(x) (((x) & RTC_SET_ALARM_HOUR_MASK) >> RTC_SET_ALARM_HOUR_LSB)
+#define RTC_SET_ALARM_HOUR_SET(x) (((x) << RTC_SET_ALARM_HOUR_LSB) & RTC_SET_ALARM_HOUR_MASK)
+#define RTC_SET_ALARM_MINUTE_MSB 14
+#define RTC_SET_ALARM_MINUTE_LSB 8
+#define RTC_SET_ALARM_MINUTE_MASK 0x00007f00
+#define RTC_SET_ALARM_MINUTE_GET(x) (((x) & RTC_SET_ALARM_MINUTE_MASK) >> RTC_SET_ALARM_MINUTE_LSB)
+#define RTC_SET_ALARM_MINUTE_SET(x) (((x) << RTC_SET_ALARM_MINUTE_LSB) & RTC_SET_ALARM_MINUTE_MASK)
+#define RTC_SET_ALARM_SECOND_MSB 6
+#define RTC_SET_ALARM_SECOND_LSB 0
+#define RTC_SET_ALARM_SECOND_MASK 0x0000007f
+#define RTC_SET_ALARM_SECOND_GET(x) (((x) & RTC_SET_ALARM_SECOND_MASK) >> RTC_SET_ALARM_SECOND_LSB)
+#define RTC_SET_ALARM_SECOND_SET(x) (((x) << RTC_SET_ALARM_SECOND_LSB) & RTC_SET_ALARM_SECOND_MASK)
+
+#define RTC_CONFIG_ADDRESS 0x0c0000c0
+#define RTC_CONFIG_OFFSET 0x000000c0
+#define RTC_CONFIG_BCD_MSB 2
+#define RTC_CONFIG_BCD_LSB 2
+#define RTC_CONFIG_BCD_MASK 0x00000004
+#define RTC_CONFIG_BCD_GET(x) (((x) & RTC_CONFIG_BCD_MASK) >> RTC_CONFIG_BCD_LSB)
+#define RTC_CONFIG_BCD_SET(x) (((x) << RTC_CONFIG_BCD_LSB) & RTC_CONFIG_BCD_MASK)
+#define RTC_CONFIG_TWELVE_HOUR_MSB 1
+#define RTC_CONFIG_TWELVE_HOUR_LSB 1
+#define RTC_CONFIG_TWELVE_HOUR_MASK 0x00000002
+#define RTC_CONFIG_TWELVE_HOUR_GET(x) (((x) & RTC_CONFIG_TWELVE_HOUR_MASK) >> RTC_CONFIG_TWELVE_HOUR_LSB)
+#define RTC_CONFIG_TWELVE_HOUR_SET(x) (((x) << RTC_CONFIG_TWELVE_HOUR_LSB) & RTC_CONFIG_TWELVE_HOUR_MASK)
+#define RTC_CONFIG_DSE_MSB 0
+#define RTC_CONFIG_DSE_LSB 0
+#define RTC_CONFIG_DSE_MASK 0x00000001
+#define RTC_CONFIG_DSE_GET(x) (((x) & RTC_CONFIG_DSE_MASK) >> RTC_CONFIG_DSE_LSB)
+#define RTC_CONFIG_DSE_SET(x) (((x) << RTC_CONFIG_DSE_LSB) & RTC_CONFIG_DSE_MASK)
+
+#define RTC_ALARM_STATUS_ADDRESS 0x0c0000c4
+#define RTC_ALARM_STATUS_OFFSET 0x000000c4
+#define RTC_ALARM_STATUS_ENABLE_MSB 1
+#define RTC_ALARM_STATUS_ENABLE_LSB 1
+#define RTC_ALARM_STATUS_ENABLE_MASK 0x00000002
+#define RTC_ALARM_STATUS_ENABLE_GET(x) (((x) & RTC_ALARM_STATUS_ENABLE_MASK) >> RTC_ALARM_STATUS_ENABLE_LSB)
+#define RTC_ALARM_STATUS_ENABLE_SET(x) (((x) << RTC_ALARM_STATUS_ENABLE_LSB) & RTC_ALARM_STATUS_ENABLE_MASK)
+#define RTC_ALARM_STATUS_INTERRUPT_MSB 0
+#define RTC_ALARM_STATUS_INTERRUPT_LSB 0
+#define RTC_ALARM_STATUS_INTERRUPT_MASK 0x00000001
+#define RTC_ALARM_STATUS_INTERRUPT_GET(x) (((x) & RTC_ALARM_STATUS_INTERRUPT_MASK) >> RTC_ALARM_STATUS_INTERRUPT_LSB)
+#define RTC_ALARM_STATUS_INTERRUPT_SET(x) (((x) << RTC_ALARM_STATUS_INTERRUPT_LSB) & RTC_ALARM_STATUS_INTERRUPT_MASK)
+
+#define UART_WAKEUP_ADDRESS 0x0c0000c8
+#define UART_WAKEUP_OFFSET 0x000000c8
+#define UART_WAKEUP_ENABLE_MSB 0
+#define UART_WAKEUP_ENABLE_LSB 0
+#define UART_WAKEUP_ENABLE_MASK 0x00000001
+#define UART_WAKEUP_ENABLE_GET(x) (((x) & UART_WAKEUP_ENABLE_MASK) >> UART_WAKEUP_ENABLE_LSB)
+#define UART_WAKEUP_ENABLE_SET(x) (((x) << UART_WAKEUP_ENABLE_LSB) & UART_WAKEUP_ENABLE_MASK)
+
+#define RESET_CAUSE_ADDRESS 0x0c0000cc
+#define RESET_CAUSE_OFFSET 0x000000cc
+#define RESET_CAUSE_LAST_MSB 2
+#define RESET_CAUSE_LAST_LSB 0
+#define RESET_CAUSE_LAST_MASK 0x00000007
+#define RESET_CAUSE_LAST_GET(x) (((x) & RESET_CAUSE_LAST_MASK) >> RESET_CAUSE_LAST_LSB)
+#define RESET_CAUSE_LAST_SET(x) (((x) << RESET_CAUSE_LAST_LSB) & RESET_CAUSE_LAST_MASK)
+
+#define SYSTEM_SLEEP_ADDRESS 0x0c0000d0
+#define SYSTEM_SLEEP_OFFSET 0x000000d0
+#define SYSTEM_SLEEP_HOST_IF_MSB 4
+#define SYSTEM_SLEEP_HOST_IF_LSB 4
+#define SYSTEM_SLEEP_HOST_IF_MASK 0x00000010
+#define SYSTEM_SLEEP_HOST_IF_GET(x) (((x) & SYSTEM_SLEEP_HOST_IF_MASK) >> SYSTEM_SLEEP_HOST_IF_LSB)
+#define SYSTEM_SLEEP_HOST_IF_SET(x) (((x) << SYSTEM_SLEEP_HOST_IF_LSB) & SYSTEM_SLEEP_HOST_IF_MASK)
+#define SYSTEM_SLEEP_MBOX_MSB 3
+#define SYSTEM_SLEEP_MBOX_LSB 3
+#define SYSTEM_SLEEP_MBOX_MASK 0x00000008
+#define SYSTEM_SLEEP_MBOX_GET(x) (((x) & SYSTEM_SLEEP_MBOX_MASK) >> SYSTEM_SLEEP_MBOX_LSB)
+#define SYSTEM_SLEEP_MBOX_SET(x) (((x) << SYSTEM_SLEEP_MBOX_LSB) & SYSTEM_SLEEP_MBOX_MASK)
+#define SYSTEM_SLEEP_MAC_IF_MSB 2
+#define SYSTEM_SLEEP_MAC_IF_LSB 2
+#define SYSTEM_SLEEP_MAC_IF_MASK 0x00000004
+#define SYSTEM_SLEEP_MAC_IF_GET(x) (((x) & SYSTEM_SLEEP_MAC_IF_MASK) >> SYSTEM_SLEEP_MAC_IF_LSB)
+#define SYSTEM_SLEEP_MAC_IF_SET(x) (((x) << SYSTEM_SLEEP_MAC_IF_LSB) & SYSTEM_SLEEP_MAC_IF_MASK)
+#define SYSTEM_SLEEP_LIGHT_MSB 1
+#define SYSTEM_SLEEP_LIGHT_LSB 1
+#define SYSTEM_SLEEP_LIGHT_MASK 0x00000002
+#define SYSTEM_SLEEP_LIGHT_GET(x) (((x) & SYSTEM_SLEEP_LIGHT_MASK) >> SYSTEM_SLEEP_LIGHT_LSB)
+#define SYSTEM_SLEEP_LIGHT_SET(x) (((x) << SYSTEM_SLEEP_LIGHT_LSB) & SYSTEM_SLEEP_LIGHT_MASK)
+#define SYSTEM_SLEEP_DISABLE_MSB 0
+#define SYSTEM_SLEEP_DISABLE_LSB 0
+#define SYSTEM_SLEEP_DISABLE_MASK 0x00000001
+#define SYSTEM_SLEEP_DISABLE_GET(x) (((x) & SYSTEM_SLEEP_DISABLE_MASK) >> SYSTEM_SLEEP_DISABLE_LSB)
+#define SYSTEM_SLEEP_DISABLE_SET(x) (((x) << SYSTEM_SLEEP_DISABLE_LSB) & SYSTEM_SLEEP_DISABLE_MASK)
+
+#define LDO_VOLTAGE_ADDRESS 0x0c0000d4
+#define LDO_VOLTAGE_OFFSET 0x000000d4
+#define LDO_VOLTAGE_SLEEP_MSB 14
+#define LDO_VOLTAGE_SLEEP_LSB 12
+#define LDO_VOLTAGE_SLEEP_MASK 0x00007000
+#define LDO_VOLTAGE_SLEEP_GET(x) (((x) & LDO_VOLTAGE_SLEEP_MASK) >> LDO_VOLTAGE_SLEEP_LSB)
+#define LDO_VOLTAGE_SLEEP_SET(x) (((x) << LDO_VOLTAGE_SLEEP_LSB) & LDO_VOLTAGE_SLEEP_MASK)
+#define LDO_VOLTAGE_WAKEUP_MSB 10
+#define LDO_VOLTAGE_WAKEUP_LSB 8
+#define LDO_VOLTAGE_WAKEUP_MASK 0x00000700
+#define LDO_VOLTAGE_WAKEUP_GET(x) (((x) & LDO_VOLTAGE_WAKEUP_MASK) >> LDO_VOLTAGE_WAKEUP_LSB)
+#define LDO_VOLTAGE_WAKEUP_SET(x) (((x) << LDO_VOLTAGE_WAKEUP_LSB) & LDO_VOLTAGE_WAKEUP_MASK)
+#define LDO_VOLTAGE_SOC_ON_MSB 6
+#define LDO_VOLTAGE_SOC_ON_LSB 4
+#define LDO_VOLTAGE_SOC_ON_MASK 0x00000070
+#define LDO_VOLTAGE_SOC_ON_GET(x) (((x) & LDO_VOLTAGE_SOC_ON_MASK) >> LDO_VOLTAGE_SOC_ON_LSB)
+#define LDO_VOLTAGE_SOC_ON_SET(x) (((x) << LDO_VOLTAGE_SOC_ON_LSB) & LDO_VOLTAGE_SOC_ON_MASK)
+#define LDO_VOLTAGE_ON_MSB 2
+#define LDO_VOLTAGE_ON_LSB 0
+#define LDO_VOLTAGE_ON_MASK 0x00000007
+#define LDO_VOLTAGE_ON_GET(x) (((x) & LDO_VOLTAGE_ON_MASK) >> LDO_VOLTAGE_ON_LSB)
+#define LDO_VOLTAGE_ON_SET(x) (((x) << LDO_VOLTAGE_ON_LSB) & LDO_VOLTAGE_ON_MASK)
+
+#define LDO_A_VOLTAGE_ADDRESS 0x0c0000d8
+#define LDO_A_VOLTAGE_OFFSET 0x000000d8
+#define LDO_A_VOLTAGE_SLEEP_MSB 14
+#define LDO_A_VOLTAGE_SLEEP_LSB 12
+#define LDO_A_VOLTAGE_SLEEP_MASK 0x00007000
+#define LDO_A_VOLTAGE_SLEEP_GET(x) (((x) & LDO_A_VOLTAGE_SLEEP_MASK) >> LDO_A_VOLTAGE_SLEEP_LSB)
+#define LDO_A_VOLTAGE_SLEEP_SET(x) (((x) << LDO_A_VOLTAGE_SLEEP_LSB) & LDO_A_VOLTAGE_SLEEP_MASK)
+#define LDO_A_VOLTAGE_WAKEUP_MSB 10
+#define LDO_A_VOLTAGE_WAKEUP_LSB 8
+#define LDO_A_VOLTAGE_WAKEUP_MASK 0x00000700
+#define LDO_A_VOLTAGE_WAKEUP_GET(x) (((x) & LDO_A_VOLTAGE_WAKEUP_MASK) >> LDO_A_VOLTAGE_WAKEUP_LSB)
+#define LDO_A_VOLTAGE_WAKEUP_SET(x) (((x) << LDO_A_VOLTAGE_WAKEUP_LSB) & LDO_A_VOLTAGE_WAKEUP_MASK)
+#define LDO_A_VOLTAGE_SOC_ON_MSB 6
+#define LDO_A_VOLTAGE_SOC_ON_LSB 4
+#define LDO_A_VOLTAGE_SOC_ON_MASK 0x00000070
+#define LDO_A_VOLTAGE_SOC_ON_GET(x) (((x) & LDO_A_VOLTAGE_SOC_ON_MASK) >> LDO_A_VOLTAGE_SOC_ON_LSB)
+#define LDO_A_VOLTAGE_SOC_ON_SET(x) (((x) << LDO_A_VOLTAGE_SOC_ON_LSB) & LDO_A_VOLTAGE_SOC_ON_MASK)
+#define LDO_A_VOLTAGE_ON_MSB 2
+#define LDO_A_VOLTAGE_ON_LSB 0
+#define LDO_A_VOLTAGE_ON_MASK 0x00000007
+#define LDO_A_VOLTAGE_ON_GET(x) (((x) & LDO_A_VOLTAGE_ON_MASK) >> LDO_A_VOLTAGE_ON_LSB)
+#define LDO_A_VOLTAGE_ON_SET(x) (((x) << LDO_A_VOLTAGE_ON_LSB) & LDO_A_VOLTAGE_ON_MASK)
+
+#define SDIO_LDO_VOLTAGE_ADDRESS 0x0c0000dc
+#define SDIO_LDO_VOLTAGE_OFFSET 0x000000dc
+#define SDIO_LDO_VOLTAGE_OFF_MSB 18
+#define SDIO_LDO_VOLTAGE_OFF_LSB 16
+#define SDIO_LDO_VOLTAGE_OFF_MASK 0x00070000
+#define SDIO_LDO_VOLTAGE_OFF_GET(x) (((x) & SDIO_LDO_VOLTAGE_OFF_MASK) >> SDIO_LDO_VOLTAGE_OFF_LSB)
+#define SDIO_LDO_VOLTAGE_OFF_SET(x) (((x) << SDIO_LDO_VOLTAGE_OFF_LSB) & SDIO_LDO_VOLTAGE_OFF_MASK)
+#define SDIO_LDO_VOLTAGE_SLEEP_MSB 14
+#define SDIO_LDO_VOLTAGE_SLEEP_LSB 12
+#define SDIO_LDO_VOLTAGE_SLEEP_MASK 0x00007000
+#define SDIO_LDO_VOLTAGE_SLEEP_GET(x) (((x) & SDIO_LDO_VOLTAGE_SLEEP_MASK) >> SDIO_LDO_VOLTAGE_SLEEP_LSB)
+#define SDIO_LDO_VOLTAGE_SLEEP_SET(x) (((x) << SDIO_LDO_VOLTAGE_SLEEP_LSB) & SDIO_LDO_VOLTAGE_SLEEP_MASK)
+#define SDIO_LDO_VOLTAGE_WAKEUP_MSB 10
+#define SDIO_LDO_VOLTAGE_WAKEUP_LSB 8
+#define SDIO_LDO_VOLTAGE_WAKEUP_MASK 0x00000700
+#define SDIO_LDO_VOLTAGE_WAKEUP_GET(x) (((x) & SDIO_LDO_VOLTAGE_WAKEUP_MASK) >> SDIO_LDO_VOLTAGE_WAKEUP_LSB)
+#define SDIO_LDO_VOLTAGE_WAKEUP_SET(x) (((x) << SDIO_LDO_VOLTAGE_WAKEUP_LSB) & SDIO_LDO_VOLTAGE_WAKEUP_MASK)
+#define SDIO_LDO_VOLTAGE_SOC_ON_MSB 6
+#define SDIO_LDO_VOLTAGE_SOC_ON_LSB 4
+#define SDIO_LDO_VOLTAGE_SOC_ON_MASK 0x00000070
+#define SDIO_LDO_VOLTAGE_SOC_ON_GET(x) (((x) & SDIO_LDO_VOLTAGE_SOC_ON_MASK) >> SDIO_LDO_VOLTAGE_SOC_ON_LSB)
+#define SDIO_LDO_VOLTAGE_SOC_ON_SET(x) (((x) << SDIO_LDO_VOLTAGE_SOC_ON_LSB) & SDIO_LDO_VOLTAGE_SOC_ON_MASK)
+#define SDIO_LDO_VOLTAGE_ON_MSB 2
+#define SDIO_LDO_VOLTAGE_ON_LSB 0
+#define SDIO_LDO_VOLTAGE_ON_MASK 0x00000007
+#define SDIO_LDO_VOLTAGE_ON_GET(x) (((x) & SDIO_LDO_VOLTAGE_ON_MASK) >> SDIO_LDO_VOLTAGE_ON_LSB)
+#define SDIO_LDO_VOLTAGE_ON_SET(x) (((x) << SDIO_LDO_VOLTAGE_ON_LSB) & SDIO_LDO_VOLTAGE_ON_MASK)
+
+#define CORE_PAD_ENABLE_ADDRESS 0x0c0000e0
+#define CORE_PAD_ENABLE_OFFSET 0x000000e0
+#define CORE_PAD_ENABLE_SLEEP_MSB 3
+#define CORE_PAD_ENABLE_SLEEP_LSB 3
+#define CORE_PAD_ENABLE_SLEEP_MASK 0x00000008
+#define CORE_PAD_ENABLE_SLEEP_GET(x) (((x) & CORE_PAD_ENABLE_SLEEP_MASK) >> CORE_PAD_ENABLE_SLEEP_LSB)
+#define CORE_PAD_ENABLE_SLEEP_SET(x) (((x) << CORE_PAD_ENABLE_SLEEP_LSB) & CORE_PAD_ENABLE_SLEEP_MASK)
+#define CORE_PAD_ENABLE_WAKEUP_MSB 2
+#define CORE_PAD_ENABLE_WAKEUP_LSB 2
+#define CORE_PAD_ENABLE_WAKEUP_MASK 0x00000004
+#define CORE_PAD_ENABLE_WAKEUP_GET(x) (((x) & CORE_PAD_ENABLE_WAKEUP_MASK) >> CORE_PAD_ENABLE_WAKEUP_LSB)
+#define CORE_PAD_ENABLE_WAKEUP_SET(x) (((x) << CORE_PAD_ENABLE_WAKEUP_LSB) & CORE_PAD_ENABLE_WAKEUP_MASK)
+#define CORE_PAD_ENABLE_SOC_ON_MSB 1
+#define CORE_PAD_ENABLE_SOC_ON_LSB 1
+#define CORE_PAD_ENABLE_SOC_ON_MASK 0x00000002
+#define CORE_PAD_ENABLE_SOC_ON_GET(x) (((x) & CORE_PAD_ENABLE_SOC_ON_MASK) >> CORE_PAD_ENABLE_SOC_ON_LSB)
+#define CORE_PAD_ENABLE_SOC_ON_SET(x) (((x) << CORE_PAD_ENABLE_SOC_ON_LSB) & CORE_PAD_ENABLE_SOC_ON_MASK)
+#define CORE_PAD_ENABLE_ON_MSB 0
+#define CORE_PAD_ENABLE_ON_LSB 0
+#define CORE_PAD_ENABLE_ON_MASK 0x00000001
+#define CORE_PAD_ENABLE_ON_GET(x) (((x) & CORE_PAD_ENABLE_ON_MASK) >> CORE_PAD_ENABLE_ON_LSB)
+#define CORE_PAD_ENABLE_ON_SET(x) (((x) << CORE_PAD_ENABLE_ON_LSB) & CORE_PAD_ENABLE_ON_MASK)
+
+#define SDIO_WRAPPER_ADDRESS 0x0c0000e4
+#define SDIO_WRAPPER_OFFSET 0x000000e4
+#define SDIO_WRAPPER_SLEEP_MSB 3
+#define SDIO_WRAPPER_SLEEP_LSB 3
+#define SDIO_WRAPPER_SLEEP_MASK 0x00000008
+#define SDIO_WRAPPER_SLEEP_GET(x) (((x) & SDIO_WRAPPER_SLEEP_MASK) >> SDIO_WRAPPER_SLEEP_LSB)
+#define SDIO_WRAPPER_SLEEP_SET(x) (((x) << SDIO_WRAPPER_SLEEP_LSB) & SDIO_WRAPPER_SLEEP_MASK)
+#define SDIO_WRAPPER_WAKEUP_MSB 2
+#define SDIO_WRAPPER_WAKEUP_LSB 2
+#define SDIO_WRAPPER_WAKEUP_MASK 0x00000004
+#define SDIO_WRAPPER_WAKEUP_GET(x) (((x) & SDIO_WRAPPER_WAKEUP_MASK) >> SDIO_WRAPPER_WAKEUP_LSB)
+#define SDIO_WRAPPER_WAKEUP_SET(x) (((x) << SDIO_WRAPPER_WAKEUP_LSB) & SDIO_WRAPPER_WAKEUP_MASK)
+#define SDIO_WRAPPER_SOC_ON_MSB 1
+#define SDIO_WRAPPER_SOC_ON_LSB 1
+#define SDIO_WRAPPER_SOC_ON_MASK 0x00000002
+#define SDIO_WRAPPER_SOC_ON_GET(x) (((x) & SDIO_WRAPPER_SOC_ON_MASK) >> SDIO_WRAPPER_SOC_ON_LSB)
+#define SDIO_WRAPPER_SOC_ON_SET(x) (((x) << SDIO_WRAPPER_SOC_ON_LSB) & SDIO_WRAPPER_SOC_ON_MASK)
+#define SDIO_WRAPPER_ON_MSB 0
+#define SDIO_WRAPPER_ON_LSB 0
+#define SDIO_WRAPPER_ON_MASK 0x00000001
+#define SDIO_WRAPPER_ON_GET(x) (((x) & SDIO_WRAPPER_ON_MASK) >> SDIO_WRAPPER_ON_LSB)
+#define SDIO_WRAPPER_ON_SET(x) (((x) << SDIO_WRAPPER_ON_LSB) & SDIO_WRAPPER_ON_MASK)
+
+#define MAC_SLEEP_CONTROL_ADDRESS 0x0c0000e8
+#define MAC_SLEEP_CONTROL_OFFSET 0x000000e8
+#define MAC_SLEEP_CONTROL_ENABLE_MSB 1
+#define MAC_SLEEP_CONTROL_ENABLE_LSB 0
+#define MAC_SLEEP_CONTROL_ENABLE_MASK 0x00000003
+#define MAC_SLEEP_CONTROL_ENABLE_GET(x) (((x) & MAC_SLEEP_CONTROL_ENABLE_MASK) >> MAC_SLEEP_CONTROL_ENABLE_LSB)
+#define MAC_SLEEP_CONTROL_ENABLE_SET(x) (((x) << MAC_SLEEP_CONTROL_ENABLE_LSB) & MAC_SLEEP_CONTROL_ENABLE_MASK)
+
+#define KEEP_AWAKE_ADDRESS 0x0c0000ec
+#define KEEP_AWAKE_OFFSET 0x000000ec
+#define KEEP_AWAKE_COUNT_MSB 7
+#define KEEP_AWAKE_COUNT_LSB 0
+#define KEEP_AWAKE_COUNT_MASK 0x000000ff
+#define KEEP_AWAKE_COUNT_GET(x) (((x) & KEEP_AWAKE_COUNT_MASK) >> KEEP_AWAKE_COUNT_LSB)
+#define KEEP_AWAKE_COUNT_SET(x) (((x) << KEEP_AWAKE_COUNT_LSB) & KEEP_AWAKE_COUNT_MASK)
+
+#define CHIP_REV_ADDRESS 0x0c0000f0
+#define CHIP_REV_OFFSET 0x000000f0
+#define CHIP_REV_ID_MSB 7
+#define CHIP_REV_ID_LSB 0
+#define CHIP_REV_ID_MASK 0x000000ff
+#define CHIP_REV_ID_GET(x) (((x) & CHIP_REV_ID_MASK) >> CHIP_REV_ID_LSB)
+#define CHIP_REV_ID_SET(x) (((x) << CHIP_REV_ID_LSB) & CHIP_REV_ID_MASK)
+
+#define DERIVED_RTC_CLK_ADDRESS 0x0c0000f4
+#define DERIVED_RTC_CLK_OFFSET 0x000000f4
+#define DERIVED_RTC_CLK_EXTERNAL_DETECT_MSB 18
+#define DERIVED_RTC_CLK_EXTERNAL_DETECT_LSB 18
+#define DERIVED_RTC_CLK_EXTERNAL_DETECT_MASK 0x00040000
+#define DERIVED_RTC_CLK_EXTERNAL_DETECT_GET(x) (((x) & DERIVED_RTC_CLK_EXTERNAL_DETECT_MASK) >> DERIVED_RTC_CLK_EXTERNAL_DETECT_LSB)
+#define DERIVED_RTC_CLK_EXTERNAL_DETECT_SET(x) (((x) << DERIVED_RTC_CLK_EXTERNAL_DETECT_LSB) & DERIVED_RTC_CLK_EXTERNAL_DETECT_MASK)
+#define DERIVED_RTC_CLK_FORCE_MSB 17
+#define DERIVED_RTC_CLK_FORCE_LSB 16
+#define DERIVED_RTC_CLK_FORCE_MASK 0x00030000
+#define DERIVED_RTC_CLK_FORCE_GET(x) (((x) & DERIVED_RTC_CLK_FORCE_MASK) >> DERIVED_RTC_CLK_FORCE_LSB)
+#define DERIVED_RTC_CLK_FORCE_SET(x) (((x) << DERIVED_RTC_CLK_FORCE_LSB) & DERIVED_RTC_CLK_FORCE_MASK)
+#define DERIVED_RTC_CLK_PERIOD_MSB 15
+#define DERIVED_RTC_CLK_PERIOD_LSB 1
+#define DERIVED_RTC_CLK_PERIOD_MASK 0x0000fffe
+#define DERIVED_RTC_CLK_PERIOD_GET(x) (((x) & DERIVED_RTC_CLK_PERIOD_MASK) >> DERIVED_RTC_CLK_PERIOD_LSB)
+#define DERIVED_RTC_CLK_PERIOD_SET(x) (((x) << DERIVED_RTC_CLK_PERIOD_LSB) & DERIVED_RTC_CLK_PERIOD_MASK)
+
+#define ACG_DISABLE_ADDRESS 0x0c0000f8
+#define ACG_DISABLE_OFFSET 0x000000f8
+#define ACG_DISABLE_CPU_MSB 3
+#define ACG_DISABLE_CPU_LSB 3
+#define ACG_DISABLE_CPU_MASK 0x00000008
+#define ACG_DISABLE_CPU_GET(x) (((x) & ACG_DISABLE_CPU_MASK) >> ACG_DISABLE_CPU_LSB)
+#define ACG_DISABLE_CPU_SET(x) (((x) << ACG_DISABLE_CPU_LSB) & ACG_DISABLE_CPU_MASK)
+#define ACG_DISABLE_SDIO_MSB 2
+#define ACG_DISABLE_SDIO_LSB 2
+#define ACG_DISABLE_SDIO_MASK 0x00000004
+#define ACG_DISABLE_SDIO_GET(x) (((x) & ACG_DISABLE_SDIO_MASK) >> ACG_DISABLE_SDIO_LSB)
+#define ACG_DISABLE_SDIO_SET(x) (((x) << ACG_DISABLE_SDIO_LSB) & ACG_DISABLE_SDIO_MASK)
+#define ACG_DISABLE_BB_AND_BBB_MSB 1
+#define ACG_DISABLE_BB_AND_BBB_LSB 1
+#define ACG_DISABLE_BB_AND_BBB_MASK 0x00000002
+#define ACG_DISABLE_BB_AND_BBB_GET(x) (((x) & ACG_DISABLE_BB_AND_BBB_MASK) >> ACG_DISABLE_BB_AND_BBB_LSB)
+#define ACG_DISABLE_BB_AND_BBB_SET(x) (((x) << ACG_DISABLE_BB_AND_BBB_LSB) & ACG_DISABLE_BB_AND_BBB_MASK)
+#define ACG_DISABLE_AMBA_MAC_MSB 0
+#define ACG_DISABLE_AMBA_MAC_LSB 0
+#define ACG_DISABLE_AMBA_MAC_MASK 0x00000001
+#define ACG_DISABLE_AMBA_MAC_GET(x) (((x) & ACG_DISABLE_AMBA_MAC_MASK) >> ACG_DISABLE_AMBA_MAC_LSB)
+#define ACG_DISABLE_AMBA_MAC_SET(x) (((x) << ACG_DISABLE_AMBA_MAC_LSB) & ACG_DISABLE_AMBA_MAC_MASK)
+
+#define KEY_ENABLE_ADDRESS 0x0c0000fc
+#define KEY_ENABLE_OFFSET 0x000000fc
+#define KEY_ENABLE_ON_MSB 0
+#define KEY_ENABLE_ON_LSB 0
+#define KEY_ENABLE_ON_MASK 0x00000001
+#define KEY_ENABLE_ON_GET(x) (((x) & KEY_ENABLE_ON_MASK) >> KEY_ENABLE_ON_LSB)
+#define KEY_ENABLE_ON_SET(x) (((x) << KEY_ENABLE_ON_LSB) & KEY_ENABLE_ON_MASK)
+
+#define KEY_DEBOUNCE_ADDRESS 0x0c000100
+#define KEY_DEBOUNCE_OFFSET 0x00000100
+#define KEY_DEBOUNCE_TIME_MSB 3
+#define KEY_DEBOUNCE_TIME_LSB 0
+#define KEY_DEBOUNCE_TIME_MASK 0x0000000f
+#define KEY_DEBOUNCE_TIME_GET(x) (((x) & KEY_DEBOUNCE_TIME_MASK) >> KEY_DEBOUNCE_TIME_LSB)
+#define KEY_DEBOUNCE_TIME_SET(x) (((x) << KEY_DEBOUNCE_TIME_LSB) & KEY_DEBOUNCE_TIME_MASK)
+
+#define KEY_LONG_PRESS_ADDRESS 0x0c000104
+#define KEY_LONG_PRESS_OFFSET 0x00000104
+#define KEY_LONG_PRESS_TIME_MSB 5
+#define KEY_LONG_PRESS_TIME_LSB 0
+#define KEY_LONG_PRESS_TIME_MASK 0x0000003f
+#define KEY_LONG_PRESS_TIME_GET(x) (((x) & KEY_LONG_PRESS_TIME_MASK) >> KEY_LONG_PRESS_TIME_LSB)
+#define KEY_LONG_PRESS_TIME_SET(x) (((x) << KEY_LONG_PRESS_TIME_LSB) & KEY_LONG_PRESS_TIME_MASK)
+
+#define KEY_REPEAT_ADDRESS 0x0c000108
+#define KEY_REPEAT_OFFSET 0x00000108
+#define KEY_REPEAT_TIME_MSB 5
+#define KEY_REPEAT_TIME_LSB 0
+#define KEY_REPEAT_TIME_MASK 0x0000003f
+#define KEY_REPEAT_TIME_GET(x) (((x) & KEY_REPEAT_TIME_MASK) >> KEY_REPEAT_TIME_LSB)
+#define KEY_REPEAT_TIME_SET(x) (((x) << KEY_REPEAT_TIME_LSB) & KEY_REPEAT_TIME_MASK)
+
+#define KEY_MATRIX_LO_ADDRESS 0x0c00010c
+#define KEY_MATRIX_LO_OFFSET 0x0000010c
+#define KEY_MATRIX_LO_ROW0_MSB 31
+#define KEY_MATRIX_LO_ROW0_LSB 24
+#define KEY_MATRIX_LO_ROW0_MASK 0xff000000
+#define KEY_MATRIX_LO_ROW0_GET(x) (((x) & KEY_MATRIX_LO_ROW0_MASK) >> KEY_MATRIX_LO_ROW0_LSB)
+#define KEY_MATRIX_LO_ROW0_SET(x) (((x) << KEY_MATRIX_LO_ROW0_LSB) & KEY_MATRIX_LO_ROW0_MASK)
+#define KEY_MATRIX_LO_ROW1_MSB 23
+#define KEY_MATRIX_LO_ROW1_LSB 16
+#define KEY_MATRIX_LO_ROW1_MASK 0x00ff0000
+#define KEY_MATRIX_LO_ROW1_GET(x) (((x) & KEY_MATRIX_LO_ROW1_MASK) >> KEY_MATRIX_LO_ROW1_LSB)
+#define KEY_MATRIX_LO_ROW1_SET(x) (((x) << KEY_MATRIX_LO_ROW1_LSB) & KEY_MATRIX_LO_ROW1_MASK)
+#define KEY_MATRIX_LO_ROW2_MSB 15
+#define KEY_MATRIX_LO_ROW2_LSB 8
+#define KEY_MATRIX_LO_ROW2_MASK 0x0000ff00
+#define KEY_MATRIX_LO_ROW2_GET(x) (((x) & KEY_MATRIX_LO_ROW2_MASK) >> KEY_MATRIX_LO_ROW2_LSB)
+#define KEY_MATRIX_LO_ROW2_SET(x) (((x) << KEY_MATRIX_LO_ROW2_LSB) & KEY_MATRIX_LO_ROW2_MASK)
+#define KEY_MATRIX_LO_ROW3_MSB 7
+#define KEY_MATRIX_LO_ROW3_LSB 0
+#define KEY_MATRIX_LO_ROW3_MASK 0x000000ff
+#define KEY_MATRIX_LO_ROW3_GET(x) (((x) & KEY_MATRIX_LO_ROW3_MASK) >> KEY_MATRIX_LO_ROW3_LSB)
+#define KEY_MATRIX_LO_ROW3_SET(x) (((x) << KEY_MATRIX_LO_ROW3_LSB) & KEY_MATRIX_LO_ROW3_MASK)
+
+#define KEY_MATRIX_HI_ADDRESS 0x0c000110
+#define KEY_MATRIX_HI_OFFSET 0x00000110
+#define KEY_MATRIX_HI_ROW4_MSB 31
+#define KEY_MATRIX_HI_ROW4_LSB 24
+#define KEY_MATRIX_HI_ROW4_MASK 0xff000000
+#define KEY_MATRIX_HI_ROW4_GET(x) (((x) & KEY_MATRIX_HI_ROW4_MASK) >> KEY_MATRIX_HI_ROW4_LSB)
+#define KEY_MATRIX_HI_ROW4_SET(x) (((x) << KEY_MATRIX_HI_ROW4_LSB) & KEY_MATRIX_HI_ROW4_MASK)
+#define KEY_MATRIX_HI_ROW5_MSB 23
+#define KEY_MATRIX_HI_ROW5_LSB 16
+#define KEY_MATRIX_HI_ROW5_MASK 0x00ff0000
+#define KEY_MATRIX_HI_ROW5_GET(x) (((x) & KEY_MATRIX_HI_ROW5_MASK) >> KEY_MATRIX_HI_ROW5_LSB)
+#define KEY_MATRIX_HI_ROW5_SET(x) (((x) << KEY_MATRIX_HI_ROW5_LSB) & KEY_MATRIX_HI_ROW5_MASK)
+#define KEY_MATRIX_HI_ROW6_MSB 15
+#define KEY_MATRIX_HI_ROW6_LSB 8
+#define KEY_MATRIX_HI_ROW6_MASK 0x0000ff00
+#define KEY_MATRIX_HI_ROW6_GET(x) (((x) & KEY_MATRIX_HI_ROW6_MASK) >> KEY_MATRIX_HI_ROW6_LSB)
+#define KEY_MATRIX_HI_ROW6_SET(x) (((x) << KEY_MATRIX_HI_ROW6_LSB) & KEY_MATRIX_HI_ROW6_MASK)
+#define KEY_MATRIX_HI_ROW7_MSB 7
+#define KEY_MATRIX_HI_ROW7_LSB 0
+#define KEY_MATRIX_HI_ROW7_MASK 0x000000ff
+#define KEY_MATRIX_HI_ROW7_GET(x) (((x) & KEY_MATRIX_HI_ROW7_MASK) >> KEY_MATRIX_HI_ROW7_LSB)
+#define KEY_MATRIX_HI_ROW7_SET(x) (((x) << KEY_MATRIX_HI_ROW7_LSB) & KEY_MATRIX_HI_ROW7_MASK)
+
+#define KEY_CODE_ADDRESS 0x0c000114
+#define KEY_CODE_OFFSET 0x00000114
+#define KEY_CODE_MULTIPRESS_MSB 7
+#define KEY_CODE_MULTIPRESS_LSB 7
+#define KEY_CODE_MULTIPRESS_MASK 0x00000080
+#define KEY_CODE_MULTIPRESS_GET(x) (((x) & KEY_CODE_MULTIPRESS_MASK) >> KEY_CODE_MULTIPRESS_LSB)
+#define KEY_CODE_MULTIPRESS_SET(x) (((x) << KEY_CODE_MULTIPRESS_LSB) & KEY_CODE_MULTIPRESS_MASK)
+#define KEY_CODE_ROW_MSB 6
+#define KEY_CODE_ROW_LSB 4
+#define KEY_CODE_ROW_MASK 0x00000070
+#define KEY_CODE_ROW_GET(x) (((x) & KEY_CODE_ROW_MASK) >> KEY_CODE_ROW_LSB)
+#define KEY_CODE_ROW_SET(x) (((x) << KEY_CODE_ROW_LSB) & KEY_CODE_ROW_MASK)
+#define KEY_CODE_COL_MSB 2
+#define KEY_CODE_COL_LSB 0
+#define KEY_CODE_COL_MASK 0x00000007
+#define KEY_CODE_COL_GET(x) (((x) & KEY_CODE_COL_MASK) >> KEY_CODE_COL_LSB)
+#define KEY_CODE_COL_SET(x) (((x) << KEY_CODE_COL_LSB) & KEY_CODE_COL_MASK)
+
+#define KEY_STATUS_ADDRESS 0x0c000118
+#define KEY_STATUS_OFFSET 0x00000118
+#define KEY_STATUS_KEY_RELEASE_ENABLE_MSB 7
+#define KEY_STATUS_KEY_RELEASE_ENABLE_LSB 7
+#define KEY_STATUS_KEY_RELEASE_ENABLE_MASK 0x00000080
+#define KEY_STATUS_KEY_RELEASE_ENABLE_GET(x) (((x) & KEY_STATUS_KEY_RELEASE_ENABLE_MASK) >> KEY_STATUS_KEY_RELEASE_ENABLE_LSB)
+#define KEY_STATUS_KEY_RELEASE_ENABLE_SET(x) (((x) << KEY_STATUS_KEY_RELEASE_ENABLE_LSB) & KEY_STATUS_KEY_RELEASE_ENABLE_MASK)
+#define KEY_STATUS_FIRST_PRESS_ENABLE_MSB 6
+#define KEY_STATUS_FIRST_PRESS_ENABLE_LSB 6
+#define KEY_STATUS_FIRST_PRESS_ENABLE_MASK 0x00000040
+#define KEY_STATUS_FIRST_PRESS_ENABLE_GET(x) (((x) & KEY_STATUS_FIRST_PRESS_ENABLE_MASK) >> KEY_STATUS_FIRST_PRESS_ENABLE_LSB)
+#define KEY_STATUS_FIRST_PRESS_ENABLE_SET(x) (((x) << KEY_STATUS_FIRST_PRESS_ENABLE_LSB) & KEY_STATUS_FIRST_PRESS_ENABLE_MASK)
+#define KEY_STATUS_LONG_PRESS_ENABLE_MSB 5
+#define KEY_STATUS_LONG_PRESS_ENABLE_LSB 5
+#define KEY_STATUS_LONG_PRESS_ENABLE_MASK 0x00000020
+#define KEY_STATUS_LONG_PRESS_ENABLE_GET(x) (((x) & KEY_STATUS_LONG_PRESS_ENABLE_MASK) >> KEY_STATUS_LONG_PRESS_ENABLE_LSB)
+#define KEY_STATUS_LONG_PRESS_ENABLE_SET(x) (((x) << KEY_STATUS_LONG_PRESS_ENABLE_LSB) & KEY_STATUS_LONG_PRESS_ENABLE_MASK)
+#define KEY_STATUS_REPEAT_PRESS_ENABLE_MSB 4
+#define KEY_STATUS_REPEAT_PRESS_ENABLE_LSB 4
+#define KEY_STATUS_REPEAT_PRESS_ENABLE_MASK 0x00000010
+#define KEY_STATUS_REPEAT_PRESS_ENABLE_GET(x) (((x) & KEY_STATUS_REPEAT_PRESS_ENABLE_MASK) >> KEY_STATUS_REPEAT_PRESS_ENABLE_LSB)
+#define KEY_STATUS_REPEAT_PRESS_ENABLE_SET(x) (((x) << KEY_STATUS_REPEAT_PRESS_ENABLE_LSB) & KEY_STATUS_REPEAT_PRESS_ENABLE_MASK)
+#define KEY_STATUS_KEY_RELEASE_INTERRUPT_MSB 3
+#define KEY_STATUS_KEY_RELEASE_INTERRUPT_LSB 3
+#define KEY_STATUS_KEY_RELEASE_INTERRUPT_MASK 0x00000008
+#define KEY_STATUS_KEY_RELEASE_INTERRUPT_GET(x) (((x) & KEY_STATUS_KEY_RELEASE_INTERRUPT_MASK) >> KEY_STATUS_KEY_RELEASE_INTERRUPT_LSB)
+#define KEY_STATUS_KEY_RELEASE_INTERRUPT_SET(x) (((x) << KEY_STATUS_KEY_RELEASE_INTERRUPT_LSB) & KEY_STATUS_KEY_RELEASE_INTERRUPT_MASK)
+#define KEY_STATUS_FIRST_PRESS_INTERRUPT_MSB 2
+#define KEY_STATUS_FIRST_PRESS_INTERRUPT_LSB 2
+#define KEY_STATUS_FIRST_PRESS_INTERRUPT_MASK 0x00000004
+#define KEY_STATUS_FIRST_PRESS_INTERRUPT_GET(x) (((x) & KEY_STATUS_FIRST_PRESS_INTERRUPT_MASK) >> KEY_STATUS_FIRST_PRESS_INTERRUPT_LSB)
+#define KEY_STATUS_FIRST_PRESS_INTERRUPT_SET(x) (((x) << KEY_STATUS_FIRST_PRESS_INTERRUPT_LSB) & KEY_STATUS_FIRST_PRESS_INTERRUPT_MASK)
+#define KEY_STATUS_LONG_PRESS_INTERRUPT_MSB 1
+#define KEY_STATUS_LONG_PRESS_INTERRUPT_LSB 1
+#define KEY_STATUS_LONG_PRESS_INTERRUPT_MASK 0x00000002
+#define KEY_STATUS_LONG_PRESS_INTERRUPT_GET(x) (((x) & KEY_STATUS_LONG_PRESS_INTERRUPT_MASK) >> KEY_STATUS_LONG_PRESS_INTERRUPT_LSB)
+#define KEY_STATUS_LONG_PRESS_INTERRUPT_SET(x) (((x) << KEY_STATUS_LONG_PRESS_INTERRUPT_LSB) & KEY_STATUS_LONG_PRESS_INTERRUPT_MASK)
+#define KEY_STATUS_REPEAT_PRESS_INTERRUPT_MSB 0
+#define KEY_STATUS_REPEAT_PRESS_INTERRUPT_LSB 0
+#define KEY_STATUS_REPEAT_PRESS_INTERRUPT_MASK 0x00000001
+#define KEY_STATUS_REPEAT_PRESS_INTERRUPT_GET(x) (((x) & KEY_STATUS_REPEAT_PRESS_INTERRUPT_MASK) >> KEY_STATUS_REPEAT_PRESS_INTERRUPT_LSB)
+#define KEY_STATUS_REPEAT_PRESS_INTERRUPT_SET(x) (((x) << KEY_STATUS_REPEAT_PRESS_INTERRUPT_LSB) & KEY_STATUS_REPEAT_PRESS_INTERRUPT_MASK)
+
+#ifndef __ASSEMBLER__
+typedef struct rtc_reg_s {
+ volatile unsigned int reset_control;
+ volatile unsigned int xtal_control;
+ volatile unsigned int tcxo_detect;
+ volatile unsigned int xtal_test;
+ volatile unsigned int quadrature;
+ volatile unsigned int pll_control;
+ volatile unsigned int pll_settle;
+ volatile unsigned int xtal_settle;
+ volatile unsigned int core_clock;
+ volatile unsigned int cpu_clock;
+ volatile unsigned int clock_out;
+ volatile unsigned int clock_control;
+ volatile unsigned int bias_override;
+ volatile unsigned int ref_voltage_trim;
+ volatile unsigned int ldo_control;
+ volatile unsigned int wdt_control;
+ volatile unsigned int wdt_status;
+ volatile unsigned int wdt;
+ volatile unsigned int wdt_count;
+ volatile unsigned int wdt_reset;
+ volatile unsigned int int_status;
+ volatile unsigned int lf_timer0;
+ volatile unsigned int lf_timer_count0;
+ volatile unsigned int lf_timer_control0;
+ volatile unsigned int lf_timer_status0;
+ volatile unsigned int lf_timer1;
+ volatile unsigned int lf_timer_count1;
+ volatile unsigned int lf_timer_control1;
+ volatile unsigned int lf_timer_status1;
+ volatile unsigned int lf_timer2;
+ volatile unsigned int lf_timer_count2;
+ volatile unsigned int lf_timer_control2;
+ volatile unsigned int lf_timer_status2;
+ volatile unsigned int lf_timer3;
+ volatile unsigned int lf_timer_count3;
+ volatile unsigned int lf_timer_control3;
+ volatile unsigned int lf_timer_status3;
+ volatile unsigned int hf_timer;
+ volatile unsigned int hf_timer_count;
+ volatile unsigned int hf_lf_count;
+ volatile unsigned int hf_timer_control;
+ volatile unsigned int hf_timer_status;
+ volatile unsigned int rtc_control;
+ volatile unsigned int rtc_time;
+ volatile unsigned int rtc_date;
+ volatile unsigned int rtc_set_time;
+ volatile unsigned int rtc_set_date;
+ volatile unsigned int rtc_set_alarm;
+ volatile unsigned int rtc_config;
+ volatile unsigned int rtc_alarm_status;
+ volatile unsigned int uart_wakeup;
+ volatile unsigned int reset_cause;
+ volatile unsigned int system_sleep;
+ volatile unsigned int ldo_voltage;
+ volatile unsigned int ldo_a_voltage;
+ volatile unsigned int sdio_ldo_voltage;
+ volatile unsigned int core_pad_enable;
+ volatile unsigned int sdio_wrapper;
+ volatile unsigned int mac_sleep_control;
+ volatile unsigned int keep_awake;
+ volatile unsigned int chip_rev;
+ volatile unsigned int derived_rtc_clk;
+ volatile unsigned int acg_disable;
+ volatile unsigned int key_enable;
+ volatile unsigned int key_debounce;
+ volatile unsigned int key_long_press;
+ volatile unsigned int key_repeat;
+ volatile unsigned int key_matrix_lo;
+ volatile unsigned int key_matrix_hi;
+ volatile unsigned int key_code;
+ volatile unsigned int key_status;
+} rtc_reg_t;
+#endif /* __ASSEMBLER__ */
+
+#endif /* _RTC_H_ */
Added: developers/nbd/ar6k/include/ieee80211.h
===================================================================
--- developers/nbd/ar6k/include/ieee80211.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/ieee80211.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,333 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * Copyright 2006 ATheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * */
+#ifndef _NET80211_IEEE80211_H_
+#define _NET80211_IEEE80211_H_
+
+/*
+ * 802.11 protocol definitions.
+ */
+
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+/* is 802.11 address multicast/broadcast? */
+#define IEEE80211_IS_MULTICAST(_a) (*(_a) & 0x01)
+#define IEEE80211_ADDR_EQ(addr1, addr2) \
+ (A_MEMCMP(addr1, addr2, IEEE80211_ADDR_LEN) == 0)
+
+#define IEEE80211_KEYBUF_SIZE 16
+#define IEEE80211_MICBUF_SIZE (8+8) /* space for both tx and rx */
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering. In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define IEEE80211_CIPHER_WEP 0
+#define IEEE80211_CIPHER_TKIP 1
+#define IEEE80211_CIPHER_AES_OCB 2
+#define IEEE80211_CIPHER_AES_CCM 3
+#define IEEE80211_CIPHER_CKIP 5
+#define IEEE80211_CIPHER_CCKM_KRK 6
+#define IEEE80211_CIPHER_NONE 7 /* pseudo value */
+
+#define IEEE80211_CIPHER_MAX (IEEE80211_CIPHER_NONE+1)
+
+#define IEEE80211_IS_VALID_WEP_CIPHER_LEN(len) \
+ (((len) == 5) || ((len) == 13) || ((len) == 16))
+
+/*
+ * generic definitions for IEEE 802.11 frames
+ */
+struct ieee80211_frame {
+ A_UINT8 i_fc[2];
+ A_UINT8 i_dur[2];
+ A_UINT8 i_addr1[IEEE80211_ADDR_LEN];
+ A_UINT8 i_addr2[IEEE80211_ADDR_LEN];
+ A_UINT8 i_addr3[IEEE80211_ADDR_LEN];
+ A_UINT8 i_seq[2];
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+} __ATTRIB_PACK;
+
+#define IEEE80211_FC0_VERSION_MASK 0x03
+#define IEEE80211_FC0_VERSION_SHIFT 0
+#define IEEE80211_FC0_VERSION_0 0x00
+#define IEEE80211_FC0_TYPE_MASK 0x0c
+#define IEEE80211_FC0_TYPE_SHIFT 2
+#define IEEE80211_FC0_TYPE_MGT 0x00
+#define IEEE80211_FC0_TYPE_CTL 0x04
+#define IEEE80211_FC0_TYPE_DATA 0x08
+
+#define IEEE80211_FC0_SUBTYPE_MASK 0xf0
+#define IEEE80211_FC0_SUBTYPE_SHIFT 4
+/* for TYPE_MGT */
+#define IEEE80211_FC0_SUBTYPE_ASSOC_REQ 0x00
+#define IEEE80211_FC0_SUBTYPE_ASSOC_RESP 0x10
+#define IEEE80211_FC0_SUBTYPE_REASSOC_REQ 0x20
+#define IEEE80211_FC0_SUBTYPE_REASSOC_RESP 0x30
+#define IEEE80211_FC0_SUBTYPE_PROBE_REQ 0x40
+#define IEEE80211_FC0_SUBTYPE_PROBE_RESP 0x50
+#define IEEE80211_FC0_SUBTYPE_BEACON 0x80
+#define IEEE80211_FC0_SUBTYPE_ATIM 0x90
+#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
+#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0
+#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0
+/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
+#define IEEE80211_FC0_SUBTYPE_RTS 0xb0
+#define IEEE80211_FC0_SUBTYPE_CTS 0xc0
+#define IEEE80211_FC0_SUBTYPE_ACK 0xd0
+#define IEEE80211_FC0_SUBTYPE_CF_END 0xe0
+#define IEEE80211_FC0_SUBTYPE_CF_END_ACK 0xf0
+/* for TYPE_DATA (bit combination) */
+#define IEEE80211_FC0_SUBTYPE_DATA 0x00
+#define IEEE80211_FC0_SUBTYPE_CF_ACK 0x10
+#define IEEE80211_FC0_SUBTYPE_CF_POLL 0x20
+#define IEEE80211_FC0_SUBTYPE_CF_ACPL 0x30
+#define IEEE80211_FC0_SUBTYPE_NODATA 0x40
+#define IEEE80211_FC0_SUBTYPE_CFACK 0x50
+#define IEEE80211_FC0_SUBTYPE_CFPOLL 0x60
+#define IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK 0x70
+#define IEEE80211_FC0_SUBTYPE_QOS 0x80
+#define IEEE80211_FC0_SUBTYPE_QOS_NULL 0xc0
+
+#define IEEE80211_FC1_DIR_MASK 0x03
+#define IEEE80211_FC1_DIR_NODS 0x00 /* STA->STA */
+#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
+#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */
+#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+
+#define IEEE80211_FC1_MORE_FRAG 0x04
+#define IEEE80211_FC1_RETRY 0x08
+#define IEEE80211_FC1_PWR_MGT 0x10
+#define IEEE80211_FC1_MORE_DATA 0x20
+#define IEEE80211_FC1_WEP 0x40
+#define IEEE80211_FC1_ORDER 0x80
+
+#define IEEE80211_SEQ_FRAG_MASK 0x000f
+#define IEEE80211_SEQ_FRAG_SHIFT 0
+#define IEEE80211_SEQ_SEQ_MASK 0xfff0
+#define IEEE80211_SEQ_SEQ_SHIFT 4
+
+#define IEEE80211_NWID_LEN 32
+
+/*
+ * 802.11 rate set.
+ */
+#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
+#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
+
+#define WMM_NUM_AC 4 /* 4 AC categories */
+
+#define WMM_PARAM_ACI_M 0x60 /* Mask for ACI field */
+#define WMM_PARAM_ACI_S 5 /* Shift for ACI field */
+#define WMM_PARAM_ACM_M 0x10 /* Mask for ACM bit */
+#define WMM_PARAM_ACM_S 4 /* Shift for ACM bit */
+#define WMM_PARAM_AIFSN_M 0x0f /* Mask for aifsn field */
+#define WMM_PARAM_LOGCWMIN_M 0x0f /* Mask for CwMin field (in log) */
+#define WMM_PARAM_LOGCWMAX_M 0xf0 /* Mask for CwMax field (in log) */
+#define WMM_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */
+
+#define WMM_AC_TO_TID(_ac) ( \
+ ((_ac) == WMM_AC_VO) ? 6 : \
+ ((_ac) == WMM_AC_VI) ? 5 : \
+ ((_ac) == WMM_AC_BK) ? 1 : \
+ 0)
+
+#define TID_TO_WMM_AC(_tid) ( \
+ ((_tid) < 1) ? WMM_AC_BE : \
+ ((_tid) < 3) ? WMM_AC_BK : \
+ ((_tid) < 6) ? WMM_AC_VI : \
+ WMM_AC_VO)
+/*
+ * Management information element payloads.
+ */
+
+enum {
+ IEEE80211_ELEMID_SSID = 0,
+ IEEE80211_ELEMID_RATES = 1,
+ IEEE80211_ELEMID_FHPARMS = 2,
+ IEEE80211_ELEMID_DSPARMS = 3,
+ IEEE80211_ELEMID_CFPARMS = 4,
+ IEEE80211_ELEMID_TIM = 5,
+ IEEE80211_ELEMID_IBSSPARMS = 6,
+ IEEE80211_ELEMID_COUNTRY = 7,
+ IEEE80211_ELEMID_CHALLENGE = 16,
+ /* 17-31 reserved for challenge text extension */
+ IEEE80211_ELEMID_PWRCNSTR = 32,
+ IEEE80211_ELEMID_PWRCAP = 33,
+ IEEE80211_ELEMID_TPCREQ = 34,
+ IEEE80211_ELEMID_TPCREP = 35,
+ IEEE80211_ELEMID_SUPPCHAN = 36,
+ IEEE80211_ELEMID_CHANSWITCH = 37,
+ IEEE80211_ELEMID_MEASREQ = 38,
+ IEEE80211_ELEMID_MEASREP = 39,
+ IEEE80211_ELEMID_QUIET = 40,
+ IEEE80211_ELEMID_IBSSDFS = 41,
+ IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_RSN = 48,
+ IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_TPC = 150,
+ IEEE80211_ELEMID_CCKM = 156,
+ IEEE80211_ELEMID_VENDOR = 221, /* vendor private */
+};
+
+#define ATH_OUI 0x7f0300 /* Atheros OUI */
+#define ATH_OUI_TYPE 0x01
+#define ATH_OUI_SUBTYPE 0x01
+#define ATH_OUI_VERSION 0x00
+
+#define WPA_OUI 0xf25000
+#define WPA_OUI_TYPE 0x01
+#define WPA_VERSION 1 /* current supported version */
+
+#define WPA_CSE_NULL 0x00
+#define WPA_CSE_WEP40 0x01
+#define WPA_CSE_TKIP 0x02
+#define WPA_CSE_CCMP 0x04
+#define WPA_CSE_WEP104 0x05
+
+#define WPA_ASE_NONE 0x00
+#define WPA_ASE_8021X_UNSPEC 0x01
+#define WPA_ASE_8021X_PSK 0x02
+
+#define RSN_OUI 0xac0f00
+#define RSN_VERSION 1 /* current supported version */
+
+#define RSN_CSE_NULL 0x00
+#define RSN_CSE_WEP40 0x01
+#define RSN_CSE_TKIP 0x02
+#define RSN_CSE_WRAP 0x03
+#define RSN_CSE_CCMP 0x04
+#define RSN_CSE_WEP104 0x05
+
+#define RSN_ASE_NONE 0x00
+#define RSN_ASE_8021X_UNSPEC 0x01
+#define RSN_ASE_8021X_PSK 0x02
+
+#define RSN_CAP_PREAUTH 0x01
+
+#define WMM_OUI 0xf25000
+#define WMM_OUI_TYPE 0x02
+#define WMM_INFO_OUI_SUBTYPE 0x00
+#define WMM_PARAM_OUI_SUBTYPE 0x01
+#define WMM_VERSION 1
+
+/* WMM stream classes */
+#define WMM_NUM_AC 4
+#define WMM_AC_BE 0 /* best effort */
+#define WMM_AC_BK 1 /* background */
+#define WMM_AC_VI 2 /* video */
+#define WMM_AC_VO 3 /* voice */
+
+/* TSPEC related */
+#define ACTION_CATEGORY_CODE_TSPEC 17
+#define ACTION_CODE_TSPEC_ADDTS 0
+#define ACTION_CODE_TSPEC_ADDTS_RESP 1
+#define ACTION_CODE_TSPEC_DELTS 2
+
+typedef enum {
+ TSPEC_STATUS_CODE_ADMISSION_ACCEPTED = 0,
+ TSPEC_STATUS_CODE_ADDTS_INVALID_PARAMS = 0x1,
+ TSPEC_STATUS_CODE_ADDTS_REQUEST_REFUSED = 0x3,
+ TSPEC_STATUS_CODE_UNSPECIFIED_QOS_RELATED_FAILURE = 0xC8,
+ TSPEC_STATUS_CODE_REQUESTED_REFUSED_POLICY_CONFIGURATION = 0xC9,
+ TSPEC_STATUS_CODE_INSUFFCIENT_BANDWIDTH = 0xCA,
+ TSPEC_STATUS_CODE_INVALID_PARAMS = 0xCB,
+ TSPEC_STATUS_CODE_DELTS_SENT = 0x30,
+ TSPEC_STATUS_CODE_DELTS_RECV = 0x31,
+} TSPEC_STATUS_CODE;
+
+/*
+ * WMM/802.11e Tspec Element
+ */
+typedef struct wmm_tspec_ie_t {
+ A_UINT8 elementId;
+ A_UINT8 len;
+ A_UINT8 oui[3];
+ A_UINT8 ouiType;
+ A_UINT8 ouiSubType;
+ A_UINT8 version;
+ A_UINT16 tsInfo_info;
+ A_UINT8 tsInfo_reserved;
+ A_UINT16 nominalMSDU;
+ A_UINT16 maxMSDU;
+ A_UINT32 minServiceInt;
+ A_UINT32 maxServiceInt;
+ A_UINT32 inactivityInt;
+ A_UINT32 suspensionInt;
+ A_UINT32 serviceStartTime;
+ A_UINT32 minDataRate;
+ A_UINT32 meanDataRate;
+ A_UINT32 peakDataRate;
+ A_UINT32 maxBurstSize;
+ A_UINT32 delayBound;
+ A_UINT32 minPhyRate;
+ A_UINT16 sba;
+ A_UINT16 mediumTime;
+} __ATTRIB_PACK WMM_TSPEC_IE;
+
+
+/*
+ * BEACON management packets
+ *
+ * octet timestamp[8]
+ * octet beacon interval[2]
+ * octet capability information[2]
+ * information element
+ * octet elemid
+ * octet length
+ * octet information[length]
+ */
+
+#define IEEE80211_BEACON_INTERVAL(beacon) \
+ ((beacon)[8] | ((beacon)[9] << 8))
+#define IEEE80211_BEACON_CAPABILITY(beacon) \
+ ((beacon)[10] | ((beacon)[11] << 8))
+
+#define IEEE80211_CAPINFO_ESS 0x0001
+#define IEEE80211_CAPINFO_IBSS 0x0002
+#define IEEE80211_CAPINFO_CF_POLLABLE 0x0004
+#define IEEE80211_CAPINFO_CF_POLLREQ 0x0008
+#define IEEE80211_CAPINFO_PRIVACY 0x0010
+#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020
+#define IEEE80211_CAPINFO_PBCC 0x0040
+#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080
+/* bits 8-9 are reserved */
+#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400
+#define IEEE80211_CAPINFO_APSD 0x0800
+/* bit 12 is reserved */
+#define IEEE80211_CAPINFO_DSSSOFDM 0x2000
+/* bits 14-15 are reserved */
+
+/*
+ * Authentication Modes
+ */
+
+enum ieee80211_authmode {
+ IEEE80211_AUTH_NONE = 0,
+ IEEE80211_AUTH_OPEN = 1,
+ IEEE80211_AUTH_SHARED = 2,
+ IEEE80211_AUTH_8021X = 3,
+ IEEE80211_AUTH_AUTO = 4, /* auto-select/accept */
+ /* NB: these are used only for ioctls */
+ IEEE80211_AUTH_WPA = 5, /* WPA/RSN w/ 802.1x */
+ IEEE80211_AUTH_WPA_PSK = 6, /* WPA/RSN w/ PSK */
+ IEEE80211_AUTH_WPA_CCKM = 7, /* WPA/RSN IE w/ CCKM */
+};
+
+#endif /* _NET80211_IEEE80211_H_ */
Added: developers/nbd/ar6k/include/ieee80211_ioctl.h
===================================================================
--- developers/nbd/ar6k/include/ieee80211_ioctl.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/ieee80211_ioctl.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * Portions Copyright (c) 2001 Atsushi Onoe, 2002-2005 Sam Leffler, Errno Consulting
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions for the AR6000 linux driver.
+ *
+ */
+
+#ifndef _IEEE80211_IOCTL_H_
+#define _IEEE80211_IOCTL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Extracted from the MADWIFI net80211/ieee80211_ioctl.h
+ */
+
+/*
+ * WPA/RSN get/set key request. Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving. The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap). They key length
+ * must include any MIC key data; otherwise it should be no
+ more than IEEE80211_KEYBUF_SIZE.
+ */
+struct ieee80211req_key {
+ u_int8_t ik_type; /* key/cipher type */
+ u_int8_t ik_pad;
+ u_int16_t ik_keyix; /* key index */
+ u_int8_t ik_keylen; /* key length in bytes */
+ u_int8_t ik_flags;
+#define IEEE80211_KEY_XMIT 0x01
+#define IEEE80211_KEY_RECV 0x02
+#define IEEE80211_KEY_DEFAULT 0x80 /* default xmit key */
+ u_int8_t ik_macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t ik_keyrsc; /* key receive sequence counter */
+ u_int64_t ik_keytsc; /* key transmit sequence counter */
+ u_int8_t ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+};
+/*
+ * Delete a key either by index or address. Set the index
+ * to IEEE80211_KEYIX_NONE when deleting a unicast key.
+ */
+struct ieee80211req_del_key {
+ u_int8_t idk_keyix; /* key index */
+ u_int8_t idk_macaddr[IEEE80211_ADDR_LEN];
+};
+/*
+ * MLME state manipulation request. IEEE80211_MLME_ASSOC
+ * only makes sense when operating as a station. The other
+ * requests can be used when operating as a station or an
+ * ap (to effect a station).
+ */
+struct ieee80211req_mlme {
+ u_int8_t im_op; /* operation to perform */
+#define IEEE80211_MLME_ASSOC 1 /* associate station */
+#define IEEE80211_MLME_DISASSOC 2 /* disassociate station */
+#define IEEE80211_MLME_DEAUTH 3 /* deauthenticate station */
+#define IEEE80211_MLME_AUTHORIZE 4 /* authorize station */
+#define IEEE80211_MLME_UNAUTHORIZE 5 /* unauthorize station */
+ u_int16_t im_reason; /* 802.11 reason code */
+ u_int8_t im_macaddr[IEEE80211_ADDR_LEN];
+};
+
+struct ieee80211req_addpmkid {
+ u_int8_t pi_bssid[IEEE80211_ADDR_LEN];
+ u_int8_t pi_enable;
+ u_int8_t pi_pmkid[16];
+};
+
+#define AUTH_ALG_OPEN_SYSTEM 0x01
+#define AUTH_ALG_SHARED_KEY 0x02
+#define AUTH_ALG_LEAP 0x04
+
+struct ieee80211req_authalg {
+ u_int8_t auth_alg;
+};
+
+enum {
+ IEEE80211_PARAM_AUTHMODE = 3, /* Authentication Mode */
+ IEEE80211_PARAM_MCASTCIPHER = 5,
+ IEEE80211_PARAM_MCASTKEYLEN = 6, /* multicast key length */
+ IEEE80211_PARAM_UCASTCIPHER = 8,
+ IEEE80211_PARAM_UCASTKEYLEN = 9, /* unicast key length */
+ IEEE80211_PARAM_WPA = 10, /* WPA mode (0,1,2) */
+ IEEE80211_PARAM_ROAMING = 12, /* roaming mode */
+ IEEE80211_PARAM_PRIVACY = 13, /* privacy invoked */
+ IEEE80211_PARAM_COUNTERMEASURES = 14, /* WPA/TKIP countermeasures */
+ IEEE80211_PARAM_DROPUNENCRYPTED = 15, /* discard unencrypted frames */
+};
+
+/*
+ * Values for IEEE80211_PARAM_WPA
+ */
+#define WPA_MODE_WPA1 1
+#define WPA_MODE_WPA2 2
+#define WPA_MODE_AUTO 3
+#define WPA_MODE_NONE 4
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _IEEE80211_IOCTL_H_ */
Added: developers/nbd/ar6k/include/ieee80211_node.h
===================================================================
--- developers/nbd/ar6k/include/ieee80211_node.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/ieee80211_node.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * Copyright 2006 ATheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+#ifndef _IEEE80211_NODE_H_
+#define _IEEE80211_NODE_H_
+
+/*
+ * Node locking definitions.
+ */
+#define IEEE80211_NODE_LOCK_INIT(_nt) A_MUTEX_INIT(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt)
+#define IEEE80211_NODE_LOCK(_nt) A_MUTEX_LOCK(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_UNLOCK(_nt) A_MUTEX_UNLOCK(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK_BH(_nt) A_MUTEX_LOCK(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_UNLOCK_BH(_nt) A_MUTEX_UNLOCK(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK_ASSERT(_nt)
+
+/*
+ * Node reference counting definitions.
+ *
+ * ieee80211_node_initref initialize the reference count to 1
+ * ieee80211_node_incref add a reference
+ * ieee80211_node_decref remove a reference
+ * ieee80211_node_dectestref remove a reference and return 1 if this
+ * is the last reference, otherwise 0
+ * ieee80211_node_refcnt reference count for printing (only)
+ */
+#define ieee80211_node_initref(_ni) ((_ni)->ni_refcnt = 1)
+#define ieee80211_node_incref(_ni) ((_ni)->ni_refcnt++)
+#define ieee80211_node_decref(_ni) ((_ni)->ni_refcnt--)
+#define ieee80211_node_dectestref(_ni) (((_ni)->ni_refcnt--) == 0)
+#define ieee80211_node_refcnt(_ni) ((_ni)->ni_refcnt)
+
+#define IEEE80211_NODE_HASHSIZE 32
+/* simple hash is enough for variation of macaddr */
+#define IEEE80211_NODE_HASH(addr) \
+ (((const A_UINT8 *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+ IEEE80211_NODE_HASHSIZE)
+
+/*
+ * Table of ieee80211_node instances. Each ieee80211com
+ * has at least one for holding the scan candidates.
+ * When operating as an access point or in ibss mode there
+ * is a second table for associated stations or neighbors.
+ */
+struct ieee80211_node_table {
+ void *nt_wmip; /* back reference */
+ A_MUTEX_T nt_nodelock; /* on node table */
+ struct bss *nt_node_first; /* information of all nodes */
+ struct bss *nt_node_last; /* information of all nodes */
+ struct bss *nt_hash[IEEE80211_NODE_HASHSIZE];
+ const char *nt_name; /* for debugging */
+ A_UINT32 nt_scangen; /* gen# for timeout scan */
+ A_TIMER nt_inact_timer;
+};
+
+#define WLAN_NODE_INACT_TIMEOUT_MSEC 10000
+
+#endif /* _IEEE80211_NODE_H_ */
Added: developers/nbd/ar6k/include/osapi.h
===================================================================
--- developers/nbd/ar6k/include/osapi.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/osapi.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions of the basic atheros data types.
+ * It is used to map the data types in atheros files to a platform specific
+ * type.
+ *
+ */
+
+#ifndef _OSAPI_H_
+#define _OSAPI_H_
+
+#ifdef __KERNEL__
+
+#ifdef __linux__
+#include "../include/osapi_linux.h"
+#endif
+
+/*
+ * OS specific network buffer acess routines
+ */
+void *a_netbuf_alloc(int size);
+void a_netbuf_free(void *bufPtr);
+void *a_netbuf_to_data(void *bufPtr);
+A_UINT32 a_netbuf_to_len(void *bufPtr);
+A_STATUS a_netbuf_push(void *bufPtr, A_INT32 len);
+A_STATUS a_netbuf_put(void *bufPtr, A_INT32 len);
+A_STATUS a_netbuf_trim(void *bufPtr, A_INT32 len);
+A_INT32 a_netbuf_headroom(void *bufPtr);
+A_STATUS a_netbuf_pull(void *bufPtr, A_INT32 len);
+A_UINT32 a_copy_to_user(void *to, const void *from, A_UINT32 n);
+A_UINT32 a_copy_from_user(void *to, const void *from, A_UINT32 n);
+#endif /* __KERNEL__ */
+
+#ifdef __GNUC__
+#define __ATTRIB_PACK __attribute__ ((packed))
+#define __ATTRIB_PRINTF __attribute__ ((format (printf, 1, 2)))
+#define __ATTRIB_NORETURN __attribute__ ((noreturn))
+#ifndef INLINE
+#define INLINE __inline__
+#endif
+#else /* Not GCC */
+#define __ATTRIB_PACK
+#define __ATTRIB_PRINTF
+#define __ATTRIB_NORETURN
+#ifndef INLINE
+#define INLINE __inline
+#endif
+#endif /* End __GNUC__ */
+
+#endif /* _OSAPI_H_ */
Added: developers/nbd/ar6k/include/osapi_linux.h
===================================================================
--- developers/nbd/ar6k/include/osapi_linux.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/osapi_linux.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions of the basic atheros data types.
+ * It is used to map the data types in atheros files to a platform specific
+ * type.
+ *
+ */
+
+#ifndef _OSAPI_LINUX_H_
+#define _OSAPI_LINUX_H_
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include <linux/jiffies.h>
+#endif
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#ifdef KERNEL_2_4
+#include <asm/arch/irq.h>
+#include <asm/irq.h>
+#endif
+
+/*
+ * Endianes macros
+ */
+#define A_BE2CPU8(x) ntohb(x)
+#define A_BE2CPU16(x) ntohs(x)
+#define A_BE2CPU32(x) ntohl(x)
+
+#define A_LE2CPU8(x) (x)
+#define A_LE2CPU16(x) (x)
+#define A_LE2CPU32(x) (x)
+
+#define A_CPU2BE8(x) htonb(x)
+#define A_CPU2BE16(x) htons(x)
+#define A_CPU2BE32(x) htonl(x)
+
+#define A_MEMCPY(dst, src, len) memcpy((A_UINT8 *)(dst), (src), (len))
+#define A_MEMZERO(addr, len) memset(addr, 0, len)
+#define A_MEMCMP(addr1, addr2, len) memcmp((addr1), (addr2), (len))
+#define A_MALLOC(size) kmalloc((size), GFP_KERNEL)
+#define A_MALLOC_NOWAIT(size) kmalloc((size), GFP_ATOMIC)
+#define A_FREE(addr) kfree(addr)
+#define A_PRINTF(args...) printk(args)
+
+/* Mutual Exclusion */
+typedef spinlock_t A_MUTEX_T;
+#define A_MUTEX_INIT(mutex) spin_lock_init(mutex)
+#define A_MUTEX_LOCK(mutex) spin_lock_bh(mutex)
+#define A_MUTEX_UNLOCK(mutex) spin_unlock_bh(mutex)
+
+/*
+ * Timer Functions
+ */
+#define A_MDELAY(msecs) mdelay(msecs)
+typedef struct timer_list A_TIMER;
+
+#define A_INIT_TIMER(pTimer, pFunction, pArg) do { \
+ init_timer(pTimer); \
+ (pTimer)->function = (pFunction); \
+ (pTimer)->data = (unsigned long)(pArg); \
+} while (0)
+
+/*
+ * Start a Timer that elapses after 'periodMSec' milli-seconds
+ * Support is provided for a one-shot timer. The 'repeatFlag' is
+ * ignored.
+ */
+#define A_TIMEOUT_MS(pTimer, periodMSec, repeatFlag) do { \
+ if (repeatFlag) { \
+ printk("\n" __FILE__ ":%d: Timer Repeat requested\n",__LINE__); \
+ panic("Timer Repeat"); \
+ } \
+ mod_timer((pTimer), jiffies + HZ * (periodMSec) / 1000); \
+} while (0)
+
+/*
+ * Cancel the Timer.
+ */
+#define A_UNTIMEOUT(pTimer) do { \
+ del_timer((pTimer)); \
+} while (0)
+
+#define A_DELETE_TIMER(pTimer) do { \
+} while (0)
+
+/*
+ * Wait Queue related functions
+ */
+typedef wait_queue_head_t A_WAITQUEUE_HEAD;
+#define A_INIT_WAITQUEUE_HEAD(head) init_waitqueue_head(head)
+#ifdef mvlcee31_2_4_20_omap2420_gsm_gprs
+#ifndef wait_event_interruptible_timeout
+#define __wait_event_interruptible_timeout(wq, condition, ret) \
+do { \
+ wait_queue_t __wait; \
+ init_waitqueue_entry(&__wait, current); \
+ \
+ add_wait_queue(&wq, &__wait); \
+ for (;;) { \
+ set_current_state(TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (!signal_pending(current)) { \
+ ret = schedule_timeout(ret); \
+ if (!ret) \
+ break; \
+ continue; \
+ } \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ current->state = TASK_RUNNING; \
+ remove_wait_queue(&wq, &__wait); \
+} while (0)
+
+#define wait_event_interruptible_timeout(wq, condition, timeout) \
+({ \
+ long __ret = timeout; \
+ if (!(condition)) \
+ __wait_event_interruptible_timeout(wq, condition, __ret); \
+ __ret; \
+})
+#endif /* wait_event_interruptible_timeout */
+
+#define A_WAIT_EVENT_INTERRUPTIBLE_TIMEOUT(head, condition, timeout) do { \
+ wait_event_interruptible_timeout(head, condition, timeout); \
+} while (0)
+#else
+#define A_WAIT_EVENT_INTERRUPTIBLE_TIMEOUT(head, condition, timeout) do { \
+ wait_event_interruptible_timeout(head, condition, timeout); \
+} while (0)
+#endif /* mvlcee31_2_4_20-omap2420_gsm_gprs */
+
+#define A_WAKE_UP(head) wake_up(head)
+
+#ifdef DEBUG
+#define A_ASSERT(expr) \
+ if (!(expr)) { \
+ printk( "\n" __FILE__ ":%d: Assertion " #expr " failed!\n",__LINE__); \
+ panic(#expr); \
+ }
+
+#else
+#define A_ASSERT(expr)
+#endif /* DEBUG */
+
+#endif /* _OSAPI_LINUX_H_ */
Added: developers/nbd/ar6k/include/queue.h
===================================================================
--- developers/nbd/ar6k/include/queue.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/queue.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.58 2004/04/07 04:19:49 imp Exp $
+ * $Id: //depot/sw/releases/etnaGPL1.1/host/include/queue.h#1 $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - - - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ *
+ */
+#define QUEUE_MACRO_DEBUG 0
+#if QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ char * lastfile;
+ int lastline;
+ char * prevfile;
+ int prevline;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (0)
+
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *) \
+ ((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define ATH_LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT((elm)->field.tqe_next); \
+ TRASHIT((elm)->field.tqe_prev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+
+#ifdef _KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER)
+
+static __inline void
+insque(void *a, void *b)
+{
+ struct quehead *element = (struct quehead *)a,
+ *head = (struct quehead *)b;
+
+ element->qh_link = head->qh_link;
+ element->qh_rlink = head;
+ head->qh_link = element;
+ element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+ struct quehead *element = (struct quehead *)a;
+
+ element->qh_link->qh_rlink = element->qh_rlink;
+ element->qh_rlink->qh_link = element->qh_link;
+ element->qh_rlink = 0;
+}
+
+#else /* !(__GNUC__ || __INTEL_COMPILER) */
+
+void insque(void *a, void *b);
+void remque(void *a);
+
+#endif /* __GNUC__ || __INTEL_COMPILER */
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
Added: developers/nbd/ar6k/include/wlan_api.h
===================================================================
--- developers/nbd/ar6k/include/wlan_api.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/wlan_api.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the API for the host wlan module
+ *
+ *
+ */
+
+#ifndef _HOST_WLAN_API_H_
+#define _HOST_WLAN_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ieee80211_node_table;
+struct ieee80211_frame;
+
+struct ieee80211_common_ie {
+ A_UINT16 ie_chan;
+ A_UINT8 *ie_tstamp;
+ A_UINT8 *ie_ssid;
+ A_UINT8 *ie_rates;
+ A_UINT8 *ie_xrates;
+ A_UINT8 *ie_country;
+ A_UINT8 *ie_wpa;
+ A_UINT8 *ie_wmm;
+ A_UINT8 *ie_ath;
+ A_UINT16 ie_capInfo;
+ A_UINT16 ie_beaconInt;
+ A_UINT8 *ie_tim;
+ A_UINT8 *ie_chswitch;
+ A_UINT8 ie_erp;
+};
+
+typedef struct bss {
+ A_UINT8 ni_macaddr[6];
+ A_INT8 ni_rssi;
+ struct bss *ni_list_next;
+ struct bss *ni_list_prev;
+ struct bss *ni_hash_next;
+ struct bss *ni_hash_prev;
+ struct ieee80211_common_ie ni_cie;
+ A_UINT8 *ni_buf;
+ struct ieee80211_node_table *ni_table;
+ A_UINT32 ni_refcnt;
+ int ni_scangen;
+ A_UINT32 ni_tstamp;
+} bss_t;
+
+typedef void wlan_node_iter_func(void *arg, bss_t *);
+
+bss_t *wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size);
+void wlan_node_free(bss_t *ni);
+void wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
+ const A_UINT8 *macaddr);
+bss_t *wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr);
+void wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni);
+void wlan_free_allnodes(struct ieee80211_node_table *nt);
+void wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
+ void *arg);
+
+void wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt);
+void wlan_node_table_reset(struct ieee80211_node_table *nt);
+void wlan_node_table_cleanup(struct ieee80211_node_table *nt);
+
+A_STATUS wlan_parse_beacon(A_UINT8 *buf, int framelen,
+ struct ieee80211_common_ie *cie);
+
+A_UINT16 wlan_ieee2freq(int chan);
+A_UINT32 wlan_freq2ieee(A_UINT16 freq);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HOST_WLAN_API_H_ */
Added: developers/nbd/ar6k/include/wmi.h
===================================================================
--- developers/nbd/ar6k/include/wmi.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/wmi.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,1141 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * This file contains the definitions of the WMI protocol specified in the
+ * Wireless Module Interface (WMI). It includes definitions of all the
+ * commands and events. Commands are messages from the host to the WM.
+ * Events and Replies are messages from the WM to the host.
+ *
+ * Ownership of correctness in regards to WMI commands
+ * belongs to the host driver and the WM is not required to validate
+ * parameters for value, proper range, or any other checking.
+ *
+ * $Id: //depot/sw/releases/etnaGPL1.1/include/wmi.h#1 $
+ *
+ */
+
+#ifndef _WMI_H_
+#define _WMI_H_
+
+#include "wmix.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WMI_PROTOCOL_VERSION 0x0001
+#define WMI_PROTOCOL_REVISION 0x0000
+
+#define ATH_MAC_LEN 6 /* length of mac in bytes */
+#define WMI_CMD_MAX_LEN 100
+#define WMI_CONTROL_MSG_MAX_LEN 256
+#define WMI_OPT_CONTROL_MSG_MAX_LEN 1536
+#define IS_ETHERTYPE(_typeOrLen) ((_typeOrLen) >= 0x0600)
+#define RFC1042OUI {0x00, 0x00, 0x00}
+
+#define IP_ETHERTYPE 0x0800
+
+/*
+ * Data Path
+ */
+typedef struct {
+ A_UINT8 dstMac[ATH_MAC_LEN];
+ A_UINT8 srcMac[ATH_MAC_LEN];
+ A_UINT16 typeOrLen;
+} __ATTRIB_PACK ATH_MAC_HDR;
+
+typedef struct {
+ A_UINT8 dsap;
+ A_UINT8 ssap;
+ A_UINT8 cntl;
+ A_UINT8 orgCode[3];
+ A_UINT16 etherType;
+} __ATTRIB_PACK ATH_LLC_SNAP_HDR;
+
+typedef enum {
+ DATA_MSGTYPE = 0x0,
+ CNTL_MSGTYPE,
+ SYNC_MSGTYPE
+} WMI_MSG_TYPE;
+
+
+typedef struct {
+ A_INT8 rssi;
+ A_UINT8 info; /* WMI_MSG_TYPE in lower 2 bits - b1b0 */
+ /* UP in next 3 bits - b4b3b2 */
+#define WMI_DATA_HDR_MSG_TYPE_MASK 0x03
+#define WMI_DATA_HDR_MSG_TYPE_SHIFT 0
+#define WMI_DATA_HDR_UP_MASK 0x07
+#define WMI_DATA_HDR_UP_SHIFT 2
+#define WMI_DATA_HDR_IS_MSG_TYPE(h, t) (((h)->info & (WMI_DATA_HDR_MSG_TYPE_MASK)) == (t))
+} __ATTRIB_PACK WMI_DATA_HDR;
+
+/*
+ * Control Path
+ */
+typedef struct {
+ A_UINT16 commandId;
+} __ATTRIB_PACK WMI_CMD_HDR; /* used for commands and events */
+
+/*
+ * List of Commnands
+ */
+typedef enum {
+ WMI_CONNECT_CMDID = 0x0001,
+ WMI_RECONNECT_CMDID,
+ WMI_DISCONNECT_CMDID,
+ WMI_SYNCHRONIZE_CMDID,
+ WMI_CREATE_PSTREAM_CMDID,
+ WMI_DELETE_PSTREAM_CMDID,
+ WMI_START_SCAN_CMDID,
+ WMI_SET_SCAN_PARAMS_CMDID,
+ WMI_SET_BSS_FILTER_CMDID,
+ WMI_SET_PROBED_SSID_CMDID,
+ WMI_SET_LISTEN_INT_CMDID,
+ WMI_SET_BMISS_TIME_CMDID,
+ WMI_SET_DISC_TIMEOUT_CMDID,
+ WMI_GET_CHANNEL_LIST_CMDID,
+ WMI_SET_BEACON_INT_CMDID,
+ WMI_GET_STATISTICS_CMDID,
+ WMI_SET_CHANNEL_PARAMS_CMDID,
+ WMI_SET_POWER_MODE_CMDID,
+ WMI_SET_IBSS_PM_CAPS_CMDID,
+ WMI_SET_POWER_PARAMS_CMDID,
+ WMI_SET_POWERSAVE_TIMERS_CMDID,
+ WMI_ADD_CIPHER_KEY_CMDID,
+ WMI_DELETE_CIPHER_KEY_CMDID,
+ WMI_ADD_KRK_CMDID,
+ WMI_DELETE_KRK_CMDID,
+ WMI_SET_PMKID_CMDID,
+ WMI_SET_TX_PWR_CMDID,
+ WMI_GET_TX_PWR_CMDID,
+ WMI_SET_ASSOC_INFO_CMDID,
+ WMI_ADD_BAD_AP_CMDID,
+ WMI_DELETE_BAD_AP_CMDID,
+ WMI_SET_TKIP_COUNTERMEASURES_CMDID,
+ WMI_RSSI_THRESHOLD_PARAMS_CMDID,
+ WMI_TARGET_ERROR_REPORT_BITMASK_CMDID,
+ WMI_SET_ACCESS_PARAMS_CMDID,
+ WMI_SET_RETRY_LIMITS_CMDID,
+ WMI_SET_OPT_MODE_CMDID,
+ WMI_OPT_TX_FRAME_CMDID,
+ WMI_SET_VOICE_PKT_SIZE_CMDID,
+ WMI_SET_MAX_SP_LEN_CMDID,
+ WMI_SET_ROAM_CTRL_CMDID,
+ WMI_GET_ROAM_TBL_CMDID,
+ WMI_GET_ROAM_DATA_CMDID,
+ WMI_ENABLE_RM_CMDID,
+ WMI_SET_MAX_OFFHOME_DURATION_CMDID,
+ WMI_EXTENSION_CMDID, /* Non-wireless extensions */
+ WMI_SNR_THRESHOLD_PARAMS_CMDID,
+ WMI_LQ_THRESHOLD_PARAMS_CMDID,
+ WMI_SET_LPREAMBLE_CMDID,
+ WMI_SET_RTS_CMDID,
+ WMI_CLR_RSSI_SNR_CMDID,
+ WMI_SET_FIXRATES_CMDID,
+ WMI_GET_FIXRATES_CMDID,
+ WMI_SET_AUTH_MODE_CMDID,
+ /*
+ * Developer commands starts at 0xF000
+ */
+ WMI_SET_BITRATE_CMDID = 0xF000,
+ WMI_GET_BITRATE_CMDID,
+} WMI_COMMAND_ID;
+
+/*
+ * Connect Command
+ */
+typedef enum {
+ INFRA_NETWORK = 0x01,
+ ADHOC_NETWORK = 0x02,
+} NETWORK_TYPE;
+
+typedef enum {
+ OPEN_AUTH = 0x01,
+ SHARED_AUTH = 0x02,
+ LEAP_AUTH = 0x04,
+} DOT11_AUTH_MODE;
+
+typedef enum {
+ NONE_AUTH = 0x01,
+ WPA_AUTH = 0x02,
+ WPA_PSK_AUTH = 0x03,
+ WPA2_AUTH = 0x04,
+ WPA2_PSK_AUTH = 0x05,
+ WPA_AUTH_CCKM = 0x06,
+ WPA2_AUTH_CCKM = 0x07,
+} AUTH_MODE;
+
+typedef enum {
+ NONE_CRYPT = 0x01,
+ WEP_CRYPT = 0x02,
+ TKIP_CRYPT = 0x03,
+ AES_CRYPT = 0x04,
+} CRYPTO_TYPE;
+
+#define WMI_MIN_CRYPTO_TYPE NONE_CRYPT
+#define WMI_MAX_CRYPTO_TYPE (AES_CRYPT + 1)
+
+#define WMI_MIN_KEY_INDEX 0
+#define WMI_MAX_KEY_INDEX 3
+
+#define WMI_MAX_KEY_LEN 32
+
+#define WMI_MAX_SSID_LEN 32
+
+typedef struct {
+ A_UINT8 networkType;
+ A_UINT8 dot11AuthMode;
+ A_UINT8 authMode;
+ A_UINT8 pairwiseCryptoType;
+ A_UINT8 pairwiseCryptoLen;
+ A_UINT8 groupCryptoType;
+ A_UINT8 groupCryptoLen;
+ A_UINT8 ssidLength;
+ A_UCHAR ssid[WMI_MAX_SSID_LEN];
+ A_UINT16 channel;
+ A_UINT8 bssid[ATH_MAC_LEN];
+} __ATTRIB_PACK WMI_CONNECT_CMD;
+
+/*
+ * WMI_RECONNECT_CMDID
+ */
+typedef struct {
+ A_UINT16 channel; /* hint */
+ A_UINT8 bssid[ATH_MAC_LEN]; /* mandatory if set */
+} __ATTRIB_PACK WMI_RECONNECT_CMD;
+
+/*
+ * WMI_ADD_CIPHER_KEY_CMDID
+ */
+typedef enum {
+ PAIRWISE_USAGE = 0x00,
+ GROUP_USAGE = 0x01,
+ TX_USAGE = 0x02, /* default Tx Key - Static WEP only */
+} KEY_USAGE;
+
+typedef struct {
+ A_UINT8 keyIndex;
+ A_UINT8 keyType;
+ A_UINT8 keyUsage; /* KEY_USAGE */
+ A_UINT8 keyLength;
+ A_UINT8 keyRSC[8]; /* key replay sequence counter */
+ A_UINT8 key[WMI_MAX_KEY_LEN];
+} __ATTRIB_PACK WMI_ADD_CIPHER_KEY_CMD;
+
+/*
+ * WMI_DELETE_CIPHER_KEY_CMDID
+ */
+typedef struct {
+ A_UINT8 keyIndex;
+} __ATTRIB_PACK WMI_DELETE_CIPHER_KEY_CMD;
+
+#define WMI_KRK_LEN 16
+/*
+ * WMI_ADD_KRK_CMDID
+ */
+typedef struct {
+ A_UINT8 krk[WMI_KRK_LEN];
+} __ATTRIB_PACK WMI_ADD_KRK_CMD;
+
+/*
+ * WMI_SET_TKIP_COUNTERMEASURES_CMDID
+ */
+typedef enum {
+ WMI_TKIP_CM_DISABLE = 0x0,
+ WMI_TKIP_CM_ENABLE = 0x1,
+} WMI_TKIP_CM_CONTROL;
+
+typedef struct {
+ A_UINT8 cm_en; /* WMI_TKIP_CM_CONTROL */
+} __ATTRIB_PACK WMI_SET_TKIP_COUNTERMEASURES_CMD;
+
+/*
+ * WMI_SET_PMKID_CMDID
+ */
+typedef enum {
+ PMKID_DISABLE = 0,
+ PMKID_ENABLE = 1,
+} PMKID_ENABLE_FLG;
+
+typedef struct {
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_UINT8 enable; /* PMKID_ENABLE_FLG */
+ A_UINT8 pmkid[16];
+} __ATTRIB_PACK WMI_SET_PMKID_CMD;
+
+/*
+ * WMI_START_SCAN_CMD
+ */
+typedef enum {
+ WMI_LONG_SCAN = 0,
+ WMI_SHORT_SCAN = 1,
+} WMI_SCAN_TYPE;
+
+typedef struct {
+ A_UINT8 scanType; /* WMI_SCAN_TYPE */
+} __ATTRIB_PACK WMI_START_SCAN_CMD;
+
+/*
+ * WMI_SET_SCAN_PARAMS_CMDID
+ */
+#define WMI_SHORTSCANRATIO_DEFAULT 3
+
+typedef struct {
+ A_UINT16 fg_start_period; /* seconds */
+ A_UINT16 fg_end_period; /* seconds */
+ A_UINT16 bg_period; /* seconds */
+ A_UINT16 act_chdwell_time; /* msec */
+ A_UINT16 pas_chdwell_time; /* msec */
+ A_UINT8 shortScanRatio; /* how many shorts scan for one long */
+} __ATTRIB_PACK WMI_SCAN_PARAMS_CMD;
+
+/*
+ * WMI_SET_BSS_FILTER_CMDID
+ */
+typedef enum {
+ NONE_BSS_FILTER = 0x0, /* no beacons forwarded */
+ ALL_BSS_FILTER, /* all beacons forwarded */
+ PROFILE_FILTER, /* only beacons matching profile */
+ ALL_BUT_PROFILE_FILTER, /* all but beacons matching profile */
+ CURRENT_BSS_FILTER, /* only beacons matching current BSS */
+ ALL_BUT_BSS_FILTER, /* all but beacons matching BSS */
+ PROBED_SSID_FILTER, /* beacons matching probed ssid */
+ LAST_BSS_FILTER, /* marker only */
+} WMI_BSS_FILTER;
+
+typedef struct {
+ A_UINT8 bssFilter; /* see WMI_BSS_FILTER */
+} __ATTRIB_PACK WMI_BSS_FILTER_CMD;
+
+/*
+ * WMI_SET_PROBED_SSID_CMDID
+ */
+#define MAX_PROBED_SSID_INDEX 5
+
+typedef enum {
+ DISABLE_SSID_FLAG = 0, /* disables entry */
+ SPECIFIC_SSID_FLAG = 0x01, /* probes specified ssid */
+ ANY_SSID_FLAG = 0x02, /* probes for any ssid */
+} WMI_SSID_FLAG;
+
+typedef struct {
+ A_UINT8 entryIndex; /* 0 to MAX_PROBED_SSID_INDEX */
+ A_UINT8 flag; /* WMI_SSID_FLG */
+ A_UINT8 ssidLength;
+ A_UINT8 ssid[32];
+} __ATTRIB_PACK WMI_PROBED_SSID_CMD;
+
+/*
+ * WMI_SET_LISTEN_INT_CMDID
+ * The Listen interval is between 100 and 1000 TUs
+ */
+#define MIN_LISTEN_INTERVAL 100
+#define MAX_LISTEN_INTERVAL 1000
+#define MIN_LISTEN_BEACONS 1
+#define MAX_LISTEN_BEACONS 50
+
+typedef struct {
+ A_UINT16 listenInterval;
+ A_UINT16 numBeacons;
+} __ATTRIB_PACK WMI_LISTEN_INT_CMD;
+
+/*
+ * WMI_SET_BEACON_INT_CMDID
+ */
+typedef struct {
+ A_UINT16 beaconInterval;
+} __ATTRIB_PACK WMI_BEACON_INT_CMD;
+
+/*
+ * WMI_SET_BMISS_TIME_CMDID
+ * valid values are between 1000 and 5000 TUs
+ */
+
+#define MIN_BMISS_TIME 1000
+#define MAX_BMISS_TIME 5000
+#define MIN_BMISS_BEACONS 5
+#define MAX_BMISS_BEACONS 50
+
+typedef struct {
+ A_UINT16 bmissTime;
+ A_UINT16 numBeacons;
+} __ATTRIB_PACK WMI_BMISS_TIME_CMD;
+
+/*
+ * WMI_SET_POWER_MODE_CMDID
+ */
+typedef enum {
+ REC_POWER = 0x01,
+ MAX_PERF_POWER,
+} WMI_POWER_MODE;
+
+typedef struct {
+ A_UINT8 powerMode; /* WMI_POWER_MODE */
+} __ATTRIB_PACK WMI_POWER_MODE_CMD;
+
+/*
+ * WMI_SET_POWER_PARAMS_CMDID
+ */
+typedef enum {
+ IGNORE_DTIM = 0x01,
+ NORMAL_DTIM = 0x02,
+ STICK_DTIM = 0x03,
+} WMI_DTIM_POLICY;
+
+typedef struct {
+ A_UINT16 idle_period; /* msec */
+ A_UINT16 pspoll_number;
+ A_UINT16 dtim_policy;
+} __ATTRIB_PACK WMI_POWER_PARAMS_CMD;
+
+typedef struct {
+ A_UINT8 power_saving;
+ A_UINT8 ttl; /* number of beacon periods */
+ A_UINT16 atim_windows; /* msec */
+ A_UINT16 timeout_value; /* msec */
+} __ATTRIB_PACK WMI_IBSS_PM_CAPS_CMD;
+
+/*
+ * WMI_SET_POWERSAVE_TIMERS_CMDID
+ */
+typedef struct {
+ A_UINT16 psPollTimeout; /* msec */
+ A_UINT16 eospTimeout; /* msec */
+} __ATTRIB_PACK WMI_POWERSAVE_TIMERS_CMD;
+
+/*
+ * WMI_SET_VOICE_PKT_SIZE_CMDID
+ */
+typedef struct {
+ A_UINT16 voicePktSize;
+} __ATTRIB_PACK WMI_SET_VOICE_PKT_SIZE_CMD;
+
+/*
+ * WMI_SET_MAX_SP_LEN_CMDID
+ */
+typedef enum {
+ DELIVER_ALL_PKT = 0x0,
+ DELIVER_2_PKT = 0x1,
+ DELIVER_4_PKT = 0x2,
+ DELIVER_6_PKT = 0x3,
+} APSD_SP_LEN_TYPE;
+
+typedef struct {
+ A_UINT8 maxSPLen;
+} __ATTRIB_PACK WMI_SET_MAX_SP_LEN_CMD;
+
+/*
+ * WMI_SET_DISC_TIMEOUT_CMDID
+ */
+typedef struct {
+ A_UINT8 disconnectTimeout; /* seconds */
+} __ATTRIB_PACK WMI_DISC_TIMEOUT_CMD;
+
+typedef enum {
+ UPLINK_TRAFFIC = 0,
+ DNLINK_TRAFFIC = 1,
+ BIDIR_TRAFFIC = 2,
+} DIR_TYPE;
+/*
+ * WMI_CREATE_PSTREAM_CMDID
+ */
+typedef enum {
+ DISABLE_FOR_THIS_AC = 0,
+ ENABLE_FOR_THIS_AC = 1,
+ ENABLE_FOR_ALL_AC = 2,
+} VOICEPS_CAP_TYPE;
+
+typedef enum {
+ TRAFFIC_TYPE_APERIODIC = 0,
+ TRAFFIC_TYPE_PERIODIC = 1,
+}TRAFFIC_TYPE;
+
+typedef struct {
+ A_UINT8 trafficClass;
+ A_UINT8 trafficDirection; /* DIR_TYPE */
+ A_UINT8 rxQueueNum;
+ A_UINT8 trafficType; /* TRAFFIC_TYPE */
+ A_UINT8 voicePSCapability; /* VOICEPS_CAP_TYPE */
+ A_UINT8 tsid;
+ A_UINT8 userPriority; /* 802.1D user priority */
+ A_UINT16 nominalMSDU; /* in octects */
+ A_UINT16 maxMSDU; /* in octects */
+ A_UINT32 minServiceInt; /* in milli-sec */
+ A_UINT32 maxServiceInt; /* in milli-sec */
+ A_UINT32 inactivityInt; /* in milli-sec */
+ A_UINT32 suspensionInt; /* in milli-sec */
+ A_UINT32 serviceStartTime;
+ A_UINT32 minDataRate; /* in bps */
+ A_UINT32 meanDataRate; /* in bps */
+ A_UINT32 peakDataRate; /* in bps */
+ A_UINT32 maxBurstSize;
+ A_UINT32 delayBound;
+ A_UINT32 minPhyRate; /* in bps */
+ A_UINT32 sba;
+ A_UINT32 mediumTime;
+} __ATTRIB_PACK WMI_CREATE_PSTREAM_CMD;
+
+/*
+ * WMI_DELETE_PSTREAM_CMDID
+ */
+typedef struct {
+ A_UINT8 txQueueNumber;
+ A_UINT8 rxQueueNumber;
+ A_UINT8 trafficDirection;
+ A_UINT8 trafficClass;
+} __ATTRIB_PACK WMI_DELETE_PSTREAM_CMD;
+
+/*
+ * WMI_SET_CHANNEL_PARAMS_CMDID
+ */
+typedef enum {
+ WMI_11A_MODE = 0x1,
+ WMI_11G_MODE = 0x2,
+ WMI_11AG_MODE = 0x3,
+ WMI_11B_MODE = 0x4,
+ WMI_11GONLY_MODE = 0x5,
+} WMI_PHY_MODE;
+
+#define WMI_MAX_CHANNELS 32
+
+typedef struct {
+ A_UINT8 phyMode; /* see WMI_PHY_MODE */
+ A_UINT8 numChannels; /* how many channels follow */
+ A_UINT16 channelList[1]; /* channels in Mhz */
+} __ATTRIB_PACK WMI_CHANNEL_PARAMS_CMD;
+
+
+/*
+ * WMI_RSSI_THRESHOLD_PARAMS_CMDID
+ * Setting the polltime to 0 would disable polling.
+ * Threshold values are in the ascending order, and should agree to:
+ * (lowThreshold_lowerVal < lowThreshold_upperVal < highThreshold_lowerVal
+ * < highThreshold_upperVal)
+ */
+
+typedef struct WMI_RSSI_THRESHOLD_PARAMS{
+ A_UINT8 highThreshold_upperVal;
+ A_UINT8 highThreshold_lowerVal;
+ A_UINT8 lowThreshold_upperVal;
+ A_UINT8 lowThreshold_lowerVal;
+ A_UINT32 pollTime; /* Polling time in seconds */
+} __ATTRIB_PACK WMI_RSSI_THRESHOLD_PARAMS_CMD;
+
+/*
+ * WMI_TARGET_ERROR_REPORT_BITMASK_CMDID
+ * Sets the error reporting event bitmask in target. Target clears it
+ * upon an error. Subsequent errors are counted, but not reported
+ * via event, unless the bitmask is set again.
+ */
+typedef struct {
+ A_UINT32 bitmask;
+} __ATTRIB_PACK WMI_TARGET_ERROR_REPORT_BITMASK;
+
+/*
+ * WMI_SET_TX_PWR_CMDID
+ */
+typedef struct {
+ A_UINT8 dbM; /* in dbM units */
+} __ATTRIB_PACK WMI_SET_TX_PWR_CMD, WMI_TX_PWR_REPLY;
+
+/*
+ * WMI_SET_ASSOC_INFO_CMDID
+ *
+ * A maximum of 2 private IEs can be sent in the [Re]Assoc request.
+ * A 3rd one, the CCX version IE can also be set from the host.
+ */
+#define WMI_MAX_ASSOC_INFO_TYPE 2
+#define WMI_CCX_VER_IE 2 /* ieType to set CCX Version IE */
+
+#define WMI_MAX_ASSOC_INFO_LEN 240
+
+typedef struct {
+ A_UINT8 ieType;
+ A_UINT8 bufferSize;
+ A_UINT8 assocInfo[1]; /* up to WMI_MAX_ASSOC_INFO_LEN */
+} __ATTRIB_PACK WMI_SET_ASSOC_INFO_CMD;
+
+
+/*
+ * WMI_GET_TX_PWR_CMDID does not take any parameters
+ */
+
+/*
+ * WMI_ADD_BAD_AP_CMDID
+ */
+#define WMI_MAX_BAD_AP_INDEX 1
+
+typedef struct {
+ A_UINT8 badApIndex; /* 0 to WMI_MAX_BAD_AP_INDEX */
+ A_UINT8 bssid[ATH_MAC_LEN];
+} __ATTRIB_PACK WMI_ADD_BAD_AP_CMD;
+
+/*
+ * WMI_DELETE_BAD_AP_CMDID
+ */
+typedef struct {
+ A_UINT8 badApIndex; /* 0 to WMI_MAX_BAD_AP_INDEX */
+} __ATTRIB_PACK WMI_DELETE_BAD_AP_CMD;
+
+/*
+ * WMI_SET_ACCESS_PARAMS_CMDID
+ */
+#define WMI_DEFAULT_TXOP_ACPARAM 0 /* implies one MSDU */
+#define WMI_DEFAULT_ECWMIN_ACPARAM 4 /* corresponds to CWmin of 15 */
+#define WMI_DEFAULT_ECWMAX_ACPARAM 10 /* corresponds to CWmax of 1023 */
+#define WMI_MAX_CW_ACPARAM 15 /* maximum eCWmin or eCWmax */
+#define WMI_DEFAULT_AIFSN_ACPARAM 2
+#define WMI_MAX_AIFSN_ACPARAM 15
+typedef struct {
+ A_UINT16 txop; /* in units of 32 usec */
+ A_UINT8 eCWmin;
+ A_UINT8 eCWmax;
+ A_UINT8 aifsn;
+} __ATTRIB_PACK WMI_SET_ACCESS_PARAMS_CMD;
+
+
+/*
+ * WMI_SET_RETRY_LIMITS_CMDID
+ *
+ * This command is used to customize the number of retries the
+ * wlan device will perform on a given frame.
+ */
+#define WMI_MIN_RETRIES 2
+#define WMI_MAX_RETRIES 13
+typedef enum {
+ MGMT_FRAMETYPE = 0,
+ CONTROL_FRAMETYPE = 1,
+ DATA_FRAMETYPE = 2
+} WMI_FRAMETYPE;
+
+typedef struct {
+ A_UINT8 frameType; /* WMI_FRAMETYPE */
+ A_UINT8 trafficClass; /* applies only to DATA_FRAMETYPE */
+ A_UINT8 maxRetries;
+} __ATTRIB_PACK WMI_SET_RETRY_LIMITS_CMD;
+/*
+ * WMI_SET_ROAM_CTRL_CMDID
+ *
+ * This command is used to influence the Roaming behaviour
+ * Set the host biases of the BSSs before setting the roam mode as bias
+ * based.
+ */
+
+/*
+ * Different types of Roam Control
+ */
+
+typedef enum {
+ WMI_FORCE_ROAM = 1, /* Roam to the specified BSSID */
+ WMI_SET_ROAM_MODE = 2, /* default ,progd bias, no roam */
+ WMI_SET_HOST_BIAS = 3, /* Set the Host Bias */
+} WMI_ROAM_CTRL_TYPE;
+
+#define WMI_MIN_ROAM_CTRL_TYPE WMI_FORCE_ROAM
+#define WMI_MAX_ROAM_CTRL_TYPE WMI_SET_HOST_BIAS
+
+/*
+ * ROAM MODES
+ */
+
+typedef enum {
+ WMI_DEFAULT_ROAM_MODE = 1, /* RSSI based ROAM */
+ WMI_HOST_BIAS_ROAM_MODE = 2, /* HOST BIAS based ROAM */
+ WMI_LOCK_BSS_MODE = 3, /* Lock to the Current BSS - no Roam */
+ WMI_DISABLE_ROAM = 4 /* disables all auto-nomous roaming
+ behaviour */
+} WMI_ROAM_MODE;
+
+/*
+ * BSS HOST BIAS INFO
+ */
+
+typedef struct {
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_INT8 bias;
+} __ATTRIB_PACK WMI_BSS_BIAS;
+
+typedef struct {
+ A_UINT8 numBss;
+ WMI_BSS_BIAS bssBias[1];
+} __ATTRIB_PACK WMI_BSS_BIAS_INFO;
+
+typedef struct {
+ A_UINT8 roamCtrlType;
+ union {
+ A_UINT8 bssid[ATH_MAC_LEN]; /* WMI_FORCE_ROAM */
+ A_UINT8 roamMode; /* WMI_SET_ROAM_MODE */
+ WMI_BSS_BIAS_INFO bssBiasInfo; /* WMI_SET_HOST_BIAS */
+ } __ATTRIB_PACK info;
+} __ATTRIB_PACK WMI_SET_ROAM_CTRL_CMD;
+
+/*
+ * WMI_ENABLE_RM_CMDID
+ */
+typedef struct {
+ A_BOOL enable_radio_measurements;
+} __ATTRIB_PACK WMI_ENABLE_RM_CMD;
+
+/*
+ * WMI_SET_MAX_OFFHOME_DURATION_CMDID
+ */
+typedef struct {
+ A_UINT8 max_offhome_duration;
+} __ATTRIB_PACK WMI_SET_MAX_OFFHOME_DURATION_CMD;
+
+
+
+/*
+ * Command Replies
+ */
+
+/*
+ * WMI_GET_CHANNEL_LIST_CMDID reply
+ */
+typedef struct {
+ A_UINT8 reserved1;
+ A_UINT8 numChannels; /* number of channels in reply */
+ A_UINT16 channelList[1]; /* channel in Mhz */
+} __ATTRIB_PACK WMI_CHANNEL_LIST_REPLY;
+
+typedef enum {
+ A_SUCCEEDED = A_OK,
+ A_MBOX1_OK = 1,
+ A_MBOX2_OK = 2,
+ A_MBOX3_OK = 3,
+ A_FAILED_DELETE_INVALID_MBOX = 253,
+ A_FAILED_CREATE_REMOVE_PSTREAM_FIRST = 254,
+} PSTREAM_REPLY_STATUS;
+
+typedef struct {
+ A_UINT8 status; /* PSTREAM_REPLY_STATUS */
+ A_UINT8 txQueueNumber;
+ A_UINT8 rxQueueNumber;
+ A_UINT8 trafficClass;
+ A_UINT8 trafficDirection; /* DIR_TYPE */
+} __ATTRIB_PACK WMI_CRE_PRIORITY_STREAM_REPLY;
+
+typedef struct {
+ A_UINT8 status; /* PSTREAM_REPLY_STATUS */
+ A_UINT8 txQueueNumber;
+ A_UINT8 rxQueueNumber;
+ A_UINT8 trafficDirection; /* DIR_TYPE */
+ A_UINT8 trafficClass;
+} __ATTRIB_PACK WMI_DEL_PRIORITY_STREAM_REPLY;
+
+
+/*
+ * List of Events (target to host)
+ */
+typedef enum {
+ WMI_READY_EVENTID = 0x1001,
+ WMI_CONNECT_EVENTID,
+ WMI_DISCONNECT_EVENTID,
+ WMI_BSSINFO_EVENTID,
+ WMI_CMDERROR_EVENTID,
+ WMI_REGDOMAIN_EVENTID,
+ WMI_PSTREAM_TIMEOUT_EVENTID,
+ WMI_NEIGHBOR_REPORT_EVENTID,
+ WMI_TKIP_MICERR_EVENTID,
+ WMI_SCAN_COMPLETE_EVENTID,
+ WMI_REPORT_STATISTICS_EVENTID,
+ WMI_RSSI_THRESHOLD_EVENTID,
+ WMI_ERROR_REPORT_EVENTID,
+ WMI_OPT_RX_FRAME_EVENTID,
+ WMI_REPORT_ROAM_TBL_EVENTID,
+ WMI_EXTENSION_EVENTID,
+ WMI_CAC_EVENTID,
+ WMI_SNR_THRESHOLD_EVENTID,
+ WMI_LQ_THRESHOLD_EVENTID,
+ WMI_TX_RETRY_ERR_EVENTID,
+ WMI_REPORT_ROAM_DATA_EVENTID,
+} WMI_EVENT_ID;
+
+typedef enum {
+ WMI_11A_CAPABILITY = 1,
+ WMI_11G_CAPABILITY = 2,
+ WMI_11AG_CAPABILITY = 3,
+} WMI_PHY_CAPABILITY;
+
+typedef struct {
+ A_UINT8 macaddr[ATH_MAC_LEN];
+ A_UINT8 phyCapability; /* WMI_PHY_CAPABILITY */
+} __ATTRIB_PACK WMI_READY_EVENT;
+
+/*
+ * Connect Event
+ */
+typedef struct {
+ A_UINT16 channel;
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_UINT16 listenInterval;
+ A_UINT8 beaconIeLen;
+ A_UINT8 assocReqLen;
+ A_UINT8 assocRespLen;
+ A_UINT8 assocInfo[1];
+} __ATTRIB_PACK WMI_CONNECT_EVENT;
+
+/*
+ * Disconnect Event
+ */
+typedef enum {
+ NO_NETWORK_AVAIL = 0x01,
+ LOST_LINK = 0x02, /* bmiss */
+ DISCONNECT_CMD = 0x03,
+ BSS_DISCONNECTED = 0x04,
+ AUTH_FAILED = 0x05,
+ ASSOC_FAILED = 0x06,
+ NO_RESOURCES_AVAIL = 0x07,
+ CSERV_DISCONNECT = 0x08,
+ INVALID_PROFILE = 0x0a,
+} WMI_DISCONNECT_REASON;
+
+typedef struct {
+ A_UINT8 disconnectReason; /* see WMI_DISCONNECT_REASON */
+ A_UINT8 bssid[ATH_MAC_LEN]; /* set if known */
+ A_UINT8 assocRespLen;
+ A_UINT8 assocInfo[1];
+} __ATTRIB_PACK WMI_DISCONNECT_EVENT;
+
+/*
+ * BSS Info Event.
+ * Mechanism used to inform host of the presence and characteristic of
+ * wireless networks present. Consists of bss info header followed by
+ * the beacon or probe-response frame body. The 802.11 header is not included.
+ */
+typedef enum {
+ BEACON_FTYPE = 0x1,
+ PROBERESP_FTYPE,
+} WMI_BI_FTYPE;
+
+typedef struct {
+ A_UINT16 channel;
+ A_UINT8 frameType; /* see WMI_BI_FTYPE */
+ A_INT8 rssi;
+ A_UINT8 bssid[ATH_MAC_LEN];
+} __ATTRIB_PACK WMI_BSS_INFO_HDR;
+
+/*
+ * Command Error Event
+ */
+typedef enum {
+ INVALID_PARAM = 0x01,
+ ILLEGAL_STATE = 0x02,
+ INTERNAL_ERROR = 0x03,
+} WMI_ERROR_CODE;
+
+typedef struct {
+ A_UINT16 commandId;
+ A_UINT8 errorCode;
+} __ATTRIB_PACK WMI_CMD_ERROR_EVENT;
+
+/*
+ * New Regulatory Domain Event
+ */
+typedef struct {
+ A_UINT32 regDomain;
+} __ATTRIB_PACK WMI_REG_DOMAIN_EVENT;
+
+typedef struct {
+ A_UINT8 txQueueNumber;
+ A_UINT8 rxQueueNumber;
+ A_UINT8 trafficDirection;
+} __ATTRIB_PACK WMI_PSTREAM_TIMEOUT_EVENT;
+
+/*
+ * The WMI_NEIGHBOR_REPORT Event is generated by the target to inform
+ * the host of BSS's it has found that matches the current profile.
+ * It can be used by the host to cache PMKs and/to initiate pre-authentication
+ * if the BSS supports it. The first bssid is always the current associated
+ * BSS.
+ * The bssid and bssFlags information repeats according to the number
+ * or APs reported.
+ */
+typedef enum {
+ WMI_DEFAULT_BSS_FLAGS = 0x00,
+ WMI_PREAUTH_CAPABLE_BSS = 0x01,
+ WMI_PMKID_VALID_BSS = 0x02,
+} WMI_BSS_FLAGS;
+
+typedef struct {
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_UINT8 bssFlags; /* see WMI_BSS_FLAGS */
+} __ATTRIB_PACK WMI_NEIGHBOR_INFO;
+
+typedef struct {
+ A_INT8 numberOfAps;
+ WMI_NEIGHBOR_INFO neighbor[1];
+} __ATTRIB_PACK WMI_NEIGHBOR_REPORT_EVENT;
+
+/*
+ * TKIP MIC Error Event
+ */
+typedef struct {
+ A_UINT8 keyid;
+ A_UINT8 ismcast;
+} __ATTRIB_PACK WMI_TKIP_MICERR_EVENT;
+
+/*
+ * WMI_SCAN_COMPLETE_EVENTID - no parameters
+ */
+
+#define MAX_OPT_DATA_LEN 1400
+
+/*
+ * WMI_SET_ADHOC_BSSID_CMDID
+ */
+typedef struct {
+ A_UINT8 bssid[ATH_MAC_LEN];
+} __ATTRIB_PACK WMI_SET_ADHOC_BSSID_CMD;
+
+/*
+ * WMI_SET_OPT_MODE_CMDID
+ */
+typedef enum {
+ SPECIAL_OFF,
+ SPECIAL_ON,
+} OPT_MODE_TYPE;
+
+typedef struct {
+ A_UINT8 optMode;
+} __ATTRIB_PACK WMI_SET_OPT_MODE_CMD;
+
+/*
+ * WMI_TX_OPT_FRAME_CMDID
+ */
+typedef enum {
+ OPT_PROBE_REQ = 0x01,
+ OPT_PROBE_RESP = 0x02,
+ OPT_CPPP_START = 0x03,
+ OPT_CPPP_STOP = 0x04,
+} __ATTRIB_PACK WMI_OPT_FTYPE;
+
+typedef struct {
+ A_UINT8 frmType;
+ A_UINT8 dstAddr[ATH_MAC_LEN];
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_UINT16 optIEDataLen;
+ A_UINT8 *optIEData;
+} __ATTRIB_PACK WMI_OPT_TX_FRAME_CMD;
+
+/*
+ * Special frame receive Event.
+ * Mechanism used to inform host of the receiption of the special frames.
+ * Consists of special frame info header followed by special frame body.
+ * The 802.11 header is not included.
+ */
+typedef struct {
+ A_UINT16 channel;
+ A_UINT8 frameType; /* see WMI_OPT_FTYPE */
+ A_INT8 rssi;
+ A_UINT8 srcAddr[ATH_MAC_LEN];
+ A_UINT8 bssid[ATH_MAC_LEN];
+} __ATTRIB_PACK WMI_OPT_RX_INFO_HDR;
+
+/*
+ * Reporting statistics.
+ */
+typedef struct {
+ A_UINT32 tx_packets;
+ A_UINT32 tx_bytes;
+ A_UINT32 tx_unicast_pkts;
+ A_UINT32 tx_unicast_bytes;
+ A_UINT32 tx_multicast_pkts;
+ A_UINT32 tx_multicast_bytes;
+ A_UINT32 tx_broadcast_pkts;
+ A_UINT32 tx_broadcast_bytes;
+ A_UINT32 tx_rts_success_cnt;
+ A_UINT32 tx_packet_per_ac[4];
+
+ A_UINT32 tx_errors;
+ A_UINT32 tx_failed_cnt;
+ A_UINT32 tx_retry_cnt;
+ A_UINT32 tx_rts_fail_cnt;
+}__ATTRIB_PACK tx_stats_t;
+
+typedef struct {
+ A_UINT32 rx_packets;
+ A_UINT32 rx_bytes;
+ A_UINT32 rx_unicast_pkts;
+ A_UINT32 rx_unicast_bytes;
+ A_UINT32 rx_multicast_pkts;
+ A_UINT32 rx_multicast_bytes;
+ A_UINT32 rx_broadcast_pkts;
+ A_UINT32 rx_broadcast_bytes;
+ A_UINT32 rx_fragment_pkt;
+
+ A_UINT32 rx_errors;
+ A_UINT32 rx_crcerr;
+ A_UINT32 rx_key_cache_miss;
+ A_UINT32 rx_decrypt_err;
+ A_UINT32 rx_duplicate_frames;
+}__ATTRIB_PACK rx_stats_t;
+
+typedef struct {
+ A_UINT32 tkip_local_mic_failure;
+ A_UINT32 tkip_counter_measures_invoked;
+ A_UINT32 tkip_replays;
+ A_UINT32 tkip_format_errors;
+ A_UINT32 ccmp_format_errors;
+ A_UINT32 ccmp_replays;
+}__ATTRIB_PACK tkip_ccmp_stats_t;
+
+typedef struct {
+ A_UINT32 power_save_failure_cnt;
+}__ATTRIB_PACK pm_stats_t;
+
+typedef struct {
+ A_UINT32 cs_bmiss_cnt;
+ A_UINT32 cs_lowRssi_cnt;
+ A_UINT16 cs_connect_cnt;
+ A_UINT16 cs_disconnect_cnt;
+ A_UINT8 cs_aveBeacon_rssi;
+ A_UINT8 cs_lastRoam_msec;
+}__ATTRIB_PACK cserv_stats_t;
+
+typedef struct {
+ tx_stats_t tx_stats;
+ rx_stats_t rx_stats;
+ tkip_ccmp_stats_t tkipCcmpStats;
+}__ATTRIB_PACK wlan_net_stats_t;
+
+typedef struct {
+ wlan_net_stats_t txrxStats;
+ cserv_stats_t cservStats;
+ pm_stats_t pmStats;
+ A_INT16 noise_floor_calibation;
+}__ATTRIB_PACK WMI_TARGET_STATS;
+
+/*
+ * WMI_RSSI_THRESHOLD_EVENTID.
+ * Indicate the RSSI events to host. Events are indicated when we breach a
+ * thresold value.
+ */
+typedef enum{
+ WMI_RSSI_LOWTHRESHOLD_BELOW_LOWERVAL=1,
+ WMI_RSSI_LOWTHRESHOLD_LOWERVAL,
+ WMI_RSSI_LOWTHRESHOLD_UPPERVAL,
+ WMI_RSSI_HIGHTHRESHOLD_LOWERVAL,
+ WMI_RSSI_HIGHTHRESHOLD_HIGHERVAL
+}WMI_RSSI_THRESHOLD_VAL;
+
+typedef struct {
+ A_UINT8 range;
+}__ATTRIB_PACK WMI_RSSI_THRESHOLD_EVENT;
+
+/*
+ * WMI_ERROR_REPORT_EVENTID
+ */
+typedef enum{
+ WMI_TARGET_PM_ERR_FAIL = 0x00000001,
+ WMI_TARGET_KEY_NOT_FOUND = 0x00000002,
+ WMI_TARGET_DECRYPTION_ERR = 0x00000004,
+ WMI_TARGET_BMISS = 0x00000008,
+ WMI_PSDISABLE_NODE_JOIN = 0x00000010
+}__ATTRIB_PACK WMI_TARGET_ERROR_VAL;
+
+typedef struct {
+ A_UINT32 errorVal;
+}__ATTRIB_PACK WMI_TARGET_ERROR_REPORT_EVENT;
+
+/*
+ * WMI_REPORT_ROAM_TBL_EVENTID
+ */
+#define MAX_ROAM_TBL_CAND 5
+
+typedef struct {
+ A_UINT8 bssid[ATH_MAC_LEN];
+ A_INT8 rssi;
+ A_INT8 rssidt;
+ A_INT8 last_rssi;
+ A_INT32 roam_util;
+ A_INT8 util;
+ A_INT8 bias;
+} __ATTRIB_PACK WMI_BSS_ROAM_INFO;
+
+
+typedef struct {
+ A_UINT8 roamMode;
+ A_UINT8 numEntries;
+ WMI_BSS_ROAM_INFO bssRoamInfo[1];
+} __ATTRIB_PACK WMI_TARGET_ROAM_TBL;
+
+/*
+ * WMI_CAC_EVENTID
+ */
+typedef enum {
+ CAC_INDICATION_ADMISSION = 0x00,
+ CAC_INDICATION_ADMISSION_RESP = 0x01,
+ CAC_INDICATION_DELETE = 0x02,
+ CAC_INDICATION_NO_RESP = 0x03,
+}CAC_INDICATION;
+
+#define WMM_TSPEC_IE_LEN 63
+
+typedef struct {
+ A_UINT8 ac;
+ A_UINT8 cac_indication;
+ A_UINT8 statusCode;
+ A_UINT8 tspecSuggestion[WMM_TSPEC_IE_LEN];
+}__ATTRIB_PACK WMI_CAC_EVENT;
+
+/*
+ * developer commands
+ */
+
+/*
+ * WMI_SET_BITRATE_CMDID
+ *
+ * Get bit rate cmd uses same definition as set bit rate cmd
+ */
+typedef enum {
+ RATE_AUTO = -1,
+ RATE_1Mb = 0,
+ RATE_2Mb = 1,
+ RATE_5_5Mb = 2,
+ RATE_11Mb = 3,
+ RATE_6Mb = 4,
+ RATE_9Mb = 5,
+ RATE_12Mb = 6,
+ RATE_18Mb = 7,
+ RATE_24Mb = 8,
+ RATE_36Mb = 9,
+ RATE_48Mb = 10,
+ RATE_54Mb = 11,
+} WMI_BIT_RATE;
+
+typedef struct {
+ A_INT8 rateIndex; /* see WMI_BIT_RATE */
+} __ATTRIB_PACK WMI_BIT_RATE_CMD, WMI_BIT_RATE_REPLY;
+
+typedef enum {
+ ROAM_DATA_TIME = 1, /* Get The Roam Time Data */
+} ROAM_DATA_TYPE;
+
+typedef struct {
+ A_UINT32 disassoc_time;
+ A_UINT8 disassoc_bssid[ATH_MAC_LEN];
+ A_INT8 disassoc_bss_rssi;
+ A_UINT32 no_txrx_time;
+ A_UINT32 assoc_time;
+ A_UINT8 assoc_bssid[ATH_MAC_LEN];
+ A_INT8 assoc_bss_rssi;
+ A_UINT32 allow_txrx_time;
+ A_UINT32 last_data_txrx_time;
+ A_UINT32 first_data_txrx_time;
+} __ATTRIB_PACK WMI_TARGET_ROAM_TIME;
+
+typedef struct {
+ A_UINT8 roamDataType;
+ union {
+ WMI_TARGET_ROAM_TIME roamTime;
+ } u;
+} __ATTRIB_PACK WMI_TARGET_ROAM_DATA;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WMI_H_ */
Added: developers/nbd/ar6k/include/wmi_api.h
===================================================================
--- developers/nbd/ar6k/include/wmi_api.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/wmi_api.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains the definitions for the Wireless Module Interface (WMI).
+ *
+ *
+ */
+
+#ifndef _WMI_API_H_
+#define _WMI_API_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define WMI_HIGH_PRIORITY_MBOX ENDPOINT4
+#define WMI_LOW_PRIORITY_MBOX ENDPOINT3
+#define WMI_BEST_EFFORT_MBOX ENDPOINT2
+#define WMI_CONTROL_MBOX ENDPOINT1
+#define WMI_MBOX_COUNT 4
+#define WMI_MAX_NUM_PRI_STREAMS 2
+#define WMI_NOT_MAPPED (-1)
+#define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000 /* 5 seconds */
+
+typedef enum {
+ CCX_V4_COMPLIANCE = 0x1,
+}TSPEC_PARAM_COMPLIANCE;
+
+struct wmi_t;
+
+void *wmi_init(void *devt);
+
+void wmi_qos_state_init(struct wmi_t *wmip);
+void wmi_shutdown(struct wmi_t *wmip);
+A_INT8 wmi_get_mapped_qos_queue(struct wmi_t *wmip, A_UINT8 dir, A_UINT8 trafficClass);
+A_STATUS wmi_dix_2_dot3(struct wmi_t *wmip, void *osbuf);
+A_STATUS wmi_data_hdr_add(struct wmi_t *wmip, void *osbuf, A_UINT8 msgType);
+A_STATUS wmi_dot3_2_dix(struct wmi_t *wmip, void *osbuf);
+A_STATUS wmi_data_hdr_remove(struct wmi_t *wmip, void *osbuf);
+A_STATUS wmi_syncpoint(struct wmi_t *wmip);
+A_STATUS wmi_syncpoint_reset(struct wmi_t *wmip);
+HTC_ENDPOINT_ID wmi_get_endpoint(struct wmi_t *wmip, A_UINT8 trafficClass);
+A_UINT8 wmi_implicit_create_pstream(struct wmi_t *wmip, void *osbuf, A_UINT8 dir);
+
+A_STATUS wmi_control_rx(struct wmi_t *wmip, void *osbuf);
+void wmi_iterate_nodes(struct wmi_t *wmip, wlan_node_iter_func *f, void *arg);
+
+typedef enum {
+ NO_SYNC_WMIFLAG = 0,
+ SYNC_BEFORE_WMIFLAG, /* transmit all queued data before cmd */
+ SYNC_AFTER_WMIFLAG, /* any new data waits until cmd execs */
+ SYNC_BOTH_WMIFLAG,
+ END_WMIFLAG /* end marker */
+} WMI_SYNC_FLAG;
+
+A_STATUS wmi_cmd_send(struct wmi_t *wmip, void *osbuf, WMI_COMMAND_ID cmdId,
+ WMI_SYNC_FLAG flag);
+A_STATUS wmi_connect_cmd(struct wmi_t *wmip,
+ NETWORK_TYPE netType,
+ DOT11_AUTH_MODE dot11AuthMode,
+ AUTH_MODE authMode,
+ CRYPTO_TYPE pairwiseCrypto,
+ A_UINT8 pairwiseCryptoLen,
+ CRYPTO_TYPE groupCrypto,
+ A_UINT8 groupCryptoLen,
+ int ssidLength,
+ A_UCHAR *ssid,
+ A_UINT8 *bssid,
+ A_UINT16 channel);
+A_STATUS wmi_reconnect_cmd(struct wmi_t *wmip,
+ A_UINT8 *bssid,
+ A_UINT16 channel);
+A_STATUS wmi_disconnect_cmd(struct wmi_t *wmip);
+A_STATUS wmi_getrev_cmd(struct wmi_t *wmip);
+A_STATUS wmi_startscan_cmd(struct wmi_t *wmip, WMI_SCAN_TYPE scanType);
+A_STATUS wmi_scanparams_cmd(struct wmi_t *wmip, A_UINT16 fg_start_sec,
+ A_UINT16 fg_end_sec, A_UINT16 bg_sec,
+ A_UINT16 act_chdw_msec, A_UINT16 pas_chdw_msec,
+ A_UINT8 shScanRatio);
+A_STATUS wmi_bssfilter_cmd(struct wmi_t *wmip, A_UINT8 filter);
+A_STATUS wmi_probedSsid_cmd(struct wmi_t *wmip, A_UINT8 index, A_UINT8 flag,
+ A_UINT8 ssidLength, A_UCHAR *ssid);
+A_STATUS wmi_listeninterval_cmd(struct wmi_t *wmip, A_UINT16 listenInterval, A_UINT16 listenBeacons);
+A_STATUS wmi_bmisstime_cmd(struct wmi_t *wmip, A_UINT16 bmisstime, A_UINT16 bmissbeacons);
+A_STATUS wmi_associnfo_cmd(struct wmi_t *wmip, A_UINT8 ieType,
+ A_UINT8 ieLen, A_UINT8 *ieInfo);
+A_STATUS wmi_powermode_cmd(struct wmi_t *wmip, A_UINT8 powerMode);
+A_STATUS wmi_ibsspmcaps_cmd(struct wmi_t *wmip, A_UINT8 pmEnable, A_UINT8 ttl,
+ A_UINT16 atim_windows, A_UINT16 timeout_value);
+A_STATUS wmi_pmparams_cmd(struct wmi_t *wmip, A_UINT16 idlePeriod,
+ A_UINT16 psPollNum, A_UINT16 dtimPolicy);
+A_STATUS wmi_disctimeout_cmd(struct wmi_t *wmip, A_UINT8 timeout);
+A_STATUS wmi_sync_cmd(struct wmi_t *wmip, A_UINT8 syncNumber);
+A_STATUS wmi_create_pstream_cmd(struct wmi_t *wmip, WMI_CREATE_PSTREAM_CMD *pstream);
+A_STATUS wmi_delete_pstream_cmd(struct wmi_t *wmip, A_UINT8 txQueueNumber,
+ A_UINT8 rxQueueNumber, A_UINT8 trafficDirection);
+A_STATUS wmi_set_bitrate_cmd(struct wmi_t *wmip, A_INT32 rate);
+A_STATUS wmi_get_bitrate_cmd(struct wmi_t *wmip);
+A_INT8 wmi_validate_bitrate(struct wmi_t *wmip, A_INT32 rate);
+A_STATUS wmi_get_regDomain_cmd(struct wmi_t *wmip);
+A_STATUS wmi_get_channelList_cmd(struct wmi_t *wmip);
+A_STATUS wmi_set_channelParams_cmd(struct wmi_t *wmip, WMI_PHY_MODE mode,
+ A_INT8 numChan, A_UINT16 *channelList);
+A_STATUS wmi_set_link_threshold_params(struct wmi_t *wmip,
+ A_UINT8 highThreshold_upperVal,
+ A_UINT8 highThreshold_lowerVal,
+ A_UINT8 lowThreshold_upperVal,
+ A_UINT8 lowThreshold_lowerVal,
+ A_UINT32 pollTime);
+A_STATUS wmi_set_error_report_bitmask(struct wmi_t *wmip, A_UINT32 bitmask);
+
+A_STATUS wmi_get_stats_cmd(struct wmi_t *wmip);
+A_STATUS wmi_addKey_cmd(struct wmi_t *wmip, A_UINT8 keyIndex,
+ CRYPTO_TYPE keyType, A_UINT8 keyUsage,
+ A_UINT8 keyLength,A_UINT8 *keyRSC,
+ A_UINT8 *keyMaterial, WMI_SYNC_FLAG sync_flag);
+A_STATUS wmi_add_krk_cmd(struct wmi_t *wmip, A_UINT8 *krk);
+A_STATUS wmi_delete_krk_cmd(struct wmi_t *wmip);
+A_STATUS wmi_deleteKey_cmd(struct wmi_t *wmip, A_UINT8 keyIndex);
+A_STATUS wmi_set_txPwr_cmd(struct wmi_t *wmip, A_UINT8 dbM);
+A_STATUS wmi_get_txPwr_cmd(struct wmi_t *wmip);
+A_STATUS wmi_addBadAp_cmd(struct wmi_t *wmip, A_UINT8 apIndex, A_UINT8 *bssid);
+A_STATUS wmi_deleteBadAp_cmd(struct wmi_t *wmip, A_UINT8 apIndex);
+A_STATUS wmi_set_tkip_countermeasures_cmd(struct wmi_t *wmip, A_BOOL en);
+A_STATUS wmi_setPmkid_cmd(struct wmi_t *wmip, A_UINT8 *bssid, A_UINT8 *pmkId,
+ A_BOOL set);
+A_STATUS wmi_set_access_params_cmd(struct wmi_t *wmip, A_UINT16 txop,
+ A_UINT8 eCWmin, A_UINT8 eCWmax,
+ A_UINT8 aifsn);
+A_STATUS wmi_set_retry_limits_cmd(struct wmi_t *wmip, A_UINT8 frameType,
+ A_UINT8 trafficClass, A_UINT8 maxRetries);
+
+void wmi_get_current_bssid(struct wmi_t *wmip, A_UINT8 *bssid);
+
+A_STATUS wmi_get_roam_tbl_cmd(struct wmi_t *wmip);
+A_STATUS wmi_get_roam_data_cmd(struct wmi_t *wmip, A_UINT8 roamDataType);
+A_STATUS wmi_set_roam_ctrl_cmd(struct wmi_t *wmip, WMI_SET_ROAM_CTRL_CMD *p,
+ A_UINT8 size);
+A_STATUS wmi_set_powersave_timers_cmd(struct wmi_t *wmip,
+ WMI_POWERSAVE_TIMERS_CMD *pCmd,
+ A_UINT8 size);
+
+A_STATUS wmi_set_opt_mode_cmd(struct wmi_t *wmip, A_UINT8 optMode);
+A_STATUS wmi_opt_tx_frame_cmd(struct wmi_t *wmip,
+ A_UINT8 frmType,
+ A_UINT8 *dstMacAddr,
+ A_UINT8 *bssid,
+ A_UINT16 optIEDataLen,
+ A_UINT8 *optIEData);
+
+A_STATUS wmi_set_adhoc_bconIntvl_cmd(struct wmi_t *wmip, A_UINT16 intvl);
+A_STATUS wmi_set_voice_pkt_size_cmd(struct wmi_t *wmip, A_UINT16 voicePktSize);
+A_STATUS wmi_set_max_sp_len_cmd(struct wmi_t *wmip, A_UINT8 maxSpLen);
+A_UINT8 convert_userPriority_to_trafficClass(A_UINT8 userPriority);
+A_UINT8 wmi_get_power_mode_cmd(struct wmi_t *wmip);
+A_STATUS wmi_verify_tspec_params(WMI_CREATE_PSTREAM_CMD *pCmd, A_BOOL tspecCompliance);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WMI_API_H_ */
Added: developers/nbd/ar6k/include/wmi_host.h
===================================================================
--- developers/nbd/ar6k/include/wmi_host.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/wmi_host.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This file contains local definitios for the wmi host module.
+ *
+ */
+
+#ifndef _WMI_HOST_H_
+#define _WMI_HOST_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * IP QoS Field definitions according to 802.1p
+ */
+#define BEST_EFFORT_PRI 0
+#define BACKGROUND_PRI 1
+#define EXCELLENT_EFFORT_PRI 3
+#define CONTROLLED_LOAD_PRI 4
+#define VIDEO_PRI 5
+#define VOICE_PRI 6
+#define NETWORK_CONTROL_PRI 7
+
+struct wmi_stats {
+ A_UINT32 cmd_len_err;
+ A_UINT32 cmd_id_err;
+};
+
+struct wmi_priority_state {
+ A_UINT8 inUse;
+ A_UINT8 mbox;
+};
+
+struct wmi_mbox_state {
+ A_UINT8 trafficClass;
+ A_INT8 priorityNum;
+};
+
+struct wmi_t {
+ A_BOOL wmi_ready;
+ A_BOOL wmi_numQoSStream;
+ struct wmi_priority_state wmi_priority[WMI_MAX_NUM_PRI_STREAMS];
+ struct wmi_mbox_state wmi_mboxMap[2][WMI_MBOX_COUNT];
+ A_INT8 wmi_trafficClassMap[2][WMM_NUM_AC];
+ A_UINT8 wmi_olderPriRxMbox;
+ A_UINT8 wmi_newerPriRxMbox;
+ void *wmi_devt;
+ struct wmi_stats wmi_stats;
+ struct ieee80211_node_table wmi_scan_table;
+ A_BOOL wmi_pstreamCmdInProgress[2][WMM_NUM_AC];
+ A_BOOL wmi_cpstreamCmdInProgress;
+ A_UINT8 wmi_bssid[ATH_MAC_LEN];
+ A_UINT8 wmi_powerMode;
+ A_UINT8 wmi_phyMode;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WMI_HOST_H_ */
Added: developers/nbd/ar6k/include/wmix.h
===================================================================
--- developers/nbd/ar6k/include/wmix.h 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/include/wmix.h 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2004-2005 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * This file contains extensions of the WMI protocol specified in the
+ * Wireless Module Interface (WMI). It includes definitions of all
+ * extended commands and events. Extensions include useful commands
+ * that are not directly related to wireless activities. They may
+ * be hardware-specific, and they might not be supported on all
+ * implementations.
+ *
+ * Extended WMIX commands are encapsulated in a WMI message with
+ * cmd=WMI_EXTENSION_CMD.
+ *
+ * $Id: //depot/sw/releases/etnaGPL1.1/include/wmix.h#1 $
+ *
+ */
+
+#ifndef _WMIX_H_
+#define _WMIX_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Extended WMI commands are those that are needed during wireless
+ * operation, but which are not really wireless commands. This allows,
+ * for instance, platform-specific commands. Extended WMI commands are
+ * embedded in a WMI command message with WMI_COMMAND_ID=WMI_EXTENSION_CMDID.
+ * Extended WMI events are similarly embedded in a WMI event message with
+ * WMI_EVENT_ID=WMI_EXTENSION_EVENTID.
+ */
+typedef struct {
+ A_UINT16 commandId;
+} __ATTRIB_PACK WMIX_CMD_HDR;
+
+typedef enum {
+ WMIX_DSETOPEN_REPLY_CMDID = 0x2001,
+ WMIX_DSETDATA_REPLY_CMDID,
+ WMIX_GPIO_OUTPUT_SET_CMDID,
+ WMIX_GPIO_INPUT_GET_CMDID,
+ WMIX_GPIO_REGISTER_SET_CMDID,
+ WMIX_GPIO_REGISTER_GET_CMDID,
+ WMIX_GPIO_INTR_ACK_CMDID,
+} WMIX_COMMAND_ID;
+
+typedef enum {
+ WMIX_DSETOPENREQ_EVENTID = 0x3001,
+ WMIX_DSETCLOSE_EVENTID,
+ WMIX_DSETDATAREQ_EVENTID,
+ WMIX_GPIO_INTR_EVENTID,
+ WMIX_GPIO_DATA_EVENTID,
+ WMIX_GPIO_ACK_EVENTID,
+} WMIX_EVENT_ID;
+
+/*
+ * =============DataSet support=================
+ */
+
+/*
+ * WMIX_DSETOPENREQ_EVENTID
+ * DataSet Open Request Event
+ */
+typedef struct {
+ A_UINT32 dset_id;
+ A_UINT32 targ_dset_handle; /* echo'ed, not used by Host, */
+ A_UINT32 targ_reply_fn; /* echo'ed, not used by Host, */
+ A_UINT32 targ_reply_arg; /* echo'ed, not used by Host, */
+} __ATTRIB_PACK WMIX_DSETOPENREQ_EVENT;
+
+/*
+ * WMIX_DSETCLOSE_EVENTID
+ * DataSet Close Event
+ */
+typedef struct {
+ A_UINT32 access_cookie;
+} __ATTRIB_PACK WMIX_DSETCLOSE_EVENT;
+
+/*
+ * WMIX_DSETDATAREQ_EVENTID
+ * DataSet Data Request Event
+ */
+typedef struct {
+ A_UINT32 access_cookie;
+ A_UINT32 offset;
+ A_UINT32 length;
+ A_UINT32 targ_buf; /* echo'ed, not used by Host, */
+ A_UINT32 targ_reply_fn; /* echo'ed, not used by Host, */
+ A_UINT32 targ_reply_arg; /* echo'ed, not used by Host, */
+} __ATTRIB_PACK WMIX_DSETDATAREQ_EVENT;
+
+typedef struct {
+ A_UINT32 status;
+ A_UINT32 targ_dset_handle;
+ A_UINT32 targ_reply_fn;
+ A_UINT32 targ_reply_arg;
+ A_UINT32 access_cookie;
+ A_UINT32 size;
+ A_UINT32 version;
+} __ATTRIB_PACK WMIX_DSETOPEN_REPLY_CMD;
+
+typedef struct {
+ A_UINT32 status;
+ A_UINT32 targ_buf;
+ A_UINT32 targ_reply_fn;
+ A_UINT32 targ_reply_arg;
+ A_UINT32 length;
+ A_UINT8 buf[1];
+} __ATTRIB_PACK WMIX_DSETDATA_REPLY_CMD;
+
+
+/*
+ * =============GPIO support=================
+ * All masks are 18-bit masks with bit N operating on GPIO pin N.
+ */
+
+#if defined(AR6000)
+#include "AR6000/AR6000_gpio.h"
+#endif /* AR6000 */
+
+/*
+ * Set GPIO pin output state.
+ * In order for output to be driven, a pin must be enabled for output.
+ * This can be done during initialization through the GPIO Configuration
+ * DataSet, or during operation with the enable_mask.
+ *
+ * If a request is made to simultaneously set/clear or set/disable or
+ * clear/disable or disable/enable, results are undefined.
+ */
+typedef struct {
+ A_UINT32 set_mask; /* pins to set */
+ A_UINT32 clear_mask; /* pins to clear */
+ A_UINT32 enable_mask; /* pins to enable for output */
+ A_UINT32 disable_mask; /* pins to disable/tristate */
+} __ATTRIB_PACK WMIX_GPIO_OUTPUT_SET_CMD;
+
+/*
+ * Set a GPIO register. For debug/exceptional cases.
+ * Values for gpioreg_id are GPIO_REGISTER_IDs, defined in a
+ * platform-dependent header.
+ */
+typedef struct {
+ A_UINT32 gpioreg_id; /* GPIO register ID */
+ A_UINT32 value; /* value to write */
+} __ATTRIB_PACK WMIX_GPIO_REGISTER_SET_CMD;
+
+/* Get a GPIO register. For debug/exceptional cases. */
+typedef struct {
+ A_UINT32 gpioreg_id; /* GPIO register to read */
+} __ATTRIB_PACK WMIX_GPIO_REGISTER_GET_CMD;
+
+/*
+ * Host acknowledges and re-arms GPIO interrupts. A single
+ * message should be used to acknowledge all interrupts that
+ * were delivered in an earlier WMIX_GPIO_INTR_EVENT message.
+ */
+typedef struct {
+ A_UINT32 ack_mask; /* interrupts to acknowledge */
+} __ATTRIB_PACK WMIX_GPIO_INTR_ACK_CMD;
+
+/*
+ * Target informs Host of GPIO interrupts that have ocurred since the
+ * last WMIX_GIPO_INTR_ACK_CMD was received. Additional information --
+ * the current GPIO input values is provided -- in order to support
+ * use of a GPIO interrupt as a Data Valid signal for other GPIO pins.
+ */
+typedef struct {
+ A_UINT32 intr_mask; /* pending GPIO interrupts */
+ A_UINT32 input_values; /* recent GPIO input values */
+} __ATTRIB_PACK WMIX_GPIO_INTR_EVENT;
+
+/*
+ * Target responds to Host's earlier WMIX_GPIO_INPUT_GET_CMDID request
+ * using a GPIO_DATA_EVENT with
+ * value set to the mask of GPIO pin inputs and
+ * reg_id set to GPIO_ID_NONE
+ *
+ *
+ * Target responds to Hosts's earlier WMIX_GPIO_REGISTER_GET_CMDID request
+ * using a GPIO_DATA_EVENT with
+ * value set to the value of the requested register and
+ * reg_id identifying the register (reflects the original request)
+ * NB: reg_id supports the future possibility of unsolicited
+ * WMIX_GPIO_DATA_EVENTs (for polling GPIO input), and it may
+ * simplify Host GPIO support.
+ */
+typedef struct {
+ A_UINT32 value;
+ A_UINT32 reg_id;
+} __ATTRIB_PACK WMIX_GPIO_DATA_EVENT;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _WMIX_H_ */
Added: developers/nbd/ar6k/wlan/Makefile
===================================================================
--- developers/nbd/ar6k/wlan/Makefile 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/wlan/Makefile 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SDIO_AR6000_WLAN) += wlan_node.o wlan_recv_beacon.o wlan_utils.o
+
Added: developers/nbd/ar6k/wlan/wlan_node.c
===================================================================
--- developers/nbd/ar6k/wlan/wlan_node.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/wlan/wlan_node.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2006 Atheros Communications
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ */
+/*
+ * IEEE 802.11 node handling support.
+ */
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/ieee80211.h"
+#include "../include/wlan_api.h"
+#include "../include/ieee80211_node.h"
+#include "../include/htc.h"
+#include "../include/wmi.h"
+#include "../include/wmi_api.h"
+
+#ifdef DEBUG
+static int wlan_node_debug= 1;
+#define ND_PRINTF if (wlan_node_debug) A_PRINTF
+#else
+#define ND_PRINTF(args...)
+#endif
+
+static void wlan_node_timeout(unsigned long arg);
+static bss_t * _ieee80211_find_node(struct ieee80211_node_table *nt,
+ const A_UINT8 *macaddr);
+
+bss_t *
+wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size)
+{
+ bss_t *ni;
+
+ ni = A_MALLOC_NOWAIT(sizeof(bss_t));
+
+ if (ni != NULL) {
+ ni->ni_buf = A_MALLOC_NOWAIT(wh_size);
+ if (ni->ni_buf == NULL) {
+ A_FREE(ni);
+ ni = NULL;
+ }
+ }
+
+ /* Make sure our lists are clean */
+ ni->ni_list_next = NULL;
+ ni->ni_list_prev = NULL;
+ ni->ni_hash_next = NULL;
+ ni->ni_hash_prev = NULL;
+
+ return ni;
+}
+
+void
+wlan_node_free(bss_t *ni)
+{
+ if (ni->ni_buf != NULL) {
+ A_FREE(ni->ni_buf);
+ }
+
+ A_FREE(ni);
+}
+
+void
+wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
+ const A_UINT8 *macaddr)
+{
+ int hash;
+
+ A_MEMCPY(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN);
+ hash = IEEE80211_NODE_HASH(macaddr);
+ ieee80211_node_initref(ni); /* mark referenced */
+
+ ni->ni_tstamp = jiffies + (WLAN_NODE_INACT_TIMEOUT_MSEC / 1000) * HZ;
+ IEEE80211_NODE_LOCK_BH(nt);
+
+ /* Insert at the end of the node list */
+ ni->ni_list_next = NULL;
+ ni->ni_list_prev = nt->nt_node_last;
+ if(nt->nt_node_last != NULL)
+ {
+ nt->nt_node_last->ni_list_next = ni;
+ }
+ nt->nt_node_last = ni;
+ if(nt->nt_node_first == NULL)
+ {
+ nt->nt_node_first = ni;
+ }
+
+ /* Insert into the hash list i.e. the bucket */
+ if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL)
+ {
+ nt->nt_hash[hash]->ni_hash_prev = ni;
+ }
+ ni->ni_hash_prev = NULL;
+ nt->nt_hash[hash] = ni;
+
+ IEEE80211_NODE_UNLOCK_BH(nt);
+}
+
+static bss_t *
+_ieee80211_find_node(struct ieee80211_node_table *nt,
+ const A_UINT8 *macaddr)
+{
+ bss_t *ni;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_node_incref(ni); /* mark referenced */
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+bss_t *
+wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr)
+{
+ bss_t *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = _ieee80211_find_node(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
+
+/*
+ * Reclaim a node. If this is the last reference count then
+ * do the normal free work. Otherwise remove it from the node
+ * table and mark it gone by clearing the back-reference.
+ */
+void
+wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni)
+{
+ IEEE80211_NODE_LOCK(nt);
+
+ if(ni->ni_list_prev == NULL)
+ {
+ /* First in list so fix the list head */
+ nt->nt_node_first = ni->ni_list_next;
+ }
+ else
+ {
+ ni->ni_list_prev->ni_list_next = ni->ni_list_next;
+ }
+
+ if(ni->ni_list_next == NULL)
+ {
+ /* Last in list so fix list tail */
+ nt->nt_node_last = ni->ni_list_prev;
+ }
+ else
+ {
+ ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
+ }
+
+ if(ni->ni_hash_prev == NULL)
+ {
+ /* First in list so fix the list head */
+ int hash;
+ hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
+ nt->nt_hash[hash] = ni->ni_hash_next;
+ }
+ else
+ {
+ ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
+ }
+
+ if(ni->ni_hash_next != NULL)
+ {
+ ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
+ }
+ wlan_node_free(ni);
+
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+wlan_node_dec_free(bss_t *ni)
+{
+ if (ieee80211_node_dectestref(ni)) {
+ wlan_node_free(ni);
+ }
+}
+
+void
+wlan_free_allnodes(struct ieee80211_node_table *nt)
+{
+ bss_t *ni;
+
+ while ((ni = nt->nt_node_first) != NULL) {
+ wlan_node_reclaim(nt, ni);
+ }
+}
+
+void
+wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
+ void *arg)
+{
+ bss_t *ni;
+ A_UINT32 gen;
+
+ gen = nt->nt_scangen++;
+ IEEE80211_NODE_LOCK(nt);
+ for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
+ if (ni->ni_scangen != gen) {
+ ni->ni_scangen = gen;
+ (void) ieee80211_node_incref(ni);
+ (*f)(arg, ni);
+ wlan_node_dec_free(ni);
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+/*
+ * Node table support.
+ */
+void
+wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt)
+{
+ int i;
+ ND_PRINTF("node table = 0x%x\n", (A_UINT32)nt);
+ IEEE80211_NODE_LOCK_INIT(nt);
+
+ nt->nt_node_first = nt->nt_node_last = NULL;
+ for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
+ {
+ nt->nt_hash[i] = NULL;
+ }
+ A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt);
+ A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0);
+ nt->nt_wmip = wmip;
+}
+
+static void
+wlan_node_timeout(unsigned long arg)
+{
+ struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg;
+ bss_t *bss, *nextBss;
+ A_UINT8 myBssid[IEEE80211_ADDR_LEN];
+
+ wmi_get_current_bssid(nt->nt_wmip, myBssid);
+
+ bss = nt->nt_node_first;
+ while (bss != NULL) {
+ nextBss = bss->ni_list_next;
+ if ((A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0) &&
+ (bss->ni_tstamp <= jiffies))
+ {
+ /*
+ * free up all but the current bss - if set
+ */
+ wlan_node_reclaim(nt, bss);
+ }
+ bss = nextBss;
+ }
+
+ A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0);
+}
+
+void
+wlan_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+ A_UNTIMEOUT(&nt->nt_inact_timer);
+ wlan_free_allnodes(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
+}
Added: developers/nbd/ar6k/wlan/wlan_recv_beacon.c
===================================================================
--- developers/nbd/ar6k/wlan/wlan_recv_beacon.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/wlan/wlan_recv_beacon.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * Copyright 2006 Atheros Communications, Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ */
+/*
+ * IEEE 802.11 input handling.
+ */
+
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/wmi.h"
+#include "../include/ieee80211.h"
+#include "../include/wlan_api.h"
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
+ if ((_len) < (_minlen)) { \
+ return A_EINVAL; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
+ if ((__elem) == NULL) { \
+ return A_EINVAL; \
+ } \
+ if ((__elem)[1] > (__maxlen)) { \
+ return A_EINVAL; \
+ } \
+} while (0)
+
+
+/* unaligned little endian access */
+#define LE_READ_2(p) \
+ ((A_UINT16) \
+ ((((A_UINT8 *)(p))[0] ) | (((A_UINT8 *)(p))[1] << 8)))
+
+#define LE_READ_4(p) \
+ ((A_UINT32) \
+ ((((A_UINT8 *)(p))[0] ) | (((A_UINT8 *)(p))[1] << 8) | \
+ (((A_UINT8 *)(p))[2] << 16) | (((A_UINT8 *)(p))[3] << 24)))
+
+
+static int __inline
+iswpaoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmmoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WMM_OUI_TYPE<<24)|WMM_OUI);
+}
+
+static int __inline
+iswmmparam(const A_UINT8 *frm)
+{
+ return frm[1] > 5 && frm[6] == WMM_PARAM_OUI_SUBTYPE;
+}
+
+static int __inline
+iswmminfo(const A_UINT8 *frm)
+{
+ return frm[1] > 5 && frm[6] == WMM_INFO_OUI_SUBTYPE;
+}
+
+static int __inline
+isatherosoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+A_STATUS
+wlan_parse_beacon(A_UINT8 *buf, int framelen, struct ieee80211_common_ie *cie)
+{
+ A_UINT8 *frm, *efrm;
+
+ frm = buf;
+ efrm = (A_UINT8 *) (frm + framelen);
+
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ * [tlv] WMM
+ * [tlv] WPA or RSN
+ * [tlv] Atheros Advanced Capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+ A_MEMZERO(cie, sizeof(*cie));
+
+ cie->ie_tstamp = frm; frm += 8;
+ cie->ie_beaconInt = A_LE2CPU16(*(A_UINT16 *)frm); frm += 2;
+ cie->ie_capInfo = A_LE2CPU16(*(A_UINT16 *)frm); frm += 2;
+ cie->ie_chan = 0;
+
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ cie->ie_ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ cie->ie_rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ cie->ie_country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
+ cie->ie_chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ cie->ie_tim = frm;
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ cie->ie_xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ A_PRINTF("Discarding ERP Element - Bad Len\n");
+ return A_EINVAL;
+ }
+ cie->ie_erp = frm[2];
+ break;
+ case IEEE80211_ELEMID_RSN:
+ cie->ie_wpa = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm)) {
+ cie->ie_wpa = frm;
+ } else if (iswmmoui(frm)) {
+ cie->ie_wmm = frm;
+ } else if (isatherosoui(frm)) {
+ cie->ie_ath = frm;
+ }
+ break;
+ default:
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(cie->ie_rates, IEEE80211_RATE_MAXSIZE);
+ IEEE80211_VERIFY_ELEMENT(cie->ie_ssid, IEEE80211_NWID_LEN);
+
+ return A_OK;
+}
Added: developers/nbd/ar6k/wlan/wlan_utils.c
===================================================================
--- developers/nbd/ar6k/wlan/wlan_utils.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/wlan/wlan_utils.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This module implements frequently used wlan utilies
+ *
+ */
+
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+
+/*
+ * converts ieee channel number to frequency
+ */
+A_UINT16
+wlan_ieee2freq(int chan)
+{
+ if (chan == 14) {
+ return 2484;
+ }
+ if (chan < 14) { /* 0-13 */
+ return (2407 + (chan*5));
+ }
+ if (chan < 27) { /* 15-26 */
+ return (2512 + ((chan-15)*20));
+ }
+ return (5000 + (chan*5));
+}
+
+/*
+ * Converts MHz frequency to IEEE channel number.
+ */
+A_UINT32
+wlan_freq2ieee(A_UINT16 freq)
+{
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ if (freq < 5000)
+ return 15 + ((freq - 2512) / 20);
+ return (freq - 5000) / 5;
+}
Added: developers/nbd/ar6k/wmi/wmi.c
===================================================================
--- developers/nbd/ar6k/wmi/wmi.c 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/ar6k/wmi/wmi.c 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2871 @@
+/*
+ * Copyright (c) 2004-2006 Atheros Communications Inc.
+ *
+ * Wireless Network driver for Atheros AR6001
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ * This module implements the hardware independent layer of the
+ * Wireless Module Interface (WMI) protocol.
+ *
+ */
+
+#include "../include/athdefs.h"
+#include "../include/athtypes.h"
+#include "../include/osapi.h"
+#include "../include/htc.h"
+#include "../include/wmi.h"
+#include "../include/ieee80211.h"
+#include "../include/ieee80211_node.h"
+#include "../include/wlan_api.h"
+#include "../include/wmi_api.h"
+#include "../include/dset_api.h"
+#include "../include/gpio_api.h"
+#include "../include/wmi_host.h"
+#include "../include/athdrv.h"
+#include "../include/ar6000_api.h"
+
+#ifdef DEBUG
+int wmi_debug = 0;
+#define WMI_DEBUG_PRINTF(args...) if (wmi_debug) printk(args);
+#define WMI_DEBUG_PRINTF2(args...) if (wmi_debug > 1) printk(args);
+#else
+#define WMI_DEBUG_PRINTF(args...)
+#define WMI_DEBUG_PRINTF2(args...)
+#endif
+
+static A_STATUS wmi_ready_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+
+static A_STATUS wmi_connect_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_disconnect_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_tkip_micerr_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_bssInfo_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_opt_frame_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_pstream_timeout_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_sync_point(struct wmi_t *wmip);
+
+static A_STATUS wmi_create_pstream_reply_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_delete_pstream_reply_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_bitrate_reply_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_channelList_reply_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_regDomain_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_txPwr_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_neighborReport_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_dset_open_req_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+#if CONFIG_HOST_DSET_SUPPORT
+static A_STATUS wmi_dset_close_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_dset_data_req_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+static A_STATUS wmi_scanComplete_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_errorEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_statsEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_rssiThresholdEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_reportErrorEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_cac_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_roam_tbl_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+static A_STATUS wmi_roam_data_event_rx(struct wmi_t *wmip, A_UINT8 *datap,
+ int len);
+#if CONFIG_HOST_GPIO_SUPPORT
+static A_STATUS wmi_gpio_intr_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_gpio_data_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+static A_STATUS wmi_gpio_ack_rx(struct wmi_t *wmip, A_UINT8 *datap, int len);
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+static const A_INT32 wmi_rateTable[] = {
+ 1000,
+ 2000,
+ 5500,
+ 11000,
+ 6000,
+ 9000,
+ 12000,
+ 18000,
+ 24000,
+ 36000,
+ 48000,
+ 54000,
+ 0};
+
+#define MODE_A_SUPPORT_RATE_START 4
+#define MODE_A_SUPPORT_RATE_STOP 11
+
+#define MODE_GONLY_SUPPORT_RATE_START MODE_A_SUPPORT_RATE_START
+#define MODE_GONLY_SUPPORT_RATE_STOP MODE_A_SUPPORT_RATE_STOP
+
+#define MODE_B_SUPPORT_RATE_START 0
+#define MODE_B_SUPPORT_RATE_STOP 3
+
+
+/* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */
+const A_UINT8 up_to_ac[]= {
+ WMM_AC_BE,
+ WMM_AC_BK,
+ WMM_AC_BK,
+ WMM_AC_BE,
+ WMM_AC_VI,
+ WMM_AC_VI,
+ WMM_AC_VO,
+ WMM_AC_VO,
+ };
+void *
+wmi_init(void *devt)
+{
+ struct wmi_t *wmip;
+
+ A_UINT8 i;
+ wmip = A_MALLOC(sizeof(struct wmi_t));
+ if (wmip == NULL) {
+ return (NULL);
+ }
+ A_MEMZERO(wmip, sizeof(*wmip));
+
+ wmip->wmi_devt = devt;
+ wlan_node_table_init(wmip, &wmip->wmi_scan_table);
+ for (i = UPLINK_TRAFFIC; i < BIDIR_TRAFFIC; i++) {
+ wmip->wmi_pstreamCmdInProgress[i][WMM_AC_BE] = FALSE;
+ wmip->wmi_pstreamCmdInProgress[i][WMM_AC_BK] = FALSE;
+ wmip->wmi_pstreamCmdInProgress[i][WMM_AC_VI] = FALSE;
+ wmip->wmi_pstreamCmdInProgress[i][WMM_AC_VO] = FALSE;
+ }
+ wmip->wmi_cpstreamCmdInProgress = FALSE;
+ wmi_qos_state_init(wmip);
+ wmip->wmi_powerMode = REC_POWER;
+ wmip->wmi_phyMode = WMI_11G_MODE;
+
+ return (wmip);
+}
+
+void
+wmi_qos_state_init(struct wmi_t *wmip)
+{
+ A_UINT8 i;
+
+ if (wmip == NULL) {
+ return;
+ }
+
+ /* Initialize QoS States */
+ wmip->wmi_numQoSStream = 0;
+
+ for (i = 0; i < WMI_MAX_NUM_PRI_STREAMS; i++) {
+ wmip->wmi_priority[i].inUse = 0;
+ wmip->wmi_priority[i].mbox = WMI_BEST_EFFORT_MBOX;
+ }
+
+ for (i = 0; i < WMM_NUM_AC; i++) {
+ wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][i] = WMI_NOT_MAPPED;
+ wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][i] = WMI_NOT_MAPPED;
+ }
+ wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][WMM_AC_BE] = WMI_BEST_EFFORT_MBOX;
+ wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][WMM_AC_BE] = WMI_BEST_EFFORT_MBOX;
+
+ for (i = 0; i < WMI_MBOX_COUNT; i++) {
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][i].trafficClass = WMM_AC_BE;
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][i].priorityNum = WMI_NOT_MAPPED;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][i].trafficClass = WMM_AC_BE;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][i].priorityNum = WMI_NOT_MAPPED;
+ }
+
+ wmip->wmi_olderPriRxMbox = WMI_HIGH_PRIORITY_MBOX;
+ wmip->wmi_newerPriRxMbox = WMI_LOW_PRIORITY_MBOX;
+ ar6000_set_numdataendpts(wmip->wmi_devt, 1);
+}
+
+void
+wmi_shutdown(struct wmi_t *wmip)
+{
+ if (wmip != NULL) {
+ wlan_node_table_cleanup(&wmip->wmi_scan_table);
+ A_FREE(wmip);
+ }
+}
+
+/*
+ * performs DIX to 802.3 encapsulation for transmit packets.
+ * uses passed in buffer. Returns buffer or NULL if failed.
+ * Assumes the entire DIX header is contigous and that there is
+ * enough room in the buffer for a 802.3 mac header and LLC+SNAP headers.
+ */
+A_STATUS
+wmi_dix_2_dot3(struct wmi_t *wmip, void *osbuf)
+{
+ A_UINT8 *datap;
+ A_UINT16 typeorlen;
+ ATH_MAC_HDR macHdr;
+ ATH_LLC_SNAP_HDR *llcHdr;
+
+ A_ASSERT(osbuf != NULL);
+
+ if (a_netbuf_headroom(osbuf) <
+ (sizeof(ATH_LLC_SNAP_HDR) + sizeof(WMI_DATA_HDR)))
+ {
+ return A_NO_MEMORY;
+ }
+
+ datap = a_netbuf_to_data(osbuf);
+
+ typeorlen = *(A_UINT16 *)(datap + ATH_MAC_LEN + ATH_MAC_LEN);
+
+ if (!IS_ETHERTYPE(A_BE2CPU16(typeorlen))) {
+ /*
+ * packet is already in 802.3 format - return success
+ */
+ WMI_DEBUG_PRINTF("packet already 802.3\n");
+ return (A_OK);
+ }
+
+ /*
+ * Save mac fields and length to be inserted later
+ */
+ A_MEMCPY(macHdr.dstMac, datap, ATH_MAC_LEN);
+ A_MEMCPY(macHdr.srcMac, datap + ATH_MAC_LEN, ATH_MAC_LEN);
+ macHdr.typeOrLen = A_CPU2BE16(a_netbuf_to_len(osbuf) - sizeof(ATH_MAC_HDR) +
+ sizeof(ATH_LLC_SNAP_HDR));
+
+ /*
+ * Make room for LLC+SNAP headers
+ */
+ if (a_netbuf_push(osbuf, sizeof(ATH_LLC_SNAP_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ datap = a_netbuf_to_data(osbuf);
+
+ A_MEMCPY(datap, &macHdr, sizeof (ATH_MAC_HDR));
+
+ llcHdr = (ATH_LLC_SNAP_HDR *)(datap + sizeof(ATH_MAC_HDR));
+ llcHdr->dsap = 0xAA;
+ llcHdr->ssap = 0xAA;
+ llcHdr->cntl = 0x03;
+ llcHdr->orgCode[0] = 0x0;
+ llcHdr->orgCode[1] = 0x0;
+ llcHdr->orgCode[2] = 0x0;
+ llcHdr->etherType = typeorlen;
+
+ return (A_OK);
+}
+
+/*
+ * Adds a WMI data header
+ * Assumes there is enough room in the buffer to add header.
+ */
+A_STATUS
+wmi_data_hdr_add(struct wmi_t *wmip, void *osbuf, A_UINT8 msgType)
+{
+ WMI_DATA_HDR *dtHdr;
+
+ A_ASSERT(osbuf != NULL);
+
+ if (a_netbuf_push(osbuf, sizeof(WMI_DATA_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ dtHdr = (WMI_DATA_HDR *)a_netbuf_to_data(osbuf);
+ dtHdr->info = msgType;
+ dtHdr->rssi = 0;
+
+ return (A_OK);
+}
+A_UINT8 wmi_implicit_create_pstream(struct wmi_t *wmip, void *osbuf, A_UINT8 dir)
+{
+ A_UINT8 *datap;
+ A_UINT8 trafficClass, userPriority = 0;
+ ATH_LLC_SNAP_HDR *llcHdr;
+ A_UINT16 ipType = IP_ETHERTYPE;
+ WMI_DATA_HDR *dtHdr;
+ WMI_CREATE_PSTREAM_CMD cmd;
+
+ A_ASSERT(osbuf != NULL);
+ A_ASSERT(dir == DNLINK_TRAFFIC || dir == UPLINK_TRAFFIC);
+ datap = a_netbuf_to_data(osbuf);
+ llcHdr = (ATH_LLC_SNAP_HDR *)(datap + sizeof(WMI_DATA_HDR) +
+ sizeof(ATH_MAC_HDR));
+
+ if (llcHdr->etherType == htons(ipType)) {
+ /* Extract the endpoint info from the TOS field in the IP header */
+ userPriority = ar6000_iptos_to_userPriority(((A_UINT8 *)llcHdr) + sizeof(ATH_LLC_SNAP_HDR));
+ trafficClass = convert_userPriority_to_trafficClass(userPriority);
+ } else {
+ trafficClass = WMM_AC_BE;
+ }
+
+
+ dtHdr = (WMI_DATA_HDR *)datap;
+ if(dir==UPLINK_TRAFFIC)
+ dtHdr->info |= (userPriority & WMI_DATA_HDR_UP_MASK) << WMI_DATA_HDR_UP_SHIFT; /* lower 3-bits are 802.1d priority */
+
+ /* Implicitly determine whether a create_pstream_cmd is needed for this trafficClass */
+ if (!((trafficClass == WMM_AC_BE) || (wmip->wmi_trafficClassMap[dir][trafficClass] != WMI_NOT_MAPPED))) {
+ A_UINT8 reqMbox;
+ /*
+ * If the data pkt is tagged with a priority that doesn't have a MBOX created,
+ * we must first call create_pstream_cmd
+ * Originally, assume traffic goes to BE MBox,
+ * until the driver receives the reply from the create_pstream cmd
+ */
+ wmip->wmi_trafficClassMap[dir][trafficClass] = WMI_BEST_EFFORT_MBOX;
+ if (dir == DNLINK_TRAFFIC) {
+ /* determine which mailbox to use */
+ reqMbox = wmip->wmi_olderPriRxMbox;
+ } else {
+ reqMbox = WMI_BEST_EFFORT_MBOX;
+ }
+
+ A_MEMZERO(&cmd, sizeof(cmd));
+ cmd.trafficClass = trafficClass;
+ cmd.rxQueueNum = reqMbox;
+ cmd.userPriority = userPriority;
+ cmd.inactivityInt = WMI_IMPLICIT_PSTREAM_INACTIVITY_INT;
+
+ if (wmi_create_pstream_cmd(wmip, &cmd) != A_OK) {
+ wmip->wmi_trafficClassMap[dir][trafficClass] = WMI_NOT_MAPPED;
+ trafficClass = WMM_AC_BE;
+ }
+
+ }
+ return trafficClass;
+}
+
+HTC_ENDPOINT_ID
+wmi_get_endpoint(struct wmi_t *wmip, A_UINT8 trafficClass)
+{
+ WMI_DEBUG_PRINTF2("1 pkt of traffic class(%d) to mbox(%d)\n",
+ trafficClass, wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][trafficClass]);
+ return wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][trafficClass];
+}
+
+/*
+ * performs 802.3 to DIX encapsulation for received packets.
+ * Assumes the entire 802.3 header is contigous.
+ */
+A_STATUS
+wmi_dot3_2_dix(struct wmi_t *wmip, void *osbuf)
+{
+ A_UINT8 *datap;
+ ATH_MAC_HDR macHdr;
+ ATH_LLC_SNAP_HDR *llcHdr;
+
+ A_ASSERT(osbuf != NULL);
+ datap = a_netbuf_to_data(osbuf);
+
+ A_MEMCPY(&macHdr, datap, sizeof(ATH_MAC_HDR));
+ llcHdr = (ATH_LLC_SNAP_HDR *)(datap + sizeof(ATH_MAC_HDR));
+ macHdr.typeOrLen = llcHdr->etherType;
+
+ if (a_netbuf_pull(osbuf, sizeof(ATH_LLC_SNAP_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ datap = a_netbuf_to_data(osbuf);
+
+ A_MEMCPY(datap, &macHdr, sizeof (ATH_MAC_HDR));
+
+ return (A_OK);
+}
+
+/*
+ * Removes a WMI data header
+ */
+A_STATUS
+wmi_data_hdr_remove(struct wmi_t *wmip, void *osbuf)
+{
+ A_ASSERT(osbuf != NULL);
+
+ return (a_netbuf_pull(osbuf, sizeof(WMI_DATA_HDR)));
+}
+
+void
+wmi_iterate_nodes(struct wmi_t *wmip, wlan_node_iter_func *f, void *arg)
+{
+ wlan_iterate_nodes(&wmip->wmi_scan_table, f, arg);
+}
+
+/*
+ * WMI Extended Event received from Target.
+ */
+A_STATUS
+wmi_control_rx_xtnd(struct wmi_t *wmip, void *osbuf)
+{
+ WMIX_CMD_HDR *cmd;
+ A_UINT16 id;
+ A_UINT8 *datap;
+ A_UINT32 len;
+ A_STATUS status = A_OK;
+
+ if (a_netbuf_to_len(osbuf) < sizeof(WMIX_CMD_HDR)) {
+ WMI_DEBUG_PRINTF("wmi extended event rx: bad packet\n");
+ wmip->wmi_stats.cmd_len_err++;
+ a_netbuf_free(osbuf);
+ return A_ERROR;
+ }
+
+ cmd = (WMIX_CMD_HDR *)a_netbuf_to_data(osbuf);
+ id = cmd->commandId;
+
+ if (a_netbuf_pull(osbuf, sizeof(WMIX_CMD_HDR)) != A_OK) {
+ WMI_DEBUG_PRINTF("wmi extended event rx: bad packet\n");
+ wmip->wmi_stats.cmd_len_err++;
+ a_netbuf_free(osbuf);
+ return A_ERROR;
+ }
+
+ datap = a_netbuf_to_data(osbuf);
+ len = a_netbuf_to_len(osbuf);
+
+ switch (id) {
+ case (WMIX_DSETOPENREQ_EVENTID):
+ status = wmi_dset_open_req_rx(wmip, datap, len);
+ break;
+#if CONFIG_HOST_DSET_SUPPORT
+ case (WMIX_DSETCLOSE_EVENTID):
+ status = wmi_dset_close_rx(wmip, datap, len);
+ break;
+ case (WMIX_DSETDATAREQ_EVENTID):
+ status = wmi_dset_data_req_rx(wmip, datap, len);
+ break;
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+#if CONFIG_HOST_GPIO_SUPPORT
+ case (WMIX_GPIO_INTR_EVENTID):
+ wmi_gpio_intr_rx(wmip, datap, len);
+ break;
+ case (WMIX_GPIO_DATA_EVENTID):
+ wmi_gpio_data_rx(wmip, datap, len);
+ break;
+ case (WMIX_GPIO_ACK_EVENTID):
+ wmi_gpio_ack_rx(wmip, datap, len);
+ break;
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+ default:
+ WMI_DEBUG_PRINTF("Host received unknown extended reply/event with id 0x%x\n",
+ id);
+ wmip->wmi_stats.cmd_id_err++;
+ status = A_ERROR;
+ break;
+ }
+
+ return status;
+}
+
+/*
+ * Control Path
+ */
+A_STATUS
+wmi_control_rx(struct wmi_t *wmip, void *osbuf)
+{
+ WMI_CMD_HDR *cmd;
+ A_UINT16 id;
+ A_UINT8 *datap;
+ A_UINT32 len;
+ A_STATUS status = A_OK;
+
+ A_ASSERT(osbuf != NULL);
+ if (a_netbuf_to_len(osbuf) < sizeof(WMI_CMD_HDR)) {
+ WMI_DEBUG_PRINTF("wmi event rx: bad packet\n");
+ wmip->wmi_stats.cmd_len_err++;
+ a_netbuf_free(osbuf);
+ return A_ERROR;
+ }
+
+ cmd = (WMI_CMD_HDR *)a_netbuf_to_data(osbuf);
+ id = cmd->commandId;
+
+ if (a_netbuf_pull(osbuf, sizeof(WMI_CMD_HDR)) != A_OK) {
+ WMI_DEBUG_PRINTF("wmi event rx: bad packet\n");
+ wmip->wmi_stats.cmd_len_err++;
+ a_netbuf_free(osbuf);
+ return A_ERROR;
+ }
+
+ datap = a_netbuf_to_data(osbuf);
+ len = a_netbuf_to_len(osbuf);
+
+ switch (id) {
+ case (WMI_CREATE_PSTREAM_CMDID):
+ status = wmi_create_pstream_reply_rx(wmip, datap, len);
+ break;
+ case (WMI_DELETE_PSTREAM_CMDID):
+ status = wmi_delete_pstream_reply_rx(wmip, datap, len);
+ break;
+ case (WMI_GET_BITRATE_CMDID):
+ status = wmi_bitrate_reply_rx(wmip, datap, len);
+ break;
+ case (WMI_GET_CHANNEL_LIST_CMDID):
+ status = wmi_channelList_reply_rx(wmip, datap, len);
+ break;
+ case (WMI_GET_TX_PWR_CMDID):
+ status = wmi_txPwr_reply_rx(wmip, datap, len);
+ break;
+ case (WMI_READY_EVENTID):
+ status = wmi_ready_event_rx(wmip, datap, len);
+ break;
+ case (WMI_CONNECT_EVENTID):
+ status = wmi_connect_event_rx(wmip, datap, len);
+ break;
+ case (WMI_DISCONNECT_EVENTID):
+ status = wmi_disconnect_event_rx(wmip, datap, len);
+ break;
+ case (WMI_TKIP_MICERR_EVENTID):
+ status = wmi_tkip_micerr_event_rx(wmip, datap, len);
+ break;
+ case (WMI_BSSINFO_EVENTID):
+ status = wmi_bssInfo_event_rx(wmip, datap, len);
+ break;
+ case (WMI_REGDOMAIN_EVENTID):
+ status = wmi_regDomain_event_rx(wmip, datap, len);
+ break;
+ case (WMI_PSTREAM_TIMEOUT_EVENTID):
+ status = wmi_pstream_timeout_event_rx(wmip, datap, len);
+ break;
+ case (WMI_NEIGHBOR_REPORT_EVENTID):
+ status = wmi_neighborReport_event_rx(wmip, datap, len);
+ break;
+ case (WMI_SCAN_COMPLETE_EVENTID):
+ status = wmi_scanComplete_rx(wmip, datap, len);
+ break;
+ case (WMI_CMDERROR_EVENTID):
+ status = wmi_errorEvent_rx(wmip, datap, len);
+ break;
+ case (WMI_REPORT_STATISTICS_EVENTID):
+ status = wmi_statsEvent_rx(wmip, datap, len);
+ break;
+ case (WMI_RSSI_THRESHOLD_EVENTID):
+ status = wmi_rssiThresholdEvent_rx(wmip, datap, len);
+ break;
+ case (WMI_ERROR_REPORT_EVENTID):
+ status = wmi_reportErrorEvent_rx(wmip, datap, len);
+ break;
+ case (WMI_OPT_RX_FRAME_EVENTID):
+ status = wmi_opt_frame_event_rx(wmip, datap, len);
+ break;
+ case (WMI_REPORT_ROAM_TBL_EVENTID):
+ status = wmi_roam_tbl_event_rx(wmip, datap, len);
+ break;
+ case (WMI_EXTENSION_EVENTID):
+ status = wmi_control_rx_xtnd(wmip, osbuf);
+ break;
+ case (WMI_CAC_EVENTID):
+ status = wmi_cac_event_rx(wmip, datap, len);
+ break;
+ case (WMI_REPORT_ROAM_DATA_EVENTID):
+ status = wmi_roam_data_event_rx(wmip, datap, len);
+ break;
+ default:
+ WMI_DEBUG_PRINTF("Host received unknown reply/event with id 0x%x\n",
+ id);
+ wmip->wmi_stats.cmd_id_err++;
+ status = A_ERROR;
+ break;
+ }
+
+ a_netbuf_free(osbuf);
+
+ return status;
+}
+
+static A_STATUS
+wmi_create_pstream_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_CRE_PRIORITY_STREAM_REPLY *reply;
+ A_INT8 priNum = 0, i;
+ A_UINT8 dir;
+
+ if (len < sizeof(WMI_CRE_PRIORITY_STREAM_REPLY)) {
+ WMI_DEBUG_PRINTF ("wmi: create pstream reply too short (%d)!!\n", len);
+ return A_EINVAL;
+ }
+
+ reply = (WMI_CRE_PRIORITY_STREAM_REPLY *)datap;
+ WMI_DEBUG_PRINTF("wmi: create pstream reply: status = %d, tx mbox = %d rx mbox %d\n",
+ reply->status, reply->txQueueNumber, reply->rxQueueNumber);
+
+ dir = reply->trafficDirection;
+ if (dir == BIDIR_TRAFFIC) {
+ wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][reply->trafficClass] = FALSE;
+ wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][reply->trafficClass] = FALSE;
+ } else {
+ wmip->wmi_pstreamCmdInProgress[dir][reply->trafficClass] = FALSE;
+ }
+ wmip->wmi_cpstreamCmdInProgress = FALSE;
+
+ if (reply->status == A_FAILED_CREATE_REMOVE_PSTREAM_FIRST) {
+ /* do nothing */
+ return A_OK;
+ }
+
+ if (reply->status == A_SUCCEEDED) {
+ if (reply->trafficDirection == DNLINK_TRAFFIC ||
+ reply->trafficDirection == BIDIR_TRAFFIC) {
+ wmip->wmi_olderPriRxMbox = wmip->wmi_newerPriRxMbox;
+ wmip->wmi_newerPriRxMbox = reply->rxQueueNumber;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][reply->rxQueueNumber].trafficClass = reply->trafficClass;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][reply->rxQueueNumber].priorityNum = 0;
+ wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][reply->trafficClass] = reply->rxQueueNumber;
+ }
+
+ if (reply->trafficDirection == UPLINK_TRAFFIC ||
+ reply->trafficDirection == BIDIR_TRAFFIC) {
+ wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][reply->trafficClass] = reply->txQueueNumber;
+ /* In case of 3rd pstreams being created, target automatically deletes the old pstream
+ * and accomodates the this one. Though, the host does not know of this implicit
+ * deletion,
+ */
+ if (wmip->wmi_numQoSStream < WMI_MAX_NUM_PRI_STREAMS)
+ wmip->wmi_numQoSStream++;
+ for (i = 0; i < WMI_MAX_NUM_PRI_STREAMS; i++) {
+ if (!wmip->wmi_priority[i].inUse) {
+ priNum = i;
+ wmip->wmi_priority[i].inUse = 1;
+ wmip->wmi_priority[i].mbox = reply->txQueueNumber;
+ break;
+ }
+ }
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][reply->txQueueNumber].trafficClass = reply->trafficClass;
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][reply->txQueueNumber].priorityNum = priNum;
+ ar6000_set_numdataendpts(wmip->wmi_devt, wmip->wmi_numQoSStream+1);
+ }
+ }
+
+ return reply->status;
+}
+
+static A_STATUS
+wmi_delete_pstream_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_DEL_PRIORITY_STREAM_REPLY *reply;
+ A_UINT8 dir;
+
+ if (len < sizeof(WMI_DEL_PRIORITY_STREAM_REPLY)) {
+ return A_EINVAL;
+ }
+
+ reply = (WMI_DEL_PRIORITY_STREAM_REPLY *)datap;
+ WMI_DEBUG_PRINTF("wmi: delete pstream reply: status = %d, rx mbox = %d tx mbox = %d\n", reply->status,
+ reply->rxQueueNumber, reply->txQueueNumber);
+
+ dir = reply->trafficDirection;
+ if (dir == BIDIR_TRAFFIC) {
+ wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][reply->trafficClass] = FALSE;
+ wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][reply->trafficClass] = FALSE;
+ } else {
+ wmip->wmi_pstreamCmdInProgress[dir][reply->trafficClass] = FALSE;
+ }
+
+ if (reply->status == A_OK) {
+ /* update internal states for Rx Path */
+ if (reply->trafficDirection == DNLINK_TRAFFIC || reply->trafficDirection == BIDIR_TRAFFIC) {
+ A_UINT8 qNum = reply->rxQueueNumber;
+ A_UINT8 class = wmip->wmi_mboxMap[DNLINK_TRAFFIC][qNum].trafficClass;
+ wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][class] = WMI_NOT_MAPPED;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][qNum].priorityNum = WMI_NOT_MAPPED;
+ wmip->wmi_mboxMap[DNLINK_TRAFFIC][qNum].trafficClass = WMM_AC_BE;
+ if (wmip->wmi_newerPriRxMbox == qNum) {
+ wmip->wmi_newerPriRxMbox = wmip->wmi_olderPriRxMbox;
+ wmip->wmi_olderPriRxMbox = qNum;
+ }
+ }
+ }
+
+ return reply->status;
+}
+
+static A_STATUS
+wmi_ready_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_READY_EVENT *ev = (WMI_READY_EVENT *)datap;
+
+ if (len < sizeof(WMI_READY_EVENT)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi: ready event\n");
+ wmip->wmi_ready = TRUE;
+ ar6000_ready_event(wmip->wmi_devt, ev->macaddr, ev->phyCapability);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_connect_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_CONNECT_EVENT *ev;
+
+ if (len < sizeof(WMI_CONNECT_EVENT)) {
+ return A_EINVAL;
+ }
+ ev = (WMI_CONNECT_EVENT *)datap;
+#ifdef DEBUG
+ WMI_DEBUG_PRINTF("wmi: connected event at freq %d ", ev->channel);
+ WMI_DEBUG_PRINTF("with bssid %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+ ev->bssid[0], ev->bssid[1], ev->bssid[2],
+ ev->bssid[3], ev->bssid[4], ev->bssid[5]);
+#endif /* DEBUG */
+
+ A_MEMCPY(wmip->wmi_bssid, ev->bssid, ATH_MAC_LEN);
+
+ ar6000_connect_event(wmip->wmi_devt, ev->channel, ev->bssid,
+ ev->listenInterval, ev->beaconIeLen, ev->assocReqLen,
+ ev->assocRespLen, ev->assocInfo);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_regDomain_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_REG_DOMAIN_EVENT *ev;
+
+ if (len < sizeof(*ev)) {
+ return A_EINVAL;
+ }
+ ev = (WMI_REG_DOMAIN_EVENT *)datap;
+
+ ar6000_regDomain_event(wmip->wmi_devt, ev->regDomain);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_neighborReport_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_NEIGHBOR_REPORT_EVENT *ev;
+ int numAps;
+
+ if (len < sizeof(*ev)) {
+ return A_EINVAL;
+ }
+ ev = (WMI_NEIGHBOR_REPORT_EVENT *)datap;
+ numAps = ev->numberOfAps;
+
+ if (len < (sizeof(*ev) + ((numAps - 1) * sizeof(WMI_NEIGHBOR_INFO)))) {
+ return A_EINVAL;
+ }
+
+ ar6000_neighborReport_event(wmip->wmi_devt, numAps, ev->neighbor);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_disconnect_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_DISCONNECT_EVENT *ev;
+
+ if (len < sizeof(WMI_DISCONNECT_EVENT)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi: disconnected event\n");
+
+ ev = (WMI_DISCONNECT_EVENT *)datap;
+
+ A_MEMZERO(wmip->wmi_bssid, sizeof(wmip->wmi_bssid));
+
+ ar6000_disconnect_event(wmip->wmi_devt, ev->disconnectReason, ev->bssid,
+ ev->assocRespLen, ev->assocInfo);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_tkip_micerr_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TKIP_MICERR_EVENT *ev;
+
+ if (len < sizeof(*ev)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi: tkip micerr event\n");
+
+ ev = (WMI_TKIP_MICERR_EVENT *)datap;
+ ar6000_tkip_micerr_event(wmip->wmi_devt, ev->keyid, ev->ismcast);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_bssInfo_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ bss_t *bss;
+ WMI_BSS_INFO_HDR *bih;
+ A_UINT8 *buf;
+
+ if (len <= sizeof(WMI_BSS_INFO_HDR)) {
+ return A_EINVAL;
+ }
+
+ bih = (WMI_BSS_INFO_HDR *)datap;
+ buf = datap + sizeof(WMI_BSS_INFO_HDR);
+ len -= sizeof(WMI_BSS_INFO_HDR);
+
+ WMI_DEBUG_PRINTF2("wmi: bssInfo event %2.2x:%2.2x\n",
+ bih->bssid[4], bih->bssid[5]);
+
+ bss = wlan_find_node(&wmip->wmi_scan_table, bih->bssid);
+ if (bss != NULL) {
+ /*
+ * Free up the node. Not the most efficient process given
+ * we are about to allocate a new node but it is simple and should be
+ * adequate.
+ */
+ wlan_node_reclaim(&wmip->wmi_scan_table, bss);
+ }
+
+ bss = wlan_node_alloc(&wmip->wmi_scan_table, len);
+ if (bss == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ bss->ni_rssi = bih->rssi;
+ A_ASSERT(bss->ni_buf != NULL);
+ A_MEMCPY(bss->ni_buf, buf, len);
+
+ if (wlan_parse_beacon(bss->ni_buf, len, &bss->ni_cie) != A_OK) {
+ wlan_node_free(bss);
+ return A_EINVAL;
+ }
+
+ /*
+ * Update the frequency in ie_chan, overwriting of channel number
+ * which is done in wlan_parse_beacon
+ */
+ bss->ni_cie.ie_chan = bih->channel;
+ wlan_setup_node(&wmip->wmi_scan_table, bss, bih->bssid);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_opt_frame_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ bss_t *bss;
+ WMI_OPT_RX_INFO_HDR *bih;
+ A_UINT8 *buf;
+
+ if (len <= sizeof(WMI_OPT_RX_INFO_HDR)) {
+ return A_EINVAL;
+ }
+
+ bih = (WMI_OPT_RX_INFO_HDR *)datap;
+ buf = datap + sizeof(WMI_OPT_RX_INFO_HDR);
+ len -= sizeof(WMI_OPT_RX_INFO_HDR);
+
+ WMI_DEBUG_PRINTF2("wmi: opt frame event %2.2x:%2.2x\n",
+ bih->bssid[4], bih->bssid[5]);
+
+ bss = wlan_find_node(&wmip->wmi_scan_table, bih->bssid);
+ if (bss != NULL) {
+ /*
+ * Free up the node. Not the most efficient process given
+ * we are about to allocate a new node but it is simple and should be
+ * adequate.
+ */
+ wlan_node_reclaim(&wmip->wmi_scan_table, bss);
+ }
+
+ bss = wlan_node_alloc(&wmip->wmi_scan_table, len);
+ if (bss == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ bss->ni_rssi = bih->rssi;
+ bss->ni_cie.ie_chan = bih->channel;
+ A_ASSERT(bss->ni_buf != NULL);
+ A_MEMCPY(bss->ni_buf, buf, len);
+ wlan_setup_node(&wmip->wmi_scan_table, bss, bih->bssid);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_pstream_timeout_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_PSTREAM_TIMEOUT_EVENT *ev;
+
+ if (len < sizeof(WMI_PSTREAM_TIMEOUT_EVENT)) {
+ return A_EINVAL;
+ }
+
+ WMI_DEBUG_PRINTF("wmi: pstream timeout event\n");
+
+ ev = (WMI_PSTREAM_TIMEOUT_EVENT *)datap;
+
+ wmi_delete_pstream_cmd(wmip, ev->txQueueNumber, ev->rxQueueNumber, ev->trafficDirection);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_bitrate_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_BIT_RATE_CMD *reply;
+ A_INT32 rate;
+
+ if (len < sizeof(WMI_BIT_RATE_CMD)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_BIT_RATE_CMD *)datap;
+ WMI_DEBUG_PRINTF("wmi: get bit rate reply %d\n", reply->rateIndex);
+
+ if (reply->rateIndex == RATE_AUTO) {
+ rate = RATE_AUTO;
+ } else {
+ rate = wmi_rateTable[reply->rateIndex];
+ }
+
+ ar6000_bitrate_rx(wmip->wmi_devt, rate);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_channelList_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_CHANNEL_LIST_REPLY *reply;
+
+ if (len < sizeof(WMI_CHANNEL_LIST_REPLY)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_CHANNEL_LIST_REPLY *)datap;
+ WMI_DEBUG_PRINTF("wmi: get channel list reply\n");
+
+ ar6000_channelList_rx(wmip->wmi_devt, reply->numChannels,
+ reply->channelList);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_txPwr_reply_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TX_PWR_REPLY *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_TX_PWR_REPLY *)datap;
+ WMI_DEBUG_PRINTF("wmi: get tx pwr reply\n");
+
+ ar6000_txPwr_rx(wmip->wmi_devt, reply->dbM);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_dset_open_req_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMIX_DSETOPENREQ_EVENT *dsetopenreq;
+
+ if (len < sizeof(WMIX_DSETOPENREQ_EVENT)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi_dset_open_req_rx: DataSet Open Request event\n");
+
+ dsetopenreq = (WMIX_DSETOPENREQ_EVENT *)datap;
+ WMI_DEBUG_PRINTF("wmi_dset_open_req_rx: dset_id=0x%x\n", dsetopenreq->dset_id);
+ ar6000_dset_open_req(wmip->wmi_devt,
+ dsetopenreq->dset_id,
+ dsetopenreq->targ_dset_handle,
+ dsetopenreq->targ_reply_fn,
+ dsetopenreq->targ_reply_arg);
+
+ return A_OK;
+}
+
+#if CONFIG_HOST_DSET_SUPPORT
+static A_STATUS
+wmi_dset_close_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMIX_DSETCLOSE_EVENT *dsetclose;
+
+ if (len < sizeof(WMIX_DSETCLOSE_EVENT)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi: DataSet Close event\n");
+
+ dsetclose = (WMIX_DSETCLOSE_EVENT *)datap;
+ ar6000_dset_close(wmip->wmi_devt, dsetclose->access_cookie);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_dset_data_req_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMIX_DSETDATAREQ_EVENT *dsetdatareq;
+
+ if (len < sizeof(WMIX_DSETDATAREQ_EVENT)) {
+ return A_EINVAL;
+ }
+ WMI_DEBUG_PRINTF("wmi: DataSet Data Request event\n");
+
+ dsetdatareq = (WMIX_DSETDATAREQ_EVENT *)datap;
+ ar6000_dset_data_req(wmip->wmi_devt,
+ dsetdatareq->access_cookie,
+ dsetdatareq->offset,
+ dsetdatareq->length,
+ dsetdatareq->targ_buf,
+ dsetdatareq->targ_reply_fn,
+ dsetdatareq->targ_reply_arg);
+
+ return A_OK;
+}
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+static A_STATUS
+wmi_scanComplete_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ ar6000_scanComplete_event(wmip->wmi_devt);
+
+ return A_OK;
+}
+
+/*
+ * Target is reporting a programming error. This is for
+ * developer aid only. Target only checks a few common violations
+ * and it is responsibility of host to do all error checking.
+ * Behavior of target after wmi error event is undefined.
+ * A reset is recommended.
+ */
+static A_STATUS
+wmi_errorEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_CMD_ERROR_EVENT *ev;
+
+ ev = (WMI_CMD_ERROR_EVENT *)datap;
+ A_PRINTF("Programming Error: cmd=%d ", ev->commandId);
+ switch (ev->errorCode) {
+ case (INVALID_PARAM):
+ A_PRINTF("Illegal Parameter\n");
+ break;
+ case (ILLEGAL_STATE):
+ A_PRINTF("Illegal State\n");
+ break;
+ case (INTERNAL_ERROR):
+ A_PRINTF("Internal Error\n");
+ break;
+ }
+
+ return A_OK;
+}
+
+
+static A_STATUS
+wmi_statsEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TARGET_STATS *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_TARGET_STATS *)datap;
+ WMI_DEBUG_PRINTF("wmi: target stats event\n");
+
+ ar6000_targetStats_event(wmip->wmi_devt, reply);
+
+ return A_OK;
+}
+
+
+
+static A_STATUS
+wmi_rssiThresholdEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_RSSI_THRESHOLD_EVENT *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_RSSI_THRESHOLD_EVENT *)datap;
+ WMI_DEBUG_PRINTF("wmi: rssi threshold event\n");
+
+ ar6000_rssiThreshold_event(wmip->wmi_devt, reply->range);
+
+ return A_OK;
+}
+
+
+static A_STATUS
+wmi_reportErrorEvent_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TARGET_ERROR_REPORT_EVENT *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_TARGET_ERROR_REPORT_EVENT *)datap;
+ WMI_DEBUG_PRINTF("wmi: report error event\n");
+
+ ar6000_reportError_event(wmip->wmi_devt, reply->errorVal);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_cac_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_CAC_EVENT *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_CAC_EVENT *)datap;
+ WMI_DEBUG_PRINTF("wmi: report CAC event\n");
+
+ ar6000_cac_event(wmip->wmi_devt, reply->ac,
+ reply->cac_indication, reply->statusCode,
+ reply->tspecSuggestion);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_roam_tbl_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TARGET_ROAM_TBL *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_TARGET_ROAM_TBL *)datap;
+ WMI_DEBUG_PRINTF("wmi: target ROAM TABLE event\n");
+
+ ar6000_roam_tbl_event(wmip->wmi_devt, reply);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_roam_data_event_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_TARGET_ROAM_DATA *reply;
+
+ if (len < sizeof(*reply)) {
+ return A_EINVAL;
+ }
+ reply = (WMI_TARGET_ROAM_DATA *)datap;
+ WMI_DEBUG_PRINTF("wmi: target ROAM DATA event\n");
+
+ ar6000_roam_data_event(wmip->wmi_devt, reply);
+
+ return A_OK;
+}
+
+#if CONFIG_HOST_GPIO_SUPPORT
+static A_STATUS
+wmi_gpio_intr_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMIX_GPIO_INTR_EVENT *gpio_intr = (WMIX_GPIO_INTR_EVENT *)datap;
+
+ WMI_DEBUG_PRINTF("wmi GPIO interrupt received intrmask=0x%x input=0x%x.\n",
+ gpio_intr->intr_mask, gpio_intr->input_values);
+
+ ar6000_gpio_intr_rx(gpio_intr->intr_mask, gpio_intr->input_values);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_gpio_data_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMIX_GPIO_DATA_EVENT *gpio_data = (WMIX_GPIO_DATA_EVENT *)datap;
+
+ WMI_DEBUG_PRINTF("wmi GPIO data received reg=%d value=0x%x.\n",
+ gpio_data->reg_id, gpio_data->value);
+
+ ar6000_gpio_data_rx(gpio_data->reg_id, gpio_data->value);
+
+ return A_OK;
+}
+
+static A_STATUS
+wmi_gpio_ack_rx(struct wmi_t *wmip, A_UINT8 *datap, int len)
+{
+ WMI_DEBUG_PRINTF("wmi GPIO ACK received.\n");
+
+ ar6000_gpio_ack_rx();
+
+ return A_OK;
+}
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+/*
+ * Called to send a wmi command. Command specific data is already built
+ * on osbuf and current osbuf->data points to it.
+ */
+A_STATUS
+wmi_cmd_send(struct wmi_t *wmip, void *osbuf, WMI_COMMAND_ID cmdId,
+ WMI_SYNC_FLAG syncflag)
+{
+#define IS_LONG_CMD(cmdId) (cmdId == WMI_OPT_TX_FRAME_CMDID)
+ WMI_CMD_HDR *cHdr;
+ A_UINT8 mbox = WMI_CONTROL_MBOX;
+
+ A_ASSERT(osbuf != NULL);
+
+ if (syncflag >= END_WMIFLAG) {
+ return A_EINVAL;
+ }
+
+ if ((syncflag == SYNC_BEFORE_WMIFLAG) || (syncflag == SYNC_BOTH_WMIFLAG)) {
+ /*
+ * We want to make sure all data currently queued is transmitted before
+ * the cmd execution. Establish a new sync point.
+ */
+ wmi_sync_point(wmip);
+ }
+
+ if (a_netbuf_push(osbuf, sizeof(WMI_CMD_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ cHdr = (WMI_CMD_HDR *)a_netbuf_to_data(osbuf);
+ cHdr->commandId = cmdId;
+
+ /*
+ * Send cmd, some via control pipe, others via data pipe
+ */
+ if (IS_LONG_CMD(cmdId)) {
+ wmi_data_hdr_add(wmip, osbuf, CNTL_MSGTYPE);
+ mbox = WMI_BEST_EFFORT_MBOX;
+ }
+ ar6000_control_tx(wmip->wmi_devt, osbuf, mbox);
+
+ if ((syncflag == SYNC_AFTER_WMIFLAG) || (syncflag == SYNC_BOTH_WMIFLAG)) {
+ /*
+ * We want to make sure all new data queued waits for the command to
+ * execute. Establish a new sync point.
+ */
+ wmi_sync_point(wmip);
+ }
+
+ return (A_OK);
+#undef IS_LONG_CMD
+}
+
+A_STATUS
+wmi_cmd_send_xtnd(struct wmi_t *wmip, void *osbuf, WMI_COMMAND_ID cmdId,
+ WMI_SYNC_FLAG syncflag)
+{
+ WMIX_CMD_HDR *cHdr;
+
+ if (a_netbuf_push(osbuf, sizeof(WMIX_CMD_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ cHdr = (WMIX_CMD_HDR *)a_netbuf_to_data(osbuf);
+ cHdr->commandId = cmdId;
+
+ return wmi_cmd_send(wmip, osbuf, WMI_EXTENSION_CMDID, syncflag);
+}
+
+A_STATUS
+wmi_connect_cmd(struct wmi_t *wmip, NETWORK_TYPE netType,
+ DOT11_AUTH_MODE dot11AuthMode, AUTH_MODE authMode,
+ CRYPTO_TYPE pairwiseCrypto, A_UINT8 pairwiseCryptoLen,
+ CRYPTO_TYPE groupCrypto,A_UINT8 groupCryptoLen,
+ int ssidLength, A_UCHAR *ssid,
+ A_UINT8 *bssid, A_UINT16 channel)
+{
+ void *osbuf;
+ WMI_CONNECT_CMD *cc;
+
+ if ((pairwiseCrypto == NONE_CRYPT) && (groupCrypto != NONE_CRYPT)) {
+ return A_EINVAL;
+ }
+ if ((pairwiseCrypto != NONE_CRYPT) && (groupCrypto == NONE_CRYPT)) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(WMI_CONNECT_CMD));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(WMI_CONNECT_CMD));
+
+ cc = (WMI_CONNECT_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cc, sizeof(*cc));
+
+ A_MEMCPY(cc->ssid, ssid, ssidLength);
+ cc->ssidLength = ssidLength;
+ cc->networkType = netType;
+ cc->dot11AuthMode = dot11AuthMode;
+ cc->authMode = authMode;
+ cc->pairwiseCryptoType = pairwiseCrypto;
+ cc->pairwiseCryptoLen = pairwiseCryptoLen;
+ cc->groupCryptoType = groupCrypto;
+ cc->groupCryptoLen = groupCryptoLen;
+ cc->channel = channel;
+
+ if (bssid != NULL) {
+ A_MEMCPY(cc->bssid, bssid, ATH_MAC_LEN);
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_CONNECT_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_reconnect_cmd(struct wmi_t *wmip, A_UINT8 *bssid, A_UINT16 channel)
+{
+ void *osbuf;
+ WMI_RECONNECT_CMD *cc;
+
+ osbuf = a_netbuf_alloc(sizeof(WMI_RECONNECT_CMD));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(WMI_RECONNECT_CMD));
+
+ cc = (WMI_RECONNECT_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cc, sizeof(*cc));
+
+ cc->channel = channel;
+
+ if (bssid != NULL) {
+ A_MEMCPY(cc->bssid, bssid, ATH_MAC_LEN);
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_RECONNECT_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_disconnect_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+ A_STATUS status;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ status = (wmi_cmd_send(wmip, osbuf, WMI_DISCONNECT_CMDID,
+ SYNC_BEFORE_WMIFLAG));
+ wmi_qos_state_init(wmip);
+ return status;
+}
+
+A_STATUS
+wmi_startscan_cmd(struct wmi_t *wmip, WMI_SCAN_TYPE scanType)
+{
+ void *osbuf;
+ WMI_START_SCAN_CMD *sc;
+
+ if ((scanType != WMI_LONG_SCAN) && (scanType != WMI_SHORT_SCAN)) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*sc));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*sc));
+
+ sc = (WMI_START_SCAN_CMD *)(a_netbuf_to_data(osbuf));
+ sc->scanType = scanType;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_START_SCAN_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_scanparams_cmd(struct wmi_t *wmip, A_UINT16 fg_start_sec,
+ A_UINT16 fg_end_sec, A_UINT16 bg_sec,
+ A_UINT16 act_chdw_msec, A_UINT16 pas_chdw_msec,
+ A_UINT8 shScanRatio)
+{
+ void *osbuf;
+ WMI_SCAN_PARAMS_CMD *sc;
+
+ osbuf = a_netbuf_alloc(sizeof(*sc));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*sc));
+
+ sc = (WMI_SCAN_PARAMS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(sc, sizeof(*sc));
+ sc->fg_start_period = fg_start_sec;
+ sc->fg_end_period = fg_end_sec;
+ sc->bg_period = bg_sec;
+ sc->act_chdwell_time = act_chdw_msec;
+ sc->pas_chdwell_time = pas_chdw_msec;
+ sc->shortScanRatio = shScanRatio;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_SCAN_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_bssfilter_cmd(struct wmi_t *wmip, A_UINT8 filter)
+{
+ void *osbuf;
+ WMI_BSS_FILTER_CMD *cmd;
+
+ if (filter >= LAST_BSS_FILTER) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_BSS_FILTER_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->bssFilter = filter;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_BSS_FILTER_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_probedSsid_cmd(struct wmi_t *wmip, A_UINT8 index, A_UINT8 flag,
+ A_UINT8 ssidLength, A_UCHAR *ssid)
+{
+ void *osbuf;
+ WMI_PROBED_SSID_CMD *cmd;
+
+ if (index > MAX_PROBED_SSID_INDEX) {
+ return A_EINVAL;
+ }
+ if (ssidLength > sizeof(cmd->ssid)) {
+ return A_EINVAL;
+ }
+ if ((flag & (DISABLE_SSID_FLAG | ANY_SSID_FLAG)) && (ssidLength > 0)) {
+ return A_EINVAL;
+ }
+ if ((flag & SPECIFIC_SSID_FLAG) && !ssidLength) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_PROBED_SSID_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->entryIndex = index;
+ cmd->flag = flag;
+ cmd->ssidLength = ssidLength;
+ A_MEMCPY(cmd->ssid, ssid, ssidLength);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_PROBED_SSID_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_listeninterval_cmd(struct wmi_t *wmip, A_UINT16 listenInterval, A_UINT16 listenBeacons)
+{
+ void *osbuf;
+ WMI_LISTEN_INT_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_LISTEN_INT_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->listenInterval = listenInterval;
+ cmd->numBeacons = listenBeacons;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_LISTEN_INT_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_bmisstime_cmd(struct wmi_t *wmip, A_UINT16 bmissTime, A_UINT16 bmissBeacons)
+{
+ void *osbuf;
+ WMI_BMISS_TIME_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_BMISS_TIME_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->bmissTime = bmissTime;
+ cmd->numBeacons = bmissBeacons;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_BMISS_TIME_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_associnfo_cmd(struct wmi_t *wmip, A_UINT8 ieType,
+ A_UINT8 ieLen, A_UINT8 *ieInfo)
+{
+ void *osbuf;
+ WMI_SET_ASSOC_INFO_CMD *cmd;
+ A_UINT16 cmdLen;
+
+ cmdLen = sizeof(*cmd) + ieLen - 1;
+ osbuf = a_netbuf_alloc(cmdLen);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, cmdLen);
+
+ cmd = (WMI_SET_ASSOC_INFO_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, cmdLen);
+ cmd->ieType = ieType;
+ cmd->bufferSize = ieLen;
+ A_MEMCPY(cmd->assocInfo, ieInfo, ieLen);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_ASSOC_INFO_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_powermode_cmd(struct wmi_t *wmip, A_UINT8 powerMode)
+{
+ void *osbuf;
+ WMI_POWER_MODE_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_POWER_MODE_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->powerMode = powerMode;
+ wmip->wmi_powerMode = powerMode;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_POWER_MODE_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_ibsspmcaps_cmd(struct wmi_t *wmip, A_UINT8 pmEnable, A_UINT8 ttl,
+ A_UINT16 atim_windows, A_UINT16 timeout_value)
+{
+ void *osbuf;
+ WMI_IBSS_PM_CAPS_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_IBSS_PM_CAPS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->power_saving = pmEnable;
+ cmd->ttl = ttl;
+ cmd->atim_windows = atim_windows;
+ cmd->timeout_value = timeout_value;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_IBSS_PM_CAPS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_pmparams_cmd(struct wmi_t *wmip, A_UINT16 idlePeriod,
+ A_UINT16 psPollNum, A_UINT16 dtimPolicy)
+{
+ void *osbuf;
+ WMI_POWER_PARAMS_CMD *pm;
+
+ osbuf = a_netbuf_alloc(sizeof(*pm));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*pm));
+
+ pm = (WMI_POWER_PARAMS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(pm, sizeof(*pm));
+ pm->idle_period = idlePeriod;
+ pm->pspoll_number = psPollNum;
+ pm->dtim_policy = dtimPolicy;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_POWER_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_disctimeout_cmd(struct wmi_t *wmip, A_UINT8 timeout)
+{
+ void *osbuf;
+ WMI_DISC_TIMEOUT_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_DISC_TIMEOUT_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->disconnectTimeout = timeout;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_DISC_TIMEOUT_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_addKey_cmd(struct wmi_t *wmip, A_UINT8 keyIndex, CRYPTO_TYPE keyType,
+ A_UINT8 keyUsage, A_UINT8 keyLength, A_UINT8 *keyRSC,
+ A_UINT8 *keyMaterial, WMI_SYNC_FLAG sync_flag)
+{
+ void *osbuf;
+ WMI_ADD_CIPHER_KEY_CMD *cmd;
+
+ if ((keyIndex > WMI_MAX_KEY_INDEX) || (keyLength > WMI_MAX_KEY_LEN) ||
+ (keyMaterial == NULL))
+ {
+ return A_EINVAL;
+ }
+
+ if ((WEP_CRYPT != keyType) && (NULL == keyRSC)) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_ADD_CIPHER_KEY_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->keyIndex = keyIndex;
+ cmd->keyType = keyType;
+ cmd->keyUsage = keyUsage;
+ cmd->keyLength = keyLength;
+ A_MEMCPY(cmd->key, keyMaterial, keyLength);
+ if (NULL != keyRSC) {
+ A_MEMCPY(cmd->keyRSC, keyRSC, sizeof(cmd->keyRSC));
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_ADD_CIPHER_KEY_CMDID, sync_flag));
+}
+
+A_STATUS
+wmi_add_krk_cmd(struct wmi_t *wmip, A_UINT8 *krk)
+{
+ void *osbuf;
+ WMI_ADD_KRK_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_ADD_KRK_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ A_MEMCPY(cmd->krk, krk, WMI_KRK_LEN);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_ADD_KRK_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_delete_krk_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0);
+
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_DELETE_KRK_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_deleteKey_cmd(struct wmi_t *wmip, A_UINT8 keyIndex)
+{
+ void *osbuf;
+ WMI_DELETE_CIPHER_KEY_CMD *cmd;
+
+ if (keyIndex > WMI_MAX_KEY_INDEX) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_DELETE_CIPHER_KEY_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->keyIndex = keyIndex;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_DELETE_CIPHER_KEY_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_setPmkid_cmd(struct wmi_t *wmip, A_UINT8 *bssid, A_UINT8 *pmkId,
+ A_BOOL set)
+{
+ void *osbuf;
+ WMI_SET_PMKID_CMD *cmd;
+
+ if (bssid == NULL) {
+ return A_EINVAL;
+ }
+
+ if ((set == TRUE) && (pmkId == NULL)) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_PMKID_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMCPY(cmd->bssid, bssid, sizeof(cmd->bssid));
+ if (set == TRUE) {
+ A_MEMCPY(cmd->pmkid, pmkId, sizeof(cmd->pmkid));
+ cmd->enable = PMKID_ENABLE;
+ } else {
+ A_MEMZERO(cmd->pmkid, sizeof(cmd->pmkid));
+ cmd->enable = PMKID_DISABLE;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_PMKID_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_tkip_countermeasures_cmd(struct wmi_t *wmip, A_BOOL en)
+{
+ void *osbuf;
+ WMI_SET_TKIP_COUNTERMEASURES_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_TKIP_COUNTERMEASURES_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->cm_en = (en == TRUE)? WMI_TKIP_CM_ENABLE : WMI_TKIP_CM_DISABLE;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_TKIP_COUNTERMEASURES_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_dataSync_send(struct wmi_t *wmip, void *osbuf, int endPoint)
+{
+ WMI_DATA_HDR *dtHdr;
+
+ A_ASSERT(endPoint != WMI_CONTROL_MBOX);
+ A_ASSERT(osbuf != NULL);
+
+ if (a_netbuf_push(osbuf, sizeof(WMI_DATA_HDR)) != A_OK) {
+ return A_NO_MEMORY;
+ }
+
+ dtHdr = (WMI_DATA_HDR *)a_netbuf_to_data(osbuf);
+ dtHdr->info =
+ (SYNC_MSGTYPE & WMI_DATA_HDR_MSG_TYPE_MASK) << WMI_DATA_HDR_MSG_TYPE_SHIFT;
+
+ WMI_DEBUG_PRINTF("wmi sync pkt sent on mbox %d\n", endPoint);
+
+ return (ar6000_control_tx(wmip->wmi_devt, osbuf, endPoint));
+}
+
+static A_STATUS
+wmi_sync_point(struct wmi_t *wmip)
+{
+ void *cmd_osbuf, *be_osbuf, *pri_osbuf[WMI_MAX_NUM_PRI_STREAMS];
+ A_UINT8 i;
+ A_STATUS status;
+
+ WMI_DEBUG_PRINTF("wmi_sync_point\n");
+ /*
+ * We allocate all network buffers needed so we will be able to
+ * send all required frames.
+ */
+ cmd_osbuf = a_netbuf_alloc(0); /* no payload */
+ if (cmd_osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ be_osbuf = a_netbuf_alloc(0); /* no payload */
+ if (be_osbuf == NULL) {
+ a_netbuf_free(cmd_osbuf);
+ return A_NO_MEMORY;
+ }
+
+ for (i = 0; i < wmip->wmi_numQoSStream; i++) {
+ pri_osbuf[i] = a_netbuf_alloc(0); /* no payload */
+ if (pri_osbuf[i] == NULL) {
+ A_UINT8 j;
+ a_netbuf_free(be_osbuf);
+ a_netbuf_free(cmd_osbuf);
+ /* free previously allocated bufs */
+ for (j = 0; j < i; j++) {
+ a_netbuf_free(pri_osbuf[j]);
+ }
+ return A_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Send sync cmd followed by sync data messages on all endpoints being
+ * used
+ */
+ status = wmi_cmd_send(wmip, cmd_osbuf, WMI_SYNCHRONIZE_CMDID,
+ NO_SYNC_WMIFLAG);
+
+ if (status == A_OK) {
+ status = wmi_dataSync_send(wmip, be_osbuf, WMI_BEST_EFFORT_MBOX);
+ }
+
+ if (status == A_OK) {
+ A_UINT8 priIndex = 0;
+ for (i = 0; i < wmip->wmi_numQoSStream; i++) {
+ while (priIndex < WMI_MAX_NUM_PRI_STREAMS &&
+ (!wmip->wmi_priority[priIndex].inUse)) {
+ priIndex++;
+ }
+ if (priIndex >= WMI_MAX_NUM_PRI_STREAMS) {
+ break;
+ }
+ status = wmi_dataSync_send(wmip, pri_osbuf[i], wmip->wmi_priority[priIndex].mbox);
+ if (status != A_OK) {
+ break;
+ }
+ priIndex++;
+ }
+ }
+
+ return (status);
+}
+
+A_STATUS
+wmi_create_pstream_cmd(struct wmi_t *wmip, WMI_CREATE_PSTREAM_CMD *params)
+{
+ void *osbuf;
+ WMI_CREATE_PSTREAM_CMD *cmd;
+
+ /* Validate all the parameters. */
+ if( !((params->userPriority < 8) &&
+ (params->trafficDirection == BIDIR_TRAFFIC ||
+ params->trafficDirection == UPLINK_TRAFFIC ||
+ params->trafficDirection == DNLINK_TRAFFIC ) &&
+ (params->userPriority <= 0x7) &&
+ (convert_userPriority_to_trafficClass(params->userPriority) == params->trafficClass) &&
+ (params->trafficClass != WMM_AC_BE) &&
+ (params->trafficType == TRAFFIC_TYPE_APERIODIC ||
+ params->trafficType == TRAFFIC_TYPE_PERIODIC ) &&
+ (params->voicePSCapability == DISABLE_FOR_THIS_AC ||
+ params->voicePSCapability == ENABLE_FOR_THIS_AC ||
+ params->voicePSCapability == ENABLE_FOR_ALL_AC) &&
+ (params->tsid < 15)) )
+ {
+ return A_EINVAL;
+ }
+
+
+ if (params->trafficDirection == BIDIR_TRAFFIC) {
+ if (wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][params->trafficClass]
+ || wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][params->trafficClass]
+ || wmip->wmi_cpstreamCmdInProgress) {
+ WMI_DEBUG_PRINTF("create %d too busy !\n",params->trafficClass);
+ return A_EBUSY;
+ }
+ wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][params->trafficClass] = TRUE;
+ wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][params->trafficClass] = TRUE;
+ } else {
+ if (wmip->wmi_pstreamCmdInProgress[params->trafficDirection][params->trafficClass]
+ || wmip->wmi_cpstreamCmdInProgress) {
+ WMI_DEBUG_PRINTF("create %d too busy !\n",params->trafficClass);
+ return A_EBUSY;
+ }
+ wmip->wmi_pstreamCmdInProgress[params->trafficDirection][params->trafficClass] = TRUE;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ wmip->wmi_cpstreamCmdInProgress = TRUE;
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ WMI_DEBUG_PRINTF("Sending create_pstream_cmd: ac=%d, rxQ=%d, dir=%d\n",
+ params->trafficClass, params->rxQueueNum, params->trafficDirection);
+ cmd = (WMI_CREATE_PSTREAM_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ A_MEMCPY(cmd, params, sizeof(*cmd));
+
+ if (params->rxQueueNum == 0xFF) {
+ if(wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][params->trafficClass] != WMI_NOT_MAPPED)
+ cmd->rxQueueNum = wmip->wmi_trafficClassMap[DNLINK_TRAFFIC][params->trafficClass];
+ else
+ cmd->rxQueueNum = wmip->wmi_olderPriRxMbox;
+ } else {
+ cmd->rxQueueNum = params->rxQueueNum;
+ }
+
+ /* mike: should be SYNC_BEFORE_WMIFLAG */
+ return (wmi_cmd_send(wmip, osbuf, WMI_CREATE_PSTREAM_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_delete_pstream_cmd(struct wmi_t *wmip, A_UINT8 txQueueNumber, A_UINT8 rxQueueNumber, A_UINT8 dir)
+{
+ void *osbuf;
+ WMI_DELETE_PSTREAM_CMD *cmd;
+ A_STATUS status;
+ A_UINT8 class;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ if (dir == BIDIR_TRAFFIC) {
+ class = wmip->wmi_mboxMap[UPLINK_TRAFFIC][txQueueNumber].trafficClass;
+ if (wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][class]
+ || wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][class]) {
+ return A_EBUSY;
+ }
+ wmip->wmi_pstreamCmdInProgress[UPLINK_TRAFFIC][class] = TRUE;
+ wmip->wmi_pstreamCmdInProgress[DNLINK_TRAFFIC][class] = TRUE;
+ } else {
+ if (dir == UPLINK_TRAFFIC) {
+ class = wmip->wmi_mboxMap[UPLINK_TRAFFIC][txQueueNumber].trafficClass;
+ } else {
+ class = wmip->wmi_mboxMap[DNLINK_TRAFFIC][rxQueueNumber].trafficClass;
+ }
+ if (wmip->wmi_pstreamCmdInProgress[dir][class]) {
+ return A_EBUSY;
+ }
+ wmip->wmi_pstreamCmdInProgress[dir][class] = TRUE;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_DELETE_PSTREAM_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->txQueueNumber = txQueueNumber;
+ cmd->rxQueueNumber = rxQueueNumber;
+ cmd->trafficDirection = dir;
+ cmd->trafficClass = class;
+ WMI_DEBUG_PRINTF("Sending delete_pstream_cmd: txQ=%d, rxQ=%d, dir=%d\n",
+ txQueueNumber, rxQueueNumber, dir);
+ status = (wmi_cmd_send(wmip, osbuf, WMI_DELETE_PSTREAM_CMDID,
+ (dir == UPLINK_TRAFFIC || dir == BIDIR_TRAFFIC) ? SYNC_BEFORE_WMIFLAG : NO_SYNC_WMIFLAG));
+
+ if (class != WMM_AC_BE) {
+ /* Update internal states */
+ if (dir == UPLINK_TRAFFIC || dir == BIDIR_TRAFFIC) {
+ wmip->wmi_numQoSStream--;
+ ar6000_set_numdataendpts(wmip->wmi_devt, wmip->wmi_numQoSStream+1);
+ wmip->wmi_priority[wmip->wmi_mboxMap[UPLINK_TRAFFIC][txQueueNumber].priorityNum].inUse = 0;
+ wmip->wmi_trafficClassMap[UPLINK_TRAFFIC][class] = WMI_NOT_MAPPED;
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][txQueueNumber].priorityNum = WMI_NOT_MAPPED;
+ wmip->wmi_mboxMap[UPLINK_TRAFFIC][txQueueNumber].trafficClass = WMM_AC_BE;
+ }
+ }
+
+ return status;
+}
+
+
+/*
+ * used to set the bit rate. rate is in Kbps. If rate == -1
+ * then auto selection is used.
+ */
+A_STATUS
+wmi_set_bitrate_cmd(struct wmi_t *wmip, A_INT32 rate)
+{
+ void *osbuf;
+ WMI_BIT_RATE_CMD *cmd;
+ A_INT8 index;
+
+ index = wmi_validate_bitrate(wmip, rate);
+ if(index == A_EINVAL){
+ return A_EINVAL;
+ }
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_BIT_RATE_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+
+ cmd->rateIndex = index;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_BITRATE_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_get_bitrate_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_BITRATE_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_INT8
+wmi_validate_bitrate(struct wmi_t *wmip, A_INT32 rate)
+{
+ A_INT8 i;
+ if (rate != -1)
+ {
+ for (i=0;;i++)
+ {
+ if (wmi_rateTable[i] == 0) {
+ return A_EINVAL;
+ }
+ if (wmi_rateTable[i] == rate) {
+ break;
+ }
+ }
+ }
+ else{
+ i = -1;
+ }
+
+ if (wmip->wmi_phyMode == WMI_11A_MODE &&
+ (i < MODE_A_SUPPORT_RATE_START || i > MODE_A_SUPPORT_RATE_STOP))
+ {
+ return A_EINVAL;
+ } else if (wmip->wmi_phyMode == WMI_11B_MODE &&
+ (i < MODE_B_SUPPORT_RATE_START || i > MODE_B_SUPPORT_RATE_STOP))
+ {
+ return A_EINVAL;
+ } else if (wmip->wmi_phyMode == WMI_11GONLY_MODE &&
+ (i < MODE_GONLY_SUPPORT_RATE_START || i > MODE_GONLY_SUPPORT_RATE_STOP))
+ {
+ return A_EINVAL;
+ }
+
+ return i;
+ }
+
+A_STATUS
+wmi_get_channelList_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_CHANNEL_LIST_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+/*
+ * used to generate a wmi sey channel Parameters cmd.
+ * mode should always be specified and corresponds to the phy mode of the
+ * wlan.
+ * numChan should alway sbe specified. If zero indicates that all available
+ * channels should be used.
+ * channelList is an array of channel frequencies (in Mhz) which the radio
+ * should limit its operation to. It should be NULL if numChan == 0. Size of
+ * array should correspond to numChan entries.
+ */
+A_STATUS
+wmi_set_channelParams_cmd(struct wmi_t *wmip, WMI_PHY_MODE mode, A_INT8 numChan,
+ A_UINT16 *channelList)
+{
+ void *osbuf;
+ WMI_CHANNEL_PARAMS_CMD *cmd;
+ A_INT8 size;
+
+ size = sizeof (*cmd);
+
+ if (numChan) {
+ if (numChan > WMI_MAX_CHANNELS) {
+ return A_EINVAL;
+ }
+ size += sizeof(A_UINT16) * (numChan - 1);
+ }
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_CHANNEL_PARAMS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, size);
+
+ wmip->wmi_phyMode = mode;
+ cmd->phyMode = mode;
+ cmd->numChannels = numChan;
+ A_MEMCPY(cmd->channelList, channelList, numChan * sizeof(A_UINT16));
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_CHANNEL_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_link_threshold_params(struct wmi_t *wmip,
+ A_UINT8 highThreshold_upperVal,
+ A_UINT8 highThreshold_lowerVal,
+ A_UINT8 lowThreshold_upperVal,
+ A_UINT8 lowThreshold_lowerVal,
+ A_UINT32 pollTime)
+{
+ void *osbuf;
+ A_INT8 size;
+ WMI_RSSI_THRESHOLD_PARAMS_CMD *cmd;
+
+ /* These values are in ascending order */
+ if( highThreshold_upperVal <= highThreshold_lowerVal ||
+ lowThreshold_upperVal <= lowThreshold_lowerVal ||
+ highThreshold_lowerVal <= lowThreshold_upperVal)
+ return A_EINVAL;
+
+ size = sizeof (*cmd);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_RSSI_THRESHOLD_PARAMS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, size);
+
+ cmd->highThreshold_upperVal = highThreshold_upperVal;
+ cmd->highThreshold_lowerVal = highThreshold_lowerVal;
+ cmd->lowThreshold_upperVal = lowThreshold_upperVal;
+ cmd->lowThreshold_lowerVal = lowThreshold_lowerVal;
+ cmd->pollTime = pollTime;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_RSSI_THRESHOLD_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+
+A_STATUS
+wmi_set_error_report_bitmask(struct wmi_t *wmip, A_UINT32 mask)
+{
+ void *osbuf;
+ A_INT8 size;
+ WMI_TARGET_ERROR_REPORT_BITMASK *cmd;
+
+ size = sizeof (*cmd);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_TARGET_ERROR_REPORT_BITMASK *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, size);
+
+ cmd->bitmask = mask;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_TARGET_ERROR_REPORT_BITMASK_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+
+A_STATUS
+wmi_get_stats_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_STATISTICS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_addBadAp_cmd(struct wmi_t *wmip, A_UINT8 apIndex, A_UINT8 *bssid)
+{
+ void *osbuf;
+ WMI_ADD_BAD_AP_CMD *cmd;
+
+ if ((bssid == NULL) || (apIndex > WMI_MAX_BAD_AP_INDEX)) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_ADD_BAD_AP_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->badApIndex = apIndex;
+ A_MEMCPY(cmd->bssid, bssid, sizeof(cmd->bssid));
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_ADD_BAD_AP_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_deleteBadAp_cmd(struct wmi_t *wmip, A_UINT8 apIndex)
+{
+ void *osbuf;
+ WMI_DELETE_BAD_AP_CMD *cmd;
+
+ if (apIndex > WMI_MAX_BAD_AP_INDEX) {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_DELETE_BAD_AP_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->badApIndex = apIndex;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_DELETE_BAD_AP_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_txPwr_cmd(struct wmi_t *wmip, A_UINT8 dbM)
+{
+ void *osbuf;
+ WMI_SET_TX_PWR_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_TX_PWR_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->dbM = dbM;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_TX_PWR_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_get_txPwr_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_TX_PWR_CMDID, NO_SYNC_WMIFLAG));
+}
+
+A_INT8
+wmi_get_mapped_qos_queue(struct wmi_t *wmip, A_UINT8 dir, A_UINT8 trafficClass)
+{
+ if ((dir == UPLINK_TRAFFIC || dir == DNLINK_TRAFFIC) &&
+ (trafficClass <= WMM_AC_VO)) {
+ return wmip->wmi_trafficClassMap[dir][trafficClass];
+ } else {
+ return WMI_NOT_MAPPED;
+ }
+}
+
+A_STATUS
+wmi_get_roam_tbl_cmd(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ osbuf = a_netbuf_alloc(0); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_ROAM_TBL_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_get_roam_data_cmd(struct wmi_t *wmip, A_UINT8 roamDataType)
+{
+ void *osbuf;
+ A_UINT32 size = sizeof(A_UINT8);
+ WMI_TARGET_ROAM_DATA *cmd;
+
+ osbuf = a_netbuf_alloc(size); /* no payload */
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_TARGET_ROAM_DATA *)(a_netbuf_to_data(osbuf));
+ cmd->roamDataType = roamDataType;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_GET_ROAM_DATA_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_roam_ctrl_cmd(struct wmi_t *wmip, WMI_SET_ROAM_CTRL_CMD *p,
+ A_UINT8 size)
+{
+ void *osbuf;
+ WMI_SET_ROAM_CTRL_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_SET_ROAM_CTRL_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, size);
+
+ A_MEMCPY(cmd, p, size);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_ROAM_CTRL_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_powersave_timers_cmd(struct wmi_t *wmip,
+ WMI_POWERSAVE_TIMERS_CMD *pCmd,
+ A_UINT8 size)
+{
+ void *osbuf;
+ WMI_POWERSAVE_TIMERS_CMD *cmd;
+
+ /* These timers can't be zero */
+ if(!pCmd->psPollTimeout || !pCmd->eospTimeout)
+ return A_EINVAL;
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, size);
+
+ cmd = (WMI_POWERSAVE_TIMERS_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, size);
+
+ A_MEMCPY(cmd, pCmd, size);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_POWERSAVE_TIMERS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_dset_open_reply(struct wmi_t *wmip,
+ A_UINT32 status,
+ A_UINT32 access_cookie,
+ A_UINT32 dset_size,
+ A_UINT32 dset_version,
+ A_UINT32 targ_handle,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg)
+{
+ void *osbuf;
+ WMIX_DSETOPEN_REPLY_CMD *open_reply;
+
+ WMI_DEBUG_PRINTF("wmi_dset_open_reply: wmip=0x%x\n", (int)wmip);
+
+ osbuf = a_netbuf_alloc(sizeof(*open_reply));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*open_reply));
+ open_reply = (WMIX_DSETOPEN_REPLY_CMD *)(a_netbuf_to_data(osbuf));
+
+ open_reply->status = status;
+ open_reply->targ_dset_handle = targ_handle;
+ open_reply->targ_reply_fn = targ_reply_fn;
+ open_reply->targ_reply_arg = targ_reply_arg;
+ open_reply->access_cookie = access_cookie;
+ open_reply->size = dset_size;
+ open_reply->version = dset_version;
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_DSETOPEN_REPLY_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+#if CONFIG_HOST_DSET_SUPPORT
+A_STATUS
+wmi_dset_data_reply(struct wmi_t *wmip,
+ A_UINT32 status,
+ A_UINT8 *user_buf,
+ A_UINT32 length,
+ A_UINT32 targ_buf,
+ A_UINT32 targ_reply_fn,
+ A_UINT32 targ_reply_arg)
+{
+ void *osbuf;
+ WMIX_DSETDATA_REPLY_CMD *data_reply;
+ int size;
+
+ size = sizeof(*data_reply) + length;
+
+ WMI_DEBUG_PRINTF("wmi_dset_data_reply: length=%d status=%d\n", length, status);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(osbuf, size);
+ data_reply = (WMIX_DSETDATA_REPLY_CMD *)(a_netbuf_to_data(osbuf));
+
+ data_reply->status = status;
+ data_reply->targ_buf = targ_buf;
+ data_reply->targ_reply_fn = targ_reply_fn;
+ data_reply->targ_reply_arg = targ_reply_arg;
+ data_reply->length = length;
+
+ if (status == A_OK) {
+ if (a_copy_from_user(data_reply->buf, user_buf, length)) {
+ return A_ERROR;
+ }
+ }
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_DSETDATA_REPLY_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+#endif /* CONFIG_HOST_DSET_SUPPORT */
+
+#if CONFIG_HOST_GPIO_SUPPORT
+/* Send a command to Target to change GPIO output pins. */
+A_STATUS
+wmi_gpio_output_set(struct wmi_t *wmip,
+ A_UINT32 set_mask,
+ A_UINT32 clear_mask,
+ A_UINT32 enable_mask,
+ A_UINT32 disable_mask)
+{
+ void *osbuf;
+ WMIX_GPIO_OUTPUT_SET_CMD *output_set;
+ int size;
+
+ size = sizeof(*output_set);
+
+ WMI_DEBUG_PRINTF("wmi_gpio_output_set: set=0x%x clear=0x%x enb=0x%x dis=0x%x\n",
+ set_mask, clear_mask, enable_mask, disable_mask);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(osbuf, size);
+ output_set = (WMIX_GPIO_OUTPUT_SET_CMD *)(a_netbuf_to_data(osbuf));
+
+ output_set->set_mask = set_mask;
+ output_set->clear_mask = clear_mask;
+ output_set->enable_mask = enable_mask;
+ output_set->disable_mask = disable_mask;
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_GPIO_OUTPUT_SET_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+/* Send a command to the Target requesting state of the GPIO input pins */
+A_STATUS
+wmi_gpio_input_get(struct wmi_t *wmip)
+{
+ void *osbuf;
+
+ WMI_DEBUG_PRINTF("wmi_gpio_input_get\n");
+
+ osbuf = a_netbuf_alloc(0);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_GPIO_INPUT_GET_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+/* Send a command to the Target that changes the value of a GPIO register. */
+A_STATUS
+wmi_gpio_register_set(struct wmi_t *wmip,
+ A_UINT32 gpioreg_id,
+ A_UINT32 value)
+{
+ void *osbuf;
+ WMIX_GPIO_REGISTER_SET_CMD *register_set;
+ int size;
+
+ size = sizeof(*register_set);
+
+ WMI_DEBUG_PRINTF("wmi_gpio_register_set: reg=%d value=0x%x\n",
+ gpioreg_id, value);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(osbuf, size);
+ register_set = (WMIX_GPIO_REGISTER_SET_CMD *)(a_netbuf_to_data(osbuf));
+
+ register_set->gpioreg_id = gpioreg_id;
+ register_set->value = value;
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_GPIO_REGISTER_SET_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+/* Send a command to the Target to fetch the value of a GPIO register. */
+A_STATUS
+wmi_gpio_register_get(struct wmi_t *wmip,
+ A_UINT32 gpioreg_id)
+{
+ void *osbuf;
+ WMIX_GPIO_REGISTER_GET_CMD *register_get;
+ int size;
+
+ size = sizeof(*register_get);
+
+ WMI_DEBUG_PRINTF("wmi_gpio_register_get: reg=%d\n", gpioreg_id);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(osbuf, size);
+ register_get = (WMIX_GPIO_REGISTER_GET_CMD *)(a_netbuf_to_data(osbuf));
+
+ register_get->gpioreg_id = gpioreg_id;
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_GPIO_REGISTER_GET_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+/* Send a command to the Target acknowledging some GPIO interrupts. */
+A_STATUS
+wmi_gpio_intr_ack(struct wmi_t *wmip,
+ A_UINT32 ack_mask)
+{
+ void *osbuf;
+ WMIX_GPIO_INTR_ACK_CMD *intr_ack;
+ int size;
+
+ size = sizeof(*intr_ack);
+
+ WMI_DEBUG_PRINTF("wmi_gpio_intr_ack: ack_mask=0x%x\n", ack_mask);
+
+ osbuf = a_netbuf_alloc(size);
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+ a_netbuf_put(osbuf, size);
+ intr_ack = (WMIX_GPIO_INTR_ACK_CMD *)(a_netbuf_to_data(osbuf));
+
+ intr_ack->ack_mask = ack_mask;
+
+ return (wmi_cmd_send_xtnd(wmip, osbuf, WMIX_GPIO_INTR_ACK_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+#endif /* CONFIG_HOST_GPIO_SUPPORT */
+
+A_STATUS
+wmi_set_access_params_cmd(struct wmi_t *wmip, A_UINT16 txop, A_UINT8 eCWmin,
+ A_UINT8 eCWmax, A_UINT8 aifsn)
+{
+ void *osbuf;
+ WMI_SET_ACCESS_PARAMS_CMD *cmd;
+
+ if ((eCWmin > WMI_MAX_CW_ACPARAM) || (eCWmax > WMI_MAX_CW_ACPARAM) ||
+ (aifsn > WMI_MAX_AIFSN_ACPARAM))
+ {
+ return A_EINVAL;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_ACCESS_PARAMS_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->txop = txop;
+ cmd->eCWmin = eCWmin;
+ cmd->eCWmax = eCWmax;
+ cmd->aifsn = aifsn;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_ACCESS_PARAMS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_retry_limits_cmd(struct wmi_t *wmip, A_UINT8 frameType,
+ A_UINT8 trafficClass, A_UINT8 maxRetries)
+{
+ void *osbuf;
+ WMI_SET_RETRY_LIMITS_CMD *cmd;
+
+ if ((frameType != MGMT_FRAMETYPE) && (frameType != CONTROL_FRAMETYPE) &&
+ (frameType != DATA_FRAMETYPE))
+ {
+ return A_EINVAL;
+ }
+
+ if (maxRetries > WMI_MAX_RETRIES) {
+ return A_EINVAL;
+ }
+
+ if (frameType != DATA_FRAMETYPE) {
+ trafficClass = 0;
+ }
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_RETRY_LIMITS_CMD *)(a_netbuf_to_data(osbuf));
+ cmd->frameType = frameType;
+ cmd->trafficClass = trafficClass;
+ cmd->maxRetries = maxRetries;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_RETRY_LIMITS_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+void
+wmi_get_current_bssid(struct wmi_t *wmip, A_UINT8 *bssid)
+{
+ if (bssid != NULL) {
+ A_MEMCPY(bssid, wmip->wmi_bssid, ATH_MAC_LEN);
+ }
+}
+
+A_STATUS
+wmi_set_opt_mode_cmd(struct wmi_t *wmip, A_UINT8 optMode)
+{
+ void *osbuf;
+ WMI_SET_OPT_MODE_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_OPT_MODE_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->optMode = optMode;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_OPT_MODE_CMDID,
+ SYNC_BOTH_WMIFLAG));
+}
+
+A_STATUS
+wmi_opt_tx_frame_cmd(struct wmi_t *wmip,
+ A_UINT8 frmType,
+ A_UINT8 *dstMacAddr,
+ A_UINT8 *bssid,
+ A_UINT16 optIEDataLen,
+ A_UINT8 *optIEData)
+{
+ void *osbuf;
+ WMI_OPT_TX_FRAME_CMD *cmd;
+ osbuf = a_netbuf_alloc(optIEDataLen + sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, (optIEDataLen + sizeof(*cmd)));
+
+ cmd = (WMI_OPT_TX_FRAME_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, (optIEDataLen + sizeof(*cmd)));
+
+ cmd->frmType = frmType;
+ cmd->optIEDataLen = optIEDataLen;
+ cmd->optIEData = (A_UINT8 *)((int)cmd + sizeof(*cmd));
+ A_MEMCPY(cmd->bssid, bssid, sizeof(cmd->bssid));
+ A_MEMCPY(cmd->dstAddr, dstMacAddr, sizeof(cmd->dstAddr));
+ A_MEMCPY(cmd->optIEData, optIEData, optIEDataLen);
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_OPT_TX_FRAME_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_STATUS
+wmi_set_adhoc_bconIntvl_cmd(struct wmi_t *wmip, A_UINT16 intvl)
+{
+ void *osbuf;
+ WMI_BEACON_INT_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_BEACON_INT_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->beaconInterval = intvl;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_BEACON_INT_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+
+A_STATUS
+wmi_set_voice_pkt_size_cmd(struct wmi_t *wmip, A_UINT16 voicePktSize)
+{
+ void *osbuf;
+ WMI_SET_VOICE_PKT_SIZE_CMD *cmd;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_VOICE_PKT_SIZE_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->voicePktSize = voicePktSize;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_VOICE_PKT_SIZE_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+
+A_STATUS
+wmi_set_max_sp_len_cmd(struct wmi_t *wmip, A_UINT8 maxSPLen)
+{
+ void *osbuf;
+ WMI_SET_MAX_SP_LEN_CMD *cmd;
+
+ /* maxSPLen is a two-bit value. If user trys to set anything
+ * other than this, then its invalid
+ */
+ if(maxSPLen & ~0x03)
+ return A_EINVAL;
+
+ osbuf = a_netbuf_alloc(sizeof(*cmd));
+ if (osbuf == NULL) {
+ return A_NO_MEMORY;
+ }
+
+ a_netbuf_put(osbuf, sizeof(*cmd));
+
+ cmd = (WMI_SET_MAX_SP_LEN_CMD *)(a_netbuf_to_data(osbuf));
+ A_MEMZERO(cmd, sizeof(*cmd));
+ cmd->maxSPLen = maxSPLen;
+
+ return (wmi_cmd_send(wmip, osbuf, WMI_SET_MAX_SP_LEN_CMDID,
+ NO_SYNC_WMIFLAG));
+}
+
+A_UINT8
+convert_userPriority_to_trafficClass(A_UINT8 userPriority)
+{
+ return (up_to_ac[userPriority & 0x7]);
+}
+
+A_UINT8
+wmi_get_power_mode_cmd(struct wmi_t *wmip)
+{
+ return wmip->wmi_powerMode;
+}
+
+A_STATUS
+wmi_verify_tspec_params(WMI_CREATE_PSTREAM_CMD *pCmd, A_BOOL tspecCompliance)
+{
+ A_STATUS ret = A_OK;
+
+#define TSPEC_SUSPENSION_INTERVAL_CCXV4_DEF (~0)
+#define TSPEC_SERVICE_START_TIME_CCXV4_DEF 0
+#define TSPEC_MAX_BURST_SIZE_CCXV4_DEF 0
+#define TSPEC_DELAY_BOUND_CCXV4_DEF 0
+#define TSPEC_MEDIUM_TIME_CCXV4_DEF 0
+#define TSPEC_SBA_CCXV4_DEF 0x2000 /* factor is 1 */
+
+ /* Verify TSPEC params for CCX v4 compliance */
+ if(tspecCompliance == CCX_V4_COMPLIANCE) {
+ if ((pCmd->suspensionInt != TSPEC_SUSPENSION_INTERVAL_CCXV4_DEF) ||
+ (pCmd->serviceStartTime != TSPEC_SERVICE_START_TIME_CCXV4_DEF) ||
+ (pCmd->minDataRate != pCmd->meanDataRate) ||
+ (pCmd->minDataRate != pCmd->peakDataRate) ||
+ (pCmd->maxBurstSize != TSPEC_MAX_BURST_SIZE_CCXV4_DEF) ||
+ (pCmd->delayBound != TSPEC_DELAY_BOUND_CCXV4_DEF) ||
+ (pCmd->sba != TSPEC_SBA_CCXV4_DEF) ||
+ (pCmd->mediumTime != TSPEC_MEDIUM_TIME_CCXV4_DEF)) {
+
+ WMI_DEBUG_PRINTF("%s: Invalid TSPEC params\n", __FUNCTION__);
+ A_PRINTF("%s: Invalid TSPEC params\n", __FUNCTION__);
+ ret = A_EINVAL;
+ }
+ }
+
+ return ret;
+}
Added: developers/nbd/patches-2.6.22/100-asoc.patch
===================================================================
--- developers/nbd/patches-2.6.22/100-asoc.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/100-asoc.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,21306 @@
+Index: linux-2.6.22.1/sound/soc/codecs/ak4535.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/ak4535.c 2007-07-16 15:07:33.844265944 +0200
+@@ -0,0 +1,697 @@
++/*
++ * ak4535.c -- AK4535 ALSA Soc Audio driver
++ *
++ * Copyright 2005 Openedhand Ltd.
++ *
++ * Author: Richard Purdie <richard at openedhand.com>
++ *
++ * Based on wm8753.c by Liam Girdwood
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "ak4535.h"
++
++#define AUDIO_NAME "ak4535"
++#define AK4535_VERSION "0.3"
++
++struct snd_soc_codec_device soc_codec_dev_ak4535;
++
++/* codec private data */
++struct ak4535_priv {
++ unsigned int sysclk;
++};
++
++/*
++ * ak4535 register cache
++ */
++static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
++ 0x0000, 0x0080, 0x0000, 0x0003,
++ 0x0002, 0x0000, 0x0011, 0x0001,
++ 0x0000, 0x0040, 0x0036, 0x0010,
++ 0x0000, 0x0000, 0x0057, 0x0000,
++};
++
++/*
++ * read ak4535 register cache
++ */
++static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= AK4535_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write ak4535 register cache
++ */
++static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= AK4535_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the AK4535 register space
++ */
++static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D8 AK4535 register offset
++ * D7...D0 register data
++ */
++ data[0] = reg & 0xff;
++ data[1] = value & 0xff;
++
++ ak4535_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
++static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
++static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
++static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
++static const char *ak4535_mic_select[] = {"Internal", "External"};
++
++static const struct soc_enum ak4535_enum[] = {
++ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
++ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
++ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
++ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
++ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
++};
++
++static const struct snd_kcontrol_new ak4535_snd_controls[] = {
++ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
++ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
++ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
++ SOC_ENUM("Headphone Output", ak4535_enum[2]),
++ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
++ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
++ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
++ SOC_ENUM("Mic Select", ak4535_enum[4]),
++ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
++ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
++ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
++ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
++ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
++ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
++ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
++ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
++ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
++ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
++ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
++};
++
++/* add non dapm controls */
++static int ak4535_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&ak4535_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Mono 1 Mixer */
++static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
++ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
++ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
++};
++
++/* Stereo Mixer */
++static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
++ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
++ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
++ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
++};
++
++/* Input Mixer */
++static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
++ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
++ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
++};
++
++/* Input mux */
++static const struct snd_kcontrol_new ak4535_input_mux_control =
++ SOC_DAPM_ENUM("Input Select", ak4535_enum[0]);
++
++/* HP L switch */
++static const struct snd_kcontrol_new ak4535_hpl_control =
++ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
++
++/* HP R switch */
++static const struct snd_kcontrol_new ak4535_hpr_control =
++ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
++
++/* Speaker switch */
++static const struct snd_kcontrol_new ak4535_spk_control =
++ SOC_DAPM_SINGLE("Switch", AK4535_MODE2, 0, 0, 0);
++
++/* mono 2 switch */
++static const struct snd_kcontrol_new ak4535_mono2_control =
++ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
++
++/* Line out switch */
++static const struct snd_kcontrol_new ak4535_line_control =
++ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
++
++/* ak4535 dapm widgets */
++static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
++ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
++ &ak4535_stereo_mixer_controls[0],
++ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
++ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
++ &ak4535_mono1_mixer_controls[0],
++ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
++ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
++ &ak4535_input_mixer_controls[0],
++ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
++ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
++ &ak4535_input_mux_control),
++ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
++ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
++ &ak4535_mono2_control),
++ SND_SOC_DAPM_SWITCH("Speaker Enable", SND_SOC_NOPM, 0, 0,
++ &ak4535_spk_control),
++ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
++ &ak4535_line_control),
++ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
++ &ak4535_hpl_control),
++ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
++ &ak4535_hpr_control),
++ SND_SOC_DAPM_OUTPUT("LOUT"),
++ SND_SOC_DAPM_OUTPUT("HPL"),
++ SND_SOC_DAPM_OUTPUT("ROUT"),
++ SND_SOC_DAPM_OUTPUT("HPR"),
++ SND_SOC_DAPM_OUTPUT("SPP"),
++ SND_SOC_DAPM_OUTPUT("SPN"),
++ SND_SOC_DAPM_OUTPUT("MOUT1"),
++ SND_SOC_DAPM_OUTPUT("MOUT2"),
++ SND_SOC_DAPM_OUTPUT("MICOUT"),
++ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 1),
++ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
++
++ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
++ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
++ SND_SOC_DAPM_INPUT("MICIN"),
++ SND_SOC_DAPM_INPUT("MICEXT"),
++ SND_SOC_DAPM_INPUT("AUX"),
++ SND_SOC_DAPM_INPUT("MIN"),
++ SND_SOC_DAPM_INPUT("AIN"),
++};
++
++static const char *audio_map[][3] = {
++ /*stereo mixer */
++ {"Stereo Mixer", "Playback Switch", "DAC"},
++ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
++ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
++
++ /* mono1 mixer */
++ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
++ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
++
++ /* mono2 mixer */
++ {"Mono2 Mixer", "Mono Playback Switch", "Stereo Mixer"},
++
++ /* Mic */
++ {"AIN", NULL, "Mic"},
++ {"Input Mux", "Internal", "Mic Int Bias"},
++ {"Input Mux", "External", "Mic Ext Bias"},
++ {"Mic Int Bias", NULL, "MICIN"},
++ {"Mic Ext Bias", NULL, "MICEXT"},
++ {"MICOUT", NULL, "Input Mux"},
++
++ /* line out */
++ {"LOUT", "Switch", "Line"},
++ {"ROUT", "Switch", "Line Out Enable"},
++ {"Line Out Enable", NULL, "Line Out"},
++ {"Line Out", NULL, "Stereo Mixer"},
++
++ /* mono1 out */
++ {"MOUT1", NULL, "Mono Out"},
++ {"Mono Out", NULL, "Mono Mixer"},
++
++ /* left HP */
++ {"HPL", "Switch", "Left HP Enable"},
++ {"Left HP Enable", NULL, "HP L Amp"},
++ {"HP L Amp", NULL, "Stereo Mixer"},
++
++ /* right HP */
++ {"HPR", "Switch", "Right HP Enable"},
++ {"Right HP Enable", NULL, "HP R Amp"},
++ {"HP R Amp", NULL, "Stereo Mixer"},
++
++ /* speaker */
++ {"SPP", "Switch", "Speaker Enable"},
++ {"SPN", "Switch", "Speaker Enable"},
++ {"Speaker Enable", NULL, "Spk Amp"},
++ {"Spk Amp", NULL, "MIN"},
++
++ /* mono 2 */
++ {"MOUT2", "Switch", "Mono 2 Enable"},
++ {"Mono 2 Enable", NULL, "Stereo Mixer"},
++
++ /* Aux In */
++ {"Aux In", NULL, "AUX"},
++
++ /* ADC */
++ {"ADC", NULL, "Input Mixer"},
++ {"Input Mixer", "Mic Capture Switch", "Mic"},
++ {"Input Mixer", "Aux Capture Switch", "Aux In"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int ak4535_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(ak4535_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &ak4535_dapm_widgets[i]);
++ }
++
++ /* set up audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static int ak4535_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct ak4535_priv *ak4535 = codec->private_data;
++
++ ak4535->sysclk = freq;
++ return 0;
++}
++
++static int ak4535_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct ak4535_priv *ak4535 = codec->private_data;
++ u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
++ int rate = params_rate(params), fs = 256;
++
++ if (rate)
++ fs = ak4535->sysclk / rate;
++
++ /* set fs */
++ switch (fs) {
++ case 1024:
++ mode2 |= (0x2 << 5);
++ break;
++ case 512:
++ mode2 |= (0x1 << 5);
++ break;
++ case 256:
++ break;
++ }
++
++ /* set rate */
++ ak4535_write(codec, AK4535_MODE2, mode2);
++ return 0;
++}
++
++static int ak4535_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u8 mode1 = 0;
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ mode1 = 0x0002;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ mode1 = 0x0001;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* use 32 fs for BCLK to save power */
++ mode1 |= 0x4;
++
++ ak4535_write(codec, AK4535_MODE1, mode1);
++ return 0;
++}
++
++static int ak4535_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
++ if (mute)
++ ak4535_write(codec, AK4535_DAC, mute_reg);
++ else
++ ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
++ return 0;
++}
++
++static int ak4535_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++ ak4535_write(codec, AK4535_PM1, 0x80);
++ ak4535_write(codec, AK4535_PM2, 0x0);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, inactive */
++ ak4535_write(codec, AK4535_PM1, 0x0);
++ ak4535_write(codec, AK4535_PM2, 0x80);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++struct snd_soc_codec_dai ak4535_dai = {
++ .name = "AK4535",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = AK4535_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = AK4535_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .hw_params = ak4535_hw_params,
++ },
++ .dai_ops = {
++ .set_fmt = ak4535_set_dai_fmt,
++ .digital_mute = ak4535_mute,
++ .set_sysclk = ak4535_set_dai_sysclk,
++ },
++};
++EXPORT_SYMBOL_GPL(ak4535_dai);
++
++static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int ak4535_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(ak4535_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ ak4535_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the AK4535 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int ak4535_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "AK4535";
++ codec->owner = THIS_MODULE;
++ codec->read = ak4535_read_reg_cache;
++ codec->write = ak4535_write;
++ codec->dapm_event = ak4535_dapm_event;
++ codec->dai = &ak4535_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(ak4535_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, ak4535_reg,
++ sizeof(u16) * ARRAY_SIZE(ak4535_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ak4535_reg);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ printk(KERN_ERR "ak4535: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ ak4535_add_controls(codec);
++ ak4535_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "ak4535: failed to register card\n");
++ goto card_err;
++ }
++
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++
++ return ret;
++}
++
++static struct snd_soc_device *ak4535_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++#define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver ak4535_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = ak4535_socdev;
++ struct ak4535_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = ak4535_init(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "failed to initialise AK4535\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int ak4535_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int ak4535_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, ak4535_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver ak4535_i2c_driver = {
++ .driver = {
++ .name = "AK4535 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_AK4535,
++ .attach_adapter = ak4535_i2c_attach,
++ .detach_client = ak4535_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "AK4535",
++ .driver = &ak4535_i2c_driver,
++};
++#endif
++
++static int ak4535_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct ak4535_setup_data *setup;
++ struct snd_soc_codec* codec;
++ struct ak4535_priv *ak4535;
++ int ret = 0;
++
++ printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
++ if (ak4535 == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++
++ codec->private_data = ak4535;
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ ak4535_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&ak4535_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int ak4535_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec* codec = socdev->codec;
++
++ if (codec->control_data)
++ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&ak4535_i2c_driver);
++#endif
++ kfree(codec->private_data);
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_ak4535 = {
++ .probe = ak4535_probe,
++ .remove = ak4535_remove,
++ .suspend = ak4535_suspend,
++ .resume = ak4535_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
++
++MODULE_DESCRIPTION("Soc AK4535 driver");
++MODULE_AUTHOR("Richard Purdie");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/ak4535.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/ak4535.h 2007-07-16 15:07:33.848266172 +0200
+@@ -0,0 +1,46 @@
++/*
++ * ak4535.h -- AK4535 Soc Audio driver
++ *
++ * Copyright 2005 Openedhand Ltd.
++ *
++ * Author: Richard Purdie <richard at openedhand.com>
++ *
++ * Based on wm8753.h
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _AK4535_H
++#define _AK4535_H
++
++/* AK4535 register space */
++
++#define AK4535_PM1 0x0
++#define AK4535_PM2 0x1
++#define AK4535_SIG1 0x2
++#define AK4535_SIG2 0x3
++#define AK4535_MODE1 0x4
++#define AK4535_MODE2 0x5
++#define AK4535_DAC 0x6
++#define AK4535_MIC 0x7
++#define AK4535_TIMER 0x8
++#define AK4535_ALC1 0x9
++#define AK4535_ALC2 0xa
++#define AK4535_PGA 0xb
++#define AK4535_LATT 0xc
++#define AK4535_RATT 0xd
++#define AK4535_VOL 0xe
++#define AK4535_STATUS 0xf
++
++#define AK4535_CACHEREGNUM 0x10
++
++struct ak4535_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai ak4535_dai;
++extern struct snd_soc_codec_device soc_codec_dev_ak4535;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/uda1380.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/uda1380.c 2007-07-16 15:07:33.872267542 +0200
+@@ -0,0 +1,745 @@
++/*
++ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Modified by Richard Purdie <richard at openedhand.com> to fit into SoC
++ * codec model.
++ *
++ * Copyright (c) 2005 Giorgio Padrin <giorgio at mandarinlogiq.org>
++ * Copyright 2005 Openedhand Ltd.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/initval.h>
++#include <sound/info.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "uda1380.h"
++
++#define UDA1380_VERSION "0.5"
++#define AUDIO_NAME "uda1380"
++/*
++ * Debug
++ */
++
++#define UDA1380_DEBUG 0
++
++#ifdef UDA1380_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++
++/*
++ * uda1380 register cache
++ */
++static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
++ 0x0502, 0x0000, 0x0000, 0x3f3f,
++ 0x0202, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0xff00, 0x0000, 0x4800,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x8000, 0x0002, 0x0000,
++};
++
++/*
++ * read uda1380 register cache
++ */
++static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == UDA1380_RESET)
++ return 0;
++ if (reg >= UDA1380_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write uda1380 register cache
++ */
++static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= UDA1380_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the UDA1380 register space
++ */
++static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[3];
++
++ /* data is
++ * data[0] is register offset
++ * data[1] is MS byte
++ * data[2] is LS byte
++ */
++ data[0] = reg;
++ data[1] = (value & 0xff00) >> 8;
++ data[2] = value & 0x00ff;
++
++ uda1380_write_reg_cache (codec, reg, value);
++
++ /* the interpolator & decimator regs must only be written when the
++ * codec DAI is active.
++ */
++ if (!codec->active && (reg >= UDA1380_MVOL))
++ return 0;
++ dbg("uda hw write %x val %x\n", reg, value);
++ if (codec->hw_write(codec->control_data, data, 3) == 3) {
++ unsigned int val;
++ i2c_master_send(codec->control_data, data, 1);
++ i2c_master_recv(codec->control_data, data, 2);
++ val = (data[0]<<8) | data[1];
++ if (val != value) {
++ dbg("READ BACK VAL %x\n", (data[0]<<8) | data[1]);
++ return -EIO;
++ }
++ return 0;
++ } else
++ return -EIO;
++}
++
++#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
++
++/* declarations of ALSA reg_elem_REAL controls */
++static const char *uda1380_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz",
++ "96kHz"};
++static const char *uda1380_input_sel[] = {"Line", "Mic"};
++static const char *uda1380_output_sel[] = {"Direct", "Mixer"};
++static const char *uda1380_spf_mode[] = {"Flat", "Minimum1", "Minimum2",
++ "Maximum"};
++
++static const struct soc_enum uda1380_enum[] = {
++ SOC_ENUM_DOUBLE(UDA1380_DEEMP, 0, 8, 5, uda1380_deemp),
++ SOC_ENUM_SINGLE(UDA1380_ADC, 3, 2, uda1380_input_sel),
++ SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode),
++ SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel), /* R02_EN_AVC */
++};
++
++static const struct snd_kcontrol_new uda1380_snd_controls[] = {
++ SOC_DOUBLE("Playback Volume", UDA1380_MVOL, 0, 8, 255, 1),
++ SOC_DOUBLE("Mixer Volume", UDA1380_MIXVOL, 0, 8, 255, 1),
++ SOC_ENUM("Sound Processing Filter Mode", uda1380_enum[2]),
++ SOC_DOUBLE("Treble Volume", UDA1380_MODE, 4, 12, 3, 0),
++ SOC_DOUBLE("Bass Volume", UDA1380_MODE, 0, 8, 15, 0),
++ SOC_ENUM("Playback De-emphasis", uda1380_enum[0]),
++ SOC_DOUBLE("Capture Volume", UDA1380_DEC, 0, 8, 127, 0),
++ SOC_DOUBLE("Line Capture Volume", UDA1380_PGA, 0, 8, 15, 0),
++ SOC_SINGLE("Mic Capture Volume", UDA1380_PGA, 8, 11, 0),
++ SOC_DOUBLE("Playback Switch", UDA1380_DEEMP, 3, 11, 1, 1),
++ SOC_SINGLE("Capture Switch", UDA1380_PGA, 15, 1, 0),
++ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),
++ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),
++ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
++ SOC_SINGLE("Silence", UDA1380_MIXER, 7, 1, 0),
++ SOC_SINGLE("Silence Detection", UDA1380_MIXER, 6, 1, 0),
++};
++
++/* add non dapm controls */
++static int uda1380_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&uda1380_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Input mux */
++static const struct snd_kcontrol_new uda1380_input_mux_control =
++ SOC_DAPM_ENUM("Input Select", uda1380_enum[1]);
++
++/* Output mux */
++static const struct snd_kcontrol_new uda1380_output_mux_control =
++ SOC_DAPM_ENUM("Output Select", uda1380_enum[3]);
++
++static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
++ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
++ &uda1380_input_mux_control),
++ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
++ &uda1380_output_mux_control),
++ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
++ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
++ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
++ SND_SOC_DAPM_INPUT("VINM"),
++ SND_SOC_DAPM_INPUT("VINL"),
++ SND_SOC_DAPM_INPUT("VINR"),
++ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
++ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
++ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
++ SND_SOC_DAPM_OUTPUT("VOUTL"),
++ SND_SOC_DAPM_OUTPUT("VOUTR"),
++ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
++ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
++};
++
++static const char *audio_map[][3] = {
++
++ /* output mux */
++ {"HeadPhone Driver", NULL, "Output Mux"},
++ {"VOUTR", NULL, "Output Mux"},
++ {"VOUTL", NULL, "Output Mux"},
++
++ {"Analog Mixer", NULL, "VINR"},
++ {"Analog Mixer", NULL, "VINL"},
++ {"Analog Mixer", NULL, "DAC"},
++
++ {"Output Mux", "Direct", "DAC"},
++ {"Output Mux", "Mixer", "Analog Mixer"},
++
++ /* headphone driver */
++ {"VOUTLHP", NULL, "HeadPhone Driver"},
++ {"VOUTRHP", NULL, "HeadPhone Driver"},
++
++ /* input mux */
++ {"Left ADC", NULL, "Input Mux"},
++ {"Input Mux", "Mic", "Mic LNA"},
++ {"Input Mux", "Line", "Left PGA"},
++
++ /* right input */
++ {"Right ADC", NULL, "Right PGA"},
++
++ /* inputs */
++ {"Mic LNA", NULL, "VINM"},
++ {"Left PGA", NULL, "VINL"},
++ {"Right PGA", NULL, "VINR"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int uda1380_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++)
++ snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]);
++
++ /* set up audio path interconnects */
++ for (i = 0; audio_map[i][0] != NULL; i++)
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static int uda1380_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ int iface;
++ /* set up DAI based upon fmt */
++
++ iface = uda1380_read_reg_cache (codec, UDA1380_IFACE);
++ iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
++
++ /* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= R01_SFORI_I2S | R01_SFORO_I2S;
++ break;
++ case SND_SOC_DAIFMT_LSB:
++ iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
++ break;
++ case SND_SOC_DAIFMT_MSB:
++ iface |= R01_SFORI_MSB | R01_SFORO_I2S;
++ }
++
++ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
++ iface |= R01_SIM;
++
++ uda1380_write(codec, UDA1380_IFACE, iface);
++
++ return 0;
++}
++
++/*
++ * Flush reg cache
++ * We can only write the interpolator and decimator registers
++ * when the DAI is being clocked by the CPU DAI. It's up to the
++ * machine and cpu DAI driver to do this before we are called.
++ */
++static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, reg_start, reg_end, clk;
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ reg_start = UDA1380_MVOL;
++ reg_end = UDA1380_MIXER;
++ } else {
++ reg_start = UDA1380_DEC;
++ reg_end = UDA1380_AGC;
++ }
++
++ /* FIXME disable DAC_CLK */
++ clk = uda1380_read_reg_cache (codec, 00);
++ uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
++
++ for (reg = reg_start; reg <= reg_end; reg++ ) {
++ dbg("reg %x val %x\n",reg, uda1380_read_reg_cache (codec, reg));
++ uda1380_write(codec, reg, uda1380_read_reg_cache (codec, reg));
++ }
++
++ /* FIXME enable DAC_CLK */
++ uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
++
++ return 0;
++}
++
++static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
++
++ /* set WSPLL power and divider if running from this clock */
++ if (clk & R00_DAC_CLK) {
++ int rate = params_rate(params);
++ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
++ clk &= ~0x3; /* clear SEL_LOOP_DIV */
++ switch (rate) {
++ case 6250 ... 12500:
++ clk |= 0x0;
++ break;
++ case 12501 ... 25000:
++ clk |= 0x1;
++ break;
++ case 25001 ... 50000:
++ clk |= 0x2;
++ break;
++ case 50001 ... 100000:
++ clk |= 0x3;
++ break;
++ }
++ uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
++ }
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ clk |= R00_EN_DAC | R00_EN_INT;
++ else
++ clk |= R00_EN_ADC | R00_EN_DEC;
++
++ uda1380_write(codec, UDA1380_CLK, clk);
++ return 0;
++}
++
++static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
++
++ /* shut down WSPLL power if running from this clock */
++ if (clk & R00_DAC_CLK) {
++ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
++ uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
++ }
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ clk &= ~(R00_EN_DAC | R00_EN_INT);
++ else
++ clk &= ~(R00_EN_ADC | R00_EN_DEC);
++
++ uda1380_write(codec, UDA1380_CLK, clk);
++}
++
++static int uda1380_mute(struct snd_soc_codec_dai *codec_dai, int mute)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & 0xbfff;
++
++ /* FIXME: mute(codec,0) is called when the magician clock is already
++ * set to WSPLL, but for some unknown reason writing to interpolator
++ * registers works only when clocked by SYSCLK */
++ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
++ uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
++ if (mute)
++ uda1380_write(codec, UDA1380_DEEMP, mute_reg | 0x4000);
++ else
++ uda1380_write(codec, UDA1380_DEEMP, mute_reg);
++ uda1380_write(codec, UDA1380_CLK, clk);
++ return 0;
++}
++
++static int uda1380_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ /* enable internal bias */
++ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except internal bias */
++ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, inactive */
++ uda1380_write(codec, UDA1380_PM, 0x0);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
++
++struct snd_soc_codec_dai uda1380_dai[] = {
++{
++ .name = "UDA1380",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = UDA1380_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = UDA1380_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .hw_params = uda1380_pcm_hw_params,
++ .shutdown = uda1380_pcm_shutdown,
++ .prepare = uda1380_pcm_prepare,
++ },
++ .dai_ops = {
++ .digital_mute = uda1380_mute,
++ .set_fmt = uda1380_set_dai_fmt,
++ },
++},
++{/* playback only - dual interface */
++ .name = "UDA1380",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = UDA1380_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ },
++ .ops = {
++ .hw_params = uda1380_pcm_hw_params,
++ .shutdown = uda1380_pcm_shutdown,
++ .prepare = uda1380_pcm_prepare,
++ },
++ .dai_ops = {
++ .digital_mute = uda1380_mute,
++ .set_fmt = uda1380_set_dai_fmt,
++ },
++},
++{ /* capture only - dual interface*/
++ .name = "UDA1380",
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = UDA1380_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ },
++ .ops = {
++ .hw_params = uda1380_pcm_hw_params,
++ .shutdown = uda1380_pcm_shutdown,
++ .prepare = uda1380_pcm_prepare,
++ },
++ .dai_ops = {
++ .set_fmt = uda1380_set_dai_fmt,
++ },
++},
++};
++EXPORT_SYMBOL_GPL(uda1380_dai);
++
++static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int uda1380_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ uda1380_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the UDA1380 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "UDA1380";
++ codec->owner = THIS_MODULE;
++ codec->read = uda1380_read_reg_cache;
++ codec->write = uda1380_write;
++ codec->dapm_event = uda1380_dapm_event;
++ codec->dai = uda1380_dai;
++ codec->num_dai = ARRAY_SIZE(uda1380_dai);
++ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
++ codec->reg_cache =
++ kcalloc(ARRAY_SIZE(uda1380_reg), sizeof(u16), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, uda1380_reg,
++ sizeof(u16) * ARRAY_SIZE(uda1380_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(uda1380_reg);
++ uda1380_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "uda1380: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ /* set clock input */
++ switch (dac_clk) {
++ case UDA1380_DAC_CLK_SYSCLK:
++ uda1380_write(codec, UDA1380_CLK, 0);
++ break;
++ case UDA1380_DAC_CLK_WSPLL:
++ uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
++ break;
++ }
++
++ /* uda1380 init */
++ uda1380_add_controls(codec);
++ uda1380_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "uda1380: failed to register card\n");
++ goto card_err;
++ }
++
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *uda1380_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++#define I2C_DRIVERID_UDA1380 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver uda1380_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = uda1380_socdev;
++ struct uda1380_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = uda1380_init(socdev, setup->dac_clk);
++ if (ret < 0) {
++ printk(KERN_ERR "failed to initialise UDA1380\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int uda1380_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int uda1380_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, uda1380_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver uda1380_i2c_driver = {
++ .driver = {
++ .name = "UDA1380 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_UDA1380,
++ .attach_adapter = uda1380_i2c_attach,
++ .detach_client = uda1380_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "UDA1380",
++ .driver = &uda1380_i2c_driver,
++};
++#endif
++
++static int uda1380_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct uda1380_setup_data *setup;
++ struct snd_soc_codec* codec;
++ int ret = 0;
++
++ printk(KERN_INFO "UDA1380 Audio Codec %s", UDA1380_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ uda1380_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&uda1380_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int uda1380_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec* codec = socdev->codec;
++
++ if (codec->control_data)
++ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&uda1380_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_uda1380 = {
++ .probe = uda1380_probe,
++ .remove = uda1380_remove,
++ .suspend = uda1380_suspend,
++ .resume = uda1380_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
++
++MODULE_AUTHOR("Giorgio Padrin");
++MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/uda1380.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/uda1380.h 2007-07-16 15:07:33.892268682 +0200
+@@ -0,0 +1,89 @@
++/*
++ * Audio support for Philips UDA1380
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Copyright (c) 2005 Giorgio Padrin <giorgio at mandarinlogiq.org>
++ */
++
++#ifndef _UDA1380_H
++#define _UDA1380_H
++
++#define UDA1380_CLK 0x00
++#define UDA1380_IFACE 0x01
++#define UDA1380_PM 0x02
++#define UDA1380_AMIX 0x03
++#define UDA1380_HP 0x04
++#define UDA1380_MVOL 0x10
++#define UDA1380_MIXVOL 0x11
++#define UDA1380_MODE 0x12
++#define UDA1380_DEEMP 0x13
++#define UDA1380_MIXER 0x14
++#define UDA1380_INTSTAT 0x18
++#define UDA1380_DEC 0x20
++#define UDA1380_PGA 0x21
++#define UDA1380_ADC 0x22
++#define UDA1380_AGC 0x23
++#define UDA1380_DECSTAT 0x28
++#define UDA1380_RESET 0x7f
++
++#define UDA1380_CACHEREGNUM 0x24
++
++/* Register flags */
++#define R00_EN_ADC 0x0800
++#define R00_EN_DEC 0x0400
++#define R00_EN_DAC 0x0200
++#define R00_EN_INT 0x0100
++#define R00_DAC_CLK 0x0010
++#define R01_SFORI_I2S 0x0000
++#define R01_SFORI_LSB16 0x0100
++#define R01_SFORI_LSB18 0x0200
++#define R01_SFORI_LSB20 0x0300
++#define R01_SFORI_MSB 0x0500
++#define R01_SFORI_MASK 0x0700
++#define R01_SFORO_I2S 0x0000
++#define R01_SFORO_LSB16 0x0001
++#define R01_SFORO_LSB18 0x0002
++#define R01_SFORO_LSB20 0x0003
++#define R01_SFORO_LSB24 0x0004
++#define R01_SFORO_MSB 0x0005
++#define R01_SFORO_MASK 0x0007
++#define R01_SEL_SOURCE 0x0040
++#define R01_SIM 0x0010
++#define R02_PON_PLL 0x8000
++#define R02_PON_HP 0x2000
++#define R02_PON_DAC 0x0400
++#define R02_PON_BIAS 0x0100
++#define R02_EN_AVC 0x0080
++#define R02_PON_AVC 0x0040
++#define R02_PON_LNA 0x0010
++#define R02_PON_PGAL 0x0008
++#define R02_PON_ADCL 0x0004
++#define R02_PON_PGAR 0x0002
++#define R02_PON_ADCR 0x0001
++#define R13_MTM 0x4000
++#define R14_SILENCE 0x0080
++#define R14_SDET_ON 0x0040
++#define R21_MT_ADC 0x8000
++#define R22_SEL_LNA 0x0008
++#define R22_SEL_MIC 0x0004
++#define R22_SKIP_DCFIL 0x0002
++#define R23_AGC_EN 0x0001
++
++struct uda1380_setup_data {
++ unsigned short i2c_address;
++ int dac_clk;
++#define UDA1380_DAC_CLK_SYSCLK 0
++#define UDA1380_DAC_CLK_WSPLL 1
++};
++
++#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
++#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
++#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
++
++extern struct snd_soc_codec_dai uda1380_dai[3];
++extern struct snd_soc_codec_device soc_codec_dev_uda1380;
++
++#endif /* _UDA1380_H */
+Index: linux-2.6.22.1/sound/soc/codecs/wm8772.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8772.c 2007-07-16 15:07:33.996274607 +0200
+@@ -0,0 +1,603 @@
++/*
++ * wm8772.c -- WM8772 ALSA Soc Audio driver
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8772.h"
++
++#define AUDIO_NAME "WM8772"
++#define WM8772_VERSION "0.4"
++
++/* codec private data */
++struct wm8772_priv {
++ unsigned int adcclk;
++ unsigned int dacclk;
++};
++
++/*
++ * wm8772 register cache
++ * We can't read the WM8772 register space when we
++ * are using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8772_reg[] = {
++ 0x00ff, 0x00ff, 0x0120, 0x0000, /* 0 */
++ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /* 4 */
++ 0x00ff, 0x0000, 0x0080, 0x0040, /* 8 */
++ 0x0000
++};
++
++/*
++ * read wm8772 register cache
++ */
++static inline unsigned int wm8772_read_reg_cache(struct snd_soc_codec * codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg > WM8772_CACHE_REGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8772 register cache
++ */
++static inline void wm8772_write_reg_cache(struct snd_soc_codec * codec,
++ unsigned int reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg > WM8772_CACHE_REGNUM)
++ return;
++ cache[reg] = value;
++}
++
++static int wm8772_write(struct snd_soc_codec * codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8772 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8772_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -1;
++}
++
++#define wm8772_reset(c) wm8772_write(c, WM8772_RESET, 0)
++
++/*
++ * WM8772 Controls
++ */
++static const char *wm8772_zero_flag[] = {"All Ch", "Ch 1", "Ch 2", "Ch3"};
++
++static const struct soc_enum wm8772_enum[] = {
++SOC_ENUM_SINGLE(WM8772_DACCTRL, 0, 4, wm8772_zero_flag),
++};
++
++static const struct snd_kcontrol_new wm8772_snd_controls[] = {
++
++SOC_SINGLE("Left1 Playback Volume", WM8772_LDAC1VOL, 0, 255, 0),
++SOC_SINGLE("Left2 Playback Volume", WM8772_LDAC2VOL, 0, 255, 0),
++SOC_SINGLE("Left3 Playback Volume", WM8772_LDAC3VOL, 0, 255, 0),
++SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC1VOL, 0, 255, 0),
++SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC2VOL, 0, 255, 0),
++SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC3VOL, 0, 255, 0),
++SOC_SINGLE("Master Playback Volume", WM8772_MDACVOL, 0, 255, 0),
++
++SOC_SINGLE("Playback Switch", WM8772_DACCH, 0, 1, 0),
++SOC_SINGLE("Capture Switch", WM8772_ADCCTRL, 2, 1, 0),
++
++SOC_SINGLE("Demp1 Playback Switch", WM8772_DACCTRL, 6, 1, 0),
++SOC_SINGLE("Demp2 Playback Switch", WM8772_DACCTRL, 7, 1, 0),
++SOC_SINGLE("Demp3 Playback Switch", WM8772_DACCTRL, 8, 1, 0),
++
++SOC_SINGLE("Phase Invert 1 Switch", WM8772_IFACE, 6, 1, 0),
++SOC_SINGLE("Phase Invert 2 Switch", WM8772_IFACE, 7, 1, 0),
++SOC_SINGLE("Phase Invert 3 Switch", WM8772_IFACE, 8, 1, 0),
++
++SOC_SINGLE("Playback ZC Switch", WM8772_DACCTRL, 0, 1, 0),
++
++SOC_SINGLE("Capture High Pass Switch", WM8772_ADCCTRL, 3, 1, 0),
++};
++
++/* add non dapm controls */
++static int wm8772_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8772_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8772_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
++static int wm8772_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct wm8772_priv *wm8772 = codec->private_data;
++
++ switch (freq) {
++ case 4096000:
++ case 5644800:
++ case 6144000:
++ case 8192000:
++ case 8467000:
++ case 9216000:
++ case 11289600:
++ case 12000000:
++ case 12288000:
++ case 16934400:
++ case 18432000:
++ case 22579200:
++ case 24576000:
++ case 33868800:
++ case 36864000:
++ if (clk_id == WM8772_DACCLK) {
++ wm8772->dacclk = freq;
++ return 0;
++ } else if (clk_id == WM8772_ADCCLK) {
++ wm8772->adcclk = freq;
++ return 0;
++ }
++ }
++ return -EINVAL;
++}
++
++static int wm8772_set_dac_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 diface = wm8772_read_reg_cache(codec, WM8772_IFACE) & 0x1f0;
++ u16 diface_ctrl = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0x1ef;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ diface_ctrl |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ diface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ diface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ diface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ diface |= 0x0007;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ diface |= 0x0008;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8772_write(codec, WM8772_DACRATE, diface_ctrl);
++ wm8772_write(codec, WM8772_IFACE, diface);
++ return 0;
++}
++
++static int wm8772_set_adc_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 aiface = 0;
++ u16 aiface_ctrl = wm8772_read_reg_cache(codec, WM8772_ADCCTRL) & 0x1cf;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ aiface |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ aiface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ aiface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ aiface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ aiface |= 0x0003;
++ aiface_ctrl |= 0x0010;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ aiface_ctrl |= 0x0020;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8772_write(codec, WM8772_ADCCTRL, aiface_ctrl);
++ wm8772_write(codec, WM8772_ADCRATE, aiface);
++ return 0;
++}
++
++static int wm8772_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct wm8772_priv *wm8772 = codec->private_data;
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++
++ u16 diface = wm8772_read_reg_cache(codec, WM8772_IFACE) & 0x1cf;
++ u16 diface_ctrl = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0x3f;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ diface |= 0x0010;
++ break;
++ case SNDRV_PCM_FORMAT_S24_3LE:
++ diface |= 0x0020;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ diface |= 0x0030;
++ break;
++ }
++
++ /* set rate */
++ switch (wm8772->dacclk / params_rate(params)) {
++ case 768:
++ diface_ctrl |= (0x5 << 6);
++ break;
++ case 512:
++ diface_ctrl |= (0x4 << 6);
++ break;
++ case 384:
++ diface_ctrl |= (0x3 << 6);
++ break;
++ case 256:
++ diface_ctrl |= (0x2 << 6);
++ break;
++ case 192:
++ diface_ctrl |= (0x1 << 6);
++ break;
++ }
++
++ wm8772_write(codec, WM8772_DACRATE, diface_ctrl);
++ wm8772_write(codec, WM8772_IFACE, diface);
++
++ } else {
++
++ u16 aiface = wm8772_read_reg_cache(codec, WM8772_ADCRATE) & 0x113;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ aiface |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ aiface |= 0x0008;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ aiface |= 0x000c;
++ break;
++ }
++
++ /* set rate */
++ switch (wm8772->adcclk / params_rate(params)) {
++ case 768:
++ aiface |= (0x5 << 5);
++ break;
++ case 512:
++ aiface |= (0x4 << 5);
++ break;
++ case 384:
++ aiface |= (0x3 << 5);
++ break;
++ case 256:
++ aiface |= (0x2 << 5);
++ break;
++ }
++
++ wm8772_write(codec, WM8772_ADCRATE, aiface);
++ }
++
++ return 0;
++}
++
++static int wm8772_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ u16 master = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0xffe0;
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ wm8772_write(codec, WM8772_DACRATE, master);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++ wm8772_write(codec, WM8772_DACRATE, master | 0x0f);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8772_write(codec, WM8772_DACRATE, master | 0x1f);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++struct snd_soc_codec_dai wm8772_dai[] = {
++{
++ .name = "WM8772",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 2,
++ .channels_max = 6,
++ },
++ .ops = {
++ .hw_params = wm8772_hw_params,
++ },
++ .dai_ops = {
++ .set_fmt = wm8772_set_dac_dai_fmt,
++ .set_sysclk = wm8772_set_dai_sysclk,
++ },
++},
++{
++ .name = "WM8772",
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 2,
++ .channels_max = 2,
++ },
++ .ops = {
++ .hw_params = wm8772_hw_params,
++ },
++ .dai_ops = {
++ .set_fmt = wm8772_set_adc_dai_fmt,
++ .set_sysclk = wm8772_set_dai_sysclk,
++ },
++},
++};
++EXPORT_SYMBOL_GPL(wm8772_dai);
++
++static int wm8772_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8772_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8772_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8772_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8772 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8772_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "WM8772";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8772_read_reg_cache;
++ codec->write = wm8772_write;
++ codec->dapm_event = wm8772_dapm_event;
++ codec->dai = wm8772_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8772_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8772_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8772_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8772_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8772_reg);
++
++ wm8772_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "wm8772: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* set the update bits */
++ reg = wm8772_read_reg_cache(codec, WM8772_MDACVOL);
++ wm8772_write(codec, WM8772_MDACVOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_LDAC1VOL);
++ wm8772_write(codec, WM8772_LDAC1VOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_LDAC2VOL);
++ wm8772_write(codec, WM8772_LDAC2VOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_LDAC3VOL);
++ wm8772_write(codec, WM8772_LDAC3VOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_RDAC1VOL);
++ wm8772_write(codec, WM8772_RDAC1VOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_RDAC2VOL);
++ wm8772_write(codec, WM8772_RDAC2VOL, reg | 0x0100);
++ reg = wm8772_read_reg_cache(codec, WM8772_RDAC3VOL);
++ wm8772_write(codec, WM8772_RDAC3VOL, reg | 0x0100);
++
++ wm8772_add_controls(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8772: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8772_socdev;
++
++static int wm8772_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8772_setup_data *setup;
++ struct snd_soc_codec *codec;
++ struct wm8772_priv *wm8772;
++ int ret = 0;
++
++ printk(KERN_INFO "WM8772 Audio Codec %s", WM8772_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ wm8772 = kzalloc(sizeof(struct wm8772_priv), GFP_KERNEL);
++ if (wm8772 == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++
++ codec->private_data = wm8772;
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8772_socdev = socdev;
++
++ /* Add other interfaces here */
++#warning do SPI device probe here and then call wm8772_init()
++
++ return ret;
++}
++
++/* power down chip */
++static int wm8772_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ kfree(codec->private_data);
++ kfree(codec->reg_cache);
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8772 = {
++ .probe = wm8772_probe,
++ .remove = wm8772_remove,
++ .suspend = wm8772_suspend,
++ .resume = wm8772_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8772);
++
++MODULE_DESCRIPTION("ASoC WM8772 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8772.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8772.h 2007-07-16 15:07:34.016275747 +0200
+@@ -0,0 +1,46 @@
++/*
++ * wm8772.h -- audio driver for WM8772
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#ifndef _WM8772_H
++#define _WM8772_H
++
++/* WM8772 register space */
++
++#define WM8772_LDAC1VOL 0x00
++#define WM8772_RDAC1VOL 0x01
++#define WM8772_DACCH 0x02
++#define WM8772_IFACE 0x03
++#define WM8772_LDAC2VOL 0x04
++#define WM8772_RDAC2VOL 0x05
++#define WM8772_LDAC3VOL 0x06
++#define WM8772_RDAC3VOL 0x07
++#define WM8772_MDACVOL 0x08
++#define WM8772_DACCTRL 0x09
++#define WM8772_DACRATE 0x0a
++#define WM8772_ADCRATE 0x0b
++#define WM8772_ADCCTRL 0x0c
++#define WM8772_RESET 0x1f
++
++#define WM8772_CACHE_REGNUM 10
++
++#define WM8772_DACCLK 0
++#define WM8772_ADCCLK 1
++
++#define WM8753_DAI_DAC 0
++#define WM8753_DAI_ADC 1
++
++extern struct snd_soc_codec_dai wm8772_dai[2];
++extern struct snd_soc_codec_device soc_codec_dev_wm8772;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm8971.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8971.c 2007-07-16 15:07:34.028276432 +0200
+@@ -0,0 +1,971 @@
++/*
++ * wm8971.c -- WM8971 ALSA SoC Audio driver
++ *
++ * Copyright 2005 Lab126, Inc.
++ *
++ * Author: Kenneth Kiraly <kiraly at lab126.com>
++ *
++ * Based on wm8753.c by Liam Girdwood
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8971.h"
++
++#define AUDIO_NAME "wm8971"
++#define WM8971_VERSION "0.9"
++
++#undef WM8971_DEBUG
++
++#ifdef WM8971_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++#define WM8971_REG_COUNT 43
++
++static struct workqueue_struct *wm8971_workq = NULL;
++
++/* codec private data */
++struct wm8971_priv {
++ unsigned int sysclk;
++};
++
++/*
++ * wm8971 register cache
++ * We can't read the WM8971 register space when we
++ * are using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8971_reg[] = {
++ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
++ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
++ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
++ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
++ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
++ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
++ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
++ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
++ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
++ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
++ 0x0079, 0x0079, 0x0079, /* 40 */
++};
++
++static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg < WM8971_REG_COUNT)
++ return cache[reg];
++
++ return -1;
++}
++
++static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg < WM8971_REG_COUNT)
++ cache[reg] = value;
++}
++
++static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8753 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8971_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
++
++/* WM8971 Controls */
++static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
++static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
++ "200Hz @ 48kHz" };
++static const char *wm8971_treble[] = { "8kHz", "4kHz" };
++static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
++static const char *wm8971_ng_type[] = { "Constant PGA Gain",
++ "Mute ADC Output" };
++static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
++static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
++ "Mono (Right)", "Digital Mono"};
++static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
++static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
++ "Differential"};
++static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
++ "Differential"};
++static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
++static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
++static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
++ "L + R Invert"};
++
++static const struct soc_enum wm8971_enum[] = {
++ SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */
++ SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
++ SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
++ SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
++ SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */
++ SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
++ SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
++ SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
++ SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
++ SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
++ SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
++ SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
++ SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */
++ SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
++};
++
++static const struct snd_kcontrol_new wm8971_snd_controls[] = {
++ SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
++ SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, 6, 1, 0),
++ SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
++
++ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
++ WM8971_ROUT1V, 7, 1, 0),
++ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
++ WM8971_ROUT2V, 7, 1, 0),
++ SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
++
++ SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
++
++ SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
++ WM8971_LOUTM2, 4, 7, 1),
++ SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
++ WM8971_ROUTM2, 4, 7, 1),
++ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
++ WM8971_MOUTM2, 4, 7, 1),
++
++ SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
++ WM8971_ROUT1V, 0, 127, 0),
++ SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
++ WM8971_ROUT2V, 0, 127, 0),
++
++ SOC_ENUM("Bass Boost", wm8971_enum[0]),
++ SOC_ENUM("Bass Filter", wm8971_enum[1]),
++ SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
++
++ SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
++ SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
++
++ SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
++
++ SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
++ SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
++
++ SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
++ SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
++ SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
++ SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
++ SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
++ SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
++ SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
++ SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
++ SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
++ SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
++
++ SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
++ SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
++
++ SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
++ SOC_ENUM("Playback Function", wm8971_enum[6]),
++ SOC_ENUM("Playback Phase", wm8971_enum[7]),
++
++ SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
++};
++
++/* add non-DAPM controls */
++static int wm8971_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8971_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
++/*
++ * DAPM Controls
++ */
++
++/* Left Mixer */
++static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
++SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
++SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
++SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
++SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
++};
++
++/* Right Mixer */
++static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
++SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
++SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
++SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
++};
++
++/* Mono Mixer */
++static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
++SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
++SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
++SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
++};
++
++/* Left Line Mux */
++static const struct snd_kcontrol_new wm8971_left_line_controls =
++SOC_DAPM_ENUM("Route", wm8971_enum[8]);
++
++/* Right Line Mux */
++static const struct snd_kcontrol_new wm8971_right_line_controls =
++SOC_DAPM_ENUM("Route", wm8971_enum[9]);
++
++/* Left PGA Mux */
++static const struct snd_kcontrol_new wm8971_left_pga_controls =
++SOC_DAPM_ENUM("Route", wm8971_enum[10]);
++
++/* Right PGA Mux */
++static const struct snd_kcontrol_new wm8971_right_pga_controls =
++SOC_DAPM_ENUM("Route", wm8971_enum[11]);
++
++/* Mono ADC Mux */
++static const struct snd_kcontrol_new wm8971_monomux_controls =
++SOC_DAPM_ENUM("Route", wm8971_enum[13]);
++
++static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
++ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8971_left_mixer_controls[0],
++ ARRAY_SIZE(wm8971_left_mixer_controls)),
++ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8971_right_mixer_controls[0],
++ ARRAY_SIZE(wm8971_right_mixer_controls)),
++ SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
++ &wm8971_mono_mixer_controls[0],
++ ARRAY_SIZE(wm8971_mono_mixer_controls)),
++
++ SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
++ SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
++ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
++ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
++ SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
++
++ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
++ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
++ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
++
++ SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
++ &wm8971_left_pga_controls),
++ SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
++ &wm8971_right_pga_controls),
++ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
++ &wm8971_left_line_controls),
++ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
++ &wm8971_right_line_controls),
++
++ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
++ &wm8971_monomux_controls),
++ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
++ &wm8971_monomux_controls),
++
++ SND_SOC_DAPM_OUTPUT("LOUT1"),
++ SND_SOC_DAPM_OUTPUT("ROUT1"),
++ SND_SOC_DAPM_OUTPUT("LOUT2"),
++ SND_SOC_DAPM_OUTPUT("ROUT2"),
++ SND_SOC_DAPM_OUTPUT("MONO"),
++
++ SND_SOC_DAPM_INPUT("LINPUT1"),
++ SND_SOC_DAPM_INPUT("RINPUT1"),
++ SND_SOC_DAPM_INPUT("MIC"),
++};
++
++static const char *audio_map[][3] = {
++ /* left mixer */
++ {"Left Mixer", "Playback Switch", "Left DAC"},
++ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
++ {"Left Mixer", "Right Playback Switch", "Right DAC"},
++ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
++
++ /* right mixer */
++ {"Right Mixer", "Left Playback Switch", "Left DAC"},
++ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
++ {"Right Mixer", "Playback Switch", "Right DAC"},
++ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
++
++ /* left out 1 */
++ {"Left Out 1", NULL, "Left Mixer"},
++ {"LOUT1", NULL, "Left Out 1"},
++
++ /* left out 2 */
++ {"Left Out 2", NULL, "Left Mixer"},
++ {"LOUT2", NULL, "Left Out 2"},
++
++ /* right out 1 */
++ {"Right Out 1", NULL, "Right Mixer"},
++ {"ROUT1", NULL, "Right Out 1"},
++
++ /* right out 2 */
++ {"Right Out 2", NULL, "Right Mixer"},
++ {"ROUT2", NULL, "Right Out 2"},
++
++ /* mono mixer */
++ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
++ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
++ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
++ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
++
++ /* mono out */
++ {"Mono Out", NULL, "Mono Mixer"},
++ {"MONO1", NULL, "Mono Out"},
++
++ /* Left Line Mux */
++ {"Left Line Mux", "Line", "LINPUT1"},
++ {"Left Line Mux", "PGA", "Left PGA Mux"},
++ {"Left Line Mux", "Differential", "Differential Mux"},
++
++ /* Right Line Mux */
++ {"Right Line Mux", "Line", "RINPUT1"},
++ {"Right Line Mux", "Mic", "MIC"},
++ {"Right Line Mux", "PGA", "Right PGA Mux"},
++ {"Right Line Mux", "Differential", "Differential Mux"},
++
++ /* Left PGA Mux */
++ {"Left PGA Mux", "Line", "LINPUT1"},
++ {"Left PGA Mux", "Differential", "Differential Mux"},
++
++ /* Right PGA Mux */
++ {"Right PGA Mux", "Line", "RINPUT1"},
++ {"Right PGA Mux", "Differential", "Differential Mux"},
++
++ /* Differential Mux */
++ {"Differential Mux", "Line", "LINPUT1"},
++ {"Differential Mux", "Line", "RINPUT1"},
++
++ /* Left ADC Mux */
++ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
++ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
++ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
++
++ /* Right ADC Mux */
++ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
++ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
++ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
++
++ /* ADC */
++ {"Left ADC", NULL, "Left ADC Mux"},
++ {"Right ADC", NULL, "Right ADC Mux"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8971_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8971_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8971_dapm_widgets[i]);
++ }
++
++ /* set up audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct _coeff_div {
++ u32 mclk;
++ u32 rate;
++ u16 fs;
++ u8 sr:5;
++ u8 usb:1;
++};
++
++/* codec hifi mclk clock divider coefficients */
++static const struct _coeff_div coeff_div[] = {
++ /* 8k */
++ {12288000, 8000, 1536, 0x6, 0x0},
++ {11289600, 8000, 1408, 0x16, 0x0},
++ {18432000, 8000, 2304, 0x7, 0x0},
++ {16934400, 8000, 2112, 0x17, 0x0},
++ {12000000, 8000, 1500, 0x6, 0x1},
++
++ /* 11.025k */
++ {11289600, 11025, 1024, 0x18, 0x0},
++ {16934400, 11025, 1536, 0x19, 0x0},
++ {12000000, 11025, 1088, 0x19, 0x1},
++
++ /* 16k */
++ {12288000, 16000, 768, 0xa, 0x0},
++ {18432000, 16000, 1152, 0xb, 0x0},
++ {12000000, 16000, 750, 0xa, 0x1},
++
++ /* 22.05k */
++ {11289600, 22050, 512, 0x1a, 0x0},
++ {16934400, 22050, 768, 0x1b, 0x0},
++ {12000000, 22050, 544, 0x1b, 0x1},
++
++ /* 32k */
++ {12288000, 32000, 384, 0xc, 0x0},
++ {18432000, 32000, 576, 0xd, 0x0},
++ {12000000, 32000, 375, 0xa, 0x1},
++
++ /* 44.1k */
++ {11289600, 44100, 256, 0x10, 0x0},
++ {16934400, 44100, 384, 0x11, 0x0},
++ {12000000, 44100, 272, 0x11, 0x1},
++
++ /* 48k */
++ {12288000, 48000, 256, 0x0, 0x0},
++ {18432000, 48000, 384, 0x1, 0x0},
++ {12000000, 48000, 250, 0x0, 0x1},
++
++ /* 88.2k */
++ {11289600, 88200, 128, 0x1e, 0x0},
++ {16934400, 88200, 192, 0x1f, 0x0},
++ {12000000, 88200, 136, 0x1f, 0x1},
++
++ /* 96k */
++ {12288000, 96000, 128, 0xe, 0x0},
++ {18432000, 96000, 192, 0xf, 0x0},
++ {12000000, 96000, 125, 0xe, 0x1},
++};
++
++static int get_coeff(int mclk, int rate)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
++ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
++ return i;
++ }
++ return -EINVAL;
++}
++
++static int wm8971_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct wm8971_priv *wm8971 = codec->private_data;
++
++ switch (freq) {
++ case 11289600:
++ case 12000000:
++ case 12288000:
++ case 16934400:
++ case 18432000:
++ wm8971->sysclk = freq;
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static int wm8971_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ iface = 0x0040;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ iface |= 0x0013;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0090;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0080;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0010;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8971_write(codec, WM8971_IFACE, iface);
++ return 0;
++}
++
++static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct wm8971_priv *wm8971 = codec->private_data;
++ u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
++ u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
++ int coeff = get_coeff(wm8971->sysclk, params_rate(params));
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0008;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ iface |= 0x000c;
++ break;
++ }
++
++ /* set iface & srate */
++ wm8971_write(codec, WM8971_IFACE, iface);
++ if (coeff >= 0)
++ wm8971_write(codec, WM8971_SRATE, srate |
++ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
++
++ return 0;
++}
++
++static int wm8971_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
++
++ if (mute)
++ wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
++ else
++ wm8971_write(codec, WM8971_ADCDAC, mute_reg);
++ return 0;
++}
++
++static int wm8971_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* set vmid to 50k and unmute dac */
++ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ /* set vmid to 5k for quick power up */
++ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* mute dac and set vmid to 500k, enable VREF */
++ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ wm8971_write(codec, WM8971_PWR1, 0x0001);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8971_dai = {
++ .name = "WM8971",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8971_RATES,
++ .formats = WM8971_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8971_RATES,
++ .formats = WM8971_FORMATS,},
++ .ops = {
++ .hw_params = wm8971_pcm_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8971_mute,
++ .set_fmt = wm8971_set_dai_fmt,
++ .set_sysclk = wm8971_set_dai_sysclk,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8971_dai);
++
++static void wm8971_work(struct work_struct *work)
++{
++ struct snd_soc_codec *codec =
++ container_of(work, struct snd_soc_codec, delayed_work.work);
++ wm8971_dapm_event(codec, codec->dapm_state);
++}
++
++static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8971_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) {
++ if (i + 1 == WM8971_RESET)
++ continue;
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++
++ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* charge wm8971 caps */
++ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
++ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
++ codec->dapm_state = SNDRV_CTL_POWER_D0;
++ queue_delayed_work(wm8971_workq, &codec->delayed_work,
++ msecs_to_jiffies(1000));
++ }
++
++ return 0;
++}
++
++static int wm8971_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "WM8971";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8971_read_reg_cache;
++ codec->write = wm8971_write;
++ codec->dapm_event = wm8971_dapm_event;
++ codec->dai = &wm8971_dai;
++ codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
++ codec->num_dai = 1;
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8971_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8971_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8971_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8971_reg);
++
++ wm8971_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8971: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* charge output caps */
++ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
++ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
++ queue_delayed_work(wm8971_workq, &codec->delayed_work,
++ msecs_to_jiffies(1000));
++
++ /* set the update bits */
++ reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
++ wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
++ reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
++ wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
++
++ reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
++ wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
++ reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
++ wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
++
++ reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
++ wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
++ reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
++ wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
++
++ reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
++ wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
++ reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
++ wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
++
++ wm8971_add_controls(codec);
++ wm8971_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8971: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++static struct snd_soc_device *wm8971_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8731 2 wire address is determined by GPIO5
++ * state during powerup.
++ * low = 0x1a
++ * high = 0x1b
++ */
++#define I2C_DRIVERID_WM8971 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8971_i2c_driver;
++static struct i2c_client client_template;
++
++static int wm8971_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8971_socdev;
++ struct wm8971_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(i2c, codec);
++
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8971_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise WM8971\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int wm8971_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8971_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8971_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8971_i2c_driver = {
++ .driver = {
++ .name = "WM8971 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8971,
++ .attach_adapter = wm8971_i2c_attach,
++ .detach_client = wm8971_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8971",
++ .driver = &wm8971_i2c_driver,
++};
++#endif
++
++static int wm8971_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8971_setup_data *setup;
++ struct snd_soc_codec *codec;
++ struct wm8971_priv *wm8971;
++ int ret = 0;
++
++ info("WM8971 Audio Codec %s", WM8971_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
++ if (wm8971 == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++
++ codec->private_data = wm8971;
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++ wm8971_socdev = socdev;
++
++ INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
++ wm8971_workq = create_workqueue("wm8971");
++ if (wm8971_workq == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8971_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++
++ return ret;
++}
++
++/* power down chip */
++static int wm8971_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ if (wm8971_workq)
++ destroy_workqueue(wm8971_workq);
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8971_i2c_driver);
++#endif
++ kfree(codec->private_data);
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8971 = {
++ .probe = wm8971_probe,
++ .remove = wm8971_remove,
++ .suspend = wm8971_suspend,
++ .resume = wm8971_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
++
++MODULE_DESCRIPTION("ASoC WM8971 driver");
++MODULE_AUTHOR("Lab126");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8971.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8971.h 2007-07-16 15:07:34.076279166 +0200
+@@ -0,0 +1,63 @@
++/*
++ * wm8971.h -- audio driver for WM8971
++ *
++ * Copyright 2005 Lab126, Inc.
++ *
++ * Author: Kenneth Kiraly <kiraly at lab126.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#ifndef _WM8971_H
++#define _WM8971_H
++
++#define WM8971_LINVOL 0x00
++#define WM8971_RINVOL 0x01
++#define WM8971_LOUT1V 0x02
++#define WM8971_ROUT1V 0x03
++#define WM8971_ADCDAC 0x05
++#define WM8971_IFACE 0x07
++#define WM8971_SRATE 0x08
++#define WM8971_LDAC 0x0a
++#define WM8971_RDAC 0x0b
++#define WM8971_BASS 0x0c
++#define WM8971_TREBLE 0x0d
++#define WM8971_RESET 0x0f
++#define WM8971_ALC1 0x11
++#define WM8971_ALC2 0x12
++#define WM8971_ALC3 0x13
++#define WM8971_NGATE 0x14
++#define WM8971_LADC 0x15
++#define WM8971_RADC 0x16
++#define WM8971_ADCTL1 0x17
++#define WM8971_ADCTL2 0x18
++#define WM8971_PWR1 0x19
++#define WM8971_PWR2 0x1a
++#define WM8971_ADCTL3 0x1b
++#define WM8971_ADCIN 0x1f
++#define WM8971_LADCIN 0x20
++#define WM8971_RADCIN 0x21
++#define WM8971_LOUTM1 0x22
++#define WM8971_LOUTM2 0x23
++#define WM8971_ROUTM1 0x24
++#define WM8971_ROUTM2 0x25
++#define WM8971_MOUTM1 0x26
++#define WM8971_MOUTM2 0x27
++#define WM8971_LOUT2V 0x28
++#define WM8971_ROUT2V 0x29
++#define WM8971_MOUTV 0x2A
++
++#define WM8971_SYSCLK 0
++
++struct wm8971_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8971_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8971;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm8974.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8974.c 2007-07-16 15:07:34.104280762 +0200
+@@ -0,0 +1,873 @@
++/*
++ * wm8974.c -- WM8974 ALSA Soc Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ *
++ * Author: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8974.h"
++
++#define AUDIO_NAME "wm8974"
++#define WM8974_VERSION "0.6"
++
++/*
++ * Debug
++ */
++
++#define WM8974_DEBUG 0
++
++#ifdef WM8974_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8974;
++
++/*
++ * wm8974 register cache
++ * We can't read the WM8974 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0050, 0x0000, 0x0140, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x00ff,
++ 0x0000, 0x0000, 0x0100, 0x00ff,
++ 0x0000, 0x0000, 0x012c, 0x002c,
++ 0x002c, 0x002c, 0x002c, 0x0000,
++ 0x0032, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0038, 0x000b, 0x0032, 0x0000,
++ 0x0008, 0x000c, 0x0093, 0x00e9,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0003, 0x0010, 0x0000, 0x0000,
++ 0x0000, 0x0002, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0039, 0x0000,
++ 0x0000,
++};
++
++/*
++ * read wm8974 register cache
++ */
++static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec * codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8974_RESET)
++ return 0;
++ if (reg >= WM8974_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8974 register cache
++ */
++static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8974_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8974 register space
++ */
++static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8974 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8974_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
++
++static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
++static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
++static const char *wm8974_eqmode[] = {"Capture", "Playback" };
++static const char *wm8974_bw[] = {"Narrow", "Wide" };
++static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
++static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
++static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
++static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
++static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
++static const char *wm8974_alc[] = {"ALC", "Limiter" };
++
++static const struct soc_enum wm8974_enum[] = {
++ SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
++ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
++ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
++ SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
++
++ SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
++ SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
++ SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
++ SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
++
++ SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
++ SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
++ SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
++ SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
++
++ SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
++ SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
++};
++
++static const struct snd_kcontrol_new wm8974_snd_controls[] = {
++
++SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
++
++SOC_ENUM("DAC Companding", wm8974_enum[1]),
++SOC_ENUM("ADC Companding", wm8974_enum[0]),
++
++SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
++SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
++
++SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
++
++SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
++SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
++
++SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
++
++SOC_ENUM("Equaliser Function", wm8974_enum[3]),
++SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
++SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
++SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
++SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
++SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
++SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
++SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
++SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
++SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
++SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
++
++SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
++SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
++SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
++
++SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
++SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
++
++SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
++SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
++SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
++
++SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
++SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
++SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
++
++SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
++SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
++SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
++
++SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
++SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
++
++SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
++SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
++
++SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
++SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
++SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
++
++SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
++SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
++};
++
++/* add non dapm controls */
++static int wm8974_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8974_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Speaker Output Mixer */
++static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
++};
++
++/* Mono Output Mixer */
++static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
++};
++
++/* AUX Input boost vol */
++static const struct snd_kcontrol_new wm8974_aux_boost_controls =
++SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
++
++/* Mic Input boost vol */
++static const struct snd_kcontrol_new wm8974_mic_boost_controls =
++SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
++
++/* Capture boost switch */
++static const struct snd_kcontrol_new wm8974_capture_boost_controls =
++SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
++
++/* Aux In to PGA */
++static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
++SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
++
++/* Mic P In to PGA */
++static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
++SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
++
++/* Mic N In to PGA */
++static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
++SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
++
++static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
++SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
++ &wm8974_speaker_mixer_controls[0],
++ ARRAY_SIZE(wm8974_speaker_mixer_controls)),
++SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
++ &wm8974_mono_mixer_controls[0],
++ ARRAY_SIZE(wm8974_mono_mixer_controls)),
++SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
++SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
++SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
++
++SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
++ &wm8974_aux_boost_controls, 1),
++SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
++ &wm8974_mic_boost_controls, 1),
++SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
++ &wm8974_capture_boost_controls),
++
++SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
++
++SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
++
++SND_SOC_DAPM_INPUT("MICN"),
++SND_SOC_DAPM_INPUT("MICP"),
++SND_SOC_DAPM_INPUT("AUX"),
++SND_SOC_DAPM_OUTPUT("MONOOUT"),
++SND_SOC_DAPM_OUTPUT("SPKOUTP"),
++SND_SOC_DAPM_OUTPUT("SPKOUTN"),
++};
++
++static const char *audio_map[][3] = {
++ /* Mono output mixer */
++ {"Mono Mixer", "PCM Playback Switch", "DAC"},
++ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Speaker output mixer */
++ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
++ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Outputs */
++ {"Mono Out", NULL, "Mono Mixer"},
++ {"MONOOUT", NULL, "Mono Out"},
++ {"SpkN Out", NULL, "Speaker Mixer"},
++ {"SpkP Out", NULL, "Speaker Mixer"},
++ {"SPKOUTN", NULL, "SpkN Out"},
++ {"SPKOUTP", NULL, "SpkP Out"},
++
++ /* Boost Mixer */
++ {"Boost Mixer", NULL, "ADC"},
++ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
++ {"Aux Boost", "Aux Volume", "Boost Mixer"},
++ {"Capture Boost", "Capture Switch", "Boost Mixer"},
++ {"Mic Boost", "Mic Volume", "Boost Mixer"},
++
++ /* Inputs */
++ {"MICP", NULL, "Mic Boost"},
++ {"MICN", NULL, "Mic PGA"},
++ {"Mic PGA", NULL, "Capture Boost"},
++ {"AUX", NULL, "Aux Input"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8974_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8974_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8974_dapm_widgets[i]);
++ }
++
++ /* set up audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct pll_ {
++ unsigned int in_hz, out_hz;
++ unsigned int pre:4; /* prescale - 1 */
++ unsigned int n:4;
++ unsigned int k;
++};
++
++struct pll_ pll[] = {
++ {12000000, 11289600, 0, 7, 0x86c220},
++ {12000000, 12288000, 0, 8, 0x3126e8},
++ {13000000, 11289600, 0, 6, 0xf28bd4},
++ {13000000, 12288000, 0, 7, 0x8fd525},
++ {12288000, 11289600, 0, 7, 0x59999a},
++ {11289600, 12288000, 0, 8, 0x80dee9},
++ /* liam - add more entries */
++};
++
++static int wm8974_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ int i;
++ u16 reg;
++
++ if(freq_in == 0 || freq_out == 0) {
++ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
++ wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
++ return 0;
++ }
++
++ for(i = 0; i < ARRAY_SIZE(pll); i++) {
++ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
++ wm8974_write(codec, WM8974_PLLN, (pll[i].pre << 4) | pll[i].n);
++ wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
++ wm8974_write(codec, WM8974_PLLK1, (pll[i].k >> 9) && 0x1ff);
++ wm8974_write(codec, WM8974_PLLK1, pll[i].k && 0x1ff);
++ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
++ wm8974_write(codec, WM8974_POWER1, reg | 0x020);
++ return 0;
++ }
++ }
++ return -EINVAL;
++}
++
++/*
++ * Configure WM8974 clock dividers.
++ */
++static int wm8974_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8974_OPCLKDIV:
++ reg = wm8974_read_reg_cache(codec, WM8974_GPIO & 0x1cf);
++ wm8974_write(codec, WM8974_GPIO, reg | div);
++ break;
++ case WM8974_MCLKDIV:
++ reg = wm8974_read_reg_cache(codec, WM8974_CLOCK & 0x1f);
++ wm8974_write(codec, WM8974_CLOCK, reg | div);
++ break;
++ case WM8974_ADCCLK:
++ reg = wm8974_read_reg_cache(codec, WM8974_ADC & 0x1f7);
++ wm8974_write(codec, WM8974_ADC, reg | div);
++ break;
++ case WM8974_DACCLK:
++ reg = wm8974_read_reg_cache(codec, WM8974_DAC & 0x1f7);
++ wm8974_write(codec, WM8974_DAC, reg | div);
++ break;
++ case WM8974_BCLKDIV:
++ reg = wm8974_read_reg_cache(codec, WM8974_CLOCK & 0x1e3);
++ wm8974_write(codec, WM8974_CLOCK, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int wm8974_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++ u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ clk |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0008;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x00018;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0180;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0100;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0080;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8974_write(codec, WM8974_IFACE, iface);
++ wm8974_write(codec, WM8974_CLOCK, clk);
++ return 0;
++}
++
++static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x19f;
++ u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0020;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0040;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ iface |= 0x0060;
++ break;
++ }
++
++ /* filter coefficient */
++ switch (params_rate(params)) {
++ case SNDRV_PCM_RATE_8000:
++ adn |= 0x5 << 1;
++ break;
++ case SNDRV_PCM_RATE_11025:
++ adn |= 0x4 << 1;
++ break;
++ case SNDRV_PCM_RATE_16000:
++ adn |= 0x3 << 1;
++ break;
++ case SNDRV_PCM_RATE_22050:
++ adn |= 0x2 << 1;
++ break;
++ case SNDRV_PCM_RATE_32000:
++ adn |= 0x1 << 1;
++ break;
++ case SNDRV_PCM_RATE_44100:
++ break;
++ }
++
++ wm8974_write(codec, WM8974_IFACE, iface);
++ wm8974_write(codec, WM8974_ADD, adn);
++ return 0;
++}
++
++static int wm8974_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
++
++ if(mute)
++ wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
++ else
++ wm8974_write(codec, WM8974_DAC, mute_reg);
++ return 0;
++}
++
++/* liam need to make this lower power with dapm */
++static int wm8974_dapm_event(struct snd_soc_codec *codec, int event)
++{
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ wm8974_write(codec, WM8974_POWER1, 0x1ff);
++ wm8974_write(codec, WM8974_POWER2, 0x1ff);
++ wm8974_write(codec, WM8974_POWER3, 0x1ff);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8974_write(codec, WM8974_POWER1, 0x0);
++ wm8974_write(codec, WM8974_POWER2, 0x0);
++ wm8974_write(codec, WM8974_POWER3, 0x0);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8974_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8974_dai = {
++ .name = "WM8974 HiFi",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM8974_RATES,
++ .formats = WM8974_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM8974_RATES,
++ .formats = WM8974_FORMATS,},
++ .ops = {
++ .hw_params = wm8974_pcm_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8974_mute,
++ .set_fmt = wm8974_set_dai_fmt,
++ .set_clkdiv = wm8974_set_dai_clkdiv,
++ .set_pll = wm8974_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8974_dai);
++
++static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8974_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8974_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8974 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8974_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "WM8974";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8974_read_reg_cache;
++ codec->write = wm8974_write;
++ codec->dapm_event = wm8974_dapm_event;
++ codec->dai = &wm8974_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8974_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8974_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8974_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8974_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8974_reg);
++
++ wm8974_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "wm8974: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8974_add_controls(codec);
++ wm8974_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8974: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8974_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8974 2 wire address is 0x1a
++ */
++#define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8974_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8974_socdev;
++ struct wm8974_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if(ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8974_init(socdev);
++ if(ret < 0) {
++ err("failed to initialise WM8974\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int wm8974_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec *codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8974_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8974_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8974_i2c_driver = {
++ .driver = {
++ .name = "WM8974 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8974,
++ .attach_adapter = wm8974_i2c_attach,
++ .detach_client = wm8974_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8974",
++ .driver = &wm8974_i2c_driver,
++};
++#endif
++
++static int wm8974_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8974_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8974 Audio Codec %s", WM8974_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8974_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8974_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8974_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8974_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8974 = {
++ .probe = wm8974_probe,
++ .remove = wm8974_remove,
++ .suspend = wm8974_suspend,
++ .resume = wm8974_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
++
++MODULE_DESCRIPTION("ASoC WM8974 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8974.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8974.h 2007-07-16 15:07:34.124281902 +0200
+@@ -0,0 +1,104 @@
++/*
++ * wm8974.h -- WM8974 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8974_H
++#define _WM8974_H
++
++/* WM8974 register space */
++
++#define WM8974_RESET 0x0
++#define WM8974_POWER1 0x1
++#define WM8974_POWER2 0x2
++#define WM8974_POWER3 0x3
++#define WM8974_IFACE 0x4
++#define WM8974_COMP 0x5
++#define WM8974_CLOCK 0x6
++#define WM8974_ADD 0x7
++#define WM8974_GPIO 0x8
++#define WM8974_DAC 0xa
++#define WM8974_DACVOL 0xb
++#define WM8974_ADC 0xe
++#define WM8974_ADCVOL 0xf
++#define WM8974_EQ1 0x12
++#define WM8974_EQ2 0x13
++#define WM8974_EQ3 0x14
++#define WM8974_EQ4 0x15
++#define WM8974_EQ5 0x16
++#define WM8974_DACLIM1 0x18
++#define WM8974_DACLIM2 0x19
++#define WM8974_NOTCH1 0x1b
++#define WM8974_NOTCH2 0x1c
++#define WM8974_NOTCH3 0x1d
++#define WM8974_NOTCH4 0x1e
++#define WM8974_ALC1 0x20
++#define WM8974_ALC2 0x21
++#define WM8974_ALC3 0x22
++#define WM8974_NGATE 0x23
++#define WM8974_PLLN 0x24
++#define WM8974_PLLK1 0x25
++#define WM8974_PLLK2 0x26
++#define WM8974_PLLK3 0x27
++#define WM8974_ATTEN 0x28
++#define WM8974_INPUT 0x2c
++#define WM8974_INPPGA 0x2d
++#define WM8974_ADCBOOST 0x2f
++#define WM8974_OUTPUT 0x31
++#define WM8974_SPKMIX 0x32
++#define WM8974_SPKVOL 0x36
++#define WM8974_MONOMIX 0x38
++
++#define WM8974_CACHEREGNUM 57
++
++/* Clock divider Id's */
++#define WM8974_OPCLKDIV 0
++#define WM8974_MCLKDIV 1
++#define WM8974_ADCCLK 2
++#define WM8974_DACCLK 3
++#define WM8974_BCLKDIV 4
++
++/* DAC clock dividers */
++#define WM8974_DACCLK_F2 (1 << 3)
++#define WM8974_DACCLK_F4 (0 << 3)
++
++/* ADC clock dividers */
++#define WM8974_ADCCLK_F2 (1 << 3)
++#define WM8974_ADCCLK_F4 (0 << 3)
++
++/* PLL Out dividers */
++#define WM8974_OPCLKDIV_1 (0 << 4)
++#define WM8974_OPCLKDIV_2 (1 << 4)
++#define WM8974_OPCLKDIV_3 (2 << 4)
++#define WM8974_OPCLKDIV_4 (3 << 4)
++
++/* BCLK clock dividers */
++#define WM8974_BCLKDIV_1 (0 << 2)
++#define WM8974_BCLKDIV_2 (1 << 2)
++#define WM8974_BCLKDIV_4 (2 << 2)
++#define WM8974_BCLKDIV_8 (3 << 2)
++#define WM8974_BCLKDIV_16 (4 << 2)
++#define WM8974_BCLKDIV_32 (5 << 2)
++
++/* MCLK clock dividers */
++#define WM8974_MCLKDIV_1 (0 << 5)
++#define WM8974_MCLKDIV_1_5 (1 << 5)
++#define WM8974_MCLKDIV_2 (2 << 5)
++#define WM8974_MCLKDIV_3 (3 << 5)
++#define WM8974_MCLKDIV_4 (4 << 5)
++#define WM8974_MCLKDIV_6 (5 << 5)
++#define WM8974_MCLKDIV_8 (6 << 5)
++#define WM8974_MCLKDIV_12 (7 << 5)
++
++
++struct wm8974_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8974_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8974;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm9713.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm9713.c 2007-07-16 15:07:34.148283271 +0200
+@@ -0,0 +1,1220 @@
++/*
++ * wm9713.c -- ALSA Soc WM9713 codec support
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 4th Feb 2006 Initial version.
++ *
++ * Features:-
++ *
++ * o Support for AC97 Codec, Voice DAC and Aux DAC
++ * o Support for DAPM
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "wm9713.h"
++
++#define WM9713_VERSION "0.12"
++
++struct wm9713_priv {
++ u32 pll_in; /* PLL input frequency */
++ u32 pll_out; /* PLL output frequency */
++};
++
++static unsigned int ac97_read(struct snd_soc_codec *codec,
++ unsigned int reg);
++static int ac97_write(struct snd_soc_codec *codec,
++ unsigned int reg, unsigned int val);
++
++/*
++ * WM9713 register cache
++ * Reg 0x3c bit 15 is used by touch driver.
++ */
++static const u16 wm9713_reg[] = {
++ 0x6174, 0x8080, 0x8080, 0x8080, // 6
++ 0xc880, 0xe808, 0xe808, 0x0808, // e
++ 0x00da, 0x8000, 0xd600, 0xaaa0, // 16
++ 0xaaa0, 0xaaa0, 0x0000, 0x0000, // 1e
++ 0x0f0f, 0x0040, 0x0000, 0x7f00, // 26
++ 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
++ 0x0000, 0xbb80, 0x0000, 0x4523, // 36
++ 0x0000, 0x2000, 0x7eff, 0xffff, // 3e
++ 0x0000, 0x0000, 0x0080, 0x0000, // 46
++ 0x0000, 0x0000, 0xfffe, 0xffff, // 4e
++ 0x0000, 0x0000, 0x0000, 0xfffe, // 56
++ 0x4000, 0x0000, 0x0000, 0x0000, // 5e
++ 0xb032, 0x3e00, 0x0000, 0x0000, // 66
++ 0x0000, 0x0000, 0x0000, 0x0000, // 6e
++ 0x0000, 0x0000, 0x0000, 0x0006, // 76
++ 0x0001, 0x0000, 0x574d, 0x4c13, // 7e
++ 0x0000, 0x0000, 0x0000 // virtual hp & mic mixers
++};
++
++/* virtual HP mixers regs */
++#define HPL_MIXER 0x80
++#define HPR_MIXER 0x82
++#define MICB_MUX 0x82
++
++static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
++static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
++static const char *wm9713_rec_src[] =
++ {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
++ "Mono Out", "Zh"};
++static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
++static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
++static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
++ "Mono Vmid", "Inv Vmid"};
++static const char *wm9713_spk_pga[] =
++ {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
++ "Speaker Vmid", "Inv Vmid"};
++static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
++ "Headphone Vmid"};
++static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
++static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
++static const char *wm9713_dac_inv[] =
++ {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
++ "Headphone Mono", "NC", "Vmid"};
++static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
++static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
++static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
++static const char *wm9713_micb_select[] = {"MPB", "MPA"};
++
++static const struct soc_enum wm9713_enum[] = {
++SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
++SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
++SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */
++SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */
++SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/
++SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
++SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
++SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
++SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
++SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
++SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
++SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
++};
++
++static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
++SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
++SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
++SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
++SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE,15, 7, 1, 1),
++SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
++SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
++SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
++SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
++
++SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
++SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
++
++SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
++SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
++SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
++SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
++
++SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
++SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
++SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
++
++SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
++SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
++SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
++SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
++SOC_ENUM("ALC Function", wm9713_enum[6]),
++SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
++SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
++SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
++SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
++SOC_ENUM("ALC NG Type", wm9713_enum[17]),
++SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
++
++SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
++SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
++
++SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
++SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
++SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
++
++SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
++SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
++SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
++
++SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
++SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
++SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
++SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
++
++SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
++SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
++SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
++
++SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
++SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
++SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
++
++SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
++SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
++SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
++
++SOC_ENUM("Bass Control", wm9713_enum[16]),
++SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
++SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
++SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
++SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
++SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
++
++SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
++SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
++SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
++};
++
++/* add non dapm controls */
++static int wm9713_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm9713_snd_ac97_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
++/* We have to create a fake left and right HP mixers because
++ * the codec only has a single control that is shared by both channels.
++ * This makes it impossible to determine the audio path using the current
++ * register map, thus we add a new (virtual) register to help determine the
++ * audio route within the device.
++ */
++static int mixer_event (struct snd_soc_dapm_widget *w, int event)
++{
++ u16 l, r, beep, tone, phone, rec, pcm, aux;
++
++ l = ac97_read(w->codec, HPL_MIXER);
++ r = ac97_read(w->codec, HPR_MIXER);
++ beep = ac97_read(w->codec, AC97_PC_BEEP);
++ tone = ac97_read(w->codec, AC97_MASTER_TONE);
++ phone = ac97_read(w->codec, AC97_PHONE);
++ rec = ac97_read(w->codec, AC97_REC_SEL);
++ pcm = ac97_read(w->codec, AC97_PCM);
++ aux = ac97_read(w->codec, AC97_AUX);
++
++ if (event & SND_SOC_DAPM_PRE_REG)
++ return 0;
++ if (l & 0x1 || r & 0x1)
++ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
++
++ if (l & 0x2 || r & 0x2)
++ ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
++
++ if (l & 0x4 || r & 0x4)
++ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
++
++ if (l & 0x8 || r & 0x8)
++ ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
++
++ if (l & 0x10 || r & 0x10)
++ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
++
++ if (l & 0x20 || r & 0x20)
++ ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
++ else
++ ac97_write(w->codec, AC97_AUX, aux | 0x8000);
++
++ return 0;
++}
++
++/* Left Headphone Mixers */
++static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
++SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
++SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
++SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
++SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
++};
++
++/* Right Headphone Mixers */
++static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
++SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
++SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
++SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
++SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
++};
++
++/* headphone capture mux */
++static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[1]);
++
++/* headphone mic mux */
++static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[0]);
++
++/* Speaker Mixer */
++static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
++SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
++SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
++SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
++SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
++SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
++SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
++};
++
++/* Mono Mixer */
++static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
++SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
++SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
++SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
++SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
++SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
++SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
++SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
++SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
++};
++
++/* mono mic mux */
++static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[2]);
++
++/* mono output mux */
++static const struct snd_kcontrol_new wm9713_mono_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[7]);
++
++/* speaker left output mux */
++static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[8]);
++
++/* speaker right output mux */
++static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[9]);
++
++/* headphone left output mux */
++static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[10]);
++
++/* headphone right output mux */
++static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[11]);
++
++/* Out3 mux */
++static const struct snd_kcontrol_new wm9713_out3_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[12]);
++
++/* Out4 mux */
++static const struct snd_kcontrol_new wm9713_out4_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[13]);
++
++/* DAC inv mux 1 */
++static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[14]);
++
++/* DAC inv mux 2 */
++static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[15]);
++
++/* Capture source left */
++static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[3]);
++
++/* Capture source right */
++static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[4]);
++
++/* mic source */
++static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[18]);
++
++/* mic source B virtual control */
++static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
++SOC_DAPM_ENUM("Route", wm9713_enum[19]);
++
++static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
++SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hp_rec_mux_controls),
++SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hp_mic_mux_controls),
++SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_mono_mic_mux_controls),
++SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_mono_mux_controls),
++SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hp_spkl_mux_controls),
++SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hp_spkr_mux_controls),
++SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hpl_out_mux_controls),
++SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_hpr_out_mux_controls),
++SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_out3_mux_controls),
++SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
++ &wm9713_out4_mux_controls),
++SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
++ &wm9713_dac_inv1_mux_controls),
++SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
++ &wm9713_dac_inv2_mux_controls),
++SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
++ &wm9713_rec_srcl_mux_controls),
++SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
++ &wm9713_rec_srcr_mux_controls),
++SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
++ &wm9713_mic_sel_mux_controls ),
++SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
++ &wm9713_micb_sel_mux_controls ),
++SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
++ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
++ mixer_event, SND_SOC_DAPM_POST_REG),
++SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
++ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
++ mixer_event, SND_SOC_DAPM_POST_REG),
++SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
++ &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
++SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
++ &wm9713_speaker_mixer_controls[0],
++ ARRAY_SIZE(wm9713_speaker_mixer_controls)),
++SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
++SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
++SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
++SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
++SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
++SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
++SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
++SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
++SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
++SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
++SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
++SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
++SND_SOC_DAPM_OUTPUT("MONO"),
++SND_SOC_DAPM_OUTPUT("HPL"),
++SND_SOC_DAPM_OUTPUT("HPR"),
++SND_SOC_DAPM_OUTPUT("SPKL"),
++SND_SOC_DAPM_OUTPUT("SPKR"),
++SND_SOC_DAPM_OUTPUT("OUT3"),
++SND_SOC_DAPM_OUTPUT("OUT4"),
++SND_SOC_DAPM_INPUT("LINEL"),
++SND_SOC_DAPM_INPUT("LINER"),
++SND_SOC_DAPM_INPUT("MONOIN"),
++SND_SOC_DAPM_INPUT("PCBEEP"),
++SND_SOC_DAPM_INPUT("MIC1"),
++SND_SOC_DAPM_INPUT("MIC2A"),
++SND_SOC_DAPM_INPUT("MIC2B"),
++SND_SOC_DAPM_VMID("VMID"),
++};
++
++static const char *audio_map[][3] = {
++ /* left HP mixer */
++ {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
++ {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
++ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
++ {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
++ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
++ {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"},
++ {"Left HP Mixer", NULL, "Capture Headphone Mux"},
++
++ /* right HP mixer */
++ {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
++ {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
++ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
++ {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
++ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
++ {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"},
++ {"Right HP Mixer", NULL, "Capture Headphone Mux"},
++
++ /* virtual mixer - mixes left & right channels for spk and mono */
++ {"AC97 Mixer", NULL, "Left DAC"},
++ {"AC97 Mixer", NULL, "Right DAC"},
++ {"Line Mixer", NULL, "Right Line In"},
++ {"Line Mixer", NULL, "Left Line In"},
++ {"HP Mixer", NULL, "Left HP Mixer"},
++ {"HP Mixer", NULL, "Right HP Mixer"},
++ {"Capture Mixer", NULL, "Left Capture Source"},
++ {"Capture Mixer", NULL, "Right Capture Source"},
++
++ /* speaker mixer */
++ {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
++ {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
++ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
++ {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
++ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
++ {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
++
++ /* mono mixer */
++ {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
++ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
++ {"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
++ {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
++ {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"},
++ {"Mono Mixer", NULL, "Capture Mono Mux"},
++
++ /* DAC inv mux 1 */
++ {"DAC Inv Mux 1", "Mono", "Mono Mixer"},
++ {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
++ {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
++ {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
++ {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
++
++ /* DAC inv mux 2 */
++ {"DAC Inv Mux 2", "Mono", "Mono Mixer"},
++ {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
++ {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
++ {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
++ {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
++
++ /* headphone left mux */
++ {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
++
++ /* headphone right mux */
++ {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
++
++ /* speaker left mux */
++ {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
++ {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
++ {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
++
++ /* speaker right mux */
++ {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
++ {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
++ {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
++
++ /* mono mux */
++ {"Mono Out Mux", "Mono", "Mono Mixer"},
++ {"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
++
++ /* out 3 mux */
++ {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
++
++ /* out 4 mux */
++ {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
++
++ /* output pga */
++ {"HPL", NULL, "Left Headphone"},
++ {"Left Headphone", NULL, "Left Headphone Out Mux"},
++ {"HPR", NULL, "Right Headphone"},
++ {"Right Headphone", NULL, "Right Headphone Out Mux"},
++ {"OUT3", NULL, "Out 3"},
++ {"Out 3", NULL, "Out 3 Mux"},
++ {"OUT4", NULL, "Out 4"},
++ {"Out 4", NULL, "Out 4 Mux"},
++ {"SPKL", NULL, "Left Speaker"},
++ {"Left Speaker", NULL, "Left Speaker Out Mux"},
++ {"SPKR", NULL, "Right Speaker"},
++ {"Right Speaker", NULL, "Right Speaker Out Mux"},
++ {"MONO", NULL, "Mono Out"},
++ {"Mono Out", NULL, "Mono Out Mux"},
++
++ /* input pga */
++ {"Left Line In", NULL, "LINEL"},
++ {"Right Line In", NULL, "LINER"},
++ {"Mono In", NULL, "MONOIN"},
++ {"Mic A PGA", NULL, "Mic A Pre Amp"},
++ {"Mic B PGA", NULL, "Mic B Pre Amp"},
++
++ /* left capture select */
++ {"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
++ {"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
++ {"Left Capture Source", "Line", "LINEL"},
++ {"Left Capture Source", "Mono In", "MONOIN"},
++ {"Left Capture Source", "Headphone", "Left HP Mixer"},
++ {"Left Capture Source", "Speaker", "Speaker Mixer"},
++ {"Left Capture Source", "Mono Out", "Mono Mixer"},
++
++ /* right capture select */
++ {"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
++ {"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
++ {"Right Capture Source", "Line", "LINER"},
++ {"Right Capture Source", "Mono In", "MONOIN"},
++ {"Right Capture Source", "Headphone", "Right HP Mixer"},
++ {"Right Capture Source", "Speaker", "Speaker Mixer"},
++ {"Right Capture Source", "Mono Out", "Mono Mixer"},
++
++ /* left ADC */
++ {"Left ADC", NULL, "Left Capture Source"},
++
++ /* right ADC */
++ {"Right ADC", NULL, "Right Capture Source"},
++
++ /* mic */
++ {"Mic A Pre Amp", NULL, "Mic A Source"},
++ {"Mic A Source", "Mic 1", "MIC1"},
++ {"Mic A Source", "Mic 2 A", "MIC2A"},
++ {"Mic A Source", "Mic 2 B", "Mic B Source"},
++ {"Mic B Pre Amp", "MPB", "Mic B Source"},
++ {"Mic B Source", NULL, "MIC2B"},
++
++ /* headphone capture */
++ {"Capture Headphone Mux", "Stereo", "Capture Mixer"},
++ {"Capture Headphone Mux", "Left", "Left Capture Source"},
++ {"Capture Headphone Mux", "Right", "Right Capture Source"},
++
++ /* mono capture */
++ {"Capture Mono Mux", "Stereo", "Capture Mixer"},
++ {"Capture Mono Mux", "Left", "Left Capture Source"},
++ {"Capture Mono Mux", "Right", "Right Capture Source"},
++
++ {NULL, NULL, NULL},
++};
++
++static int wm9713_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
++ }
++
++ /* set up audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static unsigned int ac97_read(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++
++ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
++ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
++ reg == AC97_CD)
++ return soc_ac97_ops.read(codec->ac97, reg);
++ else {
++ reg = reg >> 1;
++
++ if (reg > (ARRAY_SIZE(wm9713_reg)))
++ return -EIO;
++
++ return cache[reg];
++ }
++}
++
++static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int val)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg < 0x7c)
++ soc_ac97_ops.write(codec->ac97, reg, val);
++ reg = reg >> 1;
++ if (reg <= (ARRAY_SIZE(wm9713_reg)))
++ cache[reg] = val;
++
++ return 0;
++}
++
++struct pll_ {
++ unsigned int in_hz;
++ unsigned int lf:1; /* allows low frequency use */
++ unsigned int sdm:1; /* allows fraction n div */
++ unsigned int divsel:1; /* enables input clock div */
++ unsigned int divctl:1; /* input clock divider */
++ unsigned int n:4;
++ unsigned int k;
++};
++
++struct pll_ pll[] = {
++ {13000000, 0, 1, 0, 0, 7, 0x23f488},
++ {2048000, 1, 0, 0, 0, 12, 0x0},
++ {4096000, 1, 0, 0, 0, 6, 0x0},
++ {12288000, 0, 0, 0, 0, 8, 0x0},
++ /* liam - add more entries */
++};
++
++static int wm9713_set_pll(struct snd_soc_codec *codec,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct wm9713_priv *wm9713 = codec->private_data;
++ int i;
++ u16 reg, reg2;
++
++ /* turn PLL off ? */
++ if (freq_in == 0 || freq_out == 0) {
++ /* disable PLL power and select ext source */
++ reg = ac97_read(codec, AC97_HANDSET_RATE);
++ ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
++ reg = ac97_read(codec, AC97_EXTENDED_MID);
++ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
++ wm9713->pll_out = 0;
++ return 0;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(pll); i++) {
++ if (pll[i].in_hz == freq_in)
++ goto found;
++ }
++ return -EINVAL;
++
++found:
++ if (pll[i].sdm == 0) {
++ reg = (pll[i].n << 12) | (pll[i].lf << 11) |
++ (pll[i].divsel << 9) | (pll[i].divctl << 8);
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++ } else {
++ /* write the fractional k to the reg 0x46 pages */
++ reg2 = (pll[i].n << 12) | (pll[i].lf << 11) | (pll[i].sdm << 10) |
++ (pll[i].divsel << 9) | (pll[i].divctl << 8);
++
++ reg = reg2 | (0x5 << 4) | (pll[i].k >> 20); /* K [21:20] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++
++ reg = reg2 | (0x4 << 4) | ((pll[i].k >> 16) & 0xf); /* K [19:16] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++
++ reg = reg2 | (0x3 << 4) | ((pll[i].k >> 12) & 0xf); /* K [15:12] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++
++ reg = reg2 | (0x2 << 4) | ((pll[i].k >> 8) & 0xf); /* K [11:8] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++
++ reg = reg2 | (0x1 << 4) | ((pll[i].k >> 4) & 0xf); /* K [7:4] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++
++ reg = reg2 | (0x0 << 4) | (pll[i].k & 0xf); /* K [3:0] */
++ ac97_write(codec, AC97_LINE1_LEVEL, reg);
++ }
++
++ /* turn PLL on and select as source */
++ reg = ac97_read(codec, AC97_EXTENDED_MID);
++ ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
++ reg = ac97_read(codec, AC97_HANDSET_RATE);
++ ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
++ wm9713->pll_out = freq_out;
++ wm9713->pll_in = freq_in;
++
++ /* wait 10ms AC97 link frames for the link to stabilise */
++ schedule_timeout_interruptible(msecs_to_jiffies(10));
++ return 0;
++}
++
++static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
++}
++
++/*
++ * Tristate the PCM DAI lines, tristate can be disabled by calling
++ * wm9713_set_dai_fmt()
++ */
++static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
++ int tristate)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
++
++ if (tristate)
++ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
++
++ return 0;
++}
++
++/*
++ * Configure WM9713 clock dividers.
++ * Voice DAC needs 256 FS
++ */
++static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM9713_PCMCLK_DIV:
++ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
++ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
++ break;
++ case WM9713_CLKA_MULT:
++ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
++ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
++ break;
++ case WM9713_CLKB_MULT:
++ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
++ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
++ break;
++ case WM9713_HIFI_DIV:
++ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
++ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
++ break;
++ case WM9713_PCMBCLK_DIV:
++ reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
++ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++};
++
++static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffe2;
++ u16 reg = 0x8000;
++
++ /* clock masters */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK){
++ case SND_SOC_DAIFMT_CBM_CFM:
++ reg |= 0x4000;
++ gpio |= 0x0008;
++ break;
++ case SND_SOC_DAIFMT_CBM_CFS:
++ reg |= 0x6000;
++ gpio |= 0x000c;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ reg |= 0x0200;
++ gpio |= 0x000d;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFM:
++ gpio |= 0x0009;
++ break;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_IB_IF:
++ reg |= 0x00c0;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ reg |= 0x0080;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ reg |= 0x0040;
++ break;
++ }
++
++ /* DAI format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ reg |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ reg |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ reg |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ reg |= 0x0043;
++ break;
++ }
++
++ ac97_write(codec, AC97_GPIO_CFG, gpio);
++ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
++ return 0;
++}
++
++static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
++
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ reg |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ reg |= 0x0008;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ reg |= 0x000c;
++ break;
++ }
++
++ /* enable PCM interface in master mode */
++ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
++ return 0;
++}
++
++static void wm9713_voiceshutdown(snd_pcm_substream_t *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 status;
++
++ /* Gracefully shut down the voice interface. */
++ status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
++ ac97_write(codec,AC97_HANDSET_RATE,0x0280);
++ schedule_timeout_interruptible(msecs_to_jiffies(1));
++ ac97_write(codec,AC97_HANDSET_RATE,0x0F80);
++ ac97_write(codec,AC97_EXTENDED_MID,status);
++}
++
++static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg;
++ u16 vra;
++
++ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
++ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ reg = AC97_PCM_FRONT_DAC_RATE;
++ else
++ reg = AC97_PCM_LR_ADC_RATE;
++
++ return ac97_write(codec, reg, runtime->rate);
++}
++
++static int ac97_aux_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 vra, xsle;
++
++ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
++ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
++ xsle = ac97_read(codec, AC97_PCI_SID);
++ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
++
++ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
++ return -ENODEV;
++
++ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
++}
++
++#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
++
++#define WM9713_PCM_FORMATS \
++ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
++ SNDRV_PCM_FORMAT_S24_LE)
++
++struct snd_soc_codec_dai wm9713_dai[] = {
++{
++ .name = "AC97 HiFi",
++ .playback = {
++ .stream_name = "HiFi Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM9713_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .stream_name = "HiFi Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM9713_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .prepare = ac97_hifi_prepare,},
++ },
++ {
++ .name = "AC97 Aux",
++ .playback = {
++ .stream_name = "Aux Playback",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM9713_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .prepare = ac97_aux_prepare,},
++ },
++ {
++ .name = "WM9713 Voice",
++ .playback = {
++ .stream_name = "Voice Playback",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM9713_RATES,
++ .formats = WM9713_PCM_FORMATS,},
++ .capture = {
++ .stream_name = "Voice Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM9713_RATES,
++ .formats = WM9713_PCM_FORMATS,},
++ .ops = {
++ .hw_params = wm9713_pcm_hw_params,
++ .shutdown = wm9713_voiceshutdown,},
++ .dai_ops = {
++ .set_clkdiv = wm9713_set_dai_clkdiv,
++ .set_pll = wm9713_set_dai_pll,
++ .set_fmt = wm9713_set_dai_fmt,
++ .set_tristate = wm9713_set_dai_tristate,
++ },
++ },
++};
++EXPORT_SYMBOL_GPL(wm9713_dai);
++
++int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
++{
++ if (try_warm && soc_ac97_ops.warm_reset) {
++ soc_ac97_ops.warm_reset(codec->ac97);
++ if (!(ac97_read(codec, 0) & 0x8000))
++ return 1;
++ }
++
++ soc_ac97_ops.reset(codec->ac97);
++ if (ac97_read(codec, 0) & 0x8000)
++ return -EIO;
++ return 0;
++}
++EXPORT_SYMBOL_GPL(wm9713_reset);
++
++static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ u16 reg;
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* enable thermal shutdown */
++ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
++ ac97_write(codec, AC97_EXTENDED_MID, reg);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* enable master bias and vmid */
++ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
++ ac97_write(codec, AC97_EXTENDED_MID, reg);
++ ac97_write(codec, AC97_POWERDOWN, 0x0000);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* disable everything including AC link */
++ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
++ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
++ ac97_write(codec, AC97_POWERDOWN, 0xffff);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++static int wm9713_soc_suspend(struct platform_device *pdev,
++ pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm9713_soc_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ struct wm9713_priv *wm9713 = codec->private_data;
++ int i, ret;
++ u16 *cache = codec->reg_cache;
++
++ if ((ret = wm9713_reset(codec, 1)) < 0){
++ printk(KERN_ERR "could not reset AC97 codec\n");
++ return ret;
++ }
++
++ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* do we need to re-start the PLL ? */
++ if (wm9713->pll_out)
++ wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out);
++
++ /* only synchronise the codec if warm reset failed */
++ if (ret == 0) {
++ for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i+=2) {
++ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
++ i == AC97_EXTENDED_MSTATUS || i > 0x66)
++ continue;
++ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
++ }
++ }
++
++ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
++ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
++
++ return ret;
++}
++
++static int wm9713_soc_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec;
++ int ret = 0, reg;
++
++ printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
++
++ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (socdev->codec == NULL)
++ return -ENOMEM;
++ codec = socdev->codec;
++ mutex_init(&codec->mutex);
++
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm9713_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL){
++ ret = -ENOMEM;
++ goto cache_err;
++ }
++ memcpy(codec->reg_cache, wm9713_reg,
++ sizeof(u16) * ARRAY_SIZE(wm9713_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9713_reg);
++ codec->reg_cache_step = 2;
++
++ codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
++ if (codec->private_data == NULL) {
++ ret = -ENOMEM;
++ goto priv_err;
++ }
++
++ codec->name = "WM9713";
++ codec->owner = THIS_MODULE;
++ codec->dai = wm9713_dai;
++ codec->num_dai = ARRAY_SIZE(wm9713_dai);
++ codec->write = ac97_write;
++ codec->read = ac97_read;
++ codec->dapm_event = wm9713_dapm_event;
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
++ if (ret < 0)
++ goto codec_err;
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0)
++ goto pcm_err;
++
++ /* do a cold reset for the controller and then try
++ * a warm reset followed by an optional cold reset for codec */
++ wm9713_reset(codec, 0);
++ ret = wm9713_reset(codec, 1);
++ if (ret < 0) {
++ printk(KERN_ERR "AC97 link error\n");
++ goto reset_err;
++ }
++
++ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* unmute the adc - move to kcontrol */
++ reg = ac97_read(codec, AC97_CD) & 0x7fff;
++ ac97_write(codec, AC97_CD, reg);
++
++ wm9713_add_controls(codec);
++ wm9713_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0)
++ goto reset_err;
++ return 0;
++
++reset_err:
++ snd_soc_free_pcms(socdev);
++
++pcm_err:
++ snd_soc_free_ac97_codec(codec);
++
++codec_err:
++ kfree(codec->private_data);
++
++priv_err:
++ kfree(codec->reg_cache);
++
++cache_err:
++ kfree(socdev->codec);
++ socdev->codec = NULL;
++ return ret;
++}
++
++static int wm9713_soc_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec == NULL)
++ return 0;
++
++ snd_soc_dapm_free(socdev);
++ snd_soc_free_pcms(socdev);
++ snd_soc_free_ac97_codec(codec);
++ kfree(codec->private_data);
++ kfree(codec->reg_cache);
++ kfree(codec->dai);
++ kfree(codec);
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm9713 = {
++ .probe = wm9713_soc_probe,
++ .remove = wm9713_soc_remove,
++ .suspend = wm9713_soc_suspend,
++ .resume = wm9713_soc_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713);
++
++MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm9713.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm9713.h 2007-07-16 15:07:34.168284411 +0200
+@@ -0,0 +1,51 @@
++/*
++ * wm9713.h -- WM9713 Soc Audio driver
++ */
++
++#ifndef _WM9713_H
++#define _WM9713_H
++
++/* clock inputs */
++#define WM9713_CLKA_PIN 0
++#define WM9713_CLKB_PIN 1
++
++/* clock divider ID's */
++#define WM9713_PCMCLK_DIV 0
++#define WM9713_CLKA_MULT 1
++#define WM9713_CLKB_MULT 2
++#define WM9713_HIFI_DIV 3
++#define WM9713_PCMBCLK_DIV 4
++
++/* PCM clk div */
++#define WM9713_PCMDIV(x) ((x - 1) << 8)
++
++/* HiFi Div */
++#define WM9713_HIFIDIV(x) ((x - 1) << 12)
++
++/* MCLK clock mulitipliers */
++#define WM9713_CLKA_X1 (0 << 1)
++#define WM9713_CLKA_X2 (1 << 1)
++#define WM9713_CLKB_X1 (0 << 2)
++#define WM9713_CLKB_X2 (1 << 2)
++
++/* MCLK clock MUX */
++#define WM9713_CLK_MUX_A (0 << 0)
++#define WM9713_CLK_MUX_B (1 << 0)
++
++/* Voice DAI BCLK divider */
++#define WM9713_PCMBCLK_DIV_1 (0 << 9)
++#define WM9713_PCMBCLK_DIV_2 (1 << 9)
++#define WM9713_PCMBCLK_DIV_4 (2 << 9)
++#define WM9713_PCMBCLK_DIV_8 (3 << 9)
++#define WM9713_PCMBCLK_DIV_16 (4 << 9)
++
++#define WM9713_DAI_AC97_HIFI 0
++#define WM9713_DAI_AC97_AUX 1
++#define WM9713_DAI_PCM_VOICE 2
++
++extern struct snd_soc_codec_device soc_codec_dev_wm9713;
++extern struct snd_soc_codec_dai wm9713_dai[3];
++
++int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
++
++#endif
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone.c 2007-07-16 15:07:34.192285777 +0200
+@@ -0,0 +1,127 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 30th Oct 2005 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/ac97.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++
++static struct snd_soc_machine mainstone;
++static long mst_audio_suspend_mask;
++
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ mst_audio_suspend_mask = MST_MSCWR2;
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static struct snd_soc_machine_config codecs[] = {
++{
++ .name = "AC97",
++ .sname = "AC97 HiFi",
++ .iface = &pxa_ac97_interface[0],
++},
++{
++ .name = "AC97 Aux",
++ .sname = "AC97 Aux",
++ .iface = &pxa_ac97_interface[1],
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .config = codecs,
++ .nconfigs = ARRAY_SIZE(codecs),
++};
++
++static struct snd_soc_device mainstone_snd_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_ac97,
++};
++
++static struct platform_device *mainstone_snd_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
++ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
++ ret = platform_device_add(mainstone_snd_device);
++
++ if (ret)
++ platform_device_put(mainstone_snd_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_baseband.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_baseband.c 2007-07-16 15:07:34.232288057 +0200
+@@ -0,0 +1,212 @@
++/*
++ * mainstone_baseband.c
++ * Mainstone Example Baseband modem -- ALSA Soc Audio Layer
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 15th Apr 2006 Initial version.
++ *
++ * This is example code to demonstrate connecting a baseband modem to the PCM
++ * DAI on the WM9713 codec on the Intel Mainstone platform. It is by no means
++ * complete as it requires code to control the modem.
++ *
++ * The architecture consists of the WM9713 AC97 DAI connected to the PXA27x
++ * AC97 controller and the WM9713 PCM DAI connected to the basebands DAI. The
++ * baseband is controlled via a serial port. Audio is routed between the PXA27x
++ * and the baseband via internal WM9713 analog paths.
++ *
++ * This driver is not the baseband modem driver. This driver only calls
++ * functions from the Baseband driver to set up it's PCM DAI.
++ *
++ * It's intended to use this driver as follows:-
++ *
++ * 1. open() WM9713 PCM audio device.
++ * 2. open() serial device (for AT commands).
++ * 3. configure PCM audio device (rate etc) - sets up WM9713 PCM DAI,
++ * this will also set up the baseband PCM DAI (via calling baseband driver).
++ * 4. send any further AT commands to set up baseband.
++ * 5. configure codec audio mixer paths.
++ * 6. open(), configure and read/write AC97 audio device - to Tx/Rx voice
++ *
++ * The PCM audio device is opened but IO is never performed on it as the IO is
++ * directly between the codec and the baseband (and not the CPU).
++ *
++ * TODO:
++ * o Implement callbacks
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/audio.h>
++#include <asm/arch/ssp.h>
++
++#include "../codecs/wm9713.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++#include "pxa2xx-ssp.h"
++
++static struct snd_soc_machine mainstone;
++
++/* Do specific baseband PCM voice startup here */
++static int baseband_startup(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++/* Do specific baseband PCM voice shutdown here */
++static void baseband_shutdown (struct snd_pcm_substream *substream)
++{
++}
++
++/* Do specific baseband modem PCM voice hw params init here */
++static int baseband_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ return 0;
++}
++
++/* Do specific baseband modem PCM voice hw params free here */
++static int baseband_hw_free(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++/*
++ * Baseband Processor DAI
++ */
++static struct snd_soc_cpu_dai baseband_dai =
++{ .name = "Baseband",
++ .id = 0,
++ .type = SND_SOC_DAI_PCM,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .startup = baseband_startup,
++ .shutdown = baseband_shutdown,
++ .hw_params = baseband_hw_params,
++ .hw_free = baseband_hw_free,
++ },
++};
++
++/* PM */
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mainstone_wm9713_init(struct snd_soc_codec *codec)
++{
++ return 0;
++}
++
++/* the physical audio connections between the WM9713, Baseband and pxa2xx */
++static struct snd_soc_dai_link mainstone_dai[] = {
++{
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
++ .init = mainstone_wm9713_init,
++},
++{
++ .name = "AC97 Aux",
++ .stream_name = "AC97 Aux",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
++},
++{
++ .name = "Baseband",
++ .stream_name = "Voice",
++ .cpu_dai = &baseband_dai,
++ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct snd_soc_device mainstone_snd_ac97_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm9713,
++};
++
++static struct platform_device *mainstone_snd_ac97_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_ac97_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
++ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
++
++ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
++ platform_device_put(mainstone_snd_ac97_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_ac97_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Mainstone Example Baseband PCM Interface");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_bluetooth.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_bluetooth.c 2007-07-16 15:07:34.248288967 +0200
+@@ -0,0 +1,371 @@
++/*
++ * mainstone_bluetooth.c
++ * Mainstone Example Bluetooth -- ALSA Soc Audio Layer
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 15th May 2006 Initial version.
++ *
++ * This is example code to demonstrate connecting a bluetooth codec to the PCM
++ * DAI on the WM8753 codec on the Intel Mainstone platform. It is by no means
++ * complete as it requires code to control the BT codec.
++ *
++ * The architecture consists of the WM8753 HIFI DAI connected to the PXA27x
++ * I2S controller and the WM8753 PCM DAI connected to the bluetooth DAI. The
++ * bluetooth codec and wm8753 are controlled via I2C. Audio is routed between
++ * the PXA27x and the bluetooth via internal WM8753 analog paths.
++ *
++ * This example supports the following audio input/outputs.
++ *
++ * o Board mounted Mic and Speaker (spk has amplifier)
++ * o Headphones via jack socket
++ * o BT source and sink
++ *
++ * This driver is not the bluetooth codec driver. This driver only calls
++ * functions from the Bluetooth driver to set up it's PCM DAI.
++ *
++ * It's intended to use the driver as follows:-
++ *
++ * 1. open() WM8753 PCM audio device.
++ * 2. configure PCM audio device (rate etc) - sets up WM8753 PCM DAI,
++ * this should also set up the BT codec DAI (via calling bt driver).
++ * 3. configure codec audio mixer paths.
++ * 4. open(), configure and read/write HIFI audio device - to Tx/Rx voice
++ *
++ * The PCM audio device is opened but IO is never performed on it as the IO is
++ * directly between the codec and the BT codec (and not the CPU).
++ *
++ * TODO:
++ * o Implement callbacks
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/audio.h>
++#include <asm/arch/ssp.h>
++
++#include "../codecs/wm8753.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++#include "pxa2xx-ssp.h"
++
++static struct snd_soc_machine mainstone;
++
++/* Do specific bluetooth PCM startup here */
++static int bt_startup(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++/* Do specific bluetooth PCM shutdown here */
++static void bt_shutdown (struct snd_pcm_substream *substream)
++{
++}
++
++/* Do pecific bluetooth PCM hw params init here */
++static int bt_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ return 0;
++}
++
++/* Do specific bluetooth PCM hw params free here */
++static int bt_hw_free(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++#define BT_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100)
++
++/*
++ * BT Codec DAI
++ */
++static struct snd_soc_cpu_dai bt_dai =
++{ .name = "Bluetooth",
++ .id = 0,
++ .type = SND_SOC_DAI_PCM,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = BT_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = BT_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .startup = bt_startup,
++ .shutdown = bt_shutdown,
++ .hw_params = bt_hw_params,
++ .hw_free = bt_hw_free,
++ },
++};
++
++/* PM */
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++/*
++ * Machine audio functions.
++ *
++ * The machine now has 3 extra audio controls.
++ *
++ * Jack function: Sets function (device plugged into Jack) to nothing (Off)
++ * or Headphones.
++ *
++ * Mic function: Set the on board Mic to On or Off
++ * Spk function: Set the on board Spk to On or Off
++ *
++ * example: BT playback (of far end) and capture (of near end)
++ * Set Mic and Speaker to On, open BT alsa interface as above and set up
++ * internal audio paths.
++ */
++
++static int machine_jack_func = 0;
++static int machine_spk_func = 0;
++static int machine_mic_func = 0;
++
++static int machine_get_jack(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = machine_jack_func;
++ return 0;
++}
++
++static int machine_set_jack(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++ machine_jack_func = ucontrol->value.integer.value[0];
++ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", machine_jack_func);
++ return 0;
++}
++
++static int machine_get_spk(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = machine_spk_func;
++ return 0;
++}
++
++static int machine_set_spk(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (machine_spk_func == ucontrol->value.integer.value[0])
++ return 0;
++
++ machine_spk_func = ucontrol->value.integer.value[0];
++ snd_soc_dapm_set_endpoint(codec, "Spk", machine_spk_func);
++ return 1;
++}
++
++static int machine_get_mic(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = machine_spk_func;
++ return 0;
++}
++
++static int machine_set_mic(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (machine_spk_func == ucontrol->value.integer.value[0])
++ return 0;
++
++ machine_spk_func = ucontrol->value.integer.value[0];
++ snd_soc_dapm_set_endpoint(codec, "Mic", machine_mic_func);
++ return 1;
++}
++
++/* turns on board speaker amp on/off */
++static int machine_amp_event(struct snd_soc_dapm_widget *w, int event)
++{
++#if 0
++ if (SND_SOC_DAPM_EVENT_ON(event))
++ /* on */
++ else
++ /* off */
++#endif
++ return 0;
++}
++
++/* machine dapm widgets */
++static const struct snd_soc_dapm_widget machine_dapm_widgets[] = {
++SND_SOC_DAPM_HP("Headphone Jack", NULL),
++SND_SOC_DAPM_SPK("Spk", machine_amp_event),
++SND_SOC_DAPM_MIC("Mic", NULL),
++};
++
++/* machine connections to the codec pins */
++static const char* audio_map[][3] = {
++
++ /* headphone connected to LOUT1, ROUT1 */
++ {"Headphone Jack", NULL, "LOUT"},
++ {"Headphone Jack", NULL, "ROUT"},
++
++ /* speaker connected to LOUT2, ROUT2 */
++ {"Spk", NULL, "ROUT2"},
++ {"Spk", NULL, "LOUT2"},
++
++ /* mic is connected to MIC1 (via Mic Bias) */
++ {"MIC1", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic"},
++
++ {NULL, NULL, NULL},
++};
++
++static const char* jack_function[] = {"Off", "Headphone"};
++static const char* spk_function[] = {"Off", "On"};
++static const char* mic_function[] = {"Off", "On"};
++static const struct soc_enum machine_ctl_enum[] = {
++ SOC_ENUM_SINGLE_EXT(2, jack_function),
++ SOC_ENUM_SINGLE_EXT(2, spk_function),
++ SOC_ENUM_SINGLE_EXT(2, mic_function),
++};
++
++static const struct snd_kcontrol_new wm8753_machine_controls[] = {
++ SOC_ENUM_EXT("Jack Function", machine_ctl_enum[0], machine_get_jack, machine_set_jack),
++ SOC_ENUM_EXT("Speaker Function", machine_ctl_enum[1], machine_get_spk, machine_set_spk),
++ SOC_ENUM_EXT("Mic Function", machine_ctl_enum[2], machine_get_mic, machine_set_mic),
++};
++
++static int mainstone_wm8753_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* not used on this machine - e.g. will never be powered up */
++ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
++ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
++ snd_soc_dapm_set_endpoint(codec, "MONO2", 0);
++ snd_soc_dapm_set_endpoint(codec, "MONO1", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
++ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
++
++ /* Add machine specific controls */
++ for (i = 0; i < ARRAY_SIZE(wm8753_machine_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8753_machine_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ /* Add machine specific widgets */
++ for(i = 0; i < ARRAY_SIZE(machine_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &machine_dapm_widgets[i]);
++ }
++
++ /* Set up machine specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{ /* Hifi Playback - for similatious use with voice below */
++ .name = "WM8753",
++ .stream_name = "WM8753 HiFi",
++ .cpu_dai = &pxa_i2s_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
++ .init = mainstone_wm8753_init,
++},
++{ /* Voice via BT */
++ .name = "Bluetooth",
++ .stream_name = "Voice",
++ .cpu_dai = &bt_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct snd_soc_device mainstone_snd_wm8753_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8753,
++};
++
++static struct platform_device *mainstone_snd_wm8753_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_wm8753_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_wm8753_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_wm8753_device, &mainstone_snd_wm8753_devdata);
++ mainstone_snd_wm8753_devdata.dev = &mainstone_snd_wm8753_device->dev;
++
++ if((ret = platform_device_add(mainstone_snd_wm8753_device)) != 0)
++ platform_device_put(mainstone_snd_wm8753_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_wm8753_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("Mainstone Example Bluetooth PCM Interface");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_wm8731.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_wm8731.c 2007-07-16 15:07:34.264289881 +0200
+@@ -0,0 +1,203 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 5th June 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm8731.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++
++static struct snd_soc_machine mainstone;
++
++static int mainstone_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int clk = 0;
++ int ret = 0;
++
++ switch (params_rate(params)) {
++ case 8000:
++ case 16000:
++ case 48000:
++ case 96000:
++ clk = 12288000;
++ break;
++ case 11025:
++ case 22050:
++ case 44100:
++ clk = 11289600;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set the I2S system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static struct snd_soc_ops mainstone_ops = {
++ .hw_params = mainstone_hw_params,
++};
++
++static const struct snd_soc_dapm_widget dapm_widgets[] = {
++ SND_SOC_DAPM_MIC("Int Mic", NULL),
++ SND_SOC_DAPM_SPK("Ext Spk", NULL),
++};
++
++static const char* intercon[][3] = {
++
++ /* speaker connected to LHPOUT */
++ {"Ext Spk", NULL, "LHPOUT"},
++
++ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
++ {"MICIN", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Int Mic"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++/*
++ * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
++ */
++static int mainstone_wm8731_init(struct snd_soc_codec *codec)
++{
++ int i;
++
++
++ /* Add specific widgets */
++ for(i = 0; i < ARRAY_SIZE(dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &dapm_widgets[i]);
++ }
++
++ /* Set up specific audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
++ }
++
++ /* not connected */
++ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
++ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
++
++ /* always connected */
++ snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
++ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
++
++ snd_soc_dapm_sync_endpoints(codec);
++
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{
++ .name = "WM8731",
++ .stream_name = "WM8731 HiFi",
++ .cpu_dai = &pxa_i2s_dai,
++ .codec_dai = &wm8731_dai,
++ .init = mainstone_wm8731_init,
++ .ops = &mainstone_ops,
++ },
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct wm8731_setup_data corgi_wm8731_setup = {
++ .i2c_address = 0x1b,
++};
++
++static struct snd_soc_device mainstone_snd_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8731,
++ .codec_data = &corgi_wm8731_setup,
++};
++
++static struct platform_device *mainstone_snd_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
++ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
++ ret = platform_device_add(mainstone_snd_device);
++
++ if (ret)
++ platform_device_put(mainstone_snd_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM8731 Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_wm8753.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_wm8753.c 2007-07-16 15:07:34.280290792 +0200
+@@ -0,0 +1,547 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 30th Oct 2005 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm8753.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++#include "pxa2xx-ssp.h"
++
++/*
++ * SSP GPIO's
++ */
++#define GPIO26_SSP1RX_MD (26 | GPIO_ALT_FN_1_IN)
++#define GPIO25_SSP1TX_MD (25 | GPIO_ALT_FN_2_OUT)
++#define GPIO23_SSP1CLKS_MD (23 | GPIO_ALT_FN_2_IN)
++#define GPIO24_SSP1FRMS_MD (24 | GPIO_ALT_FN_2_IN)
++#define GPIO23_SSP1CLKM_MD (23 | GPIO_ALT_FN_2_OUT)
++#define GPIO24_SSP1FRMM_MD (24 | GPIO_ALT_FN_2_OUT)
++#define GPIO53_SSP1SYSCLK_MD (53 | GPIO_ALT_FN_2_OUT)
++
++#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
++#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
++#define GPIO22_SSP2CLKS_MD (22 | GPIO_ALT_FN_3_IN)
++#define GPIO88_SSP2FRMS_MD (88 | GPIO_ALT_FN_3_IN)
++#define GPIO22_SSP2CLKM_MD (22 | GPIO_ALT_FN_3_OUT)
++#define GPIO88_SSP2FRMM_MD (88 | GPIO_ALT_FN_3_OUT)
++#define GPIO22_SSP2SYSCLK_MD (22 | GPIO_ALT_FN_2_OUT)
++
++#define GPIO82_SSP3RX_MD (82 | GPIO_ALT_FN_1_IN)
++#define GPIO81_SSP3TX_MD (81 | GPIO_ALT_FN_1_OUT)
++#define GPIO84_SSP3CLKS_MD (84 | GPIO_ALT_FN_1_IN)
++#define GPIO83_SSP3FRMS_MD (83 | GPIO_ALT_FN_1_IN)
++#define GPIO84_SSP3CLKM_MD (84 | GPIO_ALT_FN_1_OUT)
++#define GPIO83_SSP3FRMM_MD (83 | GPIO_ALT_FN_1_OUT)
++#define GPIO45_SSP3SYSCLK_MD (45 | GPIO_ALT_FN_3_OUT)
++
++#if 0
++static struct pxa2xx_gpio ssp_gpios[3][4] = {
++ {{ /* SSP1 SND_SOC_DAIFMT_CBM_CFM */
++ .rx = GPIO26_SSP1RX_MD,
++ .tx = GPIO25_SSP1TX_MD,
++ .clk = (23 | GPIO_ALT_FN_2_IN),
++ .frm = (24 | GPIO_ALT_FN_2_IN),
++ .sys = GPIO53_SSP1SYSCLK_MD,
++ },
++ { /* SSP1 SND_SOC_DAIFMT_CBS_CFS */
++ .rx = GPIO26_SSP1RX_MD,
++ .tx = GPIO25_SSP1TX_MD,
++ .clk = (23 | GPIO_ALT_FN_2_OUT),
++ .frm = (24 | GPIO_ALT_FN_2_OUT),
++ .sys = GPIO53_SSP1SYSCLK_MD,
++ },
++ { /* SSP1 SND_SOC_DAIFMT_CBS_CFM */
++ .rx = GPIO26_SSP1RX_MD,
++ .tx = GPIO25_SSP1TX_MD,
++ .clk = (23 | GPIO_ALT_FN_2_OUT),
++ .frm = (24 | GPIO_ALT_FN_2_IN),
++ .sys = GPIO53_SSP1SYSCLK_MD,
++ },
++ { /* SSP1 SND_SOC_DAIFMT_CBM_CFS */
++ .rx = GPIO26_SSP1RX_MD,
++ .tx = GPIO25_SSP1TX_MD,
++ .clk = (23 | GPIO_ALT_FN_2_IN),
++ .frm = (24 | GPIO_ALT_FN_2_OUT),
++ .sys = GPIO53_SSP1SYSCLK_MD,
++ }},
++ {{ /* SSP2 SND_SOC_DAIFMT_CBM_CFM */
++ .rx = GPIO11_SSP2RX_MD,
++ .tx = GPIO13_SSP2TX_MD,
++ .clk = (22 | GPIO_ALT_FN_3_IN),
++ .frm = (88 | GPIO_ALT_FN_3_IN),
++ .sys = GPIO22_SSP2SYSCLK_MD,
++ },
++ { /* SSP2 SND_SOC_DAIFMT_CBS_CFS */
++ .rx = GPIO11_SSP2RX_MD,
++ .tx = GPIO13_SSP2TX_MD,
++ .clk = (22 | GPIO_ALT_FN_3_OUT),
++ .frm = (88 | GPIO_ALT_FN_3_OUT),
++ .sys = GPIO22_SSP2SYSCLK_MD,
++ },
++ { /* SSP2 SND_SOC_DAIFMT_CBS_CFM */
++ .rx = GPIO11_SSP2RX_MD,
++ .tx = GPIO13_SSP2TX_MD,
++ .clk = (22 | GPIO_ALT_FN_3_OUT),
++ .frm = (88 | GPIO_ALT_FN_3_IN),
++ .sys = GPIO22_SSP2SYSCLK_MD,
++ },
++ { /* SSP2 SND_SOC_DAIFMT_CBM_CFS */
++ .rx = GPIO11_SSP2RX_MD,
++ .tx = GPIO13_SSP2TX_MD,
++ .clk = (22 | GPIO_ALT_FN_3_IN),
++ .frm = (88 | GPIO_ALT_FN_3_OUT),
++ .sys = GPIO22_SSP2SYSCLK_MD,
++ }},
++ {{ /* SSP3 SND_SOC_DAIFMT_CBM_CFM */
++ .rx = GPIO82_SSP3RX_MD,
++ .tx = GPIO81_SSP3TX_MD,
++ .clk = (84 | GPIO_ALT_FN_3_IN),
++ .frm = (83 | GPIO_ALT_FN_3_IN),
++ .sys = GPIO45_SSP3SYSCLK_MD,
++ },
++ { /* SSP3 SND_SOC_DAIFMT_CBS_CFS */
++ .rx = GPIO82_SSP3RX_MD,
++ .tx = GPIO81_SSP3TX_MD,
++ .clk = (84 | GPIO_ALT_FN_3_OUT),
++ .frm = (83 | GPIO_ALT_FN_3_OUT),
++ .sys = GPIO45_SSP3SYSCLK_MD,
++ },
++ { /* SSP3 SND_SOC_DAIFMT_CBS_CFM */
++ .rx = GPIO82_SSP3RX_MD,
++ .tx = GPIO81_SSP3TX_MD,
++ .clk = (84 | GPIO_ALT_FN_3_OUT),
++ .frm = (83 | GPIO_ALT_FN_3_IN),
++ .sys = GPIO45_SSP3SYSCLK_MD,
++ },
++ { /* SSP3 SND_SOC_DAIFMT_CBM_CFS */
++ .rx = GPIO82_SSP3RX_MD,
++ .tx = GPIO81_SSP3TX_MD,
++ .clk = (84 | GPIO_ALT_FN_3_IN),
++ .frm = (83 | GPIO_ALT_FN_3_OUT),
++ .sys = GPIO45_SSP3SYSCLK_MD,
++ }},
++};
++#endif
++
++static struct snd_soc_machine mainstone;
++
++static int mainstone_hifi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0, fmt = 0;
++ int ret = 0;
++
++ /*
++ * The WM8753 is far better at generating accurate audio clocks than the
++ * pxa2xx I2S controller, so we will use it as master when we can.
++ * i.e all rates except 8k and 16k as BCLK must be 64 * rate when the
++ * pxa27x or pxa25x is slave. Note this restriction does not apply to SSP
++ * I2S emulation mode.
++ */
++ switch (params_rate(params)) {
++ case 8000:
++ case 16000:
++ fmt = SND_SOC_DAIFMT_CBS_CFS;
++ pll_out = 12288000;
++ break;
++ case 48000:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 12288000;
++ break;
++ case 96000:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 12288000;
++ break;
++ case 11025:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_16;
++ pll_out = 11289600;
++ break;
++ case 22050:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_8;
++ pll_out = 11289600;
++ break;
++ case 44100:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 11289600;
++ break;
++ case 88200:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 11289600;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set the I2S system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is 13 MHz */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 13000000, pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int mainstone_hifi_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
++}
++
++/*
++ * Mainstone WM8753 HiFi DAI opserations.
++ */
++static struct snd_soc_ops mainstone_hifi_ops = {
++ .hw_params = mainstone_hifi_hw_params,
++ .hw_free = mainstone_hifi_hw_free,
++};
++
++static int mainstone_voice_startup(struct snd_pcm_substream *substream)
++{
++ /* enable USB on the go MUX so we can use SSPFRM2 */
++ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
++ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
++
++ return 0;
++}
++
++static void mainstone_voice_shutdown(struct snd_pcm_substream *substream)
++{
++// struct snd_soc_pcm_runtime *rtd = substream->private_data;
++
++ /* disable USB on the go MUX so we can use ttyS0 */
++ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
++ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
++
++ /* liam may need to tristate DAI */
++}
++
++static int mainstone_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0, pcmdiv = 0;
++ int ret = 0;
++
++ /*
++ * The WM8753 is far better at generating accurate audio clocks than the
++ * pxa2xx SSP controller, so we will use it as master when we can.
++ */
++ switch (params_rate(params)) {
++ case 8000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 256kHz */
++ break;
++ case 16000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_3; /* 4.096 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 512kHz */
++ break;
++ case 48000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_1; /* 12.288 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 1.536 MHz */
++ break;
++ case 11025:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_4; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 352.8 kHz */
++ break;
++ case 22050:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_2; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 705.6 kHz */
++ break;
++ case 44100:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_1; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 1.4112 MHz */
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_VXCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set codec PCM division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is 13 MHz */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 13000000, pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int mainstone_voice_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
++}
++
++static struct snd_soc_ops mainstone_voice_ops = {
++ .startup = mainstone_voice_startup,
++ .shutdown = mainstone_voice_shutdown,
++ .hw_params = mainstone_voice_hw_params,
++ .hw_free = mainstone_voice_hw_free,
++};
++
++static long mst_audio_suspend_mask;
++
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ mst_audio_suspend_mask = MST_MSCWR2;
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++/* example machine audio_mapnections */
++static const char* audio_map[][3] = {
++
++ /* mic is connected to mic1 - with bias */
++ {"MIC1", NULL, "Mic Bias"},
++ {"MIC1N", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic1 Jack"},
++ {"Mic Bias", NULL, "Mic1 Jack"},
++
++ {"ACIN", NULL, "ACOP"},
++ {NULL, NULL, NULL},
++};
++
++/* headphone detect support on my board */
++static const char * hp_pol[] = {"Headphone", "Speaker"};
++static const struct soc_enum wm8753_enum =
++ SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
++
++static const struct snd_kcontrol_new wm8753_mainstone_controls[] = {
++ SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
++ SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
++};
++
++/*
++ * This is an example machine initialisation for a wm8753 connected to a
++ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int mainstone_wm8753_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* set up mainstone codec pins */
++ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
++ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
++
++ /* add mainstone specific controls */
++ for (i = 0; i < ARRAY_SIZE(wm8753_mainstone_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8753_mainstone_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ /* set up mainstone specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{ /* Hifi Playback - for similatious use with voice below */
++ .name = "WM8753",
++ .stream_name = "WM8753 HiFi",
++ .cpu_dai = &pxa_i2s_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
++ .init = mainstone_wm8753_init,
++ .ops = &mainstone_hifi_ops,
++},
++{ /* Voice via BT */
++ .name = "Bluetooth",
++ .stream_name = "Voice",
++ .cpu_dai = &pxa_ssp_dai[1],
++ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
++ .ops = &mainstone_voice_ops,
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct wm8753_setup_data mainstone_wm8753_setup = {
++ .i2c_address = 0x1a,
++};
++
++static struct snd_soc_device mainstone_snd_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8753,
++ .codec_data = &mainstone_wm8753_setup,
++};
++
++static struct platform_device *mainstone_snd_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
++ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
++ ret = platform_device_add(mainstone_snd_device);
++
++ if (ret)
++ platform_device_put(mainstone_snd_device);
++
++ /* SSP port 2 slave */
++ pxa_gpio_mode(GPIO11_SSP2RX_MD);
++ pxa_gpio_mode(GPIO13_SSP2TX_MD);
++ pxa_gpio_mode(GPIO22_SSP2CLKS_MD);
++ pxa_gpio_mode(GPIO88_SSP2FRMS_MD);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM8753 Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_wm8974.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_wm8974.c 2007-07-16 15:07:34.300291932 +0200
+@@ -0,0 +1,104 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 30th Oct 2005 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm8974.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++
++static struct snd_soc_machine mainstone;
++
++static int mainstone_wm8974_init(struct snd_soc_codec *codec)
++{
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{
++ .name = "WM8974",
++ .stream_name = "WM8974 HiFi",
++ .cpu_dai = &pxa_i2s_dai,
++ .codec_dai = &wm8974_dai,
++ .init = mainstone_wm8974_init,
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct wm8974_setup_data mainstone_wm8974_setup = {
++ .i2c_address = 0x1a,
++};
++
++static struct snd_soc_device mainstone_snd_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8974,
++ .codec_data = &mainstone_wm8974_setup,
++};
++
++static struct platform_device *mainstone_snd_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
++ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
++ ret = platform_device_add(mainstone_snd_device);
++
++ if (ret)
++ platform_device_put(mainstone_snd_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_wm9712.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_wm9712.c 2007-07-16 15:07:34.316292846 +0200
+@@ -0,0 +1,172 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 29th Jan 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm9712.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++
++static struct snd_soc_machine mainstone;
++static long mst_audio_suspend_mask;
++
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ mst_audio_suspend_mask = MST_MSCWR2;
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++/* mainstone machine dapm widgets */
++static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
++ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
++};
++
++/* example machine interconnections */
++static const char* intercon[][3] = {
++
++ /* mic is connected to mic1 - with bias */
++ {"MIC1", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic (Internal)"},
++
++ {NULL, NULL, NULL},
++};
++
++/*
++ * This is an example machine initialisation for a wm8753 connected to a
++ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int mainstone_wm9712_init(struct snd_soc_codec *codec)
++{
++ int i;
++
++ /* set up mainstone codec pins */
++ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
++ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
++
++ /* Add mainstone specific widgets */
++ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
++ }
++
++ /* set up mainstone specific audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
++ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
++ .init = mainstone_wm9712_init,
++},
++{
++ .name = "AC97 Aux",
++ .stream_name = "AC97 Aux",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
++ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct snd_soc_device mainstone_snd_ac97_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm9712,
++};
++
++static struct platform_device *mainstone_snd_ac97_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_ac97_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
++ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
++
++ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
++ platform_device_put(mainstone_snd_ac97_device);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_ac97_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM9712 Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/mainstone_wm9713.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/mainstone_wm9713.c 2007-07-16 15:07:34.340294212 +0200
+@@ -0,0 +1,318 @@
++/*
++ * mainstone.c -- SoC audio for Mainstone
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 29th Jan 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/mainstone.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/wm9713.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ac97.h"
++#include "pxa2xx-ssp.h"
++
++#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
++#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
++#define GPIO22_SSP2CLKS_MD (22 | GPIO_ALT_FN_3_IN)
++#define GPIO88_SSP2FRMS_MD (88 | GPIO_ALT_FN_3_IN)
++#define GPIO22_SSP2CLKM_MD (22 | GPIO_ALT_FN_3_OUT)
++#define GPIO88_SSP2FRMM_MD (88 | GPIO_ALT_FN_3_OUT)
++#define GPIO22_SSP2SYSCLK_MD (22 | GPIO_ALT_FN_2_OUT)
++
++static struct snd_soc_machine mainstone;
++
++static int mainstone_voice_startup(struct snd_pcm_substream *substream)
++{
++ /* enable USB on the go MUX so we can use SSPFRM2 */
++ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
++ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
++ return 0;
++}
++
++static void mainstone_voice_shutdown(struct snd_pcm_substream *substream)
++{
++ /* disable USB on the go MUX so we can use ttyS0 */
++ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
++ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
++}
++
++static int mainstone_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int bclk = 0, pcmdiv = 0;
++ int ret = 0;
++
++ switch (params_rate(params)) {
++ case 8000:
++ pcmdiv = WM9713_PCMDIV(12); /* 2.048 MHz */
++ bclk = WM9713_PCMBCLK_DIV_16; /* 128kHz */
++ break;
++ case 16000:
++ pcmdiv = WM9713_PCMDIV(6); /* 4.096 MHz */
++ bclk = WM9713_PCMBCLK_DIV_16; /* 256kHz */
++ break;
++ case 48000:
++ pcmdiv = WM9713_PCMDIV(2); /* 12.288 MHz */
++ bclk = WM9713_PCMBCLK_DIV_16; /* 512kHz */
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM9713_PCMBCLK_DIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set codec PCM division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, pcmdiv);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static struct snd_soc_ops mainstone_voice_ops = {
++ .startup = mainstone_voice_startup,
++ .shutdown = mainstone_voice_shutdown,
++ .hw_params = mainstone_voice_hw_params,
++};
++
++static int test = 0;
++static int get_test(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = test;
++ return 0;
++}
++
++static int set_test(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ test = ucontrol->value.integer.value[0];
++ if(test) {
++
++ } else {
++
++ }
++ return 0;
++}
++
++static long mst_audio_suspend_mask;
++
++static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ mst_audio_suspend_mask = MST_MSCWR2;
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_resume(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_probe(struct platform_device *pdev)
++{
++ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static int mainstone_remove(struct platform_device *pdev)
++{
++ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
++ return 0;
++}
++
++static const char* test_function[] = {"Off", "On"};
++static const struct soc_enum mainstone_enum[] = {
++ SOC_ENUM_SINGLE_EXT(2, test_function),
++};
++
++static const struct snd_kcontrol_new mainstone_controls[] = {
++ SOC_ENUM_EXT("ATest Function", mainstone_enum[0], get_test, set_test),
++};
++
++/* mainstone machine dapm widgets */
++static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
++ SND_SOC_DAPM_MIC("Mic 1", NULL),
++ SND_SOC_DAPM_MIC("Mic 2", NULL),
++ SND_SOC_DAPM_MIC("Mic 3", NULL),
++};
++
++/* example machine audio_mapnections */
++static const char* audio_map[][3] = {
++
++ /* mic is connected to mic1 - with bias */
++ {"MIC1", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic 1"},
++ /* mic is connected to mic2A - with bias */
++ {"MIC2A", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic 2"},
++ /* mic is connected to mic2B - with bias */
++ {"MIC2B", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic 3"},
++
++ {NULL, NULL, NULL},
++};
++
++/*
++ * This is an example machine initialisation for a wm9713 connected to a
++ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int mainstone_wm9713_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* set up mainstone codec pins */
++ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
++ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
++
++ /* Add test specific controls */
++ for (i = 0; i < ARRAY_SIZE(mainstone_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&mainstone_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ /* Add mainstone specific widgets */
++ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
++ }
++
++ /* set up mainstone specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
++ audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link mainstone_dai[] = {
++{
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
++ .init = mainstone_wm9713_init,
++},
++{
++ .name = "AC97 Aux",
++ .stream_name = "AC97 Aux",
++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
++ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
++},
++{
++ .name = "WM9713",
++ .stream_name = "WM9713 Voice",
++ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP2],
++ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
++ .ops = &mainstone_voice_ops,
++},
++};
++
++static struct snd_soc_machine mainstone = {
++ .name = "Mainstone",
++ .probe = mainstone_probe,
++ .remove = mainstone_remove,
++ .suspend_pre = mainstone_suspend,
++ .resume_post = mainstone_resume,
++ .dai_link = mainstone_dai,
++ .num_links = ARRAY_SIZE(mainstone_dai),
++};
++
++static struct snd_soc_device mainstone_snd_ac97_devdata = {
++ .machine = &mainstone,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm9713,
++};
++
++static struct platform_device *mainstone_snd_ac97_device;
++
++static int __init mainstone_init(void)
++{
++ int ret;
++
++ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
++ if (!mainstone_snd_ac97_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
++ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
++
++ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
++ platform_device_put(mainstone_snd_ac97_device);
++
++ /* SSP port 2 slave */
++ pxa_gpio_mode(GPIO11_SSP2RX_MD);
++ pxa_gpio_mode(GPIO13_SSP2TX_MD);
++ pxa_gpio_mode(GPIO22_SSP2CLKS_MD);
++ pxa_gpio_mode(GPIO88_SSP2FRMS_MD);
++
++ return ret;
++}
++
++static void __exit mainstone_exit(void)
++{
++ platform_device_unregister(mainstone_snd_ac97_device);
++}
++
++module_init(mainstone_init);
++module_exit(mainstone_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM9713 Mainstone");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/pxa/pxa2xx-ssp.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/pxa2xx-ssp.c 2007-07-16 15:07:34.368295807 +0200
+@@ -0,0 +1,666 @@
++/*
++ * pxa2xx-ssp.c -- ALSA Soc Audio Layer
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 12th Aug 2005 Initial version.
++ *
++ * TODO:
++ * o Test network mode for > 16bit sample size
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/audio.h>
++#include <asm/arch/ssp.h>
++
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-ssp.h"
++
++#define PXA_SSP_DEBUG 0
++
++#if PXA_SSP_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++
++/*
++ * SSP audio private data
++ */
++struct ssp_priv {
++ unsigned int sysclk;
++};
++
++static struct ssp_priv ssp_clk[3];
++static struct ssp_dev ssp[3];
++#ifdef CONFIG_PM
++static struct ssp_state ssp_state[3];
++#endif
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_out = {
++ .name = "SSP1 PCM Mono out",
++ .dev_addr = __PREG(SSDR_P1),
++ .drcmr = &DRCMRTXSSDR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_in = {
++ .name = "SSP1 PCM Mono in",
++ .dev_addr = __PREG(SSDR_P1),
++ .drcmr = &DRCMRRXSSDR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_out = {
++ .name = "SSP1 PCM Stereo out",
++ .dev_addr = __PREG(SSDR_P1),
++ .drcmr = &DRCMRTXSSDR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_in = {
++ .name = "SSP1 PCM Stereo in",
++ .dev_addr = __PREG(SSDR_P1),
++ .drcmr = &DRCMRRXSSDR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_out = {
++ .name = "SSP2 PCM Mono out",
++ .dev_addr = __PREG(SSDR_P2),
++ .drcmr = &DRCMRTXSS2DR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_in = {
++ .name = "SSP2 PCM Mono in",
++ .dev_addr = __PREG(SSDR_P2),
++ .drcmr = &DRCMRRXSS2DR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_out = {
++ .name = "SSP2 PCM Stereo out",
++ .dev_addr = __PREG(SSDR_P2),
++ .drcmr = &DRCMRTXSS2DR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_in = {
++ .name = "SSP2 PCM Stereo in",
++ .dev_addr = __PREG(SSDR_P2),
++ .drcmr = &DRCMRRXSS2DR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_out = {
++ .name = "SSP3 PCM Mono out",
++ .dev_addr = __PREG(SSDR_P3),
++ .drcmr = &DRCMRTXSS3DR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_in = {
++ .name = "SSP3 PCM Mono in",
++ .dev_addr = __PREG(SSDR_P3),
++ .drcmr = &DRCMRRXSS3DR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH2,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_out = {
++ .name = "SSP3 PCM Stereo out",
++ .dev_addr = __PREG(SSDR_P3),
++ .drcmr = &DRCMRTXSS3DR,
++ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_in = {
++ .name = "SSP3 PCM Stereo in",
++ .dev_addr = __PREG(SSDR_P3),
++ .drcmr = &DRCMRRXSS3DR,
++ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
++ DCMD_BURST16 | DCMD_WIDTH4,
++};
++
++static struct pxa2xx_pcm_dma_params *ssp_dma_params[3][4] = {
++ {&pxa2xx_ssp1_pcm_mono_out, &pxa2xx_ssp1_pcm_mono_in,
++ &pxa2xx_ssp1_pcm_stereo_out,&pxa2xx_ssp1_pcm_stereo_in,},
++ {&pxa2xx_ssp2_pcm_mono_out, &pxa2xx_ssp2_pcm_mono_in,
++ &pxa2xx_ssp2_pcm_stereo_out, &pxa2xx_ssp2_pcm_stereo_in,},
++ {&pxa2xx_ssp3_pcm_mono_out, &pxa2xx_ssp3_pcm_mono_in,
++ &pxa2xx_ssp3_pcm_stereo_out,&pxa2xx_ssp3_pcm_stereo_in,},
++};
++
++static int pxa2xx_ssp_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ int ret = 0;
++
++ if (!rtd->dai->cpu_dai->active) {
++ ret = ssp_init (&ssp[cpu_dai->id], cpu_dai->id + 1,
++ SSP_NO_IRQ);
++ if (ret < 0)
++ return ret;
++ ssp_disable(&ssp[cpu_dai->id]);
++ }
++ return ret;
++}
++
++static void pxa2xx_ssp_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ if (!cpu_dai->active) {
++ ssp_disable(&ssp[cpu_dai->id]);
++ ssp_exit(&ssp[cpu_dai->id]);
++ }
++}
++
++#if defined (CONFIG_PXA27x)
++static int cken[3] = {CKEN23_SSP1, CKEN3_SSP2, CKEN4_SSP3};
++#else
++static int cken[3] = {CKEN3_SSP, CKEN9_NSSP, CKEN10_ASSP};
++#endif
++
++#ifdef CONFIG_PM
++
++static int pxa2xx_ssp_suspend(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if (!dai->active)
++ return 0;
++
++ ssp_save_state(&ssp[dai->id], &ssp_state[dai->id]);
++ pxa_set_cken(cken[dai->id], 0);
++ return 0;
++}
++
++static int pxa2xx_ssp_resume(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if (!dai->active)
++ return 0;
++
++ pxa_set_cken(cken[dai->id], 1);
++ ssp_restore_state(&ssp[dai->id], &ssp_state[dai->id]);
++ ssp_enable(&ssp[dai->id]);
++
++ return 0;
++}
++
++#else
++#define pxa2xx_ssp_suspend NULL
++#define pxa2xx_ssp_resume NULL
++#endif
++
++/*
++ * Set the SSP ports SYSCLK.
++ */
++static int pxa2xx_ssp_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ int port = cpu_dai->id + 1;
++ u32 sscr0 = SSCR0_P(port) &
++ ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
++
++ dbg("pxa2xx_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d",
++ cpu_dai->id, clk_id, freq);
++
++ switch (clk_id) {
++ case PXA2XX_SSP_CLK_NET_PLL:
++ sscr0 |= SSCR0_MOD;
++ case PXA2XX_SSP_CLK_PLL:
++ /* Internal PLL is fixed on pxa25x and pxa27x */
++#ifdef CONFIG_PXA27x
++ ssp_clk[cpu_dai->id].sysclk = 13000000;
++#else
++ ssp_clk[cpu_dai->id].sysclk = 1843200;
++#endif
++ break;
++ case PXA2XX_SSP_CLK_EXT:
++ ssp_clk[cpu_dai->id].sysclk = freq;
++ sscr0 |= SSCR0_ECS;
++ break;
++ case PXA2XX_SSP_CLK_NET:
++ ssp_clk[cpu_dai->id].sysclk = freq;
++ sscr0 |= SSCR0_NCS | SSCR0_MOD;
++ break;
++ case PXA2XX_SSP_CLK_AUDIO:
++ ssp_clk[cpu_dai->id].sysclk = 0;
++ SSCR0_P(port) |= SSCR0_SerClkDiv(1);
++ sscr0 |= SSCR0_ADC;
++ break;
++ default:
++ return -ENODEV;
++ }
++
++ /* the SSP CKEN clock must be disabled when changing SSP clock mode */
++ pxa_set_cken(cken[cpu_dai->id], 0);
++ SSCR0_P(port) |= sscr0;
++ pxa_set_cken(cken[cpu_dai->id], 1);
++ return 0;
++}
++
++/*
++ * Set the SSP clock dividers.
++ */
++static int pxa2xx_ssp_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
++ int div_id, int div)
++{
++ int port = cpu_dai->id + 1;
++
++ switch (div_id) {
++ case PXA2XX_SSP_AUDIO_DIV_ACDS:
++ SSACD_P(port) &= ~ 0x7;
++ SSACD_P(port) |= SSACD_ACDS(div);
++ break;
++ case PXA2XX_SSP_AUDIO_DIV_SCDB:
++ SSACD_P(port) &= ~0x8;
++ if (div == PXA2XX_SSP_CLK_SCDB_1)
++ SSACD_P(port) |= SSACD_SCDB;
++ break;
++ case PXA2XX_SSP_DIV_SCR:
++ SSCR0_P(port) &= ~SSCR0_SCR;
++ SSCR0_P(port) |= SSCR0_SerClkDiv(div);
++ break;
++ default:
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++/*
++ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
++ */
++static int pxa2xx_ssp_set_dai_pll(struct snd_soc_cpu_dai *cpu_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ int port = cpu_dai->id + 1;
++
++ SSACD_P(port) &= ~0x70;
++ switch (freq_out) {
++ case 5622000:
++ break;
++ case 11345000:
++ SSACD_P(port) |= (0x1 << 4);
++ break;
++ case 12235000:
++ SSACD_P(port) |= (0x2 << 4);
++ break;
++ case 14857000:
++ SSACD_P(port) |= (0x3 << 4);
++ break;
++ case 32842000:
++ SSACD_P(port) |= (0x4 << 4);
++ break;
++ case 48000000:
++ SSACD_P(port) |= (0x5 << 4);
++ break;
++ }
++ return 0;
++}
++
++/*
++ * Set the active slots in TDM/Network mode
++ */
++static int pxa2xx_ssp_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai,
++ unsigned int mask, int slots)
++{
++ int port = cpu_dai->id + 1;
++
++ SSCR0_P(port) &= ~SSCR0_SlotsPerFrm(7);
++
++ /* set number of active slots */
++ SSCR0_P(port) |= SSCR0_SlotsPerFrm(slots);
++
++ /* set active slot mask */
++ SSTSA_P(port) = mask;
++ SSRSA_P(port) = mask;
++ return 0;
++}
++
++/*
++ * Tristate the SSP DAI lines
++ */
++static int pxa2xx_ssp_set_dai_tristate(struct snd_soc_cpu_dai *cpu_dai,
++ int tristate)
++{
++ int port = cpu_dai->id + 1;
++
++ if (tristate)
++ SSCR1_P(port) &= ~SSCR1_TTE;
++ else
++ SSCR1_P(port) |= SSCR1_TTE;
++
++ return 0;
++}
++
++/*
++ * Set up the SSP DAI format.
++ * The SSP Port must be inactive before calling this function as the
++ * physical interface format is changed.
++ */
++static int pxa2xx_ssp_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
++ unsigned int fmt)
++{
++ int port = cpu_dai->id + 1;
++
++ /* reset port settings */
++ SSCR0_P(port) = 0;
++ SSCR1_P(port) = 0;
++ SSPSP_P(port) = 0;
++
++ /* NOTE: I2S emulation is still very much work in progress here */
++
++ /* FIXME: this is what wince uses for msb */
++ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_MSB) {
++ SSCR0_P(port) = SSCR0_EDSS | SSCR0_TISSP | SSCR0_DataSize(16);
++ goto master;
++ }
++
++ /* check for I2S emulation mode - handle it separately */
++ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) {
++ /* 8.4.11 */
++
++ /* Only SSCR0[NCS] or SSCR0[ECS] bit fields settings are optional */
++ SSCR0_P(port) = SSCR0_EDSS | SSCR0_PSP | SSCR0_DataSize(16);
++
++ /* set FIFO thresholds */
++ SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1);
++
++ /* normal: */
++ /* all bit fields must be cleared except: FSRT = 1 and
++ * SFRMWDTH = 16, DMYSTART=0,1) */
++ SSPSP_P(port) = SSPSP_FSRT | SSPSP_SFRMWDTH(16) | SSPSP_DMYSTRT(0);
++ goto master;
++ }
++
++ SSCR0_P(port) |= SSCR0_PSP;
++ SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1) |
++ SSCR1_TRAIL | SSCR1_RWOT;
++
++master:
++ switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ SSCR1_P(port) |= (SSCR1_SCLKDIR | SSCR1_SFRMDIR);
++ break;
++ case SND_SOC_DAIFMT_CBM_CFS:
++ SSCR1_P(port) |= SSCR1_SCLKDIR;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFM:
++ SSCR1_P(port) |= SSCR1_SFRMDIR;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ SSPSP_P(port) |= SSPSP_SFRMP | SSPSP_FSRT;
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_DSP_A:
++ SSPSP_P(port) |= SSPSP_DMYSTRT(1);
++ case SND_SOC_DAIFMT_DSP_B:
++ SSPSP_P(port) |= SSPSP_SCMODE(2);
++ break;
++ case SND_SOC_DAIFMT_I2S:
++ case SND_SOC_DAIFMT_MSB:
++ /* handled above */
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++/*
++ * Set the SSP audio DMA parameters and sample size.
++ * Can be called multiple times by oss emulation.
++ */
++static int pxa2xx_ssp_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ int dma = 0, chn = params_channels(params);
++ int port = cpu_dai->id + 1;
++
++ /* select correct DMA params */
++ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
++ dma = 1; /* capture DMA offset is 1,3 */
++ if (chn == 2)
++ dma += 2; /* stereo DMA offset is 2, mono is 0 */
++ cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
++
++ dbg("pxa2xx_ssp_hw_params: dma %d", dma);
++
++ /* we can only change the settings if the port is not in use */
++ if (SSCR0_P(port) & SSCR0_SSE)
++ return 0;
++
++ /* clear selected SSP bits */
++ SSCR0_P(port) &= ~(SSCR0_DSS | SSCR0_EDSS);
++
++ /* bit size */
++ switch(params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ SSCR0_P(port) |= SSCR0_DataSize(16);
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ SSCR0_P(port) |=(SSCR0_EDSS | SSCR0_DataSize(8));
++ /* we must be in network mode (2 slots) for 24 bit stereo */
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ SSCR0_P(port) |= (SSCR0_EDSS | SSCR0_DataSize(16));
++ /* we must be in network mode (2 slots) for 32 bit stereo */
++ break;
++ }
++
++ dbg("SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x",
++ SSCR0_P(port), SSCR1_P(port),
++ SSTO_P(port), SSPSP_P(port),
++ SSSR_P(port), SSACD_P(port));
++
++ return 0;
++}
++
++static int pxa2xx_ssp_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ int ret = 0;
++ int port = cpu_dai->id + 1;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_RESUME:
++ ssp_enable(&ssp[cpu_dai->id]);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ SSCR1_P(port) |= SSCR1_TSRE;
++ else
++ SSCR1_P(port) |= SSCR1_RSRE;
++ SSSR_P(port) |= SSSR_P(port);
++ break;
++ case SNDRV_PCM_TRIGGER_START:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ SSCR1_P(port) |= SSCR1_TSRE;
++ else
++ SSCR1_P(port) |= SSCR1_RSRE;
++ ssp_enable(&ssp[cpu_dai->id]);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ SSCR1_P(port) &= ~SSCR1_TSRE;
++ else
++ SSCR1_P(port) &= ~SSCR1_RSRE;
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ ssp_disable(&ssp[cpu_dai->id]);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ SSCR1_P(port) &= ~SSCR1_TSRE;
++ else
++ SSCR1_P(port) &= ~SSCR1_RSRE;
++ break;
++
++ default:
++ ret = -EINVAL;
++ }
++
++ dbg("SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x SSPSP 0x%08x SSSR 0x%08x",
++ SSCR0_P(port), SSCR1_P(port),
++ SSTO_P(port), SSPSP_P(port),
++ SSSR_P(port));
++
++ return ret;
++}
++
++#define PXA2XX_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++#define PXA2XX_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
++ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
++
++struct snd_soc_cpu_dai pxa_ssp_dai[] = {
++ { .name = "pxa2xx-ssp1",
++ .id = 0,
++ .type = SND_SOC_DAI_PCM,
++ .suspend = pxa2xx_ssp_suspend,
++ .resume = pxa2xx_ssp_resume,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .ops = {
++ .startup = pxa2xx_ssp_startup,
++ .shutdown = pxa2xx_ssp_shutdown,
++ .trigger = pxa2xx_ssp_trigger,
++ .hw_params = pxa2xx_ssp_hw_params,},
++ .dai_ops = {
++ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
++ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
++ .set_pll = pxa2xx_ssp_set_dai_pll,
++ .set_fmt = pxa2xx_ssp_set_dai_fmt,
++ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
++ .set_tristate = pxa2xx_ssp_set_dai_tristate,
++ },
++ },
++ { .name = "pxa2xx-ssp2",
++ .id = 1,
++ .type = SND_SOC_DAI_PCM,
++ .suspend = pxa2xx_ssp_suspend,
++ .resume = pxa2xx_ssp_resume,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .ops = {
++ .startup = pxa2xx_ssp_startup,
++ .shutdown = pxa2xx_ssp_shutdown,
++ .trigger = pxa2xx_ssp_trigger,
++ .hw_params = pxa2xx_ssp_hw_params,},
++ .dai_ops = {
++ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
++ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
++ .set_pll = pxa2xx_ssp_set_dai_pll,
++ .set_fmt = pxa2xx_ssp_set_dai_fmt,
++ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
++ .set_tristate = pxa2xx_ssp_set_dai_tristate,
++ },
++ },
++ { .name = "pxa2xx-ssp3",
++ .id = 2,
++ .type = SND_SOC_DAI_PCM,
++ .suspend = pxa2xx_ssp_suspend,
++ .resume = pxa2xx_ssp_resume,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = PXA2XX_SSP_RATES,
++ .formats = PXA2XX_SSP_FORMATS,},
++ .ops = {
++ .startup = pxa2xx_ssp_startup,
++ .shutdown = pxa2xx_ssp_shutdown,
++ .trigger = pxa2xx_ssp_trigger,
++ .hw_params = pxa2xx_ssp_hw_params,},
++ .dai_ops = {
++ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
++ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
++ .set_pll = pxa2xx_ssp_set_dai_pll,
++ .set_fmt = pxa2xx_ssp_set_dai_fmt,
++ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
++ .set_tristate = pxa2xx_ssp_set_dai_tristate,
++ },
++ },
++};
++EXPORT_SYMBOL_GPL(pxa_ssp_dai);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("pxa2xx SSP/PCM SoC Interface");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/imx/imx-ssi.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx-ssi.c 2007-07-16 15:07:34.408298086 +0200
+@@ -0,0 +1,591 @@
++/*
++ * imx-ssi.c -- SSI driver for Freescale IMX
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 29th Aug 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/dma-mapping.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <asm/arch/dma.h>
++#include <asm/arch/spba.h>
++#include <asm/arch/clock.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++
++#include "imx-ssi.h"
++#include "imx31-pcm.h"
++
++static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_out = {
++ .name = "SSI1 PCM Stereo out",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = emi_2_per,
++ .watermark_level = SDMA_TXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI1_STX0,
++ .event_id = DMA_REQ_SSI1_TX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_in = {
++ .name = "SSI1 PCM Stereo in",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_RXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI1_SRX0,
++ .event_id = DMA_REQ_SSI1_RX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_out = {
++ .name = "SSI2 PCM Stereo out",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_TXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI2_STX0,
++ .event_id = DMA_REQ_SSI2_TX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_in = {
++ .name = "SSI2 PCM Stereo in",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_RXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI2_SRX0,
++ .event_id = DMA_REQ_SSI2_RX1,
++ .peripheral_type = SSI,
++ },
++};
++
++/*
++ * SSI system clock configuration.
++ */
++static int imx_ssi_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ u32 scr;
++
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ scr = __raw_readw(SSI1_SCR);
++ else
++ scr = __raw_readw(SSI2_SCR);
++
++ switch (clk_id) {
++ case IMX_SSP_SYS_CLK:
++ if (dir == SND_SOC_CLOCK_OUT)
++ scr |= SSI_SCR_SYS_CLK_EN;
++ else
++ scr &= ~SSI_SCR_SYS_CLK_EN;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ __raw_writew(scr, SSI1_SCR);
++ else
++ __raw_writew(scr, SSI2_SCR);
++
++ return 0;
++}
++
++/*
++ * SSI Clock dividers
++ */
++static int imx_ssi_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
++ int div_id, int div)
++{
++ u32 stccr;
++
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ stccr = __raw_readw(SSI1_STCCR);
++ else
++ stccr = __raw_readw(SSI2_STCCR);
++
++ switch (div_id) {
++ case IMX_SSI_DIV_2:
++ stccr &= ~SSI_STCCR_DIV2;
++ stccr |= div;
++ break;
++ case IMX_SSI_DIV_PSR:
++ stccr &= ~SSI_STCCR_PSR;
++ stccr |= div;
++ break;
++ case IMX_SSI_DIV_PM:
++ stccr &= ~0xff;
++ stccr |= SSI_STCCR_PM(div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ __raw_writew(stccr, SSI1_STCCR);
++ else
++ __raw_writew(stccr, SSI2_STCCR);
++
++ return 0;
++}
++
++/*
++ * SSI Network Mode or TDM slots configuration.
++ */
++static int imx_ssi_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai,
++ unsigned int mask, int slots)
++{
++ u32 stmsk, srmsk, scr, stccr;
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ stmsk = __raw_readw(SSI1_STMSK);
++ srmsk = __raw_readw(SSI1_SRMSK);
++ scr = __raw_readw(SSI1_SCR);
++ stccr = __raw_readw(SSI1_STCCR);
++ } else {
++ stmsk = __raw_readw(SSI2_STMSK);
++ srmsk = __raw_readw(SSI2_SRMSK);
++ scr = __raw_readw(SSI2_SCR);
++ stccr = __raw_readw(SSI2_STCCR);
++ }
++
++ stmsk = srmsk = mask;
++ scr |= SSI_SCR_NET;
++ stccr &= ~0x1f00;
++ stccr |= SSI_STCCR_DC(slots);
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(stmsk, SSI1_STMSK);
++ __raw_writew(srmsk, SSI1_SRMSK);
++ __raw_writew(scr, SSI1_SCR);
++ __raw_writew(stccr, SSI1_STCCR);
++ } else {
++ __raw_writew(stmsk, SSI2_STMSK);
++ __raw_writew(srmsk, SSI2_SRMSK);
++ __raw_writew(scr, SSI2_SCR);
++ __raw_writew(stccr, SSI2_STCCR);
++ }
++
++ return 0;
++}
++
++/*
++ * SSI DAI format configuration.
++ */
++static int imx_ssi_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
++ unsigned int fmt)
++{
++ u32 stcr = 0, srcr = 0;
++
++ /* DAI mode */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ stcr |= SSI_STCR_TSCKP | SSI_STCR_TFSI |
++ SSI_STCR_TEFS | SSI_STCR_TXBIT0;
++ srcr |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
++ SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ stcr |= SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TXBIT0;
++ srcr |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI | SSI_SRCR_RXBIT0;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ stcr |= SSI_STCR_TEFS; // data 1 bit after sync
++ srcr |= SSI_SRCR_REFS; // data 1 bit after sync
++ case SND_SOC_DAIFMT_DSP_A:
++ stcr |= SSI_STCR_TFSL; // frame is 1 bclk long
++ srcr |= SSI_SRCR_RFSL; // frame is 1 bclk long
++
++ /* DAI clock inversion */
++ switch(fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_IB_IF:
++ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
++ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ stcr |= SSI_STCR_TSCKP;
++ srcr |= SSI_SRCR_RSCKP;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ stcr |= SSI_STCR_TFSI;
++ srcr |= SSI_SRCR_RFSI;
++ break;
++ }
++ break;
++ }
++
++ /* DAI clock master masks */
++ switch(fmt & SND_SOC_DAIFMT_CLOCK_MASK){
++ case SND_SOC_DAIFMT_CBM_CFM:
++ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
++ srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFM:
++ stcr |= SSI_STCR_TFDIR;
++ srcr |= SSI_SRCR_RFDIR;
++ break;
++ case SND_SOC_DAIFMT_CBM_CFS:
++ stcr |= SSI_STCR_TXDIR;
++ srcr |= SSI_SRCR_RXDIR;
++ break;
++ }
++
++ /* async */
++ //if (rtd->cpu_dai->flags & SND_SOC_DAI_ASYNC)
++ // SSI1_SCR |= SSI_SCR_SYN;
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(stcr, SSI1_STCR);
++ __raw_writew(0, SSI1_STCCR);
++ __raw_writew(srcr, SSI1_SRCR);
++ __raw_writew(0, SSI1_SRCCR);
++ } else {
++ __raw_writew(stcr, SSI2_STCR);
++ __raw_writew(0, SSI2_STCCR);
++ __raw_writew(srcr, SSI2_SRCR);
++ __raw_writew(0, SSI2_SRCCR);
++ }
++
++ return 0;
++}
++
++static int imx_ssi_set_dai_tristate(struct snd_soc_cpu_dai *cpu_dai,
++ int tristate)
++{
++ // via GPIO ??
++ return 0;
++}
++
++static int imx_ssi_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ mxc_clks_enable(SSI1_BAUD);
++ else
++ mxc_clks_enable(SSI2_BAUD);
++ return 0;
++}
++
++static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ u32 stccr, stcr;
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ stccr = __raw_readw(SSI1_STCCR) & 0x600ff;
++ stcr = __raw_readw(SSI1_STCR);
++ } else {
++ stccr = __raw_readw(SSI2_STCCR) & 0x600ff;
++ stcr = __raw_readw(SSI2_STCR);
++ }
++
++ /* DAI data (word) size */
++ switch(params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ stccr |= SSI_STCCR_WL(16);
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ stccr |= SSI_STCCR_WL(20);
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ stccr |= SSI_STCCR_WL(24);
++ break;
++ }
++
++ /* TDM - todo, only fifo 0 atm */
++ stcr |= SSI_STCR_TFEN0;
++ stccr |= SSI_STCCR_DC(params_channels(params));
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(stcr, SSI1_STCR);
++ __raw_writew(stccr, SSI1_STCCR);
++ } else {
++ __raw_writew(stcr, SSI2_STCR);
++ __raw_writew(stccr, SSI2_STCCR);
++ }
++
++ return 0;
++}
++
++static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ u32 srccr, srcr;
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ srccr = __raw_readw(SSI1_SRCCR) & 0x600ff;
++ srcr = __raw_readw(SSI1_SRCR);
++ } else {
++ srccr = __raw_readw(SSI2_SRCCR) & 0x600ff;
++ srcr = __raw_readw(SSI2_SRCR);
++ }
++
++ /* DAI data (word) size */
++ switch(params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ srccr |= SSI_SRCCR_WL(16);
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ srccr |= SSI_SRCCR_WL(20);
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ srccr |= SSI_SRCCR_WL(24);
++ break;
++ }
++
++ /* TDM - todo, only fifo 0 atm */
++ srcr |= SSI_SRCR_RFEN0;
++ srccr |= SSI_SRCCR_DC(params_channels(params));
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(srcr, SSI1_SRCR);
++ __raw_writew(srccr, SSI1_SRCCR);
++ } else {
++ __raw_writew(srcr, SSI2_SRCR);
++ __raw_writew(srccr, SSI2_SRCCR);
++ }
++ return 0;
++}
++
++static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ /* Tx/Rx config */
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out;
++ else
++ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out;
++ return imx_ssi_hw_tx_params(substream, params);
++ } else {
++ if (cpu_dai->id == IMX_DAI_SSI1)
++ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in;
++ else
++ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in;
++ return imx_ssi_hw_rx_params(substream, params);
++ }
++}
++
++static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ u32 scr, sier;
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ scr = __raw_readw(SSI1_SCR) & 0x600ff;
++ sier = __raw_readw(SSI1_SIER);
++ } else {
++ scr = __raw_readw(SSI2_SCR) & 0x600ff;
++ sier = __raw_readw(SSI2_SIER);
++ }
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ scr |= SSI_SCR_TE;
++ sier |= SSI_SIER_TDMAE;
++ } else {
++ scr |= SSI_SCR_RE;
++ sier |= SSI_SIER_RDMAE;
++ }
++ scr |= SSI_SCR_SSIEN;
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ scr |= SSI_SCR_TE;
++ else
++ scr |= SSI_SCR_RE;
++ scr |= SSI_SCR_SSIEN;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ sier |= SSI_SIER_TDMAE;
++ else
++ sier |= SSI_SIER_RDMAE;
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ scr &= ~SSI_SCR_SSIEN;
++ case SNDRV_PCM_TRIGGER_STOP:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ scr &= ~SSI_SCR_TE;
++ else
++ scr &= ~SSI_SCR_RE;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ sier &= ~SSI_SIER_TDMAE;
++ else
++ sier &= ~SSI_SIER_TDMAE;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(scr, SSI1_SCR);
++ __raw_writew(sier, SSI1_SIER);
++ } else {
++ __raw_writew(scr, SSI2_SCR);
++ __raw_writew(sier, SSI2_SIER);
++ }
++
++ return 0;
++}
++
++static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ /* shutdown SSI */
++ if (!cpu_dai->active) {
++ if(cpu_dai->id == IMX_DAI_SSI1) {
++ __raw_writew(__raw_readw(SSI1_SCR) & ~SSI_SCR_SSIEN, SSI1_SCR);
++ mxc_clks_disable(SSI1_BAUD);
++ } else {
++ __raw_writew(__raw_readw(SSI2_SCR) & ~SSI_SCR_SSIEN, SSI2_SCR);
++ mxc_clks_disable(SSI2_BAUD);
++ }
++ }
++}
++
++#ifdef CONFIG_PM
++static int imx_ssi_suspend(struct platform_device *dev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if(!dai->active)
++ return 0;
++
++ // do we need to disable any clocks
++
++ return 0;
++}
++
++static int imx_ssi_resume(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if(!dai->active)
++ return 0;
++
++ // do we need to enable any clocks
++ return 0;
++}
++
++#else
++#define imx_ssi_suspend NULL
++#define imx_ssi_resume NULL
++#endif
++
++#define IMX_SSI_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
++ SNDRV_PCM_RATE_96000)
++
++#define IMX_SSI_BITS \
++ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_cpu_dai imx_ssi_pcm_dai[] = {
++{
++ .name = "imx-i2s-1",
++ .id = IMX_DAI_SSI1,
++ .type = SND_SOC_DAI_I2S,
++ .suspend = imx_ssi_suspend,
++ .resume = imx_ssi_resume,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .formats = IMX_SSI_BITS,
++ .rates = IMX_SSI_RATES,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .formats = IMX_SSI_BITS,
++ .rates = IMX_SSI_RATES,},
++ .ops = {
++ .startup = imx_ssi_startup,
++ .shutdown = imx_ssi_shutdown,
++ .trigger = imx_ssi_trigger,
++ .hw_params = imx_ssi_hw_params,},
++ .dai_ops = {
++ .set_sysclk = imx_ssi_set_dai_sysclk,
++ .set_clkdiv = imx_ssi_set_dai_clkdiv,
++ .set_fmt = imx_ssi_set_dai_fmt,
++ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
++ .set_tristate = imx_ssi_set_dai_tristate,
++ },
++},
++{
++ .name = "imx-i2s-2",
++ .id = IMX_DAI_SSI2,
++ .type = SND_SOC_DAI_I2S,
++ .suspend = imx_ssi_suspend,
++ .resume = imx_ssi_resume,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .formats = IMX_SSI_BITS,
++ .rates = IMX_SSI_RATES,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 2,
++ .formats = IMX_SSI_BITS,
++ .rates = IMX_SSI_RATES,},
++ .ops = {
++ .startup = imx_ssi_startup,
++ .shutdown = imx_ssi_shutdown,
++ .trigger = imx_ssi_trigger,
++ .hw_params = imx_ssi_hw_params,},
++ .dai_ops = {
++ .set_sysclk = imx_ssi_set_dai_sysclk,
++ .set_clkdiv = imx_ssi_set_dai_clkdiv,
++ .set_fmt = imx_ssi_set_dai_fmt,
++ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
++ .set_tristate = imx_ssi_set_dai_tristate,
++ },
++},};
++EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("i.MX ASoC I2S driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/imx/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/Kconfig 2007-07-16 15:07:34.436299682 +0200
+@@ -0,0 +1,31 @@
++menu "SoC Audio for the Freescale i.MX"
++
++config SND_MXC_SOC
++ tristate "SoC Audio for the Freescale i.MX CPU"
++ depends on ARCH_MXC && SND
++ select SND_PCM
++ help
++ Say Y or M if you want to add support for codecs attached to
++ the MXC AC97, I2S or SSP interface. You will also need
++ to select the audio interfaces to support below.
++
++config SND_MXC_AC97
++ tristate
++ select SND_AC97_CODEC
++
++config SND_MXC_SOC_AC97
++ tristate
++ select AC97_BUS
++
++config SND_MXC_SOC_SSI
++ tristate
++
++config SND_SOC_MX31ADS_WM8753
++ tristate "SoC Audio support for MX31 - WM8753"
++ depends on SND_MXC_SOC && ARCH_MX3
++ select SND_MXC_SOC_SSI
++ help
++ Say Y if you want to add support for SoC audio on MX31ADS
++ with the WM8753.
++
++endmenu
+Index: linux-2.6.22.1/sound/soc/imx/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/Makefile 2007-07-16 15:07:34.460301051 +0200
+@@ -0,0 +1,18 @@
++# i.MX Platform Support
++snd-soc-imx21-objs := imx21-pcm.o
++snd-soc-imx31-objs := imx31-pcm.o
++snd-soc-imx-ac97-objs := imx-ac97.o
++snd-soc-imx-ssi-objs := imx-ssi.o
++
++obj-$(CONFIG_SND_MXC_SOC) += snd-soc-imx31.o
++obj-$(CONFIG_SND_MXC_SOC_AC97) += snd-soc-imx-ac97.o
++obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-imx-ssi.o
++
++# i.MX Machine Support
++snd-soc-mx31ads-wm8753-objs := mx31ads_wm8753.o
++obj-$(CONFIG_SND_SOC_MX31ADS_WM8753) += snd-soc-mx31ads-wm8753.o
++snd-soc-mx21ads-wm8753-objs := mx21ads_wm8753.o
++obj-$(CONFIG_SND_SOC_MX21ADS_WM8753) += snd-soc-mx21ads-wm8753.o
++snd-soc-mx21ads-wm8731-objs := mx21ads_wm8731.o
++obj-$(CONFIG_SND_SOC_MX21ADS_WM8731) += snd-soc-mx21ads-wm8731.o
++
+Index: linux-2.6.22.1/sound/soc/codecs/wm8711.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8711.c 2007-07-16 15:07:34.484302420 +0200
+@@ -0,0 +1,715 @@
++/*
++ * wm8711.c -- WM8711 ALSA SoC Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics
++ *
++ * Author: Mike Arthur <linux at wolfsonmicro.com>
++ *
++ * Based on wm8731.c by Richard Purdie
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8711.h"
++
++#define AUDIO_NAME "wm8711"
++#define WM8711_VERSION "0.3"
++
++/*
++ * Debug
++ */
++
++#define WM8711_DEBUG 0
++
++#ifdef WM8711_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8711;
++
++/* codec private data */
++struct wm8711_priv {
++ unsigned int sysclk;
++};
++
++/*
++ * wm8711 register cache
++ * We can't read the WM8711 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ * There is no point in caching the reset register
++ */
++static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
++ 0x0079, 0x0079, 0x000a, 0x0008,
++ 0x009f, 0x000a, 0x0000, 0x0000
++};
++
++/*
++ * read wm8711 register cache
++ */
++static inline unsigned int wm8711_read_reg_cache(struct snd_soc_codec * codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8711_RESET)
++ return 0;
++ if (reg >= WM8711_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8711 register cache
++ */
++static inline void wm8711_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8711_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8711 register space
++ */
++static int wm8711_write(struct snd_soc_codec * codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8753 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8711_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8711_reset(c) wm8711_write(c, WM8711_RESET, 0)
++
++static const struct snd_kcontrol_new wm8711_snd_controls[] = {
++
++SOC_DOUBLE_R("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
++ 0, 127, 0),
++SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
++ 7, 1, 0),
++
++};
++
++/* add non dapm controls */
++static int wm8711_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8711_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8711_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Output Mixer */
++static const snd_kcontrol_new_t wm8711_output_mixer_controls[] = {
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
++SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
++};
++
++static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
++SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
++ &wm8711_output_mixer_controls[0],
++ ARRAY_SIZE(wm8711_output_mixer_controls)),
++SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
++SND_SOC_DAPM_OUTPUT("LOUT"),
++SND_SOC_DAPM_OUTPUT("LHPOUT"),
++SND_SOC_DAPM_OUTPUT("ROUT"),
++SND_SOC_DAPM_OUTPUT("RHPOUT"),
++};
++
++static const char *intercon[][3] = {
++ /* output mixer */
++ {"Output Mixer", "Line Bypass Switch", "Line Input"},
++ {"Output Mixer", "HiFi Playback Switch", "DAC"},
++
++ /* outputs */
++ {"RHPOUT", NULL, "Output Mixer"},
++ {"ROUT", NULL, "Output Mixer"},
++ {"LHPOUT", NULL, "Output Mixer"},
++ {"LOUT", NULL, "Output Mixer"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8711_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8711_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8711_dapm_widgets[i]);
++ }
++
++ /* set up audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1],
++ intercon[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct _coeff_div {
++ u32 mclk;
++ u32 rate;
++ u16 fs;
++ u8 sr:4;
++ u8 bosr:1;
++ u8 usb:1;
++};
++
++/* codec mclk clock divider coefficients */
++static const struct _coeff_div coeff_div[] = {
++ /* 48k */
++ {12288000, 48000, 256, 0x0, 0x0, 0x0},
++ {18432000, 48000, 384, 0x0, 0x1, 0x0},
++ {12000000, 48000, 250, 0x0, 0x0, 0x1},
++
++ /* 32k */
++ {12288000, 32000, 384, 0x6, 0x0, 0x0},
++ {18432000, 32000, 576, 0x6, 0x1, 0x0},
++ {12000000, 32000, 375, 0x6, 0x0, 0x1},
++
++ /* 8k */
++ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
++ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
++ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
++ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
++ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
++
++ /* 96k */
++ {12288000, 96000, 128, 0x7, 0x0, 0x0},
++ {18432000, 96000, 192, 0x7, 0x1, 0x0},
++ {12000000, 96000, 125, 0x7, 0x0, 0x1},
++
++ /* 44.1k */
++ {11289600, 44100, 256, 0x8, 0x0, 0x0},
++ {16934400, 44100, 384, 0x8, 0x1, 0x0},
++ {12000000, 44100, 272, 0x8, 0x1, 0x1},
++
++ /* 88.2k */
++ {11289600, 88200, 128, 0xf, 0x0, 0x0},
++ {16934400, 88200, 192, 0xf, 0x1, 0x0},
++ {12000000, 88200, 136, 0xf, 0x1, 0x1},
++};
++
++static inline int get_coeff(int mclk, int rate)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
++ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
++ return i;
++ }
++ return 0;
++}
++
++static int wm8711_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct wm8711_priv *wm8711 = codec->private_data;
++ u16 iface = wm8711_read_reg_cache(codec, WM8711_IFACE) & 0xfffc;
++ int i = get_coeff(wm8711->sysclk, params_rate(params));
++ u16 srate = (coeff_div[i].sr << 2) |
++ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
++
++ wm8711_write(codec, WM8711_SRATE, srate);
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0008;
++ break;
++ }
++
++ wm8711_write(codec, WM8711_IFACE, iface);
++ return 0;
++}
++
++static int wm8711_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++
++ /* set active */
++ wm8711_write(codec, WM8711_ACTIVE, 0x0001);
++ return 0;
++}
++
++static void wm8711_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++
++ /* deactivate */
++ if (!codec->active) {
++ udelay(50);
++ wm8711_write(codec, WM8711_ACTIVE, 0x0);
++ }
++}
++
++static int wm8711_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8711_read_reg_cache(codec, WM8711_APDIGI) & 0xfff7;
++
++ if (mute)
++ wm8711_write(codec, WM8711_APDIGI, mute_reg | 0x8);
++ else
++ wm8711_write(codec, WM8711_APDIGI, mute_reg);
++
++ return 0;
++}
++
++static int wm8711_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ struct wm8711_priv *wm8711 = codec->private_data;
++
++ switch (freq) {
++ case 11289600:
++ case 12000000:
++ case 12288000:
++ case 16934400:
++ case 18432000:
++ wm8711->sysclk = freq;
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static int wm8711_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ iface |= 0x0040;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ iface |= 0x0013;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0090;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0080;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0010;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* set iface */
++ wm8711_write(codec, WM8711_IFACE, iface);
++ return 0;
++}
++
++
++static int wm8711_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ u16 reg = wm8711_read_reg_cache(codec, WM8711_PWR) & 0xff7f;
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, osc on, dac unmute */
++ wm8711_write(codec, WM8711_PWR, reg);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, */
++ wm8711_write(codec, WM8711_PWR, reg | 0x0040);
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8711_write(codec, WM8711_ACTIVE, 0x0);
++ wm8711_write(codec, WM8711_PWR, 0xffff);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8711_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
++ SNDRV_PCM_RATE_96000)
++
++#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8711_dai = {
++ .name = "WM8711",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8711_RATES,
++ .formats = WM8711_FORMATS,},
++ .ops = {
++ .prepare = wm8711_pcm_prepare,
++ .hw_params = wm8711_hw_params,
++ .shutdown = wm8711_shutdown,
++ },
++ .dai_ops = {
++ .digital_mute = wm8711_mute,
++ .set_sysclk = wm8711_set_dai_sysclk,
++ .set_fmt = wm8711_set_dai_fmt,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8711_dai);
++
++static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8711_write(codec, WM8711_ACTIVE, 0x0);
++ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8711_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8711_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8711 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8711_init(struct snd_soc_device* socdev)
++{
++ struct snd_soc_codec* codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "WM8711";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8711_read_reg_cache;
++ codec->write = wm8711_write;
++ codec->dapm_event = wm8711_dapm_event;
++ codec->dai = &wm8711_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8711_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8711_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8711_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8711_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8711_reg);
++
++ wm8711_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8711: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* set the update bits */
++ reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V);
++ wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100);
++ reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V);
++ wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100);
++
++ wm8711_add_controls(codec);
++ wm8711_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8711: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8711_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8711 2 wire address is determined by GPIO5
++ * state during powerup.
++ * low = 0x1a
++ * high = 0x1b
++ */
++#define I2C_DRIVERID_WM8711 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8711_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8711_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8711_socdev;
++ struct wm8711_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(i2c, codec);
++
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8711_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise WM8711\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++
++}
++
++static int wm8711_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8711_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8711_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8711_i2c_driver = {
++ .driver = {
++ .name = "WM8711 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8711,
++ .attach_adapter = wm8711_i2c_attach,
++ .detach_client = wm8711_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8711",
++ .driver = &wm8711_i2c_driver,
++};
++#endif
++
++static int wm8711_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8711_setup_data *setup;
++ struct snd_soc_codec* codec;
++ struct wm8711_priv *wm8711;
++ int ret = 0;
++
++ info("WM8711 Audio Codec %s", WM8711_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
++ if (wm8711 == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++
++ codec->private_data = wm8711;
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8711_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8711_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8711_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8711_i2c_driver);
++#endif
++ kfree(codec->private_data);
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8711 = {
++ .probe = wm8711_probe,
++ .remove = wm8711_remove,
++ .suspend = wm8711_suspend,
++ .resume = wm8711_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
++
++MODULE_DESCRIPTION("ASoC WM8711 driver");
++MODULE_AUTHOR("Mike Arthur");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8711.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8711.h 2007-07-16 15:07:34.512304012 +0200
+@@ -0,0 +1,42 @@
++/*
++ * wm8711.h -- WM8711 Soc Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics
++ *
++ * Author: Mike Arthur <linux at wolfsonmicro.com>
++ *
++ * Based on wm8731.h
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8711_H
++#define _WM8711_H
++
++/* WM8711 register space */
++
++#define WM8711_LOUT1V 0x02
++#define WM8711_ROUT1V 0x03
++#define WM8711_APANA 0x04
++#define WM8711_APDIGI 0x05
++#define WM8711_PWR 0x06
++#define WM8711_IFACE 0x07
++#define WM8711_SRATE 0x08
++#define WM8711_ACTIVE 0x09
++#define WM8711_RESET 0x0f
++
++#define WM8711_CACHEREGNUM 8
++
++#define WM8711_SYSCLK 0
++#define WM8711_DAI 0
++
++struct wm8711_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8711_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8711;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm8980.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8980.c 2007-07-16 15:07:34.532305155 +0200
+@@ -0,0 +1,923 @@
++/*
++ * wm8980.c -- WM8980 ALSA Soc Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ *
++ * Authors:
++ * Mike Arthur <linux at wolfsonmicro.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8980.h"
++
++#define AUDIO_NAME "wm8980"
++#define WM8980_VERSION "0.3"
++
++/*
++ * Debug
++ */
++
++#define WM8980_DEBUG 0
++
++#ifdef WM8980_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8980;
++
++/*
++ * wm8980 register cache
++ * We can't read the WM8980 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8980_reg[WM8980_CACHEREGNUM] = {
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0050, 0x0000, 0x0140, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x00ff,
++ 0x00ff, 0x0000, 0x0100, 0x00ff,
++ 0x00ff, 0x0000, 0x012c, 0x002c,
++ 0x002c, 0x002c, 0x002c, 0x0000,
++ 0x0032, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0038, 0x000b, 0x0032, 0x0000,
++ 0x0008, 0x000c, 0x0093, 0x00e9,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0033, 0x0010, 0x0010, 0x0100,
++ 0x0100, 0x0002, 0x0001, 0x0001,
++ 0x0039, 0x0039, 0x0039, 0x0039,
++ 0x0001, 0x0001,
++};
++
++/*
++ * read wm8980 register cache
++ */
++static inline unsigned int wm8980_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8980_RESET)
++ return 0;
++ if (reg >= WM8980_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8980 register cache
++ */
++static inline void wm8980_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8980_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8980 register space
++ */
++static int wm8980_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8980 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8980_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -1;
++}
++
++#define wm8980_reset(c) wm8980_write(c, WM8980_RESET, 0)
++
++static const char *wm8980_companding[] = {"Off", "NC", "u-law", "A-law" };
++static const char *wm8980_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
++static const char *wm8980_eqmode[] = {"Capture", "Playback" };
++static const char *wm8980_bw[] = {"Narrow", "Wide" };
++static const char *wm8980_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
++static const char *wm8980_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
++static const char *wm8980_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
++static const char *wm8980_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
++static const char *wm8980_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
++static const char *wm8980_alc[] =
++ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
++
++static const struct soc_enum wm8980_enum[] = {
++ SOC_ENUM_SINGLE(WM8980_COMP, 1, 4, wm8980_companding), /* adc */
++ SOC_ENUM_SINGLE(WM8980_COMP, 3, 4, wm8980_companding), /* dac */
++ SOC_ENUM_SINGLE(WM8980_DAC, 4, 4, wm8980_deemp),
++ SOC_ENUM_SINGLE(WM8980_EQ1, 8, 2, wm8980_eqmode),
++
++ SOC_ENUM_SINGLE(WM8980_EQ1, 5, 4, wm8980_eq1),
++ SOC_ENUM_SINGLE(WM8980_EQ2, 8, 2, wm8980_bw),
++ SOC_ENUM_SINGLE(WM8980_EQ2, 5, 4, wm8980_eq2),
++ SOC_ENUM_SINGLE(WM8980_EQ3, 8, 2, wm8980_bw),
++
++ SOC_ENUM_SINGLE(WM8980_EQ3, 5, 4, wm8980_eq3),
++ SOC_ENUM_SINGLE(WM8980_EQ4, 8, 2, wm8980_bw),
++ SOC_ENUM_SINGLE(WM8980_EQ4, 5, 4, wm8980_eq4),
++ SOC_ENUM_SINGLE(WM8980_EQ5, 8, 2, wm8980_bw),
++
++ SOC_ENUM_SINGLE(WM8980_EQ5, 5, 4, wm8980_eq5),
++ SOC_ENUM_SINGLE(WM8980_ALC3, 8, 2, wm8980_alc),
++};
++
++static const struct snd_kcontrol_new wm8980_snd_controls[] = {
++SOC_SINGLE("Digital Loopback Switch", WM8980_COMP, 0, 1, 0),
++
++SOC_ENUM("ADC Companding", wm8980_enum[0]),
++SOC_ENUM("DAC Companding", wm8980_enum[1]),
++
++SOC_SINGLE("Jack Detection Enable", WM8980_JACK1, 6, 1, 0),
++
++SOC_SINGLE("DAC Right Inversion Switch", WM8980_DAC, 1, 1, 0),
++SOC_SINGLE("DAC Left Inversion Switch", WM8980_DAC, 0, 1, 0),
++
++SOC_SINGLE("Left Playback Volume", WM8980_DACVOLL, 0, 127, 0),
++SOC_SINGLE("Right Playback Volume", WM8980_DACVOLR, 0, 127, 0),
++
++SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Cut Off", WM8980_ADC, 4, 7, 0),
++SOC_SINGLE("Right ADC Inversion Switch", WM8980_ADC, 1, 1, 0),
++SOC_SINGLE("Left ADC Inversion Switch", WM8980_ADC, 0, 1, 0),
++
++SOC_SINGLE("Left Capture Volume", WM8980_ADCVOLL, 0, 127, 0),
++SOC_SINGLE("Right Capture Volume", WM8980_ADCVOLR, 0, 127, 0),
++
++SOC_ENUM("Equaliser Function", wm8980_enum[3]),
++SOC_ENUM("EQ1 Cut Off", wm8980_enum[4]),
++SOC_SINGLE("EQ1 Volume", WM8980_EQ1, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ2 Bandwith", wm8980_enum[5]),
++SOC_ENUM("EQ2 Cut Off", wm8980_enum[6]),
++SOC_SINGLE("EQ2 Volume", WM8980_EQ2, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ3 Bandwith", wm8980_enum[7]),
++SOC_ENUM("EQ3 Cut Off", wm8980_enum[8]),
++SOC_SINGLE("EQ3 Volume", WM8980_EQ3, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ4 Bandwith", wm8980_enum[9]),
++SOC_ENUM("EQ4 Cut Off", wm8980_enum[10]),
++SOC_SINGLE("EQ4 Volume", WM8980_EQ4, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ5 Bandwith", wm8980_enum[11]),
++SOC_ENUM("EQ5 Cut Off", wm8980_enum[12]),
++SOC_SINGLE("EQ5 Volume", WM8980_EQ5, 0, 31, 1),
++
++SOC_SINGLE("DAC Playback Limiter Switch", WM8980_DACLIM1, 8, 1, 0),
++SOC_SINGLE("DAC Playback Limiter Decay", WM8980_DACLIM1, 4, 15, 0),
++SOC_SINGLE("DAC Playback Limiter Attack", WM8980_DACLIM1, 0, 15, 0),
++
++SOC_SINGLE("DAC Playback Limiter Threshold", WM8980_DACLIM2, 4, 7, 0),
++SOC_SINGLE("DAC Playback Limiter Boost", WM8980_DACLIM2, 0, 15, 0),
++
++SOC_SINGLE("ALC Enable Switch", WM8980_ALC1, 8, 1, 0),
++SOC_SINGLE("ALC Capture Max Gain", WM8980_ALC1, 3, 7, 0),
++SOC_SINGLE("ALC Capture Min Gain", WM8980_ALC1, 0, 7, 0),
++
++SOC_SINGLE("ALC Capture ZC Switch", WM8980_ALC2, 8, 1, 0),
++SOC_SINGLE("ALC Capture Hold", WM8980_ALC2, 4, 7, 0),
++SOC_SINGLE("ALC Capture Target", WM8980_ALC2, 0, 15, 0),
++
++SOC_ENUM("ALC Capture Mode", wm8980_enum[13]),
++SOC_SINGLE("ALC Capture Decay", WM8980_ALC3, 4, 15, 0),
++SOC_SINGLE("ALC Capture Attack", WM8980_ALC3, 0, 15, 0),
++
++SOC_SINGLE("ALC Capture Noise Gate Switch", WM8980_NGATE, 3, 1, 0),
++SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8980_NGATE, 0, 7, 0),
++
++SOC_SINGLE("Left Capture PGA ZC Switch", WM8980_INPPGAL, 7, 1, 0),
++SOC_SINGLE("Left Capture PGA Volume", WM8980_INPPGAL, 0, 63, 0),
++
++SOC_SINGLE("Right Capture PGA ZC Switch", WM8980_INPPGAR, 7, 1, 0),
++SOC_SINGLE("Right Capture PGA Volume", WM8980_INPPGAR, 0, 63, 0),
++
++SOC_SINGLE("Left Headphone Playback ZC Switch", WM8980_HPVOLL, 7, 1, 0),
++SOC_SINGLE("Left Headphone Playback Switch", WM8980_HPVOLL, 6, 1, 1),
++SOC_SINGLE("Left Headphone Playback Volume", WM8980_HPVOLL, 0, 63, 0),
++
++SOC_SINGLE("Right Headphone Playback ZC Switch", WM8980_HPVOLR, 7, 1, 0),
++SOC_SINGLE("Right Headphone Playback Switch", WM8980_HPVOLR, 6, 1, 1),
++SOC_SINGLE("Right Headphone Playback Volume", WM8980_HPVOLR, 0, 63, 0),
++
++SOC_SINGLE("Left Speaker Playback ZC Switch", WM8980_SPKVOLL, 7, 1, 0),
++SOC_SINGLE("Left Speaker Playback Switch", WM8980_SPKVOLL, 6, 1, 1),
++SOC_SINGLE("Left Speaker Playback Volume", WM8980_SPKVOLL, 0, 63, 0),
++
++SOC_SINGLE("Right Speaker Playback ZC Switch", WM8980_SPKVOLR, 7, 1, 0),
++SOC_SINGLE("Right Speaker Playback Switch", WM8980_SPKVOLR, 6, 1, 1),
++SOC_SINGLE("Right Speaker Playback Volume", WM8980_SPKVOLR, 0, 63, 0),
++
++SOC_DOUBLE_R("Capture Boost(+20dB)", WM8980_ADCBOOSTL, WM8980_ADCBOOSTR,
++ 8, 1, 0),
++};
++
++/* add non dapm controls */
++static int wm8980_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8980_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8980_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Left Output Mixer */
++static const snd_kcontrol_new_t wm8980_left_mixer_controls[] = {
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_OUTPUT, 6, 1, 1),
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_MIXL, 0, 1, 1),
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXL, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXL, 5, 1, 0),
++};
++
++/* Right Output Mixer */
++static const snd_kcontrol_new_t wm8980_right_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_OUTPUT, 5, 1, 1),
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_MIXR, 0, 1, 1),
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXR, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXR, 5, 1, 0),
++};
++
++/* Left AUX Input boost vol */
++static const snd_kcontrol_new_t wm8980_laux_boost_controls =
++SOC_DAPM_SINGLE("Left Aux Volume", WM8980_ADCBOOSTL, 0, 3, 0);
++
++/* Right AUX Input boost vol */
++static const snd_kcontrol_new_t wm8980_raux_boost_controls =
++SOC_DAPM_SINGLE("Right Aux Volume", WM8980_ADCBOOSTR, 0, 3, 0);
++
++/* Left Input boost vol */
++static const snd_kcontrol_new_t wm8980_lmic_boost_controls =
++SOC_DAPM_SINGLE("Left Input Volume", WM8980_ADCBOOSTL, 4, 3, 0);
++
++/* Right Input boost vol */
++static const snd_kcontrol_new_t wm8980_rmic_boost_controls =
++SOC_DAPM_SINGLE("Right Input Volume", WM8980_ADCBOOSTR, 4, 3, 0);
++
++/* Left Aux In to PGA */
++static const snd_kcontrol_new_t wm8980_laux_capture_boost_controls =
++SOC_DAPM_SINGLE("Left Capture Switch", WM8980_ADCBOOSTL, 8, 1, 0);
++
++/* Right Aux In to PGA */
++static const snd_kcontrol_new_t wm8980_raux_capture_boost_controls =
++SOC_DAPM_SINGLE("Right Capture Switch", WM8980_ADCBOOSTR, 8, 1, 0);
++
++/* Left Input P In to PGA */
++static const snd_kcontrol_new_t wm8980_lmicp_capture_boost_controls =
++SOC_DAPM_SINGLE("Left Input P Capture Boost Switch", WM8980_INPUT, 0, 1, 0);
++
++/* Right Input P In to PGA */
++static const snd_kcontrol_new_t wm8980_rmicp_capture_boost_controls =
++SOC_DAPM_SINGLE("Right Input P Capture Boost Switch", WM8980_INPUT, 4, 1, 0);
++
++/* Left Input N In to PGA */
++static const snd_kcontrol_new_t wm8980_lmicn_capture_boost_controls =
++SOC_DAPM_SINGLE("Left Input N Capture Boost Switch", WM8980_INPUT, 1, 1, 0);
++
++/* Right Input N In to PGA */
++static const snd_kcontrol_new_t wm8980_rmicn_capture_boost_controls =
++SOC_DAPM_SINGLE("Right Input N Capture Boost Switch", WM8980_INPUT, 5, 1, 0);
++
++// TODO Widgets
++static const struct snd_soc_dapm_widget wm8980_dapm_widgets[] = {
++#if 0
++//SND_SOC_DAPM_MUTE("Mono Mute", WM8980_MONOMIX, 6, 0),
++//SND_SOC_DAPM_MUTE("Speaker Mute", WM8980_SPKMIX, 6, 0),
++
++SND_SOC_DAPM_MIXER("Speaker Mixer", WM8980_POWER3, 2, 0,
++ &wm8980_speaker_mixer_controls[0],
++ ARRAY_SIZE(wm8980_speaker_mixer_controls)),
++SND_SOC_DAPM_MIXER("Mono Mixer", WM8980_POWER3, 3, 0,
++ &wm8980_mono_mixer_controls[0],
++ ARRAY_SIZE(wm8980_mono_mixer_controls)),
++SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8980_POWER3, 0, 0),
++SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8980_POWER3, 0, 0),
++SND_SOC_DAPM_PGA("Aux Input", WM8980_POWER1, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkN Out", WM8980_POWER3, 5, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkP Out", WM8980_POWER3, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mono Out", WM8980_POWER3, 7, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mic PGA", WM8980_POWER2, 2, 0, NULL, 0),
++
++SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
++ &wm8980_aux_boost_controls, 1),
++SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
++ &wm8980_mic_boost_controls, 1),
++SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
++ &wm8980_capture_boost_controls),
++
++SND_SOC_DAPM_MIXER("Boost Mixer", WM8980_POWER2, 4, 0, NULL, 0),
++
++SND_SOC_DAPM_MICBIAS("Mic Bias", WM8980_POWER1, 4, 0),
++
++SND_SOC_DAPM_INPUT("MICN"),
++SND_SOC_DAPM_INPUT("MICP"),
++SND_SOC_DAPM_INPUT("AUX"),
++SND_SOC_DAPM_OUTPUT("MONOOUT"),
++SND_SOC_DAPM_OUTPUT("SPKOUTP"),
++SND_SOC_DAPM_OUTPUT("SPKOUTN"),
++#endif
++};
++
++static const char *audio_map[][3] = {
++ /* Mono output mixer */
++ {"Mono Mixer", "PCM Playback Switch", "DAC"},
++ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Speaker output mixer */
++ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
++ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Outputs */
++ {"Mono Out", NULL, "Mono Mixer"},
++ {"MONOOUT", NULL, "Mono Out"},
++ {"SpkN Out", NULL, "Speaker Mixer"},
++ {"SpkP Out", NULL, "Speaker Mixer"},
++ {"SPKOUTN", NULL, "SpkN Out"},
++ {"SPKOUTP", NULL, "SpkP Out"},
++
++ /* Boost Mixer */
++ {"Boost Mixer", NULL, "ADC"},
++ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
++ {"Aux Boost", "Aux Volume", "Boost Mixer"},
++ {"Capture Boost", "Capture Switch", "Boost Mixer"},
++ {"Mic Boost", "Mic Volume", "Boost Mixer"},
++
++ /* Inputs */
++ {"MICP", NULL, "Mic Boost"},
++ {"MICN", NULL, "Mic PGA"},
++ {"Mic PGA", NULL, "Capture Boost"},
++ {"AUX", NULL, "Aux Input"},
++
++ /* */
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8980_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8980_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8980_dapm_widgets[i]);
++ }
++
++ /* set up audio path map */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
++ audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct pll_ {
++ unsigned int in_hz, out_hz;
++ unsigned int pre:4; /* prescale - 1 */
++ unsigned int n:4;
++ unsigned int k;
++};
++
++struct pll_ pll[] = {
++ {12000000, 11289600, 0, 7, 0x86c220},
++ {12000000, 12288000, 0, 8, 0x3126e8},
++ {13000000, 11289600, 0, 6, 0xf28bd4},
++ {13000000, 12288000, 0, 7, 0x8fd525},
++ {12288000, 11289600, 0, 7, 0x59999a},
++ {11289600, 12288000, 0, 8, 0x80dee9},
++ /* TODO: liam - add more entries */
++};
++
++static int wm8980_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ int i;
++ u16 reg;
++
++ if(freq_in == 0 || freq_out == 0) {
++ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
++ wm8980_write(codec, WM8980_POWER1, reg & 0x1df);
++ return 0;
++ }
++
++ for(i = 0; i < ARRAY_SIZE(pll); i++) {
++ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
++ wm8980_write(codec, WM8980_PLLN, (pll[i].pre << 4) | pll[i].n);
++ wm8980_write(codec, WM8980_PLLK1, pll[i].k >> 18);
++ wm8980_write(codec, WM8980_PLLK1, (pll[i].k >> 9) && 0x1ff);
++ wm8980_write(codec, WM8980_PLLK1, pll[i].k && 0x1ff);
++ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
++ wm8980_write(codec, WM8980_POWER1, reg | 0x020);
++ return 0;
++ }
++ }
++ return -EINVAL;
++}
++
++static int wm8980_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = wm8980_read_reg_cache(codec, WM8980_IFACE) & 0x3;
++ u16 clk = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0xfffe;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ clk |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0008;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x00018;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0180;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0100;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0080;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8980_write(codec, WM8980_IFACE, iface);
++ return 0;
++}
++
++static int wm8980_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8980_read_reg_cache(codec, WM8980_IFACE) & 0xff9f;
++ u16 adn = wm8980_read_reg_cache(codec, WM8980_ADD) & 0x1f1;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0020;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0040;
++ break;
++ }
++
++ /* filter coefficient */
++ switch (params_rate(params)) {
++ case SNDRV_PCM_RATE_8000:
++ adn |= 0x5 << 1;
++ break;
++ case SNDRV_PCM_RATE_11025:
++ adn |= 0x4 << 1;
++ break;
++ case SNDRV_PCM_RATE_16000:
++ adn |= 0x3 << 1;
++ break;
++ case SNDRV_PCM_RATE_22050:
++ adn |= 0x2 << 1;
++ break;
++ case SNDRV_PCM_RATE_32000:
++ adn |= 0x1 << 1;
++ break;
++ }
++
++ /* set iface */
++ wm8980_write(codec, WM8980_IFACE, iface);
++ wm8980_write(codec, WM8980_ADD, adn);
++ return 0;
++}
++
++static int wm8980_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8980_MCLKDIV:
++ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x11f;
++ wm8980_write(codec, WM8980_CLOCK, reg | div);
++ break;
++ case WM8980_BCLKDIV:
++ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x1c7;
++ wm8980_write(codec, WM8980_CLOCK, reg | div);
++ break;
++ case WM8980_OPCLKDIV:
++ reg = wm8980_read_reg_cache(codec, WM8980_GPIO) & 0x1cf;
++ wm8980_write(codec, WM8980_GPIO, reg | div);
++ break;
++ case WM8980_DACOSR:
++ reg = wm8980_read_reg_cache(codec, WM8980_DAC) & 0x1f7;
++ wm8980_write(codec, WM8980_DAC, reg | div);
++ break;
++ case WM8980_ADCOSR:
++ reg = wm8980_read_reg_cache(codec, WM8980_ADC) & 0x1f7;
++ wm8980_write(codec, WM8980_ADC, reg | div);
++ break;
++ case WM8980_MCLKSEL:
++ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x0ff;
++ wm8980_write(codec, WM8980_CLOCK, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int wm8980_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8980_read_reg_cache(codec, WM8980_DAC) & 0xffbf;
++
++ if(mute)
++ wm8980_write(codec, WM8980_DAC, mute_reg | 0x40);
++ else
++ wm8980_write(codec, WM8980_DAC, mute_reg);
++
++ return 0;
++}
++
++/* TODO: liam need to make this lower power with dapm */
++static int wm8980_dapm_event(struct snd_soc_codec *codec, int event)
++{
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ wm8980_write(codec, WM8980_POWER1, 0x1ff);
++ wm8980_write(codec, WM8980_POWER2, 0x1ff);
++ wm8980_write(codec, WM8980_POWER3, 0x1ff);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8980_write(codec, WM8980_POWER1, 0x0);
++ wm8980_write(codec, WM8980_POWER2, 0x0);
++ wm8980_write(codec, WM8980_POWER3, 0x0);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8980_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++#define WM8980_FORMATS \
++ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
++ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE)
++
++struct snd_soc_codec_dai wm8980_dai = {
++ .name = "WM8980 HiFi",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8980_RATES,
++ .formats = WM8980_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8980_RATES,
++ .formats = WM8980_FORMATS,},
++ .ops = {
++ .hw_params = wm8980_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8980_mute,
++ .set_fmt = wm8980_set_dai_fmt,
++ .set_clkdiv = wm8980_set_dai_clkdiv,
++ .set_pll = wm8980_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8980_dai);
++
++static int wm8980_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8980_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8980_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8980_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8980 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8980_init(struct snd_soc_device* socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "WM8980";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8980_read_reg_cache;
++ codec->write = wm8980_write;
++ codec->dapm_event = wm8980_dapm_event;
++ codec->dai = &wm8980_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8980_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8980_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8980_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8980_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8980_reg);
++
++ wm8980_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "wm8980: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8980_add_controls(codec);
++ wm8980_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8980: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8980_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8980 2 wire address is 0x1a
++ */
++#define I2C_DRIVERID_WM8980 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8980_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8980_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8980_socdev;
++ struct wm8980_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(i2c, codec);
++
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if(ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8980_init(socdev);
++ if(ret < 0) {
++ err("failed to initialise WM8980\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++
++}
++
++static int wm8980_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec *codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8980_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8980_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8980_i2c_driver = {
++ .driver = {
++ .name = "WM8980 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8980,
++ .attach_adapter = wm8980_i2c_attach,
++ .detach_client = wm8980_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8980",
++ .driver = &wm8980_i2c_driver,
++};
++#endif
++
++static int wm8980_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8980_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8980 Audio Codec %s", WM8980_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8980_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8980_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8980_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8980_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8980 = {
++ .probe = wm8980_probe,
++ .remove = wm8980_remove,
++ .suspend = wm8980_suspend,
++ .resume = wm8980_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8980);
++
++MODULE_DESCRIPTION("ASoC WM8980 driver");
++MODULE_AUTHOR("Mike Arthur");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8980.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8980.h 2007-07-16 15:07:34.564306976 +0200
+@@ -0,0 +1,116 @@
++/*
++ * wm8980.h -- WM8980 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8980_H
++#define _WM8980_H
++
++/* WM8980 register space */
++
++#define WM8980_RESET 0x0
++#define WM8980_POWER1 0x1
++#define WM8980_POWER2 0x2
++#define WM8980_POWER3 0x3
++#define WM8980_IFACE 0x4
++#define WM8980_COMP 0x5
++#define WM8980_CLOCK 0x6
++#define WM8980_ADD 0x7
++#define WM8980_GPIO 0x8
++#define WM8980_JACK1 0x9
++#define WM8980_DAC 0xa
++#define WM8980_DACVOLL 0xb
++#define WM8980_DACVOLR 0xc
++#define WM8980_JACK2 0xd
++#define WM8980_ADC 0xe
++#define WM8980_ADCVOLL 0xf
++#define WM8980_ADCVOLR 0x10
++#define WM8980_EQ1 0x12
++#define WM8980_EQ2 0x13
++#define WM8980_EQ3 0x14
++#define WM8980_EQ4 0x15
++#define WM8980_EQ5 0x16
++#define WM8980_DACLIM1 0x18
++#define WM8980_DACLIM2 0x19
++#define WM8980_NOTCH1 0x1b
++#define WM8980_NOTCH2 0x1c
++#define WM8980_NOTCH3 0x1d
++#define WM8980_NOTCH4 0x1e
++#define WM8980_ALC1 0x20
++#define WM8980_ALC2 0x21
++#define WM8980_ALC3 0x22
++#define WM8980_NGATE 0x23
++#define WM8980_PLLN 0x24
++#define WM8980_PLLK1 0x25
++#define WM8980_PLLK2 0x26
++#define WM8980_PLLK3 0x27
++#define WM8980_VIDEO 0x28
++#define WM8980_3D 0x29
++#define WM8980_BEEP 0x2b
++#define WM8980_INPUT 0x2c
++#define WM8980_INPPGAL 0x2d
++#define WM8980_INPPGAR 0x2e
++#define WM8980_ADCBOOSTL 0x2f
++#define WM8980_ADCBOOSTR 0x30
++#define WM8980_OUTPUT 0x31
++#define WM8980_MIXL 0x32
++#define WM8980_MIXR 0x33
++#define WM8980_HPVOLL 0x34
++#define WM8980_HPVOLR 0x35
++#define WM8980_SPKVOLL 0x36
++#define WM8980_SPKVOLR 0x37
++#define WM8980_OUT3MIX 0x38
++#define WM8980_MONOMIX 0x39
++
++#define WM8980_CACHEREGNUM 58
++
++/*
++ * WM8980 Clock dividers
++ */
++#define WM8980_MCLKDIV 0
++#define WM8980_BCLKDIV 1
++#define WM8980_OPCLKDIV 2
++#define WM8980_DACOSR 3
++#define WM8980_ADCOSR 4
++#define WM8980_MCLKSEL 5
++
++#define WM8980_MCLK_MCLK (0 << 8)
++#define WM8980_MCLK_PLL (1 << 8)
++
++#define WM8980_MCLK_DIV_1 (0 << 5)
++#define WM8980_MCLK_DIV_1_5 (1 << 5)
++#define WM8980_MCLK_DIV_2 (2 << 5)
++#define WM8980_MCLK_DIV_3 (3 << 5)
++#define WM8980_MCLK_DIV_4 (4 << 5)
++#define WM8980_MCLK_DIV_5_5 (5 << 5)
++#define WM8980_MCLK_DIV_6 (6 << 5)
++
++#define WM8980_BCLK_DIV_1 (0 << 2)
++#define WM8980_BCLK_DIV_2 (1 << 2)
++#define WM8980_BCLK_DIV_4 (2 << 2)
++#define WM8980_BCLK_DIV_8 (3 << 2)
++#define WM8980_BCLK_DIV_16 (4 << 2)
++#define WM8980_BCLK_DIV_32 (5 << 2)
++
++#define WM8980_DACOSR_64 (0 << 3)
++#define WM8980_DACOSR_128 (1 << 3)
++
++#define WM8980_ADCOSR_64 (0 << 3)
++#define WM8980_ADCOSR_128 (1 << 3)
++
++#define WM8980_OPCLK_DIV_1 (0 << 4)
++#define WM8980_OPCLK_DIV_2 (1 << 4)
++#define WM8980_OPCLK_DIV_3 (2 << 4)
++#define WM8980_OPCLK_DIV_4 (3 << 4)
++
++struct wm8980_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8980_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8980;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm8510.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8510.c 2007-07-16 15:07:34.584308116 +0200
+@@ -0,0 +1,860 @@
++/*
++ * wm8510.c -- WM8510 ALSA Soc Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ *
++ * Author: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8510.h"
++
++#define AUDIO_NAME "wm8510"
++#define WM8510_VERSION "0.6"
++
++/*
++ * Debug
++ */
++
++#define WM8510_DEBUG 0
++
++#ifdef WM8510_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8510;
++
++/*
++ * wm8510 register cache
++ * We can't read the WM8510 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0050, 0x0000, 0x0140, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x00ff,
++ 0x0000, 0x0000, 0x0100, 0x00ff,
++ 0x0000, 0x0000, 0x012c, 0x002c,
++ 0x002c, 0x002c, 0x002c, 0x0000,
++ 0x0032, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0038, 0x000b, 0x0032, 0x0000,
++ 0x0008, 0x000c, 0x0093, 0x00e9,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0003, 0x0010, 0x0000, 0x0000,
++ 0x0000, 0x0002, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0039, 0x0000,
++ 0x0000,
++};
++
++/*
++ * read wm8510 register cache
++ */
++static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec * codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8510_RESET)
++ return 0;
++ if (reg >= WM8510_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8510 register cache
++ */
++static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8510_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8510 register space
++ */
++static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8510 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8510_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
++
++static const char *wm8510_companding[] = {"Off", "NC", "u-law", "A-law" };
++static const char *wm8510_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
++static const char *wm8510_alc[] = {"ALC", "Limiter" };
++
++static const struct soc_enum wm8510_enum[] = {
++ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
++ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
++ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
++ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
++};
++
++static const struct snd_kcontrol_new wm8510_snd_controls[] = {
++
++SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
++
++SOC_ENUM("DAC Companding", wm8510_enum[1]),
++SOC_ENUM("ADC Companding", wm8510_enum[0]),
++
++SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
++SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
++
++SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
++
++SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
++SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
++
++SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
++
++SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
++SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
++SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
++
++SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
++SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
++
++SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
++SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
++SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
++
++SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
++SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
++SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
++
++SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
++SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
++SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
++
++SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
++SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
++
++SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
++SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
++
++SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
++SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
++SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
++
++SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
++SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 0),
++};
++
++/* add non dapm controls */
++static int wm8510_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8510_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Speaker Output Mixer */
++static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 1),
++};
++
++/* Mono Output Mixer */
++static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
++SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 1),
++};
++
++/* AUX Input boost vol */
++static const struct snd_kcontrol_new wm8510_aux_boost_controls =
++SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0);
++
++/* Mic Input boost vol */
++static const struct snd_kcontrol_new wm8510_mic_boost_controls =
++SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0);
++
++/* Capture boost switch */
++static const struct snd_kcontrol_new wm8510_capture_boost_controls =
++SOC_DAPM_SINGLE("Capture Boost Switch", WM8510_INPPGA, 6, 1, 0);
++
++/* Aux In to PGA */
++static const struct snd_kcontrol_new wm8510_aux_capture_boost_controls =
++SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8510_INPPGA, 2, 1, 0);
++
++/* Mic P In to PGA */
++static const struct snd_kcontrol_new wm8510_micp_capture_boost_controls =
++SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8510_INPPGA, 0, 1, 0);
++
++/* Mic N In to PGA */
++static const struct snd_kcontrol_new wm8510_micn_capture_boost_controls =
++SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8510_INPPGA, 1, 1, 0);
++
++static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
++SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
++ &wm8510_speaker_mixer_controls[0],
++ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
++SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
++ &wm8510_mono_mixer_controls[0],
++ ARRAY_SIZE(wm8510_mono_mixer_controls)),
++SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
++SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER3, 0, 0),
++SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, NULL, 0),
++
++SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
++ &wm8510_aux_boost_controls, 1),
++SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
++ &wm8510_mic_boost_controls, 1),
++SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
++ &wm8510_capture_boost_controls),
++
++SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, NULL, 0),
++
++SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
++
++SND_SOC_DAPM_INPUT("MICN"),
++SND_SOC_DAPM_INPUT("MICP"),
++SND_SOC_DAPM_INPUT("AUX"),
++SND_SOC_DAPM_OUTPUT("MONOOUT"),
++SND_SOC_DAPM_OUTPUT("SPKOUTP"),
++SND_SOC_DAPM_OUTPUT("SPKOUTN"),
++};
++
++static const char *audio_map[][3] = {
++ /* Mono output mixer */
++ {"Mono Mixer", "PCM Playback Switch", "DAC"},
++ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Speaker output mixer */
++ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
++ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Outputs */
++ {"Mono Out", NULL, "Mono Mixer"},
++ {"MONOOUT", NULL, "Mono Out"},
++ {"SpkN Out", NULL, "Speaker Mixer"},
++ {"SpkP Out", NULL, "Speaker Mixer"},
++ {"SPKOUTN", NULL, "SpkN Out"},
++ {"SPKOUTP", NULL, "SpkP Out"},
++
++ /* Boost Mixer */
++ {"Boost Mixer", NULL, "ADC"},
++ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
++ {"Aux Boost", "Aux Volume", "Boost Mixer"},
++ {"Capture Boost", "Capture Switch", "Boost Mixer"},
++ {"Mic Boost", "Mic Volume", "Boost Mixer"},
++
++ /* Inputs */
++ {"MICP", NULL, "Mic Boost"},
++ {"MICN", NULL, "Mic PGA"},
++ {"Mic PGA", NULL, "Capture Boost"},
++ {"AUX", NULL, "Aux Input"},
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8510_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8510_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8510_dapm_widgets[i]);
++ }
++
++ /* set up audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct pll_ {
++ unsigned int pre_div:4; /* prescale - 1 */
++ unsigned int n:4;
++ unsigned int k;
++};
++
++static struct pll_ pll_div;
++
++/* The size in bits of the pll divide multiplied by 10
++ * to allow rounding later */
++#define FIXED_PLL_SIZE ((1 << 24) * 10)
++
++static void pll_factors(unsigned int target, unsigned int source)
++{
++ unsigned long long Kpart;
++ unsigned int K, Ndiv, Nmod;
++
++ Ndiv = target / source;
++ if (Ndiv < 6) {
++ source >>= 1;
++ pll_div.pre_div = 1;
++ Ndiv = target / source;
++ } else
++ pll_div.pre_div = 0;
++
++ if ((Ndiv < 6) || (Ndiv > 12))
++ printk(KERN_WARNING
++ "WM8510 N value outwith recommended range! N = %d\n",Ndiv);
++
++ pll_div.n = Ndiv;
++ Nmod = target % source;
++ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
++
++ do_div(Kpart, source);
++
++ K = Kpart & 0xFFFFFFFF;
++
++ /* Check if we need to round */
++ if ((K % 10) >= 5)
++ K += 5;
++
++ /* Move down to proper range now rounding is done */
++ K /= 10;
++
++ pll_div.k = K;
++}
++
++static int wm8510_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ if(freq_in == 0 || freq_out == 0) {
++ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
++ wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
++ return 0;
++ }
++
++ pll_factors(freq_out*8, freq_in);
++
++ wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
++ wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
++ wm8510_write(codec, WM8510_PLLK1, (pll_div.k >> 9) && 0x1ff);
++ wm8510_write(codec, WM8510_PLLK1, pll_div.k && 0x1ff);
++ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
++ wm8510_write(codec, WM8510_POWER1, reg | 0x020);
++ return 0;
++
++}
++
++/*
++ * Configure WM8510 clock dividers.
++ */
++static int wm8510_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8510_OPCLKDIV:
++ reg = wm8510_read_reg_cache(codec, WM8510_GPIO & 0x1cf);
++ wm8510_write(codec, WM8510_GPIO, reg | div);
++ break;
++ case WM8510_MCLKDIV:
++ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1f);
++ wm8510_write(codec, WM8510_CLOCK, reg | div);
++ break;
++ case WM8510_ADCCLK:
++ reg = wm8510_read_reg_cache(codec, WM8510_ADC & 0x1f7);
++ wm8510_write(codec, WM8510_ADC, reg | div);
++ break;
++ case WM8510_DACCLK:
++ reg = wm8510_read_reg_cache(codec, WM8510_DAC & 0x1f7);
++ wm8510_write(codec, WM8510_DAC, reg | div);
++ break;
++ case WM8510_BCLKDIV:
++ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1e3);
++ wm8510_write(codec, WM8510_CLOCK, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int wm8510_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++ u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ clk |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0008;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x00018;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0180;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0100;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0080;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8510_write(codec, WM8510_IFACE, iface);
++ wm8510_write(codec, WM8510_CLOCK, clk);
++ return 0;
++}
++
++static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x19f;
++ u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0020;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0040;
++ break;
++ case SNDRV_PCM_FORMAT_S32_LE:
++ iface |= 0x0060;
++ break;
++ }
++
++ /* filter coefficient */
++ switch (params_rate(params)) {
++ case SNDRV_PCM_RATE_8000:
++ adn |= 0x5 << 1;
++ break;
++ case SNDRV_PCM_RATE_11025:
++ adn |= 0x4 << 1;
++ break;
++ case SNDRV_PCM_RATE_16000:
++ adn |= 0x3 << 1;
++ break;
++ case SNDRV_PCM_RATE_22050:
++ adn |= 0x2 << 1;
++ break;
++ case SNDRV_PCM_RATE_32000:
++ adn |= 0x1 << 1;
++ break;
++ case SNDRV_PCM_RATE_44100:
++ break;
++ }
++
++ wm8510_write(codec, WM8510_IFACE, iface);
++ wm8510_write(codec, WM8510_ADD, adn);
++ return 0;
++}
++
++static int wm8510_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
++
++ if(mute)
++ wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
++ else
++ wm8510_write(codec, WM8510_DAC, mute_reg);
++ return 0;
++}
++
++/* liam need to make this lower power with dapm */
++static int wm8510_dapm_event(struct snd_soc_codec *codec, int event)
++{
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ wm8510_write(codec, WM8510_POWER1, 0x1ff);
++ wm8510_write(codec, WM8510_POWER2, 0x1ff);
++ wm8510_write(codec, WM8510_POWER3, 0x1ff);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8510_write(codec, WM8510_POWER1, 0x0);
++ wm8510_write(codec, WM8510_POWER2, 0x0);
++ wm8510_write(codec, WM8510_POWER3, 0x0);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8510_dai = {
++ .name = "WM8510 HiFi",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM8510_RATES,
++ .formats = WM8510_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM8510_RATES,
++ .formats = WM8510_FORMATS,},
++ .ops = {
++ .hw_params = wm8510_pcm_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8510_mute,
++ .set_fmt = wm8510_set_dai_fmt,
++ .set_clkdiv = wm8510_set_dai_clkdiv,
++ .set_pll = wm8510_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8510_dai);
++
++static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8510_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8510_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8510 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8510_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "WM8510";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8510_read_reg_cache;
++ codec->write = wm8510_write;
++ codec->dapm_event = wm8510_dapm_event;
++ codec->dai = &wm8510_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8510_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8510_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8510_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8510_reg);
++
++ wm8510_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "wm8510: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8510_add_controls(codec);
++ wm8510_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8510: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8510_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8510 2 wire address is 0x1a
++ */
++#define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8510_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8510_socdev;
++ struct wm8510_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if(ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8510_init(socdev);
++ if(ret < 0) {
++ err("failed to initialise WM8510\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int wm8510_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec *codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8510_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8510_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8510_i2c_driver = {
++ .driver = {
++ .name = "WM8510 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8510,
++ .attach_adapter = wm8510_i2c_attach,
++ .detach_client = wm8510_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8510",
++ .driver = &wm8510_i2c_driver,
++};
++#endif
++
++static int wm8510_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8510_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8510 Audio Codec %s", WM8510_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8510_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8510_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8510_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8510_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8510 = {
++ .probe = wm8510_probe,
++ .remove = wm8510_remove,
++ .suspend = wm8510_suspend,
++ .resume = wm8510_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
++
++MODULE_DESCRIPTION("ASoC WM8510 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8510.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8510.h 2007-07-16 15:07:34.628310625 +0200
+@@ -0,0 +1,103 @@
++/*
++ * wm8510.h -- WM8510 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8510_H
++#define _WM8510_H
++
++/* WM8510 register space */
++
++#define WM8510_RESET 0x0
++#define WM8510_POWER1 0x1
++#define WM8510_POWER2 0x2
++#define WM8510_POWER3 0x3
++#define WM8510_IFACE 0x4
++#define WM8510_COMP 0x5
++#define WM8510_CLOCK 0x6
++#define WM8510_ADD 0x7
++#define WM8510_GPIO 0x8
++#define WM8510_DAC 0xa
++#define WM8510_DACVOL 0xb
++#define WM8510_ADC 0xe
++#define WM8510_ADCVOL 0xf
++#define WM8510_EQ1 0x12
++#define WM8510_EQ2 0x13
++#define WM8510_EQ3 0x14
++#define WM8510_EQ4 0x15
++#define WM8510_EQ5 0x16
++#define WM8510_DACLIM1 0x18
++#define WM8510_DACLIM2 0x19
++#define WM8510_NOTCH1 0x1b
++#define WM8510_NOTCH2 0x1c
++#define WM8510_NOTCH3 0x1d
++#define WM8510_NOTCH4 0x1e
++#define WM8510_ALC1 0x20
++#define WM8510_ALC2 0x21
++#define WM8510_ALC3 0x22
++#define WM8510_NGATE 0x23
++#define WM8510_PLLN 0x24
++#define WM8510_PLLK1 0x25
++#define WM8510_PLLK2 0x26
++#define WM8510_PLLK3 0x27
++#define WM8510_ATTEN 0x28
++#define WM8510_INPUT 0x2c
++#define WM8510_INPPGA 0x2d
++#define WM8510_ADCBOOST 0x2f
++#define WM8510_OUTPUT 0x31
++#define WM8510_SPKMIX 0x32
++#define WM8510_SPKVOL 0x36
++#define WM8510_MONOMIX 0x38
++
++#define WM8510_CACHEREGNUM 57
++
++/* Clock divider Id's */
++#define WM8510_OPCLKDIV 0
++#define WM8510_MCLKDIV 1
++#define WM8510_ADCCLK 2
++#define WM8510_DACCLK 3
++#define WM8510_BCLKDIV 4
++
++/* DAC clock dividers */
++#define WM8510_DACCLK_F2 (1 << 3)
++#define WM8510_DACCLK_F4 (0 << 3)
++
++/* ADC clock dividers */
++#define WM8510_ADCCLK_F2 (1 << 3)
++#define WM8510_ADCCLK_F4 (0 << 3)
++
++/* PLL Out dividers */
++#define WM8510_OPCLKDIV_1 (0 << 4)
++#define WM8510_OPCLKDIV_2 (1 << 4)
++#define WM8510_OPCLKDIV_3 (2 << 4)
++#define WM8510_OPCLKDIV_4 (3 << 4)
++
++/* BCLK clock dividers */
++#define WM8510_BCLKDIV_1 (0 << 2)
++#define WM8510_BCLKDIV_2 (1 << 2)
++#define WM8510_BCLKDIV_4 (2 << 2)
++#define WM8510_BCLKDIV_8 (3 << 2)
++#define WM8510_BCLKDIV_16 (4 << 2)
++#define WM8510_BCLKDIV_32 (5 << 2)
++
++/* MCLK clock dividers */
++#define WM8510_MCLKDIV_1 (0 << 5)
++#define WM8510_MCLKDIV_1_5 (1 << 5)
++#define WM8510_MCLKDIV_2 (2 << 5)
++#define WM8510_MCLKDIV_3 (3 << 5)
++#define WM8510_MCLKDIV_4 (4 << 5)
++#define WM8510_MCLKDIV_6 (5 << 5)
++#define WM8510_MCLKDIV_8 (6 << 5)
++#define WM8510_MCLKDIV_12 (7 << 5)
++
++struct wm8510_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8510_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8510;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/imx/imx-ac97.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx-ac97.c 2007-07-16 15:07:34.652311994 +0200
+@@ -0,0 +1,222 @@
++/*
++ * imx-ssi.c -- SSI driver for Freescale IMX
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 29th Aug 2006 Initial version.
++ *
++ */
++
++
++static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_out = {
++ .name = "SSI1 PCM Stereo out",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = emi_2_per,
++ .watermark_level = SDMA_TXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI1_STX0,
++ .event_id = DMA_REQ_SSI1_TX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_in = {
++ .name = "SSI1 PCM Stereo in",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_RXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI1_SRX0,
++ .event_id = DMA_REQ_SSI1_RX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_out = {
++ .name = "SSI2 PCM Stereo out",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_TXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI2_STX0,
++ .event_id = DMA_REQ_SSI2_TX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_in = {
++ .name = "SSI2 PCM Stereo in",
++ .params = {
++ .bd_number = 1,
++ .transfer_type = per_2_emi,
++ .watermark_level = SDMA_RXFIFO_WATERMARK,
++ .word_size = TRANSFER_16BIT, // maybe add this in setup func
++ .per_address = SSI2_SRX0,
++ .event_id = DMA_REQ_SSI2_RX1,
++ .peripheral_type = SSI,
++ },
++};
++
++static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
++{
++}
++
++static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
++{
++}
++
++static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
++{
++}
++
++static void imx_ssi_ac97_cold_reset(struct snd_ac97 *ac97)
++{
++}
++
++struct snd_ac97_bus_ops soc_ac97_ops = {
++ .read = imx_ssi_ac97_read,
++ .write = imx_ssi_ac97_write,
++ .warm_reset = imx_ssi_ac97_warm_reset,
++ .reset = imx_ssi_ac97_cold_reset,
++};
++
++
++static intimx_ssi1_ac97_probe(struct platform_device *pdev)
++{
++ int ret;
++
++
++ return ret;
++}
++
++static void imx_ssi1_ac97_remove(struct platform_device *pdev)
++{
++ /* shutdown SSI */
++ if(rtd->cpu_dai->id == 0)
++ SSI1_SCR &= ~SSI_SCR_SSIEN;
++ else
++ SSI2_SCR &= ~SSI_SCR_SSIEN;
++ }
++
++}
++
++static int imx_ssi1_ac97_prepare(struct snd_pcm_substream *substream)
++{
++ // set vra
++}
++
++static int imx_ssi_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++
++ if (!rtd->cpu_dai->active) {
++
++ }
++
++ return 0;
++}
++
++static int imx_ssi1_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++
++ return ret;
++}
++
++static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++
++
++}
++
++#ifdef CONFIG_PM
++static int imx_ssi_suspend(struct platform_device *dev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if(!dai->active)
++ return 0;
++
++
++ return 0;
++}
++
++static int imx_ssi_resume(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++ if(!dai->active)
++ return 0;
++
++ return 0;
++}
++
++#else
++#define imx_ssi_suspend NULL
++#define imx_ssi_resume NULL
++#endif
++
++#define IMX_AC97_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
++ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++struct snd_soc_cpu_dai imx_ssi_ac97_dai = {
++ .name = "imx-ac97-1",
++ .id = 0,
++ .type = SND_SOC_DAI_AC97,
++ .suspend = imx_ssi_suspend,
++ .resume = imx_ssi_resume,
++ .playback = {
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = IMX_AC97_RATES,},
++ .capture = {
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = IMX_AC97_RATES,},
++ .ops = {
++ .probe = imx_ac97_probe,
++ .remove = imx_ac97_shutdown,
++ .trigger = imx_ssi_trigger,
++ .prepare = imx_ssi_ac97_prepare,},
++},
++{
++ .name = "imx-ac97-2",
++ .id = 1,
++ .type = SND_SOC_DAI_AC97,
++ .suspend = imx_ssi_suspend,
++ .resume = imx_ssi_resume,
++ .playback = {
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = IMX_AC97_RATES,},
++ .capture = {
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = IMX_AC97_RATES,},
++ .ops = {
++ .probe = imx_ac97_probe,
++ .remove = imx_ac97_shutdown,
++ .trigger = imx_ssi_trigger,
++ .prepare = imx_ssi_ac97_prepare,},
++};
++
++EXPORT_SYMBOL_GPL(imx_ssi_ac97_dai);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("i.MX ASoC AC97 driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8976.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8976.c 2007-07-16 15:07:34.676313360 +0200
+@@ -0,0 +1,885 @@
++/*
++ * wm8976.c -- WM8976 ALSA Soc Audio driver
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8976.h"
++
++#define AUDIO_NAME "wm8976"
++#define WM8976_VERSION "0.4"
++
++/*
++ * Debug
++ */
++
++#define WM8976_DEBUG 0
++
++#ifdef WM8976_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8976;
++
++/*
++ * wm8976 register cache
++ * We can't read the WM8976 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8976_reg[WM8976_CACHEREGNUM] = {
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0050, 0x0000, 0x0140, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x00ff,
++ 0x00ff, 0x0000, 0x0100, 0x00ff,
++ 0x00ff, 0x0000, 0x012c, 0x002c,
++ 0x002c, 0x002c, 0x002c, 0x0000,
++ 0x0032, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0038, 0x000b, 0x0032, 0x0000,
++ 0x0008, 0x000c, 0x0093, 0x00e9,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0033, 0x0010, 0x0010, 0x0100,
++ 0x0100, 0x0002, 0x0001, 0x0001,
++ 0x0039, 0x0039, 0x0039, 0x0039,
++ 0x0001, 0x0001,
++};
++
++/*
++ * read wm8976 register cache
++ */
++static inline unsigned int wm8976_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8976_RESET)
++ return 0;
++ if (reg >= WM8976_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8976 register cache
++ */
++static inline void wm8976_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8976_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8976 register space
++ */
++static int wm8976_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8976 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8976_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -1;
++}
++
++#define wm8976_reset(c) wm8976_write(c, WM8976_RESET, 0)
++
++static const char *wm8976_companding[] = {"Off", "NC", "u-law", "A-law" };
++static const char *wm8976_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
++static const char *wm8976_eqmode[] = {"Capture", "Playback" };
++static const char *wm8976_bw[] = {"Narrow", "Wide" };
++static const char *wm8976_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
++static const char *wm8976_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
++static const char *wm8976_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
++static const char *wm8976_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
++static const char *wm8976_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
++static const char *wm8976_alc[] =
++ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
++
++static const struct soc_enum wm8976_enum[] = {
++ SOC_ENUM_SINGLE(WM8976_COMP, 1, 4, wm8976_companding), /* adc */
++ SOC_ENUM_SINGLE(WM8976_COMP, 3, 4, wm8976_companding), /* dac */
++ SOC_ENUM_SINGLE(WM8976_DAC, 4, 4, wm8976_deemp),
++ SOC_ENUM_SINGLE(WM8976_EQ1, 8, 2, wm8976_eqmode),
++
++ SOC_ENUM_SINGLE(WM8976_EQ1, 5, 4, wm8976_eq1),
++ SOC_ENUM_SINGLE(WM8976_EQ2, 8, 2, wm8976_bw),
++ SOC_ENUM_SINGLE(WM8976_EQ2, 5, 4, wm8976_eq2),
++ SOC_ENUM_SINGLE(WM8976_EQ3, 8, 2, wm8976_bw),
++
++ SOC_ENUM_SINGLE(WM8976_EQ3, 5, 4, wm8976_eq3),
++ SOC_ENUM_SINGLE(WM8976_EQ4, 8, 2, wm8976_bw),
++ SOC_ENUM_SINGLE(WM8976_EQ4, 5, 4, wm8976_eq4),
++ SOC_ENUM_SINGLE(WM8976_EQ5, 8, 2, wm8976_bw),
++
++ SOC_ENUM_SINGLE(WM8976_EQ5, 5, 4, wm8976_eq5),
++ SOC_ENUM_SINGLE(WM8976_ALC3, 8, 2, wm8976_alc),
++};
++
++static const struct snd_kcontrol_new wm8976_snd_controls[] = {
++SOC_SINGLE("Digital Loopback Switch", WM8976_COMP, 0, 1, 0),
++
++SOC_ENUM("ADC Companding", wm8976_enum[0]),
++SOC_ENUM("DAC Companding", wm8976_enum[1]),
++
++SOC_SINGLE("Jack Detection Enable", WM8976_JACK1, 6, 1, 0),
++
++SOC_DOUBLE("DAC Inversion Switch", WM8976_DAC, 0, 1, 1, 0),
++
++SOC_DOUBLE_R("Headphone Playback Volume", WM8976_DACVOLL, WM8976_DACVOLR, 0, 127, 0),
++
++SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
++SOC_SINGLE("High Pass Cut Off", WM8976_ADC, 4, 7, 0),
++
++SOC_DOUBLE("ADC Inversion Switch", WM8976_ADC, 0, 1, 1, 0),
++
++SOC_SINGLE("Capture Volume", WM8976_ADCVOL, 0, 127, 0),
++
++SOC_ENUM("Equaliser Function", wm8976_enum[3]),
++SOC_ENUM("EQ1 Cut Off", wm8976_enum[4]),
++SOC_SINGLE("EQ1 Volume", WM8976_EQ1, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ2 Bandwith", wm8976_enum[5]),
++SOC_ENUM("EQ2 Cut Off", wm8976_enum[6]),
++SOC_SINGLE("EQ2 Volume", WM8976_EQ2, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ3 Bandwith", wm8976_enum[7]),
++SOC_ENUM("EQ3 Cut Off", wm8976_enum[8]),
++SOC_SINGLE("EQ3 Volume", WM8976_EQ3, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ4 Bandwith", wm8976_enum[9]),
++SOC_ENUM("EQ4 Cut Off", wm8976_enum[10]),
++SOC_SINGLE("EQ4 Volume", WM8976_EQ4, 0, 31, 1),
++
++SOC_ENUM("Equaliser EQ5 Bandwith", wm8976_enum[11]),
++SOC_ENUM("EQ5 Cut Off", wm8976_enum[12]),
++SOC_SINGLE("EQ5 Volume", WM8976_EQ5, 0, 31, 1),
++
++SOC_SINGLE("DAC Playback Limiter Switch", WM8976_DACLIM1, 8, 1, 0),
++SOC_SINGLE("DAC Playback Limiter Decay", WM8976_DACLIM1, 4, 15, 0),
++SOC_SINGLE("DAC Playback Limiter Attack", WM8976_DACLIM1, 0, 15, 0),
++
++SOC_SINGLE("DAC Playback Limiter Threshold", WM8976_DACLIM2, 4, 7, 0),
++SOC_SINGLE("DAC Playback Limiter Boost", WM8976_DACLIM2, 0, 15, 0),
++
++SOC_SINGLE("ALC Enable Switch", WM8976_ALC1, 8, 1, 0),
++SOC_SINGLE("ALC Capture Max Gain", WM8976_ALC1, 3, 7, 0),
++SOC_SINGLE("ALC Capture Min Gain", WM8976_ALC1, 0, 7, 0),
++
++SOC_SINGLE("ALC Capture ZC Switch", WM8976_ALC2, 8, 1, 0),
++SOC_SINGLE("ALC Capture Hold", WM8976_ALC2, 4, 7, 0),
++SOC_SINGLE("ALC Capture Target", WM8976_ALC2, 0, 15, 0),
++
++SOC_ENUM("ALC Capture Mode", wm8976_enum[13]),
++SOC_SINGLE("ALC Capture Decay", WM8976_ALC3, 4, 15, 0),
++SOC_SINGLE("ALC Capture Attack", WM8976_ALC3, 0, 15, 0),
++
++SOC_SINGLE("ALC Capture Noise Gate Switch", WM8976_NGATE, 3, 1, 0),
++SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8976_NGATE, 0, 7, 0),
++
++SOC_SINGLE("Capture PGA ZC Switch", WM8976_INPPGA, 7, 1, 0),
++SOC_SINGLE("Capture PGA Volume", WM8976_INPPGA, 0, 63, 0),
++
++SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8976_HPVOLL, WM8976_HPVOLR, 7, 1, 0),
++SOC_DOUBLE_R("Headphone Playback Switch", WM8976_HPVOLL, WM8976_HPVOLR, 6, 1, 1),
++SOC_DOUBLE_R("Headphone Playback Volume", WM8976_HPVOLL, WM8976_HPVOLR, 0, 63, 0),
++
++SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 7, 1, 0),
++SOC_DOUBLE_R("Speaker Playback Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 6, 1, 1),
++SOC_DOUBLE_R("Speaker Playback Volume", WM8976_SPKVOLL, WM8976_SPKVOLR, 0, 63, 0),
++
++SOC_SINGLE("Capture Boost(+20dB)", WM8976_ADCBOOST, 8, 1, 0),
++};
++
++/* add non dapm controls */
++static int wm8976_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8976_snd_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8976_snd_controls[i],codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Left Output Mixer */
++static const snd_kcontrol_new_t wm8976_left_mixer_controls[] = {
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_OUTPUT, 6, 1, 1),
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_MIXL, 0, 1, 1),
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXL, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXL, 5, 1, 0),
++};
++
++/* Right Output Mixer */
++static const snd_kcontrol_new_t wm8976_right_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_OUTPUT, 5, 1, 1),
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_MIXR, 0, 1, 1),
++SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXR, 1, 1, 0),
++SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXR, 5, 1, 0),
++};
++
++/* Left AUX Input boost vol */
++static const snd_kcontrol_new_t wm8976_laux_boost_controls =
++SOC_DAPM_SINGLE("Aux Volume", WM8976_ADCBOOST, 0, 3, 0);
++
++/* Left Input boost vol */
++static const snd_kcontrol_new_t wm8976_lmic_boost_controls =
++SOC_DAPM_SINGLE("Input Volume", WM8976_ADCBOOST, 4, 3, 0);
++
++/* Left Aux In to PGA */
++static const snd_kcontrol_new_t wm8976_laux_capture_boost_controls =
++SOC_DAPM_SINGLE("Capture Switch", WM8976_ADCBOOST, 8, 1, 0);
++
++/* Left Input P In to PGA */
++static const snd_kcontrol_new_t wm8976_lmicp_capture_boost_controls =
++SOC_DAPM_SINGLE("Input P Capture Boost Switch", WM8976_INPUT, 0, 1, 0);
++
++/* Left Input N In to PGA */
++static const snd_kcontrol_new_t wm8976_lmicn_capture_boost_controls =
++SOC_DAPM_SINGLE("Input N Capture Boost Switch", WM8976_INPUT, 1, 1, 0);
++
++// TODO Widgets
++static const struct snd_soc_dapm_widget wm8976_dapm_widgets[] = {
++#if 0
++//SND_SOC_DAPM_MUTE("Mono Mute", WM8976_MONOMIX, 6, 0),
++//SND_SOC_DAPM_MUTE("Speaker Mute", WM8976_SPKMIX, 6, 0),
++
++SND_SOC_DAPM_MIXER("Speaker Mixer", WM8976_POWER3, 2, 0,
++ &wm8976_speaker_mixer_controls[0],
++ ARRAY_SIZE(wm8976_speaker_mixer_controls)),
++SND_SOC_DAPM_MIXER("Mono Mixer", WM8976_POWER3, 3, 0,
++ &wm8976_mono_mixer_controls[0],
++ ARRAY_SIZE(wm8976_mono_mixer_controls)),
++SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8976_POWER3, 0, 0),
++SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8976_POWER3, 0, 0),
++SND_SOC_DAPM_PGA("Aux Input", WM8976_POWER1, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkN Out", WM8976_POWER3, 5, 0, NULL, 0),
++SND_SOC_DAPM_PGA("SpkP Out", WM8976_POWER3, 6, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mono Out", WM8976_POWER3, 7, 0, NULL, 0),
++SND_SOC_DAPM_PGA("Mic PGA", WM8976_POWER2, 2, 0, NULL, 0),
++
++SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
++ &wm8976_aux_boost_controls, 1),
++SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
++ &wm8976_mic_boost_controls, 1),
++SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
++ &wm8976_capture_boost_controls),
++
++SND_SOC_DAPM_MIXER("Boost Mixer", WM8976_POWER2, 4, 0, NULL, 0),
++
++SND_SOC_DAPM_MICBIAS("Mic Bias", WM8976_POWER1, 4, 0),
++
++SND_SOC_DAPM_INPUT("MICN"),
++SND_SOC_DAPM_INPUT("MICP"),
++SND_SOC_DAPM_INPUT("AUX"),
++SND_SOC_DAPM_OUTPUT("MONOOUT"),
++SND_SOC_DAPM_OUTPUT("SPKOUTP"),
++SND_SOC_DAPM_OUTPUT("SPKOUTN"),
++#endif
++};
++
++static const char *audio_map[][3] = {
++ /* Mono output mixer */
++ {"Mono Mixer", "PCM Playback Switch", "DAC"},
++ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Speaker output mixer */
++ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
++ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
++ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
++
++ /* Outputs */
++ {"Mono Out", NULL, "Mono Mixer"},
++ {"MONOOUT", NULL, "Mono Out"},
++ {"SpkN Out", NULL, "Speaker Mixer"},
++ {"SpkP Out", NULL, "Speaker Mixer"},
++ {"SPKOUTN", NULL, "SpkN Out"},
++ {"SPKOUTP", NULL, "SpkP Out"},
++
++ /* Boost Mixer */
++ {"Boost Mixer", NULL, "ADC"},
++ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
++ {"Aux Boost", "Aux Volume", "Boost Mixer"},
++ {"Capture Boost", "Capture Switch", "Boost Mixer"},
++ {"Mic Boost", "Mic Volume", "Boost Mixer"},
++
++ /* Inputs */
++ {"MICP", NULL, "Mic Boost"},
++ {"MICN", NULL, "Mic PGA"},
++ {"Mic PGA", NULL, "Capture Boost"},
++ {"AUX", NULL, "Aux Input"},
++
++ /* */
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8976_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8976_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8976_dapm_widgets[i]);
++ }
++
++ /* set up audio path map */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
++ audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++struct pll_ {
++ unsigned int in_hz, out_hz;
++ unsigned int pre:4; /* prescale - 1 */
++ unsigned int n:4;
++ unsigned int k;
++};
++
++struct pll_ pll[] = {
++ {12000000, 11289600, 0, 7, 0x86c220},
++ {12000000, 12288000, 0, 8, 0x3126e8},
++ {13000000, 11289600, 0, 6, 0xf28bd4},
++ {13000000, 12288000, 0, 7, 0x8fd525},
++ {12288000, 11289600, 0, 7, 0x59999a},
++ {11289600, 12288000, 0, 8, 0x80dee9},
++ /* TODO: liam - add more entries */
++};
++
++static int wm8976_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ int i;
++ u16 reg;
++
++ if(freq_in == 0 || freq_out == 0) {
++ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
++ wm8976_write(codec, WM8976_POWER1, reg & 0x1df);
++ return 0;
++ }
++
++ for(i = 0; i < ARRAY_SIZE(pll); i++) {
++ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
++ wm8976_write(codec, WM8976_PLLN, (pll[i].pre << 4) | pll[i].n);
++ wm8976_write(codec, WM8976_PLLK1, pll[i].k >> 18);
++ wm8976_write(codec, WM8976_PLLK1, (pll[i].k >> 9) && 0x1ff);
++ wm8976_write(codec, WM8976_PLLK1, pll[i].k && 0x1ff);
++ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
++ wm8976_write(codec, WM8976_POWER1, reg | 0x020);
++ return 0;
++ }
++ }
++ return -EINVAL;
++}
++
++static int wm8976_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = wm8976_read_reg_cache(codec, WM8976_IFACE) & 0x3;
++ u16 clk = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0xfffe;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ clk |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0010;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0008;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x00018;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0180;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0100;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0080;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ wm8976_write(codec, WM8976_IFACE, iface);
++ return 0;
++}
++
++static int wm8976_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8976_read_reg_cache(codec, WM8976_IFACE) & 0xff9f;
++ u16 adn = wm8976_read_reg_cache(codec, WM8976_ADD) & 0x1f1;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0020;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0040;
++ break;
++ }
++
++ /* filter coefficient */
++ switch (params_rate(params)) {
++ case SNDRV_PCM_RATE_8000:
++ adn |= 0x5 << 1;
++ break;
++ case SNDRV_PCM_RATE_11025:
++ adn |= 0x4 << 1;
++ break;
++ case SNDRV_PCM_RATE_16000:
++ adn |= 0x3 << 1;
++ break;
++ case SNDRV_PCM_RATE_22050:
++ adn |= 0x2 << 1;
++ break;
++ case SNDRV_PCM_RATE_32000:
++ adn |= 0x1 << 1;
++ break;
++ }
++
++ /* set iface */
++ wm8976_write(codec, WM8976_IFACE, iface);
++ wm8976_write(codec, WM8976_ADD, adn);
++ return 0;
++}
++
++static int wm8976_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8976_MCLKDIV:
++ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x11f;
++ wm8976_write(codec, WM8976_CLOCK, reg | div);
++ break;
++ case WM8976_BCLKDIV:
++ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x1c7;
++ wm8976_write(codec, WM8976_CLOCK, reg | div);
++ break;
++ case WM8976_OPCLKDIV:
++ reg = wm8976_read_reg_cache(codec, WM8976_GPIO) & 0x1cf;
++ wm8976_write(codec, WM8976_GPIO, reg | div);
++ break;
++ case WM8976_DACOSR:
++ reg = wm8976_read_reg_cache(codec, WM8976_DAC) & 0x1f7;
++ wm8976_write(codec, WM8976_DAC, reg | div);
++ break;
++ case WM8976_ADCOSR:
++ reg = wm8976_read_reg_cache(codec, WM8976_ADC) & 0x1f7;
++ wm8976_write(codec, WM8976_ADC, reg | div);
++ break;
++ case WM8976_MCLKSEL:
++ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x0ff;
++ wm8976_write(codec, WM8976_CLOCK, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int wm8976_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8976_read_reg_cache(codec, WM8976_DAC) & 0xffbf;
++
++ if(mute)
++ wm8976_write(codec, WM8976_DAC, mute_reg | 0x40);
++ else
++ wm8976_write(codec, WM8976_DAC, mute_reg);
++
++ return 0;
++}
++
++/* TODO: liam need to make this lower power with dapm */
++static int wm8976_dapm_event(struct snd_soc_codec *codec, int event)
++{
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, clk and osc on, dac unmute, active */
++ wm8976_write(codec, WM8976_POWER1, 0x1ff);
++ wm8976_write(codec, WM8976_POWER2, 0x1ff);
++ wm8976_write(codec, WM8976_POWER3, 0x1ff);
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, dac mute, inactive */
++
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ wm8976_write(codec, WM8976_POWER1, 0x0);
++ wm8976_write(codec, WM8976_POWER2, 0x0);
++ wm8976_write(codec, WM8976_POWER3, 0x0);
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++#define WM8976_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++#define WM8976_FORMATS \
++ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
++ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE)
++
++struct snd_soc_codec_dai wm8976_dai = {
++ .name = "WM8976 HiFi",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8976_RATES,
++ .formats = WM8976_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = WM8976_RATES,
++ .formats = WM8976_FORMATS,},
++ .ops = {
++ .hw_params = wm8976_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8976_mute,
++ .set_fmt = wm8976_set_dai_fmt,
++ .set_clkdiv = wm8976_set_dai_clkdiv,
++ .set_pll = wm8976_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8976_dai);
++
++static int wm8976_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8976_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8976_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8976_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8976 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8976_init(struct snd_soc_device* socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "WM8976";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8976_read_reg_cache;
++ codec->write = wm8976_write;
++ codec->dapm_event = wm8976_dapm_event;
++ codec->dai = &wm8976_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8976_reg);
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8976_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, wm8976_reg,
++ sizeof(u16) * ARRAY_SIZE(wm8976_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8976_reg);
++
++ wm8976_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if(ret < 0) {
++ printk(KERN_ERR "wm8976: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8976_add_controls(codec);
++ wm8976_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8976: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8976_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8976 2 wire address is 0x1a
++ */
++#define I2C_DRIVERID_WM8976 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8976_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8976_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8976_socdev;
++ struct wm8976_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(i2c, codec);
++
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if(ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8976_init(socdev);
++ if(ret < 0) {
++ err("failed to initialise WM8976\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++
++}
++
++static int wm8976_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec *codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8976_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8976_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8976_i2c_driver = {
++ .driver = {
++ .name = "WM8976 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8976,
++ .attach_adapter = wm8976_i2c_attach,
++ .detach_client = wm8976_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8976",
++ .driver = &wm8976_i2c_driver,
++};
++#endif
++
++static int wm8976_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8976_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8976 Audio Codec %s", WM8976_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8976_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8976_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8976_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8976_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8976 = {
++ .probe = wm8976_probe,
++ .remove = wm8976_remove,
++ .suspend = wm8976_suspend,
++ .resume = wm8976_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8976);
++
++MODULE_DESCRIPTION("ASoC WM8976 driver");
++MODULE_AUTHOR("Graeme Gregory");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8976.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8976.h 2007-07-16 15:07:34.720315869 +0200
+@@ -0,0 +1,112 @@
++/*
++ * wm8976.h -- WM8976 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8976_H
++#define _WM8976_H
++
++/* WM8976 register space */
++
++#define WM8976_RESET 0x0
++#define WM8976_POWER1 0x1
++#define WM8976_POWER2 0x2
++#define WM8976_POWER3 0x3
++#define WM8976_IFACE 0x4
++#define WM8976_COMP 0x5
++#define WM8976_CLOCK 0x6
++#define WM8976_ADD 0x7
++#define WM8976_GPIO 0x8
++#define WM8976_JACK1 0x9
++#define WM8976_DAC 0xa
++#define WM8976_DACVOLL 0xb
++#define WM8976_DACVOLR 0xc
++#define WM8976_JACK2 0xd
++#define WM8976_ADC 0xe
++#define WM8976_ADCVOL 0xf
++#define WM8976_EQ1 0x12
++#define WM8976_EQ2 0x13
++#define WM8976_EQ3 0x14
++#define WM8976_EQ4 0x15
++#define WM8976_EQ5 0x16
++#define WM8976_DACLIM1 0x18
++#define WM8976_DACLIM2 0x19
++#define WM8976_NOTCH1 0x1b
++#define WM8976_NOTCH2 0x1c
++#define WM8976_NOTCH3 0x1d
++#define WM8976_NOTCH4 0x1e
++#define WM8976_ALC1 0x20
++#define WM8976_ALC2 0x21
++#define WM8976_ALC3 0x22
++#define WM8976_NGATE 0x23
++#define WM8976_PLLN 0x24
++#define WM8976_PLLK1 0x25
++#define WM8976_PLLK2 0x26
++#define WM8976_PLLK3 0x27
++#define WM8976_3D 0x29
++#define WM8976_BEEP 0x2b
++#define WM8976_INPUT 0x2c
++#define WM8976_INPPGA 0x2d
++#define WM8976_ADCBOOST 0x2f
++#define WM8976_OUTPUT 0x31
++#define WM8976_MIXL 0x32
++#define WM8976_MIXR 0x33
++#define WM8976_HPVOLL 0x34
++#define WM8976_HPVOLR 0x35
++#define WM8976_SPKVOLL 0x36
++#define WM8976_SPKVOLR 0x37
++#define WM8976_OUT3MIX 0x38
++#define WM8976_MONOMIX 0x39
++
++#define WM8976_CACHEREGNUM 58
++
++/*
++ * WM8976 Clock dividers
++ */
++#define WM8976_MCLKDIV 0
++#define WM8976_BCLKDIV 1
++#define WM8976_OPCLKDIV 2
++#define WM8976_DACOSR 3
++#define WM8976_ADCOSR 4
++#define WM8976_MCLKSEL 5
++
++#define WM8976_MCLK_MCLK (0 << 8)
++#define WM8976_MCLK_PLL (1 << 8)
++
++#define WM8976_MCLK_DIV_1 (0 << 5)
++#define WM8976_MCLK_DIV_1_5 (1 << 5)
++#define WM8976_MCLK_DIV_2 (2 << 5)
++#define WM8976_MCLK_DIV_3 (3 << 5)
++#define WM8976_MCLK_DIV_4 (4 << 5)
++#define WM8976_MCLK_DIV_5_5 (5 << 5)
++#define WM8976_MCLK_DIV_6 (6 << 5)
++
++#define WM8976_BCLK_DIV_1 (0 << 2)
++#define WM8976_BCLK_DIV_2 (1 << 2)
++#define WM8976_BCLK_DIV_4 (2 << 2)
++#define WM8976_BCLK_DIV_8 (3 << 2)
++#define WM8976_BCLK_DIV_16 (4 << 2)
++#define WM8976_BCLK_DIV_32 (5 << 2)
++
++#define WM8976_DACOSR_64 (0 << 3)
++#define WM8976_DACOSR_128 (1 << 3)
++
++#define WM8976_ADCOSR_64 (0 << 3)
++#define WM8976_ADCOSR_128 (1 << 3)
++
++#define WM8976_OPCLK_DIV_1 (0 << 4)
++#define WM8976_OPCLK_DIV_2 (1 << 4)
++#define WM8976_OPCLK_DIV_3 (2 << 4)
++#define WM8976_OPCLK_DIV_4 (3 << 4)
++
++struct wm8976_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8976_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8976;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/imx/imx21-pcm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx21-pcm.c 2007-07-16 15:07:34.744317236 +0200
+@@ -0,0 +1,454 @@
++/*
++ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
++ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Revision history
++ * 29th Aug 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/dma-mapping.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <asm/dma.h>
++#include <asm/hardware.h>
++
++#include "imx-pcm.h"
++
++/* debug */
++#define IMX_DEBUG 0
++#if IMX_DEBUG
++#define dbg(format, arg...) printk(format, ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++
++static const struct snd_pcm_hardware mxc_pcm_hardware = {
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE,
++ .buffer_bytes_max = 32 * 1024,
++ .period_bytes_min = 64,
++ .period_bytes_max = 8 * 1024,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++};
++
++struct mxc_runtime_data {
++ int dma_ch;
++ struct mxc_pcm_dma_param *dma_params;
++};
++
++/*!
++ * This function stops the current dma transfert for playback
++ * and clears the dma pointers.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ */
++static void audio_stop_dma(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
++ unsigned int offset dma_size * s->periods;
++ unsigned long flags;
++
++ spin_lock_irqsave(&prtd->dma_lock, flags);
++
++ dbg("MXC : audio_stop_dma active = 0\n");
++ prtd->active = 0;
++ prtd->period = 0;
++ prtd->periods = 0;
++
++ /* this stops the dma channel and clears the buffer ptrs */
++ mxc_dma_stop(prtd->dma_wchannel);
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_TO_DEVICE);
++ else
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_FROM_DEVICE);
++
++ spin_unlock_irqrestore(&prtd->dma_lock, flags);
++}
++
++/*!
++ * This function is called whenever a new audio block needs to be
++ * transferred to mc13783. The function receives the address and the size
++ * of the new block and start a new DMA transfer.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ */
++static int dma_new_period(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int dma_size;
++ unsigned int offset;
++ int ret=0;
++ dma_request_t sdma_request;
++
++ if (prtd->active){
++ memset(&sdma_request, 0, sizeof(dma_request_t));
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ dbg("s->period (%x) runtime->periods (%d)\n",
++ s->period,runtime->periods);
++ dbg("runtime->period_size (%d) dma_size (%d)\n",
++ (unsigned int)runtime->period_size,
++ runtime->dma_bytes);
++
++ offset = dma_size * prtd->period;
++ snd_assert(dma_size <= DMA_BUF_SIZE, );
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
++ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
++ else
++ sdma_request.destAddr = (char*)(dma_map_single(NULL,
++ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
++ sdma_request.count = dma_size;
++
++ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
++ runtime->dma_bytes);
++
++ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
++ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
++ dbg("audio_process_dma: cannot queue DMA buffer\
++ (%i)\n", ret);
++ return err;
++ }
++ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
++ prtd->period++;
++ prtd->period %= runtime->periods;
++ }
++ return ret;
++}
++
++
++/*!
++ * This is a callback which will be called
++ * when a TX transfer finishes. The call occurs
++ * in interrupt context.
++ *
++ * @param dat pointer to the structure of the current stream.
++ *
++ */
++static void audio_dma_irq(void *data)
++{
++ struct snd_pcm_substream *substream;
++ struct snd_pcm_runtime *runtime;
++ struct mxc_runtime_data *prtd;
++ unsigned int dma_size;
++ unsigned int previous_period;
++ unsigned int offset;
++
++ substream = data;
++ runtime = substream->runtime;
++ prtd = runtime->private_data;
++ previous_period = prtd->periods;
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ offset = dma_size * previous_period;
++
++ prtd->tx_spin = 0;
++ prtd->periods++;
++ prtd->periods %= runtime->periods;
++
++ /*
++ * Give back to the CPU the access to the non cached memory
++ */
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_TO_DEVICE);
++ else
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_FROM_DEVICE);
++ /*
++ * If we are getting a callback for an active stream then we inform
++ * the PCM middle layer we've finished a period
++ */
++ if (prtd->active)
++ snd_pcm_period_elapsed(substream);
++
++ /*
++ * Trig next DMA transfer
++ */
++ dma_new_period(substream);
++}
++
++/*!
++ * This function configures the hardware to allow audio
++ * playback operations. It is called by ALSA framework.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ * @return 0 on success, -1 otherwise.
++ */
++static int
++snd_mxc_prepare(struct snd_pcm_substream *substream)
++{
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ int ret = 0;
++ prtd->period = 0;
++ prtd->periods = 0;
++
++ dma_channel_params params;
++ int channel = 0; // passed in ?
++
++ if ((ret = mxc_request_dma(&channel, "ALSA TX SDMA") < 0)){
++ dbg("error requesting a write dma channel\n");
++ return ret;
++ }
++
++ /* configure DMA params */
++ memset(¶ms, 0, sizeof(dma_channel_params));
++ params.bd_number = 1;
++ params.arg = s;
++ params.callback = callback;
++ params.transfer_type = emi_2_per;
++ params.watermark_level = SDMA_TXFIFO_WATERMARK;
++ params.word_size = TRANSFER_16BIT;
++ //dbg(KERN_ERR "activating connection SSI1 - SDMA\n");
++ params.per_address = SSI1_BASE_ADDR;
++ params.event_id = DMA_REQ_SSI1_TX1;
++ params.peripheral_type = SSI;
++
++ /* set up chn with params */
++ mxc_dma_setup_channel(channel, ¶ms);
++ s->dma_wchannel = channel;
++
++ return ret;
++}
++
++static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int ret;
++
++ if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
++ return ret;
++ runtime->dma_addr = virt_to_phys(runtime->dma_area);
++
++ return ret;
++}
++
++static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct mxc_runtime_data *prtd = substream->runtime->private_data;
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ prtd->tx_spin = 0;
++ /* requested stream startup */
++ prtd->active = 1;
++ ret = dma_new_period(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ /* requested stream shutdown */
++ ret = audio_stop_dma(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ prtd->active = 0;
++ prtd->periods = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ prtd->active = 1;
++ prtd->tx_spin = 0;
++ ret = dma_new_period(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ prtd->active = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ prtd->active = 1;
++ if (prtd->old_offset) {
++ prtd->tx_spin = 0;
++ ret = dma_new_period(substream);
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int offset = 0;
++
++ /* tx_spin value is used here to check if a transfert is active */
++ if (prtd->tx_spin){
++ offset = (runtime->period_size * (prtd->periods)) +
++ (runtime->period_size >> 1);
++ if (offset >= runtime->buffer_size)
++ offset = runtime->period_size >> 1;
++ } else {
++ offset = (runtime->period_size * (s->periods));
++ if (offset >= runtime->buffer_size)
++ offset = 0;
++ }
++
++ return offset;
++}
++
++
++static int mxc_pcm_open(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd;
++ int ret;
++
++ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
++
++ if ((err = snd_pcm_hw_constraint_integer(runtime,
++ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
++ return err;
++ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
++ SNDRV_PCM_HW_PARAM_RATE, &hw_playback_rates)) < 0)
++ return err;
++ msleep(10); // liam - why
++
++ /* setup DMA controller for playback */
++ if((err = configure_write_channel(&mxc_mc13783->s[SNDRV_PCM_STREAM_PLAYBACK],
++ audio_dma_irq)) < 0 )
++ return err;
++
++ if((prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL)) == NULL) {
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ runtime->private_data = prtd;
++ return 0;
++
++ err1:
++ kfree(prtd);
++ out:
++ return ret;
++}
++
++static int mxc_pcm_close(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++
++// mxc_mc13783_t *chip;
++ audio_stream_t *s;
++ device_data_t* device;
++ int ssi;
++
++ //chip = snd_pcm_substream_chip(substream);
++ s = &chip->s[substream->pstr->stream];
++ device = &s->stream_device;
++ ssi = device->ssi;
++
++ //disable_stereodac();
++
++ ssi_transmit_enable(ssi, false);
++ ssi_interrupt_disable(ssi, ssi_tx_dma_interrupt_enable);
++ ssi_tx_fifo_enable(ssi, ssi_fifo_0, false);
++ ssi_enable(ssi, false);
++
++ chip->s[substream->pstr->stream].stream = NULL;
++
++ return 0;
++}
++
++static int
++mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
++ runtime->dma_area,
++ runtime->dma_addr,
++ runtime->dma_bytes);
++}
++
++struct snd_pcm_ops mxc_pcm_ops = {
++ .open = mxc_pcm_open,
++ .close = mxc_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = mxc_pcm_hw_params,
++ .hw_free = mxc_pcm_hw_free,
++ .prepare = mxc_pcm_prepare,
++ .trigger = mxc_pcm_trigger,
++ .pointer = mxc_pcm_pointer,
++ .mmap = mxc_pcm_mmap,
++};
++
++static u64 mxc_pcm_dmamask = 0xffffffff;
++
++int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
++ struct snd_pcm *pcm)
++{
++ int ret = 0;
++
++ if (!card->dev->dma_mask)
++ card->dev->dma_mask = &mxc_pcm_dmamask;
++ if (!card->dev->coherent_dma_mask)
++ card->dev->coherent_dma_mask = 0xffffffff;
++
++ if (dai->playback.channels_min) {
++ ret = mxc_pcm_preallocate_dma_buffer(pcm,
++ SNDRV_PCM_STREAM_PLAYBACK);
++ if (ret)
++ goto out;
++ }
++
++ if (dai->capture.channels_min) {
++ ret = mxc_pcm_preallocate_dma_buffer(pcm,
++ SNDRV_PCM_STREAM_CAPTURE);
++ if (ret)
++ goto out;
++ }
++ out:
++ return ret;
++}
++
++struct snd_soc_platform mxc_soc_platform = {
++ .name = "mxc-audio",
++ .pcm_ops = &mxc_pcm_ops,
++ .pcm_new = mxc_pcm_new,
++ .pcm_free = mxc_pcm_free_dma_buffers,
++};
++
++EXPORT_SYMBOL_GPL(mxc_soc_platform);
++
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_DESCRIPTION("Freescale i.MX PCM DMA module");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/imx/imx21-pcm.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx21-pcm.h 2007-07-16 15:07:34.768318602 +0200
+@@ -0,0 +1,237 @@
++/*
++ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _MXC_PCM_H
++#define _MXC_PCM_H
++
++struct {
++ char *name; /* stream identifier */
++ dma_channel_params dma_params;
++} mxc_pcm_dma_param;
++
++extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
++
++/* platform data */
++extern struct snd_soc_platform mxc_soc_platform;
++extern struct snd_ac97_bus_ops mxc_ac97_ops;
++
++/* temp until imx-regs.h is up2date */
++#define SSI1_STX0 __REG(IMX_SSI1_BASE + 0x00)
++#define SSI1_STX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x00)
++#define SSI1_STX1 __REG(IMX_SSI1_BASE + 0x04)
++#define SSI1_STX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x04)
++#define SSI1_SRX0 __REG(IMX_SSI1_BASE + 0x08)
++#define SSI1_SRX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x08)
++#define SSI1_SRX1 __REG(IMX_SSI1_BASE + 0x0c)
++#define SSI1_SRX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x0c)
++#define SSI1_SCR __REG(IMX_SSI1_BASE + 0x10)
++#define SSI1_SISR __REG(IMX_SSI1_BASE + 0x14)
++#define SSI1_SIER __REG(IMX_SSI1_BASE + 0x18)
++#define SSI1_STCR __REG(IMX_SSI1_BASE + 0x1c)
++#define SSI1_SRCR __REG(IMX_SSI1_BASE + 0x20)
++#define SSI1_STCCR __REG(IMX_SSI1_BASE + 0x24)
++#define SSI1_SRCCR __REG(IMX_SSI1_BASE + 0x28)
++#define SSI1_SFCSR __REG(IMX_SSI1_BASE + 0x2c)
++#define SSI1_STR __REG(IMX_SSI1_BASE + 0x30)
++#define SSI1_SOR __REG(IMX_SSI1_BASE + 0x34)
++#define SSI1_SACNT __REG(IMX_SSI1_BASE + 0x38)
++#define SSI1_SACADD __REG(IMX_SSI1_BASE + 0x3c)
++#define SSI1_SACDAT __REG(IMX_SSI1_BASE + 0x40)
++#define SSI1_SATAG __REG(IMX_SSI1_BASE + 0x44)
++#define SSI1_STMSK __REG(IMX_SSI1_BASE + 0x48)
++#define SSI1_SRMSK __REG(IMX_SSI1_BASE + 0x4c)
++
++#define SSI2_STX0 __REG(IMX_SSI2_BASE + 0x00)
++#define SSI2_STX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x00)
++#define SSI2_STX1 __REG(IMX_SSI2_BASE + 0x04)
++#define SSI2_STX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x04)
++#define SSI2_SRX0 __REG(IMX_SSI2_BASE + 0x08)
++#define SSI2_SRX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x08)
++#define SSI2_SRX1 __REG(IMX_SSI2_BASE + 0x0c)
++#define SSI2_SRX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x0c)
++#define SSI2_SCR __REG(IMX_SSI2_BASE + 0x10)
++#define SSI2_SISR __REG(IMX_SSI2_BASE + 0x14)
++#define SSI2_SIER __REG(IMX_SSI2_BASE + 0x18)
++#define SSI2_STCR __REG(IMX_SSI2_BASE + 0x1c)
++#define SSI2_SRCR __REG(IMX_SSI2_BASE + 0x20)
++#define SSI2_STCCR __REG(IMX_SSI2_BASE + 0x24)
++#define SSI2_SRCCR __REG(IMX_SSI2_BASE + 0x28)
++#define SSI2_SFCSR __REG(IMX_SSI2_BASE + 0x2c)
++#define SSI2_STR __REG(IMX_SSI2_BASE + 0x30)
++#define SSI2_SOR __REG(IMX_SSI2_BASE + 0x34)
++#define SSI2_SACNT __REG(IMX_SSI2_BASE + 0x38)
++#define SSI2_SACADD __REG(IMX_SSI2_BASE + 0x3c)
++#define SSI2_SACDAT __REG(IMX_SSI2_BASE + 0x40)
++#define SSI2_SATAG __REG(IMX_SSI2_BASE + 0x44)
++#define SSI2_STMSK __REG(IMX_SSI2_BASE + 0x48)
++#define SSI2_SRMSK __REG(IMX_SSI2_BASE + 0x4c)
++
++#define SSI_SCR_CLK_IST (1 << 9)
++#define SSI_SCR_TCH_EN (1 << 8)
++#define SSI_SCR_SYS_CLK_EN (1 << 7)
++#define SSI_SCR_I2S_MODE_NORM (0 << 5)
++#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
++#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
++#define SSI_SCR_SYN (1 << 4)
++#define SSI_SCR_NET (1 << 3)
++#define SSI_SCR_RE (1 << 2)
++#define SSI_SCR_TE (1 << 1)
++#define SSI_SCR_SSIEN (1 << 0)
++
++#define SSI_SISR_CMDAU (1 << 18)
++#define SSI_SISR_CMDDU (1 << 17)
++#define SSI_SISR_RXT (1 << 16)
++#define SSI_SISR_RDR1 (1 << 15)
++#define SSI_SISR_RDR0 (1 << 14)
++#define SSI_SISR_TDE1 (1 << 13)
++#define SSI_SISR_TDE0 (1 << 12)
++#define SSI_SISR_ROE1 (1 << 11)
++#define SSI_SISR_ROE0 (1 << 10)
++#define SSI_SISR_TUE1 (1 << 9)
++#define SSI_SISR_TUE0 (1 << 8)
++#define SSI_SISR_TFS (1 << 7)
++#define SSI_SISR_RFS (1 << 6)
++#define SSI_SISR_TLS (1 << 5)
++#define SSI_SISR_RLS (1 << 4)
++#define SSI_SISR_RFF1 (1 << 3)
++#define SSI_SISR_RFF0 (1 << 2)
++#define SSI_SISR_TFE1 (1 << 1)
++#define SSI_SISR_TFE0 (1 << 0)
++
++#define SSI_SIER_RDMAE (1 << 22)
++#define SSI_SIER_RIE (1 << 21)
++#define SSI_SIER_TDMAE (1 << 20)
++#define SSI_SIER_TIE (1 << 19)
++#define SSI_SIER_CMDAU_EN (1 << 18)
++#define SSI_SIER_CMDDU_EN (1 << 17)
++#define SSI_SIER_RXT_EN (1 << 16)
++#define SSI_SIER_RDR1_EN (1 << 15)
++#define SSI_SIER_RDR0_EN (1 << 14)
++#define SSI_SIER_TDE1_EN (1 << 13)
++#define SSI_SIER_TDE0_EN (1 << 12)
++#define SSI_SIER_ROE1_EN (1 << 11)
++#define SSI_SIER_ROE0_EN (1 << 10)
++#define SSI_SIER_TUE1_EN (1 << 9)
++#define SSI_SIER_TUE0_EN (1 << 8)
++#define SSI_SIER_TFS_EN (1 << 7)
++#define SSI_SIER_RFS_EN (1 << 6)
++#define SSI_SIER_TLS_EN (1 << 5)
++#define SSI_SIER_RLS_EN (1 << 4)
++#define SSI_SIER_RFF1_EN (1 << 3)
++#define SSI_SIER_RFF0_EN (1 << 2)
++#define SSI_SIER_TFE1_EN (1 << 1)
++#define SSI_SIER_TFE0_EN (1 << 0)
++
++#define SSI_STCR_TXBIT0 (1 << 9)
++#define SSI_STCR_TFEN1 (1 << 8)
++#define SSI_STCR_TFEN0 (1 << 7)
++#define SSI_STCR_TFDIR (1 << 6)
++#define SSI_STCR_TXDIR (1 << 5)
++#define SSI_STCR_TSHFD (1 << 4)
++#define SSI_STCR_TSCKP (1 << 3)
++#define SSI_STCR_TFSI (1 << 2)
++#define SSI_STCR_TFSL (1 << 1)
++#define SSI_STCR_TEFS (1 << 0)
++
++#define SSI_SRCR_RXBIT0 (1 << 9)
++#define SSI_SRCR_RFEN1 (1 << 8)
++#define SSI_SRCR_RFEN0 (1 << 7)
++#define SSI_SRCR_RFDIR (1 << 6)
++#define SSI_SRCR_RXDIR (1 << 5)
++#define SSI_SRCR_RSHFD (1 << 4)
++#define SSI_SRCR_RSCKP (1 << 3)
++#define SSI_SRCR_RFSI (1 << 2)
++#define SSI_SRCR_RFSL (1 << 1)
++#define SSI_SRCR_REFS (1 << 0)
++
++#define SSI_STCCR_DIV2 (1 << 18)
++#define SSI_STCCR_PSR (1 << 15)
++#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
++#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
++#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
++
++#define SSI_SRCCR_DIV2 (1 << 18)
++#define SSI_SRCCR_PSR (1 << 15)
++#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
++#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
++#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
++
++
++#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
++#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
++#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
++#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
++#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
++#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
++#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
++#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
++
++#define SSI_STR_TEST (1 << 15)
++#define SSI_STR_RCK2TCK (1 << 14)
++#define SSI_STR_RFS2TFS (1 << 13)
++#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
++#define SSI_STR_TXD2RXD (1 << 7)
++#define SSI_STR_TCK2RCK (1 << 6)
++#define SSI_STR_TFS2RFS (1 << 5)
++#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
++
++#define SSI_SOR_CLKOFF (1 << 6)
++#define SSI_SOR_RX_CLR (1 << 5)
++#define SSI_SOR_TX_CLR (1 << 4)
++#define SSI_SOR_INIT (1 << 3)
++#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
++#define SSI_SOR_SYNRST (1 << 0)
++
++#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
++#define SSI_SACNT_WR (x << 4)
++#define SSI_SACNT_RD (x << 3)
++#define SSI_SACNT_TIF (x << 2)
++#define SSI_SACNT_FV (x << 1)
++#define SSI_SACNT_A97EN (x << 0)
++
++
++/* AUDMUX registers */
++#define AUDMUX_HPCR1 __REG(IMX_AUDMUX_BASE + 0x00)
++#define AUDMUX_HPCR2 __REG(IMX_AUDMUX_BASE + 0x04)
++#define AUDMUX_HPCR3 __REG(IMX_AUDMUX_BASE + 0x08)
++#define AUDMUX_PPCR1 __REG(IMX_AUDMUX_BASE + 0x10)
++#define AUDMUX_PPCR2 __REG(IMX_AUDMUX_BASE + 0x14)
++#define AUDMUX_PPCR3 __REG(IMX_AUDMUX_BASE + 0x18)
++
++#define AUDMUX_HPCR_TFSDIR (1 << 31)
++#define AUDMUX_HPCR_TCLKDIR (1 << 30)
++#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
++#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
++#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
++#define AUDMUX_HPCR_RFSDIR (1 << 25)
++#define AUDMUX_HPCR_RCLKDIR (1 << 24)
++#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
++#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
++#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
++#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
++#define AUDMUX_HPCR_SYN (1 << 12)
++#define AUDMUX_HPCR_TXRXEN (1 << 10)
++#define AUDMUX_HPCR_INMEN (1 << 8)
++#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
++
++#define AUDMUX_PPCR_TFSDIR (1 << 31)
++#define AUDMUX_PPCR_TCLKDIR (1 << 30)
++#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
++#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
++#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
++#define AUDMUX_PPCR_RFSDIR (1 << 25)
++#define AUDMUX_PPCR_RCLKDIR (1 << 24)
++#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
++#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
++#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
++#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
++#define AUDMUX_PPCR_SYN (1 << 12)
++#define AUDMUX_PPCR_TXRXEN (1 << 10)
++
++
++#endif
+Index: linux-2.6.22.1/sound/soc/imx/imx31-pcm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx31-pcm.c 2007-07-16 15:07:34.792319970 +0200
+@@ -0,0 +1,417 @@
++/*
++ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
++ *
++ * Copyright 2006 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
++ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Revision history
++ * 29th Aug 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/dma-mapping.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <asm/arch/dma.h>
++#include <asm/arch/spba.h>
++#include <asm/arch/clock.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++
++#include "imx31-pcm.h"
++
++/* debug */
++#define IMX_DEBUG 0
++#if IMX_DEBUG
++#define dbg(format, arg...) printk(format, ## arg)
++#else
++#define dbg(format, arg...)
++#endif
++
++static const struct snd_pcm_hardware mxc_pcm_hardware = {
++ .info = (SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME),
++ .formats = SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE,
++ .buffer_bytes_max = 32 * 1024,
++ .period_bytes_min = 64,
++ .period_bytes_max = 8 * 1024,
++ .periods_min = 2,
++ .periods_max = 255,
++ .fifo_size = 0,
++};
++
++struct mxc_runtime_data {
++ int dma_ch;
++ struct mxc_pcm_dma_param *dma_params;
++ spinlock_t dma_lock;
++ int active, period, periods;
++ int dma_wchannel;
++ int tx_spin, rx_spin;
++ int old_offset;
++};
++
++/*!
++ * This function stops the current dma transfer for playback
++ * and clears the dma pointers.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ */
++static void audio_stop_dma(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
++ unsigned int offset = dma_size * runtime->periods;
++ unsigned long flags;
++
++ spin_lock_irqsave(&prtd->dma_lock, flags);
++
++ dbg("MXC : audio_stop_dma active = 0\n");
++ prtd->active = 0;
++ prtd->period = 0;
++ prtd->periods = 0;
++
++ /* this stops the dma channel and clears the buffer ptrs */
++ mxc_dma_stop(prtd->dma_wchannel);
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_TO_DEVICE);
++ else
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_FROM_DEVICE);
++
++ spin_unlock_irqrestore(&prtd->dma_lock, flags);
++}
++
++/*!
++ * This function is called whenever a new audio block needs to be
++ * transferred to mc13783. The function receives the address and the size
++ * of the new block and start a new DMA transfer.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ */
++static int dma_new_period(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int dma_size;
++ unsigned int offset;
++ int ret = 0;
++ dma_request_t sdma_request;
++
++ if (prtd->active){
++ memset(&sdma_request, 0, sizeof(dma_request_t));
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ dbg("s->period (%x) runtime->periods (%d)\n",
++ s->period,runtime->periods);
++ dbg("runtime->period_size (%d) dma_size (%d)\n",
++ (unsigned int)runtime->period_size,
++ runtime->dma_bytes);
++
++ offset = dma_size * prtd->period;
++// snd_assert(dma_size <= DMA_BUF_SIZE, );
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
++ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
++ else
++ sdma_request.destAddr = (char*)(dma_map_single(NULL,
++ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
++ sdma_request.count = dma_size;
++
++ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
++ runtime->dma_bytes);
++
++ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
++ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
++ dbg("audio_process_dma: cannot queue DMA buffer\
++ (%i)\n", ret);
++ return ret;
++ }
++ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
++ prtd->period++;
++ prtd->period %= runtime->periods;
++ }
++ return ret;
++}
++
++
++/*!
++ * This is a callback which will be called
++ * when a TX transfer finishes. The call occurs
++ * in interrupt context.
++ *
++ * @param dat pointer to the structure of the current stream.
++ *
++ */
++static void audio_dma_irq(void *data)
++{
++ struct snd_pcm_substream *substream;
++ struct snd_pcm_runtime *runtime;
++ struct mxc_runtime_data *prtd;
++ unsigned int dma_size;
++ unsigned int previous_period;
++ unsigned int offset;
++
++ substream = data;
++ runtime = substream->runtime;
++ prtd = runtime->private_data;
++ previous_period = prtd->periods;
++ dma_size = frames_to_bytes(runtime, runtime->period_size);
++ offset = dma_size * previous_period;
++
++ prtd->tx_spin = 0;
++ prtd->periods++;
++ prtd->periods %= runtime->periods;
++
++ /*
++ * Give back to the CPU the access to the non cached memory
++ */
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_TO_DEVICE);
++ else
++ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
++ DMA_FROM_DEVICE);
++ /*
++ * If we are getting a callback for an active stream then we inform
++ * the PCM middle layer we've finished a period
++ */
++ if (prtd->active)
++ snd_pcm_period_elapsed(substream);
++
++ /*
++ * Trig next DMA transfer
++ */
++ dma_new_period(substream);
++}
++
++/*!
++ * This function configures the hardware to allow audio
++ * playback operations. It is called by ALSA framework.
++ *
++ * @param substream pointer to the structure of the current stream.
++ *
++ * @return 0 on success, -1 otherwise.
++ */
++static int
++mxc_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ dma_channel_params *params = rtd->dai->cpu_dai->dma_data;
++ int ret = 0, channel = 0; // passed in ?;
++
++ prtd->period = 0;
++ prtd->periods = 0;
++
++ if(substream == SNDRV_PCM_STREAM_PLAYBACK) {
++ ret = mxc_request_dma(&channel, "ALSA TX SDMA");
++ if (ret < 0) {
++ dbg("error requesting a write dma channel\n");
++ return ret;
++ }
++
++ } else {
++ ret = mxc_request_dma(&channel, "ALSA RX SDMA");
++ if (ret < 0) {
++ dbg("error requesting a read dma channel\n");
++ return ret;
++ }
++ }
++
++ /* set up chn with params */
++ params->callback = audio_dma_irq;
++ mxc_dma_setup_channel(channel, params);
++ prtd->dma_wchannel = channel;
++
++ return ret;
++}
++
++static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ int ret;
++
++ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
++ if(ret < 0)
++ return ret;
++ runtime->dma_addr = virt_to_phys(runtime->dma_area);
++
++ return ret;
++}
++
++static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct mxc_runtime_data *prtd = substream->runtime->private_data;
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ prtd->tx_spin = 0;
++ /* requested stream startup */
++ prtd->active = 1;
++ ret = dma_new_period(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ /* requested stream shutdown */
++ audio_stop_dma(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ prtd->active = 0;
++ prtd->periods = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ prtd->active = 1;
++ prtd->tx_spin = 0;
++ ret = dma_new_period(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ prtd->active = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ prtd->active = 1;
++ if (prtd->old_offset) {
++ prtd->tx_spin = 0;
++ ret = dma_new_period(substream);
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++ unsigned int offset = 0;
++
++ /* tx_spin value is used here to check if a transfert is active */
++ if (prtd->tx_spin){
++ offset = (runtime->period_size * (prtd->periods)) +
++ (runtime->period_size >> 1);
++ if (offset >= runtime->buffer_size)
++ offset = runtime->period_size >> 1;
++ } else {
++ offset = (runtime->period_size * (prtd->periods));
++ if (offset >= runtime->buffer_size)
++ offset = 0;
++ }
++
++ return offset;
++}
++
++
++static int mxc_pcm_open(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd;
++ int ret;
++
++ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
++
++ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
++ if (ret < 0)
++ return ret;
++ //ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
++ // &hw_playback_rates);
++ //if (ret < 0)
++ // return ret;
++
++ prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL);
++ if(prtd == NULL)
++ return -ENOMEM;
++
++ runtime->private_data = prtd;
++ return 0;
++}
++
++static int mxc_pcm_close(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct mxc_runtime_data *prtd = runtime->private_data;
++
++ kfree(prtd);
++ return 0;
++}
++
++static int
++mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
++ runtime->dma_area,
++ runtime->dma_addr,
++ runtime->dma_bytes);
++}
++
++struct snd_pcm_ops mxc_pcm_ops = {
++ .open = mxc_pcm_open,
++ .close = mxc_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = mxc_pcm_hw_params,
++ .hw_free = mxc_pcm_hw_free,
++ .prepare = mxc_pcm_prepare,
++ .trigger = mxc_pcm_trigger,
++ .pointer = mxc_pcm_pointer,
++ .mmap = mxc_pcm_mmap,
++};
++
++static u64 mxc_pcm_dmamask = 0xffffffff;
++
++int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
++ struct snd_pcm *pcm)
++{
++ int ret = 0;
++
++ if (!card->dev->dma_mask)
++ card->dev->dma_mask = &mxc_pcm_dmamask;
++ if (!card->dev->coherent_dma_mask)
++ card->dev->coherent_dma_mask = 0xffffffff;
++
++ return ret;
++}
++
++struct snd_soc_platform mxc_soc_platform = {
++ .name = "mxc-audio",
++ .pcm_ops = &mxc_pcm_ops,
++ .pcm_new = mxc_pcm_new,
++};
++
++EXPORT_SYMBOL_GPL(mxc_soc_platform);
++
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_DESCRIPTION("Freescale i.MX31 PCM DMA module");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/imx/imx31-pcm.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx31-pcm.h 2007-07-16 15:07:34.820321566 +0200
+@@ -0,0 +1,241 @@
++/*
++ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _MXC_PCM_H
++#define _MXC_PCM_H
++
++#include <asm/arch/dma.h>
++
++/* temp until imx-regs.h is up2date */
++#define SSI1_STX0 (SSI1_BASE_ADDR + 0x00)
++#define SSI1_STX0_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x00)
++#define SSI1_STX1 (SSI1_BASE_ADDR + 0x04)
++#define SSI1_STX1_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x04)
++#define SSI1_SRX0 (SSI1_BASE_ADDR + 0x08)
++#define SSI1_SRX0_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x08)
++#define SSI1_SRX1 (SSI1_BASE_ADDR + 0x0c)
++#define SSI1_SRX1_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x0c)
++#define SSI1_SCR (SSI1_BASE_ADDR + 0x10)
++#define SSI1_SISR (SSI1_BASE_ADDR + 0x14)
++#define SSI1_SIER (SSI1_BASE_ADDR + 0x18)
++#define SSI1_STCR (SSI1_BASE_ADDR + 0x1c)
++#define SSI1_SRCR (SSI1_BASE_ADDR + 0x20)
++#define SSI1_STCCR (SSI1_BASE_ADDR + 0x24)
++#define SSI1_SRCCR (SSI1_BASE_ADDR + 0x28)
++#define SSI1_SFCSR (SSI1_BASE_ADDR + 0x2c)
++#define SSI1_STR (SSI1_BASE_ADDR + 0x30)
++#define SSI1_SOR (SSI1_BASE_ADDR + 0x34)
++#define SSI1_SACNT (SSI1_BASE_ADDR + 0x38)
++#define SSI1_SACADD (SSI1_BASE_ADDR + 0x3c)
++#define SSI1_SACDAT (SSI1_BASE_ADDR + 0x40)
++#define SSI1_SATAG (SSI1_BASE_ADDR + 0x44)
++#define SSI1_STMSK (SSI1_BASE_ADDR + 0x48)
++#define SSI1_SRMSK (SSI1_BASE_ADDR + 0x4c)
++
++#define SSI2_STX0 (SSI2_BASE_ADDR + 0x00)
++#define SSI2_STX0_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x00)
++#define SSI2_STX1 (SSI2_BASE_ADDR + 0x04)
++#define SSI2_STX1_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x04)
++#define SSI2_SRX0 (SSI2_BASE_ADDR + 0x08)
++#define SSI2_SRX0_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x08)
++#define SSI2_SRX1 (SSI2_BASE_ADDR + 0x0c)
++#define SSI2_SRX1_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x0c)
++#define SSI2_SCR (SSI2_BASE_ADDR + 0x10)
++#define SSI2_SISR (SSI2_BASE_ADDR + 0x14)
++#define SSI2_SIER (SSI2_BASE_ADDR + 0x18)
++#define SSI2_STCR (SSI2_BASE_ADDR + 0x1c)
++#define SSI2_SRCR (SSI2_BASE_ADDR + 0x20)
++#define SSI2_STCCR (SSI2_BASE_ADDR + 0x24)
++#define SSI2_SRCCR (SSI2_BASE_ADDR + 0x28)
++#define SSI2_SFCSR (SSI2_BASE_ADDR + 0x2c)
++#define SSI2_STR (SSI2_BASE_ADDR + 0x30)
++#define SSI2_SOR (SSI2_BASE_ADDR + 0x34)
++#define SSI2_SACNT (SSI2_BASE_ADDR + 0x38)
++#define SSI2_SACADD (SSI2_BASE_ADDR + 0x3c)
++#define SSI2_SACDAT (SSI2_BASE_ADDR + 0x40)
++#define SSI2_SATAG (SSI2_BASE_ADDR + 0x44)
++#define SSI2_STMSK (SSI2_BASE_ADDR + 0x48)
++#define SSI2_SRMSK (SSI2_BASE_ADDR + 0x4c)
++
++#define SSI_SCR_CLK_IST (1 << 9)
++#define SSI_SCR_TCH_EN (1 << 8)
++#define SSI_SCR_SYS_CLK_EN (1 << 7)
++#define SSI_SCR_I2S_MODE_NORM (0 << 5)
++#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
++#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
++#define SSI_SCR_SYN (1 << 4)
++#define SSI_SCR_NET (1 << 3)
++#define SSI_SCR_RE (1 << 2)
++#define SSI_SCR_TE (1 << 1)
++#define SSI_SCR_SSIEN (1 << 0)
++
++#define SSI_SISR_CMDAU (1 << 18)
++#define SSI_SISR_CMDDU (1 << 17)
++#define SSI_SISR_RXT (1 << 16)
++#define SSI_SISR_RDR1 (1 << 15)
++#define SSI_SISR_RDR0 (1 << 14)
++#define SSI_SISR_TDE1 (1 << 13)
++#define SSI_SISR_TDE0 (1 << 12)
++#define SSI_SISR_ROE1 (1 << 11)
++#define SSI_SISR_ROE0 (1 << 10)
++#define SSI_SISR_TUE1 (1 << 9)
++#define SSI_SISR_TUE0 (1 << 8)
++#define SSI_SISR_TFS (1 << 7)
++#define SSI_SISR_RFS (1 << 6)
++#define SSI_SISR_TLS (1 << 5)
++#define SSI_SISR_RLS (1 << 4)
++#define SSI_SISR_RFF1 (1 << 3)
++#define SSI_SISR_RFF0 (1 << 2)
++#define SSI_SISR_TFE1 (1 << 1)
++#define SSI_SISR_TFE0 (1 << 0)
++
++#define SSI_SIER_RDMAE (1 << 22)
++#define SSI_SIER_RIE (1 << 21)
++#define SSI_SIER_TDMAE (1 << 20)
++#define SSI_SIER_TIE (1 << 19)
++#define SSI_SIER_CMDAU_EN (1 << 18)
++#define SSI_SIER_CMDDU_EN (1 << 17)
++#define SSI_SIER_RXT_EN (1 << 16)
++#define SSI_SIER_RDR1_EN (1 << 15)
++#define SSI_SIER_RDR0_EN (1 << 14)
++#define SSI_SIER_TDE1_EN (1 << 13)
++#define SSI_SIER_TDE0_EN (1 << 12)
++#define SSI_SIER_ROE1_EN (1 << 11)
++#define SSI_SIER_ROE0_EN (1 << 10)
++#define SSI_SIER_TUE1_EN (1 << 9)
++#define SSI_SIER_TUE0_EN (1 << 8)
++#define SSI_SIER_TFS_EN (1 << 7)
++#define SSI_SIER_RFS_EN (1 << 6)
++#define SSI_SIER_TLS_EN (1 << 5)
++#define SSI_SIER_RLS_EN (1 << 4)
++#define SSI_SIER_RFF1_EN (1 << 3)
++#define SSI_SIER_RFF0_EN (1 << 2)
++#define SSI_SIER_TFE1_EN (1 << 1)
++#define SSI_SIER_TFE0_EN (1 << 0)
++
++#define SSI_STCR_TXBIT0 (1 << 9)
++#define SSI_STCR_TFEN1 (1 << 8)
++#define SSI_STCR_TFEN0 (1 << 7)
++#define SSI_STCR_TFDIR (1 << 6)
++#define SSI_STCR_TXDIR (1 << 5)
++#define SSI_STCR_TSHFD (1 << 4)
++#define SSI_STCR_TSCKP (1 << 3)
++#define SSI_STCR_TFSI (1 << 2)
++#define SSI_STCR_TFSL (1 << 1)
++#define SSI_STCR_TEFS (1 << 0)
++
++#define SSI_SRCR_RXBIT0 (1 << 9)
++#define SSI_SRCR_RFEN1 (1 << 8)
++#define SSI_SRCR_RFEN0 (1 << 7)
++#define SSI_SRCR_RFDIR (1 << 6)
++#define SSI_SRCR_RXDIR (1 << 5)
++#define SSI_SRCR_RSHFD (1 << 4)
++#define SSI_SRCR_RSCKP (1 << 3)
++#define SSI_SRCR_RFSI (1 << 2)
++#define SSI_SRCR_RFSL (1 << 1)
++#define SSI_SRCR_REFS (1 << 0)
++
++#define SSI_STCCR_DIV2 (1 << 18)
++#define SSI_STCCR_PSR (1 << 15)
++#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
++#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
++#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
++
++#define SSI_SRCCR_DIV2 (1 << 18)
++#define SSI_SRCCR_PSR (1 << 15)
++#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
++#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
++#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
++
++
++#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
++#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
++#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
++#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
++#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
++#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
++#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
++#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
++
++#define SSI_STR_TEST (1 << 15)
++#define SSI_STR_RCK2TCK (1 << 14)
++#define SSI_STR_RFS2TFS (1 << 13)
++#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
++#define SSI_STR_TXD2RXD (1 << 7)
++#define SSI_STR_TCK2RCK (1 << 6)
++#define SSI_STR_TFS2RFS (1 << 5)
++#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
++
++#define SSI_SOR_CLKOFF (1 << 6)
++#define SSI_SOR_RX_CLR (1 << 5)
++#define SSI_SOR_TX_CLR (1 << 4)
++#define SSI_SOR_INIT (1 << 3)
++#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
++#define SSI_SOR_SYNRST (1 << 0)
++
++#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
++#define SSI_SACNT_WR (x << 4)
++#define SSI_SACNT_RD (x << 3)
++#define SSI_SACNT_TIF (x << 2)
++#define SSI_SACNT_FV (x << 1)
++#define SSI_SACNT_AC97EN (x << 0)
++
++
++/* AUDMUX registers */
++#define AUDMUX_HPCR1 (IMX_AUDMUX_BASE + 0x00)
++#define AUDMUX_HPCR2 (IMX_AUDMUX_BASE + 0x04)
++#define AUDMUX_HPCR3 (IMX_AUDMUX_BASE + 0x08)
++#define AUDMUX_PPCR1 (IMX_AUDMUX_BASE + 0x10)
++#define AUDMUX_PPCR2 (IMX_AUDMUX_BASE + 0x14)
++#define AUDMUX_PPCR3 (IMX_AUDMUX_BASE + 0x18)
++
++#define AUDMUX_HPCR_TFSDIR (1 << 31)
++#define AUDMUX_HPCR_TCLKDIR (1 << 30)
++#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
++#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
++#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
++#define AUDMUX_HPCR_RFSDIR (1 << 25)
++#define AUDMUX_HPCR_RCLKDIR (1 << 24)
++#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
++#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
++#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
++#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
++#define AUDMUX_HPCR_SYN (1 << 12)
++#define AUDMUX_HPCR_TXRXEN (1 << 10)
++#define AUDMUX_HPCR_INMEN (1 << 8)
++#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
++
++#define AUDMUX_PPCR_TFSDIR (1 << 31)
++#define AUDMUX_PPCR_TCLKDIR (1 << 30)
++#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
++#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
++#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
++#define AUDMUX_PPCR_RFSDIR (1 << 25)
++#define AUDMUX_PPCR_RCLKDIR (1 << 24)
++#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
++#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
++#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
++#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
++#define AUDMUX_PPCR_SYN (1 << 12)
++#define AUDMUX_PPCR_TXRXEN (1 << 10)
++
++#define SDMA_TXFIFO_WATERMARK 0x4
++#define SDMA_RXFIFO_WATERMARK 0x6
++
++struct mxc_pcm_dma_params {
++ char *name; /* stream identifier */
++ dma_channel_params params;
++};
++
++extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
++
++/* platform data */
++extern struct snd_soc_platform mxc_soc_platform;
++extern struct snd_ac97_bus_ops mxc_ac97_ops;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/pxa/magician.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/magician.c 2007-07-16 15:07:34.844322935 +0200
+@@ -0,0 +1,563 @@
++/*
++ * SoC audio for HTC Magician
++ *
++ * Copyright (c) 2006 Philipp Zabel <philipp.zabel at gmail.com>
++ *
++ * based on spitz.c,
++ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ * Richard Purdie <richard at openedhand.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware/scoop.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/magician.h>
++#include <asm/arch/magician_cpld.h>
++#include <asm/mach-types.h>
++#include "../codecs/uda1380.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++#include "pxa2xx-ssp.h"
++
++#define MAGICIAN_HP_OFF 0
++#define MAGICIAN_HEADSET 1
++#define MAGICIAN_HP 2
++
++#define MAGICIAN_SPK_ON 0
++#define MAGICIAN_SPK_OFF 1
++
++#define MAGICIAN_MIC 0
++#define MAGICIAN_MIC_EXT 1
++#define MAGICIAN_BT_HS 2
++
++/*
++ * SSP GPIO's
++ */
++#define GPIO26_SSP1RX_MD (26 | GPIO_ALT_FN_1_IN)
++#define GPIO25_SSP1TX_MD (25 | GPIO_ALT_FN_2_OUT)
++#define GPIO23_SSP1CLKS_MD (23 | GPIO_ALT_FN_2_IN)
++#define GPIO24_SSP1FRMS_MD (24 | GPIO_ALT_FN_2_IN)
++#define GPIO23_SSP1CLKM_MD (23 | GPIO_ALT_FN_2_OUT)
++#define GPIO24_SSP1FRMM_MD (24 | GPIO_ALT_FN_2_OUT)
++#define GPIO53_SSP1SYSCLK_MD (53 | GPIO_ALT_FN_2_OUT)
++
++static int magician_jack_func = MAGICIAN_HP_OFF;
++static int magician_spk_func = MAGICIAN_SPK_ON;
++static int magician_in_sel = MAGICIAN_MIC;
++
++extern struct platform_device magician_cpld;
++
++static void magician_ext_control(struct snd_soc_codec *codec)
++{
++ if (magician_spk_func == MAGICIAN_SPK_ON)
++ snd_soc_dapm_set_endpoint(codec, "Speaker", 1);
++ else
++ snd_soc_dapm_set_endpoint(codec, "Speaker", 0);
++
++ /* set up jack connection */
++ switch (magician_jack_func) {
++ case MAGICIAN_HP:
++ /* enable and unmute hp jack, disable mic bias */
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
++ break;
++ case MAGICIAN_HEADSET:
++ /* enable mic jack and bias, mute hp */
++ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
++ break;
++ case MAGICIAN_HP_OFF:
++ /* jack removed, everything off */
++ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
++ break;
++ }
++#if 0
++ /* fixme pH5, can we detect and config the correct Mic type ? */
++ switch(magician_in_sel) {
++ case MAGICIAN_IN_MIC:
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
++ break;
++ case MAGICIAN_IN_MIC_EXT:
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
++ break;
++ case MAGICIAN_IN_BT_HS:
++ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
++ break;
++ }
++#endif
++ snd_soc_dapm_sync_endpoints(codec);
++}
++
++static int magician_startup(snd_pcm_substream_t *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->codec;
++
++ /* check the jack status at stream startup */
++ magician_ext_control(codec);
++
++ return 0;
++}
++
++/*
++ * Magician uses SSP port for playback.
++ */
++static int magician_playback_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int acps, acds, div4;
++ int ret = 0;
++
++ /*
++ * Rate = SSPSCLK / (word size(16))
++ * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1)
++ */
++ switch (params_rate(params)) {
++ case 8000:
++ acps = 32842000;
++ acds = PXA2XX_SSP_CLK_AUDIO_DIV_32; /* wrong - 32 bits/sample */
++ div4 = PXA2XX_SSP_CLK_SCDB_4;
++ break;
++ case 11025:
++ acps = 5622000;
++ acds = PXA2XX_SSP_CLK_AUDIO_DIV_8; /* 16 bits/sample, 1 slot */
++ div4 = PXA2XX_SSP_CLK_SCDB_4;
++ break;
++ case 22050:
++ acps = 5622000;
++ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
++ div4 = PXA2XX_SSP_CLK_SCDB_4;
++ break;
++ case 44100:
++ acps = 11345000;
++ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
++ div4 = PXA2XX_SSP_CLK_SCDB_4;
++ break;
++ case 48000:
++ acps = 12235000;
++ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
++ div4 = PXA2XX_SSP_CLK_SCDB_4;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set audio clock as clock source */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_AUDIO, 0,
++ SND_SOC_CLOCK_OUT);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP audio system clock ACDS divider */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
++ PXA2XX_SSP_AUDIO_DIV_ACDS, acds);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP audio system clock SCDB divider4 */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
++ PXA2XX_SSP_AUDIO_DIV_SCDB, div4);
++ if (ret < 0)
++ return ret;
++
++ /* set SSP audio pll clock */
++ ret = cpu_dai->dai_ops.set_pll(cpu_dai, 0, 0, acps);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/*
++ * We have to enable the SSP port early so the UDA1380 can flush
++ * it's register cache. The UDA1380 can only write it's interpolator and
++ * decimator registers when the link is running.
++ */
++static int magician_playback_prepare(struct snd_pcm_substream *substream)
++{
++ /* enable SSP clock - is this needed ? */
++ SSCR0_P(1) |= SSCR0_SSE;
++
++ /* FIXME: ENABLE I2S */
++ SACR0 |= SACR0_BCKD;
++ SACR0 |= SACR0_ENB;
++ pxa_set_cken(CKEN8_I2S, 1);
++
++ return 0;
++}
++
++static int magician_playback_hw_free(struct snd_pcm_substream *substream)
++{
++ /* FIXME: DISABLE I2S */
++ SACR0 &= ~SACR0_ENB;
++ SACR0 &= ~SACR0_BCKD;
++ pxa_set_cken(CKEN8_I2S, 0);
++ return 0;
++}
++
++/*
++ * Magician uses I2S for capture.
++ */
++static int magician_capture_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ int ret = 0;
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai,
++ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set the I2S system clock as output */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
++ SND_SOC_CLOCK_OUT);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/*
++ * We have to enable the I2S port early so the UDA1380 can flush
++ * it's register cache. The UDA1380 can only write it's interpolator and
++ * decimator registers when the link is running.
++ */
++static int magician_capture_prepare(struct snd_pcm_substream *substream)
++{
++ SACR0 |= SACR0_ENB;
++ return 0;
++}
++
++static struct snd_soc_ops magician_capture_ops = {
++ .startup = magician_startup,
++ .hw_params = magician_capture_hw_params,
++ .prepare = magician_capture_prepare,
++};
++
++static struct snd_soc_ops magician_playback_ops = {
++ .startup = magician_startup,
++ .hw_params = magician_playback_hw_params,
++ .prepare = magician_playback_prepare,
++ .hw_free = magician_playback_hw_free,
++};
++
++static int magician_get_jack(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ ucontrol->value.integer.value[0] = magician_jack_func;
++ return 0;
++}
++
++static int magician_set_jack(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (magician_jack_func == ucontrol->value.integer.value[0])
++ return 0;
++
++ magician_jack_func = ucontrol->value.integer.value[0];
++ magician_ext_control(codec);
++ return 1;
++}
++
++static int magician_get_spk(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ ucontrol->value.integer.value[0] = magician_spk_func;
++ return 0;
++}
++
++static int magician_set_spk(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (magician_spk_func == ucontrol->value.integer.value[0])
++ return 0;
++
++ magician_spk_func = ucontrol->value.integer.value[0];
++ magician_ext_control(codec);
++ return 1;
++}
++
++static int magician_get_input(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ ucontrol->value.integer.value[0] = magician_in_sel;
++ return 0;
++}
++
++static int magician_set_input(snd_kcontrol_t * kcontrol,
++ snd_ctl_elem_value_t * ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (magician_in_sel == ucontrol->value.integer.value[0])
++ return 0;
++
++ magician_in_sel = ucontrol->value.integer.value[0];
++
++ switch (magician_in_sel) {
++ case MAGICIAN_MIC:
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_IN_SEL0);
++ magician_egpio_enable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_IN_SEL1);
++ break;
++ case MAGICIAN_MIC_EXT:
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_IN_SEL0);
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_IN_SEL1);
++ }
++
++ return 1;
++}
++
++static int magician_spk_power(struct snd_soc_dapm_widget *w, int event)
++{
++ if (SND_SOC_DAPM_EVENT_ON(event))
++ magician_egpio_enable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_SPK_POWER);
++ else
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_SPK_POWER);
++ return 0;
++}
++
++static int magician_hp_power(struct snd_soc_dapm_widget *w, int event)
++{
++ if (SND_SOC_DAPM_EVENT_ON(event))
++ magician_egpio_enable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_EP_POWER);
++ else
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_EP_POWER);
++ return 0;
++}
++
++static int magician_mic_bias(struct snd_soc_dapm_widget *w, int event)
++{
++ if (SND_SOC_DAPM_EVENT_ON(event))
++ magician_egpio_enable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_MIC_POWER);
++ else
++ magician_egpio_disable(&magician_cpld,
++ EGPIO_NR_MAGICIAN_MIC_POWER);
++ return 0;
++}
++
++/* magician machine dapm widgets */
++static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
++ SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
++ SND_SOC_DAPM_MIC("Mic Jack", magician_mic_bias),
++ SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
++};
++
++/* magician machine audio_map */
++static const char *audio_map[][3] = {
++
++ /* headphone connected to VOUTLHP, VOUTRHP */
++ {"Headphone Jack", NULL, "VOUTLHP"},
++ {"Headphone Jack", NULL, "VOUTRHP"},
++
++ /* ext speaker connected to VOUTL, VOUTR */
++ {"Speaker", NULL, "VOUTL"},
++ {"Speaker", NULL, "VOUTR"},
++
++ /* mic is connected to VINM */
++ {"VINM", NULL, "Mic Jack"},
++
++ /* line is connected to VINL, VINR */
++ {"VINL", NULL, "Line Jack"},
++ {"VINR", NULL, "Line Jack"},
++
++ {NULL, NULL, NULL},
++};
++
++static const char *jack_function[] = { "Off", "Headset", "Headphone" };
++static const char *spk_function[] = { "On", "Off" };
++static const char *input_select[] = { "Internal Mic", "External Mic" };
++static const struct soc_enum magician_enum[] = {
++ SOC_ENUM_SINGLE_EXT(4, jack_function),
++ SOC_ENUM_SINGLE_EXT(2, spk_function),
++ SOC_ENUM_SINGLE_EXT(2, input_select),
++};
++
++static const struct snd_kcontrol_new uda1380_magician_controls[] = {
++ SOC_ENUM_EXT("Jack Function", magician_enum[0], magician_get_jack,
++ magician_set_jack),
++ SOC_ENUM_EXT("Speaker Function", magician_enum[1], magician_get_spk,
++ magician_set_spk),
++ SOC_ENUM_EXT("Input Select", magician_enum[2], magician_get_input,
++ magician_set_input),
++};
++
++/*
++ * Logic for a uda1380 as connected on a HTC Magician
++ */
++static int magician_uda1380_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* NC codec pins */
++ snd_soc_dapm_set_endpoint(codec, "VOUTLHP", 0);
++ snd_soc_dapm_set_endpoint(codec, "VOUTRHP", 0);
++
++ /* Add magician specific controls */
++ for (i = 0; i < ARRAY_SIZE(uda1380_magician_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&uda1380_magician_controls[i],
++ codec, NULL))) < 0)
++ return err;
++ }
++
++ /* Add magician specific widgets */
++ for (i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]);
++ }
++
++ /* Set up magician specific audio path interconnects */
++ for (i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++/* magician digital audio interface glue - connects codec <--> CPU */
++static struct snd_soc_dai_link magician_dai[] = {
++{
++ .name = "uda1380",
++ .stream_name = "UDA1380 Playback",
++ .cpu_dai = &pxa_ssp_dai[0],
++ .codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK],
++ .init = magician_uda1380_init,
++ .ops = &magician_playback_ops,
++},
++{
++ .name = "uda1380",
++ .stream_name = "UDA1380 Capture",
++ .cpu_dai = &pxa_i2s_dai,
++ .codec_dai = &uda1380_dai[UDA1380_DAI_CAPTURE],
++ .ops = &magician_capture_ops,
++}
++};
++
++/* magician audio machine driver */
++static struct snd_soc_machine snd_soc_machine_magician = {
++ .name = "Magician",
++ .dai_link = magician_dai,
++ .num_links = ARRAY_SIZE(magician_dai),
++};
++
++/* magician audio private data */
++static struct uda1380_setup_data magician_uda1380_setup = {
++ .i2c_address = 0x18,
++ .dac_clk = UDA1380_DAC_CLK_WSPLL,
++};
++
++/* magician audio subsystem */
++static struct snd_soc_device magician_snd_devdata = {
++ .machine = &snd_soc_machine_magician,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_uda1380,
++ .codec_data = &magician_uda1380_setup,
++};
++
++static struct platform_device *magician_snd_device;
++
++static int __init magician_init(void)
++{
++ int ret;
++
++ if (!machine_is_magician())
++ return -ENODEV;
++
++ magician_egpio_enable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_POWER);
++
++ /* we may need to have the clock running here - pH5 */
++ magician_egpio_enable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_RESET);
++ udelay(5);
++ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_RESET);
++
++ magician_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!magician_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(magician_snd_device, &magician_snd_devdata);
++ magician_snd_devdata.dev = &magician_snd_device->dev;
++ ret = platform_device_add(magician_snd_device);
++
++ if (ret)
++ platform_device_put(magician_snd_device);
++
++ pxa_gpio_mode(GPIO53_SSP1SYSCLK_MD);
++ pxa_gpio_mode(GPIO26_SSP1RX_MD);
++ pxa_gpio_mode(GPIO25_SSP1TX_MD);
++ pxa_gpio_mode(GPIO23_SSP1CLKM_MD);
++ pxa_gpio_mode(GPIO24_SSP1FRMM_MD);
++
++ return ret;
++}
++
++static void __exit magician_exit(void)
++{
++ platform_device_unregister(magician_snd_device);
++
++ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_SPK_POWER);
++ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_EP_POWER);
++ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_MIC_POWER);
++ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_POWER);
++}
++
++module_init(magician_init);
++module_exit(magician_exit);
++
++MODULE_AUTHOR("Philipp Zabel");
++MODULE_DESCRIPTION("ALSA SoC Magician");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/templates/template-ac97.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-ac97.c 2007-07-16 15:07:34.868324304 +0200
+@@ -0,0 +1,270 @@
++/*
++ * ltemplate-ac97.c -- AC97 support for the xxx chip.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++#include <linux/mutex.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++
++#include <asm/irq.h>
++#include <asm/hardware.h>
++
++#include "template-pcm.h"
++
++#define AC97_DIR \
++ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
++
++#define AC97_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
++
++/* DAI description of AC97 controllers capabilities */
++static struct snd_soc_dai_mode template_ac97_modes[] = {
++ {
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = AC97_RATES,
++ .pcmdir = AC97_DIR,
++ },
++};
++
++/* AC97 controlller reads codec register */
++static unsigned short template_ac97_read(struct snd_ac97 *ac97,
++ unsigned short reg)
++{
++}
++
++/* AC97 controller writes to codec register */
++static void template_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
++ unsigned short val)
++{
++}
++
++/* AC97 controller asserts a warm reset */
++static void template_ac97_warm_reset(struct snd_ac97 *ac97)
++{
++}
++
++/* AC97 controller asserts a cold reset */
++static void template_ac97_cold_reset(struct snd_ac97 *ac97)
++{
++}
++
++/* AC97 controller operations */
++struct snd_ac97_bus_ops soc_ac97_ops = {
++ .read = template_ac97_read,
++ .write = template_ac97_write,
++ .warm_reset = template_ac97_warm_reset,
++ .reset = template_ac97_cold_reset,
++};
++EXPORT_SYMBOL_GPL(soc_ac97_ops);
++
++/* DMA structure describing platform specific AC97 DMA for each logical DAI */
++static struct template_pcm_dma_params template_ac97_pcm_stereo_out = {
++ .name = "AC97 PCM Stereo out",
++ .dev_addr = __PREG(PCDR),
++};
++
++static struct template_pcm_dma_params template_ac97_pcm_stereo_in = {
++ .name = "AC97 PCM Stereo in",
++ .dev_addr = __PREG(PCDR),
++};
++
++static struct template_pcm_dma_params template_ac97_pcm_aux_mono_out = {
++ .name = "AC97 Aux PCM (Slot 5) Mono out",
++ .dev_addr = __PREG(MODR),
++};
++
++static struct template_pcm_dma_params template_ac97_pcm_aux_mono_in = {
++ .name = "AC97 Aux PCM (Slot 5) Mono in",
++ .dev_addr = __PREG(MODR),
++};
++
++static struct template_pcm_dma_params template_ac97_pcm_mic_mono_in = {
++ .name = "AC97 Mic PCM (Slot 6) Mono in",
++ .dev_addr = __PREG(MCDR),
++};
++
++#ifdef CONFIG_PM
++/* suspend the AC97 controller */
++static int template_ac97_suspend(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++}
++
++/* resume the AC97 controller */
++static int template_ac97_resume(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++}
++
++#else
++#define template_ac97_suspend NULL
++#define template_ac97_resume NULL
++#endif
++
++/*
++ * Probe initialises the AC97 controller. e.g.
++ * request any IRQ's
++ * configure GPIO's
++ * enable any clocks
++ */
++static int template_ac97_probe(struct platform_device *pdev)
++{
++}
++
++/*
++ * Free's resources setup in probe()
++ */
++static void template_ac97_remove(struct platform_device *pdev)
++{
++}
++
++/*
++ * Alsa operations
++ * Only implement the required operations for your platform.
++ * These operations are specific to the AC97 controller and DAI only.
++ */
++
++ /*
++ * Called by ALSA when a PCM substream is opened, private data can be allocated.
++ */
++static int template_ac97_startup(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when a PCM substream is closed. Private data can be
++ * freed here.
++ */
++static int template_ac97_shutdown(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the PCM substream is prepared, can set format, sample
++ * rate, etc. This function is non atomic and can be called multiple times,
++ * it can refer to the runtime info.
++ */
++static int template_ac97_prepare(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the hardware params are set by application. This
++ * function can also be called multiple times and can allocate buffers
++ * (using snd_pcm_lib_* ). It's non-atomic.
++ */
++static int template_ac97_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++}
++
++/*
++ * Free's resources allocated by hw_params, can be called multiple times
++ */
++static int template_ac97_hw_free(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Starts (Triggers) audio playback or capture.
++ * Usually only needed for DMA
++ */
++static int template_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++}
++
++/*
++ * Define each AC97 slot grouping as a DAI.
++ *
++ * e.g. (below)
++ * Slots 3 & 4 = HiFi DAI
++ * Slot 5 = Aux playback
++ * Slot 7 = Mic Capture
++ *
++ * This gives 3 logical DAI's on the 1 physical AC97 DAI.
++ *
++ */
++struct snd_soc_cpu_dai template_ac97_dai[] = {
++{
++ .name = "template-ac97-HiFi",
++ .id = 0,
++ .type = SND_SOC_DAI_AC97,
++ /* DAI driver operations - only needed on 1st logical DAI in AC97 */
++ .probe = template_ac97_probe,
++ .remove = template_ac97_remove,
++ .suspend = template_ac97_suspend,
++ .resume = template_ac97_resume,
++ /* playback and capture stream info */
++ .playback = {
++ .stream_name = "AC97 Playback",
++ .channels_min = 2,
++ .channels_max = 2,},
++ .capture = {
++ .stream_name = "AC97 Capture",
++ .channels_min = 2,
++ .channels_max = 2,},
++ /* alsa PCM operations */
++ .ops = {
++ .startup = template_ac97_startup,
++ .shutdown = template_ac97_shutdown,
++ .prepare = template_ac97_prepare,
++ .trigger = template_ac97_trigger,
++ .hw_params = template_ac97_hw_params,
++ .hw_free = template_ac97_hw_free,},
++ /* DAI capabilities */
++ .caps = {
++ .num_modes = ARRAY_SIZE(template_ac97_modes),
++ .mode = template_ac97_modes,},
++},
++/* AC97 AUX playback - not supported on all controllers */
++{
++ .name = "template-ac97-aux",
++ .id = 1,
++ .type = SND_SOC_DAI_AC97,
++ .playback = {
++ .stream_name = "AC97 Aux Playback",
++ .channels_min = 1,
++ .channels_max = 1,},
++ .capture = {
++ .stream_name = "AC97 Aux Capture",
++ .channels_min = 1,
++ .channels_max = 1,},
++ .ops = {
++ .hw_params = template_ac97_hw_aux_params,},
++ .caps = {
++ .num_modes = ARRAY_SIZE(template_ac97_modes),
++ .mode = template_ac97_modes,},
++},
++/* AC97 Mic capture - not supported on all controllers */
++{
++ .name = "template-ac97-mic",
++ .id = 2,
++ .type = SND_SOC_DAI_AC97,
++ .capture = {
++ .stream_name = "AC97 Mic Capture",
++ .channels_min = 1,
++ .channels_max = 1,},
++ .ops = {
++ .hw_params = template_ac97_hw_mic_params,},
++ .caps = {
++ .num_modes = ARRAY_SIZE(template_ac97_modes),
++ .mode = template_ac97_modes,},},
++};
++EXPORT_SYMBOL_GPL(template_ac97_dai);
++
+Index: linux-2.6.22.1/sound/soc/templates/template-codec.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-codec.c 2007-07-16 15:07:34.892325670 +0200
+@@ -0,0 +1,784 @@
++/*
++ * template-codec.c -- Template Codec Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "template-codec.h"
++
++#define AUDIO_NAME "template-codec"
++#define TEMPLATE_VERSION "0.1"
++
++/*
++ * Debug
++ */
++
++#define TEMPLATE_DEBUG 0
++
++#ifdef TEMPLATE_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_template_codec;
++
++/*
++ * template_codec register cache
++ */
++static const u16 template_codec_reg[TEMPLATE_CACHEREGNUM] = {
++ 0x0097, 0x0097, 0x0079, 0x0079,
++ 0x000a, 0x0008, 0x009f, 0x000a,
++ 0x0000, 0x0000
++};
++
++/* Codec DAI can support these hardware formats */
++#define TEMPLATE_DAIFMT \
++ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
++ SND_SOC_DAIFMT_IB_IF)
++
++/* Codec DAI supports direction */
++#define TEMPLATE_DIR \
++ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
++
++/* Codec DAI supports rates */
++#define TEMPLATE_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++/* Codec DAI supports PCM word sizes */
++#define TEMPLATE_HIFI_BITS \
++ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
++
++/*
++ * Description of supported codec DAI supported modes.
++ */
++static struct snd_soc_dai_mode template_codec_modes[] = {
++ /* codec frame and clock master modes */
++ /* 8k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 1536,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 2304,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 1408,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 2112,
++ .bfs = 64,
++ },
++
++ /* 32k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_32000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 384,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_32000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 576,
++ .bfs = 64,
++ },
++
++ /* 44.1k & 48k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 256,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 384,
++ .bfs = 64,
++ },
++
++ /* 88.2 & 96k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 128,
++ .bfs = 64,
++ },
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .fs = 192,
++ .bfs = 64,
++ },
++
++ /* USB codec frame and clock master modes */
++ /* 8k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 1500,
++ .bfs = SND_SOC_FSBD(1),
++ },
++
++ /* 44.1k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_44100,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 272,
++ .bfs = SND_SOC_FSBD(1),
++ },
++
++ /* 48k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_48000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 250,
++ .bfs = SND_SOC_FSBD(1),
++ },
++
++ /* 88.2k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_88200,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 136,
++ .bfs = SND_SOC_FSBD(1),
++ },
++
++ /* 96k */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = SNDRV_PCM_RATE_96000,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 125,
++ .bfs = SND_SOC_FSBD(1),
++ },
++
++ /* codec frame and clock slave modes */
++ {
++ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = TEMPLATE_HIFI_BITS,
++ .pcmrate = TEMPLATE_RATES,
++ .pcmdir = TEMPLATE_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = SND_SOC_FS_ALL,
++ .bfs = SND_SOC_FSB_ALL,
++ },
++};
++
++/*
++ * read template_codec register cache
++ */
++static inline unsigned int template_codec_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++
++ if (reg >= TEMPLATE_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write template_codec register cache
++ */
++static inline void template_codec_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= TEMPLATE_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the template codec register space
++ */
++static int template_codec_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* format the data - codec specific */
++ data[0] = reg;
++ data[1] = value;
++
++ template_codec_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define template_codec_reset(c) template_codec_write(c, TEMPLATE_RESET, 0)
++
++
++/* template codec non DAPM controls */
++static const struct snd_kcontrol_new template_codec_snd_controls[] = {
++};
++
++/* add non dapm controls */
++static int template_codec_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(template_codec_snd_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&template_codec_snd_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* template codec DAPM controls */
++static const struct snd_soc_dapm_widget template_codec_dapm_widgets[] = {
++};
++
++/*
++ * template codec audio interconnectiosn between sink and source.
++ */
++static const char *audio_map[][3] = {
++
++
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int template_codec_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(template_codec_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &template_codec_dapm_widgets[i]);
++ }
++
++ /* set up audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++/*
++ * Configures the codec SYSCLK/MCLK (system or master clock)
++ */
++static unsigned int template_codec_config_sysclk(struct snd_soc_codec_dai *dai,
++ struct snd_soc_clock_info *info, unsigned int clk)
++{
++ dai->mclk = 0;
++
++ /* check that the calculated FS and rate actually match a clock from
++ * the machine driver */
++ if (info->fs * info->rate == clk)
++ dai->mclk = clk;
++
++ return dai->mclk;
++}
++
++/*
++ * Alsa operations
++ * Only implement the required operations for your platform.
++ * These operations are specific to the codec only.
++ */
++
++ /*
++ * Called by ALSA when a PCM substream is opened, private data can be allocated.
++ */
++static int template_codec_startup(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when a PCM substream is closed. Private data can be
++ * freed here.
++ */
++static int template_codec_shutdown(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the hardware params are set by application. This
++ * function can also be called multiple times and can allocate buffers
++ * (using snd_pcm_lib_* ). It's non-atomic.
++ */
++static int template_codec_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++}
++
++/*
++ * Free's resources allocated by hw_params, can be called multiple times
++ */
++static int template_codec_hw_free(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Starts (Triggers) audio playback or capture.
++ * Usually only needed for DMA
++ */
++static int template_codec_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++}
++
++/*
++ * Called by ALSA when the PCM substream is prepared, can set format, sample
++ * rate, etc. This function is non atomic and can be called multiple times,
++ * it can refer to the runtime info.
++ */
++static int template_codec_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++
++ /* set master/slave audio interface */
++ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ }
++
++ /* interface format */
++ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ break;
++ }
++
++ /* bit size */
++ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
++ case SNDRV_PCM_FMTBIT_S16_LE:
++ break;
++ case SNDRV_PCM_FMTBIT_S20_3LE:
++ break;
++ case SNDRV_PCM_FMTBIT_S24_LE:
++ break;
++ case SNDRV_PCM_FMTBIT_S32_LE:
++ break;
++ }
++
++ /* clock inversion */
++ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ break;
++ }
++
++ return 0;
++}
++
++/*
++ * Enable / Disable codec digital soft mute
++ */
++static int template_codec_mute(struct snd_soc_codec *codec,
++ struct snd_soc_codec_dai *dai, int mute)
++{
++}
++
++/*
++ * Codec DAPM event handler
++ * This handles codec level DAPM events
++ */
++static int template_codec_dapm_event(struct snd_soc_codec *codec, int event)
++{
++ u16 reg = template_codec_read_reg_cache(codec, TEMPLATE_PWR) & 0xff7f;
++
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* e.g. vref/mid, osc on, */
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, */
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ break;
++ }
++ codec->dapm_state = event;
++ return 0;
++}
++
++/*
++ * Define codec DAI.
++ */
++struct snd_soc_codec_dai template_codec_dai = {
++ .name = "codec xxx",
++ /* playback and capture stream info */
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ },
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ },
++ /* codec operations */
++ .config_sysclk = template_codec_config_sysclk,
++ .digital_mute = template_codec_mute,
++ /* alsa PCM operations */
++ .ops = {
++ .startup = template_codec_startup,
++ .shutdown = template_codec_shutdown,
++ .prepare = template_codec_prepare,
++ .trigger = template_codec_trigger,
++ .hw_params = template_codec_hw_params,
++ .hw_free = template_codec_hw_free,},
++ /* codec capabilities */
++ .caps = {
++ .num_modes = ARRAY_SIZE(template_codec_modes),
++ .mode = template_codec_modes,
++ },
++};
++EXPORT_SYMBOL_GPL(template_codec_dai);
++
++static int template_codec_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ template_codec_write(codec, TEMPLATE_ACTIVE, 0x0);
++ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int template_codec_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(template_codec_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ template_codec_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the TEMPLATE driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int template_codec_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "TEMPLATE";
++ codec->owner = THIS_MODULE;
++ codec->read = template_codec_read_reg_cache;
++ codec->write = template_codec_write;
++ codec->dapm_event = template_codec_dapm_event;
++ codec->dai = &template_codec_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(template_codec_reg);
++
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(template_codec_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache,
++ template_codec_reg, sizeof(u16) * ARRAY_SIZE(template_codec_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(template_codec_reg);
++
++ template_codec_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ kfree(codec->reg_cache);
++ return ret;
++ }
++
++ /* power on device */
++ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* set the update bits */
++ reg = template_codec_read_reg_cache(codec, TEMPLATE_LOUT1V);
++ template_codec_write(codec, TEMPLATE_LOUT1V, reg | 0x0100);
++ reg = template_codec_read_reg_cache(codec, TEMPLATE_ROUT1V);
++ template_codec_write(codec, TEMPLATE_ROUT1V, reg | 0x0100);
++ reg = template_codec_read_reg_cache(codec, TEMPLATE_LINVOL);
++ template_codec_write(codec, TEMPLATE_LINVOL, reg | 0x0100);
++ reg = template_codec_read_reg_cache(codec, TEMPLATE_RINVOL);
++ template_codec_write(codec, TEMPLATE_RINVOL, reg | 0x0100);
++
++ template_codec_add_controls(codec);
++ template_codec_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++ }
++
++ return ret;
++}
++
++static struct snd_soc_device *template_codec_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * TEMPLATE 2 wire address is determined by GPIO5
++ * state during powerup.
++ * low = 0x1a
++ * high = 0x1b
++ */
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver template_codec_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int template_codec_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = template_codec_socdev;
++ struct template_codec_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = template_codec_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise TEMPLATE\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int template_codec_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int template_codec_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, template_codec_codec_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver template_codec_i2c_driver = {
++ .driver = {
++ .name = "TEMPLATE I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_TEMPLATE,
++ .attach_adapter = template_codec_i2c_attach,
++ .detach_client = template_codec_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "TEMPLATE",
++ .driver = &template_codec_i2c_driver,
++};
++#endif
++
++static int template_codec_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct template_codec_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("TEMPLATE Audio Codec %s", TEMPLATE_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ template_codec_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&template_codec_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip and remove */
++static int template_codec_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&template_codec_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++/* codec device ops */
++struct snd_soc_codec_device soc_codec_dev_template_codec = {
++ .probe = template_codec_probe,
++ .remove = template_codec_remove,
++ .suspend = template_codec_suspend,
++ .resume = template_codec_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_template_codec);
++
+Index: linux-2.6.22.1/sound/soc/templates/template-i2s.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-i2s.c 2007-07-16 15:07:34.920327265 +0200
+@@ -0,0 +1,223 @@
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++
++#include <asm/hardware.h>
++
++#include "template-pcm.h"
++
++/* supported I2S DAI hardware formats */
++#define TEMPLATE_I2S_DAIFMT \
++ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
++
++/* supported I2S direction */
++#define TEMPLATE_I2S_DIR \
++ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
++
++/* supported I2S rates */
++#define TEMPLATE_I2S_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++/* I2S controller DAI capabilities */
++static struct snd_soc_dai_mode template_i2s_modes[] = {
++ /* template I2S frame and clock master modes */
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_8000,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_11025,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_16000,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_22050,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_44100,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = SNDRV_PCM_RATE_48000,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .flags = SND_SOC_DAI_BFS_DIV,
++ .fs = 256,
++ .bfs = SND_SOC_FSBD(4),
++ },
++
++ /* template I2S frame master and clock slave mode */
++ {
++ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
++ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
++ .pcmrate = TEMPLATE_I2S_RATES,
++ .pcmdir = TEMPLATE_I2S_DIR,
++ .fs = SND_SOC_FS_ALL,
++ .flags = SND_SOC_DAI_BFS_RATE,
++ .bfs = 64,
++ },
++};
++
++/* I2S controller platform specific DMA parameters */
++static struct template_pcm_dma_params template_i2s_pcm_stereo_out = {
++ .name = "I2S PCM Stereo out",
++ .dev_addr = __PREG(SADR),
++};
++
++static struct template_pcm_dma_params template_i2s_pcm_stereo_in = {
++ .name = "I2S PCM Stereo in",
++ .dev_addr = __PREG(SADR),
++};
++
++#ifdef CONFIG_PM
++/* suspend I2S controller */
++static int template_i2s_suspend(struct platform_device *dev,
++ struct snd_soc_cpu_dai *dai)
++{
++}
++
++/* resume I2S controller */
++static int template_i2s_resume(struct platform_device *pdev,
++ struct snd_soc_cpu_dai *dai)
++{
++}
++
++#else
++#define template_i2s_suspend NULL
++#define template_i2s_resume NULL
++#endif
++
++/* configure the I2S controllers MCLK or SYSCLK */
++static unsigned int template_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
++ struct snd_soc_clock_info *info, unsigned int clk)
++{
++}
++
++
++/*
++ * Alsa operations
++ * Only implement the required operations for your platform.
++ * These operations are specific to the I2S controller and DAI only.
++ */
++
++ /*
++ * Called by ALSA when a PCM substream is opened, private data can be allocated.
++ */
++static int template_i2s_startup(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when a PCM substream is closed. Private data can be
++ * freed here.
++ */
++static int template_i2s_shutdown(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the PCM substream is prepared, can set format, sample
++ * rate, etc. This function is non atomic and can be called multiple times,
++ * it can refer to the runtime info.
++ */
++static int template_i2s_prepare(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the hardware params are set by application. This
++ * function can also be called multiple times and can allocate buffers
++ * (using snd_pcm_lib_* ). It's non-atomic.
++ */
++static int template_i2s_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++}
++
++/*
++ * Free's resources allocated by hw_params, can be called multiple times
++ */
++static int template_i2s_hw_free(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Starts (Triggers) audio playback or capture.
++ * Usually only needed for DMA
++ */
++static int template_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++}
++
++
++struct snd_soc_cpu_dai template_i2s_dai = {
++ .name = "template-i2s",
++ .id = 0,
++ .type = SND_SOC_DAI_I2S,
++ .suspend = template_i2s_suspend,
++ .resume = template_i2s_resume,
++ .config_sysclk = template_i2s_config_sysclk,
++ .playback = {
++ .channels_min = 2,
++ .channels_max = 2,},
++ .capture = {
++ .channels_min = 2,
++ .channels_max = 2,},
++ .ops = {
++ .startup = template_i2s_startup,
++ .shutdown = template_i2s_shutdown,
++ .prepare = template_i2s_prepare,
++ .trigger = template_i2s_trigger,
++ .hw_params = template_i2s_hw_params,
++ .hw_free = template_i2s_hw_free,},
++ .caps = {
++ .num_modes = ARRAY_SIZE(template_i2s_modes),
++ .mode = template_i2s_modes,},
++};
++
++EXPORT_SYMBOL_GPL(template_i2s_dai);
+Index: linux-2.6.22.1/sound/soc/templates/template-pcm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-pcm.c 2007-07-16 15:07:34.944328635 +0200
+@@ -0,0 +1,166 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/dma-mapping.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++
++#include <asm/dma.h>
++#include <asm/hardware.h>
++
++#include "template-pcm.h"
++
++/* PCM hardware DMA capabilities - platform specific */
++static const struct snd_pcm_hardware template_pcm_hardware = {
++ .info = SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_MMAP_VALID |
++ SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_PAUSE |
++ SNDRV_PCM_INFO_RESUME,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_S24_LE |
++ SNDRV_PCM_FMTBIT_S32_LE,
++ .period_bytes_min = 32,
++ .period_bytes_max = 8192 - 32,
++ .periods_min = 1,
++ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
++ .buffer_bytes_max = 128 * 1024,
++ .fifo_size = 32,
++};
++
++/*
++ * Called by ALSA when the hardware params are set by application. This
++ * function can also be called multiple times and can allocate buffers
++ * (using snd_pcm_lib_* ). It's non-atomic.
++ */
++static int template_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++}
++
++/*
++ * Free's resources allocated by hw_params, can be called multiple times
++ */
++static int template_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the PCM substream is prepared, can set format, sample
++ * rate, etc. This function is non atomic and can be called multiple times,
++ * it can refer to the runtime info.
++ */
++static int template_pcm_prepare(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Starts (Triggers) audio playback or capture.
++ * Usually only needed for DMA
++ */
++static int template_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct template_runtime_data *prtd = substream->runtime->private_data;
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ break;
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ break;
++ case SNDRV_PCM_TRIGGER_RESUME:
++ break;
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ break;
++ default:
++ ret = -EINVAL;
++ }
++
++ return ret;
++}
++
++/*
++ * Returns the DMA audio frame position
++ */
++static snd_pcm_uframes_t
++template_pcm_pointer(struct snd_pcm_substream *substream)
++{
++}
++
++ /*
++ * Called by ALSA when a PCM substream is opened, private data can be allocated.
++ */
++static int template_pcm_open(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when a PCM substream is closed. Private data can be
++ * freed here.
++ */
++static int template_pcm_close(struct snd_pcm_substream *substream)
++{
++}
++
++/* map DMA audio buffer into user space */
++static int template_pcm_mmap(struct snd_pcm_substream *substream,
++ struct vm_area_struct *vma)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
++ runtime->dma_area,
++ runtime->dma_addr,
++ runtime->dma_bytes);
++}
++
++/* ALSA PCM operations */
++struct snd_pcm_ops template_pcm_ops = {
++ .open = template_pcm_open,
++ .close = template_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = template_pcm_hw_params,
++ .hw_free = template_pcm_hw_free,
++ .prepare = template_pcm_prepare,
++ .trigger = template_pcm_trigger,
++ .pointer = template_pcm_pointer,
++ .mmap = template_pcm_mmap,
++};
++
++/*
++ * Called by ASoC core to free platform DMA.
++ */
++static void template_pcm_free_dma_buffers(struct snd_pcm *pcm)
++{
++}
++
++/*
++ * Called by the ASoC core to create and initialise the platform DMA.
++ */
++int template_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
++ struct snd_pcm *pcm)
++{
++}
++
++/* template audio platform */
++struct snd_soc_platform template_soc_platform = {
++ .name = "template-audio",
++ .pcm_ops = &template_pcm_ops,
++ .pcm_new = template_pcm_new,
++ .pcm_free = template_pcm_free_dma_buffers,
++};
++EXPORT_SYMBOL_GPL(template_soc_platform);
+Index: linux-2.6.22.1/sound/soc/templates/template-pcm.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-pcm.h 2007-07-16 15:07:34.988331140 +0200
+@@ -0,0 +1,19 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _TEMPLATE_PCM_H
++#define _TEMPLATE_PCM_H
++
++/* platform specific structs can be declared here */
++
++extern struct snd_soc_cpu_dai template_ac97_dai[3];
++extern struct snd_soc_cpu_dai template_i2s_dai;
++
++/* template platform data */
++extern struct snd_soc_platform template_soc_platform;
++extern struct snd_ac97_bus_ops tempalte_ac97_ops;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/templates/template-codec.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-codec.h 2007-07-16 15:07:35.016332739 +0200
+@@ -0,0 +1,21 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _TEMPLATE_H
++#define _TEMPLATE_H
++
++/* TEMPLATE register space */
++
++#define TEMPLATE_CACHEREGNUM 10
++
++struct template_codec_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai template_codec_dai;
++extern struct snd_soc_codec_device soc_codec_dev_template_codec;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/templates/template-machine.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/templates/template-machine.c 2007-07-16 15:07:35.040334105 +0200
+@@ -0,0 +1,161 @@
++/*
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++
++#include "../codecs/template-codec.h"
++#include "template-pcm.h"
++
++/*
++ * Alsa operations
++ * Only implement the required operations for your platform.
++ * These operations are specific to the machine only.
++ */
++
++ /*
++ * Called by ALSA when a PCM substream is opened, private data can be allocated.
++ */
++static int template_machine_startup(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when a PCM substream is closed. Private data can be
++ * freed here.
++ */
++static int template_machine_shutdown(struct snd_pcm_substream *substream)
++{
++}
++
++/*
++ * Called by ALSA when the hardware params are set by application. This
++ * function can also be called multiple times and can allocate buffers
++ * (using snd_pcm_lib_* ). It's non-atomic.
++ */
++static int template_machine_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++}
++
++/*
++ * Free's resources allocated by hw_params, can be called multiple times
++ */
++static int template_machine_hw_free(struct snd_pcm_substream *substream)
++{
++}
++
++/* machine Alsa PCM operations */
++static struct snd_soc_ops template_ops = {
++ .startup = template_machine_startup,
++ .shutdown = template_machine_shutdown,
++ .hw_free = template_machine_hw_free,
++ .hw_params = template_machine_hw_params,
++};
++
++/* machine audio map (connections to the codec pins) */
++static const char *audio_map[][3] = {
++
++ {NULL, NULL, NULL},
++};
++
++/*
++ * Initialise the machine audio subsystem.
++ */
++static int template_machine_init(struct snd_soc_codec *codec)
++{
++ /* mark unused codec pins as NC */
++
++ /* Add template specific controls */
++
++ /* Add template specific widgets */
++
++ /* Set up template specific audio path audio_map */
++
++ /* synchronise subsystem */
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++/*
++ * Configure the clocking within the audio subsystem
++ */
++static unsigned int template_config_sysclk(struct snd_soc_pcm_runtime *rtd,
++ struct snd_soc_clock_info *info)
++{
++}
++
++/* template digital audio interface glue - connects codec <--> CPU */
++static struct snd_soc_dai_link template_dai = {
++ .name = "Codec",
++ .stream_name = "Stream Name",
++ .cpu_dai = &template_i2s_dai,
++ .codec_dai = &template_codec_dai,
++ .init = template_machine_init,
++ .config_sysclk = template_config_sysclk,
++};
++
++/* template audio machine driver */
++static struct snd_soc_machine snd_soc_machine_template = {
++ .name = "Machine",
++ .dai_link = &template_dai,
++ .num_links = ARRAY_SIZE(template_dai),
++ .ops = &template_ops,
++};
++
++/* template audio private data */
++static struct codec_priv_setup_data template_codec_setup = {
++ .i2c_address = 0x1b,
++};
++
++/* template audio subsystem */
++static struct snd_soc_device template_snd_devdata = {
++ .machine = &snd_soc_machine_template,
++ .platform = &template_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8731,
++ .codec_data = &template_codec_setup,
++};
++
++static struct platform_device *template_snd_device;
++
++static int __init template_init(void)
++{
++ int ret;
++
++ template_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!template_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(template_snd_device, &template_snd_devdata);
++ template_snd_devdata.dev = &template_snd_device->dev;
++ ret = platform_device_add(template_snd_device);
++
++ if (ret)
++ platform_device_put(template_snd_device);
++
++ return ret;
++}
++
++static void __exit template_exit(void)
++{
++ platform_device_unregister(template_snd_device);
++}
++
++module_init(template_init);
++module_exit(template_exit);
++
+Index: linux-2.6.22.1/sound/soc/pxa/pxa2xx-ssp.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/pxa2xx-ssp.h 2007-07-16 15:07:35.064335471 +0200
+@@ -0,0 +1,43 @@
++/*
++ * linux/sound/arm/pxa2xx-ssp.h
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _PXA2XX_SSP_H
++#define _PXA2XX_SSP_H
++
++/* pxa2xx DAI SSP ID's */
++#define PXA2XX_DAI_SSP1 0
++#define PXA2XX_DAI_SSP2 1
++#define PXA2XX_DAI_SSP3 2
++
++/* SSP clock sources */
++#define PXA2XX_SSP_CLK_PLL 0
++#define PXA2XX_SSP_CLK_EXT 1
++#define PXA2XX_SSP_CLK_NET 2
++#define PXA2XX_SSP_CLK_AUDIO 3
++#define PXA2XX_SSP_CLK_NET_PLL 4
++
++/* SSP audio dividers */
++#define PXA2XX_SSP_AUDIO_DIV_ACDS 0
++#define PXA2XX_SSP_AUDIO_DIV_SCDB 1
++#define PXA2XX_SSP_DIV_SCR 2
++
++/* SSP ACDS audio dividers values */
++#define PXA2XX_SSP_CLK_AUDIO_DIV_1 0
++#define PXA2XX_SSP_CLK_AUDIO_DIV_2 1
++#define PXA2XX_SSP_CLK_AUDIO_DIV_4 2
++#define PXA2XX_SSP_CLK_AUDIO_DIV_8 3
++#define PXA2XX_SSP_CLK_AUDIO_DIV_16 4
++#define PXA2XX_SSP_CLK_AUDIO_DIV_32 5
++
++/* SSP divider bypass */
++#define PXA2XX_SSP_CLK_SCDB_4 0
++#define PXA2XX_SSP_CLK_SCDB_1 1
++
++extern struct snd_soc_cpu_dai pxa_ssp_dai[3];
++
++#endif
+Index: linux-2.6.22.1/sound/soc/s3c24xx/smdk2440.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/s3c24xx/smdk2440.c 2007-07-16 15:07:35.412355304 +0200
+@@ -0,0 +1,318 @@
++/*
++ * smdk2440.c -- ALSA Soc Audio Layer
++ *
++ * (c) 2006 Wolfson Microelectronics PLC.
++ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * (c) 2004-2005 Simtec Electronics
++ * http://armlinux.simtec.co.uk/
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This module is a modified version of the s3c24xx I2S driver supplied by
++ * Ben Dooks of Simtec and rejigged to the ASoC style at Wolfson Microelectronics
++ *
++ * Revision history
++ * 11th Dec 2006 Merged with Simtec driver
++ * 10th Nov 2006 Initial version.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/hardware/scoop.h>
++#include <asm/arch/regs-iis.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/audio.h>
++#include <asm/io.h>
++#include <asm/arch/spi-gpio.h>
++#include "../codecs/uda1380.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-i2s.h"
++
++#define SMDK2440_DEBUG 0
++#if SMDK2440_DEBUG
++#define DBG(x...) printk(KERN_DEBUG x)
++#else
++#define DBG(x...)
++#endif
++
++/* audio clock in Hz */
++#define SMDK_CLOCK_SOURCE S3C24XX_CLKSRC_MPLL
++#define SMDK_CRYSTAL_CLOCK 16934400
++
++static int smdk2440_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->codec;
++
++ DBG("Entered smdk2440_startup\n");
++
++ return 0;
++}
++
++static void smdk2440_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->codec;
++
++ DBG("Entered smdk2440_shutdown\n");
++}
++
++static int smdk2440_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ unsigned long iis_clkrate;
++ int div, div256, div384, diff256, diff384, bclk, mclk;
++ int ret;
++ unsigned int rate=params_rate(params);
++
++ DBG("Entered %s\n",__FUNCTION__);
++
++ iis_clkrate = s3c24xx_i2s_get_clockrate();
++
++ /* Using PCLK doesnt seem to suit audio particularly well on these cpu's
++ */
++
++ div256 = iis_clkrate / (rate * 256);
++ div384 = iis_clkrate / (rate * 384);
++
++ if (((iis_clkrate / div256) - (rate * 256)) <
++ ((rate * 256) - (iis_clkrate / (div256 + 1)))) {
++ diff256 = (iis_clkrate / div256) - (rate * 256);
++ } else {
++ div256++;
++ diff256 = (iis_clkrate / div256) - (rate * 256);
++ }
++
++ if (((iis_clkrate / div384) - (rate * 384)) <
++ ((rate * 384) - (iis_clkrate / (div384 + 1)))) {
++ diff384 = (iis_clkrate / div384) - (rate * 384);
++ } else {
++ div384++;
++ diff384 = (iis_clkrate / div384) - (rate * 384);
++ }
++
++ DBG("diff256 %d, diff384 %d\n", diff256, diff384);
++
++ if (diff256<=diff384) {
++ DBG("Selected 256FS\n");
++ div = div256 - 1;
++ bclk = S3C2410_IISMOD_256FS;
++ } else {
++ DBG("Selected 384FS\n");
++ div = div384 - 1;
++ bclk = S3C2410_IISMOD_384FS;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set the audio system clock for DAC and ADC */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK,
++ rate, SND_SOC_CLOCK_OUT);
++ if (ret < 0)
++ return ret;
++
++ /* set MCLK division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, S3C2410_IISMOD_32FS );
++ if (ret < 0)
++ return ret;
++
++ /* set BCLK division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set prescaler division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
++ S3C24XX_PRESCALE(div,div));
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static struct snd_soc_ops smdk2440_ops = {
++ .startup = smdk2440_startup,
++ .shutdown = smdk2440_shutdown,
++ .hw_params = smdk2440_hw_params,
++};
++
++/* smdk2440 machine dapm widgets */
++static const struct snd_soc_dapm_widget smdk2440_dapm_widgets[] = {
++SND_SOC_DAPM_HP("Headphone Jack", NULL),
++SND_SOC_DAPM_MIC("Mic Jack", NULL),
++SND_SOC_DAPM_LINE("Line Jack", NULL),
++};
++
++/* smdk2440 machine audio map (connections to the codec pins) */
++static const char* audio_map[][3] = {
++ /* headphone connected to HPOUT */
++ {"Headphone Jack", NULL, "HPOUT"},
++
++ /* mic is connected to MICIN (via right channel of headphone jack) */
++ {"MICIN", NULL, "Mic Jack"},
++ {"MICIN", NULL, "Line Jack"},
++
++ {NULL, NULL, NULL},
++};
++
++/*
++ * Logic for a UDA1341 as attached to SMDK2440
++ */
++static int smdk2440_uda1341_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ DBG("Staring smdk2440 init\n");
++
++ /* Add smdk2440 specific widgets */
++ for(i = 0; i < ARRAY_SIZE(smdk2440_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &smdk2440_dapm_widgets[i]);
++ }
++
++ /* Set up smdk2440 specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++
++ DBG("Ending smdk2440 init\n");
++
++ return 0;
++}
++
++/* s3c24xx digital audio interface glue - connects codec <--> CPU */
++static struct snd_soc_dai_link s3c24xx_dai = {
++ .name = "WM8731",
++ .stream_name = "WM8731",
++ .cpu_dai = &s3c24xx_i2s_dai,
++ .codec_dai = uda1380_dai,
++ .init = smdk2440_uda1341_init,
++ .ops = &smdk2440_ops,
++};
++
++/* smdk2440 audio machine driver */
++static struct snd_soc_machine snd_soc_machine_smdk2440 = {
++ .name = "SMDK2440",
++ .dai_link = &s3c24xx_dai,
++ .num_links = 1,
++};
++
++static struct uda1380_setup_data smdk2440_uda1380_setup = {
++ .i2c_address = 0x00,
++};
++
++/* s3c24xx audio subsystem */
++static struct snd_soc_device s3c24xx_snd_devdata = {
++ .machine = &snd_soc_machine_smdk2440,
++ .platform = &s3c24xx_soc_platform,
++ .codec_dev = &soc_codec_dev_uda1380,
++ .codec_data = &smdk2440_uda1380_setup,
++};
++
++static struct platform_device *s3c24xx_snd_device;
++
++struct smdk2440_spi_device {
++ struct device *dev;
++};
++
++static struct smdk2440_spi_device smdk2440_spi_devdata = {
++};
++
++struct s3c2410_spigpio_info smdk2440_spi_devinfo = {
++ .pin_clk = S3C2410_GPF4,
++ .pin_mosi = S3C2410_GPF5,
++ .pin_miso = S3C2410_GPF6,
++ //.board_size,
++ //.board_info,
++ .chip_select=NULL,
++};
++
++static struct platform_device *smdk2440_spi_device;
++
++static int __init smdk2440_init(void)
++{
++ int ret;
++
++ if (!machine_is_smdk2440() && !machine_is_s3c2440()) {
++ DBG("%d\n",machine_arch_type);
++ DBG("Not a SMDK2440\n");
++ return -ENODEV;
++ }
++
++ s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!s3c24xx_snd_device) {
++ DBG("platform_dev_alloc failed\n");
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(s3c24xx_snd_device, &s3c24xx_snd_devdata);
++ s3c24xx_snd_devdata.dev = &s3c24xx_snd_device->dev;
++ ret = platform_device_add(s3c24xx_snd_device);
++
++ if (ret)
++ platform_device_put(s3c24xx_snd_device);
++
++ // Create a bitbanged SPI device
++
++ smdk2440_spi_device = platform_device_alloc("s3c24xx-spi-gpio",-1);
++ if (!smdk2440_spi_device) {
++ DBG("smdk2440_spi_device : platform_dev_alloc failed\n");
++ return -ENOMEM;
++ }
++ DBG("Return Code %d\n",ret);
++
++ platform_set_drvdata(smdk2440_spi_device, &smdk2440_spi_devdata);
++ smdk2440_spi_devdata.dev = &smdk2440_spi_device->dev;
++ smdk2440_spi_devdata.dev->platform_data = &smdk2440_spi_devinfo;
++ ret = platform_device_add(smdk2440_spi_device);
++
++ if (ret)
++ platform_device_put(smdk2440_spi_device);
++
++ return ret;
++}
++
++static void __exit smdk2440_exit(void)
++{
++ platform_device_unregister(s3c24xx_snd_device);
++}
++
++module_init(smdk2440_init);
++module_exit(smdk2440_exit);
++
++/* Module information */
++MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
++MODULE_DESCRIPTION("ALSA SoC SMDK2440");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8956.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8956.c 2007-07-16 15:07:35.440356902 +0200
+@@ -0,0 +1,724 @@
++/*
++ * wm8956.c -- WM8956 ALSA SoC Audio driver
++ *
++ * Author: Liam Girdwood
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8956.h"
++
++#define AUDIO_NAME "wm8956"
++#define WM8956_VERSION "0.2"
++
++/*
++ * Debug
++ */
++
++#define WM8956_DEBUG 0
++
++#ifdef WM8956_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8956;
++
++/*
++ * wm8956 register cache
++ * We can't read the WM8956 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8956_reg[WM8956_CACHEREGNUM] = {
++ 0x0097, 0x0097, 0x0000, 0x0000,
++ 0x0000, 0x0008, 0x0000, 0x000a,
++ 0x01c0, 0x0000, 0x00ff, 0x00ff,
++ 0x0000, 0x0000, 0x0000, 0x0000, //r15
++ 0x0000, 0x007b, 0x0100, 0x0032,
++ 0x0000, 0x00c3, 0x00c3, 0x01c0,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000, //r31
++ 0x0100, 0x0100, 0x0050, 0x0050,
++ 0x0050, 0x0050, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0040, 0x0000,
++ 0x0000, 0x0050, 0x0050, 0x0000, //47
++ 0x0002, 0x0037, 0x004d, 0x0080,
++ 0x0008, 0x0031, 0x0026, 0x00e9,
++};
++
++/*
++ * read wm8956 register cache
++ */
++static inline unsigned int wm8956_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8956_RESET)
++ return 0;
++ if (reg >= WM8956_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8956 register cache
++ */
++static inline void wm8956_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8956_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8956 register space
++ */
++static int wm8956_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8956 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8956_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8956_reset(c) wm8956_write(c, WM8956_RESET, 0)
++
++/* todo - complete enumerated controls */
++static const char *wm8956_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
++
++static const struct soc_enum wm8956_enum[] = {
++ SOC_ENUM_SINGLE(WM8956_DACCTL1, 1, 4, wm8956_deemph),
++};
++
++/* to complete */
++static const struct snd_kcontrol_new wm8956_snd_controls[] = {
++
++SOC_DOUBLE_R("Headphone Playback Volume", WM8956_LOUT1, WM8956_ROUT1,
++ 0, 127, 0),
++SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8956_LOUT1, WM8956_ROUT1,
++ 7, 1, 0),
++SOC_DOUBLE_R("PCM Volume", WM8956_LDAC, WM8956_RDAC,
++ 0, 127, 0),
++
++SOC_ENUM("Playback De-emphasis", wm8956_enum[0]),
++};
++
++/* add non dapm controls */
++static int wm8956_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8956_snd_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8956_snd_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Left Output Mixer */
++static const struct snd_kcontrol_new wm8956_loutput_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8956_LOUTMIX1, 8, 1, 0),
++};
++
++/* Right Output Mixer */
++static const struct snd_kcontrol_new wm8956_routput_mixer_controls[] = {
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8956_ROUTMIX2, 8, 1, 0),
++};
++
++static const struct snd_soc_dapm_widget wm8956_dapm_widgets[] = {
++SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8956_loutput_mixer_controls[0],
++ ARRAY_SIZE(wm8956_loutput_mixer_controls)),
++SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8956_loutput_mixer_controls[0],
++ ARRAY_SIZE(wm8956_routput_mixer_controls)),
++};
++
++static const char *intercon[][3] = {
++ /* TODO */
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8956_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8956_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8956_dapm_widgets[i]);
++ }
++
++ /* set up audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, intercon[i][0],
++ intercon[i][1], intercon[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static int wm8956_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ iface |= 0x0040;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ iface |= 0x0013;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0090;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0080;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0010;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* set iface */
++ wm8956_write(codec, WM8956_IFACE1, iface);
++ return 0;
++}
++
++static int wm8956_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8956_read_reg_cache(codec, WM8956_IFACE1) & 0xfff3;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0008;
++ break;
++ }
++
++ /* set iface */
++ wm8956_write(codec, WM8956_IFACE1, iface);
++ return 0;
++}
++
++static int wm8956_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8956_read_reg_cache(codec, WM8956_DACCTL1) & 0xfff7;
++
++ if (mute)
++ wm8956_write(codec, WM8956_DACCTL1, mute_reg | 0x8);
++ else
++ wm8956_write(codec, WM8956_DACCTL1, mute_reg);
++ return 0;
++}
++
++static int wm8956_dapm_event(struct snd_soc_codec *codec, int event)
++{
++#if 0
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, osc on, dac unmute */
++
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, */
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ break;
++ }
++#endif
++ // tmp
++ wm8956_write(codec, WM8956_POWER1, 0xffff);
++ wm8956_write(codec, WM8956_POWER2, 0xffff);
++ wm8956_write(codec, WM8956_POWER3, 0xffff);
++ codec->dapm_state = event;
++ return 0;
++}
++
++/* PLL divisors */
++struct _pll_div {
++ u32 pre_div:1;
++ u32 n:4;
++ u32 k:24;
++};
++
++static struct _pll_div pll_div;
++
++/* The size in bits of the pll divide multiplied by 10
++ * to allow rounding later */
++#define FIXED_PLL_SIZE ((1 << 24) * 10)
++
++static void pll_factors(unsigned int target, unsigned int source)
++{
++ unsigned long long Kpart;
++ unsigned int K, Ndiv, Nmod;
++
++ Ndiv = target / source;
++ if (Ndiv < 6) {
++ source >>= 1;
++ pll_div.pre_div = 1;
++ Ndiv = target / source;
++ } else
++ pll_div.pre_div = 0;
++
++ if ((Ndiv < 6) || (Ndiv > 12))
++ printk(KERN_WARNING
++ "WM8956 N value outwith recommended range! N = %d\n",Ndiv);
++
++ pll_div.n = Ndiv;
++ Nmod = target % source;
++ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
++
++ do_div(Kpart, source);
++
++ K = Kpart & 0xFFFFFFFF;
++
++ /* Check if we need to round */
++ if ((K % 10) >= 5)
++ K += 5;
++
++ /* Move down to proper range now rounding is done */
++ K /= 10;
++
++ pll_div.k = K;
++}
++
++static int wm8956_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++ int found = 0;
++#if 0
++ if (freq_in == 0 || freq_out == 0) {
++ /* disable the pll */
++ /* turn PLL power off */
++ }
++#endif
++
++ pll_factors(freq_out * 8, freq_in);
++
++ if (!found)
++ return -EINVAL;
++
++ reg = wm8956_read_reg_cache(codec, WM8956_PLLN) & 0x1e0;
++ wm8956_write(codec, WM8956_PLLN, reg | (pll_div.pre_div << 4)
++ | pll_div.n);
++ wm8956_write(codec, WM8956_PLLK1, pll_div.k >> 16 );
++ wm8956_write(codec, WM8956_PLLK2, (pll_div.k >> 8) & 0xff);
++ wm8956_write(codec, WM8956_PLLK3, pll_div.k &0xff);
++ wm8956_write(codec, WM8956_CLOCK1, 4);
++
++ return 0;
++}
++
++static int wm8956_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8956_SYSCLKSEL:
++ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1fe;
++ wm8956_write(codec, WM8956_CLOCK1, reg | div);
++ break;
++ case WM8956_SYSCLKDIV:
++ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1f9;
++ wm8956_write(codec, WM8956_CLOCK1, reg | div);
++ break;
++ case WM8956_DACDIV:
++ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1c7;
++ wm8956_write(codec, WM8956_CLOCK1, reg | div);
++ break;
++ case WM8956_OPCLKDIV:
++ reg = wm8956_read_reg_cache(codec, WM8956_PLLN) & 0x03f;
++ wm8956_write(codec, WM8956_PLLN, reg | div);
++ break;
++ case WM8956_DCLKDIV:
++ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK2) & 0x03f;
++ wm8956_write(codec, WM8956_CLOCK2, reg | div);
++ break;
++ case WM8956_TOCLKSEL:
++ reg = wm8956_read_reg_cache(codec, WM8956_ADDCTL1) & 0x1fd;
++ wm8956_write(codec, WM8956_ADDCTL1, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define WM8956_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++#define WM8956_FORMATS \
++ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8956_dai = {
++ .name = "WM8956",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8956_RATES,
++ .formats = WM8956_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8956_RATES,
++ .formats = WM8956_FORMATS,},
++ .ops = {
++ .hw_params = wm8956_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8956_mute,
++ .set_fmt = wm8956_set_dai_fmt,
++ .set_clkdiv = wm8956_set_dai_clkdiv,
++ .set_pll = wm8956_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8956_dai);
++
++
++/* To complete PM */
++static int wm8956_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8956_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8956_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8956_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8956 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8956_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "WM8956";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8956_read_reg_cache;
++ codec->write = wm8956_write;
++ codec->dapm_event = wm8956_dapm_event;
++ codec->dai = &wm8956_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8956_reg);
++
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8956_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache,
++ wm8956_reg, sizeof(u16) * ARRAY_SIZE(wm8956_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8956_reg);
++
++ wm8956_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8956: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* set the update bits */
++ reg = wm8956_read_reg_cache(codec, WM8956_LOUT1);
++ wm8956_write(codec, WM8956_LOUT1, reg | 0x0100);
++ reg = wm8956_read_reg_cache(codec, WM8956_ROUT1);
++ wm8956_write(codec, WM8956_ROUT1, reg | 0x0100);
++
++ wm8956_add_controls(codec);
++ wm8956_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8956: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8956_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8956 2 wire address is 0x1a
++ */
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8956_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8956_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8956_socdev;
++ struct wm8956_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8956_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise WM8956\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int wm8956_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8956_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8956_codec_probe);
++}
++
++// tmp
++#define I2C_DRIVERID_WM8956 0xfefe
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8956_i2c_driver = {
++ .driver = {
++ .name = "WM8956 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8956,
++ .attach_adapter = wm8956_i2c_attach,
++ .detach_client = wm8956_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8956",
++ .driver = &wm8956_i2c_driver,
++};
++#endif
++
++static int wm8956_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8956_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8956 Audio Codec %s", WM8956_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8956_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8956_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8956_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8956_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8956 = {
++ .probe = wm8956_probe,
++ .remove = wm8956_remove,
++ .suspend = wm8956_suspend,
++ .resume = wm8956_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8956);
++
++MODULE_DESCRIPTION("ASoC WM8956 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8956.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8956.h 2007-07-16 15:07:35.468358495 +0200
+@@ -0,0 +1,116 @@
++/*
++ * wm8956.h -- WM8956 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8956_H
++#define _WM8956_H
++
++/* WM8956 register space */
++
++
++#define WM8956_CACHEREGNUM 56
++
++#define WM8956_LINVOL 0x0
++#define WM8956_RINVOL 0x1
++#define WM8956_LOUT1 0x2
++#define WM8956_ROUT1 0x3
++#define WM8956_CLOCK1 0x4
++#define WM8956_DACCTL1 0x5
++#define WM8956_DACCTL2 0x6
++#define WM8956_IFACE1 0x7
++#define WM8956_CLOCK2 0x8
++#define WM8956_IFACE2 0x9
++#define WM8956_LDAC 0xa
++#define WM8956_RDAC 0xb
++
++#define WM8956_RESET 0xf
++#define WM8956_3D 0x10
++
++#define WM8956_ADDCTL1 0x17
++#define WM8956_ADDCTL2 0x18
++#define WM8956_POWER1 0x19
++#define WM8956_POWER2 0x1a
++#define WM8956_ADDCTL3 0x1b
++#define WM8956_APOP1 0x1c
++#define WM8956_APOP2 0x1d
++
++#define WM8956_LINPATH 0x20
++#define WM8956_RINPATH 0x21
++#define WM8956_LOUTMIX1 0x22
++
++#define WM8956_ROUTMIX2 0x25
++#define WM8956_MONOMIX1 0x26
++#define WM8956_MONOMIX2 0x27
++#define WM8956_LOUT2 0x28
++#define WM8956_ROUT2 0x29
++#define WM8956_MONO 0x2a
++#define WM8956_INBMIX1 0x2b
++#define WM8956_INBMIX2 0x2c
++#define WM8956_BYPASS1 0x2d
++#define WM8956_BYPASS2 0x2e
++#define WM8956_POWER3 0x2f
++#define WM8956_ADDCTL4 0x30
++#define WM8956_CLASSD1 0x31
++
++#define WM8956_CLASSD3 0x33
++#define WM8956_PLLN 0x34
++#define WM8956_PLLK1 0x35
++#define WM8956_PLLK2 0x36
++#define WM8956_PLLK3 0x37
++
++
++/*
++ * WM8956 Clock dividers
++ */
++#define WM8956_SYSCLKDIV 0
++#define WM8956_DACDIV 1
++#define WM8956_OPCLKDIV 2
++#define WM8956_DCLKDIV 3
++#define WM8956_TOCLKSEL 4
++#define WM8956_SYSCLKSEL 5
++
++#define WM8956_SYSCLK_DIV_1 (0 << 1)
++#define WM8956_SYSCLK_DIV_2 (2 << 1)
++
++#define WM8956_SYSCLK_MCLK (0 << 0)
++#define WM8956_SYSCLK_PLL (1 << 0)
++
++#define WM8956_DAC_DIV_1 (0 << 3)
++#define WM8956_DAC_DIV_1_5 (1 << 3)
++#define WM8956_DAC_DIV_2 (2 << 3)
++#define WM8956_DAC_DIV_3 (3 << 3)
++#define WM8956_DAC_DIV_4 (4 << 3)
++#define WM8956_DAC_DIV_5_5 (5 << 3)
++#define WM8956_DAC_DIV_6 (6 << 3)
++
++#define WM8956_DCLK_DIV_1_5 (0 << 6)
++#define WM8956_DCLK_DIV_2 (1 << 6)
++#define WM8956_DCLK_DIV_3 (2 << 6)
++#define WM8956_DCLK_DIV_4 (3 << 6)
++#define WM8956_DCLK_DIV_6 (4 << 6)
++#define WM8956_DCLK_DIV_8 (5 << 6)
++#define WM8956_DCLK_DIV_12 (6 << 6)
++#define WM8956_DCLK_DIV_16 (7 << 6)
++
++#define WM8956_TOCLK_F19 (0 << 1)
++#define WM8956_TOCLK_F21 (1 << 1)
++
++#define WM8956_OPCLK_DIV_1 (0 << 0)
++#define WM8956_OPCLK_DIV_2 (1 << 0)
++#define WM8956_OPCLK_DIV_3 (2 << 0)
++#define WM8956_OPCLK_DIV_4 (3 << 0)
++#define WM8956_OPCLK_DIV_5_5 (4 << 0)
++#define WM8956_OPCLK_DIV_6 (5 << 0)
++
++struct wm8956_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8956_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8956;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/codecs/wm8960.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8960.c 2007-07-16 15:07:35.488359635 +0200
+@@ -0,0 +1,766 @@
++/*
++ * wm8960.c -- WM8960 ALSA SoC Audio driver
++ *
++ * Author: Liam Girdwood
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "wm8960.h"
++
++#define AUDIO_NAME "wm8960"
++#define WM8960_VERSION "0.1"
++
++/*
++ * Debug
++ */
++
++#define WM8960_DEBUG 0
++
++#ifdef WM8960_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++struct snd_soc_codec_device soc_codec_dev_wm8960;
++
++/*
++ * wm8960 register cache
++ * We can't read the WM8960 register space when we are
++ * using 2 wire for device control, so we cache them instead.
++ */
++static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
++ 0x0097, 0x0097, 0x0000, 0x0000,
++ 0x0000, 0x0008, 0x0000, 0x000a,
++ 0x01c0, 0x0000, 0x00ff, 0x00ff,
++ 0x0000, 0x0000, 0x0000, 0x0000, //r15
++ 0x0000, 0x007b, 0x0100, 0x0032,
++ 0x0000, 0x00c3, 0x00c3, 0x01c0,
++ 0x0000, 0x0000, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0000, 0x0000, //r31
++ 0x0100, 0x0100, 0x0050, 0x0050,
++ 0x0050, 0x0050, 0x0000, 0x0000,
++ 0x0000, 0x0000, 0x0040, 0x0000,
++ 0x0000, 0x0050, 0x0050, 0x0000, //47
++ 0x0002, 0x0037, 0x004d, 0x0080,
++ 0x0008, 0x0031, 0x0026, 0x00e9,
++};
++
++/*
++ * read wm8960 register cache
++ */
++static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg == WM8960_RESET)
++ return 0;
++ if (reg >= WM8960_CACHEREGNUM)
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write wm8960 register cache
++ */
++static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
++ u16 reg, unsigned int value)
++{
++ u16 *cache = codec->reg_cache;
++ if (reg >= WM8960_CACHEREGNUM)
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * write to the WM8960 register space
++ */
++static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ u8 data[2];
++
++ /* data is
++ * D15..D9 WM8960 register offset
++ * D8...D0 register data
++ */
++ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
++ data[1] = value & 0x00ff;
++
++ wm8960_write_reg_cache (codec, reg, value);
++ if (codec->hw_write(codec->control_data, data, 2) == 2)
++ return 0;
++ else
++ return -EIO;
++}
++
++#define wm8960_reset(c) wm8960_write(c, WM8960_RESET, 0)
++
++/* enumerated controls */
++static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
++static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
++ "Right Inverted", "Stereo Inversion"};
++static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
++static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
++static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
++static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
++
++static const struct soc_enum wm8960_enum[] = {
++ SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
++ SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
++ SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
++ SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
++ SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
++ SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
++ SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
++};
++
++/* to complete */
++static const struct snd_kcontrol_new wm8960_snd_controls[] = {
++SOC_DOUBLE_R("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
++ 0, 63, 0),
++SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
++ 6, 1, 0),
++SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
++ 7, 1, 0),
++SOC_DOUBLE_R("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
++ 0, 127, 0),
++SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
++ 7, 1, 0),
++SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
++SOC_ENUM("ADC Polarity", wm8960_enum[1]),
++SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
++SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
++
++SOC_ENUM("DAC Polarity", wm8960_enum[2]),
++
++SOC_DOUBLE_R("PCM Volume", WM8960_LDAC, WM8960_RDAC,
++ 0, 127, 0),
++
++SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
++SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
++SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
++SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
++
++SOC_ENUM("ALC Function", wm8960_enum[5]),
++SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
++SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
++SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
++SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
++SOC_ENUM("ALC Mode", wm8960_enum[6]),
++SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
++SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
++
++SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
++SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
++
++SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
++ 0, 127, 0),
++};
++
++/* add non dapm controls */
++static int wm8960_add_controls(struct snd_soc_codec *codec)
++{
++ int err, i;
++
++ for (i = 0; i < ARRAY_SIZE(wm8960_snd_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8960_snd_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ return 0;
++}
++
++/* Left Output Mixer */
++static const struct snd_kcontrol_new wm8960_loutput_mixer_controls[] = {
++SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8960_LOUTMIX1, 8, 1, 0),
++};
++
++/* Right Output Mixer */
++static const struct snd_kcontrol_new wm8960_routput_mixer_controls[] = {
++SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8960_ROUTMIX2, 8, 1, 0),
++};
++
++static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
++SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8960_loutput_mixer_controls[0],
++ ARRAY_SIZE(wm8960_loutput_mixer_controls)),
++SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
++ &wm8960_loutput_mixer_controls[0],
++ ARRAY_SIZE(wm8960_routput_mixer_controls)),
++};
++
++static const char *intercon[][3] = {
++ /* TODO */
++ /* terminator */
++ {NULL, NULL, NULL},
++};
++
++static int wm8960_add_widgets(struct snd_soc_codec *codec)
++{
++ int i;
++
++ for(i = 0; i < ARRAY_SIZE(wm8960_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &wm8960_dapm_widgets[i]);
++ }
++
++ /* set up audio path interconnects */
++ for(i = 0; intercon[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, intercon[i][0],
++ intercon[i][1], intercon[i][2]);
++ }
++
++ snd_soc_dapm_new_widgets(codec);
++ return 0;
++}
++
++static int wm8960_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 iface = 0;
++
++ /* set master/slave audio interface */
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ iface |= 0x0040;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* interface format */
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ iface |= 0x0002;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ iface |= 0x0001;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ iface |= 0x0003;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ iface |= 0x0013;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* clock inversion */
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ iface |= 0x0090;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ iface |= 0x0080;
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ iface |= 0x0010;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* set iface */
++ wm8960_write(codec, WM8960_IFACE1, iface);
++ return 0;
++}
++
++static int wm8960_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_device *socdev = rtd->socdev;
++ struct snd_soc_codec *codec = socdev->codec;
++ u16 iface = wm8960_read_reg_cache(codec, WM8960_IFACE1) & 0xfff3;
++
++ /* bit size */
++ switch (params_format(params)) {
++ case SNDRV_PCM_FORMAT_S16_LE:
++ break;
++ case SNDRV_PCM_FORMAT_S20_3LE:
++ iface |= 0x0004;
++ break;
++ case SNDRV_PCM_FORMAT_S24_LE:
++ iface |= 0x0008;
++ break;
++ }
++
++ /* set iface */
++ wm8960_write(codec, WM8960_IFACE1, iface);
++ return 0;
++}
++
++static int wm8960_mute(struct snd_soc_codec_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ u16 mute_reg = wm8960_read_reg_cache(codec, WM8960_DACCTL1) & 0xfff7;
++
++ if (mute)
++ wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
++ else
++ wm8960_write(codec, WM8960_DACCTL1, mute_reg);
++ return 0;
++}
++
++static int wm8960_dapm_event(struct snd_soc_codec *codec, int event)
++{
++#if 0
++ switch (event) {
++ case SNDRV_CTL_POWER_D0: /* full On */
++ /* vref/mid, osc on, dac unmute */
++
++ break;
++ case SNDRV_CTL_POWER_D1: /* partial On */
++ case SNDRV_CTL_POWER_D2: /* partial On */
++ break;
++ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
++ /* everything off except vref/vmid, */
++ break;
++ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
++ /* everything off, dac mute, inactive */
++ break;
++ }
++#endif
++ // tmp
++ wm8960_write(codec, WM8960_POWER1, 0xffff);
++ wm8960_write(codec, WM8960_POWER2, 0xffff);
++ wm8960_write(codec, WM8960_POWER3, 0xffff);
++ codec->dapm_state = event;
++ return 0;
++}
++
++/* PLL divisors */
++struct _pll_div {
++ u32 pre_div:1;
++ u32 n:4;
++ u32 k:24;
++};
++
++static struct _pll_div pll_div;
++
++/* The size in bits of the pll divide multiplied by 10
++ * to allow rounding later */
++#define FIXED_PLL_SIZE ((1 << 24) * 10)
++
++static void pll_factors(unsigned int target, unsigned int source)
++{
++ unsigned long long Kpart;
++ unsigned int K, Ndiv, Nmod;
++
++ Ndiv = target / source;
++ if (Ndiv < 6) {
++ source >>= 1;
++ pll_div.pre_div = 1;
++ Ndiv = target / source;
++ } else
++ pll_div.pre_div = 0;
++
++ if ((Ndiv < 6) || (Ndiv > 12))
++ printk(KERN_WARNING
++ "WM8960 N value outwith recommended range! N = %d\n",Ndiv);
++
++ pll_div.n = Ndiv;
++ Nmod = target % source;
++ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
++
++ do_div(Kpart, source);
++
++ K = Kpart & 0xFFFFFFFF;
++
++ /* Check if we need to round */
++ if ((K % 10) >= 5)
++ K += 5;
++
++ /* Move down to proper range now rounding is done */
++ K /= 10;
++
++ pll_div.k = K;
++}
++
++static int wm8960_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
++ int pll_id, unsigned int freq_in, unsigned int freq_out)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++ int found = 0;
++#if 0
++ if (freq_in == 0 || freq_out == 0) {
++ /* disable the pll */
++ /* turn PLL power off */
++ }
++#endif
++
++ pll_factors(freq_out * 8, freq_in);
++
++ if (!found)
++ return -EINVAL;
++
++ reg = wm8960_read_reg_cache(codec, WM8960_PLLN) & 0x1e0;
++ wm8960_write(codec, WM8960_PLLN, reg | (pll_div.pre_div << 4)
++ | pll_div.n);
++ wm8960_write(codec, WM8960_PLLK1, pll_div.k >> 16 );
++ wm8960_write(codec, WM8960_PLLK2, (pll_div.k >> 8) & 0xff);
++ wm8960_write(codec, WM8960_PLLK3, pll_div.k &0xff);
++ wm8960_write(codec, WM8960_CLOCK1, 4);
++
++ return 0;
++}
++
++static int wm8960_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
++ int div_id, int div)
++{
++ struct snd_soc_codec *codec = codec_dai->codec;
++ u16 reg;
++
++ switch (div_id) {
++ case WM8960_SYSCLKSEL:
++ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1fe;
++ wm8960_write(codec, WM8960_CLOCK1, reg | div);
++ break;
++ case WM8960_SYSCLKDIV:
++ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1f9;
++ wm8960_write(codec, WM8960_CLOCK1, reg | div);
++ break;
++ case WM8960_DACDIV:
++ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1c7;
++ wm8960_write(codec, WM8960_CLOCK1, reg | div);
++ break;
++ case WM8960_OPCLKDIV:
++ reg = wm8960_read_reg_cache(codec, WM8960_PLLN) & 0x03f;
++ wm8960_write(codec, WM8960_PLLN, reg | div);
++ break;
++ case WM8960_DCLKDIV:
++ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK2) & 0x03f;
++ wm8960_write(codec, WM8960_CLOCK2, reg | div);
++ break;
++ case WM8960_TOCLKSEL:
++ reg = wm8960_read_reg_cache(codec, WM8960_ADDCTL1) & 0x1fd;
++ wm8960_write(codec, WM8960_ADDCTL1, reg | div);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++#define WM8960_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
++
++#define WM8960_FORMATS \
++ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE)
++
++struct snd_soc_codec_dai wm8960_dai = {
++ .name = "WM8960",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8960_RATES,
++ .formats = WM8960_FORMATS,},
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = WM8960_RATES,
++ .formats = WM8960_FORMATS,},
++ .ops = {
++ .hw_params = wm8960_hw_params,
++ },
++ .dai_ops = {
++ .digital_mute = wm8960_mute,
++ .set_fmt = wm8960_set_dai_fmt,
++ .set_clkdiv = wm8960_set_dai_clkdiv,
++ .set_pll = wm8960_set_dai_pll,
++ },
++};
++EXPORT_SYMBOL_GPL(wm8960_dai);
++
++
++/* To complete PM */
++static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++ return 0;
++}
++
++static int wm8960_resume(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++ int i;
++ u8 data[2];
++ u16 *cache = codec->reg_cache;
++
++ /* Sync reg_cache with the hardware */
++ for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
++ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
++ data[1] = cache[i] & 0x00ff;
++ codec->hw_write(codec->control_data, data, 2);
++ }
++ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++ wm8960_dapm_event(codec, codec->suspend_dapm_state);
++ return 0;
++}
++
++/*
++ * initialise the WM8960 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int wm8960_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int reg, ret = 0;
++
++ codec->name = "WM8960";
++ codec->owner = THIS_MODULE;
++ codec->read = wm8960_read_reg_cache;
++ codec->write = wm8960_write;
++ codec->dapm_event = wm8960_dapm_event;
++ codec->dai = &wm8960_dai;
++ codec->num_dai = 1;
++ codec->reg_cache_size = ARRAY_SIZE(wm8960_reg);
++
++ codec->reg_cache =
++ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8960_reg), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache,
++ wm8960_reg, sizeof(u16) * ARRAY_SIZE(wm8960_reg));
++ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8960_reg);
++
++ wm8960_reset(codec);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8960: failed to create pcms\n");
++ goto pcm_err;
++ }
++
++ /* power on device */
++ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
++
++ /* set the update bits */
++ reg = wm8960_read_reg_cache(codec, WM8960_LOUT1);
++ wm8960_write(codec, WM8960_LOUT1, reg | 0x0100);
++ reg = wm8960_read_reg_cache(codec, WM8960_ROUT1);
++ wm8960_write(codec, WM8960_ROUT1, reg | 0x0100);
++
++ wm8960_add_controls(codec);
++ wm8960_add_widgets(codec);
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ printk(KERN_ERR "wm8960: failed to register card\n");
++ goto card_err;
++ }
++ return ret;
++
++card_err:
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++pcm_err:
++ kfree(codec->reg_cache);
++ return ret;
++}
++
++static struct snd_soc_device *wm8960_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++/*
++ * WM8960 2 wire address is 0x1a
++ */
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver wm8960_i2c_driver;
++static struct i2c_client client_template;
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++
++static int wm8960_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = wm8960_socdev;
++ struct wm8960_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = wm8960_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise WM8960\n");
++ goto err;
++ }
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int wm8960_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec* codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int wm8960_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, wm8960_codec_probe);
++}
++
++// tmp
++#define I2C_DRIVERID_WM8960 0xfefe
++
++/* corgi i2c codec control layer */
++static struct i2c_driver wm8960_i2c_driver = {
++ .driver = {
++ .name = "WM8960 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_WM8960,
++ .attach_adapter = wm8960_i2c_attach,
++ .detach_client = wm8960_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "WM8960",
++ .driver = &wm8960_i2c_driver,
++};
++#endif
++
++static int wm8960_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct wm8960_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++
++ info("WM8960 Audio Codec %s", WM8960_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++
++ wm8960_socdev = socdev;
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&wm8960_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int wm8960_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (codec->control_data)
++ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
++
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&wm8960_i2c_driver);
++#endif
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_wm8960 = {
++ .probe = wm8960_probe,
++ .remove = wm8960_remove,
++ .suspend = wm8960_suspend,
++ .resume = wm8960_resume,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
++
++MODULE_DESCRIPTION("ASoC WM8960 driver");
++MODULE_AUTHOR("Liam Girdwood");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/wm8960.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/wm8960.h 2007-07-16 15:07:35.516361233 +0200
+@@ -0,0 +1,121 @@
++/*
++ * wm8960.h -- WM8960 Soc Audio driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _WM8960_H
++#define _WM8960_H
++
++/* WM8960 register space */
++
++
++#define WM8960_CACHEREGNUM 56
++
++#define WM8960_LINVOL 0x0
++#define WM8960_RINVOL 0x1
++#define WM8960_LOUT1 0x2
++#define WM8960_ROUT1 0x3
++#define WM8960_CLOCK1 0x4
++#define WM8960_DACCTL1 0x5
++#define WM8960_DACCTL2 0x6
++#define WM8960_IFACE1 0x7
++#define WM8960_CLOCK2 0x8
++#define WM8960_IFACE2 0x9
++#define WM8960_LDAC 0xa
++#define WM8960_RDAC 0xb
++
++#define WM8960_RESET 0xf
++#define WM8960_3D 0x10
++#define WM8960_ALC1 0x11
++#define WM8960_ALC2 0x12
++#define WM8960_ALC3 0x13
++#define WM8960_NOISEG 0x14
++#define WM8960_LADC 0x15
++#define WM8960_RADC 0x16
++#define WM8960_ADDCTL1 0x17
++#define WM8960_ADDCTL2 0x18
++#define WM8960_POWER1 0x19
++#define WM8960_POWER2 0x1a
++#define WM8960_ADDCTL3 0x1b
++#define WM8960_APOP1 0x1c
++#define WM8960_APOP2 0x1d
++
++#define WM8960_LINPATH 0x20
++#define WM8960_RINPATH 0x21
++#define WM8960_LOUTMIX1 0x22
++
++#define WM8960_ROUTMIX2 0x25
++#define WM8960_MONOMIX1 0x26
++#define WM8960_MONOMIX2 0x27
++#define WM8960_LOUT2 0x28
++#define WM8960_ROUT2 0x29
++#define WM8960_MONO 0x2a
++#define WM8960_INBMIX1 0x2b
++#define WM8960_INBMIX2 0x2c
++#define WM8960_BYPASS1 0x2d
++#define WM8960_BYPASS2 0x2e
++#define WM8960_POWER3 0x2f
++#define WM8960_ADDCTL4 0x30
++#define WM8960_CLASSD1 0x31
++
++#define WM8960_CLASSD3 0x33
++#define WM8960_PLLN 0x34
++#define WM8960_PLLK1 0x35
++#define WM8960_PLLK2 0x36
++#define WM8960_PLLK3 0x37
++
++
++/*
++ * WM8960 Clock dividers
++ */
++#define WM8960_SYSCLKDIV 0
++#define WM8960_DACDIV 1
++#define WM8960_OPCLKDIV 2
++#define WM8960_DCLKDIV 3
++#define WM8960_TOCLKSEL 4
++#define WM8960_SYSCLKSEL 5
++
++#define WM8960_SYSCLK_DIV_1 (0 << 1)
++#define WM8960_SYSCLK_DIV_2 (2 << 1)
++
++#define WM8960_SYSCLK_MCLK (0 << 0)
++#define WM8960_SYSCLK_PLL (1 << 0)
++
++#define WM8960_DAC_DIV_1 (0 << 3)
++#define WM8960_DAC_DIV_1_5 (1 << 3)
++#define WM8960_DAC_DIV_2 (2 << 3)
++#define WM8960_DAC_DIV_3 (3 << 3)
++#define WM8960_DAC_DIV_4 (4 << 3)
++#define WM8960_DAC_DIV_5_5 (5 << 3)
++#define WM8960_DAC_DIV_6 (6 << 3)
++
++#define WM8960_DCLK_DIV_1_5 (0 << 6)
++#define WM8960_DCLK_DIV_2 (1 << 6)
++#define WM8960_DCLK_DIV_3 (2 << 6)
++#define WM8960_DCLK_DIV_4 (3 << 6)
++#define WM8960_DCLK_DIV_6 (4 << 6)
++#define WM8960_DCLK_DIV_8 (5 << 6)
++#define WM8960_DCLK_DIV_12 (6 << 6)
++#define WM8960_DCLK_DIV_16 (7 << 6)
++
++#define WM8960_TOCLK_F19 (0 << 1)
++#define WM8960_TOCLK_F21 (1 << 1)
++
++#define WM8960_OPCLK_DIV_1 (0 << 0)
++#define WM8960_OPCLK_DIV_2 (1 << 0)
++#define WM8960_OPCLK_DIV_3 (2 << 0)
++#define WM8960_OPCLK_DIV_4 (3 << 0)
++#define WM8960_OPCLK_DIV_5_5 (4 << 0)
++#define WM8960_OPCLK_DIV_6 (5 << 0)
++
++struct wm8960_setup_data {
++ unsigned short i2c_address;
++};
++
++extern struct snd_soc_codec_dai wm8960_dai;
++extern struct snd_soc_codec_device soc_codec_dev_wm8960;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/s3c24xx/smdk2440_wm8956.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/s3c24xx/smdk2440_wm8956.c 2007-07-16 15:07:35.564363967 +0200
+@@ -0,0 +1,335 @@
++/*
++ * smdk2440.c -- ALSA Soc Audio Layer
++ *
++ * (c) 2006 Wolfson Microelectronics PLC.
++ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * (c) 2004-2005 Simtec Electronics
++ * http://armlinux.simtec.co.uk/
++ * Ben Dooks <ben at simtec.co.uk>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This module is a modified version of the s3c24xx I2S driver supplied by
++ * Ben Dooks of Simtec and rejigged to the ASoC style at Wolfson Microelectronics
++ *
++ * Revision history
++ * 11th Dec 2006 Merged with Simtec driver
++ * 10th Nov 2006 Initial version.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware/scoop.h>
++#include <asm/arch/regs-iis.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/audio.h>
++#include <asm/io.h>
++#include <asm/arch/spi-gpio.h>
++#include "../codecs/wm8956.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-i2s.h"
++
++#define SMDK2440_DEBUG 0
++#if SMDK2440_DEBUG
++#define DBG(x...) printk(KERN_DEBUG x)
++#else
++#define DBG(x...)
++#endif
++
++/* audio clock in Hz */
++#define SMDK_CLOCK_SOURCE S3C24XX_CLKSRC_MPLL
++#define SMDK_CRYSTAL_CLOCK 12000000
++
++static int smdk2440_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->codec;
++
++ DBG("Entered %s\n",__FUNCTION__);
++
++ return 0;
++}
++
++static int smdk2440_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec *codec = rtd->socdev->codec;
++
++ DBG("Entered %s\n",__FUNCTION__);
++
++ return 0;
++}
++
++static int smdk2440_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ int bclk, mclk;
++ int ret;
++ int pll;
++ int div=0,sysclkdiv=0;
++ unsigned int rate = params_rate(params);
++
++ DBG("Entered %s\n",__FUNCTION__);
++
++ /* Work out the pll dividers */
++ switch(rate)
++ {
++ case 8000:
++ case 16000:
++ case 32000:
++ case 48000:
++ pll=12288000;
++ break;
++ case 96000:
++ pll=24576000;
++ break;
++ case 11025:
++ case 22050:
++ case 44100:
++ pll=11289600;
++ break;
++ case 88200:
++ pll=22579200;
++ break;
++ default:
++ pll=12288000;
++ }
++
++ /* Work out the DAV Div */
++ switch(rate)
++ {
++ case 96000:
++ case 88200:
++ case 48000:
++ case 44100:
++ div=0;
++ break;
++ case 32000:
++ div=1;
++ break;
++ case 22050;
++ div=2;
++ break;
++ case 16000:
++ div=1;
++ sysclkdiv=2;
++ break;
++ case 11025:
++ div=4;
++ break;
++ case 8000:
++ div=6;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ ret = codec_dai->dai_ops.set_pll(codec_dai, 0, SMDK_CRYSTAL_CLOCK, pll);
++ if (ret < 0)
++ return ret;
++
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8956_SYSCLKDIV, sysclkdiv);
++ if (ret < 0)
++ return ret;
++
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8956_DACDIV, div);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the audio system clock for DAC and ADC */
++ /* 12Mhz crystal for this example */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
++ SMDK_CRYSTAL_CLOCK, SND_SOC_CLOCK_OUT);
++ if (ret < 0)
++ return ret;
++
++ /* set MCLK division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, S3C2410_IISMOD_32FS );
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static struct snd_soc_ops smdk2440_ops = {
++ .startup = smdk2440_startup,
++ .shutdown = smdk2440_shutdown,
++ .hw_params = smdk2440_hw_params,
++};
++
++/* smdk2440 machine dapm widgets */
++static const struct snd_soc_dapm_widget smdk2440_dapm_widgets[] = {
++SND_SOC_DAPM_HP("Headphone Jack", NULL),
++SND_SOC_DAPM_MIC("Mic Jack", NULL),
++SND_SOC_DAPM_LINE("Line Jack", NULL),
++};
++
++/* smdk2440 machine audio map (connections to the codec pins) */
++static const char* audio_map[][3] = {
++ /* headphone connected to HPOUT */
++ {"Headphone Jack", NULL, "HPOUT"},
++ {"MICIN", NULL, "Mic Jack"},
++ {"MICIN", NULL, "Line Jack"},
++
++ {NULL, NULL, NULL},
++};
++
++/*
++ * Logic for a wm8956 as attached to SMDK2440
++ */
++static int smdk2440_wm8956_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ DBG("Entered %s\n",__FUNCTION__);
++
++
++ /* Add smdk2440 specific widgets */
++ for(i = 0; i < ARRAY_SIZE(smdk2440_dapm_widgets); i++) {
++ snd_soc_dapm_new_control(codec, &smdk2440_dapm_widgets[i]);
++ }
++
++ /* Set up smdk2440 specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++
++ return 0;
++}
++
++/* s3c24xx digital audio interface glue - connects codec <--> CPU */
++static struct snd_soc_dai_link s3c24xx_dai = {
++ .name = "WM8731",
++ .stream_name = "WM8731",
++ .cpu_dai = &s3c24xx_i2s_dai,
++ .codec_dai = &wm8956_dai,
++ .init = smdk2440_wm8956_init,
++ .ops = &smdk2440_ops,
++};
++
++/* smdk2440 audio machine driver */
++static struct snd_soc_machine snd_soc_machine_smdk2440 = {
++ .name = "SMDK2440",
++ .dai_link = &s3c24xx_dai,
++ .num_links = 1,
++};
++
++static struct wm8956_setup_data smdk2440_wm8956_setup = {
++ .i2c_address = 0x00,
++};
++
++/* s3c24xx audio subsystem */
++static struct snd_soc_device s3c24xx_snd_devdata = {
++ .machine = &snd_soc_machine_smdk2440,
++ .platform = &s3c24xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8956,
++ .codec_data = &smdk2440_wm8956_setup,
++};
++
++static struct platform_device *s3c24xx_snd_device;
++
++struct smdk2440_spi_device {
++ struct device *dev;
++};
++
++static struct smdk2440_spi_device smdk2440_spi_devdata = {
++};
++
++struct s3c2410_spigpio_info smdk2440_spi_devinfo = {
++ .pin_clk = S3C2410_GPF4,
++ .pin_mosi = S3C2410_GPF5,
++ .pin_miso = S3C2410_GPF6,
++ //.board_size,
++ //.board_info,
++ .chip_select=NULL,
++};
++
++static struct platform_device *smdk2440_spi_device;
++
++static int __init smdk2440_init(void)
++{
++ int ret;
++
++ if (!machine_is_smdk2440() && !machine_is_s3c2440()) {
++ DBG("%d\n",machine_arch_type);
++ DBG("Not a SMDK2440\n");
++ return -ENODEV;
++ }
++
++ s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!s3c24xx_snd_device) {
++ DBG("platform_dev_alloc failed\n");
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(s3c24xx_snd_device, &s3c24xx_snd_devdata);
++ s3c24xx_snd_devdata.dev = &s3c24xx_snd_device->dev;
++ ret = platform_device_add(s3c24xx_snd_device);
++
++ if (ret)
++ platform_device_put(s3c24xx_snd_device);
++
++ // Create a bitbanged SPI device
++
++ smdk2440_spi_device = platform_device_alloc("s3c24xx-spi-gpio",-1);
++ if (!smdk2440_spi_device) {
++ DBG("smdk2440_spi_device : platform_dev_alloc failed\n");
++ return -ENOMEM;
++ }
++ DBG("Return Code %d\n",ret);
++
++ platform_set_drvdata(smdk2440_spi_device, &smdk2440_spi_devdata);
++ smdk2440_spi_devdata.dev = &smdk2440_spi_device->dev;
++ smdk2440_spi_devdata.dev->platform_data = &smdk2440_spi_devinfo;
++ ret = platform_device_add(smdk2440_spi_device);
++
++ if (ret)
++ platform_device_put(smdk2440_spi_device);
++
++ return ret;
++}
++
++static void __exit smdk2440_exit(void)
++{
++ platform_device_unregister(s3c24xx_snd_device);
++}
++
++module_init(smdk2440_init);
++module_exit(smdk2440_exit);
++
++/* Module information */
++MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
++MODULE_DESCRIPTION("ALSA SoC SMDK2440");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/imx/imx-ssi.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/imx-ssi.h 2007-07-16 15:07:35.592365563 +0200
+@@ -0,0 +1,28 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef _IMX_SSI_H
++#define _IMX_SSI_H
++
++/* pxa2xx DAI SSP ID's */
++#define IMX_DAI_SSI1 0
++#define IMX_DAI_SSI2 1
++
++/* SSI clock sources */
++#define IMX_SSP_SYS_CLK 0
++
++/* SSI audio dividers */
++#define IMX_SSI_DIV_2 0
++#define IMX_SSI_DIV_PSR 1
++#define IMX_SSI_DIV_PM 2
++
++/* SSI Div 2 */
++#define IMX_SSI_DIV_2_OFF ~SSI_STCCR_DIV2
++#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
++
++extern struct snd_soc_cpu_dai imx_ssi_pcm_dai[2];
++
++#endif
+Index: linux-2.6.22.1/sound/soc/imx/mx31ads_wm8753.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/imx/mx31ads_wm8753.c 2007-07-16 15:07:35.616366929 +0200
+@@ -0,0 +1,400 @@
++/*
++ * mx31ads_wm8753.c -- SoC audio for mx31ads
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Author: Liam Girdwood
++ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * mx31ads audio amplifier code taken from arch/arm/mach-pxa/mx31ads.c
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 30th Oct 2005 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware.h>
++
++#include "../codecs/wm8753.h"
++#include "imx31-pcm.h"
++#include "imx-ssi.h"
++
++static struct snd_soc_machine mx31ads;
++
++static int mx31ads_hifi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0, fmt = 0;
++ int ret = 0;
++
++ /*
++ * The WM8753 is better at generating accurate audio clocks than the
++ * MX31 SSI controller, so we will use it as master when we can.
++ */
++ switch (params_rate(params)) {
++ case 8000:
++ case 16000:
++ fmt = SND_SOC_DAIFMT_CBS_CFS;
++ pll_out = 12288000;
++ break;
++ case 48000:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 12288000;
++ break;
++ case 96000:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 12288000;
++ break;
++ case 11025:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_16;
++ pll_out = 11289600;
++ break;
++ case 22050:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_8;
++ pll_out = 11289600;
++ break;
++ case 44100:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 11289600;
++ break;
++ case 88200:
++ fmt = SND_SOC_DAIFMT_CBM_CFS;
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 11289600;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSI system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is 13 MHz */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 13000000, pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int mx31ads_hifi_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
++}
++
++/*
++ * mx31ads WM8753 HiFi DAI opserations.
++ */
++static struct snd_soc_ops mx31ads_hifi_ops = {
++ .hw_params = mx31ads_hifi_hw_params,
++ .hw_free = mx31ads_hifi_hw_free,
++};
++
++static int mx31ads_voice_startup(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++static void mx31ads_voice_shutdown(struct snd_pcm_substream *substream)
++{
++}
++
++static int mx31ads_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0, pcmdiv = 0;
++ int ret = 0;
++
++ /*
++ * The WM8753 is far better at generating accurate audio clocks than the
++ * pxa2xx SSP controller, so we will use it as master when we can.
++ */
++ switch (params_rate(params)) {
++ case 8000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 256kHz */
++ break;
++ case 16000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_3; /* 4.096 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 512kHz */
++ break;
++ case 48000:
++ pll_out = 12288000;
++ pcmdiv = WM8753_PCM_DIV_1; /* 12.288 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 1.536 MHz */
++ break;
++ case 11025:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_4; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 352.8 kHz */
++ break;
++ case 22050:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_2; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 705.6 kHz */
++ break;
++ case 44100:
++ pll_out = 11289600;
++ pcmdiv = WM8753_PCM_DIV_1; /* 11.2896 MHz */
++ bclk = WM8753_VXCLK_DIV_8; /* 1.4112 MHz */
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP system clock as input (unused) */
++// ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
++// SND_SOC_CLOCK_IN);
++// if (ret < 0)
++// return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_VXCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set codec PCM division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is 13 MHz */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 13000000, pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int mx31ads_voice_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
++}
++
++static struct snd_soc_ops mx31ads_voice_ops = {
++ .startup = mx31ads_voice_startup,
++ .shutdown = mx31ads_voice_shutdown,
++ .hw_params = mx31ads_voice_hw_params,
++ .hw_free = mx31ads_voice_hw_free,
++};
++
++static int mx31ads_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int mx31ads_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mx31ads_probe(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int mx31ads_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++/* example machine audio_mapnections */
++static const char* audio_map[][3] = {
++
++ /* mic is connected to mic1 - with bias */
++ {"MIC1", NULL, "Mic Bias"},
++ {"MIC1N", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Mic1 Jack"},
++ {"Mic Bias", NULL, "Mic1 Jack"},
++
++ {"ACIN", NULL, "ACOP"},
++ {NULL, NULL, NULL},
++};
++
++/* headphone detect support on my board */
++static const char * hp_pol[] = {"Headphone", "Speaker"};
++static const struct soc_enum wm8753_enum =
++ SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
++
++static const struct snd_kcontrol_new wm8753_mx31ads_controls[] = {
++ SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
++ SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
++};
++
++/*
++ * This is an example machine initialisation for a wm8753 connected to a
++ * mx31ads II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int mx31ads_wm8753_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* set up mx31ads codec pins */
++ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
++ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
++ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
++
++ /* add mx31ads specific controls */
++ for (i = 0; i < ARRAY_SIZE(wm8753_mx31ads_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8753_mx31ads_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ /* set up mx31ads specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link mx31ads_dai[] = {
++{ /* Hifi Playback - for similatious use with voice below */
++ .name = "WM8753",
++ .stream_name = "WM8753 HiFi",
++ .cpu_dai = &imx_ssi_pcm_dai[0],
++ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
++ .init = mx31ads_wm8753_init,
++ .ops = &mx31ads_hifi_ops,
++},
++//{ /* Voice via BT */
++// .name = "Bluetooth",
++// .stream_name = "Voice",
++// .cpu_dai = &pxa_ssp_dai[1],
++// .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
++// .ops = &mx31ads_voice_ops,
++//},
++};
++
++static struct snd_soc_machine mx31ads = {
++ .name = "mx31ads",
++ .probe = mx31ads_probe,
++ .remove = mx31ads_remove,
++ .suspend_pre = mx31ads_suspend,
++ .resume_post = mx31ads_resume,
++ .dai_link = mx31ads_dai,
++ .num_links = ARRAY_SIZE(mx31ads_dai),
++};
++
++static struct wm8753_setup_data mx31ads_wm8753_setup = {
++ .i2c_address = 0x1a,
++};
++
++static struct snd_soc_device mx31ads_snd_devdata = {
++ .machine = &mx31ads,
++ .platform = &mxc_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8753,
++ .codec_data = &mx31ads_wm8753_setup,
++};
++
++static struct platform_device *mx31ads_snd_device;
++
++static int __init mx31ads_init(void)
++{
++ int ret;
++
++ mx31ads_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!mx31ads_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(mx31ads_snd_device, &mx31ads_snd_devdata);
++ mx31ads_snd_devdata.dev = &mx31ads_snd_device->dev;
++ ret = platform_device_add(mx31ads_snd_device);
++
++ if (ret)
++ platform_device_put(mx31ads_snd_device);
++
++ return ret;
++}
++
++static void __exit mx31ads_exit(void)
++{
++ platform_device_unregister(mx31ads_snd_device);
++}
++
++module_init(mx31ads_init);
++module_exit(mx31ads_exit);
++
++/* Module information */
++MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM8753 mx31ads");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/s3c24xx/lm4857.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/s3c24xx/lm4857.h 2007-07-16 15:07:35.644368528 +0200
+@@ -0,0 +1,15 @@
++#ifndef LM4857_H_
++#define LM4857_H_
++
++/* The register offsets in the cache array */
++#define LM4857_MVOL 0
++#define LM4857_LVOL 1
++#define LM4857_RVOL 2
++#define LM4857_CTRL 3
++
++/* the shifts required to set these bits */
++#define LM4857_3D 5
++#define LM4857_WAKEUP 5
++#define LM4857_EPGAIN 4
++
++#endif /*LM4857_H_*/
+Index: linux-2.6.22.1/sound/soc/codecs/tlv320.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/tlv320.c 2007-07-16 15:07:35.664369667 +0200
+@@ -0,0 +1,609 @@
++/*
++ * tlv320.c -- TLV 320 ALSA Soc Audio driver
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Copyright 2006 Atlab srl.
++ *
++ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ * Nicola Perrino <nicola.perrino at atlab.it>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/i2c.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <sound/initval.h>
++
++#include "tlv320.h"
++
++#define AUDIO_NAME "tlv320"
++#define TLV320_VERSION "0.1"
++
++/*
++ * Debug
++ */
++
++//#define TLV320_DEBUG 0
++
++#ifdef TLV320_DEBUG
++#define dbg(format, arg...) \
++ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
++#else
++#define dbg(format, arg...) do {} while (0)
++#endif
++#define err(format, arg...) \
++ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
++
++
++#define TLV320_VOICE_RATES \
++ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
++ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
++ SNDRV_PCM_RATE_48000)
++
++
++#define TLV320_VOICE_BITS \
++ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
++
++
++static int caps_charge = 2000;
++static int setting = 1;
++module_param(caps_charge, int, 0);
++module_param(setting, int, 0);
++MODULE_PARM_DESC(caps_charge, "TLV320 cap charge time (msecs)");
++
++static struct workqueue_struct *tlv320_workq = NULL;
++//static struct work_struct tlv320_dapm_work;
++
++/* codec private data */
++struct tlv320_priv {
++ unsigned int sysclk;
++ unsigned int pcmclk;
++};
++
++
++#ifdef TLV320AIC24K
++/* ADDR table */
++static const unsigned char tlv320_reg_addr[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x01, /* CONTROL REG 1 */
++ 0x02, /* CONTROL REG 2 */
++ 0x03, /* CONTROL REG 3A */
++ 0x03, /* CONTROL REG 3B */
++ 0x03, /* CONTROL REG 3C */
++ 0x03, /* CONTROL REG 3D */
++ 0x04, /* CONTROL REG 4 */
++ 0x04, /* CONTROL REG 4 Bis */
++ 0x05, /* CONTROL REG 5A */
++ 0x05, /* CONTROL REG 5B */
++ 0x05, /* CONTROL REG 5C */
++ 0x05, /* CONTROL REG 5D */
++ 0x06, /* CONTROL REG 6A */
++ 0x06, /* CONTROL REG 6B */
++};
++
++/*
++ * DATA case digital SET1:
++ * SSP -> DAC -> OUT
++ * IN -> ADC -> SSP
++ * IN = HNSI (MIC)
++ * OUT = HDSO (SPKG)
++ * Usage: playback, capture streams
++ */
++static const unsigned char tlv320_reg_data_init_set1[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x49, /* CONTROL REG 1 */
++ 0x20, /* CONTROL REG 2 */
++ 0x01, /* CONTROL REG 3A */
++ 0x40, /* CONTROL REG 3B */
++ 0x81, /* CONTROL REG 3C */
++ 0xc0, /* CONTROL REG 3D */
++ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
++ 0x88,//0x90, /* CONTROL REG 4 Bis */
++ 0x00, /* CONTROL REG 5A */
++ 0x40,//(0dB) /* CONTROL REG 5B */
++ 0xbf, /* CONTROL REG 5C */
++ 0xc0, /* CONTROL REG 5D */
++ 0x02,//(HNSI) /* CONTROL REG 6A */
++ 0x81 //(HDSO) /* CONTROL REG 6B */
++};
++
++/*
++ * DATA case digital SET2:
++ * SSP -> DAC -> OUT
++ * IN -> ADC -> SSP
++ * IN = HDSI (PHONE IN)
++ * OUT = HNSO (PHONE OUT)
++ * Usage: playback, capture streams
++ */
++static const unsigned char tlv320_reg_data_init_set2[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x49, /* CONTROL REG 1 */
++ 0x20, /* CONTROL REG 2 */
++ 0x01, /* CONTROL REG 3A */
++ 0x40, /* CONTROL REG 3B */
++ 0x81, /* CONTROL REG 3C */
++ 0xc0, /* CONTROL REG 3D */
++ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
++ 0x88,//0x90, /* CONTROL REG 4 Bis */
++ 0x00, /* CONTROL REG 5A */
++ 0x52,//(-27dB) /* CONTROL REG 5B */
++ 0xbf, /* CONTROL REG 5C */
++ 0xc0, /* CONTROL REG 5D */
++ 0x01,//(PHONE IN) /* CONTROL REG 6A */
++ 0x82 //(PHONE OUT) /* CONTROL REG 6B */
++};
++
++/*
++ * DATA case analog:
++ * ADC, DAC, SSP off
++ * Headset input to output (HDSI2O -> 1)
++ * Handset input to output (HNSI2O -> 1)
++ * Usage: room monitor
++ */
++static const unsigned char tlv320_reg_data_init_set3[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x08, /* CONTROL REG 1 */
++ 0x20, /* CONTROL REG 2 */
++ 0x11, /* CONTROL REG 3A */
++ 0x40, /* CONTROL REG 3B */
++ 0x80, /* CONTROL REG 3C */
++ 0xc0, /* CONTROL REG 3D */
++ 0x00, /* CONTROL REG 4 */
++ 0x00, /* CONTROL REG 5A */
++ 0x40, /* CONTROL REG 5B */
++ 0x80, /* CONTROL REG 5C */
++ 0xc0, /* CONTROL REG 5D */
++ 0x60, /* CONTROL REG 6A */
++ 0x80 /* CONTROL REG 6B */
++};
++
++#else // TLV320AIC14k
++
++/* ADDR table */
++static const unsigned char tlv320_reg_addr[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x01, /* CONTROL REG 1 */
++ 0x02, /* CONTROL REG 2 */
++ 0x03, /* CONTROL REG 3 */
++ 0x04, /* CONTROL REG 4 */
++ 0x04, /* CONTROL REG 4 Bis */
++ 0x05, /* CONTROL REG 5A */
++ 0x05, /* CONTROL REG 5B */
++ 0x05, /* CONTROL REG 5C */
++ 0x05, /* CONTROL REG 5D */
++ 0x06 /* CONTROL REG 6 */
++};
++
++/*
++ * DATA case digital:
++ * SSP -> DAC -> OUT
++ * IN -> ADC -> SSP
++ * Usage: playback, capture streams
++ */
++static const unsigned char tlv320_reg_data_init_set1[] = {
++ 0x00, /* CONTROL REG 0 No Operation */
++ 0x41, /* CONTROL REG 1 */
++ 0x20, /* CONTROL REG 2 */
++ 0x09, /* CONTROL REG 3 */
++ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
++ 0x88,//0x90, /* CONTROL REG 4 Bis */
++ 0x2A, /* CONTROL REG 5A */
++ 0x6A, /* CONTROL REG 5B */
++ 0xbc, /* CONTROL REG 5C */
++ 0xc0, /* CONTROL REG 5D */
++ 0x00 /* CONTROL REG 6 */
++};
++#endif
++/*
++ * read tlv320 register cache
++ */
++static inline unsigned int tlv320_read_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg)
++{
++ u8 *cache = codec->reg_cache;
++ if (reg > ARRAY_SIZE(tlv320_reg_addr))
++ return -1;
++ return cache[reg];
++}
++
++/*
++ * write tlv320 register cache
++ */
++static inline void tlv320_write_reg_cache(struct snd_soc_codec *codec,
++ unsigned int reg, unsigned int value)
++{
++ u8 *cache = codec->reg_cache;
++ if (reg > ARRAY_SIZE(tlv320_reg_addr))
++ return;
++ cache[reg] = value;
++}
++
++/*
++ * read tlv320
++ */
++static int tlv320_read (struct snd_soc_codec *codec, u8 reg)
++{
++ return i2c_smbus_read_byte_data(codec->control_data, reg);
++}
++
++/*
++ * write tlv320
++ */
++static int tlv320_write(struct snd_soc_codec *codec, unsigned int reg,
++ unsigned int value)
++{
++ if (tlv320_reg_addr[reg] > 0x06)
++ return -1;
++
++ tlv320_write_reg_cache (codec, reg, value);
++
++ return i2c_smbus_write_byte_data(codec->control_data, tlv320_reg_addr[reg], value);
++}
++
++
++/*
++ * write block tlv320
++ */
++static int tlv320_write_block (struct snd_soc_codec *codec,
++ const u8 *data, unsigned int len)
++{
++ int ret = -1;
++ int i;
++
++ for (i=0; i<len; i++) {
++ dbg("addr = 0x%02x, data = 0x%02x", tlv320_reg_addr[i], data[i]);
++ if ((ret = tlv320_write(codec, i, data[i])) < 0)
++ break;
++ }
++
++ return ret;
++}
++
++
++static int tlv320_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
++ unsigned int fmt)
++{
++ dbg("tlv320_set_dai_fmt enter");
++ return 0;
++}
++
++/*
++ * Set PCM DAI bit size and sample rate.
++ */
++static int tlv320_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ dbg("tlv320_pcm_hw_params enter");
++ return 0;
++}
++
++
++static int tlv320_config_pcm_sysclk(struct snd_soc_codec_dai *codec_dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ dbg("tlv320_config_pcm_sysclk enter");
++ return 0;
++}
++
++
++/*
++ * Voice over PCM DAI
++ */
++struct snd_soc_codec_dai tlv320_dai[] = {
++{ .name = "TLV320 Voice",
++ .id = 1,
++ .playback = {
++ .stream_name = "Voice Playback",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = TLV320_VOICE_RATES,
++ .formats = TLV320_VOICE_BITS,},
++ .capture = {
++ .stream_name = "Voice Capture",
++ .channels_min = 1,
++ .channels_max = 2,
++ .rates = TLV320_VOICE_RATES,
++ .formats = TLV320_VOICE_BITS,},
++ .ops = {
++ .hw_params = tlv320_pcm_hw_params,},
++ .dai_ops = {
++ .digital_mute = NULL,
++ .set_fmt = tlv320_set_dai_fmt,
++ .set_clkdiv = NULL,
++ .set_pll = NULL,
++ .set_sysclk = tlv320_config_pcm_sysclk,
++ },
++},
++
++
++};
++EXPORT_SYMBOL_GPL(tlv320_dai);
++
++
++static void tlv320_work(struct work_struct *work)
++{
++#if 0
++ struct snd_soc_codec *codec =
++ container_of(work, struct snd_soc_codec, delayed_work.work);
++ //wm8753_dapm_event(codec, codec->dapm_state);
++#endif
++}
++
++/*
++ * initialise the TLV320 driver
++ * register the mixer and dsp interfaces with the kernel
++ */
++static int tlv320_init(struct snd_soc_device *socdev)
++{
++ struct snd_soc_codec *codec = socdev->codec;
++ int ret = 0;
++
++ codec->name = "TLV320";
++ codec->owner = THIS_MODULE;
++ codec->read = tlv320_read_reg_cache;
++ codec->write = tlv320_write;
++ codec->dai = tlv320_dai;
++ codec->num_dai = ARRAY_SIZE(tlv320_dai);
++ codec->reg_cache_size = ARRAY_SIZE(tlv320_reg_addr);
++
++ codec->reg_cache =
++ kzalloc(sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr), GFP_KERNEL);
++ if (codec->reg_cache == NULL)
++ return -ENOMEM;
++ memcpy(codec->reg_cache, tlv320_reg_addr,
++ sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr));
++ codec->reg_cache_size = sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr);
++
++ /* register pcms */
++ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
++ if (ret < 0) {
++ kfree(codec->reg_cache);
++ return ret;
++ }
++
++ queue_delayed_work(tlv320_workq,
++ &codec->delayed_work, msecs_to_jiffies(caps_charge));
++
++ ret = snd_soc_register_card(socdev);
++ if (ret < 0) {
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++ }
++
++ return ret;
++}
++
++/* If the i2c layer weren't so broken, we could pass this kind of data
++ around */
++static struct snd_soc_device *tlv320_socdev;
++
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++
++#define I2C_DRIVERID_TLV320 0xfefe /* liam - need a proper id */
++
++static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static struct i2c_driver tlv320_i2c_driver;
++static struct i2c_client client_template;
++
++static int tlv320_codec_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ struct snd_soc_device *socdev = tlv320_socdev;
++ struct tlv320_setup_data *setup = socdev->codec_data;
++ struct snd_soc_codec *codec = socdev->codec;
++ struct i2c_client *i2c;
++ int ret, len;
++ const unsigned char *data;
++
++ if (addr != setup->i2c_address)
++ return -ENODEV;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ kfree(codec);
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++ i2c_set_clientdata(i2c, codec);
++ codec->control_data = i2c;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ err("failed to attach codec at addr %x\n", addr);
++ goto err;
++ }
++
++ ret = tlv320_init(socdev);
++ if (ret < 0) {
++ err("failed to initialise TLV320\n");
++ goto err;
++ }
++
++ switch(setting) {
++ case 1:
++ data = tlv320_reg_data_init_set1;
++ len = sizeof(tlv320_reg_data_init_set1);
++ break;
++ case 2:
++ data = tlv320_reg_data_init_set2;
++ len = sizeof(tlv320_reg_data_init_set2);
++ break;
++ case 3:
++ data = tlv320_reg_data_init_set3;
++ len = sizeof(tlv320_reg_data_init_set3);
++ break;
++ default:
++ data = tlv320_reg_data_init_set1;
++ len = sizeof(tlv320_reg_data_init_set1);
++ break;
++ }
++
++ ret = tlv320_write_block(codec, data, len);
++
++ if (ret < 0) {
++ err("attach error: init status %d\n", ret);
++ } else {
++ info("attach: chip tlv320 at address 0x%02x",
++ tlv320_read(codec, 0x02) << 1);
++ }
++
++ //tlv320_write(codec, CODEC_REG6B, 0x80);
++#if 0
++ int value;
++ int i;
++
++ for (i=0; i<len; i++) {
++ value = tlv320_read(codec, tlv320_reg_addr[i]);
++ dbg("read addr = 0x%02x, data = 0x%02x", tlv320_reg_addr[i], value);
++ mdelay(10);
++ }
++
++#endif
++
++
++ return ret;
++
++err:
++ kfree(codec);
++ kfree(i2c);
++ return ret;
++}
++
++static int tlv320_i2c_detach(struct i2c_client *client)
++{
++ struct snd_soc_codec *codec = i2c_get_clientdata(client);
++ i2c_detach_client(client);
++ kfree(codec->reg_cache);
++ kfree(client);
++ return 0;
++}
++
++static int tlv320_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, tlv320_codec_probe);
++}
++
++/* tlv320 i2c codec control layer */
++static struct i2c_driver tlv320_i2c_driver = {
++ .driver = {
++ .name = "tlv320 I2C Codec",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_TLV320,
++ .attach_adapter = tlv320_i2c_attach,
++ .detach_client = tlv320_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "tlv320",
++ .driver = &tlv320_i2c_driver,
++};
++#endif
++
++static int tlv320_probe(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct tlv320_setup_data *setup;
++ struct snd_soc_codec *codec;
++ int ret = 0;
++ struct tlv320_priv *tlv320;
++
++ info("TLV320 Audio Codec %s", TLV320_VERSION);
++
++ setup = socdev->codec_data;
++ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
++ if (codec == NULL)
++ return -ENOMEM;
++
++ tlv320 = kzalloc(sizeof(struct tlv320_priv), GFP_KERNEL);
++ if (tlv320 == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++
++ codec->private_data = tlv320;
++
++ socdev->codec = codec;
++ mutex_init(&codec->mutex);
++ INIT_LIST_HEAD(&codec->dapm_widgets);
++ INIT_LIST_HEAD(&codec->dapm_paths);
++ tlv320_socdev = socdev;
++
++ INIT_DELAYED_WORK(&codec->delayed_work, tlv320_work);
++ tlv320_workq = create_workqueue("tlv320");
++ if (tlv320_workq == NULL) {
++ kfree(codec);
++ return -ENOMEM;
++ }
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ if (setup->i2c_address) {
++ normal_i2c[0] = setup->i2c_address;
++ codec->hw_write = (hw_write_t)i2c_master_send;
++ ret = i2c_add_driver(&tlv320_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++ }
++#else
++ /* Add other interfaces here */
++#endif
++ return ret;
++}
++
++/* power down chip */
++static int tlv320_remove(struct platform_device *pdev)
++{
++ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
++ struct snd_soc_codec *codec = socdev->codec;
++
++ if (tlv320_workq)
++ destroy_workqueue(tlv320_workq);
++ snd_soc_free_pcms(socdev);
++ snd_soc_dapm_free(socdev);
++#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
++ i2c_del_driver(&tlv320_i2c_driver);
++#endif
++ kfree(codec->private_data);
++ kfree(codec);
++
++ return 0;
++}
++
++struct snd_soc_codec_device soc_codec_dev_tlv320 = {
++ .probe = tlv320_probe,
++ .remove = tlv320_remove,
++};
++
++EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320);
++
++MODULE_DESCRIPTION("ASoC TLV320 driver");
++MODULE_AUTHOR("Nicola Perrino");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/codecs/tlv320.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/codecs/tlv320.h 2007-07-16 15:07:35.692371262 +0200
+@@ -0,0 +1,111 @@
++/*
++ * tlv320.h -- TLV 320 ALSA Soc Audio driver
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Copyright 2006 Atlab srl.
++ *
++ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ * Nicola Perrino <nicola.perrino at atlab.it>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#ifndef _TLV320_H
++#define _TLV320_H
++
++#define TLV320AIC24K
++
++
++/* TLV320 register space */
++#define CODEC_NOOP 0x00
++#define CODEC_REG1 0x01
++#define CODEC_REG2 0x02
++#define CODEC_REG3A 0x03
++#define CODEC_REG3B 0x04
++#define CODEC_REG3C 0x05
++#define CODEC_REG3D 0x06
++#define CODEC_REG4A 0x07
++#define CODEC_REG4B 0x08
++#define CODEC_REG5A 0x09
++#define CODEC_REG5B 0x0a
++#define CODEC_REG5C 0x0b
++#define CODEC_REG5D 0x0c
++#define CODEC_REG6A 0x0d
++#define CODEC_REG6B 0x0e
++
++
++// Control Register 1
++#define REG1_CONTINUOUS 0x40
++#define REG1_IIR_EN 0x20
++#define REG1_MIC_BIAS_235 0x08
++#define REG1_ANALOG_LOOP_BACK 0x04
++#define REG1_DIGITAL_LOOP_BACK 0x02
++#define REG1_DAC16 0x01
++
++// Control Register 2
++#define REG2_TURBO_EN 0x80
++#define REG2_FIR_BYPASS 0x40
++#define REG2_GPIO 0x02
++#define REG2_GPIO_1 0x06
++
++// Control Register 3A
++#define REG3_PWDN_ALL 0x30
++#define REG3_PWDN_ADC 0x10
++#define REG3_PWDN_DAC 0x20
++#define REG3_SW_RESET 0x08
++#define REG3_SAMPLING_FACTOR1 0x01
++#define REG3_SAMPLING_FACTOR2 0x02
++
++// Control Register 3B
++#define REG3_8KBP_EN 0x60
++#define REG3_MUTE_OUTP1 0x42
++#define REG3_MUTE_OUTP2 0x48
++#define REG3_MUTE_OUTP3 0x44
++
++// Control Register 4
++#define REG4_FSDIV_M 0x85 //M=5
++#define REG4_FSDIV_NP 0x08 //N=1, P=8
++//#define REG4_FSDIV_NP 0x01 //N=1, P=8
++#define REG4_FSDIV_NP1 0x02 //N=16, P=2
++
++// Control Register 5
++#define REG5A_ADC_GAIN 0x02 //3dB
++#define REG5A_ADC_MUTE 0x0f //Mute
++#define REG5B_DAC_GAIN 0x42 //-3dB
++#define REG5B_DAC_MUTE 0x4f //Mute
++#define REG5C_SIDETONE_MUTE 0xBF
++
++// Control Register 6
++#define REG6A_AIC24A_CH1_IN 0x08 //INP1 to ADC
++#define REG6B_AIC24A_CH1_OUT 0x82 //OUTP2 to DAC
++#define REG6A_AIC24A_CH2_IN 0x02 //INP2 to ADC
++#define REG6B_AIC24A_CH2_OUT 0x81 //OUTP3 to DAC
++
++/* clock inputs */
++#define TLV320_MCLK 0
++#define TLV320_PCMCLK 1
++
++
++struct tlv320_setup_data {
++ unsigned short i2c_address;
++};
++
++/* DAI ifmodes */
++/* mode 1 IFMODE = 00 */
++#define TLV320_DAI_MODE1_VOICE 0
++#define TLV320_DAI_MODE1_HIFI 1
++/* mode 2 IFMODE = 01 */
++#define TLV320_DAI_MODE2_VOICE 2
++/* mode 3 IFMODE = 10 */
++#define TLV320_DAI_MODE3_HIFI 3
++/* mode 4 IFMODE = 11 */
++#define TLV320_DAI_MODE4_HIFI 4
++
++extern struct snd_soc_codec_dai tlv320_dai[5];
++extern struct snd_soc_codec_device soc_codec_dev_tlv320;
++
++#endif
+Index: linux-2.6.22.1/sound/soc/pxa/amesom_tlv320.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/pxa/amesom_tlv320.c 2007-07-16 15:07:35.740373998 +0200
+@@ -0,0 +1,211 @@
++/*
++ * amesom_tlv320.c -- SoC audio for Amesom
++ *
++ * Copyright 2005 Wolfson Microelectronics PLC.
++ * Copyright 2006 Atlab srl.
++ *
++ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
++ * Nicola Perrino <nicola.perrino at atlab.it>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 5th Dec 2006 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/audio.h>
++
++#include "../codecs/tlv320.h"
++#include "pxa2xx-pcm.h"
++#include "pxa2xx-i2s.h"
++#include "pxa2xx-ssp.h"
++
++
++/*
++ * SSP2 GPIO's
++ */
++
++#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
++#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
++#define GPIO50_SSP2CLKS_MD (50 | GPIO_ALT_FN_3_IN)
++#define GPIO14_SSP2FRMS_MD (14 | GPIO_ALT_FN_2_IN)
++#define GPIO50_SSP2CLKM_MD (50 | GPIO_ALT_FN_3_OUT)
++#define GPIO14_SSP2FRMM_MD (14 | GPIO_ALT_FN_2_OUT)
++
++
++static struct snd_soc_machine amesom;
++
++
++static int amesom_probe(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int amesom_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static int tlv320_voice_startup(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++static void tlv320_voice_shutdown(struct snd_pcm_substream *substream)
++{
++ return;
++}
++
++/*
++ * Tlv320 uses SSP port for playback.
++ */
++static int tlv320_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ int ret = 0;
++
++ //printk("tlv320_voice_hw_params enter\n");
++ switch(params_rate(params)) {
++ case 8000:
++ //printk("tlv320_voice_hw_params 8000\n");
++ break;
++ case 16000:
++ //printk("tlv320_voice_hw_params 16000\n");
++ break;
++ default:
++ break;
++ }
++
++ // CODEC MASTER, SSP SLAVE
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the SSP system clock as input (unused) */
++ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_NET_PLL, 0,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set SSP slots */
++ //ret = cpu_dai->dai_ops.set_tdm_slot(cpu_dai, 0x1, slots);
++ ret = cpu_dai->dai_ops.set_tdm_slot(cpu_dai, 0x3, 1);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int tlv320_voice_hw_free(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++static struct snd_soc_ops tlv320_voice_ops = {
++ .startup = tlv320_voice_startup,
++ .shutdown = tlv320_voice_shutdown,
++ .hw_params = tlv320_voice_hw_params,
++ .hw_free = tlv320_voice_hw_free,
++};
++
++
++static struct snd_soc_dai_link amesom_dai[] = {
++{
++ .name = "TLV320",
++ .stream_name = "TLV320 Voice",
++ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP2],
++ .codec_dai = &tlv320_dai[TLV320_DAI_MODE1_VOICE],
++ .ops = &tlv320_voice_ops,
++},
++};
++
++static struct snd_soc_machine amesom = {
++ .name = "Amesom",
++ .probe = amesom_probe,
++ .remove = amesom_remove,
++ .dai_link = amesom_dai,
++ .num_links = ARRAY_SIZE(amesom_dai),
++};
++
++static struct tlv320_setup_data amesom_tlv320_setup = {
++#ifdef TLV320AIC24K //codec2
++ .i2c_address = 0x41,
++#else // TLV320AIC14k
++ .i2c_address = 0x40,
++#endif
++};
++
++static struct snd_soc_device amesom_snd_devdata = {
++ .machine = &amesom,
++ .platform = &pxa2xx_soc_platform,
++ .codec_dev = &soc_codec_dev_tlv320,
++ .codec_data = &amesom_tlv320_setup,
++};
++
++static struct platform_device *amesom_snd_device;
++
++static int __init amesom_init(void)
++{
++ int ret;
++
++ amesom_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!amesom_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(amesom_snd_device, &amesom_snd_devdata);
++ amesom_snd_devdata.dev = &amesom_snd_device->dev;
++ ret = platform_device_add(amesom_snd_device);
++
++ if (ret)
++ platform_device_put(amesom_snd_device);
++
++
++ /* SSP port 2 slave */
++ pxa_gpio_mode(GPIO11_SSP2RX_MD);
++ pxa_gpio_mode(GPIO13_SSP2TX_MD);
++ pxa_gpio_mode(GPIO50_SSP2CLKS_MD);
++ pxa_gpio_mode(GPIO14_SSP2FRMS_MD);
++
++ return ret;
++}
++
++static void __exit amesom_exit(void)
++{
++ platform_device_unregister(amesom_snd_device);
++}
++
++module_init(amesom_init);
++module_exit(amesom_exit);
++
++/* Module information */
++MODULE_AUTHOR("Nicola Perrino");
++MODULE_DESCRIPTION("ALSA SoC TLV320 Amesom");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/s3c24xx/neo1973_wm8753.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/sound/soc/s3c24xx/neo1973_wm8753.c 2007-07-16 15:07:35.764375364 +0200
+@@ -0,0 +1,686 @@
++/*
++ * neo1973_wm8753.c -- SoC audio for Neo1973
++ *
++ * Copyright 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 20th Jan 2007 Initial version.
++ * 05th Feb 2007 Rename all to Neo1973
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware/scoop.h>
++#include <asm/arch/regs-iis.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/audio.h>
++#include <asm/io.h>
++#include <asm/arch/spi-gpio.h>
++#include "../codecs/wm8753.h"
++#include "lm4857.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-i2s.h"
++
++#define NEO1973_DEBUG 0
++#if NEO1973_DEBUG
++#define DBG(x...) printk(KERN_DEBUG x)
++#else
++#define DBG(x...)
++#endif
++
++/* define the scenarios */
++#define NEO_AUDIO_OFF 0
++#define NEO_GSM_CALL_AUDIO_HANDSET 1
++#define NEO_GSM_CALL_AUDIO_HEADSET 2
++#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
++#define NEO_STEREO_TO_SPEAKERS 4
++#define NEO_STEREO_TO_HEADPHONES 5
++#define NEO_CAPTURE_HANDSET 6
++#define NEO_CAPTURE_HEADSET 7
++#define NEO_CAPTURE_BLUETOOTH 8
++
++static struct snd_soc_machine neo1973;
++static struct i2c_client *i2c;
++
++static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0;
++ int ret = 0;
++ unsigned long iis_clkrate;
++
++ iis_clkrate = s3c24xx_i2s_get_clockrate();
++
++ switch (params_rate(params)) {
++ case 8000:
++ case 16000:
++ pll_out = 12288000;
++ break;
++ case 48000:
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 12288000;
++ break;
++ case 96000:
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 12288000;
++ break;
++ case 11025:
++ bclk = WM8753_BCLK_DIV_16;
++ pll_out = 11289600;
++ break;
++ case 22050:
++ bclk = WM8753_BCLK_DIV_8;
++ pll_out = 11289600;
++ break;
++ case 44100:
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 11289600;
++ break;
++ case 88200:
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 11289600;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set MCLK division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
++ S3C2410_IISMOD_32FS );
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set prescaler division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
++ S3C24XX_PRESCALE(4,4));
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is PCLK/4 */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, iis_clkrate/4,
++ pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
++}
++
++/*
++ * Neo1973 WM8753 HiFi DAI opserations.
++ */
++static struct snd_soc_ops neo1973_hifi_ops = {
++ .hw_params = neo1973_hifi_hw_params,
++ .hw_free = neo1973_hifi_hw_free,
++};
++
++static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ unsigned int pcmdiv = 0;
++ int ret = 0;
++ unsigned long iis_clkrate;
++
++ /* todo: gg where is sysclk coming from for voice ?? */
++ iis_clkrate = s3c24xx_i2s_get_clockrate();
++
++ if (params_rate(params) != 8000)
++ return -EINVAL;
++ if(params_channels(params) != 1)
++ return -EINVAL;
++
++ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
++
++ /* todo: gg check mode (DSP_B) against CSR datasheet */
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec PCM division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
++ if (ret < 0)
++ return ret;
++
++ /* configue and enable PLL for 12.288MHz output */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, iis_clkrate/4,
++ 12288000);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
++}
++
++static struct snd_soc_ops neo1973_voice_ops = {
++ .hw_params = neo1973_voice_hw_params,
++ .hw_free = neo1973_voice_hw_free,
++};
++
++static int neo1973_scenario = 0;
++
++static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = neo1973_scenario;
++ return 0;
++}
++
++static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
++{
++ switch(neo1973_scenario) {
++ case NEO_AUDIO_OFF:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_GSM_CALL_AUDIO_HANDSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
++ break;
++ case NEO_GSM_CALL_AUDIO_HEADSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_GSM_CALL_AUDIO_BLUETOOTH:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_STEREO_TO_SPEAKERS:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_STEREO_TO_HEADPHONES:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_CAPTURE_HANDSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
++ break;
++ case NEO_CAPTURE_HEADSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_CAPTURE_BLUETOOTH:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ default:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++
++ return 0;
++}
++
++static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (neo1973_scenario == ucontrol->value.integer.value[0])
++ return 0;
++
++ neo1973_scenario = ucontrol->value.integer.value[0];
++
++ set_scenario_endpoints(codec, neo1973_scenario);
++
++ return 1;
++}
++
++static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
++
++static void lm4857_write_regs( void )
++{
++ if( i2c_master_send(i2c, lm4857_regs, 4) != 4)
++ printk(KERN_WARNING "lm4857: i2c write failed\n");
++}
++
++static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ int reg=kcontrol->private_value & 0xFF;
++ int shift = (kcontrol->private_value >> 8) & 0x0F;
++ int mask = (kcontrol->private_value >> 16) & 0xFF;
++
++ ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
++
++ return 0;
++}
++
++static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ int reg = kcontrol->private_value & 0xFF;
++ int shift = (kcontrol->private_value >> 8) & 0x0F;
++ int mask = (kcontrol->private_value >> 16) & 0xFF;
++
++ if (((lm4857_regs[reg] >> shift ) & mask) ==
++ ucontrol->value.integer.value[0])
++ return 0;
++
++ lm4857_regs[reg] &= ~ (mask << shift);
++ lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
++
++ lm4857_write_regs();
++ return 1;
++}
++
++static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
++
++ if (value)
++ value -= 5;
++
++ ucontrol->value.integer.value[0] = value;
++ return 0;
++}
++
++static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ u8 value = ucontrol->value.integer.value[0];
++
++ if (value)
++ value += 5;
++
++ if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
++ return 0;
++
++ lm4857_regs[LM4857_CTRL] &= 0xF0;
++ lm4857_regs[LM4857_CTRL] |= value;
++
++ lm4857_write_regs();
++ return 1;
++}
++
++static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
++ SND_SOC_DAPM_LINE("Audio Out", NULL),
++ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
++ SND_SOC_DAPM_LINE("GSM Line In", NULL),
++ SND_SOC_DAPM_MIC("Headset Mic", NULL),
++ SND_SOC_DAPM_MIC("Call Mic", NULL),
++};
++
++
++/* example machine audio_mapnections */
++static const char* audio_map[][3] = {
++
++ /* Connections to the lm4857 amp */
++ {"Audio Out", NULL, "LOUT1"},
++ {"Audio Out", NULL, "ROUT1"},
++
++ /* Connections to the GSM Module */
++ {"GSM Line Out", NULL, "MONO1"},
++ {"GSM Line Out", NULL, "MONO2"},
++ {"RXP", NULL, "GSM Line In"},
++ {"RXN", NULL, "GSM Line In"},
++
++ /* Connections to Headset */
++ {"MIC1", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Headset Mic"},
++
++ /* Call Mic */
++ {"MIC2", NULL, "Mic Bias"},
++ {"MIC2N", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Call Mic"},
++
++ /* Connect the ALC pins */
++ {"ACIN", NULL, "ACOP"},
++
++ {NULL, NULL, NULL},
++};
++
++static const char *lm4857_mode[] = {
++ "Off",
++ "Call Speaker",
++ "Stereo Speakers",
++ "Stereo Speakers + Headphones",
++ "Headphones"
++};
++
++static const struct soc_enum lm4857_mode_enum[] = {
++ SOC_ENUM_SINGLE_EXT(5, lm4857_mode),
++};
++
++static const char *neo_scenarios[] = {
++ "Off",
++ "GSM Handset",
++ "GSM Headset",
++ "GSM Bluetooth",
++ "Speakers",
++ "Headphones",
++ "Capture Handset",
++ "Capture Headset",
++ "Capture Bluetooth"
++};
++
++static const struct soc_enum neo_scenario_enum[] = {
++ SOC_ENUM_SINGLE_EXT(9,neo_scenarios),
++};
++
++static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
++ SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
++ lm4857_get_mode, lm4857_set_mode),
++ SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
++ neo1973_get_scenario, neo1973_set_scenario),
++ SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++};
++
++/*
++ * This is an example machine initialisation for a wm8753 connected to a
++ * neo1973 II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int neo1973_wm8753_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* set up NC codec pins */
++ snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
++ snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
++ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
++ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
++
++
++ /* set endpoints to default mode */
++ set_scenario_endpoints(codec, NEO_AUDIO_OFF);
++
++ /* Add neo1973 specific widgets */
++ for(i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
++ snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
++
++ /* add neo1973 specific controls */
++ for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
++ if ((err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8753_neo1973_controls[i],codec, NULL))) < 0)
++ return err;
++ }
++
++ /* set up neo1973 specific audio path audio_mapnects */
++ for(i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
++ audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++/*
++ * BT Codec DAI
++ */
++static struct snd_soc_cpu_dai bt_dai =
++{ .name = "Bluetooth",
++ .id = 0,
++ .type = SND_SOC_DAI_PCM,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++};
++
++static struct snd_soc_dai_link neo1973_dai[] = {
++{ /* Hifi Playback - for similatious use with voice below */
++ .name = "WM8753",
++ .stream_name = "WM8753 HiFi",
++ .cpu_dai = &s3c24xx_i2s_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
++ .init = neo1973_wm8753_init,
++ .ops = &neo1973_hifi_ops,
++},
++{ /* Voice via BT */
++ .name = "Bluetooth",
++ .stream_name = "Voice",
++ .cpu_dai = &bt_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
++ .ops = &neo1973_voice_ops,
++},
++};
++
++static struct snd_soc_machine neo1973 = {
++ .name = "neo1973",
++ .dai_link = neo1973_dai,
++ .num_links = ARRAY_SIZE(neo1973_dai),
++};
++
++static struct wm8753_setup_data neo1973_wm8753_setup = {
++ .i2c_address = 0x1a,
++};
++
++static struct snd_soc_device neo1973_snd_devdata = {
++ .machine = &neo1973,
++ .platform = &s3c24xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8753,
++ .codec_data = &neo1973_wm8753_setup,
++};
++
++static struct i2c_client client_template;
++
++static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ int ret;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ DBG("Entering %s\n", __FUNCTION__);
++
++ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
++ if (i2c == NULL){
++ return -ENOMEM;
++ }
++ memcpy(i2c, &client_template, sizeof(struct i2c_client));
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ DBG("failed to attach codec at addr %x\n", addr);
++ goto exit_err;
++ }
++
++ lm4857_write_regs();
++
++ return ret;
++
++exit_err:
++ kfree(i2c);
++ return ret;
++}
++
++static int lm4857_i2c_detach(struct i2c_client *client)
++{
++ i2c_detach_client(client);
++ kfree(client);
++ return 0;
++}
++
++static int lm4857_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, lm4857_amp_probe);
++}
++
++#define I2C_DRIVERID_LM4857 0xA5A5 /* liam - need a proper id */
++
++/* corgi i2c codec control layer */
++static struct i2c_driver lm4857_i2c_driver = {
++ .driver = {
++ .name = "LM4857 I2C Amp",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_LM4857,
++ .attach_adapter = lm4857_i2c_attach,
++ .detach_client = lm4857_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "LM4857",
++ .driver = &lm4857_i2c_driver,
++};
++
++static struct platform_device *neo1973_snd_device;
++
++static int __init neo1973_init(void)
++{
++ int ret;
++
++ neo1973_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!neo1973_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
++ neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
++ ret = platform_device_add(neo1973_snd_device);
++
++ if (ret)
++ platform_device_put(neo1973_snd_device);
++
++ ret = i2c_add_driver(&lm4857_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++
++ return ret;
++}
++
++static void __exit neo1973_exit(void)
++{
++ platform_device_unregister(neo1973_snd_device);
++}
++
++module_init(neo1973_init);
++module_exit(neo1973_exit);
++
++/* Module information */
++MODULE_AUTHOR("Graeme Gregory, graeme.gregory at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/sound/soc/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/sound/soc/Kconfig 2007-07-16 15:05:54.390598404 +0200
++++ linux-2.6.22.1/sound/soc/Kconfig 2007-07-16 15:08:07.642191977 +0200
+@@ -25,6 +25,7 @@
+
+ # All the supported Soc's
+ source "sound/soc/at91/Kconfig"
++source "sound/soc/imx/Kconfig"
+ source "sound/soc/pxa/Kconfig"
+ source "sound/soc/s3c24xx/Kconfig"
+
+Index: linux-2.6.22.1/sound/soc/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/sound/soc/Makefile 2007-07-16 15:05:54.402599089 +0200
++++ linux-2.6.22.1/sound/soc/Makefile 2007-07-16 15:08:36.483835564 +0200
+@@ -1,4 +1,4 @@
+ snd-soc-core-objs := soc-core.o soc-dapm.o
+
+ obj-$(CONFIG_SND_SOC) += snd-soc-core.o
+-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/
++obj-$(CONFIG_SND_SOC) += codecs/ at91/ imx/ pxa/ s3c24xx/
+Index: linux-2.6.22.1/sound/soc/codecs/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/sound/soc/codecs/Kconfig 2007-07-16 15:05:54.166585639 +0200
++++ linux-2.6.22.1/sound/soc/codecs/Kconfig 2007-07-16 15:07:35.884382206 +0200
+@@ -2,6 +2,14 @@
+ tristate
+ depends on SND_SOC
+
++config SND_SOC_WM8510
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8711
++ tristate
++ depends on SND_SOC
++
+ config SND_SOC_WM8731
+ tristate
+ depends on SND_SOC
+@@ -14,6 +22,46 @@
+ tristate
+ depends on SND_SOC
+
++config SND_SOC_WM8753
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8772
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8956
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8960
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8971
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8974
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8976
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_WM8980
++ tristate
++ depends on SND_SOC
++
++config SND_SOC_UDA1380
++ tristate
++ depends on SND_SOC
++
+ config SND_SOC_WM9712
+ tristate
+ depends on SND_SOC
++
++config SND_SOC_WM9713
++ tristate
++ depends on SND_SOC
+Index: linux-2.6.22.1/sound/soc/codecs/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/sound/soc/codecs/Makefile 2007-07-16 15:05:54.174586092 +0200
++++ linux-2.6.22.1/sound/soc/codecs/Makefile 2007-07-16 15:10:58.967955277 +0200
+@@ -1,11 +1,33 @@
+ snd-soc-ac97-objs := ac97.o
++snd-soc-wm8711-objs := wm8711.o
++snd-soc-wm8510-objs := wm8510.o
+ snd-soc-wm8731-objs := wm8731.o
+ snd-soc-wm8750-objs := wm8750.o
+ snd-soc-wm8753-objs := wm8753.o
++snd-soc-wm8772-objs := wm8772.o
++snd-soc-wm8956-objs := wm8956.o
++snd-soc-wm8960-objs := wm8960.o
++snd-soc-wm8971-objs := wm8971.o
++snd-soc-wm8974-objs := wm8974.o
++snd-soc-wm8976-objs := wm8976.o
++snd-soc-wm8980-objs := wm8980.o
++snd-soc-uda1380-objs := uda1380.o
+ snd-soc-wm9712-objs := wm9712.o
++snd-soc-wm9713-objs := wm9713.o
+
+ obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
++obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
++obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
+ obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
+ obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
+ obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
++obj-$(CONFIG_SND_SOC_WM8772) += snd-soc-wm8772.o
++obj-$(CONFIG_SND_SOC_WM8956) += snd-soc-wm8956.o
++obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
++obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
++obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
++obj-$(CONFIG_SND_SOC_WM8976) += snd-soc-wm8976.o
++obj-$(CONFIG_SND_SOC_WM8980) += snd-soc-wm8980.o
++obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
+ obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
++obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
Added: developers/nbd/patches-2.6.22/110-asoc-asm_hardware_h.patch
===================================================================
--- developers/nbd/patches-2.6.22/110-asoc-asm_hardware_h.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/110-asoc-asm_hardware_h.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,17 @@
+Don't include <asm/arch/hardware.h>, but <asm/hardware.h>
+
+Signed-off-by: Harald Welte <laforge at openmko.org>
+
+Index: linux-2.6.20/sound/soc/s3c24xx/neo1973_wm8753.c
+===================================================================
+--- linux-2.6.20.orig/sound/soc/s3c24xx/neo1973_wm8753.c 2007-02-15 16:27:53.000000000 +0100
++++ linux-2.6.20/sound/soc/s3c24xx/neo1973_wm8753.c 2007-02-15 16:28:02.000000000 +0100
+@@ -33,7 +33,7 @@
+ #include <asm/arch/regs-iis.h>
+ #include <asm/arch/regs-clock.h>
+ #include <asm/arch/regs-gpio.h>
+-#include <asm/arch/hardware.h>
++#include <asm/hardware.h>
+ #include <asm/arch/audio.h>
+ #include <asm/io.h>
+ #include <asm/arch/spi-gpio.h>
Added: developers/nbd/patches-2.6.22/120-asoc-platform-hw_init-pcm_emulation-fix.patch
===================================================================
--- developers/nbd/patches-2.6.22/120-asoc-platform-hw_init-pcm_emulation-fix.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/120-asoc-platform-hw_init-pcm_emulation-fix.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,45 @@
+Since the PCM emulation can call multiple times to hw_setup(), but we
+can only once allocate/request the DMA channel, we have to handle
+this gracefully.
+
+Signed-off-by: Harald Welte <laforge at openmoko.org>
+
+Index: linux-2.6.20/sound/soc/s3c24xx/s3c24xx-pcm.c
+===================================================================
+--- linux-2.6.20.orig/sound/soc/s3c24xx/s3c24xx-pcm.c 2007-02-15 21:50:25.000000000 +0100
++++ linux-2.6.20/sound/soc/s3c24xx/s3c24xx-pcm.c 2007-02-15 22:25:10.000000000 +0100
+@@ -155,18 +155,22 @@
+ if (!dma)
+ return 0;
+
+- /* prepare DMA */
+- prtd->params = dma;
+-
+- DBG("params %p, client %p, channel %d\n", prtd->params,
+- prtd->params->client, prtd->params->channel);
+-
+- ret = s3c2410_dma_request(prtd->params->channel,
+- prtd->params->client, NULL);
+-
+- if (ret) {
+- DBG(KERN_ERR "failed to get dma channel\n");
+- return ret;
++ /* this may get called several times by oss emulation
++ * with different params -HW */
++ if (prtd->params == NULL) {
++ /* prepare DMA */
++ prtd->params = dma;
++
++ DBG("params %p, client %p, channel %d\n", prtd->params,
++ prtd->params->client, prtd->params->channel);
++
++ ret = s3c2410_dma_request(prtd->params->channel,
++ prtd->params->client, NULL);
++
++ if (ret) {
++ DBG(KERN_ERR "failed to get dma channel\n");
++ return ret;
++ }
+ }
+
+ /* channel needs configuring for mem=>device, increment memory addr,
Added: developers/nbd/patches-2.6.22/130-i2c-permit_invalid_addrs.patch
===================================================================
--- developers/nbd/patches-2.6.22/130-i2c-permit_invalid_addrs.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/130-i2c-permit_invalid_addrs.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,23 @@
+We need this stupid workaround since our amplifier chip uses a 'reserved' I2C
+address
+
+Signed-off-by: Harald Welte <laforge at openmoko.org>
+
+Index: linux-2.6.20/drivers/i2c/i2c-core.c
+===================================================================
+--- linux-2.6.20.orig/drivers/i2c/i2c-core.c 2007-02-15 20:30:14.000000000 +0100
++++ linux-2.6.20/drivers/i2c/i2c-core.c 2007-02-15 20:30:22.000000000 +0100
+@@ -701,11 +701,11 @@
+ int err;
+
+ /* Make sure the address is valid */
+- if (addr < 0x03 || addr > 0x77) {
++ /*if (addr < 0x03 || addr > 0x77) {
+ dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
+ addr);
+ return -EINVAL;
+- }
++ }*/
+
+ /* Skip if already in use */
+ if (i2c_check_addr(adapter, addr))
Added: developers/nbd/patches-2.6.22/140-pm-debug_less_verbose.patch
===================================================================
--- developers/nbd/patches-2.6.22/140-pm-debug_less_verbose.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/140-pm-debug_less_verbose.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,16 @@
+Index: linux-2.6.21-moko/drivers/base/power/main.c
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/base/power/main.c
++++ linux-2.6.21-moko/drivers/base/power/main.c
+@@ -53,9 +53,9 @@
+ {
+ int error;
+
+- pr_debug("PM: Adding info for %s:%s\n",
++ /*pr_debug("PM: Adding info for %s:%s\n",
+ dev->bus ? dev->bus->name : "No Bus",
+- kobject_name(&dev->kobj));
++ kobject_name(&dev->kobj)); */
+ down(&dpm_list_sem);
+ list_add_tail(&dev->power.entry, &dpm_active);
+ device_pm_set_parent(dev, dev->parent);
Added: developers/nbd/patches-2.6.22/150-g_ether-highpower.patch
===================================================================
--- developers/nbd/patches-2.6.22/150-g_ether-highpower.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/150-g_ether-highpower.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,22 @@
+Index: linux-2.6.17.14-fic4.test/drivers/usb/gadget/ether.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/usb/gadget/ether.c 2007-01-21 21:31:48.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/usb/gadget/ether.c 2007-01-21 21:34:31.000000000 +0100
+@@ -462,7 +462,7 @@
+ .bConfigurationValue = DEV_CONFIG_VALUE,
+ .iConfiguration = STRING_CDC,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 50,
++ .bMaxPower = 250,
+ };
+
+ #ifdef CONFIG_USB_ETH_RNDIS
+@@ -476,7 +476,7 @@
+ .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
+ .iConfiguration = STRING_RNDIS,
+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 50,
++ .bMaxPower = 250,
+ };
+ #endif
+
Added: developers/nbd/patches-2.6.22/160-g_ether-vendor_product.patch
===================================================================
--- developers/nbd/patches-2.6.22/160-g_ether-vendor_product.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/160-g_ether-vendor_product.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,34 @@
+Use FIC's own USB Vendor ID rather than NetChip's
+
+Yes, we could solve this by some modprobe.conf parameters, but I'd like to
+rather not rely on this.
+
+Index: linux-2.6.20.1/drivers/usb/gadget/ether.c
+===================================================================
+--- linux-2.6.20.1.orig/drivers/usb/gadget/ether.c 2007-03-12 21:57:28.000000000 +0100
++++ linux-2.6.20.1/drivers/usb/gadget/ether.c 2007-03-12 22:01:28.000000000 +0100
+@@ -149,11 +149,8 @@
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+
+-/* Thanks to NetChip Technologies for donating this product ID.
+- * It's for devices with only CDC Ethernet configurations.
+- */
+-#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+-#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
++#define CDC_VENDOR_NUM 0x1457 /* First International Computer */
++#define CDC_PRODUCT_NUM 0x5117 /* Linux-USB Ethernet Gadget */
+
+ /* For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+@@ -174,8 +171,8 @@
+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
+ * the non-RNDIS configuration.
+ */
+-#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
+-#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
++#define RNDIS_VENDOR_NUM 0x1457 /* NetChip */
++#define RNDIS_PRODUCT_NUM 0x5122 /* Ethernet/RNDIS Gadget */
+
+
+ /* Some systems will want different product identifers published in the
Added: developers/nbd/patches-2.6.22/170-s3c2410_serial-nodebug.patch
===================================================================
--- developers/nbd/patches-2.6.22/170-s3c2410_serial-nodebug.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/170-s3c2410_serial-nodebug.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,28 @@
+Index: linux-2.6.17.7-new/drivers/serial/s3c2410.c
+===================================================================
+--- linux-2.6.17.7-new.orig/drivers/serial/s3c2410.c 2006-08-12 01:43:50.000000000 +0530
++++ linux-2.6.17.7-new/drivers/serial/s3c2410.c 2006-08-12 01:44:30.000000000 +0530
+@@ -710,10 +710,6 @@
+ int calc_deviation;
+
+ for (sptr = res; sptr < resptr; sptr++) {
+- printk(KERN_DEBUG
+- "found clk %p (%s) quot %d, calc %d\n",
+- sptr->clksrc, sptr->clksrc->name,
+- sptr->quot, sptr->calc);
+
+ calc_deviation = baud - sptr->calc;
+ if (calc_deviation < 0)
+@@ -725,12 +721,8 @@
+ }
+ }
+
+- printk(KERN_DEBUG "best %p (deviation %d)\n", best, deviation);
+ }
+
+- printk(KERN_DEBUG "selected clock %p (%s) quot %d, calc %d\n",
+- best->clksrc, best->clksrc->name, best->quot, best->calc);
+-
+ /* store results to pass back */
+
+ *clksrc = best->clksrc;
Added: developers/nbd/patches-2.6.22/180-s3c2410_udc.patch
===================================================================
--- developers/nbd/patches-2.6.22/180-s3c2410_udc.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/180-s3c2410_udc.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2150 @@
+Index: linux-2.6.20/arch/arm/mach-s3c2410/s3c2410.c
+===================================================================
+--- linux-2.6.20.orig/arch/arm/mach-s3c2410/s3c2410.c 2007-02-04 19:44:54.000000000 +0100
++++ linux-2.6.20/arch/arm/mach-s3c2410/s3c2410.c 2007-02-15 14:55:33.000000000 +0100
+@@ -39,6 +39,7 @@
+ /* Initial IO mappings */
+
+ static struct map_desc s3c2410_iodesc[] __initdata = {
++ IODESC_ENT(USBDEV),
+ IODESC_ENT(CLKPWR),
+ IODESC_ENT(LCD),
+ IODESC_ENT(TIMER),
+Index: linux-2.6.20/drivers/usb/gadget/Kconfig
+===================================================================
+--- linux-2.6.20.orig/drivers/usb/gadget/Kconfig 2007-02-04 19:44:54.000000000 +0100
++++ linux-2.6.20/drivers/usb/gadget/Kconfig 2007-02-15 14:54:49.000000000 +0100
+@@ -187,6 +187,25 @@
+
+ Select this only if your OMAP board has a Mini-AB connector.
+
++config USB_GADGET_S3C2410
++ boolean "S3C2410"
++ depends on ARCH_S3C2410
++ help
++ Samsung's S3C2410 is an ARM-4 processor with an integrated
++ full speed USB 1.1 device controller.
++ It has 4 configurable endpoints, as well as endpoint
++ zero (for control transfers).
++
++config USB_S3C2410
++ tristate
++ depends on USB_GADGET_S3C2410
++ default USB_GADGET
++ select USB_GADGET_SELECTED
++
++config USB_S3C2410_DEBUG
++ boolean "S3C2410 udc debug messages"
++ depends on USB_GADGET_S3C2410
++
+ config USB_GADGET_AT91
+ boolean "AT91 USB Device Port"
+ depends on ARCH_AT91
+Index: linux-2.6.20/drivers/usb/gadget/Makefile
+===================================================================
+--- linux-2.6.20.orig/drivers/usb/gadget/Makefile 2007-02-04 19:44:54.000000000 +0100
++++ linux-2.6.20/drivers/usb/gadget/Makefile 2007-02-15 14:54:49.000000000 +0100
+@@ -7,6 +7,7 @@
+ obj-$(CONFIG_USB_GOKU) += goku_udc.o
+ obj-$(CONFIG_USB_OMAP) += omap_udc.o
+ obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
++obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+ obj-$(CONFIG_USB_AT91) += at91_udc.o
+
+ #
+Index: linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c 2007-02-15 14:54:49.000000000 +0100
+@@ -0,0 +1,1897 @@
++/*
++ * linux/drivers/usb/gadget/s3c2410_udc.c
++ * Samsung on-chip full speed USB device controllers
++ *
++ * Copyright (C) 2004-2006 Herbert Pötzl - Arnaud Patard
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/version.h>
++#include <linux/clk.h>
++
++#include <linux/usb.h>
++#include <linux/usb_gadget.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++#include <asm/arch/irqs.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-udc.h>
++#include <asm/arch/udc.h>
++
++#include <asm/mach-types.h>
++
++#include "s3c2410_udc.h"
++
++#define ENABLE_SYSFS
++
++#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"
++#define DRIVER_VERSION "30 Apr 2006"
++#define DRIVER_AUTHOR "Herbert Pötzl <herbert at 13thfloor.at>, Arnaud Patard <arnaud.patard at rtp-net.org>"
++
++static const char gadget_name [] = "s3c2410_udc";
++static const char driver_desc [] = DRIVER_DESC;
++
++static struct s3c2410_udc *the_controller;
++static struct clk *udc_clock;
++static struct clk *usb_bus_clock;
++static void __iomem *base_addr;
++static u64 rsrc_start;
++static u64 rsrc_len;
++
++static inline u32 udc_readl(u32 reg)
++{
++ return readl(base_addr+reg);
++}
++static inline void udc_writel(u32 value, u32 reg)
++{
++ writel(value,base_addr+reg);
++}
++
++static struct s3c2410_udc_mach_info *udc_info;
++
++/*************************** DEBUG FUNCTION ***************************/
++#define DEBUG_NORMAL 1
++#define DEBUG_VERBOSE 2
++
++#ifdef CONFIG_USB_S3C2410_DEBUG
++#define USB_S3C2410_DEBUG_LEVEL 1
++
++static uint32_t s3c2410_ticks=0;
++
++static int dprintk(int level, const char *fmt, ...)
++{
++ static char printk_buf[1024];
++ static long prevticks;
++ static int invocation;
++ va_list args;
++ int len;
++
++ if (level > USB_S3C2410_DEBUG_LEVEL)
++ return 0;
++
++ if (s3c2410_ticks != prevticks) {
++ prevticks = s3c2410_ticks;
++ invocation = 0;
++ }
++
++ len = scnprintf(printk_buf, \
++ sizeof(printk_buf), "%1lu.%02d USB: ", \
++ prevticks, invocation++);
++
++ va_start(args, fmt);
++ len = vscnprintf(printk_buf+len, \
++ sizeof(printk_buf)-len, fmt, args);
++ va_end(args);
++
++ return printk("%s", printk_buf);
++}
++#else
++static int dprintk(int level, const char *fmt, ...) { return 0; }
++#endif
++#ifdef ENABLE_SYSFS
++static ssize_t s3c2410udc_regs_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
++ u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
++ u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
++ u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
++
++ addr_reg = udc_readl(S3C2410_UDC_FUNC_ADDR_REG);
++ pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
++ ep_int_reg = udc_readl(S3C2410_UDC_EP_INT_REG);
++ usb_int_reg = udc_readl(S3C2410_UDC_USB_INT_REG);
++ ep_int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++ usb_int_en_reg = udc_readl(S3C2410_UDC_USB_INT_EN_REG);
++ udc_writel(0, S3C2410_UDC_INDEX_REG);
++ ep0_csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ udc_writel(1, S3C2410_UDC_INDEX_REG);
++ ep1_i_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ ep1_i_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++ ep1_o_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ ep1_o_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++ udc_writel(2, S3C2410_UDC_INDEX_REG);
++ ep2_i_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ ep2_i_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++ ep2_o_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ ep2_o_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
++
++
++ return snprintf(buf, PAGE_SIZE, \
++ "FUNC_ADDR_REG : 0x%04X\n" \
++ "PWR_REG : 0x%04X\n" \
++ "EP_INT_REG : 0x%04X\n" \
++ "USB_INT_REG : 0x%04X\n" \
++ "EP_INT_EN_REG : 0x%04X\n" \
++ "USB_INT_EN_REG : 0x%04X\n" \
++ "EP0_CSR : 0x%04X\n" \
++ "EP1_I_CSR1 : 0x%04X\n" \
++ "EP1_I_CSR2 : 0x%04X\n" \
++ "EP1_O_CSR1 : 0x%04X\n" \
++ "EP1_O_CSR2 : 0x%04X\n" \
++ "EP2_I_CSR1 : 0x%04X\n" \
++ "EP2_I_CSR2 : 0x%04X\n" \
++ "EP2_O_CSR1 : 0x%04X\n" \
++ "EP2_O_CSR2 : 0x%04X\n", \
++ addr_reg,pwr_reg,ep_int_reg,usb_int_reg, \
++ ep_int_en_reg, usb_int_en_reg, ep0_csr, \
++ ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, \
++ ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2 \
++ );
++}
++
++static DEVICE_ATTR(regs, 0444,
++ s3c2410udc_regs_show,
++ NULL);
++#endif
++/*------------------------- I/O ----------------------------------*/
++static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status);
++static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep, int status)
++{
++ /* Sanity check */
++ if (&ep->queue != NULL)
++ while (!list_empty (&ep->queue)) {
++ struct s3c2410_request *req;
++ req = list_entry (ep->queue.next, struct s3c2410_request, queue);
++ done(ep,req,status);
++ }
++}
++
++/*
++ * done
++ */
++static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status)
++{
++ unsigned halted = ep->halted;
++
++ list_del_init(&req->queue);
++
++ if (likely (req->req.status == -EINPROGRESS))
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ ep->halted = 1;
++ req->req.complete(&ep->ep, &req->req);
++ ep->halted = halted;
++}
++
++static inline void clear_ep_state (struct s3c2410_udc *dev)
++{
++ unsigned i;
++
++ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
++ * fifos, and pending transactions mustn't be continued in any case.
++ */
++ for (i = 1; i < S3C2410_ENDPOINTS; i++)
++ nuke(dev, &dev->ep[i], -ECONNABORTED);
++}
++
++static inline int fifo_count_out(void)
++{
++ int tmp;
++
++ tmp = udc_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
++ tmp |= udc_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG);
++
++ return tmp & 0xffff;
++}
++
++/*
++ * write_packet
++ */
++static inline int
++write_packet(int fifo, struct s3c2410_request *req, unsigned max)
++{
++ unsigned len;
++ u8 *buf;
++
++ buf = req->req.buf + req->req.actual;
++ prefetch(buf);
++
++ len = min(req->req.length - req->req.actual, max);
++ dprintk(DEBUG_VERBOSE, "write_packet %d %d %d ",req->req.actual,req->req.length,len);
++ req->req.actual += len;
++ dprintk(DEBUG_VERBOSE, "%d\n",req->req.actual);
++
++ writesb(base_addr+fifo, buf, len);
++ return len;
++}
++
++static void udc_reinit(struct s3c2410_udc *dev);
++
++/*
++ * write_fifo
++ *
++ * return: 0 = still running, 1 = completed, negative = errno
++ */
++static int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
++{
++ unsigned count;
++ int is_last;
++ u32 idx;
++ int fifo_reg;
++ u32 ep_csr;
++
++
++ switch(ep->bEndpointAddress&0x7F)
++ {
++ default:
++ case 0: idx = 0;
++ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++ break;
++ case 1:
++ idx = 1;
++ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++ break;
++ case 2:
++ idx = 2;
++ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++ break;
++
++ case 3:
++ idx = 3;
++ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++ break;
++
++ case 4:
++ idx = 4;
++ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++ break;
++ }
++
++ count = write_packet(fifo_reg, req, ep->ep.maxpacket);
++
++ /* last packet is often short (sometimes a zlp) */
++ if (count != ep->ep.maxpacket)
++ is_last = 1;
++ else if (req->req.length != req->req.actual || req->req.zero)
++ is_last = 0;
++ else
++ is_last = 2;
++
++ /* Only ep0 debug messages are interesting */
++ if (!idx)
++ dprintk(DEBUG_NORMAL, "Written ep%d %d.%d of %d b [last %d,z %d]\n",idx,count,req->req.actual,req->req.length,is_last,req->req.zero);
++
++ if (is_last)
++ {
++ /* The order is important. It prevents to send 2 packet at the same time
++ **/
++ if (!idx)
++ {
++ /* If we got a reset signal, no need to say 'data sent' */
++ if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
++ set_ep0_de_in(base_addr);
++ ep->dev->ep0state=EP0_IDLE;
++ }
++ else
++ {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
++ }
++ done(ep, req, 0);
++ is_last=1;
++ }
++ else
++ {
++ if (!idx)
++ {
++ /* If we got a reset signal, no need to say 'data sent' */
++ if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
++ set_ep0_ipr(base_addr);
++ }
++ else
++ {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
++ }
++ }
++
++
++ return is_last;
++}
++
++static inline int
++read_packet(int fifo, u8 *buf, struct s3c2410_request *req, unsigned avail)
++{
++ unsigned len;
++
++ len = min(req->req.length - req->req.actual, avail);
++ req->req.actual += len;
++
++ readsb(fifo + base_addr, buf, len);
++ return len;
++}
++
++/*
++ * return: 0 = still running, 1 = queue empty, negative = errno
++ */
++static int read_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
++{
++ u8 *buf;
++ u32 ep_csr;
++ unsigned bufferspace;
++ int is_last=1;
++ unsigned avail;
++ int fifo_count = 0;
++ u32 idx;
++ int fifo_reg;
++
++
++ switch(ep->bEndpointAddress&0x7F)
++ {
++ default:
++ case 0: idx = 0;
++ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++ break;
++ case 1:
++ idx = 1;
++ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++ break;
++ case 2:
++ idx = 2;
++ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++ break;
++
++ case 3:
++ idx = 3;
++ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++ break;
++
++ case 4:
++ idx = 4;
++ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++ break;
++
++ }
++
++ if (!req->req.length) {
++ return 1;
++ }
++
++ buf = req->req.buf + req->req.actual;
++ bufferspace = req->req.length - req->req.actual;
++ if (!bufferspace)
++ {
++ dprintk(DEBUG_NORMAL, "read_fifo: Buffer full !!\n");
++ return -1;
++ }
++
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++
++ fifo_count = fifo_count_out();
++ dprintk(DEBUG_NORMAL, "fifo_read fifo count : %d\n",fifo_count);
++
++ if (fifo_count > ep->ep.maxpacket)
++ avail = ep->ep.maxpacket;
++ else
++ avail = fifo_count;
++
++ fifo_count=read_packet(fifo_reg,buf,req,avail);
++
++ /* checking this with ep0 is not accurate as we already
++ * read a control request
++ **/
++ if (idx && fifo_count < ep->ep.maxpacket) {
++ is_last = 1;
++ /* overflowed this request? flush extra data */
++ if (fifo_count != avail) {
++ req->req.status = -EOVERFLOW;
++ }
++ } else {
++ if (req->req.length <= req->req.actual)
++ is_last = 1;
++ else
++ is_last = 0;
++ }
++
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ fifo_count = fifo_count_out();
++
++ /* Only ep0 debug messages are interesting */
++ if (!idx)
++ dprintk(DEBUG_VERBOSE, "fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);
++
++
++ if (is_last) {
++ if (!idx)
++ {
++ set_ep0_de_out(base_addr);
++ ep->dev->ep0state=EP0_IDLE;
++ }
++ else
++ {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
++ }
++ done(ep, req, 0);
++ if (!list_empty(&ep->queue))
++ {
++ is_last=0;
++ req = container_of(ep->queue.next,
++ struct s3c2410_request, queue);
++ }
++ else
++ is_last=1;
++
++ }
++ else
++ {
++ if (!idx)
++ {
++ clear_ep0_opr(base_addr);
++ }
++ else
++ {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
++ }
++ }
++
++
++ return is_last;
++}
++
++
++static int
++read_fifo_crq(struct usb_ctrlrequest *crq)
++{
++ int bytes_read = 0;
++ int fifo_count = 0;
++ int i;
++
++
++ unsigned char *pOut = (unsigned char*)crq;
++
++ udc_writel(0, S3C2410_UDC_INDEX_REG);
++
++ fifo_count = fifo_count_out();
++
++ dprintk(DEBUG_NORMAL, "read_fifo_crq(): fifo_count=%d\n", fifo_count );
++
++ fifo_count = sizeof(struct usb_ctrlrequest);
++ while( fifo_count-- ) {
++ i = 0;
++
++ do {
++ *pOut = (unsigned char)udc_readl(S3C2410_UDC_EP0_FIFO_REG);
++ i++;
++ } while((fifo_count_out() != fifo_count) && (i < 10));
++
++ if ( i == 10 ) {
++ dprintk(DEBUG_NORMAL, "read_fifo(): read failure\n");
++ }
++
++ pOut++;
++ bytes_read++;
++ }
++
++ dprintk(DEBUG_VERBOSE, "read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n",
++ bytes_read, crq->bRequest, crq->bRequestType,
++ crq->wValue, crq->wIndex, crq->wLength);
++
++ return bytes_read;
++}
++static int s3c2410_get_status(struct s3c2410_udc *dev, struct usb_ctrlrequest *crq)
++{
++ u16 status = 0;
++ u8 ep_num = crq->wIndex & 0x7F;
++ u8 is_in = crq->wIndex & USB_DIR_IN;
++
++ switch(crq->bRequestType & USB_RECIP_MASK) {
++ case USB_RECIP_INTERFACE:
++ break;
++ case USB_RECIP_DEVICE:
++ status = dev->devstatus;
++ break;
++ case USB_RECIP_ENDPOINT:
++ if (ep_num>4 || crq->wLength > 2)
++ return 1;
++ if (!ep_num) {
++ udc_writel(0, S3C2410_UDC_INDEX_REG);
++ status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ status = ( (status & S3C2410_UDC_EP0_CSR_SENDSTL) == S3C2410_UDC_EP0_CSR_SENDSTL);
++ }
++ else {
++ udc_writel(ep_num, S3C2410_UDC_INDEX_REG);
++ if (is_in) {
++ status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ status = ( (status & S3C2410_UDC_ICSR1_SENTSTL) == S3C2410_UDC_ICSR1_SENTSTL);
++ }
++ else {
++ status = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++ status = ( (status & S3C2410_UDC_OCSR1_SENTSTL) == S3C2410_UDC_OCSR1_SENTSTL);
++ }
++ }
++
++ break;
++ default:
++ return 1;
++ }
++
++ /* Seems to be needed to get it working. ouch :( */
++ udelay(0x20);
++ udc_writel(status&0xFF,S3C2410_UDC_EP0_FIFO_REG);
++ udc_writel(status>>8,S3C2410_UDC_EP0_FIFO_REG);
++ set_ep0_de_in(base_addr);
++
++ return 0;
++}
++/*------------------------- usb state machine -------------------------------*/
++static void handle_ep0(struct s3c2410_udc *dev)
++{
++ u32 ep0csr;
++ struct s3c2410_ep *ep = &dev->ep [0];
++ struct s3c2410_request *req;
++ struct usb_ctrlrequest crq;
++
++ if (list_empty(&ep->queue))
++ req = NULL;
++ else
++ req = list_entry(ep->queue.next, struct s3c2410_request, queue);
++
++
++ udc_writel(0, S3C2410_UDC_INDEX_REG);
++ ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ dprintk(DEBUG_NORMAL,"ep0csr %x ep0state %s\n",ep0csr,ep0states[dev->ep0state]);
++
++ /* clear stall status */
++ if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
++ /* FIXME */
++ nuke(dev, ep, -EPIPE);
++ dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
++ clear_ep0_sst(base_addr);
++ dev->ep0state = EP0_IDLE;
++ return;
++ }
++
++ /* clear setup end */
++ if (ep0csr & S3C2410_UDC_EP0_CSR_SE
++ /* && dev->ep0state != EP0_IDLE */) {
++ dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
++ nuke(dev, ep, 0);
++ clear_ep0_se(base_addr);
++ dev->ep0state = EP0_IDLE;
++ }
++
++
++ switch (dev->ep0state) {
++ case EP0_IDLE:
++ /* start control request? */
++ if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {
++ int len, ret, tmp;
++
++ nuke (dev, ep, -EPROTO);
++
++ len = read_fifo_crq(&crq);
++ if (len != sizeof(crq)) {
++ dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
++ " wanted %d bytes got %d. Stalling out...\n",
++ sizeof(crq), len);
++ set_ep0_ss(base_addr);
++ return;
++ }
++
++ dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", crq.bRequest,crq.bRequestType, crq.wLength);
++
++
++ /* cope with automagic for some standard requests. */
++ dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
++ == USB_TYPE_STANDARD;
++ dev->req_config = 0;
++ dev->req_pending = 1;
++ switch (crq.bRequest) {
++ case USB_REQ_SET_CONFIGURATION:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
++ if (crq.bRequestType == USB_RECIP_DEVICE) {
++config_change:
++ dev->req_config = 1;
++/* clear_ep_state(dev);*/
++ set_ep0_de_out(base_addr);
++ }
++ break;
++ case USB_REQ_SET_INTERFACE:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
++ if (crq.bRequestType == USB_RECIP_INTERFACE) {
++ goto config_change;
++ }
++ break;
++
++ case USB_REQ_SET_ADDRESS:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
++ if (crq.bRequestType == USB_RECIP_DEVICE) {
++ tmp = crq.wValue & 0x7F;
++ dev->address = tmp;
++ udc_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
++ set_ep0_de_out(base_addr);
++ return;
++ }
++ break;
++
++ case USB_REQ_GET_STATUS:
++ dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
++ clear_ep0_opr(base_addr);
++ if (!s3c2410_get_status(dev, &crq)) {
++ return;
++ }
++ break;
++
++ default:
++ clear_ep0_opr(base_addr);
++ break;
++ }
++
++ if (crq.bRequestType & USB_DIR_IN)
++ dev->ep0state = EP0_IN_DATA_PHASE;
++ else
++ dev->ep0state = EP0_OUT_DATA_PHASE;
++ ret = dev->driver->setup(&dev->gadget, &crq);
++ if (ret < 0) {
++ if (dev->req_config) {
++ dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
++ crq.bRequest, ret);
++ return;
++ }
++ if (ret == -EOPNOTSUPP)
++ dprintk(DEBUG_NORMAL, "Operation not supported\n");
++ else
++ dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret);
++
++ set_ep0_ss(base_addr);
++ set_ep0_de_out(base_addr);
++ dev->ep0state = EP0_IDLE;
++ /* deferred i/o == no response yet */
++ } else if (dev->req_pending) {
++ dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
++ dev->req_pending=0;
++ }
++ dprintk(DEBUG_VERBOSE, "ep0state %s\n",ep0states[dev->ep0state]);
++ }
++ break;
++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
++ dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
++ if (!(ep0csr & 2) && req)
++ {
++ write_fifo(ep, req);
++ }
++ break;
++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
++ dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
++ if ((ep0csr & 1) && req ) {
++ read_fifo(ep,req);
++ }
++ break;
++ case EP0_END_XFER:
++ dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
++ dev->ep0state=EP0_IDLE;
++ break;
++ case EP0_STALL:
++ dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
++ dev->ep0state=EP0_IDLE;
++ break;
++ }
++}
++/*
++ * handle_ep - Manage I/O endpoints
++ */
++static void handle_ep(struct s3c2410_ep *ep)
++{
++ struct s3c2410_request *req;
++ int is_in = ep->bEndpointAddress & USB_DIR_IN;
++ u32 ep_csr1;
++ u32 idx;
++
++ if (likely (!list_empty(&ep->queue)))
++ req = list_entry(ep->queue.next,
++ struct s3c2410_request, queue);
++ else
++ req = NULL;
++
++ idx = (u32)(ep->bEndpointAddress&0x7F);
++
++ if (is_in) {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",idx,ep_csr1,req ? 1 : 0);
++
++ if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
++ {
++ dprintk(DEBUG_VERBOSE, "st\n");
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
++ return;
++ }
++
++ if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
++ {
++ write_fifo(ep,req);
++ }
++ }
++ else {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr1 = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
++ dprintk(DEBUG_VERBOSE, "ep%01d read csr:%02x\n",idx,ep_csr1);
++
++ if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
++ {
++ udc_writel(idx, S3C2410_UDC_INDEX_REG);
++ udc_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
++ return;
++ }
++ if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
++ {
++ read_fifo(ep,req);
++ }
++ }
++}
++
++#include <asm/arch/regs-irq.h>
++/*
++ * s3c2410_udc_irq - interrupt handler
++ */
++static irqreturn_t
++s3c2410_udc_irq(int irq, void *_dev)
++{
++ struct s3c2410_udc *dev = _dev;
++ int usb_status;
++ int usbd_status;
++ int pwr_reg;
++ int ep0csr;
++ int i;
++ u32 idx;
++ unsigned long flags;
++
++ spin_lock_irqsave(&dev->lock,flags);
++
++ /* Driver connected ? */
++ if (!dev->driver) {
++ /* Clear interrupts */
++ udc_writel( \
++ udc_readl(S3C2410_UDC_USB_INT_REG), \
++ S3C2410_UDC_USB_INT_REG \
++ );
++ udc_writel( \
++ udc_readl(S3C2410_UDC_EP_INT_REG), \
++ S3C2410_UDC_EP_INT_REG \
++ );
++ }
++
++ /* Save index */
++ idx = udc_readl(S3C2410_UDC_INDEX_REG);
++
++ /* Read status registers */
++ usb_status = udc_readl(S3C2410_UDC_USB_INT_REG);
++ usbd_status = udc_readl(S3C2410_UDC_EP_INT_REG);
++ pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
++
++ S3C2410_UDC_SETIX(base_addr,EP0);
++ ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++
++ // dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);
++
++ /*
++ * Now, handle interrupts. There's two types :
++ * - Reset, Resume, Suspend coming -> usb_int_reg
++ * - EP -> ep_int_reg
++ */
++
++ /* RESET */
++ if (usb_status & S3C2410_UDC_USBINT_RESET )
++ {
++ /* two kind of reset :
++ * - reset start -> pwr reg = 8
++ * - reset end -> pwr reg = 0
++ **/
++ dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ udc_writel(0x00, S3C2410_UDC_INDEX_REG);
++ udc_writel((dev->ep[0].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
++ dev->address = 0;
++
++ dev->ep0state = EP0_IDLE;
++ dev->gadget.speed = USB_SPEED_FULL;
++
++ /* clear interrupt */
++ udc_writel(S3C2410_UDC_USBINT_RESET,
++ S3C2410_UDC_USB_INT_REG);
++
++ udc_writel(idx,S3C2410_UDC_INDEX_REG);
++ spin_unlock_irqrestore(&dev->lock,flags);
++ return IRQ_HANDLED;
++ }
++
++ /* RESUME */
++ if (usb_status & S3C2410_UDC_USBINT_RESUME)
++ {
++ dprintk(DEBUG_NORMAL, "USB resume\n");
++
++ /* clear interrupt */
++ udc_writel(S3C2410_UDC_USBINT_RESUME,
++ S3C2410_UDC_USB_INT_REG);
++ if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->resume)
++ dev->driver->resume(&dev->gadget);
++ }
++
++ /* SUSPEND */
++ if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
++ {
++ dprintk(DEBUG_NORMAL, "USB suspend\n");
++
++ /* clear interrupt */
++ udc_writel(S3C2410_UDC_USBINT_SUSPEND,
++ S3C2410_UDC_USB_INT_REG);
++
++ if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->suspend)
++ dev->driver->suspend(&dev->gadget);
++
++ dev->ep0state = EP0_IDLE;
++ }
++
++ /* EP */
++ /* control traffic */
++ /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
++ * generate an interrupt
++ */
++ if (usbd_status & S3C2410_UDC_INT_EP0)
++ {
++ dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
++ /* Clear the interrupt bit by setting it to 1 */
++ udc_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
++ handle_ep0(dev);
++ }
++ /* endpoint data transfers */
++ for (i = 1; i < S3C2410_ENDPOINTS; i++) {
++ u32 tmp = 1 << i;
++ if (usbd_status & tmp) {
++ dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
++
++ /* Clear the interrupt bit by setting it to 1 */
++ udc_writel(tmp, S3C2410_UDC_EP_INT_REG);
++ handle_ep(&dev->ep[i]);
++ }
++ }
++
++
++ dprintk(DEBUG_VERBOSE,"irq: %d done.\n", irq);
++
++ /* Restore old index */
++ udc_writel(idx,S3C2410_UDC_INDEX_REG);
++
++ spin_unlock_irqrestore(&dev->lock,flags);
++
++ return IRQ_HANDLED;
++}
++/*------------------------- s3c2410_ep_ops ----------------------------------*/
++
++/*
++ * s3c2410_ep_enable
++ */
++static int
++s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
++{
++ struct s3c2410_udc *dev;
++ struct s3c2410_ep *ep;
++ u32 max, tmp;
++ unsigned long flags;
++ u32 csr1,csr2;
++ u32 int_en_reg;
++
++
++ ep = container_of (_ep, struct s3c2410_ep, ep);
++ if (!_ep || !desc || ep->desc || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT)
++ return -EINVAL;
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
++
++ local_irq_save (flags);
++ _ep->maxpacket = max & 0x7ff;
++ ep->desc = desc;
++ ep->halted = 0;
++ ep->bEndpointAddress = desc->bEndpointAddress;
++
++ /* set max packet */
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(max>>3,S3C2410_UDC_MAXP_REG);
++
++
++ /* set type, direction, address; reset fifo counters */
++ if (desc->bEndpointAddress & USB_DIR_IN)
++ {
++ csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
++ csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
++
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
++ }
++ else
++ {
++ /* don't flush he in fifo or there will be an interrupt for that
++ * endpoint */
++ csr1 = S3C2410_UDC_ICSR1_CLRDT;
++ csr2 = S3C2410_UDC_ICSR2_DMAIEN;
++
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
++
++ csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
++ csr2 = S3C2410_UDC_OCSR2_DMAIEN;
++
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
++ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
++ }
++
++
++ /* enable irqs */
++ int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++ udc_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
++
++
++ /* print some debug message */
++ tmp = desc->bEndpointAddress;
++ dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
++ _ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
++
++ local_irq_restore (flags);
++
++ return 0;
++}
++
++/*
++ * s3c2410_ep_disable
++ */
++static int s3c2410_ep_disable (struct usb_ep *_ep)
++{
++ struct s3c2410_ep *ep = container_of(_ep, struct s3c2410_ep, ep);
++ unsigned long flags;
++ u32 int_en_reg;
++
++
++ if (!_ep || !ep->desc) {
++ dprintk(DEBUG_NORMAL, "%s not enabled\n",
++ _ep ? ep->ep.name : NULL);
++ return -EINVAL;
++ }
++
++ local_irq_save(flags);
++
++ printk(KERN_ERR "ep_disable: %s\n",_ep->name);
++
++ ep->desc = NULL;
++ ep->halted = 1;
++
++ nuke (ep->dev, ep, -ESHUTDOWN);
++
++ /* disable irqs */
++ int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
++ udc_writel(int_en_reg & ~(1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
++
++ local_irq_restore(flags);
++
++ dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
++
++ return 0;
++}
++
++/*
++ * s3c2410_alloc_request
++ */
++static struct usb_request *
++s3c2410_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
++{
++ struct s3c2410_ep *ep;
++ struct s3c2410_request *req;
++
++ dprintk(DEBUG_VERBOSE,"s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
++
++ ep = container_of (_ep, struct s3c2410_ep, ep);
++ if (!_ep)
++ return NULL;
++
++ req = kzalloc (sizeof *req, mem_flags);
++ if (!req)
++ return NULL;
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++/*
++ * s3c2410_free_request
++ */
++static void
++s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct s3c2410_ep *ep;
++ struct s3c2410_request *req;
++
++ dprintk(DEBUG_VERBOSE, "s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
++
++ ep = container_of (_ep, struct s3c2410_ep, ep);
++ if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
++ return;
++
++ req = container_of (_req, struct s3c2410_request, req);
++ WARN_ON (!list_empty (&req->queue));
++ kfree (req);
++}
++
++/*
++ * s3c2410_alloc_buffer
++ */
++static void *
++s3c2410_alloc_buffer (
++ struct usb_ep *_ep,
++ unsigned bytes,
++ dma_addr_t *dma,
++ gfp_t mem_flags)
++{
++ char *retval;
++
++ dprintk(DEBUG_VERBOSE,"s3c2410_alloc_buffer()\n");
++
++ if (!the_controller->driver)
++ return NULL;
++ retval = kmalloc (bytes, mem_flags);
++ *dma = (dma_addr_t) retval;
++ return retval;
++}
++
++/*
++ * s3c2410_free_buffer
++ */
++static void
++s3c2410_free_buffer (
++ struct usb_ep *_ep,
++ void *buf,
++ dma_addr_t dma,
++ unsigned bytes)
++{
++ dprintk(DEBUG_VERBOSE, "s3c2410_free_buffer()\n");
++
++ if (bytes)
++ kfree (buf);
++}
++
++/*
++ * s3c2410_queue
++ */
++static int
++s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct s3c2410_request *req;
++ struct s3c2410_ep *ep;
++ struct s3c2410_udc *dev;
++ u32 ep_csr=0;
++ int fifo_count=0;
++ unsigned long flags;
++
++
++ ep = container_of(_ep, struct s3c2410_ep, ep);
++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
++ dprintk(DEBUG_NORMAL, "s3c2410_queue: inval 2\n");
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (unlikely (!dev->driver
++ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
++ return -ESHUTDOWN;
++ }
++
++ local_irq_save (flags);
++
++ req = container_of(_req, struct s3c2410_request, req);
++ if (unlikely (!_req || !_req->complete || !_req->buf
++ || !list_empty(&req->queue))) {
++ if (!_req)
++ dprintk(DEBUG_NORMAL, "s3c2410_queue: 1 X X X\n");
++ else
++ {
++ dprintk(DEBUG_NORMAL, "s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
++ }
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++ dprintk(DEBUG_VERBOSE,"s3c2410_queue: ep%x len %d\n",ep->bEndpointAddress,_req->length);
++
++ if (ep->bEndpointAddress) {
++ udc_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
++ fifo_count=fifo_count_out();
++ }
++ else {
++ udc_writel(0,S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
++ }
++ /* kickstart this i/o queue? */
++ if (list_empty(&ep->queue) && !ep->halted) {
++ if (ep->bEndpointAddress == 0 /* ep0 */) {
++ switch (dev->ep0state) {
++ case EP0_IN_DATA_PHASE:
++ if (write_fifo(ep, req)) {
++ dev->ep0state = EP0_IDLE;
++ req = NULL;
++ }
++ break;
++
++ case EP0_OUT_DATA_PHASE:
++ if ( (!_req->length) || ((ep_csr & 1) && read_fifo(ep,req))) {
++ dev->ep0state = EP0_IDLE;
++ req = NULL;
++ }
++ break;
++
++ default:
++ local_irq_restore(flags);
++ return -EL2HLT;
++ }
++ }
++ else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
++ && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && write_fifo(ep, req)) {
++ req = NULL;
++ } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && read_fifo(ep, req)) {
++ req = NULL;
++ }
++
++ }
++
++ /* pio or dma irq handler advances the queue. */
++ if (likely (req != 0))
++ list_add_tail(&req->queue, &ep->queue);
++
++ local_irq_restore(flags);
++
++ dprintk(DEBUG_VERBOSE, "s3c2410_queue normal end\n");
++ return 0;
++}
++
++/*
++ * s3c2410_dequeue
++ */
++static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct s3c2410_ep *ep;
++ struct s3c2410_udc *udc;
++ int retval = -EINVAL;
++ unsigned long flags;
++ struct s3c2410_request *req = NULL;
++
++ dprintk(DEBUG_VERBOSE,"s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
++
++ if (!the_controller->driver)
++ return -ESHUTDOWN;
++
++ if (!_ep || !_req)
++ return retval;
++ ep = container_of (_ep, struct s3c2410_ep, ep);
++ udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
++
++ local_irq_save (flags);
++
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req) {
++ list_del_init (&req->queue);
++ _req->status = -ECONNRESET;
++ retval = 0;
++ break;
++ }
++ }
++
++ if (retval == 0) {
++ dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n",
++ req, _ep->name, _req->length, _req->buf);
++
++ done(ep, req, -ECONNRESET);
++ }
++ local_irq_restore (flags);
++
++ return retval;
++}
++
++
++/*
++ * s3c2410_set_halt
++ */
++static int
++s3c2410_set_halt (struct usb_ep *_ep, int value)
++{
++ return 0;
++}
++
++
++static const struct usb_ep_ops s3c2410_ep_ops = {
++ .enable = s3c2410_ep_enable,
++ .disable = s3c2410_ep_disable,
++
++ .alloc_request = s3c2410_alloc_request,
++ .free_request = s3c2410_free_request,
++
++ .alloc_buffer = s3c2410_alloc_buffer,
++ .free_buffer = s3c2410_free_buffer,
++
++ .queue = s3c2410_queue,
++ .dequeue = s3c2410_dequeue,
++
++ .set_halt = s3c2410_set_halt,
++};
++
++/*------------------------- usb_gadget_ops ----------------------------------*/
++
++/*
++ * s3c2410_g_get_frame
++ */
++static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
++{
++ int tmp;
++
++ dprintk(DEBUG_VERBOSE,"s3c2410_g_get_frame()\n");
++
++ tmp = udc_readl(S3C2410_UDC_FRAME_NUM2_REG) << 8;
++ tmp |= udc_readl(S3C2410_UDC_FRAME_NUM1_REG);
++
++ return tmp & 0xffff;
++}
++
++/*
++ * s3c2410_wakeup
++ */
++static int s3c2410_wakeup (struct usb_gadget *_gadget)
++{
++
++ dprintk(DEBUG_NORMAL,"s3c2410_wakeup()\n");
++
++ return 0;
++}
++
++/*
++ * s3c2410_set_selfpowered
++ */
++static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
++{
++ struct s3c2410_udc *udc;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_set_selfpowered()\n");
++
++ udc = container_of (_gadget, struct s3c2410_udc, gadget);
++
++ if (value)
++ udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
++ else
++ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
++
++ return 0;
++}
++
++static void udc_disable(struct s3c2410_udc *dev);
++static void udc_enable(struct s3c2410_udc *dev);
++
++static int pull_up (struct s3c2410_udc *udc, int is_on)
++{
++ dprintk(DEBUG_NORMAL, "pull_up()\n");
++
++ if (udc_info && udc_info->udc_command) {
++ if (is_on)
++ udc_enable(udc);
++ else {
++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
++ if (udc->driver && udc->driver->disconnect)
++ udc->driver->disconnect(&udc->gadget);
++
++ }
++ udc_disable(udc);
++ }
++ }
++ else
++ return -EOPNOTSUPP;
++
++ return 0;
++}
++
++static int s3c2410_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
++{
++ struct s3c2410_udc *udc;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_udc_vbus_session()\n");
++ udc = container_of (_gadget, struct s3c2410_udc, gadget);
++
++ udc->vbus = (is_active != 0);
++ pull_up(udc, is_active);
++ return 0;
++}
++
++static int s3c2410_pullup (struct usb_gadget *_gadget, int is_on)
++{
++ struct s3c2410_udc *udc;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_pullup()\n");
++ udc = container_of (_gadget, struct s3c2410_udc, gadget);
++ is_on = !!is_on;
++ pull_up(udc, is_on);
++ return 0;
++}
++
++static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
++{
++ struct s3c2410_udc *dev = _dev;
++ unsigned int value;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_udc_vbus_irq()\n");
++ value = s3c2410_gpio_getpin(udc_info->vbus_pin);
++ if (udc_info->vbus_pin_inverted)
++ value = !value;
++
++ if (value != dev->vbus)
++ s3c2410_udc_vbus_session(&dev->gadget, value);
++
++ return IRQ_HANDLED;
++}
++
++static const struct usb_gadget_ops s3c2410_ops = {
++ .get_frame = s3c2410_g_get_frame,
++ .wakeup = s3c2410_wakeup,
++ .set_selfpowered = s3c2410_set_selfpowered,
++ .pullup = s3c2410_pullup,
++ .vbus_session = s3c2410_udc_vbus_session,
++};
++
++/*------------------------- gadget driver handling---------------------------*/
++/*
++ * udc_disable
++ */
++static void udc_disable(struct s3c2410_udc *dev)
++{
++ dprintk(DEBUG_NORMAL, "udc_disable called\n");
++
++ /* Disable all interrupts */
++ udc_writel(0x00, S3C2410_UDC_USB_INT_EN_REG);
++ udc_writel(0x00, S3C2410_UDC_EP_INT_EN_REG);
++
++ /* Clear the interrupt registers */
++ udc_writel( S3C2410_UDC_USBINT_RESET | \
++ S3C2410_UDC_USBINT_RESUME | \
++ S3C2410_UDC_USBINT_SUSPEND, \
++ S3C2410_UDC_USB_INT_REG);
++ udc_writel( 0x1F, S3C2410_UDC_EP_INT_REG);
++
++
++ /* Good bye, cruel world */
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
++
++ /* Set address to 0 */
++ /*udc_writel( 0x80, S3C2410_UDC_FUNC_ADDR_REG);*/
++
++ /* Set speed to unknown */
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++}
++/*
++ * udc_reinit
++ */
++static void udc_reinit(struct s3c2410_udc *dev)
++{
++ u32 i;
++
++ /* device/ep0 records init */
++ INIT_LIST_HEAD (&dev->gadget.ep_list);
++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
++ dev->ep0state = EP0_IDLE;
++
++
++ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++ struct s3c2410_ep *ep = &dev->ep[i];
++
++ if (i != 0)
++ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
++
++ ep->dev = dev;
++ ep->desc = NULL;
++ ep->halted = 0;
++ INIT_LIST_HEAD (&ep->queue);
++ }
++}
++
++/*
++ * udc_enable
++ */
++static void udc_enable(struct s3c2410_udc *dev)
++{
++ int i;
++
++ dprintk(DEBUG_NORMAL, "udc_enable called\n");
++
++ /* dev->gadget.speed = USB_SPEED_UNKNOWN; */
++ dev->gadget.speed = USB_SPEED_FULL;
++
++ /* Set MAXP for all endpoints */
++ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++
++ udc_writel(i, S3C2410_UDC_INDEX_REG);
++ udc_writel((dev->ep[i].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
++ }
++
++ /* Set default power state */
++ udc_writel(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
++
++ /* Enable reset and suspend interrupt interrupts */
++ udc_writel(1<<2 | 1<<0 ,S3C2410_UDC_USB_INT_EN_REG);
++
++ /* Enable ep0 interrupt */
++ udc_writel(0x01,S3C2410_UDC_EP_INT_EN_REG);
++
++ /* time to say "hello, world" */
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
++}
++
++
++/*
++ * nop_release
++ */
++static void nop_release (struct device *dev)
++{
++ dprintk(DEBUG_NORMAL, "%s %s\n", __FUNCTION__, dev->bus_id);
++}
++/*
++ * usb_gadget_register_driver
++ */
++int
++usb_gadget_register_driver (struct usb_gadget_driver *driver)
++{
++ struct s3c2410_udc *udc = the_controller;
++ int retval;
++
++ dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
++ driver->driver.name);
++
++ /* Sanity checks */
++ if (!udc)
++ return -ENODEV;
++ if (udc->driver)
++ return -EBUSY;
++ if (!driver->bind || !driver->setup
++ || driver->speed != USB_SPEED_FULL) {
++ printk(KERN_ERR "Invalid driver : bind %p setup %p speed %d\n",
++ driver->bind, driver->setup, driver->speed);
++ return -EINVAL;
++ }
++#if defined(MODULE)
++ if (!driver->unbind) {
++ printk(KERN_ERR "Invalid driver : no unbind method\n");
++ return -EINVAL;
++ }
++#endif
++
++ /* Hook the driver */
++ udc->driver = driver;
++ udc->gadget.dev.driver = &driver->driver;
++
++ /*Bind the driver */
++ device_add(&udc->gadget.dev);
++ dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name);
++ if ((retval = driver->bind (&udc->gadget)) != 0) {
++ device_del(&udc->gadget.dev);
++ udc->driver = NULL;
++ udc->gadget.dev.driver = NULL;
++ return retval;
++ }
++
++ /* driver->driver.bus = 0; */
++
++ /* Enable udc */
++ udc_enable(udc);
++
++ return 0;
++}
++
++
++/*
++ * usb_gadget_unregister_driver
++ */
++int
++usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
++{
++ struct s3c2410_udc *udc = the_controller;
++
++ if (!udc)
++ return -ENODEV;
++ if (!driver || driver != udc->driver)
++ return -EINVAL;
++
++ dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
++ driver->driver.name);
++
++ if (driver->disconnect)
++ driver->disconnect(&udc->gadget);
++
++ if (driver->unbind)
++ driver->unbind (&udc->gadget);
++
++ device_del(&udc->gadget.dev);
++ udc->driver = NULL;
++
++ /* Disable udc */
++ udc_disable(udc);
++
++ return 0;
++}
++
++/*---------------------------------------------------------------------------*/
++static struct s3c2410_udc memory = {
++ .gadget = {
++ .ops = &s3c2410_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = gadget_name,
++ .dev = {
++ .bus_id = "gadget",
++ .release = nop_release,
++ },
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .num = 0,
++ .ep = {
++ .name = ep0name,
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .num = 1,
++ .ep = {
++ .name = "ep1-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 1,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[2] = {
++ .num = 2,
++ .ep = {
++ .name = "ep2-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 2,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[3] = {
++ .num = 3,
++ .ep = {
++ .name = "ep3-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 3,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[4] = {
++ .num = 4,
++ .ep = {
++ .name = "ep4-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 4,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ }
++
++};
++
++/*
++ * probe - binds to the platform device
++ */
++static int s3c2410_udc_probe(struct platform_device *pdev)
++{
++ struct s3c2410_udc *udc = &memory;
++ int retval;
++ unsigned int irq;
++
++ dprintk(DEBUG_NORMAL,"s3c2410_udc_probe\n");
++
++ usb_bus_clock = clk_get(NULL, "usb-bus");
++ if (IS_ERR(usb_bus_clock)) {
++ printk(KERN_INFO "failed to get usb bus clock source\n");
++ return PTR_ERR(usb_bus_clock);
++ }
++
++ clk_enable(usb_bus_clock);
++
++ udc_clock = clk_get(NULL, "usb-device");
++ if (IS_ERR(udc_clock)) {
++ printk(KERN_INFO "failed to get udc clock source\n");
++ return PTR_ERR(udc_clock);
++ }
++
++ clk_enable(udc_clock);
++
++ mdelay(10);
++
++ dprintk(DEBUG_VERBOSE, "got and enabled clocks\n");
++
++ if (strncmp(pdev->name, "s3c2440", 7) == 0) {
++ printk("Detected S3C2440 - increasing FIFO to 128 bytes\n");
++ memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
++ }
++
++ spin_lock_init (&udc->lock);
++ udc_info = pdev->dev.platform_data;
++
++ rsrc_start = S3C2410_PA_USBDEV;
++ rsrc_len = S3C24XX_SZ_USBDEV;
++
++ if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
++ return -EBUSY;
++
++ base_addr = ioremap(rsrc_start, rsrc_len);
++ if (!base_addr) {
++ retval = -ENOMEM;
++ goto err_mem;
++ }
++
++ device_initialize(&udc->gadget.dev);
++ udc->gadget.dev.parent = &pdev->dev;
++ udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
++
++ the_controller = udc;
++ platform_set_drvdata(pdev, udc);
++
++ udc_disable(udc);
++ udc_reinit(udc);
++
++ /* irq setup after old hardware state is cleaned up */
++ retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
++ IRQF_DISABLED, gadget_name, udc);
++
++ if (retval != 0) {
++ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
++ gadget_name, IRQ_USBD, retval);
++ retval = -EBUSY;
++ goto err_map;
++ }
++
++ dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, IRQ_USBD);
++
++ if (udc_info && udc_info->vbus_pin > 0) {
++ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
++ retval = request_irq(irq, s3c2410_udc_vbus_irq,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ gadget_name, udc);
++
++ if (retval != 0) {
++ printk(KERN_ERR "%s: can't get vbus irq %i, err %d\n",
++ gadget_name, irq, retval);
++ retval = -EBUSY;
++ goto err_int;
++ }
++
++ dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, irq);
++ }
++ else {
++ udc->vbus = 1;
++ }
++
++#ifdef ENABLE_SYSFS
++ /* create device files */
++ device_create_file(&pdev->dev, &dev_attr_regs);
++#endif
++ return 0;
++err_int:
++ free_irq(IRQ_USBD, udc);
++err_map:
++ iounmap(base_addr);
++err_mem:
++ release_mem_region(rsrc_start, rsrc_len);
++
++ return retval;
++}
++
++/*
++ * s3c2410_udc_remove
++ */
++static int s3c2410_udc_remove(struct platform_device *pdev)
++{
++ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
++ unsigned int irq;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_udc_remove\n");
++ usb_gadget_unregister_driver(udc->driver);
++
++ if (udc_info && udc_info->vbus_pin > 0) {
++ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
++ free_irq(irq, udc);
++ }
++
++ free_irq(IRQ_USBD, udc);
++
++ iounmap(base_addr);
++ release_mem_region(rsrc_start, rsrc_len);
++
++ platform_set_drvdata(pdev, NULL);
++
++ if (!IS_ERR(udc_clock) && udc_clock != NULL) {
++ clk_disable(udc_clock);
++ clk_put(udc_clock);
++ udc_clock = NULL;
++ }
++
++ if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
++ clk_disable(usb_bus_clock);
++ clk_put(usb_bus_clock);
++ usb_bus_clock = NULL;
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
++{
++ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
++
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
++
++ return 0;
++}
++
++static int s3c2410_udc_resume(struct platform_device *pdev)
++{
++ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
++
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
++
++ return 0;
++}
++#else
++#define s3c2410_udc_suspend NULL
++#define s3c2410_udc_resume NULL
++#endif
++
++
++static struct platform_driver udc_driver_2410 = {
++ .driver = {
++ .name = "s3c2410-usbgadget",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410_udc_probe,
++ .remove = s3c2410_udc_remove,
++ .suspend = s3c2410_udc_suspend,
++ .resume = s3c2410_udc_resume,
++};
++
++static struct platform_driver udc_driver_2440 = {
++ .driver = {
++ .name = "s3c2440-usbgadget",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410_udc_probe,
++ .remove = s3c2410_udc_remove,
++ .suspend = s3c2410_udc_suspend,
++ .resume = s3c2410_udc_resume,
++};
++
++static int __init udc_init(void)
++{
++ int retval;
++
++ dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
++
++ retval = platform_driver_register(&udc_driver_2410);
++ if (retval)
++ return retval;
++
++ return platform_driver_register(&udc_driver_2440);
++}
++
++static void __exit udc_exit(void)
++{
++ platform_driver_unregister(&udc_driver_2410);
++ platform_driver_unregister(&udc_driver_2440);
++}
++
++
++EXPORT_SYMBOL (usb_gadget_unregister_driver);
++EXPORT_SYMBOL (usb_gadget_register_driver);
++
++module_init(udc_init);
++module_exit(udc_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
+Index: linux-2.6.20/drivers/usb/gadget/s3c2410_udc.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.20/drivers/usb/gadget/s3c2410_udc.h 2007-02-15 14:54:49.000000000 +0100
+@@ -0,0 +1,188 @@
++#ifndef _S3C2410_UDC_H
++#define _S3C2410_UDC_H
++
++struct s3c2410_ep {
++ struct list_head queue;
++ unsigned long last_io; /* jiffies timestamp */
++ struct usb_gadget *gadget;
++ struct s3c2410_udc *dev;
++ const struct usb_endpoint_descriptor *desc;
++ struct usb_ep ep;
++ u8 num;
++
++ unsigned short fifo_size;
++ u8 bEndpointAddress;
++ u8 bmAttributes;
++
++ unsigned halted : 1;
++ unsigned already_seen : 1;
++ unsigned setup_stage : 1;
++};
++
++
++/* Warning : ep0 has a fifo of 16 bytes */
++/* Don't try to set 32 or 64 */
++#define EP0_FIFO_SIZE 16
++#define EP_FIFO_SIZE 64
++#define DEFAULT_POWER_STATE 0x00
++
++#define S3C2440_EP_FIFO_SIZE 128
++
++static const char ep0name [] = "ep0";
++
++static const char *const ep_name[] = {
++ ep0name, /* everyone has ep0 */
++ /* s3c2410 four bidirectional bulk endpoints */
++ "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
++};
++
++#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)
++
++struct s3c2410_request {
++ struct list_head queue; /* ep's requests */
++ struct usb_request req;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++static const char *ep0states[]= {
++ "EP0_IDLE",
++ "EP0_IN_DATA_PHASE",
++ "EP0_OUT_DATA_PHASE",
++ "EP0_END_XFER",
++ "EP0_STALL",
++};
++
++struct s3c2410_udc {
++ spinlock_t lock;
++
++ struct s3c2410_ep ep[S3C2410_ENDPOINTS];
++ int address;
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct s3c2410_request fifo_req;
++ u8 fifo_buf[EP_FIFO_SIZE];
++ u16 devstatus;
++
++ u32 port_status;
++ int ep0state;
++
++ unsigned got_irq : 1;
++
++ unsigned req_std : 1;
++ unsigned req_config : 1;
++ unsigned req_pending : 1;
++ u8 vbus;
++};
++
++/****************** MACROS ******************/
++/* #define BIT_MASK BIT_MASK*/
++#define BIT_MASK 0xFF
++
++#define maskb(base,v,m,a) \
++ writeb((readb(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define maskw(base,v,m,a) \
++ writew((readw(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define maskl(base,v,m,a) \
++ writel((readl(base+a) & ~(m))|((v)&(m)), (base+a))
++
++#define clear_ep0_sst(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ writel(0x00, base+S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define clear_ep0_se(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_SSE, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define clear_ep0_opr(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_SOPKTRDY, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define set_ep0_ipr(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_IPKRDY, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define set_ep0_de(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_DE, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#if 0
++#define set_ep0_ss(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL|S3C2410_UDC_EP0_CSR_SOPKTRDY, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++#else
++#define set_ep0_ss(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL, \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++#endif
++
++#define set_ep0_de_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY \
++ | S3C2410_UDC_EP0_CSR_DE), \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define set_ep0_sse_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY \
++ | S3C2410_UDC_EP0_CSR_SSE), \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++#define set_ep0_de_in(base) do { \
++ S3C2410_UDC_SETIX(base,EP0); \
++ maskl(base,(S3C2410_UDC_EP0_CSR_IPKRDY \
++ | S3C2410_UDC_EP0_CSR_DE), \
++ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
++} while(0)
++
++
++
++#define clear_stall_ep1_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP1); \
++ orl(0,base+S3C2410_UDC_OUT_CSR1_REG); \
++} while(0)
++
++
++#define clear_stall_ep2_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP2); \
++ orl(0, base+S3C2410_UDC_OUT_CSR1_REG); \
++} while(0)
++
++
++#define clear_stall_ep3_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP3); \
++ orl(0,base+S3C2410_UDC_OUT_CSR1_REG); \
++} while(0)
++
++
++#define clear_stall_ep4_out(base) do { \
++ S3C2410_UDC_SETIX(base,EP4); \
++ orl(0, base+S3C2410_UDC_OUT_CSR1_REG); \
++} while(0)
++
++#endif
++
++
+
Added: developers/nbd/patches-2.6.22/190-s3c2410_touchscreen.patch
===================================================================
--- developers/nbd/patches-2.6.22/190-s3c2410_touchscreen.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/190-s3c2410_touchscreen.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,602 @@
+Index: linux-2.6.21-moko/arch/arm/plat-s3c24xx/devs.c
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/plat-s3c24xx/devs.c
++++ linux-2.6.21-moko/arch/arm/plat-s3c24xx/devs.c
+@@ -30,6 +30,7 @@
+
+ #include <asm/arch/regs-serial.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/ts.h>
+
+ #include <asm/plat-s3c24xx/devs.h>
+ #include <asm/plat-s3c24xx/cpu.h>
+@@ -206,6 +207,23 @@
+
+ EXPORT_SYMBOL(s3c_device_nand);
+
++/* Touchscreen */
++struct platform_device s3c_device_ts = {
++ .name = "s3c2410-ts",
++ .id = -1,
++};
++
++EXPORT_SYMBOL(s3c_device_ts);
++
++static struct s3c2410_ts_mach_info s3c2410ts_info;
++
++void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info)
++{
++ memcpy(&s3c2410ts_info,hard_s3c2410ts_info,sizeof(struct s3c2410_ts_mach_info));
++ s3c_device_ts.dev.platform_data = &s3c2410ts_info;
++}
++EXPORT_SYMBOL(set_s3c2410ts_info);
++
+ /* USB Device (Gadget)*/
+
+ static struct resource s3c_usbgadget_resource[] = {
+Index: linux-2.6.21-moko/include/asm-arm/plat-s3c24xx/devs.h
+===================================================================
+--- linux-2.6.21-moko.orig/include/asm-arm/plat-s3c24xx/devs.h
++++ linux-2.6.21-moko/include/asm-arm/plat-s3c24xx/devs.h
+@@ -41,6 +41,7 @@
+ extern struct platform_device s3c_device_timer3;
+
+ extern struct platform_device s3c_device_usbgadget;
++extern struct platform_device s3c_device_ts;
+
+ /* s3c2440 specific devices */
+
+Index: linux-2.6.21-moko/arch/arm/mach-s3c2410/mach-h1940.c
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/mach-s3c2410/mach-h1940.c
++++ linux-2.6.21-moko/arch/arm/mach-s3c2410/mach-h1940.c
+@@ -38,6 +38,7 @@
+ #include <asm/arch/h1940-latch.h>
+ #include <asm/arch/fb.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/ts.h>
+
+ #include <asm/plat-s3c24xx/clock.h>
+ #include <asm/plat-s3c24xx/devs.h>
+@@ -129,6 +130,11 @@
+ };
+
+
++static struct s3c2410_ts_mach_info h1940_ts_cfg __initdata = {
++ .delay = 10000,
++ .presc = 49,
++ .oversampling_shift = 2,
++};
+
+ /**
+ * Set lcd on or off
+@@ -185,6 +191,7 @@
+ &s3c_device_i2c,
+ &s3c_device_iis,
+ &s3c_device_usbgadget,
++ &s3c_device_ts,
+ &s3c_device_leds,
+ };
+
+@@ -218,6 +225,7 @@
+ u32 tmp;
+
+ s3c24xx_fb_set_platdata(&h1940_lcdcfg);
++ set_s3c2410ts_info(&h1940_ts_cfg);
+ s3c24xx_udc_set_platdata(&h1940_udc_cfg);
+
+ /* Turn off suspend on both USB ports, and switch the
+Index: linux-2.6.21-moko/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/touchscreen/Kconfig
++++ linux-2.6.21-moko/drivers/input/touchscreen/Kconfig
+@@ -54,6 +54,24 @@
+ To compile this driver as a module, choose M here: the
+ module will be called corgi_ts.
+
++config TOUCHSCREEN_S3C2410
++ tristate "Samsung S3C2410 touchscreen input driver"
++ depends on ARCH_S3C2410 && INPUT && INPUT_TOUCHSCREEN
++ select SERIO
++ help
++ Say Y here if you have the s3c2410 touchscreen.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called s3c2410_ts.
++
++config TOUCHSCREEN_S3C2410_DEBUG
++ boolean "Samsung S3C2410 touchscreen debug messages"
++ depends on TOUCHSCREEN_S3C2410
++ help
++ Select this if you want debug messages
++
+ config TOUCHSCREEN_GUNZE
+ tristate "Gunze AHL-51S touchscreen"
+ select SERIO
+Index: linux-2.6.21-moko/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/touchscreen/Makefile
++++ linux-2.6.21-moko/drivers/input/touchscreen/Makefile
+@@ -16,3 +16,4 @@
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
++obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
+Index: linux-2.6.21-moko/drivers/input/touchscreen/s3c2410_ts.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/input/touchscreen/s3c2410_ts.c
+@@ -0,0 +1,426 @@
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * Copyright (c) 2004 Arnaud Patard <arnaud.patard at rtp-net.org>
++ * iPAQ H1940 touchscreen support
++ *
++ * ChangeLog
++ *
++ * 2004-09-05: Herbert Pötzl <herbert at 13thfloor.at>
++ * - added clock (de-)allocation code
++ *
++ * 2005-03-06: Arnaud Patard <arnaud.patard at rtp-net.org>
++ * - h1940_ -> s3c2410 (this driver is now also used on the n30
++ * machines :P)
++ * - Debug messages are now enabled with the config option
++ * TOUCHSCREEN_S3C2410_DEBUG
++ * - Changed the way the value are read
++ * - Input subsystem should now work
++ * - Use ioremap and readl/writel
++ *
++ * 2005-03-23: Arnaud Patard <arnaud.patard at rtp-net.org>
++ * - Make use of some undocumented features of the touchscreen
++ * controller
++ *
++ * 2007-05-23: Harald Welte <laforge at openmoko.org>
++ * - Add proper support for S32440
++ */
++
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/input.h>
++#include <linux/init.h>
++#include <linux/serio.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++
++#include <asm/arch/regs-adc.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/ts.h>
++
++/* For ts.dev.id.version */
++#define S3C2410TSVERSION 0x0101
++
++#define TSC_SLEEP (S3C2410_ADCTSC_PULL_UP_DISABLE | S3C2410_ADCTSC_XY_PST(0))
++
++#define WAIT4INT(x) (((x)<<8) | \
++ S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
++ S3C2410_ADCTSC_XY_PST(3))
++
++#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
++ S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
++
++#define DEBUG_LVL KERN_DEBUG
++
++MODULE_AUTHOR("Arnaud Patard <arnaud.patard at rtp-net.org>");
++MODULE_DESCRIPTION("s3c2410 touchscreen driver");
++MODULE_LICENSE("GPL");
++
++/*
++ * Definitions & global arrays.
++ */
++
++
++static char *s3c2410ts_name = "s3c2410 TouchScreen";
++
++/*
++ * Per-touchscreen data.
++ */
++
++struct s3c2410ts {
++ struct input_dev *dev;
++ long xp;
++ long yp;
++ int count;
++ int shift;
++};
++
++static struct s3c2410ts ts;
++static void __iomem *base_addr;
++
++static inline void s3c2410_ts_connect(void)
++{
++ s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);
++ s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);
++ s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);
++ s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
++}
++
++static void touch_timer_fire(unsigned long data)
++{
++ unsigned long data0;
++ unsigned long data1;
++ int updown;
++
++ data0 = readl(base_addr+S3C2410_ADCDAT0);
++ data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++
++ if (updown) {
++ if (ts.count != 0) {
++ ts.xp >>= ts.shift;
++ ts.yp >>= ts.shift;
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++ {
++ struct timeval tv;
++ do_gettimeofday(&tv);
++ printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
++ }
++#endif
++
++ input_report_abs(ts.dev, ABS_X, ts.xp);
++ input_report_abs(ts.dev, ABS_Y, ts.yp);
++
++ input_report_key(ts.dev, BTN_TOUCH, 1);
++ input_report_abs(ts.dev, ABS_PRESSURE, 1);
++ input_sync(ts.dev);
++ }
++
++ ts.xp = 0;
++ ts.yp = 0;
++ ts.count = 0;
++
++ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
++ writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
++ } else {
++ ts.count = 0;
++
++ input_report_key(ts.dev, BTN_TOUCH, 0);
++ input_report_abs(ts.dev, ABS_PRESSURE, 0);
++ input_sync(ts.dev);
++
++ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++ }
++}
++
++static struct timer_list touch_timer =
++ TIMER_INITIALIZER(touch_timer_fire, 0, 0);
++
++static irqreturn_t stylus_updown(int irq, void *dev_id)
++{
++ unsigned long data0;
++ unsigned long data1;
++ int updown;
++
++ data0 = readl(base_addr+S3C2410_ADCDAT0);
++ data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++ updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
++
++ /* TODO we should never get an interrupt with updown set while
++ * the timer is running, but maybe we ought to verify that the
++ * timer isn't running anyways. */
++
++ if (updown)
++ touch_timer_fire(0);
++
++ return IRQ_HANDLED;
++}
++
++
++static irqreturn_t stylus_action(int irq, void *dev_id)
++{
++ unsigned long data0;
++ unsigned long data1;
++
++ data0 = readl(base_addr+S3C2410_ADCDAT0);
++ data1 = readl(base_addr+S3C2410_ADCDAT1);
++
++ ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
++ ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
++ ts.count++;
++
++ if (ts.count < (1<<ts.shift)) {
++ writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
++ writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
++ } else {
++ mod_timer(&touch_timer, jiffies+1);
++ writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
++ }
++
++ return IRQ_HANDLED;
++}
++
++static struct clk *adc_clock;
++
++/*
++ * The functions for inserting/removing us as a module.
++ */
++
++static int __init s3c2410ts_probe(struct platform_device *pdev)
++{
++ struct s3c2410_ts_mach_info *info;
++ struct input_dev *input_dev;
++
++ info = ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
++
++ if (!info)
++ {
++ printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
++ return -EINVAL;
++ }
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++ printk(DEBUG_LVL "Entering s3c2410ts_init\n");
++#endif
++
++ adc_clock = clk_get(NULL, "adc");
++ if (!adc_clock) {
++ printk(KERN_ERR "failed to get adc clock source\n");
++ return -ENOENT;
++ }
++ clk_enable(adc_clock);
++
++#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
++ printk(DEBUG_LVL "got and enabled clock\n");
++#endif
++
++ base_addr=ioremap(S3C2410_PA_ADC,0x20);
++ if (base_addr == NULL) {
++ printk(KERN_ERR "Failed to remap register block\n");
++ return -ENOMEM;
++ }
++
++
++ /* If we acutally are a S3C2410: Configure GPIOs */
++ if (!strcmp(pdev->name, "s3c2410-ts"))
++ s3c2410_ts_connect();
++
++ if ((info->presc&0xff) > 0)
++ writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
++ base_addr+S3C2410_ADCCON);
++ else
++ writel(0,base_addr+S3C2410_ADCCON);
++
++
++ /* Initialise registers */
++ if ((info->delay&0xffff) > 0)
++ writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
++
++ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++
++ /* Initialise input stuff */
++ memset(&ts, 0, sizeof(struct s3c2410ts));
++ input_dev = input_allocate_device();
++
++ if (!input_dev) {
++ printk(KERN_ERR "Unable to allocate the input device !!\n");
++ return -ENOMEM;
++ }
++
++ ts.dev = input_dev;
++ ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
++ ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
++ input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
++ input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
++ input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
++
++ ts.dev->private = &ts;
++ ts.dev->name = s3c2410ts_name;
++ ts.dev->id.bustype = BUS_RS232;
++ ts.dev->id.vendor = 0xDEAD;
++ ts.dev->id.product = 0xBEEF;
++ ts.dev->id.version = S3C2410TSVERSION;
++
++ ts.shift = info->oversampling_shift;
++
++ /* Get irqs */
++ if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
++ "s3c2410_action", ts.dev)) {
++ printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
++ iounmap(base_addr);
++ return -EIO;
++ }
++ if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
++ "s3c2410_action", ts.dev)) {
++ printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
++ iounmap(base_addr);
++ return -EIO;
++ }
++
++ printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
++
++ /* All went ok, so register to the input system */
++ input_register_device(ts.dev);
++
++ return 0;
++}
++
++static int s3c2410ts_remove(struct platform_device *pdev)
++{
++ disable_irq(IRQ_ADC);
++ disable_irq(IRQ_TC);
++ free_irq(IRQ_TC,ts.dev);
++ free_irq(IRQ_ADC,ts.dev);
++
++ if (adc_clock) {
++ clk_disable(adc_clock);
++ clk_put(adc_clock);
++ adc_clock = NULL;
++ }
++
++ input_unregister_device(ts.dev);
++ iounmap(base_addr);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ writel(TSC_SLEEP, base_addr+S3C2410_ADCTSC);
++ writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
++ base_addr+S3C2410_ADCCON);
++
++ disable_irq(IRQ_ADC);
++ disable_irq(IRQ_TC);
++
++ clk_disable(adc_clock);
++
++ return 0;
++}
++
++static int s3c2410ts_resume(struct platform_device *pdev)
++{
++ struct s3c2410_ts_mach_info *info =
++ ( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
++
++ clk_enable(adc_clock);
++ msleep(1);
++
++ enable_irq(IRQ_ADC);
++ enable_irq(IRQ_TC);
++
++ if ((info->presc&0xff) > 0)
++ writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
++ base_addr+S3C2410_ADCCON);
++ else
++ writel(0,base_addr+S3C2410_ADCCON);
++
++ /* Initialise registers */
++ if ((info->delay&0xffff) > 0)
++ writel(info->delay & 0xffff, base_addr+S3C2410_ADCDLY);
++
++ writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
++
++ return 0;
++}
++
++#else
++#define s3c2410ts_suspend NULL
++#define s3c2410ts_resume NULL
++#endif
++
++static struct platform_driver s3c2410ts_driver = {
++ .driver = {
++ .name = "s3c2410-ts",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410ts_probe,
++ .remove = s3c2410ts_remove,
++ .suspend = s3c2410ts_suspend,
++ .resume = s3c2410ts_resume,
++
++};
++
++static struct platform_driver s3c2440ts_driver = {
++ .driver = {
++ .name = "s3c2440-ts",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410ts_probe,
++ .remove = s3c2410ts_remove,
++ .suspend = s3c2410ts_suspend,
++ .resume = s3c2410ts_resume,
++
++};
++
++static int __init s3c2410ts_init(void)
++{
++ int rc;
++
++ rc = platform_driver_register(&s3c2410ts_driver);
++ if (rc < 0)
++ return rc;
++
++ rc = platform_driver_register(&s3c2440ts_driver);
++ if (rc < 0)
++ platform_driver_unregister(&s3c2410ts_driver);
++
++ return rc;
++}
++
++static void __exit s3c2410ts_exit(void)
++{
++ platform_driver_unregister(&s3c2440ts_driver);
++ platform_driver_unregister(&s3c2410ts_driver);
++}
++
++module_init(s3c2410ts_init);
++module_exit(s3c2410ts_exit);
++
++/*
++ Local variables:
++ compile-command: "make ARCH=arm CROSS_COMPILE=/usr/local/arm/3.3.2/bin/arm-linux- -k -C ../../.."
++ c-basic-offset: 8
++ End:
++*/
+Index: linux-2.6.21-moko/include/asm-arm/arch-s3c2410/ts.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/include/asm-arm/arch-s3c2410/ts.h
+@@ -0,0 +1,28 @@
++/* linux/include/asm/arch-s3c2410/ts.h
++ *
++ * Copyright (c) 2005 Arnaud Patard <arnaud.patard at rtp-net.org>
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ *
++ * Changelog:
++ * 24-Mar-2005 RTP Created file
++ * 03-Aug-2005 RTP Renamed to ts.h
++ */
++
++#ifndef __ASM_ARM_TS_H
++#define __ASM_ARM_TS_H
++
++struct s3c2410_ts_mach_info {
++ int delay;
++ int presc;
++ int oversampling_shift;
++};
++
++void __init set_s3c2410ts_info(struct s3c2410_ts_mach_info *hard_s3c2410ts_info);
++
++#endif /* __ASM_ARM_TS_H */
++
+Index: linux-2.6.21-moko/arch/arm/plat-s3c24xx/s3c244x.c
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/plat-s3c24xx/s3c244x.c
++++ linux-2.6.21-moko/arch/arm/plat-s3c24xx/s3c244x.c
+@@ -68,6 +68,7 @@
+
+ s3c_device_i2c.name = "s3c2440-i2c";
+ s3c_device_nand.name = "s3c2440-nand";
++ s3c_device_ts.name = "s3c2440-ts";
+ s3c_device_usbgadget.name = "s3c2440-usbgadget";
+ }
+
Added: developers/nbd/patches-2.6.22/200-s3c2410-bbt.patch
===================================================================
--- developers/nbd/patches-2.6.22/200-s3c2410-bbt.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/200-s3c2410-bbt.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,33 @@
+Index: linux-2.6.17.14-fic4.test/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mtd/nand/s3c2410.c 2007-02-02 13:02:10.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mtd/nand/s3c2410.c 2007-02-02 13:03:52.000000000 +0100
+@@ -524,7 +524,11 @@
+ chip->select_chip = s3c2410_nand_select_chip;
+ chip->chip_delay = 50;
+ chip->priv = nmtd;
++#ifdef CONFIG_MTD_NAND_S3C2410_BBT
++ chip->options = NAND_USE_FLASH_BBT;
++#else
+ chip->options = 0;
++#endif
+ chip->controller = &info->controller;
+
+ if (info->is_s3c2440) {
+Index: linux-2.6.17.14-fic4.test/drivers/mtd/nand/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mtd/nand/Kconfig 2007-02-02 13:03:56.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mtd/nand/Kconfig 2007-02-02 13:04:47.000000000 +0100
+@@ -90,6 +90,12 @@
+ No board specfic support is done by this driver, each board
+ must advertise a platform_device for the driver to attach.
+
++config MTD_NAND_S3C2410_BBT
++ bool "S3C2410 NAND bad block table"
++ depends on MTD_NAND_S3C2410
++ help
++ Enable bad block table support for S3C2410 NAND driver
++
+ config MTD_NAND_S3C2410_DEBUG
+ bool "S3C2410 NAND driver debug"
+ depends on MTD_NAND_S3C2410
Added: developers/nbd/patches-2.6.22/210-udc-nomodule-misccr.patch
===================================================================
--- developers/nbd/patches-2.6.22/210-udc-nomodule-misccr.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/210-udc-nomodule-misccr.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,17 @@
+This patch fixes S3C2410 UDC support when built as module. It also ensures
+that the USB port is switched into device mode by configuering the MISCCR
+register accordingly.
+
+Index: linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- linux-2.6.20.orig/drivers/usb/gadget/s3c2410_udc.c 2007-02-15 14:54:49.000000000 +0100
++++ linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c 2007-02-15 14:55:45.000000000 +0100
+@@ -1764,6 +1764,8 @@
+ udc->vbus = 1;
+ }
+
++ s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST|S3C2410_MISCCR_USBSUSPND0|S3C2410_MISCCR_USBSUSPND1, 0);
++
+ #ifdef ENABLE_SYSFS
+ /* create device files */
+ device_create_file(&pdev->dev, &dev_attr_regs);
Added: developers/nbd/patches-2.6.22/220-s3c2410_udc-vbus_draw_pdata.patch
===================================================================
--- developers/nbd/patches-2.6.22/220-s3c2410_udc-vbus_draw_pdata.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/220-s3c2410_udc-vbus_draw_pdata.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,47 @@
+Introduce a platform_device (machine) specific callback function
+which gets called when the amount of power we can draw from Vbus
+has changed.
+
+Index: linux-2.6.21-moko/drivers/usb/gadget/s3c2410_udc.c
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/usb/gadget/s3c2410_udc.c
++++ linux-2.6.21-moko/drivers/usb/gadget/s3c2410_udc.c
+@@ -1404,12 +1404,25 @@
+ return IRQ_HANDLED;
+ }
+
++static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned int ma)
++{
++ struct s3c2410_udc *udc;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_vbus_draw()\n");
++
++ if (udc_info && udc_info->vbus_draw)
++ return udc_info->vbus_draw(ma);
++ else
++ return 0;
++}
++
+ static const struct usb_gadget_ops s3c2410_ops = {
+ .get_frame = s3c2410_g_get_frame,
+ .wakeup = s3c2410_wakeup,
+ .set_selfpowered = s3c2410_set_selfpowered,
+ .pullup = s3c2410_pullup,
+ .vbus_session = s3c2410_udc_vbus_session,
++ .vbus_draw = s3c2410_vbus_draw,
+ };
+
+ /*------------------------- gadget driver handling---------------------------*/
+Index: linux-2.6.21-moko/include/asm-arm/arch-s3c2410/udc.h
+===================================================================
+--- linux-2.6.21-moko.orig/include/asm-arm/arch-s3c2410/udc.h
++++ linux-2.6.21-moko/include/asm-arm/arch-s3c2410/udc.h
+@@ -26,7 +26,7 @@
+
+ struct s3c2410_udc_mach_info {
+ void (*udc_command)(enum s3c2410_udc_cmd_e);
+- void (*vbus_draw)(unsigned int ma);
++ int (*vbus_draw)(unsigned int ma);
+ unsigned int vbus_pin;
+ unsigned char vbus_pin_inverted;
+ };
Added: developers/nbd/patches-2.6.22/280-qt2410-base.patch
===================================================================
--- developers/nbd/patches-2.6.22/280-qt2410-base.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/280-qt2410-base.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,361 @@
+Index: linux-2.6.20.1/arch/arm/mach-s3c2410/mach-qt2410.c.old
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.20.1/arch/arm/mach-s3c2410/mach-qt2410.c.old 2007-02-21 16:28:56.000000000 +0100
+@@ -0,0 +1,356 @@
++/*
++ *
++ * linux/arch/arm/mach-s3c2410/mach-qt2410.c
++ *
++ * Copyright (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/serial_core.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/ts.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++
++#include "devs.h"
++#include "cpu.h"
++
++#include "pm.h"
++
++static struct map_desc qt2410_iodesc[] __initdata = {
++ { 0xe0000000, __phys_to_pfn(S3C2410_CS3+0x01000000), SZ_1M, MT_DEVICE }
++};
++
++#define UCON S3C2410_UCON_DEFAULT
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg smdk2410_uartcfgs[] = {
++ [0] = {
++ .hwport = 0,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++ [1] = {
++ .hwport = 1,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++ [2] = {
++ .hwport = 2,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ }
++};
++
++/* LCD driver info */
++
++static struct s3c2410fb_mach_info qt2410_lcd_cfg __initdata = {
++ .type = S3C2410_LCDCON1_TFT,
++ .regs = {
++
++ .lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
++ S3C2410_LCDCON1_TFT |
++ S3C2410_LCDCON1_CLKVAL(0x04),
++
++ .lcdcon2 = S3C2410_LCDCON2_VBPD(1) |
++ S3C2410_LCDCON2_LINEVAL(319) |
++ S3C2410_LCDCON2_VFPD(6) |
++ S3C2410_LCDCON2_VSPW(3),
++
++ .lcdcon3 = S3C2410_LCDCON3_HBPD(12) |
++ S3C2410_LCDCON3_HOZVAL(239) |
++ S3C2410_LCDCON3_HFPD(7),
++
++ .lcdcon4 = S3C2410_LCDCON4_MVAL(0) |
++ S3C2410_LCDCON4_HSPW(3),
++
++ .lcdcon5 = S3C2410_LCDCON5_FRM565 |
++ S3C2410_LCDCON5_INVVLINE |
++ S3C2410_LCDCON5_INVVFRAME |
++ S3C2410_LCDCON5_PWREN |
++ S3C2410_LCDCON5_HWSWP,
++ },
++
++#if 0
++ /* currently setup by downloader */
++ .gpccon = 0xaa940659,
++ .gpccon_mask = 0xffffffff,
++ .gpcup = 0x0000ffff,
++ .gpcup_mask = 0xffffffff,
++ .gpdcon = 0xaa84aaa0,
++ .gpdcon_mask = 0xffffffff,
++ .gpdup = 0x0000faff,
++ .gpdup_mask = 0xffffffff,
++#endif
++
++ .lpcsel = ((0xCE6) & ~7) | 1<<4,
++
++ .width = 240,
++ .height = 320,
++
++ .xres = {
++ .min = 240,
++ .max = 240,
++ .defval = 240,
++ },
++
++ .yres = {
++ .min = 320,
++ .max = 320,
++ .defval = 320,
++ },
++
++ .bpp = {
++ .min = 16,
++ .max = 16,
++ .defval = 16,
++ },
++};
++
++static struct resource cs89x0_resources[] = {
++ [0] = {
++ .start = 0x19000000,
++ .end = 0x19000000 + 16,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_EINT9,
++ .end = IRQ_EINT9,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device cs89x0_device = {
++ .name = "cirrus-cs89x0",
++ .num_resources = ARRAY_SIZE(cs89x0_resources),
++ .resource = cs89x0_resources,
++};
++
++static struct platform_device *qt2410_devices[] __initdata = {
++ &s3c_device_usb,
++ &s3c_device_lcd,
++ &s3c_device_wdt,
++ &s3c_device_i2c,
++ &s3c_device_iis,
++ &s3c_device_sdi,
++ &s3c_device_usbgadget,
++ &s3c_device_nand,
++ &s3c_device_ts,
++ &cs89x0_device,
++};
++
++static struct s3c24xx_board qt2410_board __initdata = {
++ .devices = qt2410_devices,
++ .devices_count = ARRAY_SIZE(qt2410_devices)
++};
++
++static struct s3c2410_nand_set qt2410_nand_sets[] = {
++ [0] = {
++ .name = "qt2410-nand",
++ .nr_chips = 1,
++ },
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++ */
++
++static struct s3c2410_platform_nand qt2410_nand_info = {
++ .tacls = 20,
++ .twrph0 = 60,
++ .twrph1 = 20,
++ .nr_sets = ARRAY_SIZE(qt2410_nand_sets),
++ .sets = qt2410_nand_sets,
++};
++
++/* SPI */
++static struct spi_board_info qt2410_spi_board_info[] __initdata = {
++ {
++ .modalias = "jbt6k74",
++ /* platform_data */
++ /* controller_data */
++ /* irq */
++ .max_speed_hz = 10 * 1000 * 1000,
++ .bus_num = 1,
++ /* chip_select */
++ },
++};
++
++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
++{
++ switch (cs) {
++ case BITBANG_CS_ACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPB5, 0);
++ break;
++ case BITBANG_CS_INACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPB5, 1);
++ break;
++ }
++}
++
++static struct s3c2410_spigpio_info spi_gpio_cfg = {
++ .pin_clk = S3C2410_GPG7,
++ .pin_mosi = S3C2410_GPG6,
++ .pin_miso = S3C2410_GPG5,
++ .board_size = ARRAY_SIZE(qt2410_spi_board_info),
++ .board_info = qt2410_spi_board_info,
++ .chip_select = &spi_gpio_cs,
++};
++
++static struct resource s3c_spi_lcm_resource[] = {
++ [0] = {
++ .start = S3C2410_GPB5,
++ .end = S3C2410_GPB5,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = S3C2410_GPG5,
++ .end = S3C2410_GPG5,
++ .flags = IORESOURCE_MEM,
++ },
++ [2] = {
++ .start = S3C2410_GPG6,
++ .end = S3C2410_GPG6,
++ .flags = IORESOURCE_MEM,
++ },
++ [3] = {
++ .start = S3C2410_GPG7,
++ .end = S3C2410_GPG7,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device s3c_device_spi_lcm = {
++ .name = "s3c24xx-spi-gpio",
++ .id = 1,
++ .num_resources = ARRAY_SIZE(s3c_spi_lcm_resource),
++ .resource = s3c_spi_lcm_resource,
++ .dev = {
++ .platform_data = &spi_gpio_cfg,
++ },
++};
++
++static void qt2410_udc_pullup(enum s3c2410_udc_cmd_e cmd)
++{
++ printk(KERN_DEBUG "udc: pullup(%d)\n", cmd);
++
++ switch (cmd) {
++ case S3C2410_UDC_P_ENABLE:
++ break;
++ case S3C2410_UDC_P_DISABLE:
++ break;
++ case S3C2410_UDC_P_RESET:
++ break;
++ default:
++ break;
++ }
++}
++
++static struct s3c2410_udc_mach_info qt2410_udc_cfg = {
++ .udc_command = qt2410_udc_pullup,
++};
++
++static struct s3c2410_ts_mach_info qt2410_ts_cfg = {
++ .delay = 10000,
++ .presc = 49,
++ .oversampling_shift = 2,
++};
++
++static void __init qt2410_map_io(void)
++{
++ s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
++ s3c24xx_init_clocks(12*1000*1000);
++ s3c24xx_init_uarts(smdk2410_uartcfgs, ARRAY_SIZE(smdk2410_uartcfgs));
++ s3c24xx_set_board(&qt2410_board);
++}
++
++static void __init qt2410_machine_init(void)
++{
++ /* Configure the LEDs (even if we have no LED support)*/
++
++ s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP);
++ s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP);
++ s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP);
++ s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP);
++
++ s3c2410_gpio_setpin(S3C2410_GPF4, 1);
++ s3c2410_gpio_setpin(S3C2410_GPF5, 1);
++ s3c2410_gpio_setpin(S3C2410_GPF6, 1);
++ s3c2410_gpio_setpin(S3C2410_GPF7, 1);
++
++ s3c_device_nand.dev.platform_data = &qt2410_nand_info;
++ s3c24xx_fb_set_platdata(&qt2410_lcd_cfg);
++ s3c24xx_udc_set_platdata(&qt2410_udc_cfg);
++ set_s3c2410ts_info(&qt2410_ts_cfg);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPB5, S3C2410_GPIO_OUTPUT);
++ spi_register_board_info(qt2410_spi_board_info,
++ ARRAY_SIZE(qt2410_spi_board_info));
++ platform_device_register(&s3c_device_spi_lcm);
++
++ s3c2410_pm_init();
++}
++
++MACHINE_START(QT2410, "QT2410")
++ .phys_io = S3C2410_PA_UART,
++ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++ .boot_params = S3C2410_SDRAM_PA + 0x100,
++ .map_io = qt2410_map_io,
++ .init_irq = s3c24xx_init_irq,
++ .init_machine = qt2410_machine_init,
++ .timer = &s3c24xx_timer,
++MACHINE_END
++
++
Added: developers/nbd/patches-2.6.22/290-qt2410-cs8900.patch
===================================================================
--- developers/nbd/patches-2.6.22/290-qt2410-cs8900.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/290-qt2410-cs8900.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,61 @@
+Index: linux-2.6.17.14-fic3/drivers/net/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic3.orig/drivers/net/Kconfig 2006-12-07 16:16:46.000000000 +0100
++++ linux-2.6.17.14-fic3/drivers/net/Kconfig 2006-12-07 16:17:50.000000000 +0100
+@@ -1246,7 +1246,7 @@
+
+ config NET_PCI
+ bool "EISA, VLB, PCI and on board controllers"
+- depends on NET_ETHERNET && (ISA || EISA || PCI)
++ depends on NET_ETHERNET && (ISA || EISA || PCI || MACH_QT2410)
+ help
+ This is another class of network cards which attach directly to the
+ bus. If you have one of those, say Y and read the Ethernet-HOWTO,
+@@ -1386,7 +1386,7 @@
+
+ config CS89x0
+ tristate "CS89x0 support"
+- depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X)
++ depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_QT2410)
+ ---help---
+ Support for CS89x0 chipset based Ethernet cards. If you have a
+ network (Ethernet) card of this type, say Y and read the
+Index: linux-2.6.17.14-fic3/drivers/net/cs89x0.c
+===================================================================
+--- linux-2.6.17.14-fic3.orig/drivers/net/cs89x0.c 2006-12-07 16:16:46.000000000 +0100
++++ linux-2.6.17.14-fic3/drivers/net/cs89x0.c 2006-12-07 16:17:50.000000000 +0100
+@@ -195,6 +195,10 @@
+ #define CIRRUS_DEFAULT_IRQ VH_INTC_INT_NUM_CASCADED_INTERRUPT_1 /* Event inputs bank 1 - ID 35/bit 3 */
+ static unsigned int netcard_portlist[] __initdata = {CIRRUS_DEFAULT_BASE, 0};
+ static unsigned int cs8900_irq_map[] = {CIRRUS_DEFAULT_IRQ, 0, 0, 0};
++#elif defined(CONFIG_MACH_QT2410)
++#include <asm/arch/irqs.h>
++static unsigned int netcard_portlist [] __initdata = { 0xe0000300, 0 };
++static unsigned int cs8900_irq_map[] = { IRQ_EINT9, 0, 0, 0 };
+ #else
+ static unsigned int netcard_portlist[] __initdata =
+ { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
+@@ -830,6 +834,14 @@
+
+ printk(" IRQ %d", dev->irq);
+
++ dev->dev_addr[0] = 0x00;
++ dev->dev_addr[1] = 0x00;
++ dev->dev_addr[2] = 0xc0;
++ dev->dev_addr[3] = 0xff;
++ dev->dev_addr[4] = 0xee;
++ dev->dev_addr[5] = 0x08;
++ set_mac_address(dev, dev->dev_addr);
++
+ #if ALLOW_DMA
+ if (lp->use_dma) {
+ get_dma_channel(dev);
+@@ -1310,7 +1322,7 @@
+ else
+ #endif
+ {
+-#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X)
++#if !defined(CONFIG_MACH_IXDP2351) && !defined(CONFIG_ARCH_IXDP2X01) && !defined(CONFIG_ARCH_PNX010X) && !defined(CONFIG_MACH_QT2410)
+ if (((1 << dev->irq) & lp->irq_map) == 0) {
+ printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
+ dev->name, dev->irq, lp->irq_map);
Added: developers/nbd/patches-2.6.22/300-qt2410-s3c_mci-pdata.patch
===================================================================
--- developers/nbd/patches-2.6.22/300-qt2410-s3c_mci-pdata.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/300-qt2410-s3c_mci-pdata.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,45 @@
+This patch adds platform data to support the SD/MMC slot of the Armzone
+S3C2410 development board.
+
+Index: linux-2.6.22.1/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 15:21:45.800816182 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 15:32:42.742253141 +0200
+@@ -33,6 +33,7 @@
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi_bitbang.h>
+
++#include <linux/mmc/host.h>
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+@@ -55,6 +56,7 @@
+ #include <asm/arch/udc.h>
+ #include <asm/arch/spi.h>
+ #include <asm/arch/spi-gpio.h>
++#include <asm/arch/mci.h>
+
+ #include <asm/plat-s3c24xx/common-smdk.h>
+ #include <asm/plat-s3c24xx/devs.h>
+@@ -395,6 +397,13 @@
+
+ __setup("tft=", qt2410_tft_setup);
+
++static struct s3c24xx_mci_pdata qt2410_mmc_cfg = {
++ .gpio_wprotect = S3C2410_GPH8,
++ .gpio_detect = S3C2410_GPG10,
++ .set_power = NULL,
++ .ocr_avail = MMC_VDD_32_33,
++};
++
+ static void __init qt2410_map_io(void)
+ {
+ s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+@@ -405,6 +414,7 @@
+ static void __init qt2410_machine_init(void)
+ {
+ s3c_device_nand.dev.platform_data = &qt2410_nand_info;
++ s3c_device_sdi.dev.platform_data = &qt2410_mmc_cfg;
+
+ switch (tft_type) {
+ case 'p': /* production */
Added: developers/nbd/patches-2.6.22/310-gta01-pcf50606.patch
===================================================================
--- developers/nbd/patches-2.6.22/310-gta01-pcf50606.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/310-gta01-pcf50606.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2398 @@
+This is the PCF50606 power management unit driver for FIC GTA01
+
+Index: linux-2.6.22.1/drivers/i2c/chips/pcf50606.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50606.c 2007-07-16 16:03:14.418634428 +0200
+@@ -0,0 +1,1929 @@
++/* Philips PCF50606 Power Management Unit (PMU) driver
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org> +
++ * Matt Hsu <matt at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * This driver is a monster ;) It provides the following features
++ * - voltage control for a dozen different voltage domains
++ * - charging control for main and backup battery
++ * - rtc / alarm
++ * - watchdog
++ * - adc driver (hw_sensors like)
++ * - pwm driver
++ * - backlight
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/i2c.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/workqueue.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/watchdog.h>
++#include <linux/miscdevice.h>
++#include <linux/input.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++#include <linux/sched.h>
++#include <linux/platform_device.h>
++#include <linux/pcf50606.h>
++#include <linux/apm-emulation.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch/gta01.h>
++
++#include "pcf50606.h"
++
++#if 1
++#define DEBUGP(x, args ...) printk("%s: " x, __FUNCTION__, ## args)
++#define DEBUGPC(x, args ...) printk(x, ## args)
++#else
++#define DEBUGP(x, args ...)
++#define DEBUGPC(x, args ...)
++#endif
++
++/***********************************************************************
++ * Static data / structures
++ ***********************************************************************/
++
++static unsigned short normal_i2c[] = { 0x08, I2C_CLIENT_END };
++
++I2C_CLIENT_INSMOD_1(pcf50606);
++
++#define PCF50606_F_CHG_FAST 0x00000001 /* Charger Fast allowed */
++#define PCF50606_F_CHG_PRESENT 0x00000002 /* Charger present */
++#define PCF50606_F_CHG_FOK 0x00000004 /* Fast OK for battery */
++#define PCF50606_F_CHG_ERR 0x00000008 /* Charger Error */
++#define PCF50606_F_CHG_PROT 0x00000010 /* Charger Protection */
++#define PCF50606_F_CHG_READY 0x00000020 /* Charging completed */
++#define PCF50606_F_CHG_MASK 0x000000fc
++
++#define PCF50606_F_PWR_PRESSED 0x00000100
++#define PCF50606_F_RTC_SECOND 0x00000200
++
++enum close_state {
++ CLOSE_STATE_NOT,
++ CLOSE_STATE_ALLOW = 0x2342,
++};
++
++struct pcf50606_data {
++ struct i2c_client client;
++ struct pcf50606_platform_data *pdata;
++ struct backlight_device *backlight;
++ struct mutex lock;
++ unsigned int flags;
++ unsigned int working;
++ struct work_struct work;
++ struct rtc_device *rtc;
++ struct input_dev *input_dev;
++ int allow_close;
++ int onkey_seconds;
++ int irq;
++#ifdef CONFIG_PM
++ struct {
++ u_int8_t dcdc1, dcdc2;
++ u_int8_t dcdec1;
++ u_int8_t dcudc1;
++ u_int8_t ioregc;
++ u_int8_t d1regc1;
++ u_int8_t d2regc1;
++ u_int8_t d3regc1;
++ u_int8_t lpregc1;
++ u_int8_t adcc1, adcc2;
++ u_int8_t pwmc1;
++ u_int8_t int1m, int2m, int3m;
++ } standby_regs;
++#endif
++};
++
++static struct i2c_driver pcf50606_driver;
++
++struct pcf50606_data *pcf50606_global;
++EXPORT_SYMBOL(pcf50606_global);
++
++static struct platform_device *pcf50606_pdev;
++
++/* This is a mitsubishi TN11-3H103J T,B NTC Thermistor -10..79 centigrade */
++static const u_int16_t ntc_table_tn11_3h103j[] = {
++ /* -10 */
++ 40260, 38560, 36950, 35410, 33950, 32550, 31220, 29960, 28750, 27590,
++ 26490, 25440, 24440, 23480, 22560, 21680, 20830, 20020, 19240, 18500,
++ 17780, 17710, 16440, 15810, 15210, 14630, 14070, 13540, 13030, 12540,
++ 12070, 11620, 11190, 10780, 10380, 10000, 9635, 9286, 8950, 8629,
++ 8320, 8024, 7740, 7467, 7205, 6954, 6713, 6481, 6258, 6044,
++ 5839, 5641, 5451, 5269, 5093, 4924, 4762, 4605, 4455, 4310,
++ 4171, 4037, 3908, 3784, 3664, 3549, 3438, 3313, 3227, 3128,
++ 3032, 2939, 2850, 2763, 2680, 2600, 2522, 2448, 2375, 2306,
++ 2239, 2174, 2111, 2050, 1922, 1935, 1881, 1828, 1776, 1727,
++};
++
++
++/***********************************************************************
++ * Low-Level routines
++ ***********************************************************************/
++
++static inline int __reg_write(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ return i2c_smbus_write_byte_data(&pcf->client, reg, val);
++}
++
++static int reg_write(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_write(pcf, reg, val);
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static inline int32_t __reg_read(struct pcf50606_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ ret = i2c_smbus_read_byte_data(&pcf->client, reg);
++
++ return ret;
++}
++
++static u_int8_t reg_read(struct pcf50606_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_read(pcf, reg);
++ mutex_unlock(&pcf->lock);
++
++ return ret & 0xff;
++}
++
++static int reg_set_bit_mask(struct pcf50606_data *pcf,
++ u_int8_t reg, u_int8_t mask, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ val &= mask;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~mask;
++ tmp |= val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static int reg_clear_bits(struct pcf50606_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++/* synchronously read one ADC channel (busy-wait for result to be complete) */
++static u_int16_t adc_read(struct pcf50606_data *pcf, int channel,
++ u_int16_t *data2)
++{
++ u_int8_t adcs2, adcs1;
++ u_int16_t ret;
++
++ DEBUGP("entering (pcf=%p, channel=%u, data2=%p)\n",
++ pcf, channel, data2);
++
++ channel &= PCF50606_ADCC2_ADCMUX_MASK;
++
++ mutex_lock(&pcf->lock);
++
++ /* start ADC conversion of selected channel */
++ __reg_write(pcf, PCF50606_REG_ADCC2, channel |
++ PCF50606_ADCC2_ADCSTART | PCF50606_ADCC2_RES_10BIT);
++
++ do {
++ adcs2 = __reg_read(pcf, PCF50606_REG_ADCS2);
++ } while (!(adcs2 & PCF50606_ADCS2_ADCRDY));
++
++ adcs1 = __reg_read(pcf, PCF50606_REG_ADCS1);
++ ret = (adcs1 << 2) | (adcs2 & 0x03);
++
++ if (data2) {
++ adcs1 = __reg_read(pcf, PCF50606_REG_ADCS3);
++ *data2 = (adcs1 << 2) | (adcs2 & 0x0c);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("returning %u %u\n", ret, data2 ? *data2 : 0);
++
++ return ret;
++}
++
++/***********************************************************************
++ * Voltage / ADC
++ ***********************************************************************/
++
++static u_int8_t dcudc_voltage(unsigned int millivolts)
++{
++ if (millivolts < 900)
++ return 0;
++ if (millivolts > 5500)
++ return 0x1f;
++ if (millivolts <= 3300) {
++ millivolts -= 900;
++ return millivolts/300;
++ }
++ if (millivolts < 4000)
++ return 0x0f;
++ else {
++ millivolts -= 4000;
++ return millivolts/100;
++ }
++}
++
++static unsigned int dcudc_2voltage(u_int8_t bits)
++{
++ bits &= 0x1f;
++ if (bits < 0x08)
++ return 900 + bits * 300;
++ else if (bits < 0x10)
++ return 3300;
++ else
++ return 4000 + bits * 100;
++}
++
++static u_int8_t dcdec_voltage(unsigned int millivolts)
++{
++ if (millivolts < 900)
++ return 0;
++ else if (millivolts > 3300)
++ return 0x0f;
++
++ millivolts -= 900;
++ return millivolts/300;
++}
++
++static unsigned int dcdec_2voltage(u_int8_t bits)
++{
++ bits &= 0x0f;
++ return 900 + bits*300;
++}
++
++static u_int8_t dcdc_voltage(unsigned int millivolts)
++{
++ if (millivolts < 900)
++ return 0;
++ else if (millivolts > 3600)
++ return 0x1f;
++
++ if (millivolts < 1500) {
++ millivolts -= 900;
++ return millivolts/25;
++ } else {
++ millivolts -= 1500;
++ return 0x18 + millivolts/300;
++ }
++}
++
++static unsigned int dcdc_2voltage(u_int8_t bits)
++{
++ bits &= 0x1f;
++ if ((bits & 0x18) == 0x18)
++ return 1500 + ((bits & 0x7) * 300);
++ else
++ return 900 + (bits * 25);
++}
++
++static u_int8_t dx_voltage(unsigned int millivolts)
++{
++ DEBUGP("(mvolts=%u)\n", millivolts);
++
++ if (millivolts < 900)
++ return 0;
++ else if (millivolts > 3300)
++ return 0x18;
++
++ millivolts -= 900;
++ return millivolts/100;
++}
++
++static unsigned int dx_2voltage(u_int8_t bits)
++{
++ bits &= 0x1f;
++ return 900 + (bits * 100);
++}
++
++static const u_int8_t regulator_registers[__NUM_PCF50606_REGULATORS] = {
++ [PCF50606_REGULATOR_DCD] = PCF50606_REG_DCDC1,
++ [PCF50606_REGULATOR_DCDE] = PCF50606_REG_DCDEC1,
++ [PCF50606_REGULATOR_DCUD] = PCF50606_REG_DCUDC1,
++ [PCF50606_REGULATOR_D1REG] = PCF50606_REG_D1REGC1,
++ [PCF50606_REGULATOR_D2REG] = PCF50606_REG_D2REGC1,
++ [PCF50606_REGULATOR_D3REG] = PCF50606_REG_D3REGC1,
++ [PCF50606_REGULATOR_LPREG] = PCF50606_REG_LPREGC1,
++ [PCF50606_REGULATOR_IOREG] = PCF50606_REG_IOREGC,
++};
++
++int pcf50606_onoff_set(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg, int on)
++{
++ u_int8_t addr;
++
++ if (reg >= __NUM_PCF50606_REGULATORS)
++ return -EINVAL;
++
++ /* IOREG cannot be powered off since it powers the PMU I2C */
++ if (reg == PCF50606_REGULATOR_IOREG)
++ return -EIO;
++
++ addr = regulator_registers[reg];
++
++ if (on == 0)
++ reg_set_bit_mask(pcf, addr, 0xe0, 0x00);
++ else
++ reg_set_bit_mask(pcf, addr, 0xe0, 0xe0);
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50606_onoff_set);
++
++int pcf50606_onoff_get(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg)
++{
++ u_int8_t val, addr;
++
++ if (reg >= __NUM_PCF50606_REGULATORS)
++ return -EINVAL;
++
++ addr = regulator_registers[reg];
++ val = (reg_read(pcf, addr) & 0xe0) >> 5;
++
++ /* PWREN1 = 1, PWREN2 = 1, see table 16 of datasheet */
++ switch (val) {
++ case 0:
++ case 5:
++ return 0;
++ default:
++ return 1;
++ }
++}
++EXPORT_SYMBOL(pcf50606_onoff_get);
++
++int pcf50606_voltage_set(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg,
++ unsigned int millivolts)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++ int rc;
++
++ DEBUGP("pcf=%p, reg=%d, mvolts=%d\n", pcf, reg, millivolts);
++
++ if (reg >= __NUM_PCF50606_REGULATORS)
++ return -EINVAL;
++
++ if (millivolts > pcf->pdata->rails[reg].voltage.max)
++ return -EINVAL;
++
++ switch (reg) {
++ case PCF50606_REGULATOR_DCD:
++ volt_bits = dcdc_voltage(millivolts);
++ rc = reg_set_bit_mask(pcf, PCF50606_REG_DCDC1, 0x1f,
++ volt_bits);
++ break;
++ case PCF50606_REGULATOR_DCDE:
++ volt_bits = dcdec_voltage(millivolts);
++ rc = reg_set_bit_mask(pcf, PCF50606_REG_DCDEC1, 0x0f,
++ volt_bits);
++ break;
++ case PCF50606_REGULATOR_DCUD:
++ volt_bits = dcudc_voltage(millivolts);
++ rc = reg_set_bit_mask(pcf, PCF50606_REG_DCUDC1, 0x1f,
++ volt_bits);
++ break;
++ case PCF50606_REGULATOR_D1REG:
++ case PCF50606_REGULATOR_D2REG:
++ case PCF50606_REGULATOR_D3REG:
++ regnr = PCF50606_REG_D1REGC1 + (reg - PCF50606_REGULATOR_D1REG);
++ volt_bits = dx_voltage(millivolts);
++ DEBUGP("dx_voltage(0x%x)=%u\n", millivolts, volt_bits);
++ rc = reg_set_bit_mask(pcf, regnr, 0x1f, volt_bits);
++ break;
++ case PCF50606_REGULATOR_LPREG:
++ volt_bits = dx_voltage(millivolts);
++ rc = reg_set_bit_mask(pcf, PCF50606_REG_LPREGC1, 0x1f,
++ volt_bits);
++ break;
++ case PCF50606_REGULATOR_IOREG:
++ if (millivolts < 1800)
++ return -EINVAL;
++ volt_bits = dx_voltage(millivolts);
++ rc = reg_set_bit_mask(pcf, PCF50606_REG_IOREGC, 0x1f,
++ volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return rc;
++}
++EXPORT_SYMBOL(pcf50606_voltage_set);
++
++unsigned int pcf50606_voltage_get(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++ unsigned int rc = 0;
++
++ if (reg >= __NUM_PCF50606_REGULATORS)
++ return -EINVAL;
++
++ switch (reg) {
++ case PCF50606_REGULATOR_DCD:
++ volt_bits = reg_read(pcf, PCF50606_REG_DCDC1) & 0x1f;
++ rc = dcdc_2voltage(volt_bits);
++ break;
++ case PCF50606_REGULATOR_DCDE:
++ volt_bits = reg_read(pcf, PCF50606_REG_DCDEC1) & 0x0f;
++ rc = dcdec_2voltage(volt_bits);
++ break;
++ case PCF50606_REGULATOR_DCUD:
++ volt_bits = reg_read(pcf, PCF50606_REG_DCUDC1) & 0x1f;
++ rc = dcudc_2voltage(volt_bits);
++ break;
++ case PCF50606_REGULATOR_D1REG:
++ case PCF50606_REGULATOR_D2REG:
++ case PCF50606_REGULATOR_D3REG:
++ regnr = PCF50606_REG_D1REGC1 + (reg - PCF50606_REGULATOR_D1REG);
++ volt_bits = reg_read(pcf, regnr) & 0x1f;
++ if (volt_bits > 0x18)
++ volt_bits = 0x18;
++ rc = dx_2voltage(volt_bits);
++ break;
++ case PCF50606_REGULATOR_LPREG:
++ volt_bits = reg_read(pcf, PCF50606_REG_LPREGC1) & 0x1f;
++ if (volt_bits > 0x18)
++ volt_bits = 0x18;
++ rc = dx_2voltage(volt_bits);
++ break;
++ case PCF50606_REGULATOR_IOREG:
++ volt_bits = reg_read(pcf, PCF50606_REG_IOREGC) & 0x1f;
++ if (volt_bits > 0x18)
++ volt_bits = 0x18;
++ rc = dx_2voltage(volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return rc;
++}
++EXPORT_SYMBOL(pcf50606_voltage_get);
++
++/* go into 'STANDBY' mode, i.e. power off the main CPU and peripherals */
++void pcf50606_go_standby(void)
++{
++ reg_write(pcf50606_global, PCF50606_REG_OOCC1,
++ PCF50606_OOCC1_GOSTDBY);
++}
++EXPORT_SYMBOL(pcf50606_go_standby);
++
++void pcf50606_gpo0_set(struct pcf50606_data *pcf, int on)
++{
++ u_int8_t val;
++
++ if (on)
++ val = 0x07;
++ else
++ val = 0x0f;
++
++ reg_set_bit_mask(pcf, PCF50606_REG_GPOC1, 0x0f, val);
++}
++EXPORT_SYMBOL(pcf50606_gpo0_set);
++
++int pcf50606_gpo0_get(struct pcf50606_data *pcf)
++{
++ u_int8_t reg = reg_read(pcf, PCF50606_REG_GPOC1) & 0x0f;
++
++ if (reg == 0x07 || reg == 0x08)
++ return 1;
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50606_gpo0_get);
++
++static void pcf50606_work(struct work_struct *work)
++{
++ struct pcf50606_data *pcf =
++ container_of(work, struct pcf50606_data, work);
++ u_int8_t int1, int2, int3;
++
++ pcf->working = 1;
++
++ int1 = __reg_read(pcf, PCF50606_REG_INT1);
++ int2 = __reg_read(pcf, PCF50606_REG_INT2);
++ int3 = __reg_read(pcf, PCF50606_REG_INT3);
++
++ DEBUGP("INT1=0x%02x INT2=0x%02x INT3=0x%02x:", int1, int2, int3);
++
++ if (int1 & PCF50606_INT1_ONKEYF) {
++ /* ONKEY falling edge (start of button press) */
++ DEBUGPC("ONKEYF ");
++ pcf->flags |= PCF50606_F_PWR_PRESSED;
++ input_report_key(pcf->input_dev, KEY_POWER, 1);
++ }
++ if (int1 & PCF50606_INT1_ONKEY1S) {
++ /* ONKEY pressed for more than 1 second */
++ pcf->onkey_seconds = 0;
++ DEBUGPC("ONKEY1S ");
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50606_REG_OOCC1,
++ PCF50606_OOCC1_TOTRST,
++ PCF50606_OOCC1_TOTRST);
++ /* enable SECOND interrupt (hz tick) */
++ reg_clear_bits(pcf, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
++ }
++ if (int1 & PCF50606_INT1_ONKEYR) {
++ /* ONKEY rising edge (end of button press) */
++ DEBUGPC("ONKEYR ");
++ pcf->flags &= ~PCF50606_F_PWR_PRESSED;
++ pcf->onkey_seconds = -1;
++ input_report_key(pcf->input_dev, KEY_POWER, 0);
++ /* disable SECOND interrupt in case RTC didn't
++ * request it */
++ if (!(pcf->flags & PCF50606_F_RTC_SECOND))
++ reg_set_bit_mask(pcf, PCF50606_REG_INT1M,
++ PCF50606_INT1_SECOND,
++ PCF50606_INT1_SECOND);
++ }
++ if (int1 & PCF50606_INT1_EXTONR) {
++ DEBUGPC("EXTONR ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 1);
++ }
++ if (int1 & PCF50606_INT1_EXTONF) {
++ DEBUGPC("EXTONF ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 0);
++ }
++ if (int1 & PCF50606_INT1_SECOND) {
++ DEBUGPC("SECOND ");
++ if (pcf->flags & PCF50606_F_RTC_SECOND)
++ rtc_update_irq(pcf->rtc, 1,
++ RTC_PF | RTC_IRQF);
++
++ if (pcf->onkey_seconds >= 0 &&
++ pcf->flags & PCF50606_F_PWR_PRESSED) {
++ DEBUGP("ONKEY_SECONDS(%u, OOCC1=0x%02x) ",
++ pcf->onkey_seconds, reg_read(pcf, PCF50606_REG_OOCC1));
++ pcf->onkey_seconds++;
++ if (pcf->onkey_seconds >=
++ pcf->pdata->onkey_seconds_required) {
++ /* Ask init to do 'ctrlaltdel' */
++ DEBUGPC("SIGINT(init) ");
++ kill_proc(1, SIGINT, 1);
++ /* FIXME: what if userspace doesn't shut down? */
++ }
++ }
++ }
++ if (int1 & PCF50606_INT1_ALARM) {
++ DEBUGPC("ALARM ");
++ if (pcf->pdata->used_features & PCF50606_FEAT_RTC)
++ rtc_update_irq(pcf->rtc, 1,
++ RTC_AF | RTC_IRQF);
++ }
++
++ if (int2 & PCF50606_INT2_CHGINS) {
++ /* Charger inserted */
++ DEBUGPC("CHGINS ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 1);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags |= PCF50606_F_CHG_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50606_FEAT_MBC, PMU_EVT_INSERT);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_ADD);
++ }
++ if (int2 & PCF50606_INT2_CHGRM) {
++ /* Charger removed */
++ DEBUGPC("CHGRM ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 0);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags &= ~(PCF50606_F_CHG_MASK|PCF50606_F_CHG_PRESENT);
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50606_FEAT_MBC, PMU_EVT_INSERT);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_REMOVE);
++ }
++ if (int2 & PCF50606_INT2_CHGFOK) {
++ /* Battery ready for fast charging */
++ DEBUGPC("CHGFOK ");
++ pcf->flags |= PCF50606_F_CHG_FOK;
++ /* FIXME: signal this to userspace */
++ }
++ if (int2 & PCF50606_INT2_CHGERR) {
++ /* Error in charge mode */
++ DEBUGPC("CHGERR ");
++ pcf->flags |= PCF50606_F_CHG_ERR;
++ pcf->flags &= ~(PCF50606_F_CHG_FOK|PCF50606_F_CHG_READY);
++ /* FIXME: signal this to userspace */
++ }
++ if (int2 & PCF50606_INT2_CHGFRDY) {
++ /* Fast charge completed */
++ DEBUGPC("CHGFRDY ");
++ pcf->flags |= PCF50606_F_CHG_READY;
++ pcf->flags &= ~PCF50606_F_CHG_FOK;
++ /* FIXME: signal this to userspace */
++ }
++ if (int2 & PCF50606_INT2_CHGPROT) {
++ /* Charging protection interrupt */
++ DEBUGPC("CHGPROT ");
++ pcf->flags &= ~(PCF50606_F_CHG_FOK|PCF50606_F_CHG_READY);
++ /* FIXME: signal this to userspace */
++ }
++ if (int2 & PCF50606_INT2_CHGWD10S) {
++ /* Charger watchdog will expire in 10 seconds */
++ DEBUGPC("CHGWD10S ");
++ reg_set_bit_mask(pcf, PCF50606_REG_OOCC1,
++ PCF50606_OOCC1_WDTRST,
++ PCF50606_OOCC1_WDTRST);
++ }
++ if (int2 & PCF50606_INT2_CHGWDEXP) {
++ /* Charger watchdog expires */
++ DEBUGPC("CHGWDEXP ");
++ /* FIXME: signal this to userspace */
++ }
++
++ if (int3 & PCF50606_INT3_ADCRDY) {
++ /* ADC result ready */
++ DEBUGPC("ADCRDY ");
++ }
++ if (int3 & PCF50606_INT3_ACDINS) {
++ /* Accessory insertion detected */
++ DEBUGP("ACDINS ");
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50606_FEAT_ACD, PMU_EVT_INSERT);
++ }
++ if (int3 & PCF50606_INT3_ACDREM) {
++ /* Accessory removal detected */
++ DEBUGP("ACDREM ");
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50606_FEAT_ACD, PMU_EVT_REMOVE);
++ }
++ /* FIXME: TSCPRES */
++ if (int3 & PCF50606_INT3_LOWBAT) {
++ /* Really low battery voltage, we have 8 seconds left */
++ DEBUGPC("LOWBAT ");
++ apm_queue_event(APM_LOW_BATTERY);
++ DEBUGPC("SIGPWR(init) ");
++ kill_proc(1, SIGPWR, 1);
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50606_REG_OOCC1,
++ PCF50606_OOCC1_TOTRST,
++ PCF50606_OOCC1_TOTRST);
++ }
++ if (int3 & PCF50606_INT3_HIGHTMP) {
++ /* High temperature */
++ DEBUGPC("HIGHTMP ");
++ apm_queue_event(APM_CRITICAL_SUSPEND);
++ }
++
++ DEBUGPC("\n");
++
++ pcf->working = 0;
++ input_sync(pcf->input_dev);
++ put_device(&pcf->client.dev);
++
++ enable_irq(pcf->irq);
++}
++
++static void pcf50606_schedule_work(struct pcf50606_data *pcf)
++{
++ int status;
++
++ get_device(&pcf->client.dev);
++ status = schedule_work(&pcf->work);
++ if (!status && !pcf->working)
++ dev_dbg(&pcf->client.dev, "work item may be lost\n");
++}
++
++
++static irqreturn_t pcf50606_irq(int irq, void *_pcf)
++{
++ struct pcf50606_data *pcf = _pcf;
++
++ DEBUGP("entering(irq=%u, pcf=%p): scheduling work\n",
++ irq, _pcf);
++ pcf50606_schedule_work(pcf);
++
++ /* Disable any further interrupts until we have processed
++ * the current one */
++ disable_irq(irq);
++
++ return IRQ_HANDLED;
++}
++
++static u_int16_t adc_to_batt_millivolts(u_int16_t adc)
++{
++ u_int16_t mvolts;
++
++ mvolts = (adc * 6000) / 1024;
++
++ return mvolts;
++}
++
++#define BATTVOLT_SCALE_START 2800
++#define BATTVOLT_SCALE_END 4200
++#define BATTVOLT_SCALE_DIVIDER ((BATTVOLT_SCALE_END - BATTVOLT_SCALE_START)/100)
++
++static u_int8_t battvolt_scale(u_int16_t battvolt)
++{
++ /* FIXME: this linear scale is completely bogus */
++ u_int16_t battvolt_relative = battvolt - BATTVOLT_SCALE_START;
++ unsigned int percent = battvolt_relative / BATTVOLT_SCALE_DIVIDER;
++
++ return percent;
++}
++
++u_int16_t pcf50606_battvolt(struct pcf50606_data *pcf)
++{
++ u_int16_t adc;
++ adc = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_RES, NULL);
++
++ return adc_to_batt_millivolts(adc);
++}
++EXPORT_SYMBOL(pcf50606_battvolt);
++
++static ssize_t show_battvolt(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++
++ return sprintf(buf, "%u\n", pcf50606_battvolt(pcf));
++}
++static DEVICE_ATTR(battvolt, S_IRUGO | S_IWUSR, show_battvolt, NULL);
++
++static int reg_id_by_name(const char *name)
++{
++ int reg_id;
++
++ if (!strcmp(name, "voltage_dcd"))
++ reg_id = PCF50606_REGULATOR_DCD;
++ else if (!strcmp(name, "voltage_dcde"))
++ reg_id = PCF50606_REGULATOR_DCDE;
++ else if (!strcmp(name, "voltage_dcud"))
++ reg_id = PCF50606_REGULATOR_DCUD;
++ else if (!strcmp(name, "voltage_d1reg"))
++ reg_id = PCF50606_REGULATOR_D1REG;
++ else if (!strcmp(name, "voltage_d2reg"))
++ reg_id = PCF50606_REGULATOR_D2REG;
++ else if (!strcmp(name, "voltage_d3reg"))
++ reg_id = PCF50606_REGULATOR_D3REG;
++ else if (!strcmp(name, "voltage_lpreg"))
++ reg_id = PCF50606_REGULATOR_LPREG;
++ else if (!strcmp(name, "voltage_ioreg"))
++ reg_id = PCF50606_REGULATOR_IOREG;
++ else
++ reg_id = -1;
++
++ return reg_id;
++}
++
++static ssize_t show_vreg(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return 0;
++
++ if (pcf50606_onoff_get(pcf, reg_id) > 0)
++ return sprintf(buf, "%u\n", pcf50606_voltage_get(pcf, reg_id));
++ else
++ return strlcpy(buf, "0\n", PAGE_SIZE);
++}
++
++static ssize_t set_vreg(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ unsigned long mvolts = simple_strtoul(buf, NULL, 10);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return -EIO;
++
++ DEBUGP("attempting to set %s(%d) to %lu mvolts\n", attr->attr.name,
++ reg_id, mvolts);
++
++ if (mvolts == 0) {
++ pcf50606_onoff_set(pcf, reg_id, 0);
++ } else {
++ if (pcf50606_voltage_set(pcf, reg_id, mvolts) < 0) {
++ dev_warn(dev, "refusing to set %s(%d) to %lu mvolts "
++ "(max=%u)\n", attr->attr.name, reg_id, mvolts,
++ pcf->pdata->rails[reg_id].voltage.max);
++ return -EINVAL;
++ }
++ pcf50606_onoff_set(pcf, reg_id, 1);
++ }
++
++ return count;
++}
++
++static DEVICE_ATTR(voltage_dcd, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_dcde, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_dcud, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d1reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d2reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_d3reg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_lpreg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ioreg, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++
++/***********************************************************************
++ * Charger Control
++ ***********************************************************************/
++
++/* Enable/disable fast charging (500mA in the GTA01) */
++void pcf50606_charge_fast(struct pcf50606_data *pcf, int on)
++{
++ if (!(pcf->pdata->used_features & PCF50606_FEAT_MBC))
++ return;
++
++ if (on) {
++ /* We can allow PCF to automatically charge
++ * using Ifast */
++ pcf->flags |= PCF50606_F_CHG_FAST;
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST,
++ PCF50606_MBCC1_AUTOFST);
++ } else {
++ pcf->flags &= ~PCF50606_F_CHG_FAST;
++ /* disable automatic fast-charge */
++ reg_clear_bits(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST);
++ /* switch to idle mode to abort existing charge
++ * process */
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_CHGMOD_MASK,
++ PCF50606_MBCC1_CHGMOD_IDLE);
++ }
++}
++EXPORT_SYMBOL(pcf50606_charge_fast);
++
++#define ONE 1000000
++static inline u_int16_t adc_to_rntc(struct pcf50606_data *pcf, u_int16_t adc)
++{
++ u_int32_t r_batt = (adc * pcf->pdata->r_fix_batt) / (1023 - adc);
++ u_int16_t r_ntc;
++
++ /* The battery NTC has a parallell 10kOhms resistor */
++ r_ntc = ONE / ((ONE/r_batt) - (ONE/pcf->pdata->r_fix_batt_par));
++
++ return r_ntc;
++}
++
++static inline int16_t rntc_to_temp(u_int16_t rntc)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(ntc_table_tn11_3h103j); i++) {
++ if (rntc > ntc_table_tn11_3h103j[i])
++ return i - 10;
++ }
++ return 2342;
++}
++
++static ssize_t show_battemp(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int16_t adc;
++
++ adc = adc_read(pcf, PCF50606_ADCMUX_BATTEMP, NULL);
++
++ return sprintf(buf, "%d\n", rntc_to_temp(adc_to_rntc(pcf, adc)));
++}
++static DEVICE_ATTR(battemp, S_IRUGO | S_IWUSR, show_battemp, NULL);
++
++static inline u_int16_t adc_to_chg_milliamps(struct pcf50606_data *pcf,
++ u_int16_t adc_adcin1,
++ u_int16_t adc_batvolt)
++{
++ u_int32_t res = ((adc_adcin1 - adc_batvolt) * 6000);
++ return res / (pcf->pdata->r_sense_milli * 1024 / 1000);
++}
++
++static ssize_t show_chgcur(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int16_t adc_batvolt, adc_adcin1;
++ u_int16_t ma;
++
++ adc_batvolt = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_ADCIN1,
++ &adc_adcin1);
++ ma = adc_to_chg_milliamps(pcf, adc_adcin1, adc_batvolt);
++
++ return sprintf(buf, "%u\n", ma);
++}
++static DEVICE_ATTR(chgcur, S_IRUGO | S_IWUSR, show_chgcur, NULL);
++
++static const char *chgmode_names[] = {
++ [PCF50606_MBCC1_CHGMOD_QUAL] = "qualification",
++ [PCF50606_MBCC1_CHGMOD_PRE] = "pre",
++ [PCF50606_MBCC1_CHGMOD_TRICKLE] = "trickle",
++ [PCF50606_MBCC1_CHGMOD_FAST_CCCV] = "fast_cccv",
++ [PCF50606_MBCC1_CHGMOD_FAST_NOCC] = "fast_nocc",
++ [PCF50606_MBCC1_CHGMOD_FAST_NOCV] = "fast_nocv",
++ [PCF50606_MBCC1_CHGMOD_FAST_SW] = "fast_switch",
++ [PCF50606_MBCC1_CHGMOD_IDLE] = "idle",
++};
++
++static ssize_t show_chgmode(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++ u_int8_t chgmod = (mbcc1 & PCF50606_MBCC1_CHGMOD_MASK);
++
++ return sprintf(buf, "%s\n", chgmode_names[chgmod]);
++}
++
++static ssize_t set_chgmode(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++
++ mbcc1 &= ~PCF50606_MBCC1_CHGMOD_MASK;
++
++ if (!strcmp(buf, "qualification"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_QUAL;
++ else if (!strcmp(buf, "pre"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_PRE;
++ else if (!strcmp(buf, "trickle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_TRICKLE;
++ else if (!strcmp(buf, "fast_cccv"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_FAST_CCCV;
++ /* We don't allow the other fast modes for security reasons */
++ else if (!strcmp(buf, "idle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_IDLE;
++ else
++ return -EINVAL;
++
++ reg_write(pcf, PCF50606_REG_MBCC1, mbcc1);
++
++ return count;
++}
++
++static DEVICE_ATTR(chgmode, S_IRUGO | S_IWUSR, show_chgmode, set_chgmode);
++
++static const char *chgstate_names[] = {
++ [PCF50606_F_CHG_FAST] = "fast_enabled",
++ [PCF50606_F_CHG_PRESENT] = "present",
++ [PCF50606_F_CHG_FOK] = "fast_ok",
++ [PCF50606_F_CHG_ERR] = "error",
++ [PCF50606_F_CHG_PROT] = "protection",
++ [PCF50606_F_CHG_READY] = "ready",
++};
++
++static ssize_t show_chgstate(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ char *b = buf;
++ int i;
++
++ for (i = 0; i < 32; i++)
++ if (pcf->flags & (1 << i) && i < ARRAY_SIZE(chgstate_names))
++ b += sprintf(b, "%s ", chgstate_names[i]);
++
++ if (b > buf)
++ b += sprintf(b, "\n");
++
++ return b - buf;
++}
++static DEVICE_ATTR(chgstate, S_IRUGO | S_IWUSR, show_chgstate, NULL);
++
++/***********************************************************************
++ * APM emulation
++ ***********************************************************************/
++
++extern void (*apm_get_power_status)(struct apm_power_info *);
++
++static void pcf50606_get_power_status(struct apm_power_info *info)
++{
++ struct pcf50606_data *pcf = pcf50606_global;
++ u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++ u_int8_t chgmod = mbcc1 & PCF50606_MBCC1_CHGMOD_MASK;
++ u_int16_t battvolt = pcf50606_battvolt(pcf);
++
++ if (reg_read(pcf, PCF50606_REG_OOCS) & PCF50606_OOCS_EXTON)
++ info->ac_line_status = APM_AC_ONLINE;
++ else
++ info->ac_line_status = APM_AC_OFFLINE;
++
++ switch (chgmod) {
++ case PCF50606_MBCC1_CHGMOD_QUAL:
++ case PCF50606_MBCC1_CHGMOD_PRE:
++ case PCF50606_MBCC1_CHGMOD_IDLE:
++ info->battery_life = battvolt_scale(battvolt);
++ break;
++ default:
++ info->battery_status = APM_BATTERY_STATUS_CHARGING;
++ info->battery_flag = APM_BATTERY_FLAG_CHARGING;
++ break;
++ }
++}
++
++/***********************************************************************
++ * RTC
++ ***********************************************************************/
++
++struct pcf50606_time {
++ u_int8_t sec;
++ u_int8_t min;
++ u_int8_t hour;
++ u_int8_t wkday;
++ u_int8_t day;
++ u_int8_t month;
++ u_int8_t year;
++};
++
++static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50606_time *pcf)
++{
++ rtc->tm_sec = BCD2BIN(pcf->sec);
++ rtc->tm_min = BCD2BIN(pcf->min);
++ rtc->tm_hour = BCD2BIN(pcf->hour);
++ rtc->tm_wday = BCD2BIN(pcf->wkday);
++ rtc->tm_mday = BCD2BIN(pcf->day);
++ rtc->tm_mon = BCD2BIN(pcf->month);
++ rtc->tm_year = BCD2BIN(pcf->year) + 100;
++}
++
++static void rtc2pcf_time(struct pcf50606_time *pcf, struct rtc_time *rtc)
++{
++ pcf->sec = BIN2BCD(rtc->tm_sec);
++ pcf->min = BIN2BCD(rtc->tm_min);
++ pcf->hour = BIN2BCD(rtc->tm_hour);
++ pcf->wkday = BIN2BCD(rtc->tm_wday);
++ pcf->day = BIN2BCD(rtc->tm_mday);
++ pcf->month = BIN2BCD(rtc->tm_mon);
++ pcf->year = BIN2BCD(rtc->tm_year - 100);
++}
++
++static int pcf50606_rtc_ioctl(struct device *dev, unsigned int cmd,
++ unsigned long arg)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ switch (cmd) {
++ case RTC_PIE_OFF:
++ /* disable periodic interrupt (hz tick) */
++ pcf->flags &= ~PCF50606_F_RTC_SECOND;
++ reg_set_bit_mask(pcf, PCF50606_REG_INT1M,
++ PCF50606_INT1_SECOND, PCF50606_INT1_SECOND);
++ return 0;
++ case RTC_PIE_ON:
++ /* ensable periodic interrupt (hz tick) */
++ pcf->flags |= PCF50606_F_RTC_SECOND;
++ reg_clear_bits(pcf, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
++ return 0;
++ }
++ return -ENOIOCTLCMD;
++}
++
++static int pcf50606_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ struct pcf50606_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50606_REG_RTCSC);
++ pcf_tm.min = __reg_read(pcf, PCF50606_REG_RTCMN);
++ pcf_tm.hour = __reg_read(pcf, PCF50606_REG_RTCHR);
++ pcf_tm.wkday = __reg_read(pcf, PCF50606_REG_RTCWD);
++ pcf_tm.day = __reg_read(pcf, PCF50606_REG_RTCDT);
++ pcf_tm.month = __reg_read(pcf, PCF50606_REG_RTCMT);
++ pcf_tm.year = __reg_read(pcf, PCF50606_REG_RTCYR);
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ pcf2rtc_time(tm, &pcf_tm);
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++
++ return 0;
++}
++
++static int pcf50606_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ struct pcf50606_time pcf_tm;
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++ rtc2pcf_time(&pcf_tm, tm);
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ mutex_lock(&pcf->lock);
++ /* FIXME: disable second interrupt */
++ __reg_write(pcf, PCF50606_REG_RTCSC, pcf_tm.sec);
++ __reg_write(pcf, PCF50606_REG_RTCMN, pcf_tm.min);
++ __reg_write(pcf, PCF50606_REG_RTCHR, pcf_tm.hour);
++ __reg_write(pcf, PCF50606_REG_RTCWD, pcf_tm.wkday);
++ __reg_write(pcf, PCF50606_REG_RTCDT, pcf_tm.day);
++ __reg_write(pcf, PCF50606_REG_RTCMT, pcf_tm.month);
++ __reg_write(pcf, PCF50606_REG_RTCYR, pcf_tm.year);
++ /* FIXME: re-enable second interrupt */
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50606_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ struct pcf50606_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50606_REG_RTCSCA);
++ pcf_tm.min = __reg_read(pcf, PCF50606_REG_RTCMNA);
++ pcf_tm.hour = __reg_read(pcf, PCF50606_REG_RTCHRA);
++ pcf_tm.wkday = __reg_read(pcf, PCF50606_REG_RTCWDA);
++ pcf_tm.day = __reg_read(pcf, PCF50606_REG_RTCDTA);
++ pcf_tm.month = __reg_read(pcf, PCF50606_REG_RTCMTA);
++ pcf_tm.year = __reg_read(pcf, PCF50606_REG_RTCYRA);
++ mutex_unlock(&pcf->lock);
++
++ pcf2rtc_time(&alrm->time, &pcf_tm);
++
++ return 0;
++}
++
++static int pcf50606_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ struct pcf50606_time pcf_tm;
++ u_int8_t irqmask;
++
++ rtc2pcf_time(&pcf_tm, &alrm->time);
++
++ mutex_lock(&pcf->lock);
++
++ /* disable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50606_REG_INT1M);
++ irqmask |= PCF50606_INT1_ALARM;
++ __reg_write(pcf, PCF50606_REG_INT1M, irqmask);
++
++ __reg_write(pcf, PCF50606_REG_RTCSCA, pcf_tm.sec);
++ __reg_write(pcf, PCF50606_REG_RTCMNA, pcf_tm.min);
++ __reg_write(pcf, PCF50606_REG_RTCHRA, pcf_tm.hour);
++ __reg_write(pcf, PCF50606_REG_RTCWDA, pcf_tm.wkday);
++ __reg_write(pcf, PCF50606_REG_RTCDTA, pcf_tm.day);
++ __reg_write(pcf, PCF50606_REG_RTCMTA, pcf_tm.month);
++ __reg_write(pcf, PCF50606_REG_RTCYRA, pcf_tm.year);
++
++ if (alrm->enabled) {
++ /* (re-)enaable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50606_REG_INT1M);
++ irqmask &= ~PCF50606_INT1_ALARM;
++ __reg_write(pcf, PCF50606_REG_INT1M, irqmask);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ /* FIXME */
++ return 0;
++}
++
++static struct rtc_class_ops pcf50606_rtc_ops = {
++ .ioctl = pcf50606_rtc_ioctl,
++ .read_time = pcf50606_rtc_read_time,
++ .set_time = pcf50606_rtc_set_time,
++ .read_alarm = pcf50606_rtc_read_alarm,
++ .set_alarm = pcf50606_rtc_set_alarm,
++};
++
++/***********************************************************************
++ * Watchdog
++ ***********************************************************************/
++
++static void pcf50606_wdt_start(struct pcf50606_data *pcf)
++{
++ reg_set_bit_mask(pcf, PCF50606_REG_OOCC1, PCF50606_OOCC1_WDTRST,
++ PCF50606_OOCC1_WDTRST);
++}
++
++static void pcf50606_wdt_stop(struct pcf50606_data *pcf)
++{
++ reg_clear_bits(pcf, PCF50606_REG_OOCS, PCF50606_OOCS_WDTEXP);
++}
++
++static void pcf50606_wdt_keepalive(struct pcf50606_data *pcf)
++{
++ pcf50606_wdt_start(pcf);
++}
++
++static int pcf50606_wdt_open(struct inode *inode, struct file *file)
++{
++ struct pcf50606_data *pcf = pcf50606_global;
++
++ file->private_data = pcf;
++
++ /* start the timer */
++ pcf50606_wdt_start(pcf);
++
++ return nonseekable_open(inode, file);
++}
++
++static int pcf50606_wdt_release(struct inode *inode, struct file *file)
++{
++ struct pcf50606_data *pcf = file->private_data;
++
++ if (pcf->allow_close == CLOSE_STATE_ALLOW)
++ pcf50606_wdt_stop(pcf);
++ else {
++ printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n");
++ pcf50606_wdt_keepalive(pcf);
++ }
++
++ pcf->allow_close = CLOSE_STATE_NOT;
++
++ return 0;
++}
++
++static ssize_t pcf50606_wdt_write(struct file *file, const char __user *data,
++ size_t len, loff_t *ppos)
++{
++ struct pcf50606_data *pcf = file->private_data;
++ if (len) {
++ size_t i;
++
++ for (i = 0; i != len; i++) {
++ char c;
++ if (get_user(c, data + i))
++ return -EFAULT;
++ if (c == 'V')
++ pcf->allow_close = CLOSE_STATE_ALLOW;
++ }
++ pcf50606_wdt_keepalive(pcf);
++ }
++
++ return len;
++}
++
++static struct watchdog_info pcf50606_wdt_ident = {
++ .options = WDIOF_MAGICCLOSE,
++ .firmware_version = 0,
++ .identity = "PCF50606 Watchdog",
++};
++
++static int pcf50606_wdt_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct pcf50606_data *pcf = file->private_data;
++ void __user *argp = (void __user *)arg;
++ int __user *p = argp;
++
++ switch (cmd) {
++ case WDIOC_GETSUPPORT:
++ return copy_to_user(argp, &pcf50606_wdt_ident,
++ sizeof(pcf50606_wdt_ident)) ? -EFAULT : 0;
++ break;
++ case WDIOC_GETSTATUS:
++ case WDIOC_GETBOOTSTATUS:
++ return put_user(0, p);
++ case WDIOC_KEEPALIVE:
++ pcf50606_wdt_keepalive(pcf);
++ return 0;
++ case WDIOC_GETTIMEOUT:
++ return put_user(8, p);
++ default:
++ return -ENOIOCTLCMD;
++ }
++}
++
++static struct file_operations pcf50606_wdt_fops = {
++ .owner = THIS_MODULE,
++ .llseek = no_llseek,
++ .write = &pcf50606_wdt_write,
++ .ioctl = &pcf50606_wdt_ioctl,
++ .open = &pcf50606_wdt_open,
++ .release = &pcf50606_wdt_release,
++};
++
++static struct miscdevice pcf50606_wdt_miscdev = {
++ .minor = WATCHDOG_MINOR,
++ .name = "watchdog",
++ .fops = &pcf50606_wdt_fops,
++};
++
++/***********************************************************************
++ * PWM
++ ***********************************************************************/
++
++static const char *pwm_dc_table[] = {
++ "0/16", "1/16", "2/16", "3/16",
++ "4/16", "5/16", "6/16", "7/16",
++ "8/16", "9/16", "10/16","11/16",
++ "12/16", "13/16", "14/16", "15/16",
++};
++
++static ssize_t show_pwm_dc(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t val;
++
++ val = reg_read(pcf, PCF50606_REG_PWMC1) >> PCF50606_PWMC1_DC_SHIFT;
++ val &= 0xf;
++
++ return sprintf(buf, "%s\n", pwm_dc_table[val]);
++}
++
++static ssize_t set_pwm_dc(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t i;
++
++ for (i = 0; i < ARRAY_SIZE(pwm_dc_table); i++) {
++ if (!strncmp(buf, pwm_dc_table[i], strlen(pwm_dc_table[i]))) {
++ DEBUGP("setting pwm dc %s\n\r", pwm_dc_table[i]);
++ reg_set_bit_mask(pcf, PCF50606_REG_PWMC1, 0x1e,
++ (i << PCF50606_PWMC1_DC_SHIFT));
++ }
++ }
++ return count;
++}
++
++static DEVICE_ATTR(pwm_dc, S_IRUGO | S_IWUSR, show_pwm_dc, set_pwm_dc);
++
++static const char *pwm_clk_table[] = {
++ "512", "256", "128", "64",
++ "56300", "28100", "14100", "7000",
++};
++
++static ssize_t show_pwm_clk(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t val;
++
++ val = reg_read(pcf, PCF50606_REG_PWMC1) >> PCF50606_PWMC1_CLK_SHIFT;
++ val &= 0x7;
++
++ return sprintf(buf, "%s\n", pwm_clk_table[val]);
++}
++
++static ssize_t set_pwm_clk(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ u_int8_t i;
++
++ for (i = 0; i < ARRAY_SIZE(pwm_clk_table); i++) {
++ if (!strncmp(buf, pwm_clk_table[i], strlen(pwm_clk_table[i]))) {
++ DEBUGP("setting pwm clk %s\n\r", pwm_clk_table[i]);
++ reg_set_bit_mask(pcf, PCF50606_REG_PWMC1, 0xe0,
++ (i << PCF50606_PWMC1_CLK_SHIFT));
++ }
++ }
++ return count;
++}
++
++static DEVICE_ATTR(pwm_clk, S_IRUGO | S_IWUSR, show_pwm_clk, set_pwm_clk);
++
++static int pcf50606bl_get_intensity(struct backlight_device *bd)
++{
++ struct pcf50606_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = reg_read(pcf, PCF50606_REG_PWMC1);
++ intensity = (intensity >> PCF50606_PWMC1_DC_SHIFT);
++
++ return intensity & 0xf;
++}
++
++static int pcf50606bl_set_intensity(struct backlight_device *bd)
++{
++ struct pcf50606_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = bd->props.brightness;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ return reg_set_bit_mask(pcf, PCF50606_REG_PWMC1, 0x1e,
++ (intensity << PCF50606_PWMC1_DC_SHIFT));
++}
++
++static struct backlight_ops pcf50606bl_ops = {
++ .get_brightness = pcf50606bl_get_intensity,
++ .update_status = pcf50606bl_set_intensity,
++};
++
++/***********************************************************************
++ * Driver initialization
++ ***********************************************************************/
++
++#ifdef CONFIG_MACH_NEO1973_GTA01
++/* We currently place those platform devices here to make sure the device
++ * suspend/resume order is correct */
++static struct platform_device gta01_pm_gps_dev = {
++ .name ="gta01-pm-gps",
++};
++
++static struct platform_device gta01_pm_bt_dev = {
++ .name ="gta01-pm-bt",
++};
++#endif
++
++static struct attribute *pcf_sysfs_entries[16] = {
++ &dev_attr_voltage_dcd.attr,
++ &dev_attr_voltage_dcde.attr,
++ &dev_attr_voltage_dcud.attr,
++ &dev_attr_voltage_d1reg.attr,
++ &dev_attr_voltage_d2reg.attr,
++ &dev_attr_voltage_d3reg.attr,
++ &dev_attr_voltage_lpreg.attr,
++ &dev_attr_voltage_ioreg.attr,
++ NULL
++};
++
++static struct attribute_group pcf_attr_group = {
++ .name = NULL, /* put in device directory */
++ .attrs = pcf_sysfs_entries,
++};
++
++static void populate_sysfs_group(struct pcf50606_data *pcf)
++{
++ int i = 0;
++ struct attribute **attr;
++
++ for (attr = pcf_sysfs_entries; *attr; attr++)
++ i++;
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_MBC) {
++ pcf_sysfs_entries[i++] = &dev_attr_chgstate.attr;
++ pcf_sysfs_entries[i++] = &dev_attr_chgmode.attr;
++ }
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_CHGCUR)
++ pcf_sysfs_entries[i++] = &dev_attr_chgcur.attr;
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_BATVOLT)
++ pcf_sysfs_entries[i++] = &dev_attr_battvolt.attr;
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_BATTEMP)
++ pcf_sysfs_entries[i++] = &dev_attr_battemp.attr;
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_PWM) {
++ pcf_sysfs_entries[i++] = &dev_attr_pwm_dc.attr;
++ pcf_sysfs_entries[i++] = &dev_attr_pwm_clk.attr;
++ }
++}
++
++static int pcf50606_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *new_client;
++ struct pcf50606_data *data;
++ int err = 0;
++ int irq;
++
++ DEBUGP("entering\n");
++ if (!pcf50606_pdev) {
++ printk(KERN_ERR "pcf50606: driver needs a platform_device!\n");
++ return -EIO;
++ }
++
++ irq = platform_get_irq(pcf50606_pdev, 0);
++ if (irq < 0) {
++ dev_err(&pcf50606_pdev->dev, "no irq in platform resources!\n");
++ return -EIO;
++ }
++
++ /* At the moment, we only support one PCF50606 in a system */
++ if (pcf50606_global) {
++ dev_err(&pcf50606_pdev->dev,
++ "currently only one chip supported\n");
++ return -EBUSY;
++ }
++
++ if (!(data = kzalloc(sizeof(*data), GFP_KERNEL)))
++ return -ENOMEM;
++
++ mutex_init(&data->lock);
++ INIT_WORK(&data->work, pcf50606_work);
++ data->irq = irq;
++ data->working = 0;
++ data->onkey_seconds = -1;
++ data->pdata = pcf50606_pdev->dev.platform_data;
++
++ new_client = &data->client;
++ i2c_set_clientdata(new_client, data);
++ new_client->addr = address;
++ new_client->adapter = adapter;
++ new_client->driver = &pcf50606_driver;
++ new_client->flags = 0;
++ strlcpy(new_client->name, "pcf50606", I2C_NAME_SIZE);
++
++ /* now we try to detect the chip */
++
++ /* register with i2c core */
++ if ((err = i2c_attach_client(new_client))) {
++ dev_err(&new_client->dev,
++ "error during i2c_attach_client()\n");
++ goto exit_free;
++ }
++
++ populate_sysfs_group(data);
++
++ err = sysfs_create_group(&new_client->dev.kobj, &pcf_attr_group);
++ if (err) {
++ dev_err(&new_client->dev, "error creating sysfs group\n");
++ goto exit_detach;
++ }
++
++ /* create virtual charger 'device' */
++
++ /* input device registration */
++ data->input_dev = input_allocate_device();
++ if (!data->input_dev)
++ goto exit_sysfs;
++
++ data->input_dev->name = "FIC Neo1973 PMU events";
++ data->input_dev->phys = "FIXME";
++ data->input_dev->id.bustype = BUS_I2C;
++ data->input_dev->cdev.dev = &new_client->dev;
++
++ data->input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
++ set_bit(KEY_POWER, data->input_dev->keybit);
++ set_bit(KEY_POWER2, data->input_dev->keybit);
++ set_bit(KEY_BATTERY, data->input_dev->keybit);
++
++ input_register_device(data->input_dev);
++
++ /* register power off handler with core power management */
++ pm_power_off = &pcf50606_go_standby;
++
++ /* configure interrupt mask */
++ reg_write(data, PCF50606_REG_INT1M, PCF50606_INT1_SECOND);
++ reg_write(data, PCF50606_REG_INT2M, 0x00);
++ reg_write(data, PCF50606_REG_INT3M, PCF50606_INT3_TSCPRES);
++
++ err = request_irq(irq, pcf50606_irq, SA_INTERRUPT,
++ "pcf50606", data);
++ if (err < 0)
++ goto exit_input;
++
++ set_irq_type(irq, IRQT_FALLING);
++
++ if (enable_irq_wake(irq) < 0)
++ dev_err(&new_client->dev, "IRQ %u cannot be enabled as wake-up"
++ "source in this hardware revision!", irq);
++
++ pcf50606_global = data;
++
++ if (data->pdata->used_features & PCF50606_FEAT_RTC) {
++ data->rtc = rtc_device_register("pcf50606", &new_client->dev,
++ &pcf50606_rtc_ops, THIS_MODULE);
++ if (IS_ERR(data->rtc)) {
++ err = PTR_ERR(data->rtc);
++ goto exit_irq;
++ }
++ }
++
++ if (data->pdata->used_features & PCF50606_FEAT_WDT) {
++ err = misc_register(&pcf50606_wdt_miscdev);
++ if (err) {
++ dev_err(&new_client->dev, "cannot register miscdev on "
++ "minor=%d (%d)\n", WATCHDOG_MINOR, err);
++ goto exit_rtc;
++ }
++ }
++
++ if (data->pdata->used_features & PCF50606_FEAT_PWM) {
++ /* enable PWM controller */
++ reg_set_bit_mask(data, PCF50606_REG_PWMC1,
++ PCF50606_PWMC1_ACTSET,
++ PCF50606_PWMC1_ACTSET);
++ }
++
++ if (data->pdata->used_features & PCF50606_FEAT_PWM_BL) {
++ data->backlight = backlight_device_register("pcf50606-bl",
++ &new_client->dev,
++ data,
++ &pcf50606bl_ops);
++ if (!data->backlight)
++ goto exit_misc;
++ /* FIXME: are we sure we want default == off? */
++ data->backlight->props.max_brightness = 16;
++ data->backlight->props.power = FB_BLANK_UNBLANK;
++ data->backlight->props.brightness =
++ data->pdata->init_brightness;
++ backlight_update_status(data->backlight);
++ }
++
++ apm_get_power_status = pcf50606_get_power_status;
++
++#ifdef CONFIG_MACH_NEO1973_GTA01
++ if (machine_is_neo1973_gta01()) {
++ gta01_pm_gps_dev.dev.parent = &new_client->dev;
++ switch (system_rev) {
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ gta01_pm_bt_dev.dev.parent = &new_client->dev;
++ platform_device_register(>a01_pm_bt_dev);
++ break;
++ }
++ platform_device_register(>a01_pm_gps_dev);
++ }
++#endif
++
++ if (data->pdata->used_features & PCF50606_FEAT_ACD)
++ reg_set_bit_mask(data, PCF50606_REG_ACDC1,
++ PCF50606_ACDC1_ACDAPE, PCF50606_ACDC1_ACDAPE);
++ else
++ reg_clear_bits(data, PCF50606_REG_ACDC1,
++ PCF50606_ACDC1_ACDAPE);
++
++ return 0;
++exit_pwm:
++ if (data->pdata->used_features & PCF50606_FEAT_PWM_BL)
++ backlight_device_unregister(data->backlight);
++exit_misc:
++ if (data->pdata->used_features & PCF50606_FEAT_WDT)
++ misc_deregister(&pcf50606_wdt_miscdev);
++exit_rtc:
++ if (data->pdata->used_features & PCF50606_FEAT_RTC)
++ rtc_device_unregister(pcf50606_global->rtc);
++exit_irq:
++ free_irq(pcf50606_global->irq, pcf50606_global);
++ pcf50606_global = NULL;
++exit_input:
++ pm_power_off = NULL;
++ input_unregister_device(data->input_dev);
++exit_sysfs:
++ sysfs_remove_group(&new_client->dev.kobj, &pcf_attr_group);
++exit_detach:
++ i2c_detach_client(new_client);
++exit_free:
++ kfree(data);
++ return err;
++}
++
++static int pcf50606_attach_adapter(struct i2c_adapter *adapter)
++{
++ DEBUGP("entering, calling i2c_probe\n");
++ return i2c_probe(adapter, &addr_data, &pcf50606_detect);
++}
++
++static int pcf50606_detach_client(struct i2c_client *client)
++{
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++
++ DEBUGP("entering\n");
++
++ apm_get_power_status = NULL;
++ input_unregister_device(pcf->input_dev);
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_PWM_BL)
++ backlight_device_unregister(pcf->backlight);
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_WDT)
++ misc_deregister(&pcf50606_wdt_miscdev);
++
++ if (pcf->pdata->used_features & PCF50606_FEAT_RTC)
++ rtc_device_unregister(pcf->rtc);
++
++ free_irq(pcf->irq, pcf);
++
++ sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
++
++ pm_power_off = NULL;
++
++ kfree(pcf);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++#define INT1M_RESUMERS (PCF50606_INT1_ALARM|PCF50606_INT1_ONKEYF|PCF50606_INT1_EXTONR)
++#define INT2M_RESUMERS (PCF50606_INT2_CHGWD10S|PCF50606_INT2_CHGPROT|PCF50606_INT2_CHGERR)
++#define INT3M_RESUMERS (PCF50606_INT3_LOWBAT|PCF50606_INT3_HIGHTMP|PCF50606_INT3_ACDINS)
++static int pcf50606_suspend(struct device *dev, pm_message_t state)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++ int i;
++
++ /* The general idea is to power down all unused power supplies,
++ * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF
++ * and ALARM */
++
++ mutex_lock(&pcf->lock);
++
++ /* Save all registers that don't "survive" standby state */
++ pcf->standby_regs.dcdc1 = __reg_read(pcf, PCF50606_REG_DCDC1);
++ pcf->standby_regs.dcdc2 = __reg_read(pcf, PCF50606_REG_DCDC2);
++ pcf->standby_regs.dcdec1 = __reg_read(pcf, PCF50606_REG_DCDEC1);
++ pcf->standby_regs.dcudc1 = __reg_read(pcf, PCF50606_REG_DCUDC1);
++ pcf->standby_regs.ioregc = __reg_read(pcf, PCF50606_REG_IOREGC);
++ pcf->standby_regs.d1regc1 = __reg_read(pcf, PCF50606_REG_D1REGC1);
++ pcf->standby_regs.d2regc1 = __reg_read(pcf, PCF50606_REG_D2REGC1);
++ pcf->standby_regs.d3regc1 = __reg_read(pcf, PCF50606_REG_D3REGC1);
++ pcf->standby_regs.lpregc1 = __reg_read(pcf, PCF50606_REG_LPREGC1);
++ pcf->standby_regs.adcc1 = __reg_read(pcf, PCF50606_REG_ADCC1);
++ pcf->standby_regs.adcc2 = __reg_read(pcf, PCF50606_REG_ADCC2);
++ pcf->standby_regs.pwmc1 = __reg_read(pcf, PCF50606_REG_PWMC1);
++
++ /* switch off power supplies that are not needed during suspend */
++ for (i = 0; i < __NUM_PCF50606_REGULATORS; i++) {
++ if (!(pcf->pdata->rails[i].flags & PMU_VRAIL_F_SUSPEND_ON)) {
++ u_int8_t tmp;
++
++ /* IOREG powers the I at C interface so we cannot switch
++ * it off */
++ if (i == PCF50606_REGULATOR_IOREG)
++ continue;
++
++ DEBUGP("disabling pcf50606 regulator %u\n", i);
++ /* we cannot use pcf50606_onoff_set() because we're
++ * already under the mutex */
++ tmp = __reg_read(pcf, regulator_registers[i]);
++ tmp &= 0x1f;
++ __reg_write(pcf, regulator_registers[i], tmp);
++ }
++ }
++
++ pcf->standby_regs.int1m = __reg_read(pcf, PCF50606_REG_INT1M);
++ pcf->standby_regs.int2m = __reg_read(pcf, PCF50606_REG_INT2M);
++ pcf->standby_regs.int3m = __reg_read(pcf, PCF50606_REG_INT3M);
++ __reg_write(pcf, PCF50606_REG_INT1M, ~INT1M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50606_REG_INT2M, ~INT2M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50606_REG_INT3M, ~INT3M_RESUMERS & 0xff);
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50606_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50606_data *pcf = i2c_get_clientdata(client);
++
++ mutex_lock(&pcf->lock);
++
++ /* Resume all saved registers that don't "survive" standby state */
++ __reg_write(pcf, PCF50606_REG_INT1M, pcf->standby_regs.int1m);
++ __reg_write(pcf, PCF50606_REG_INT2M, pcf->standby_regs.int2m);
++ __reg_write(pcf, PCF50606_REG_INT3M, pcf->standby_regs.int3m);
++
++ __reg_write(pcf, PCF50606_REG_DCDC1, pcf->standby_regs.dcdc1);
++ __reg_write(pcf, PCF50606_REG_DCDC2, pcf->standby_regs.dcdc2);
++ __reg_write(pcf, PCF50606_REG_DCDEC1, pcf->standby_regs.dcdec1);
++ __reg_write(pcf, PCF50606_REG_DCUDC1, pcf->standby_regs.dcudc1);
++ __reg_write(pcf, PCF50606_REG_IOREGC, pcf->standby_regs.ioregc);
++ __reg_write(pcf, PCF50606_REG_D1REGC1, pcf->standby_regs.d1regc1);
++ __reg_write(pcf, PCF50606_REG_D2REGC1, pcf->standby_regs.d2regc1);
++ __reg_write(pcf, PCF50606_REG_D3REGC1, pcf->standby_regs.d3regc1);
++ __reg_write(pcf, PCF50606_REG_LPREGC1, pcf->standby_regs.lpregc1);
++ __reg_write(pcf, PCF50606_REG_ADCC1, pcf->standby_regs.adcc1);
++ __reg_write(pcf, PCF50606_REG_ADCC2, pcf->standby_regs.adcc2);
++ __reg_write(pcf, PCF50606_REG_PWMC1, pcf->standby_regs.pwmc1);
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++#else
++#define pcf50606_suspend NULL
++#define pcf50606_resume NULL
++#endif
++
++static struct i2c_driver pcf50606_driver = {
++ .driver = {
++ .name = "pcf50606",
++ .suspend= &pcf50606_suspend,
++ .resume = &pcf50606_resume,
++ },
++ .id = I2C_DRIVERID_PCF50606,
++ .attach_adapter = &pcf50606_attach_adapter,
++ .detach_client = &pcf50606_detach_client,
++};
++
++/* platform driver, since i2c devices don't have platform_data */
++static int __init pcf50606_plat_probe(struct platform_device *pdev)
++{
++ struct pcf50606_platform_data *pdata = pdev->dev.platform_data;
++
++ if (!pdata)
++ return -ENODEV;
++
++ pcf50606_pdev = pdev;
++
++ return 0;
++}
++
++static int pcf50606_plat_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver pcf50606_plat_driver = {
++ .probe = pcf50606_plat_probe,
++ .remove = pcf50606_plat_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "pcf50606",
++ },
++};
++
++static int __init pcf50606_init(void)
++{
++ int rc;
++
++ if (!(rc = platform_driver_register(&pcf50606_plat_driver)))
++ rc = i2c_add_driver(&pcf50606_driver);
++
++ return rc;
++}
++
++static void pcf50606_exit(void)
++{
++ i2c_del_driver(&pcf50606_driver);
++ platform_driver_unregister(&pcf50606_plat_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(pcf50606_init);
++module_exit(pcf50606_exit);
+Index: linux-2.6.22.1/drivers/i2c/chips/pcf50606.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50606.h 2007-07-16 15:41:56.429805994 +0200
+@@ -0,0 +1,302 @@
++#ifndef _PCF50606_H
++#define _PCF50606_H
++
++/* Philips PCF50606 Power Managemnt Unit (PMU) driver
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ */
++
++enum pfc50606_regs {
++ PCF50606_REG_ID = 0x00,
++ PCF50606_REG_OOCS = 0x01,
++ PCF50606_REG_INT1 = 0x02, /* Interrupt Status */
++ PCF50606_REG_INT2 = 0x03, /* Interrupt Status */
++ PCF50606_REG_INT3 = 0x04, /* Interrupt Status */
++ PCF50606_REG_INT1M = 0x05, /* Interrupt Mask */
++ PCF50606_REG_INT2M = 0x06, /* Interrupt Mask */
++ PCF50606_REG_INT3M = 0x07, /* Interrupt Mask */
++ PCF50606_REG_OOCC1 = 0x08,
++ PCF50606_REG_OOCC2 = 0x09,
++ PCF50606_REG_RTCSC = 0x0a, /* Second */
++ PCF50606_REG_RTCMN = 0x0b, /* Minute */
++ PCF50606_REG_RTCHR = 0x0c, /* Hour */
++ PCF50606_REG_RTCWD = 0x0d, /* Weekday */
++ PCF50606_REG_RTCDT = 0x0e, /* Day */
++ PCF50606_REG_RTCMT = 0x0f, /* Month */
++ PCF50606_REG_RTCYR = 0x10, /* Year */
++ PCF50606_REG_RTCSCA = 0x11, /* Alarm Second */
++ PCF50606_REG_RTCMNA = 0x12, /* Alarm Minute */
++ PCF50606_REG_RTCHRA = 0x13, /* Alarm Hour */
++ PCF50606_REG_RTCWDA = 0x14, /* Alarm Weekday */
++ PCF50606_REG_RTCDTA = 0x15, /* Alarm Day */
++ PCF50606_REG_RTCMTA = 0x16, /* Alarm Month */
++ PCF50606_REG_RTCYRA = 0x17, /* Alarm Year */
++ PCF50606_REG_PSSC = 0x18, /* Power sequencing */
++ PCF50606_REG_PWROKM = 0x19, /* PWROK mask */
++ PCF50606_REG_PWROKS = 0x1a, /* PWROK status */
++ PCF50606_REG_DCDC1 = 0x1b,
++ PCF50606_REG_DCDC2 = 0x1c,
++ PCF50606_REG_DCDC3 = 0x1d,
++ PCF50606_REG_DCDC4 = 0x1e,
++ PCF50606_REG_DCDEC1 = 0x1f,
++ PCF50606_REG_DCDEC2 = 0x20,
++ PCF50606_REG_DCUDC1 = 0x21,
++ PCF50606_REG_DCUDC2 = 0x22,
++ PCF50606_REG_IOREGC = 0x23,
++ PCF50606_REG_D1REGC1 = 0x24,
++ PCF50606_REG_D2REGC1 = 0x25,
++ PCF50606_REG_D3REGC1 = 0x26,
++ PCF50606_REG_LPREGC1 = 0x27,
++ PCF50606_REG_LPREGC2 = 0x28,
++ PCF50606_REG_MBCC1 = 0x29,
++ PCF50606_REG_MBCC2 = 0x2a,
++ PCF50606_REG_MBCC3 = 0x2b,
++ PCF50606_REG_MBCS1 = 0x2c,
++ PCF50606_REG_BBCC = 0x2d,
++ PCF50606_REG_ADCC1 = 0x2e,
++ PCF50606_REG_ADCC2 = 0x2f,
++ PCF50606_REG_ADCS1 = 0x30,
++ PCF50606_REG_ADCS2 = 0x31,
++ PCF50606_REG_ADCS3 = 0x32,
++ PCF50606_REG_ACDC1 = 0x33,
++ PCF50606_REG_BVMC = 0x34,
++ PCF50606_REG_PWMC1 = 0x35,
++ PCF50606_REG_LEDC1 = 0x36,
++ PCF50606_REG_LEDC2 = 0x37,
++ PCF50606_REG_GPOC1 = 0x38,
++ PCF50606_REG_GPOC2 = 0x39,
++ PCF50606_REG_GPOC3 = 0x3a,
++ PCF50606_REG_GPOC4 = 0x3b,
++ PCF50606_REG_GPOC5 = 0x3c,
++ __NUM_PCF50606_REGS
++};
++
++enum pcf50606_reg_oocs {
++ PFC50606_OOCS_ONKEY = 0x01,
++ PCF50606_OOCS_EXTON = 0x02,
++ PCF50606_OOCS_PWROKRST = 0x04,
++ PCF50606_OOCS_BATOK = 0x08,
++ PCF50606_OOCS_BACKOK = 0x10,
++ PCF50606_OOCS_CHGOK = 0x20,
++ PCF50606_OOCS_TEMPOK = 0x40,
++ PCF50606_OOCS_WDTEXP = 0x80,
++};
++
++enum pcf50606_reg_oocc1 {
++ PCF50606_OOCC1_GOSTDBY = 0x01,
++ PCF50606_OOCC1_TOTRST = 0x02,
++ PCF50606_OOCC1_CLK32ON = 0x04,
++ PCF50606_OOCC1_WDTRST = 0x08,
++ PCF50606_OOCC1_RTCWAK = 0x10,
++ PCF50606_OOCC1_CHGWAK = 0x20,
++ PCF50606_OOCC1_EXTONWAK_HIGH = 0x40,
++ PCF50606_OOCC1_EXTONWAK_LOW = 0x80,
++};
++
++enum pcf50606_reg_oocc2 {
++ PCF50606_OOCC2_ONKEYDB_NONE = 0x00,
++ PCF50606_OOCC2_ONKEYDB_14ms = 0x01,
++ PCF50606_OOCC2_ONKEYDB_62ms = 0x02,
++ PCF50606_OOCC2_ONKEYDB_500ms = 0x03,
++ PCF50606_OOCC2_EXTONDB_NONE = 0x00,
++ PCF50606_OOCC2_EXTONDB_14ms = 0x04,
++ PCF50606_OOCC2_EXTONDB_62ms = 0x08,
++ PCF50606_OOCC2_EXTONDB_500ms = 0x0c,
++};
++
++enum pcf50606_reg_int1 {
++ PCF50606_INT1_ONKEYR = 0x01, /* ONKEY rising edge */
++ PCF50606_INT1_ONKEYF = 0x02, /* ONKEY falling edge */
++ PCF50606_INT1_ONKEY1S = 0x04, /* OMKEY at least 1sec low */
++ PCF50606_INT1_EXTONR = 0x08, /* EXTON rising edge */
++ PCF50606_INT1_EXTONF = 0x10, /* EXTON falling edge */
++ PCF50606_INT1_SECOND = 0x40, /* RTC periodic second interrupt */
++ PCF50606_INT1_ALARM = 0x80, /* RTC alarm time is reached */
++};
++
++enum pcf50606_reg_int2 {
++ PCF50606_INT2_CHGINS = 0x01, /* Charger inserted */
++ PCF50606_INT2_CHGRM = 0x02, /* Charger removed */
++ PCF50606_INT2_CHGFOK = 0x04, /* Fast charging OK */
++ PCF50606_INT2_CHGERR = 0x08, /* Error in charging mode */
++ PCF50606_INT2_CHGFRDY = 0x10, /* Fast charge completed */
++ PCF50606_INT2_CHGPROT = 0x20, /* Charging protection interrupt */
++ PCF50606_INT2_CHGWD10S = 0x40, /* Charger watchdig expires in 10s */
++ PCF50606_INT2_CHGWDEXP = 0x80, /* Charger watchdog expires */
++};
++
++enum pcf50606_reg_int3 {
++ PCF50606_INT3_ADCRDY = 0x01, /* ADC conversion finished */
++ PCF50606_INT3_ACDINS = 0x02, /* Accessory inserted */
++ PCF50606_INT3_ACDREM = 0x04, /* Accessory removed */
++ PCF50606_INT3_TSCPRES = 0x08, /* Touch screen pressed */
++ PCF50606_INT3_LOWBAT = 0x40, /* Low battery voltage */
++ PCF50606_INT3_HIGHTMP = 0x80, /* High temperature */
++};
++
++/* used by PSSC, PWROKM, PWROKS, */
++enum pcf50606_regu {
++ PCF50606_REGU_DCD = 0x01, /* DCD in phase 2 */
++ PCF50606_REGU_DCDE = 0x02, /* DCDE in phase 2 */
++ PCF50606_REGU_DCUD = 0x04, /* DCDU in phase 2 */
++ PCF50606_REGU_IO = 0x08, /* IO in phase 2 */
++ PCF50606_REGU_D1 = 0x10, /* D1 in phase 2 */
++ PCF50606_REGU_D2 = 0x20, /* D2 in phase 2 */
++ PCF50606_REGU_D3 = 0x40, /* D3 in phase 2 */
++ PCF50606_REGU_LP = 0x80, /* LP in phase 2 */
++};
++
++enum pcf50606_reg_dcdc4 {
++ PCF50606_DCDC4_MODE_AUTO = 0x00,
++ PCF50606_DCDC4_MODE_PWM = 0x01,
++ PCF50606_DCDC4_MODE_PCF = 0x02,
++ PCF50606_DCDC4_OFF_FLOAT = 0x00,
++ PCF50606_DCDC4_OFF_BYPASS = 0x04,
++ PCF50606_DCDC4_OFF_PULLDOWN = 0x08,
++ PCF50606_DCDC4_CURLIM_500mA = 0x00,
++ PCF50606_DCDC4_CURLIM_750mA = 0x10,
++ PCF50606_DCDC4_CURLIM_1000mA = 0x20,
++ PCF50606_DCDC4_CURLIM_1250mA = 0x30,
++ PCF50606_DCDC4_TOGGLE = 0x40,
++ PCF50606_DCDC4_REGSEL_DCDC2 = 0x80,
++};
++
++enum pcf50606_reg_dcdec2 {
++ PCF50606_DCDEC2_MODE_AUTO = 0x00,
++ PCF50606_DCDEC2_MODE_PWM = 0x01,
++ PCF50606_DCDEC2_MODE_PCF = 0x02,
++ PCF50606_DCDEC2_OFF_FLOAT = 0x00,
++ PCF50606_DCDEC2_OFF_BYPASS = 0x04,
++};
++
++enum pcf50606_reg_dcudc2 {
++ PCF50606_DCUDC2_MODE_AUTO = 0x00,
++ PCF50606_DCUDC2_MODE_PWM = 0x01,
++ PCF50606_DCUDC2_MODE_PCF = 0x02,
++ PCF50606_DCUDC2_OFF_FLOAT = 0x00,
++ PCF50606_DCUDC2_OFF_BYPASS = 0x04,
++};
++
++enum pcf50606_reg_adcc1 {
++ PCF50606_ADCC1_TSCMODACT = 0x01,
++ PCF50606_ADCC1_TSCMODSTB = 0x02,
++ PCF50606_ADCC1_TRATSET = 0x04,
++ PCF50606_ADCC1_NTCSWAPE = 0x08,
++ PCF50606_ADCC1_NTCSWAOFF = 0x10,
++ PCF50606_ADCC1_EXTSYNCBREAK = 0x20,
++ /* reserved */
++ PCF50606_ADCC1_TSCINT = 0x80,
++};
++
++enum pcf50606_reg_adcc2 {
++ PCF50606_ADCC2_ADCSTART = 0x01,
++ /* see enum pcf50606_adcc2_adcmux */
++ PCF50606_ADCC2_SYNC_NONE = 0x00,
++ PCF50606_ADCC2_SYNC_TXON = 0x20,
++ PCF50606_ADCC2_SYNC_PWREN1 = 0x40,
++ PCF50606_ADCC2_SYNC_PWREN2 = 0x60,
++ PCF50606_ADCC2_RES_10BIT = 0x00,
++ PCF50606_ADCC2_RES_8BIT = 0x80,
++};
++
++#define PCF50606_ADCC2_ADCMUX_MASK (0xf << 1)
++
++#define ADCMUX_SHIFT 1
++enum pcf50606_adcc2_adcmux {
++ PCF50606_ADCMUX_BATVOLT_RES = 0x0 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_BATVOLT_SUBTR = 0x1 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_ADCIN1_RES = 0x2 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_ADCIN1_SUBTR = 0x3 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_BATTEMP = 0x4 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_ADCIN2 = 0x5 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_ADCIN3 = 0x6 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_ADCIN3_RATIO = 0x7 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_XPOS = 0x8 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_YPOS = 0x9 << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_P1 = 0xa << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_P2 = 0xb << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_BATVOLT_ADCIN1 = 0xc << ADCMUX_SHIFT,
++ PCF50606_ADCMUX_XY_SEQUENCE = 0xe << ADCMUX_SHIFT,
++ PCF50606_P1_P2_RESISTANCE = 0xf << ADCMUX_SHIFT,
++};
++
++enum pcf50606_adcs2 {
++ PCF50606_ADCS2_ADCRDY = 0x80,
++};
++
++enum pcf50606_reg_mbcc1 {
++ PCF50606_MBCC1_CHGAPE = 0x01,
++ PCF50606_MBCC1_AUTOFST = 0x02,
++#define PCF50606_MBCC1_CHGMOD_MASK 0x1c
++#define PCF50606_MBCC1_CHGMOD_SHIFT 2
++ PCF50606_MBCC1_CHGMOD_QUAL = 0x00,
++ PCF50606_MBCC1_CHGMOD_PRE = 0x04,
++ PCF50606_MBCC1_CHGMOD_TRICKLE = 0x08,
++ PCF50606_MBCC1_CHGMOD_FAST_CCCV = 0x0c,
++ PCF50606_MBCC1_CHGMOD_FAST_NOCC = 0x10,
++ PCF50606_MBCC1_CHGMOD_FAST_NOCV = 0x14,
++ PCF50606_MBCC1_CHGMOD_FAST_SW = 0x18,
++ PCF50606_MBCC1_CHGMOD_IDLE = 0x1c,
++ PCF50606_MBCC1_DETMOD_LOWCHG = 0x20,
++ PCF50606_MBCC1_DETMOD_WDRST = 0x40,
++};
++
++enum pcf50606_reg_acdc1 {
++ PCF50606_ACDC1_ACDDET = 0x01,
++ PCF50606_ACDC1_THRSHLD_1V0 = 0x00,
++ PCF50606_ACDC1_THRSHLD_1V2 = 0x02,
++ PCF50606_ACDC1_THRSHLD_1V4 = 0x04,
++ PCF50606_ACDC1_THRSHLD_1V6 = 0x06,
++ PCF50606_ACDC1_THRSHLD_1V8 = 0x08,
++ PCF50606_ACDC1_THRSHLD_2V0 = 0x0a,
++ PCF50606_ACDC1_THRSHLD_2V2 = 0x0c,
++ PCF50606_ACDC1_THRSHLD_2V4 = 0x0e,
++ PCF50606_ACDC1_DISDB = 0x10,
++ PCF50606_ACDC1_ACDAPE = 0x80,
++};
++
++enum pcf50606_reg_bvmc {
++ PCF50606_BVMC_LOWBAT = 0x01,
++ PCF50606_BVMC_THRSHLD_NULL = 0x00,
++ PCF50606_BVMC_THRSHLD_2V8 = 0x02,
++ PCF50606_BVMC_THRSHLD_2V9 = 0x04,
++ PCF50606_BVMC_THRSHLD_3V = 0x08,
++ PCF50606_BVMC_THRSHLD_3V1 = 0x08,
++ PCF50606_BVMC_THRSHLD_3V2 = 0x0a,
++ PCF50606_BVMC_THRSHLD_3V3 = 0x0c,
++ PCF50606_BVMC_THRSHLD_3V4 = 0x0e,
++ PCF50606_BVMC_DISDB = 0x10,
++};
++
++enum pcf50606_reg_pwmc1 {
++ PCF50606_PWMC1_ACTSET = 0x01,
++ PCF50606_PWMC1_PWMDC_0_16 = 0x00,
++ PCF50606_PWMC1_PWMDC_1_16 = 0x02,
++ PCF50606_PWMC1_PWMDC_2_16 = 0x04,
++ PCF50606_PWMC1_PWMDC_3_16 = 0x06,
++ PCF50606_PWMC1_PWMDC_4_16 = 0x08,
++ PCF50606_PWMC1_PWMDC_5_16 = 0x0a,
++ PCF50606_PWMC1_PWMDC_6_16 = 0x0c,
++ PCF50606_PWMC1_PWMDC_7_16 = 0x0e,
++ PCF50606_PWMC1_PWMDC_8_16 = 0x10,
++ PCF50606_PWMC1_PWMDC_9_16 = 0x12,
++ PCF50606_PWMC1_PWMDC_10_16 = 0x14,
++ PCF50606_PWMC1_PWMDC_11_16 = 0x16,
++ PCF50606_PWMC1_PWMDC_12_16 = 0x18,
++ PCF50606_PWMC1_PWMDC_13_16 = 0x1a,
++ PCF50606_PWMC1_PWMDC_14_16 = 0x1c,
++ PCF50606_PWMC1_PWMDC_15_16 = 0x1e,
++ PCF50606_PWMC1_PRESC_512Hz = 0x20,
++ PCF50606_PWMC1_PRESC_256Hz = 0x40,
++ PCF50606_PWMC1_PRESC_64Hz = 0x60,
++ PCF50606_PWMC1_PRESC_56kHz = 0x80,
++ PCF50606_PWMC1_PRESC_28kHz = 0xa0,
++ PCF50606_PWMC1_PRESC_14kHz = 0xc0,
++ PCF50606_PWMC1_PRESC_7kHz = 0xe0,
++};
++#define PCF50606_PWMC1_CLK_SHIFT 5
++#define PCF50606_PWMC1_DC_SHIFT 1
++
++#endif /* _PCF50606_H */
++
+Index: linux-2.6.22.1/drivers/i2c/chips/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Kconfig 2007-07-16 15:36:45.804104444 +0200
++++ linux-2.6.22.1/drivers/i2c/chips/Kconfig 2007-07-16 16:02:42.816833546 +0200
+@@ -35,6 +35,17 @@
+ This driver can also be built as a module. If so, the module
+ will be called eeprom.
+
++config SENSORS_PCF50606
++ tristate "Philips PCF50606"
++ depends on I2C
++ help
++ If you say yes here you get support for Philips PCF50606
++ PMU (Power Management Unit) chips.
++
++ This driver can also be built as a module. If so, the module
++ will be called pcf50606.
++
++
+ config SENSORS_PCF8574
+ tristate "Philips PCF8574 and PCF8574A"
+ depends on EXPERIMENTAL
+Index: linux-2.6.22.1/drivers/i2c/chips/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Makefile 2007-07-16 15:36:45.816105132 +0200
++++ linux-2.6.22.1/drivers/i2c/chips/Makefile 2007-07-16 16:02:42.828834229 +0200
+@@ -8,6 +8,7 @@
+ obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
+ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
+ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
++obj-$(CONFIG_SENSORS_PCF50606) += pcf50606.o
+ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
+ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+Index: linux-2.6.22.1/include/linux/i2c-id.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/i2c-id.h 2007-07-16 15:36:45.824105584 +0200
++++ linux-2.6.22.1/include/linux/i2c-id.h 2007-07-16 16:02:42.836834686 +0200
+@@ -160,6 +160,7 @@
+ #define I2C_DRIVERID_FSCHER 1046
+ #define I2C_DRIVERID_W83L785TS 1047
+ #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
++#define I2C_DRIVERID_PCF50606 1049
+
+ /*
+ * ---- Adapter types ----------------------------------------------------
+Index: linux-2.6.22.1/include/linux/pcf50606.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/pcf50606.h 2007-07-16 15:41:56.553813062 +0200
+@@ -0,0 +1,104 @@
++#ifndef _LINUX_PCF50606_H
++#define _LINUX_PCF50606_H
++
++/* public in-kernel pcf50606 api */
++enum pcf50606_regulator_id {
++ PCF50606_REGULATOR_DCD,
++ PCF50606_REGULATOR_DCDE,
++ PCF50606_REGULATOR_DCUD,
++ PCF50606_REGULATOR_D1REG,
++ PCF50606_REGULATOR_D2REG,
++ PCF50606_REGULATOR_D3REG,
++ PCF50606_REGULATOR_LPREG,
++ PCF50606_REGULATOR_IOREG,
++ __NUM_PCF50606_REGULATORS
++};
++
++struct pcf50606_data;
++extern struct pcf50606_data *pcf50606_global;
++
++extern void
++pcf50606_go_standby(void);
++
++extern void
++pcf50606_gpo0_set(struct pcf50606_data *pcf, int on);
++
++extern int
++pcf50606_gpo0_get(struct pcf50606_data *pcf);
++
++extern int
++pcf50606_voltage_set(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg,
++ unsigned int millivolts);
++extern unsigned int
++pcf50606_voltage_get(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg);
++extern int
++pcf50606_onoff_get(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg);
++
++extern int
++pcf50606_onoff_set(struct pcf50606_data *pcf,
++ enum pcf50606_regulator_id reg, int on);
++
++extern void
++pcf50606_charge_fast(struct pcf50606_data *pcf, int on);
++
++#define PMU_VRAIL_F_SUSPEND_ON 0x00000001 /* Remains on during suspend */
++#define PMU_VRAIL_F_UNUSED 0x00000002 /* This rail is not used */
++struct pmu_voltage_rail {
++ char *name;
++ unsigned int flags;
++ struct {
++ unsigned int init;
++ unsigned int max;
++ } voltage;
++};
++
++enum pmu_event {
++ PMU_EVT_NONE,
++ PMU_EVT_INSERT,
++ PMU_EVT_REMOVE,
++ __NUM_PMU_EVTS
++};
++
++typedef int pmu_cb(struct device *dev, unsigned int feature,
++ enum pmu_event event);
++
++#define PCF50606_FEAT_EXTON 0x00000001 /* not yet supported */
++#define PCF50606_FEAT_MBC 0x00000002
++#define PCF50606_FEAT_BBC 0x00000004 /* not yet supported */
++#define PCF50606_FEAT_TSC 0x00000008 /* not yet supported */
++#define PCF50606_FEAT_WDT 0x00000010
++#define PCF50606_FEAT_ACD 0x00000020
++#define PCF50606_FEAT_RTC 0x00000040
++#define PCF50606_FEAT_PWM 0x00000080
++#define PCF50606_FEAT_CHGCUR 0x00000100
++#define PCF50606_FEAT_BATVOLT 0x00000200
++#define PCF50606_FEAT_BATTEMP 0x00000400
++#define PCF50606_FEAT_PWM_BL 0x00000800
++
++struct pcf50606_platform_data {
++ /* general */
++ unsigned int used_features;
++ unsigned int onkey_seconds_required;
++
++ /* voltage regulator related */
++ struct pmu_voltage_rail rails[__NUM_PCF50606_REGULATORS];
++ unsigned int used_regulators;
++
++ /* charger related */
++ unsigned int r_fix_batt;
++ unsigned int r_fix_batt_par;
++ unsigned int r_sense_milli;
++
++ /* backlight related */
++ unsigned int init_brightness;
++
++ struct {
++ u_int8_t mbcc3; /* charger voltage / current */
++ } charger;
++ pmu_cb *cb;
++};
++
++#endif
Added: developers/nbd/patches-2.6.22/320-gta01-core.patch
===================================================================
--- developers/nbd/patches-2.6.22/320-gta01-core.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/320-gta01-core.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,864 @@
+This patch adds support for the FIC GTA01 machine type to the ARM port of
+the linux kernel.
+
+Index: linux-2.6.22.1/arch/arm/mach-s3c2410/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/Kconfig 2007-07-16 15:36:45.480085983 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/Kconfig 2007-07-16 15:50:40.487670352 +0200
+@@ -109,5 +109,12 @@
+ help
+ Say Y here if you are using the Armzone QT2410
+
++config MACH_NEO1973_GTA01
++ bool "FIC Neo1973 GSM Phone (GTA01 Hardware)"
++ select CPU_S3C2410
++ select SENSORS_PCF50606
++ help
++ Say Y here if you are using the FIC Neo1973 GSM Phone
++
+ endmenu
+
+Index: linux-2.6.22.1/arch/arm/mach-s3c2410/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/Makefile 2007-07-16 15:36:45.488086435 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/Makefile 2007-07-16 15:41:56.581814659 +0200
+@@ -29,3 +29,4 @@
+ obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o
+ obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o
+ obj-$(CONFIG_MACH_QT2410) += mach-qt2410.o
++obj-$(CONFIG_MACH_NEO1973_GTA01)+= mach-gta01.o
+Index: linux-2.6.22.1/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-gta01.c 2007-07-16 15:52:13.956996866 +0200
+@@ -0,0 +1,755 @@
++/*
++ * linux/arch/arm/mach-s3c2410/mach-gta01.c
++ *
++ * S3C2410 Machine Support for the FIC GTA01 (Neo1973)
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++#include <linux/serial_core.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++#include <linux/sysdev.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/mci.h>
++#include <asm/arch/ts.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++#include <asm/arch/usb-control.h>
++
++#include <asm/arch/gta01.h>
++
++#include <asm/plat-s3c24xx/devs.h>
++#include <asm/plat-s3c24xx/cpu.h>
++#include <asm/plat-s3c24xx/pm.h>
++
++static struct map_desc gta01_iodesc[] __initdata = {
++ {
++ .virtual = 0xe0000000,
++ .pfn = __phys_to_pfn(S3C2410_CS3+0x01000000),
++ .length = SZ_1M,
++ .type = MT_DEVICE
++ },
++};
++
++#define UCON S3C2410_UCON_DEFAULT
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg gta01_uartcfgs[] = {
++ [0] = {
++ .hwport = 0,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++ [1] = {
++ .hwport = 1,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++};
++
++/* PMU driver info */
++
++static int pmu_callback(struct device *dev, unsigned int feature,
++ enum pmu_event event)
++{
++ switch (feature) {
++ case PCF50606_FEAT_ACD:
++ switch (event) {
++ case PMU_EVT_INSERT:
++ pcf50606_charge_fast(pcf50606_global, 1);
++ break;
++ case PMU_EVT_REMOVE:
++ pcf50606_charge_fast(pcf50606_global, 0);
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static struct pcf50606_platform_data gta01_pcf_pdata = {
++ .used_features = PCF50606_FEAT_EXTON |
++ PCF50606_FEAT_MBC |
++ PCF50606_FEAT_BBC |
++ PCF50606_FEAT_RTC |
++ PCF50606_FEAT_WDT |
++ PCF50606_FEAT_CHGCUR |
++ PCF50606_FEAT_BATVOLT |
++ PCF50606_FEAT_BATTEMP,
++ .onkey_seconds_required = 3,
++ .cb = &pmu_callback,
++ .r_fix_batt = 10000,
++ .r_fix_batt_par = 10000,
++ .r_sense_milli = 220,
++ .rails = {
++ [PCF50606_REGULATOR_D1REG] = {
++ .name = "bt_3v15",
++ .voltage = {
++ .init = 3150,
++ .max = 3150,
++ },
++ },
++ [PCF50606_REGULATOR_D2REG] = {
++ .name = "gl_2v5",
++ .voltage = {
++ .init = 2500,
++ .max = 2500,
++ },
++ },
++ [PCF50606_REGULATOR_D3REG] = {
++ .name = "stby_1v8",
++ .flags = PMU_VRAIL_F_SUSPEND_ON,
++ .voltage = {
++ .init = 1800,
++ .max = 2100,
++ },
++ },
++ [PCF50606_REGULATOR_DCD] = {
++ .name = "gl_1v5",
++ .voltage = {
++ .init = 1500,
++ .max = 1500,
++ },
++ },
++ [PCF50606_REGULATOR_DCDE] = {
++ .name = "io_3v3",
++ .flags = PMU_VRAIL_F_SUSPEND_ON,
++ .voltage = {
++ .init = 3300,
++ .max = 3330,
++ },
++ },
++ [PCF50606_REGULATOR_DCUD] = {
++ .name = "core_1v8",
++ .flags = PMU_VRAIL_F_SUSPEND_ON,
++ .voltage = {
++ .init = 2100,
++ .max = 2100,
++ },
++ },
++ [PCF50606_REGULATOR_IOREG] = {
++ .name = "codec_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_LPREG] = {
++ .name = "lcm_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ }
++ },
++};
++
++static void cfg_pmu_vrail(struct pmu_voltage_rail *vrail, char *name,
++ unsigned int flags, unsigned int init,
++ unsigned int max)
++{
++ vrail->name = name;
++ vrail->flags = flags;
++ vrail->voltage.init = init;
++ vrail->voltage.max = max;
++}
++
++static void mangle_pmu_pdata_by_system_rev(void)
++{
++ switch (system_rev) {
++ case GTA01Bv4_SYSTEM_REV:
++ gta01_pcf_pdata.used_features |= PCF50606_FEAT_ACD;
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ gta01_pcf_pdata.rails[PCF50606_REGULATOR_D3REG]
++ .name = "user1";
++ gta01_pcf_pdata.rails[PCF50606_REGULATOR_D3REG]
++ .flags &= ~PMU_VRAIL_F_SUSPEND_ON;
++ gta01_pcf_pdata.rails[PCF50606_REGULATOR_D3REG]
++ .flags = PMU_VRAIL_F_UNUSED;
++ break;
++ case GTA01v4_SYSTEM_REV:
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_DCUD],
++ "core_1v8", PMU_VRAIL_F_SUSPEND_ON, 1800, 1800);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_D1REG],
++ "vrf_3v", 0, 3000, 3000);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_D3REG],
++ "vtcxo_2v8", 0, 2800, 2800);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_DCD],
++ "gl_3v5", 0, 3500, 3500);
++ break;
++ case GTA01v3_SYSTEM_REV:
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_D1REG],
++ "vrf_3v", 0, 3000, 3000);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_D2REG],
++ "sd_3v3", 0, 3300, 3300);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_D3REG],
++ "codec_3v3", 0, 3300, 3300);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_DCD],
++ "gpsio_3v3", 0, 3300, 3300);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_DCUD],
++ "core_1v8", PMU_VRAIL_F_SUSPEND_ON, 1800, 1800);
++ cfg_pmu_vrail(>a01_pcf_pdata.rails[PCF50606_REGULATOR_IOREG],
++ "vtcxo_2v8", 0, 2800, 2800);
++ break;
++ }
++}
++
++static struct resource gta01_pmu_resources[] = {
++ [0] = {
++ .flags = IORESOURCE_IRQ,
++ .start = GTA01_IRQ_PCF50606,
++ .end = GTA01_IRQ_PCF50606,
++ },
++};
++
++struct platform_device gta01_pmu_dev = {
++ .name = "pcf50606",
++ .num_resources = ARRAY_SIZE(gta01_pmu_resources),
++ .resource = gta01_pmu_resources,
++ .dev = {
++ .platform_data = >a01_pcf_pdata,
++ },
++};
++
++/* LCD driver info */
++
++/* Configuration for 480x640 toppoly TD028TTEC1 */
++static struct s3c2410fb_mach_info gta01_lcd_cfg __initdata = {
++ .regs = {
++ .lcdcon1 = S3C2410_LCDCON1_TFT16BPP |
++ S3C2410_LCDCON1_TFT |
++ S3C2410_LCDCON1_CLKVAL(0x01), /* HCLK/4 */
++
++ .lcdcon2 = S3C2410_LCDCON2_VBPD(1) | /* 2 */
++ S3C2410_LCDCON2_LINEVAL(639) |/* 640 */
++ S3C2410_LCDCON2_VFPD(15) | /* 16 */
++ S3C2410_LCDCON2_VSPW(1), /* 2 */
++
++ .lcdcon3 = S3C2410_LCDCON3_HBPD(7) | /* 8 */
++ S3C2410_LCDCON3_HOZVAL(479) | /* 480 */
++ S3C2410_LCDCON3_HFPD(103), /* 104 */
++
++ .lcdcon4 = S3C2410_LCDCON4_MVAL(0) |
++ S3C2410_LCDCON4_HSPW(7), /* 8 */
++
++ .lcdcon5 = S3C2410_LCDCON5_FRM565 |
++ S3C2410_LCDCON5_INVVCLK |
++ S3C2410_LCDCON5_INVVLINE |
++ S3C2410_LCDCON5_INVVFRAME |
++ S3C2410_LCDCON5_PWREN |
++ S3C2410_LCDCON5_HWSWP,
++ },
++
++ .lpcsel = ((0xCE6) & ~7) | 1<<4,
++ .type = S3C2410_LCDCON1_TFT,
++
++ .width = 480,
++ .height = 640,
++
++ .xres = {
++ .min = 240,
++ .max = 480,
++ .defval = 480,
++ },
++
++ .yres = {
++ .min = 320,
++ .max = 640,
++ .defval = 640,
++ },
++
++ .bpp = {
++ .min = 1,
++ .max = 16,
++ .defval = 16,
++ },
++};
++static struct platform_device *gta01_devices[] __initdata = {
++ &s3c_device_usb,
++ &s3c_device_lcd,
++ &s3c_device_wdt,
++ &s3c_device_i2c,
++ &s3c_device_iis,
++ &s3c_device_sdi,
++ &s3c_device_usbgadget,
++ &s3c_device_nand,
++ &s3c_device_ts,
++};
++
++static struct mtd_partition gta01_nand_part[] = {
++ [0] = {
++ .name = "U-Boot",
++ .size = 0x30000,
++ .offset = 0,
++ },
++ [1] = {
++ .name = "U-Boot environment",
++ .offset = 0x30000,
++ .size = 0x4000,
++ },
++ [2] = {
++ .name = "kernel",
++ .offset = 0x34000,
++ .size = SZ_2M,
++ },
++ [3] = {
++ .name = "initrd",
++ .offset = 0x234000,
++ .size = SZ_4M,
++ },
++ [4] = {
++ .name = "jffs2",
++ .offset = 0x634000,
++ .size = 0x39cc000,
++ },
++};
++
++static struct s3c2410_nand_set gta01_nand_sets[] = {
++ [0] = {
++ .name = "NAND",
++ .nr_chips = 1,
++ .nr_partitions = ARRAY_SIZE(gta01_nand_part),
++ .partitions = gta01_nand_part,
++ },
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++ */
++
++static struct s3c2410_platform_nand gta01_nand_info = {
++ .tacls = 20,
++ .twrph0 = 60,
++ .twrph1 = 20,
++ .nr_sets = ARRAY_SIZE(gta01_nand_sets),
++ .sets = gta01_nand_sets,
++};
++
++static unsigned int mmc_millivolts[] = {
++ [MMC_VDD_145_150] = 1500,
++ [MMC_VDD_150_155] = 1500,
++ [MMC_VDD_155_160] = 1600,
++ [MMC_VDD_160_165] = 1600,
++ [MMC_VDD_165_170] = 1700,
++ [MMC_VDD_17_18] = 1800,
++ [MMC_VDD_18_19] = 1900,
++ [MMC_VDD_19_20] = 2000,
++ [MMC_VDD_20_21] = 2100,
++ [MMC_VDD_21_22] = 2200,
++ [MMC_VDD_22_23] = 2300,
++ [MMC_VDD_23_24] = 2400,
++ [MMC_VDD_24_25] = 2500,
++ [MMC_VDD_25_26] = 2600,
++ [MMC_VDD_26_27] = 2700,
++ [MMC_VDD_27_28] = 2800,
++ [MMC_VDD_28_29] = 2900,
++ [MMC_VDD_29_30] = 3000,
++ [MMC_VDD_30_31] = 3100,
++ [MMC_VDD_31_32] = 3200,
++ [MMC_VDD_32_33] = 3300,
++};
++
++static void gta01_mmc_set_power(unsigned char power_mode, unsigned short vdd)
++{
++ printk(KERN_DEBUG "mmc_set_power(power_mode=%u, vdd=%u\n",
++ power_mode, vdd);
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ /* FIXME */
++ break;
++ case GTA01v4_SYSTEM_REV:
++ /* FIXME: set GTA01_GPIO_SDMMC_ON on GTA01v4 */
++ //pcf50606_voltage_set(mmc_millivolts[vdd]);
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ /* FIXME */
++ break;
++ case GTA01Bv4_SYSTEM_REV:
++ switch (power_mode) {
++ case MMC_POWER_OFF:
++ s3c2410_gpio_setpin(GTA01_GPIO_SDMMC_ON, 1);
++ break;
++ case MMC_POWER_ON:
++ s3c2410_gpio_setpin(GTA01_GPIO_SDMMC_ON, 0);
++ break;
++ }
++ break;
++ }
++}
++
++static struct s3c24xx_mci_pdata gta01_mmc_cfg = {
++ .gpio_detect = GTA01_GPIO_nSD_DETECT,
++ .set_power = >a01_mmc_set_power,
++ .ocr_avail = MMC_VDD_32_33, /* GTA01Bv2/3/4 */
++#if 0
++ .ocr_avail = MMC_VDD_145_150|MMC_VDD_150_155|MMC_VDD_155_160|
++ MMC_VDD_160_165| MMC_VDD_165_170|MMC_VDD_17_18|
++ MMC_VDD_18_19|MMC_VDD_19_20|MMC_VDD_20_21|
++ MMC_VDD_21_22|MMC_VDD_22_23|MMC_VDD_23_24|
++ MMC_VDD_24_25|MMC_VDD_25_26|MMC_VDD_26_27|
++ MMC_VDD_27_28|MMC_VDD_28_29|MMC_VDD_29_30|
++ MMC_VDD_30_31|MMC_VDD_31_32|MMC_VDD_32_33,
++#endif
++};
++
++static void gta01_udc_command(enum s3c2410_udc_cmd_e cmd)
++{
++ printk(KERN_DEBUG "%s(%d)\n", __func__, cmd);
++
++ switch (cmd) {
++ case S3C2410_UDC_P_ENABLE:
++ s3c2410_gpio_setpin(GTA01_GPIO_USB_PULLUP, 1);
++ break;
++ case S3C2410_UDC_P_DISABLE:
++ s3c2410_gpio_setpin(GTA01_GPIO_USB_PULLUP, 0);
++ break;
++ case S3C2410_UDC_P_RESET:
++ /* FIXME! */
++ break;
++ default:
++ break;
++ }
++}
++
++/* use a work queue, since I2C API inherently schedules
++ * and we get called in hardirq context from UDC driver */
++
++struct vbus_draw {
++ struct work_struct work;
++ int ma;
++};
++static struct vbus_draw gta01_udc_vbus_drawer;
++
++static void __gta01_udc_vbus_draw(struct work_struct *work)
++{
++ /* FIXME: this is a quick fix to work around boot-time
++ * ordering problems if the s3c2410_udc is initialized
++ * before the pcf50606 driver has defined pcf50606_global */
++ if (!pcf50606_global)
++ return;
++
++ if (gta01_udc_vbus_drawer.ma >= 500) {
++ /* enable fast charge */
++ printk(KERN_DEBUG "udc: enabling fast charge\n");
++ pcf50606_charge_fast(pcf50606_global, 1);
++ } else {
++ /* disable fast charge */
++ printk(KERN_DEBUG "udc: disabling fast charge\n");
++ pcf50606_charge_fast(pcf50606_global, 0);
++ }
++}
++
++static int gta01_udc_vbus_draw(unsigned int ma)
++{
++ gta01_udc_vbus_drawer.ma = ma;
++ schedule_work(>a01_udc_vbus_drawer.work);
++
++ return 0;
++}
++
++static struct s3c2410_udc_mach_info gta01_udc_cfg = {
++ .vbus_draw = gta01_udc_vbus_draw,
++};
++
++static struct s3c2410_ts_mach_info gta01_ts_cfg = {
++ .delay = 10000,
++ .presc = 49,
++ .oversampling_shift = 2,
++};
++
++/* SPI */
++
++static struct spi_board_info gta01_spi_board_info[] __initdata = {
++ {
++ .modalias = "jbt6k74",
++ /* platform_data */
++ /* controller_data */
++ /* irq */
++ .max_speed_hz = 10 * 1000 * 1000,
++ .bus_num = 1,
++ /* chip_select */
++ },
++};
++
++
++#ifdef SPI_HARD
++static struct s3c2410_spi_info spi_cfg = {
++ .pin_cs = S3C2410_GPG3,
++ .board_size = ARRAY_SIZE(gta01_spi_board_info),
++ .board_info = gta01_spi_board_info,
++};
++#else
++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
++{
++ switch (cs) {
++ case BITBANG_CS_ACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPG3, 0);
++ break;
++ case BITBANG_CS_INACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPG3, 1);
++ break;
++ }
++}
++
++static struct s3c2410_spigpio_info spi_gpio_cfg = {
++ .pin_clk = S3C2410_GPG7,
++ .pin_mosi = S3C2410_GPG6,
++ .pin_miso = S3C2410_GPG5,
++ .board_size = ARRAY_SIZE(gta01_spi_board_info),
++ .board_info = gta01_spi_board_info,
++ .chip_select = &spi_gpio_cs,
++};
++
++static struct resource s3c_spi_lcm_resource[] = {
++ [0] = {
++ .start = S3C2410_GPG3,
++ .end = S3C2410_GPG3,
++ },
++ [1] = {
++ .start = S3C2410_GPG5,
++ .end = S3C2410_GPG5,
++ },
++ [2] = {
++ .start = S3C2410_GPG6,
++ .end = S3C2410_GPG6,
++ },
++ [3] = {
++ .start = S3C2410_GPG7,
++ .end = S3C2410_GPG7,
++ },
++};
++
++struct platform_device s3c_device_spi_lcm = {
++ .name = "s3c24xx-spi-gpio",
++ .id = 1,
++ .num_resources = ARRAY_SIZE(s3c_spi_lcm_resource),
++ .resource = s3c_spi_lcm_resource,
++ .dev = {
++ .platform_data = &spi_gpio_cfg,
++ },
++};
++#endif
++
++static struct gta01bl_machinfo backlight_machinfo = {
++ .default_intensity = 1,
++ .max_intensity = 1,
++ .limit_mask = 1,
++};
++
++static struct resource gta01_bl_resources[] = {
++ [0] = {
++ .start = GTA01_GPIO_BACKLIGHT,
++ .end = GTA01_GPIO_BACKLIGHT,
++ },
++};
++
++struct platform_device gta01_bl_dev = {
++ .name = "gta01-bl",
++ .num_resources = ARRAY_SIZE(gta01_bl_resources),
++ .resource = gta01_bl_resources,
++ .dev = {
++ .platform_data = &backlight_machinfo,
++ },
++};
++
++static struct resource gta01_led_resources[] = {
++ [0] = {
++ .start = GTA01_GPIO_VIBRATOR_ON,
++ .end = GTA01_GPIO_VIBRATOR_ON,
++ },
++};
++
++struct platform_device gta01_led_dev = {
++ .name = "gta01-led",
++ .num_resources = ARRAY_SIZE(gta01_led_resources),
++ .resource = gta01_led_resources,
++};
++
++static struct resource gta01_button_resources[] = {
++ [0] = {
++ .start = GTA01_GPIO_AUX_KEY,
++ .end = GTA01_GPIO_AUX_KEY,
++ },
++ [1] = {
++ .start = GTA01_GPIO_HOLD_KEY,
++ .end = GTA01_GPIO_HOLD_KEY,
++ },
++ [2] = {
++ .start = GTA01_GPIO_JACK_INSERT,
++ .end = GTA01_GPIO_JACK_INSERT,
++ },
++};
++
++struct platform_device gta01_button_dev = {
++ .name ="gta01-button",
++ .num_resources = ARRAY_SIZE(gta01_button_resources),
++ .resource = gta01_button_resources,
++};
++
++static struct platform_device gta01_pm_gsm_dev = {
++ .name ="gta01-pm-gsm",
++};
++
++/* USB */
++static struct s3c2410_hcd_info gta01_usb_info = {
++ .port[0] = {
++ .flags = S3C_HCDFLG_USED,
++ },
++ .port[1] = {
++ .flags = 0,
++ },
++};
++
++static void __init gta01_map_io(void)
++{
++ s3c24xx_init_io(gta01_iodesc, ARRAY_SIZE(gta01_iodesc));
++ s3c24xx_init_clocks(12*1000*1000);
++ s3c24xx_init_uarts(gta01_uartcfgs, ARRAY_SIZE(gta01_uartcfgs));
++ platform_add_devices(gta01_devices, ARRAY_SIZE(gta01_devices));
++}
++
++static irqreturn_t gta01_modem_irq(int irq, void *param)
++{
++ printk(KERN_DEBUG "modem wakeup interrupt\n");
++ return IRQ_HANDLED;
++}
++
++static void __init gta01_machine_init(void)
++{
++ if (system_rev == GTA01v4_SYSTEM_REV ||
++ system_rev == GTA01Bv2_SYSTEM_REV ||
++ system_rev == GTA01Bv3_SYSTEM_REV ||
++ system_rev == GTA01Bv4_SYSTEM_REV)
++ gta01_udc_cfg.udc_command = gta01_udc_command;
++
++ s3c_device_usb.dev.platform_data = >a01_usb_info;
++ s3c_device_nand.dev.platform_data = >a01_nand_info;
++ s3c_device_sdi.dev.platform_data = >a01_mmc_cfg;
++
++ s3c24xx_fb_set_platdata(>a01_lcd_cfg);
++
++ INIT_WORK(>a01_udc_vbus_drawer.work, __gta01_udc_vbus_draw);
++ s3c24xx_udc_set_platdata(>a01_udc_cfg);
++ set_s3c2410ts_info(>a01_ts_cfg);
++
++ /* Set LCD_RESET / XRES to high */
++ s3c2410_gpio_cfgpin(S3C2410_GPC6, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_setpin(S3C2410_GPC6, 1);
++
++#ifdef SPI_HARD
++#else
++ /* SPI chip select is gpio output */
++ s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_setpin(S3C2410_GPG3, 1);
++
++ platform_device_register(&s3c_device_spi_lcm);
++#endif
++ platform_device_register(>a01_bl_dev);
++ platform_device_register(>a01_button_dev);
++ platform_device_register(>a01_pm_gsm_dev);
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ /* just use the default (GTA01_IRQ_PCF50606) */
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ /* just use the default (GTA01_IRQ_PCF50606) */
++ gta01_led_resources[0].start =
++ gta01_led_resources[0].end = GTA01Bv2_GPIO_VIBRATOR_ON;
++ break;
++ case GTA01Bv4_SYSTEM_REV:
++ gta01_pmu_resources[0].start =
++ gta01_pmu_resources[0].end = GTA01Bv4_IRQ_PCF50606;
++ gta01_led_resources[0].start =
++ gta01_led_resources[0].end = GTA01Bv4_GPIO_VIBRATOR_ON;
++ break;
++ }
++ mangle_pmu_pdata_by_system_rev();
++ platform_device_register(>a01_pmu_dev);
++ platform_device_register(>a01_led_dev);
++
++ s3c2410_pm_init();
++
++ set_irq_type(GTA01_IRQ_MODEM, IRQT_RISING);
++ request_irq(GTA01_IRQ_MODEM, gta01_modem_irq,
++ SA_INTERRUPT, "modem", NULL);
++ enable_irq_wake(GTA01_IRQ_MODEM);
++}
++
++MACHINE_START(NEO1973_GTA01, "GTA01")
++ .phys_io = S3C2410_PA_UART,
++ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++ .boot_params = S3C2410_SDRAM_PA + 0x100,
++ .map_io = gta01_map_io,
++ .init_irq = s3c24xx_init_irq,
++ .init_machine = gta01_machine_init,
++ .timer = &s3c24xx_timer,
++MACHINE_END
++
++
+Index: linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta01.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta01.h 2007-07-16 15:41:56.617816712 +0200
+@@ -0,0 +1,70 @@
++#ifndef _GTA01_H
++#define _GTA01_H
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/irqs.h>
++
++/* Different hardware revisions, passed in ATAG_REVISION by u-boot */
++#define GTA01v3_SYSTEM_REV 0x00000130
++#define GTA01v4_SYSTEM_REV 0x00000140
++#define GTA01Bv2_SYSTEM_REV 0x00000220
++#define GTA01Bv3_SYSTEM_REV 0x00000230
++#define GTA01Bv4_SYSTEM_REV 0x00000240
++
++/* Backlight */
++struct gta01bl_machinfo {
++ unsigned int default_intensity;
++ unsigned int max_intensity;
++ unsigned int limit_mask;
++};
++
++/* Definitions common to all revisions */
++#define GTA01_GPIO_BACKLIGHT S3C2410_GPB0
++#define GTA01_GPIO_GPS_PWRON S3C2410_GPB1
++#define GTA01_GPIO_MODEM_RST S3C2410_GPB6
++#define GTA01_GPIO_MODEM_ON S3C2410_GPB7
++#define GTA01_GPIO_LCD_RESET S3C2410_GPC6
++#define GTA01_GPIO_PMU_IRQ S3C2410_GPG8
++#define GTA01_GPIO_JACK_INSERT S3C2410_GPF4
++#define GTA01_GPIO_nSD_DETECT S3C2410_GPF5
++#define GTA01_GPIO_AUX_KEY S3C2410_GPF6
++#define GTA01_GPIO_HOLD_KEY S3C2410_GPF7
++#define GTA01_GPIO_VIBRATOR_ON S3C2410_GPG11
++
++#define GTA01_IRQ_MODEM IRQ_EINT1
++#define GTA01_IRQ_JACK_INSERT IRQ_EINT4
++#define GTA01_IRQ_nSD_DETECT IRQ_EINT5
++#define GTA01_IRQ_AUX_KEY IRQ_EINT6
++#define GTA01_IRQ_PCF50606 IRQ_EINT16
++
++/* GTA01v3 */
++#define GTA01v3_GPIO_nGSM_EN S3C2410_GPG9
++
++/* GTA01v4 */
++#define GTA01_GPIO_MODEM_DNLOAD S3C2410_GPG0
++
++/* GTA01Bv2 */
++#define GTA01Bv2_GPIO_nGSM_EN S3C2410_GPF2
++#define GTA01Bv2_GPIO_VIBRATOR_ON S3C2410_GPB10
++
++/* GTA01Bv3 */
++#define GTA01_GPIO_GPS_EN_3V3 S3C2410_GPG9
++
++#define GTA01_GPIO_SDMMC_ON S3C2410_GPB2
++#define GTA01_GPIO_BT_EN S3C2410_GPB5
++#define GTA01_GPIO_AB_DETECT S3C2410_GPB8
++#define GTA01_GPIO_USB_PULLUP S3C2410_GPB9
++#define GTA01_GPIO_USB_ATTACH S3C2410_GPB10
++
++#define GTA01_GPIO_GPS_EN_2V8 S3C2410_GPG9
++#define GTA01_GPIO_GPS_EN_3V S3C2410_GPG10
++#define GTA01_GPIO_GPS_RESET S3C2410_GPC0
++
++/* GTA01Bv4 */
++#define GTA01Bv4_GPIO_nNAND_WP S3C2410_GPA16
++#define GTA01Bv4_GPIO_VIBRATOR_ON S3C2410_GPB3
++#define GTA01Bv4_GPIO_PMU_IRQ S3C2410_GPG1
++
++#define GTA01Bv4_IRQ_PCF50606 IRQ_EINT9
++
++#endif /* _GTA01_H */
Added: developers/nbd/patches-2.6.22/330-gta01-jbt6k74.patch
===================================================================
--- developers/nbd/patches-2.6.22/330-gta01-jbt6k74.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/330-gta01-jbt6k74.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,587 @@
+This driver adds support for LCM initialization of the JBT6K74 LCM
+as found on the FIC GTA01 hardware
+
+Index: linux-2.6.21.3-moko/drivers/spi/Kconfig
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/spi/Kconfig
++++ linux-2.6.21.3-moko/drivers/spi/Kconfig
+@@ -160,5 +160,9 @@
+
+ # (slave support would go here)
+
++config SPI_SLAVE_JBT6K74
++ tristate "tpo JP6K74 LCM ASIC"
++ depends on SPI_MASTER && (MACH_NEO1973_GTA01 || MACH_QT2410)
++
+ endmenu # "SPI support"
+
+Index: linux-2.6.21.3-moko/drivers/spi/Makefile
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/spi/Makefile
++++ linux-2.6.21.3-moko/drivers/spi/Makefile
+@@ -30,4 +30,5 @@
+ # ... add above this line ...
+
+ # SPI slave drivers (protocol for that link)
++obj-$(CONFIG_SPI_SLAVE_JBT6K74) += jbt6k74.o
+ # ... add above this line ...
+Index: linux-2.6.21.3-moko/drivers/spi/jbt6k74.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/drivers/spi/jbt6k74.c
+@@ -0,0 +1,543 @@
++/* Linux kernel driver for the tpo JBT6K74-AS LCM ASIC
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++#include <linux/spi/spi.h>
++
++enum jbt_register {
++ JBT_REG_SLEEP_IN = 0x10,
++ JBT_REG_SLEEP_OUT = 0x11,
++
++ JBT_REG_DISPLAY_OFF = 0x28,
++ JBT_REG_DISPLAY_ON = 0x29,
++
++ JBT_REG_RGB_FORMAT = 0x3a,
++ JBT_REG_QUAD_RATE = 0x3b,
++
++ JBT_REG_POWER_ON_OFF = 0xb0,
++ JBT_REG_BOOSTER_OP = 0xb1,
++ JBT_REG_BOOSTER_MODE = 0xb2,
++ JBT_REG_BOOSTER_FREQ = 0xb3,
++ JBT_REG_OPAMP_SYSCLK = 0xb4,
++ JBT_REG_VSC_VOLTAGE = 0xb5,
++ JBT_REG_VCOM_VOLTAGE = 0xb6,
++ JBT_REG_EXT_DISPL = 0xb7,
++ JBT_REG_OUTPUT_CONTROL = 0xb8,
++ JBT_REG_DCCLK_DCEV = 0xb9,
++ JBT_REG_DISPLAY_MODE1 = 0xba,
++ JBT_REG_DISPLAY_MODE2 = 0xbb,
++ JBT_REG_DISPLAY_MODE = 0xbc,
++ JBT_REG_ASW_SLEW = 0xbd,
++ JBT_REG_DUMMY_DISPLAY = 0xbe,
++ JBT_REG_DRIVE_SYSTEM = 0xbf,
++
++ JBT_REG_SLEEP_OUT_FR_A = 0xc0,
++ JBT_REG_SLEEP_OUT_FR_B = 0xc1,
++ JBT_REG_SLEEP_OUT_FR_C = 0xc2,
++ JBT_REG_SLEEP_IN_LCCNT_D = 0xc3,
++ JBT_REG_SLEEP_IN_LCCNT_E = 0xc4,
++ JBT_REG_SLEEP_IN_LCCNT_F = 0xc5,
++ JBT_REG_SLEEP_IN_LCCNT_G = 0xc6,
++
++ JBT_REG_GAMMA1_FINE_1 = 0xc7,
++ JBT_REG_GAMMA1_FINE_2 = 0xc8,
++ JBT_REG_GAMMA1_INCLINATION = 0xc9,
++ JBT_REG_GAMMA1_BLUE_OFFSET = 0xca,
++
++ JBT_REG_BLANK_CONTROL = 0xcf,
++ JBT_REG_BLANK_TH_TV = 0xd0,
++ JBT_REG_CKV_ON_OFF = 0xd1,
++ JBT_REG_CKV_1_2 = 0xd2,
++ JBT_REG_OEV_TIMING = 0xd3,
++ JBT_REG_ASW_TIMING_1 = 0xd4,
++ JBT_REG_ASW_TIMING_2 = 0xd5,
++
++ JBT_REG_HCLOCK_VGA = 0xec,
++ JBT_REG_HCLOCK_QVGA = 0xed,
++
++};
++
++enum jbt_state {
++ JBT_STATE_DEEP_STANDBY,
++ JBT_STATE_SLEEP,
++ JBT_STATE_NORMAL,
++};
++
++static const char *jbt_state_names[] = {
++ [JBT_STATE_DEEP_STANDBY] = "deep-standby",
++ [JBT_STATE_SLEEP] = "sleep",
++ [JBT_STATE_NORMAL] = "normal",
++};
++
++#if 1
++#define DEBUGP(x, args...) printk(KERN_ERR "%s: " x, __FUNCTION__, ## args);
++#else
++#define DEBUGP(x, args...) do { } while (0)
++#endif
++
++
++#define JBT_TX_BUF_SIZE
++struct jbt_info {
++ enum jbt_state state;
++ u_int16_t tx_buf[8];
++ struct spi_device *spi_dev;
++ u_int16_t reg_cache[0xEE];
++};
++
++#define JBT_COMMAND 0x000
++#define JBT_DATA 0x100
++
++static int jbt_reg_write_nodata(struct jbt_info *jbt, u_int8_t reg)
++{
++ int rc;
++
++ jbt->tx_buf[0] = JBT_COMMAND | reg;
++
++ rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 1*sizeof(u_int16_t));
++ if (rc == 0)
++ jbt->reg_cache[reg] = 0;
++
++ return rc;
++}
++
++
++static int jbt_reg_write(struct jbt_info *jbt, u_int8_t reg, u_int8_t data)
++{
++ int rc;
++
++ jbt->tx_buf[0] = JBT_COMMAND | reg;
++ jbt->tx_buf[1] = JBT_DATA | data;
++
++ rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 2*sizeof(u_int16_t));
++ if (rc == 0)
++ jbt->reg_cache[reg] = data;
++
++ return rc;
++}
++
++static int jbt_reg_write16(struct jbt_info *jbt, u_int8_t reg, u_int16_t data)
++{
++ int rc;
++
++ jbt->tx_buf[0] = JBT_COMMAND | reg;
++ jbt->tx_buf[1] = JBT_DATA | (data >> 8);
++ jbt->tx_buf[2] = JBT_DATA | (data & 0xff);
++
++ rc = spi_write(jbt->spi_dev, (u_int8_t *)jbt->tx_buf, 3*sizeof(u_int16_t));
++ if (rc == 0)
++ jbt->reg_cache[reg] = data;
++
++ return rc;
++}
++
++static int jbt_init_regs(struct jbt_info *jbt)
++{
++ int rc;
++
++ DEBUGP("entering\n");
++
++ rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE1, 0x01);
++ rc |= jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE2, 0x00);
++ rc |= jbt_reg_write(jbt, JBT_REG_RGB_FORMAT, 0x60);
++ rc |= jbt_reg_write(jbt, JBT_REG_DRIVE_SYSTEM, 0x10);
++ rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_OP, 0x56);
++ rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_MODE, 0x33);
++ rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_FREQ, 0x11);
++ rc |= jbt_reg_write(jbt, JBT_REG_BOOSTER_FREQ, 0x11);
++ rc |= jbt_reg_write(jbt, JBT_REG_OPAMP_SYSCLK, 0x02);
++ rc |= jbt_reg_write(jbt, JBT_REG_VSC_VOLTAGE, 0x2b);
++ rc |= jbt_reg_write(jbt, JBT_REG_VCOM_VOLTAGE, 0x40);
++ rc |= jbt_reg_write(jbt, JBT_REG_EXT_DISPL, 0x03);
++ rc |= jbt_reg_write(jbt, JBT_REG_DCCLK_DCEV, 0x04);
++ rc |= jbt_reg_write(jbt, JBT_REG_ASW_SLEW, 0x02);
++ rc |= jbt_reg_write(jbt, JBT_REG_DUMMY_DISPLAY, 0x00);
++
++ rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_A, 0x11);
++ rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_B, 0x11);
++ rc |= jbt_reg_write(jbt, JBT_REG_SLEEP_OUT_FR_C, 0x11);
++ rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
++ rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
++ rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
++ rc |= jbt_reg_write16(jbt, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
++
++ rc |= jbt_reg_write16(jbt, JBT_REG_GAMMA1_FINE_1, 0x5533);
++ rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_FINE_2, 0x00);
++ rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_INCLINATION, 0x00);
++ rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
++ rc |= jbt_reg_write(jbt, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
++
++ rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_VGA, 0x1f0);
++ rc |= jbt_reg_write(jbt, JBT_REG_BLANK_CONTROL, 0x02);
++ rc |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV, 0x0804);
++ rc |= jbt_reg_write16(jbt, JBT_REG_BLANK_TH_TV, 0x0804);
++
++ rc |= jbt_reg_write(jbt, JBT_REG_CKV_ON_OFF, 0x01);
++ rc |= jbt_reg_write16(jbt, JBT_REG_CKV_1_2, 0x0000);
++
++ rc |= jbt_reg_write16(jbt, JBT_REG_OEV_TIMING, 0x0d0e);
++ rc |= jbt_reg_write16(jbt, JBT_REG_ASW_TIMING_1, 0x11a4);
++ rc |= jbt_reg_write(jbt, JBT_REG_ASW_TIMING_2, 0x0e);
++
++#if 0
++ rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_QVGA, 0x00ff);
++ rc |= jbt_reg_write16(jbt, JBT_REG_HCLOCK_QVGA, 0x00ff);
++#endif
++
++ return rc;
++}
++
++static int standby_to_sleep(struct jbt_info *jbt)
++{
++ int rc;
++
++ DEBUGP("entering\n");
++
++ /* three times command zero */
++ rc = jbt_reg_write_nodata(jbt, 0x00);
++ mdelay(1);
++ rc = jbt_reg_write_nodata(jbt, 0x00);
++ mdelay(1);
++ rc = jbt_reg_write_nodata(jbt, 0x00);
++ mdelay(1);
++
++ /* deep standby out */
++ rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x17);
++
++ return rc;
++}
++
++static int sleep_to_normal(struct jbt_info *jbt)
++{
++ int rc;
++ DEBUGP("entering\n");
++
++ /* RGB I/F on, RAM wirte off, QVGA through, SIGCON enable */
++ rc = jbt_reg_write(jbt, JBT_REG_DISPLAY_MODE, 0x80);
++
++ /* Quad mode off */
++ rc |= jbt_reg_write(jbt, JBT_REG_QUAD_RATE, 0x00);
++
++ /* AVDD on, XVDD on */
++ rc |= jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x16);
++
++ /* Output control */
++ rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0xfff9);
++
++ /* Sleep mode off */
++ rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_OUT);
++
++ /* initialize register set */
++ rc |= jbt_init_regs(jbt);
++ return rc;
++}
++
++static int normal_to_sleep(struct jbt_info *jbt)
++{
++ int rc;
++ DEBUGP("entering\n");
++
++ rc = jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
++ rc |= jbt_reg_write16(jbt, JBT_REG_OUTPUT_CONTROL, 0x8002);
++ rc |= jbt_reg_write_nodata(jbt, JBT_REG_SLEEP_IN);
++
++ return rc;
++}
++
++static int sleep_to_standby(struct jbt_info *jbt)
++{
++ DEBUGP("entering\n");
++ return jbt_reg_write(jbt, JBT_REG_POWER_ON_OFF, 0x00);
++}
++
++/* frontend function */
++int jbt6k74_enter_state(struct jbt_info *jbt, enum jbt_state new_state)
++{
++ int rc = -EINVAL;
++
++ DEBUGP("entering(old_state=%u, new_state=%u)\n", jbt->state, new_state);
++
++ switch (jbt->state) {
++ case JBT_STATE_DEEP_STANDBY:
++ switch (new_state) {
++ case JBT_STATE_DEEP_STANDBY:
++ rc = 0;
++ break;
++ case JBT_STATE_SLEEP:
++ rc = standby_to_sleep(jbt);
++ break;
++ case JBT_STATE_NORMAL:
++ /* first transition into sleep */
++ rc = standby_to_sleep(jbt);
++ /* then transition into normal */
++ rc |= sleep_to_normal(jbt);
++ break;
++ }
++ break;
++ case JBT_STATE_SLEEP:
++ switch (new_state) {
++ case JBT_STATE_SLEEP:
++ rc = 0;
++ break;
++ case JBT_STATE_DEEP_STANDBY:
++ rc = sleep_to_standby(jbt);
++ break;
++ case JBT_STATE_NORMAL:
++ rc = sleep_to_normal(jbt);
++ break;
++ }
++ break;
++ case JBT_STATE_NORMAL:
++ switch (new_state) {
++ case JBT_STATE_NORMAL:
++ rc = 0;
++ break;
++ case JBT_STATE_DEEP_STANDBY:
++ /* first transition into sleep */
++ rc = normal_to_sleep(jbt);
++ /* then transition into deep standby */
++ rc |= sleep_to_standby(jbt);
++ break;
++ case JBT_STATE_SLEEP:
++ rc = normal_to_sleep(jbt);
++ break;
++ }
++ break;
++ }
++ if (rc == 0)
++ jbt->state = new_state;
++
++ return rc;
++}
++EXPORT_SYMBOL(jbt6k74_enter_state);
++
++int jbt6k74_display_onoff(struct jbt_info *jbt, int on)
++{
++ DEBUGP("entering\n");
++ if (on)
++ return jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_ON);
++ else
++ return jbt_reg_write_nodata(jbt, JBT_REG_DISPLAY_OFF);
++}
++EXPORT_SYMBOL(jbt6k74_display_onoff);
++
++static ssize_t state_read(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct jbt_info *jbt = dev_get_drvdata(dev);
++
++ if (jbt->state >= ARRAY_SIZE(jbt_state_names))
++ return -EIO;
++
++ return sprintf(buf, "%s\n", jbt_state_names[jbt->state]);
++}
++
++static ssize_t state_write(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct jbt_info *jbt = dev_get_drvdata(dev);
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(jbt_state_names); i++) {
++ if (!strncmp(buf, jbt_state_names[i],
++ strlen(jbt_state_names[i]))) {
++ jbt6k74_enter_state(jbt, i);
++ return count;
++ }
++ }
++
++ return -EINVAL;
++}
++
++static DEVICE_ATTR(state, 0644, state_read, state_write);
++
++static int reg_by_string(const char *name)
++{
++ if (!strcmp(name, "gamma_fine1"))
++ return JBT_REG_GAMMA1_FINE_1;
++ else if (!strcmp(name, "gamma_fine2"))
++ return JBT_REG_GAMMA1_FINE_2;
++ else if (!strcmp(name, "gamma_inclination"))
++ return JBT_REG_GAMMA1_INCLINATION;
++ else
++ return JBT_REG_GAMMA1_BLUE_OFFSET;
++}
++
++static ssize_t gamma_read(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ return strlcpy(buf, "N/A\n", PAGE_SIZE);
++}
++
++static ssize_t gamma_write(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct jbt_info *jbt = dev_get_drvdata(dev);
++ int reg = reg_by_string(attr->attr.name);
++ unsigned long val = simple_strtoul(buf, NULL, 10);
++
++ jbt_reg_write(jbt, reg, val & 0xff);
++
++ return count;
++}
++
++static DEVICE_ATTR(gamma_fine1, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_fine2, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_inclination, 0644, gamma_read, gamma_write);
++static DEVICE_ATTR(gamma_blue_offset, 0644, gamma_read, gamma_write);
++
++static struct attribute *jbt_sysfs_entries[] = {
++ &dev_attr_state.attr,
++ &dev_attr_gamma_fine1.attr,
++ &dev_attr_gamma_fine2.attr,
++ &dev_attr_gamma_inclination.attr,
++ &dev_attr_gamma_blue_offset.attr,
++ NULL,
++};
++
++static struct attribute_group jbt_attr_group = {
++ .name = NULL,
++ .attrs = jbt_sysfs_entries,
++};
++
++/* linux device model infrastructure */
++
++static int __devinit jbt_probe(struct spi_device *spi)
++{
++ int rc;
++ struct jbt_info *jbt;
++ DEBUGP("entering\n");
++
++ jbt = kzalloc(sizeof(*jbt), GFP_KERNEL);
++ if (!jbt)
++ return -ENOMEM;
++
++ jbt->spi_dev = spi;
++ jbt->state = JBT_STATE_DEEP_STANDBY;
++
++ /* since we don't have MISO connected, we can't do detection */
++
++ dev_set_drvdata(&spi->dev, jbt);
++
++ spi->mode = SPI_CPOL | SPI_CPHA;
++ spi->bits_per_word = 9;
++
++ rc = spi_setup(spi);
++ if (rc < 0) {
++ printk(KERN_ERR "error during spi_setup of jbt6k74 driver\n");
++ dev_set_drvdata(&spi->dev, NULL);
++ kfree(jbt);
++ return rc;
++ }
++
++ rc = jbt6k74_enter_state(jbt, JBT_STATE_NORMAL);
++ if (rc < 0)
++ printk(KERN_WARNING "jbt6k74: cannot enter NORMAL state\n");
++
++ jbt6k74_display_onoff(jbt, 1);
++
++ rc = sysfs_create_group(&spi->dev.kobj, &jbt_attr_group);
++ if (rc) {
++ dev_set_drvdata(&spi->dev, NULL);
++ kfree(jbt);
++ return rc;
++ }
++
++ return 0;
++}
++
++static int __devexit jbt_remove(struct spi_device *spi)
++{
++ struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++
++ DEBUGP("entering\n");
++
++ sysfs_remove_group(&spi->dev.kobj, &jbt_attr_group);
++ kfree(jbt);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int jbt_suspend(struct spi_device *spi, pm_message_t state)
++{
++ struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++ DEBUGP("entering\n");
++
++ switch (state.event) {
++ case PM_EVENT_SUSPEND:
++ case 3:
++ jbt6k74_enter_state(jbt, JBT_STATE_DEEP_STANDBY);
++ return 0;
++ default:
++ return -1;
++ }
++}
++
++static int jbt_resume(struct spi_device *spi)
++{
++ struct jbt_info *jbt = dev_get_drvdata(&spi->dev);
++ DEBUGP("entering\n");
++
++ jbt6k74_enter_state(jbt, JBT_STATE_NORMAL);
++
++ return 0;
++}
++#else
++#define jbt_suspend NULL
++#define jbt_resume NULL
++#endif
++
++static struct spi_driver jbt6k74_driver = {
++ .driver = {
++ .name = "jbt6k74",
++ .owner = THIS_MODULE,
++ },
++
++ .probe = jbt_probe,
++ .remove = __devexit_p(jbt_remove),
++ .suspend = jbt_suspend,
++ .resume = jbt_resume,
++};
++
++static int __init jbt_init(void)
++{
++ DEBUGP("entering\n");
++ return spi_register_driver(&jbt6k74_driver);
++}
++
++static void __exit jbt_exit(void)
++{
++ DEBUGP("entering\n");
++ spi_unregister_driver(&jbt6k74_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(jbt_init);
++module_exit(jbt_exit);
+Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Kconfig
+===================================================================
+--- linux-2.6.21.3-moko.orig/arch/arm/mach-s3c2410/Kconfig
++++ linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Kconfig
+@@ -106,6 +106,7 @@
+ config MACH_QT2410
+ bool "QT2410"
+ select CPU_S3C2410
++ select SPI_SLAVE_JBT6K74
+ help
+ Say Y here if you are using the Armzone QT2410
+
Added: developers/nbd/patches-2.6.22/340-gta01-backlight.patch
===================================================================
--- developers/nbd/patches-2.6.22/340-gta01-backlight.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/340-gta01-backlight.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,326 @@
+This is a backlight driver for FIC's Neo1973 Phone (codename GTA01)
+
+Index: linux-2.6.21.3-moko/drivers/video/backlight/Kconfig
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/video/backlight/Kconfig
++++ linux-2.6.21.3-moko/drivers/video/backlight/Kconfig
+@@ -48,6 +48,14 @@
+ If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to
+ enable the LCD/backlight driver.
+
++config BACKLIGHT_GTA01
++ tristate "FIC Neo1973 GTA01 Backlight Driver"
++ depends on BACKLIGHT_CLASS_DEVICE && MACH_NEO1973_GTA01
++ default y
++ help
++ If you have a FIC GTA01 say y to enable the backlight driver.
++
++
+ config BACKLIGHT_HP680
+ tristate "HP Jornada 680 Backlight Driver"
+ depends on BACKLIGHT_CLASS_DEVICE && SH_HP6XX
+Index: linux-2.6.21.3-moko/drivers/video/backlight/Makefile
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/video/backlight/Makefile
++++ linux-2.6.21.3-moko/drivers/video/backlight/Makefile
+@@ -3,6 +3,7 @@
+ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
+ obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
+ obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
++obj-$(CONFIG_BACKLIGHT_GTA01) += gta01_bl.o
+ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
+ obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+ obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
+Index: linux-2.6.21.3-moko/drivers/video/backlight/gta01_bl.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/drivers/video/backlight/gta01_bl.c
+@@ -0,0 +1,288 @@
++/*
++ * Backlight Driver for FIC GTA01 (Neo1973) GSM Phone
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * based on corgi_cl.c, Copyright (c) 2004-2006 Richard Purdie
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * TODO: implement PWM, instead of simple on/off switching
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/mutex.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++#include <linux/clk.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/regs-timer.h>
++#include <asm/arch/gta01.h>
++
++static struct backlight_properties gta01bl_prop;
++static struct backlight_device *gta01_backlight_device;
++static struct gta01bl_machinfo *bl_machinfo;
++
++static unsigned long gta01bl_flags;
++
++struct gta01bl_data {
++ int intensity;
++ struct mutex mutex;
++ struct clk *clk;
++};
++
++static struct gta01bl_data gta01bl;
++
++#define GTA01BL_SUSPENDED 0x01
++#define GTA01BL_BATTLOW 0x02
++
++#define GTA01BL_FREQ 400
++
++/* On the GTA01 / Neo1973, we use a 50 or 66MHz PCLK, which gives
++ * us a 6.25..8.25MHz DIV8 clock, which is further divided by a
++ * prescaler of 4, resulting in a 1.56..2.06MHz tick. This results in a
++ * minimum frequency of 24..31Hz. At 400Hz, we need to set the count
++ * to something like 3906..5156, providing us a way sufficient resolution
++ * for display brightness adjustment. */
++
++static int gta01bl_send_intensity(struct backlight_device *bd)
++{
++ int intensity = bd->props.brightness;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (gta01bl_flags & GTA01BL_SUSPENDED)
++ intensity = 0;
++ if (gta01bl_flags & GTA01BL_BATTLOW)
++ intensity &= bl_machinfo->limit_mask;
++
++ mutex_lock(>a01bl.mutex);
++#ifdef GTA01_BACKLIGHT_ONOFF_ONLY
++ if (intensity)
++ s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++ else
++ s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 0);
++#else
++ if (intensity == bd->props.max_brightness) {
++ s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++ s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++ } else {
++ __raw_writel(intensity & 0xffff, S3C2410_TCMPB(0));
++ s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPB0_TOUT0);
++ }
++#endif
++ mutex_unlock(>a01bl.mutex);
++
++ gta01bl.intensity = intensity;
++
++ return 0;
++}
++
++static void gta01bl_init_hw(void)
++{
++ unsigned long tcon, tcfg0, tcfg1, tcnt, pclk;
++
++ pclk = clk_get_rate(gta01bl.clk);
++
++ tcon = __raw_readl(S3C2410_TCON);
++ tcfg1 = __raw_readl(S3C2410_TCFG1);
++ tcfg0 = __raw_readl(S3C2410_TCFG0);
++
++ tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
++ tcfg1 |= S3C2410_TCFG1_MUX0_DIV8;
++
++ tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
++ tcfg0 |= (4 - 1);
++
++ tcnt = (pclk / 32) / GTA01BL_FREQ;
++ tcnt--;
++
++ __raw_writel(tcfg1, S3C2410_TCFG1);
++ __raw_writel(tcfg0, S3C2410_TCFG0);
++
++#if 0
++ __raw_writel(tcnt, S3C2410_TCNTB(0));
++ __raw_writel(tcon, S3C2410_TCON);
++ __raw_writel(tcnt, S3C2410_TCNTB(0));
++#endif
++
++ /* ensure timer is stopped */
++
++ tcon &= 0xffffff00;
++ tcon |= S3C2410_TCON_T0RELOAD;
++ tcon |= S3C2410_TCON_T0MANUALUPD;
++
++ __raw_writel(tcnt, S3C2410_TCNTB(0));
++ __raw_writel(tcnt, S3C2410_TCMPB(0));
++ __raw_writel(tcon, S3C2410_TCON);
++
++ /* start the timer */
++ tcon |= S3C2410_TCON_T0START;
++ tcon &= ~S3C2410_TCON_T0MANUALUPD;
++ __raw_writel(tcon, S3C2410_TCON);
++
++ gta01bl_prop.max_brightness = tcnt;
++}
++
++#ifdef CONFIG_PM
++static int gta01bl_suspend(struct platform_device *dev, pm_message_t state)
++{
++ gta01bl_flags |= GTA01BL_SUSPENDED;
++ gta01bl_send_intensity(gta01_backlight_device);
++ return 0;
++}
++
++static int gta01bl_resume(struct platform_device *dev)
++{
++ mutex_lock(>a01bl.mutex);
++ gta01bl_init_hw();
++ mutex_unlock(>a01bl.mutex);
++
++ gta01bl_flags &= ~GTA01BL_SUSPENDED;
++ gta01bl_send_intensity(gta01_backlight_device);
++ return 0;
++}
++#else
++#define gta01bl_suspend NULL
++#define gta01bl_resume NULL
++#endif
++
++static int gta01bl_get_intensity(struct backlight_device *bd)
++{
++ return gta01bl.intensity;
++}
++
++static int gta01bl_set_intensity(struct backlight_device *bd)
++{
++ gta01bl_send_intensity(gta01_backlight_device);
++ return 0;
++}
++
++/*
++ * Called when the battery is low to limit the backlight intensity.
++ * If limit==0 clear any limit, otherwise limit the intensity
++ */
++void gta01bl_limit_intensity(int limit)
++{
++ if (limit)
++ gta01bl_flags |= GTA01BL_BATTLOW;
++ else
++ gta01bl_flags &= ~GTA01BL_BATTLOW;
++ gta01bl_send_intensity(gta01_backlight_device);
++}
++EXPORT_SYMBOL(gta01bl_limit_intensity);
++
++
++static struct backlight_ops gta01bl_ops = {
++ .get_brightness = gta01bl_get_intensity,
++ .update_status = gta01bl_set_intensity,
++};
++
++static int __init gta01bl_probe(struct platform_device *pdev)
++{
++ struct gta01bl_machinfo *machinfo = pdev->dev.platform_data;
++
++#ifdef GTA01_BACKLIGHT_ONOFF_ONLY
++ s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++ gta01bl_prop.max_brightness = 1;
++#else
++ /* use s3c_device_timer0 for PWM */
++ gta01bl.clk = clk_get(NULL, "timers");
++ if (IS_ERR(gta01bl.clk))
++ return PTR_ERR(gta01bl.clk);
++
++ clk_enable(gta01bl.clk);
++
++ gta01bl_init_hw();
++#endif
++ mutex_init(>a01bl.mutex);
++
++ if (!machinfo->limit_mask)
++ machinfo->limit_mask = -1;
++
++ gta01_backlight_device = backlight_device_register("gta01-bl",
++ &pdev->dev, NULL,
++ >a01bl_ops);
++ if (IS_ERR(gta01_backlight_device))
++ return PTR_ERR(gta01_backlight_device);
++
++ gta01bl_prop.power = FB_BLANK_UNBLANK;
++ gta01bl_prop.brightness = gta01bl_prop.max_brightness;
++ memcpy(>a01_backlight_device->props,
++ >a01bl_prop, sizeof(gta01bl_prop));
++ gta01bl_send_intensity(gta01_backlight_device);
++
++ printk("GTA01 Backlight Driver Initialized.\n");
++ return 0;
++}
++
++static int gta01bl_remove(struct platform_device *dev)
++{
++#ifndef GTA01_BACKLIGHT_ONOFF_ONLY
++ unsigned long tcon;
++
++ /* stop this timer */
++ tcon = __raw_readl(S3C2410_TCON);
++ tcon &= 0xffffff00;
++ __raw_writel(tcon, S3C2410_TCON);
++
++ clk_disable(gta01bl.clk);
++ clk_put(gta01bl.clk);
++#endif
++ backlight_device_unregister(gta01_backlight_device);
++ mutex_destroy(>a01bl.mutex);
++
++ printk("GTA01 Backlight Driver Unloaded, constant backlight\n");
++ s3c2410_gpio_cfgpin(GTA01_GPIO_BACKLIGHT, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_setpin(GTA01_GPIO_BACKLIGHT, 1);
++
++ return 0;
++}
++
++static struct platform_driver gta01bl_driver = {
++ .probe = gta01bl_probe,
++ .remove = gta01bl_remove,
++ .suspend = gta01bl_suspend,
++ .resume = gta01bl_resume,
++ .driver = {
++ .name = "gta01-bl",
++ },
++};
++
++static int __init gta01bl_init(void)
++{
++ return platform_driver_register(>a01bl_driver);
++}
++
++static void __exit gta01bl_exit(void)
++{
++ platform_driver_unregister(>a01bl_driver);
++}
++
++module_init(gta01bl_init);
++module_exit(gta01bl_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) Backlight Driver");
++MODULE_LICENSE("GPL");
Added: developers/nbd/patches-2.6.22/350-gta01-vibrator.patch
===================================================================
--- developers/nbd/patches-2.6.22/350-gta01-vibrator.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/350-gta01-vibrator.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,171 @@
+This patch adds driver support for the FIC GTA01 vibrator device. The driver
+uses the existing LED class driver framework, since there's a lot of
+similarity between the LED and the vibrator function.
+
+Index: linux-2.6.21-moko/drivers/leds/leds-gta01.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/leds/leds-gta01.c
+@@ -0,0 +1,133 @@
++/*
++ * LED driver for the FIC GTA01 (Neo1973) GSM Phone Vibrator
++ *
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * TODO: Implement PWM support for GTA01Bv4 and later
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++#include <asm/hardware.h>
++#include <asm/mach-types.h>
++#include <asm/arch/gta01.h>
++
++struct gta01_vib_priv
++{
++ struct led_classdev cdev;
++ unsigned int gpio;
++ unsigned int has_pwm;
++};
++
++static inline struct gta01_vib_priv *pdev_to_vpriv(struct platform_device *dev)
++{
++ return platform_get_drvdata(dev);
++}
++
++static inline struct gta01_vib_priv *to_vpriv(struct led_classdev *led_cdev)
++{
++ return dev_get_drvdata(led_cdev->class_dev->dev);
++}
++
++static void gta01vib_vib_set(struct led_classdev *led_cdev, enum led_brightness value)
++{
++ struct gta01_vib_priv *vp = to_vpriv(led_cdev);
++
++ if (value)
++ s3c2410_gpio_setpin(vp->gpio, 1);
++ else
++ s3c2410_gpio_setpin(vp->gpio, 0);
++}
++
++static struct led_classdev gta01_vib_led = {
++ .name = "gta01:vibrator",
++ .brightness_set = gta01vib_vib_set,
++};
++
++#ifdef CONFIG_PM
++static int gta01vib_suspend(struct platform_device *dev, pm_message_t state)
++{
++ led_classdev_suspend(>a01_vib_led);
++ return 0;
++}
++
++static int gta01vib_resume(struct platform_device *dev)
++{
++ led_classdev_resume(>a01_vib_led);
++ return 0;
++}
++#endif
++
++static int gta01vib_probe(struct platform_device *pdev)
++{
++ struct gta01_vib_priv *vp;
++ struct resource *r;
++
++ if (!machine_is_neo1973_gta01())
++ return -EIO;
++
++ r = platform_get_resource(pdev, 0, 0);
++ if (!r || !r->start)
++ return -EIO;
++
++ vp = kzalloc(sizeof(struct gta01_vib_priv), GFP_KERNEL);
++ if (!vp)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, vp);
++
++ vp->gpio = r->start;
++
++ if (vp->gpio == S3C2410_GPB3)
++ vp->has_pwm = 1;
++
++ return led_classdev_register(&pdev->dev, >a01_vib_led);
++}
++
++static int gta01vib_remove(struct platform_device *pdev)
++{
++ struct gta01_vib_priv *vp = pdev_to_vpriv(pdev);
++
++ led_classdev_unregister(>a01_vib_led);
++ platform_set_drvdata(pdev, NULL);
++ kfree(vp);
++
++ return 0;
++}
++
++static struct platform_driver gta01vib_driver = {
++ .probe = gta01vib_probe,
++ .remove = gta01vib_remove,
++#ifdef CONFIG_PM
++ .suspend = gta01vib_suspend,
++ .resume = gta01vib_resume,
++#endif
++ .driver = {
++ .name = "gta01-led",
++ },
++};
++
++static int __init gta01vib_init(void)
++{
++ return platform_driver_register(>a01vib_driver);
++}
++
++static void __exit gta01vib_exit(void)
++{
++ platform_driver_unregister(>a01vib_driver);
++}
++
++module_init(gta01vib_init);
++module_exit(gta01vib_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 Vibrator driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.21-moko/drivers/leds/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/leds/Kconfig
++++ linux-2.6.21-moko/drivers/leds/Kconfig
+@@ -94,6 +94,12 @@
+ help
+ This option enables support for the front LED on Cobalt Server
+
++config LEDS_GTA01
++ tristate "Vibrator Support for the FIC Neo1973 (GTA01) Vibrator"
++ depends on LEDS_CLASS && MACH_NEO1973_GTA01
++ help
++ This option enables support for the Vibrator on the FIC Neo1973.
++
+ comment "LED Triggers"
+
+ config LEDS_TRIGGERS
+Index: linux-2.6.21-moko/drivers/leds/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/leds/Makefile
++++ linux-2.6.21-moko/drivers/leds/Makefile
+@@ -16,6 +16,7 @@
+ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
+ obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
+ obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o
++obj-$(CONFIG_LEDS_GTA01) += leds-gta01.o
+
+ # LED Triggers
+ obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
Added: developers/nbd/patches-2.6.22/360-gta01-inputdevice.patch
===================================================================
--- developers/nbd/patches-2.6.22/360-gta01-inputdevice.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/360-gta01-inputdevice.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,289 @@
+This provides support for the GTA01 keyboard
+
+Index: linux-2.6.21-moko/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/keyboard/Kconfig
++++ linux-2.6.21-moko/drivers/input/keyboard/Kconfig
+@@ -229,4 +229,16 @@
+ To compile this driver as a module, choose M here: the
+ module will be called gpio-keys.
+
++config KEYBOARD_GTA01
++ tristate "FIC Neo1973 (GTA01) buttons"
++ depends on MACH_NEO1973_GTA01
++ default y
++ help
++ Say Y here to enable the buttons on the FIC Neo1973 (GTA01)
++ GSM phone.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gta01kbd.
++
++
+ endif
+Index: linux-2.6.21-moko/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/keyboard/Makefile
++++ linux-2.6.21-moko/drivers/input/keyboard/Makefile
+@@ -13,6 +13,7 @@
+ obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
++obj-$(CONFIG_KEYBOARD_GTA01) += gta01kbd.o
+ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
+ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+Index: linux-2.6.21-moko/drivers/input/keyboard/gta01kbd.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/input/keyboard/gta01kbd.c
+@@ -0,0 +1,249 @@
++/*
++ * Keyboard driver for FIC GTA01 (Neo1973) GSM phone
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gta01.h>
++
++struct gta01kbd {
++ struct input_dev *input;
++ unsigned int suspended;
++ unsigned long suspend_jiffies;
++};
++
++static irqreturn_t gta01kbd_aux_irq(int irq, void *dev_id)
++{
++ struct gta01kbd *gta01kbd_data = dev_id;
++
++ /* FIXME: use GPIO from platform_dev resources */
++ if (s3c2410_gpio_getpin(GTA01_GPIO_AUX_KEY))
++ input_report_key(gta01kbd_data->input, KEY_PHONE, 0);
++ else
++ input_report_key(gta01kbd_data->input, KEY_PHONE, 1);
++
++ input_sync(gta01kbd_data->input);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t gta01kbd_hold_irq(int irq, void *dev_id)
++{
++ struct gta01kbd *gta01kbd_data = dev_id;
++
++ /* FIXME: use GPIO from platform_dev resources */
++ if (s3c2410_gpio_getpin(GTA01_GPIO_HOLD_KEY))
++ input_report_key(gta01kbd_data->input, KEY_PAUSE, 1);
++ else
++ input_report_key(gta01kbd_data->input, KEY_PAUSE, 0);
++
++ input_sync(gta01kbd_data->input);
++
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t gta01kbd_headphone_irq(int irq, void *dev_id)
++{
++ struct gta01kbd *gta01kbd_data = dev_id;
++
++ /* FIXME: use GPIO from platform_dev resources */
++ if (s3c2410_gpio_getpin(GTA01_GPIO_JACK_INSERT))
++ input_report_switch(gta01kbd_data->input, SW_HEADPHONE_INSERT, 1);
++ else
++ input_report_switch(gta01kbd_data->input, SW_HEADPHONE_INSERT, 0);
++
++ input_sync(gta01kbd_data->input);
++
++ return IRQ_HANDLED;
++}
++
++#ifdef CONFIG_PM
++static int gta01kbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++ gta01kbd->suspended = 1;
++
++ return 0;
++}
++
++static int gta01kbd_resume(struct platform_device *dev)
++{
++ struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++ gta01kbd->suspended = 0;
++
++ return 0;
++}
++#else
++#define gta01kbd_suspend NULL
++#define gta01kbd_resume NULL
++#endif
++
++static int gta01kbd_probe(struct platform_device *pdev)
++{
++ struct gta01kbd *gta01kbd;
++ struct input_dev *input_dev;
++ int irq_aux, irq_hold, irq_jack;
++
++ gta01kbd = kzalloc(sizeof(struct gta01kbd), GFP_KERNEL);
++ input_dev = input_allocate_device();
++ if (!gta01kbd || !input_dev) {
++ kfree(gta01kbd);
++ input_free_device(input_dev);
++ return -ENOMEM;
++ }
++
++ if (pdev->resource[0].flags != 0)
++ return -EINVAL;
++
++ irq_aux = s3c2410_gpio_getirq(pdev->resource[0].start);
++ if (irq_aux < 0)
++ return -EINVAL;
++
++ irq_hold = s3c2410_gpio_getirq(pdev->resource[1].start);
++ if (irq_hold < 0)
++ return -EINVAL;
++
++ irq_jack = s3c2410_gpio_getirq(pdev->resource[2].start);
++ if (irq_jack < 0)
++ return -EINVAL;
++
++ platform_set_drvdata(pdev, gta01kbd);
++
++ gta01kbd->input = input_dev;
++
++#if 0
++ spin_lock_init(>a01kbd->lock);
++ /* Init Keyboard rescan timer */
++ init_timer(&corgikbd->timer);
++ corgikbd->timer.function = corgikbd_timer_callback;
++ corgikbd->timer.data = (unsigned long) corgikbd;
++
++ /* Init Hinge Timer */
++ init_timer(&corgikbd->htimer);
++ corgikbd->htimer.function = corgikbd_hinge_timer;
++ corgikbd->htimer.data = (unsigned long) corgikbd;
++
++ corgikbd->suspend_jiffies=jiffies;
++#endif
++
++ input_dev->name = "Neo1973 Buttons";
++ input_dev->phys = "gta01kbd/input0";
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++ input_dev->cdev.dev = &pdev->dev;
++ input_dev->private = gta01kbd;
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_SW);
++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++ set_bit(KEY_PHONE, input_dev->keybit);
++ set_bit(KEY_PAUSE, input_dev->keybit);
++
++ input_register_device(gta01kbd->input);
++
++ if (request_irq(irq_aux, gta01kbd_aux_irq,
++ SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
++ "Neo1973 AUX button", gta01kbd)) {
++ dev_err(&pdev->dev, "Can't get IRQ %u\n", irq_aux);
++ goto out_aux;
++ }
++ enable_irq_wake(irq_aux);
++
++ if (request_irq(irq_hold, gta01kbd_hold_irq,
++ SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
++ "Neo1973 HOLD button", gta01kbd)) {
++ dev_err(&pdev->dev, "Can't get IRQ %u\n", irq_hold);
++ goto out_hold;
++ }
++ enable_irq_wake(irq_hold);
++
++ if (request_irq(irq_jack, gta01kbd_headphone_irq,
++ SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
++ "Neo1973 Headphone Jack", gta01kbd)) {
++ dev_err(&pdev->dev, "Can't get IRQ %u\n", irq_jack);
++ goto out_jack;
++ }
++ enable_irq_wake(irq_jack);
++#if 0
++ mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
++#endif
++ return 0;
++
++out_jack:
++ free_irq(irq_hold, gta01kbd);
++out_hold:
++ free_irq(irq_aux, gta01kbd);
++out_aux:
++ input_unregister_device(gta01kbd->input);
++ input_free_device(gta01kbd->input);
++ platform_set_drvdata(pdev, NULL);
++ kfree(gta01kbd);
++
++ return -ENODEV;
++}
++
++static int gta01kbd_remove(struct platform_device *pdev)
++{
++ struct gta01kbd *gta01kbd = platform_get_drvdata(pdev);
++
++ free_irq(s3c2410_gpio_getirq(pdev->resource[2].start), gta01kbd);
++ free_irq(s3c2410_gpio_getirq(pdev->resource[1].start), gta01kbd);
++ free_irq(s3c2410_gpio_getirq(pdev->resource[0].start), gta01kbd);
++#if 0
++ del_timer_sync(&corgikbd->htimer);
++ del_timer_sync(&corgikbd->timer);
++#endif
++ input_unregister_device(gta01kbd->input);
++ input_free_device(gta01kbd->input);
++ platform_set_drvdata(pdev, NULL);
++ kfree(gta01kbd);
++
++ return 0;
++}
++
++static struct platform_driver gta01kbd_driver = {
++ .probe = gta01kbd_probe,
++ .remove = gta01kbd_remove,
++ .suspend = gta01kbd_suspend,
++ .resume = gta01kbd_resume,
++ .driver = {
++ .name = "gta01-button",
++ },
++};
++
++static int __devinit gta01kbd_init(void)
++{
++ return platform_driver_register(>a01kbd_driver);
++}
++
++static void __exit gta01kbd_exit(void)
++{
++ platform_driver_unregister(>a01kbd_driver);
++}
++
++module_init(gta01kbd_init);
++module_exit(gta01kbd_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC Neo1973 (GTA01) Buttons Driver");
++MODULE_LICENSE("GPL");
Added: developers/nbd/patches-2.6.22/370-gta01-power_control.patch
===================================================================
--- developers/nbd/patches-2.6.22/370-gta01-power_control.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/370-gta01-power_control.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,953 @@
+Index: linux-2.6.21.3-moko/arch/arm/common/Makefile
+===================================================================
+--- linux-2.6.21.3-moko.orig/arch/arm/common/Makefile
++++ linux-2.6.21.3-moko/arch/arm/common/Makefile
+@@ -17,3 +17,4 @@
+ obj-$(CONFIG_SHARP_SCOOP) += scoop.o
+ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
+ obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
++obj-$(CONFIG_MACH_NEO1973_GTA01)+= gta01_pm_gsm.o gta01_pm_gps.o gta01_pm_bt.o
+Index: linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gps.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gps.c
+@@ -0,0 +1,558 @@
++/*
++ * GPS Power Management code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gta01.h>
++
++/* This is the 2.8V supply for the RTC crystal, the mail clock crystal and
++ * the input to VDD_RF */
++static void gps_power_2v8_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ if (on)
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_IOREG, 2800);
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_IOREG, on);
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_2V8, on);
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ break;
++ }
++}
++
++static int gps_power_2v8_get(void)
++{
++ int ret = 0;
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_IOREG) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_IOREG) == 2800)
++ ret = 1;
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_2V8))
++ ret = 1;
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ break;
++ }
++
++ return ret;
++}
++
++/* This is the 3V supply (AVDD) for the external RF frontend (LNA bias) */
++static void gps_power_3v_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ if (on)
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, 3000);
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, on);
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_3V, on);
++ break;
++ }
++}
++
++static int gps_power_3v_get(void)
++{
++ int ret = 0;
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) == 3000)
++ ret = 1;
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_3V))
++ ret = 1;
++ break;
++ }
++
++ return ret;
++}
++
++/* This is the 3.3V supply for VDD_IO and VDD_LPREG input */
++static void gps_power_3v3_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ if (on)
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_DCD, 3300);
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_DCD, on);
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_EN_3V3, on);
++ break;
++ }
++}
++
++static int gps_power_3v3_get(void)
++{
++ int ret = 0;
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_DCD) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_DCD) == 3300)
++ ret = 1;
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_EN_3V3))
++ ret = 1;
++ break;
++ }
++
++ return ret;
++}
++
++/* This is the 2.5V supply for VDD_PLLREG and VDD_COREREG input */
++static void gps_power_2v5_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ /* This is CORE_1V8 and cannot be disabled */
++ break;
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (on)
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_D2REG, 2500);
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D2REG, on);
++ break;
++ }
++}
++
++static int gps_power_2v5_get(void)
++{
++ int ret = 0;
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ /* This is CORE_1V8 and cannot be disabled */
++ ret = 1;
++ break;
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_D2REG) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_D2REG) == 2500)
++ ret = 1;
++ break;
++ }
++
++ return ret;
++}
++
++/* This is the 1.5V supply for VDD_CORE */
++static void gps_power_1v5_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ /* This is switched via 2v5 */
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (on)
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_DCD, 1500);
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_DCD, on);
++ break;
++ }
++}
++
++static int gps_power_1v5_get(void)
++{
++ int ret = 0;
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ /* This is switched via 2v5 */
++ ret = 1;
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_DCD) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_DCD) == 1500)
++ ret = 1;
++ break;
++ }
++
++ return ret;
++}
++
++/* This is the POWERON pin */
++static void gps_pwron_set(int on)
++{
++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_PWRON, on);
++}
++
++static int gps_pwron_get(void)
++{
++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_PWRON))
++ return 1;
++ else
++ return 0;
++}
++
++/* This is the nRESET pin */
++static void gps_rst_set(int on)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ pcf50606_gpo0_set(pcf50606_global, on);
++ break;
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ s3c2410_gpio_setpin(GTA01_GPIO_GPS_RESET, on);
++ break;
++ }
++}
++
++static int gps_rst_get(void)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ if (pcf50606_gpo0_get(pcf50606_global))
++ return 1;
++ break;
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ if (s3c2410_gpio_getpin(GTA01_GPIO_GPS_RESET))
++ return 1;
++ break;
++ }
++
++ return 0;
++}
++
++static ssize_t power_gps_read(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ int ret = 0;
++
++ if (!strcmp(attr->attr.name, "power_tcxo_2v8")) {
++ ret = gps_power_2v8_get();
++ } else if (!strcmp(attr->attr.name, "power_avdd_3v")) {
++ ret = gps_power_3v_get();
++ } else if (!strcmp(attr->attr.name, "pwron")) {
++ ret = gps_pwron_get();
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ ret = gps_rst_get();
++ } else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) {
++ ret = gps_power_3v3_get();
++ } else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) {
++ ret = gps_power_2v5_get();
++ } else if (!strcmp(attr->attr.name, "power_core_1v5") ||
++ !strcmp(attr->attr.name, "power_vdd_core_1v5")) {
++ ret = gps_power_1v5_get();
++ }
++
++ if (ret)
++ return strlcpy(buf, "1\n", 3);
++ else
++ return strlcpy(buf, "0\n", 3);
++}
++
++static ssize_t power_gps_write(struct device *dev,
++ struct device_attribute *attr, const char *buf,
++ size_t count)
++{
++ unsigned long on = simple_strtoul(buf, NULL, 10);
++
++ if (!strcmp(attr->attr.name, "power_tcxo_2v8")) {
++ gps_power_2v8_set(on);
++ } else if (!strcmp(attr->attr.name, "power_avdd_3v")) {
++ gps_power_3v_set(on);
++ } else if (!strcmp(attr->attr.name, "pwron")) {
++ gps_pwron_set(on);
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ gps_rst_set(on);
++ } else if (!strcmp(attr->attr.name, "power_lp_io_3v3")) {
++ gps_power_3v3_set(on);
++ } else if (!strcmp(attr->attr.name, "power_pll_core_2v5")) {
++ gps_power_2v5_set(on);
++ } else if (!strcmp(attr->attr.name, "power_core_1v5") ||
++ !strcmp(attr->attr.name, "power_vdd_core_1v5")) {
++ gps_power_1v5_set(on);
++ }
++
++ return count;
++}
++
++static void gps_power_sequence_up(void)
++{
++ /* According to PMB2520 Data Sheet, Rev. 2006-06-05,
++ * Chapter 4.2.2 */
++
++ /* nRESET must be asserted low */
++ gps_rst_set(0);
++
++ /* POWERON must be de-asserted (low) */
++ gps_pwron_set(0);
++
++ /* Apply VDD_IO and VDD_LPREG_IN */
++ gps_power_3v3_set(1);
++
++ /* VDD_COREREG_IN, VDD_PLLREG_IN */
++ gps_power_1v5_set(1);
++ gps_power_2v5_set(1);
++
++ /* and VDD_RF may be applied */
++ gps_power_2v8_set(1);
++
++ /* We need to enable AVDD, since in GTA01Bv3 it is
++ * shared with RFREG_IN */
++ gps_power_3v_set(1);
++
++ msleep(3); /* Is 3ms enough? */
++
++ /* De-asert nRESET */
++ gps_rst_set(1);
++
++ /* Switch power on */
++ gps_pwron_set(1);
++
++}
++
++static void gps_power_sequence_down(void)
++{
++ /* According to PMB2520 Data Sheet, Rev. 2006-06-05,
++ * Chapter 4.2.3.1 */
++ gps_pwron_set(0);
++
++ /* Don't disable AVDD before PWRON is cleared, since
++ * in GTA01Bv3, AVDD and RFREG_IN are shared */
++ gps_power_3v_set(0);
++
++ /* Remove VDD_COREREG_IN, VDD_PLLREG_IN and VDD_REFREG_IN */
++ gps_power_1v5_set(0);
++ gps_power_2v5_set(0);
++ gps_power_2v8_set(0);
++
++ /* Remove VDD_LPREG_IN and VDD_IO */
++ gps_power_3v3_set(0);
++}
++
++
++static ssize_t power_sequence_read(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return strlcpy(buf, "power_up power_down\n", PAGE_SIZE);
++}
++
++static ssize_t power_sequence_write(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ dev_dbg(dev, "wrote: '%s'\n", buf);
++
++ if (!strncmp(buf, "power_up", 8))
++ gps_power_sequence_up();
++ else if (!strncmp(buf, "power_down", 10))
++ gps_power_sequence_down();
++ else
++ return -EINVAL;
++
++ return count;
++}
++
++static DEVICE_ATTR(power_tcxo_2v8, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_avdd_3v, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(pwron, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(reset, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_lp_io_3v3, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_pll_core_2v5, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_core_1v5, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_vdd_core_1v5, 0644, power_gps_read, power_gps_write);
++static DEVICE_ATTR(power_sequence, 0644, power_sequence_read, power_sequence_write);
++
++#ifdef CONFIG_PM
++static int gta01_pm_gps_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ /* FIXME */
++ gps_power_sequence_down();
++
++ return 0;
++}
++
++static int gta01_pm_gps_resume(struct platform_device *pdev)
++{
++ /* FIXME */
++ gps_power_sequence_up();
++
++ return 0;
++}
++#else
++#define gta01_pm_gps_suspend NULL
++#define gta01_pm_gps_resume NULL
++#endif
++
++static struct attribute *gta01_gps_sysfs_entries[] = {
++ &dev_attr_power_avdd_3v.attr,
++ &dev_attr_pwron.attr,
++ &dev_attr_reset.attr,
++ &dev_attr_power_lp_io_3v3.attr,
++ &dev_attr_power_pll_core_2v5.attr,
++ &dev_attr_power_sequence.attr,
++ NULL, /* power_core_1v5 */
++ NULL, /* power_vdd_core_1v5 */
++ NULL /* terminating entry */
++};
++
++static struct attribute_group gta01_gps_attr_group = {
++ .name = NULL,
++ .attrs = gta01_gps_sysfs_entries,
++};
++
++static int __init gta01_pm_gps_probe(struct platform_device *pdev)
++{
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_PWRON, S3C2410_GPIO_OUTPUT);
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ break;
++ case GTA01v4_SYSTEM_REV:
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT);
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_3V3, S3C2410_GPIO_OUTPUT);
++ /* fallthrough */
++ case GTA01Bv2_SYSTEM_REV:
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_2V8, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_EN_3V, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_cfgpin(GTA01_GPIO_GPS_RESET, S3C2410_GPIO_OUTPUT);
++ break;
++ default:
++ dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
++ "AGPS PM features not available!!!\n",
++ system_rev);
++ return -1;
++ break;
++ }
++
++ gps_power_sequence_down();
++
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-2] =
++ &dev_attr_power_tcxo_2v8.attr;
++ break;
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-3] =
++ &dev_attr_power_core_1v5.attr;
++ gta01_gps_sysfs_entries[ARRAY_SIZE(gta01_gps_sysfs_entries)-2] =
++ &dev_attr_power_vdd_core_1v5.attr;
++ break;
++ }
++
++ return sysfs_create_group(&pdev->dev.kobj, >a01_gps_attr_group);
++}
++
++static int gta01_pm_gps_remove(struct platform_device *pdev)
++{
++ gps_power_sequence_down();
++ sysfs_remove_group(&pdev->dev.kobj, >a01_gps_attr_group);
++
++ return 0;
++}
++
++static struct platform_driver gta01_pm_gps_driver = {
++ .probe = gta01_pm_gps_probe,
++ .remove = gta01_pm_gps_remove,
++ .suspend = gta01_pm_gps_suspend,
++ .resume = gta01_pm_gps_resume,
++ .driver = {
++ .name = "gta01-pm-gps",
++ },
++};
++
++static int __devinit gta01_pm_gps_init(void)
++{
++ return platform_driver_register(>a01_pm_gps_driver);
++}
++
++static void gta01_pm_gps_exit(void)
++{
++ platform_driver_unregister(>a01_pm_gps_driver);
++}
++
++module_init(gta01_pm_gps_init);
++module_exit(gta01_pm_gps_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) GPS Power Management");
+Index: linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gsm.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gsm.c
+@@ -0,0 +1,217 @@
++/*
++ * GSM Management code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/console.h>
++#include <linux/errno.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gta01.h>
++
++struct gta01pm_priv {
++ int gpio_ngsm_en;
++ struct console *con;
++};
++
++static struct gta01pm_priv gta01_gsm;
++
++static struct console *find_s3c24xx_console(void)
++{
++ struct console *con;
++
++ acquire_console_sem();
++
++ for (con = console_drivers; con; con = con->next) {
++ if (!strcmp(con->name, "ttySAC"))
++ break;
++ }
++
++ release_console_sem();
++
++ return con;
++}
++
++static ssize_t gsm_read(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ if (!strcmp(attr->attr.name, "power_on")) {
++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_ON))
++ goto out_1;
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_RST))
++ goto out_1;
++ } else if (!strcmp(attr->attr.name, "download")) {
++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_DNLOAD))
++ goto out_1;
++ }
++
++ return strlcpy(buf, "0\n", 3);
++out_1:
++ return strlcpy(buf, "1\n", 3);
++}
++
++static ssize_t gsm_write(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ unsigned long on = simple_strtoul(buf, NULL, 10);
++
++ if (!strcmp(attr->attr.name, "power_on")) {
++ if (on) {
++ dev_info(dev, "powering up GSM, thus disconnecting "
++ "serial console\n");
++
++ if (gta01_gsm.con)
++ console_stop(gta01_gsm.con);
++
++ if (gta01_gsm.gpio_ngsm_en)
++ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 0);
++
++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 1);
++ } else {
++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_ON, 0);
++
++ if (gta01_gsm.gpio_ngsm_en)
++ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 1);
++
++ if (gta01_gsm.con)
++ console_start(gta01_gsm.con);
++
++ dev_info(dev, "powered down GSM, thus enabling "
++ "serial console\n");
++ }
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_RST, on);
++ } else if (!strcmp(attr->attr.name, "download")) {
++ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_DNLOAD, on);
++ }
++
++ return count;
++}
++
++static DEVICE_ATTR(power_on, 0644, gsm_read, gsm_write);
++static DEVICE_ATTR(reset, 0644, gsm_read, gsm_write);
++static DEVICE_ATTR(download, 0644, gsm_read, gsm_write);
++
++#ifdef CONFIG_PM
++static int gta01_gsm_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we
++ * don't need to do anything here */
++
++ return 0;
++}
++
++static int gta01_gsm_resume(struct platform_device *pdev)
++{
++ /* GPIO state is saved/restored by S3C2410 core GPIO driver, so we
++ * don't need to do anything here */
++
++ /* Make sure that the kernel console on the serial port is still
++ * disabled. FIXME: resume ordering race with serial driver! */
++ if (s3c2410_gpio_getpin(GTA01_GPIO_MODEM_ON) && gta01_gsm.con)
++ console_stop(gta01_gsm.con);
++
++ return 0;
++}
++#else
++#define gta01_gsm_suspend NULL
++#define gta01_gsm_resume NULL
++#endif
++
++static struct attribute *gta01_gsm_sysfs_entries[] = {
++ &dev_attr_power_on.attr,
++ &dev_attr_reset.attr,
++ NULL,
++ NULL
++};
++
++static struct attribute_group gta01_gsm_attr_group = {
++ .name = NULL,
++ .attrs = gta01_gsm_sysfs_entries,
++};
++
++static int __init gta01_gsm_probe(struct platform_device *pdev)
++{
++ switch (system_rev) {
++ case GTA01v3_SYSTEM_REV:
++ gta01_gsm.gpio_ngsm_en = GTA01v3_GPIO_nGSM_EN;
++ break;
++ case GTA01v4_SYSTEM_REV:
++ gta01_gsm.gpio_ngsm_en = 0;
++ break;
++ case GTA01Bv2_SYSTEM_REV:
++ case GTA01Bv3_SYSTEM_REV:
++ case GTA01Bv4_SYSTEM_REV:
++ gta01_gsm.gpio_ngsm_en = GTA01Bv2_GPIO_nGSM_EN;
++ s3c2410_gpio_setpin(GTA01v3_GPIO_nGSM_EN, 0);
++ break;
++ default:
++ dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
++ "some PM features not available!!!\n",
++ system_rev);
++ break;
++ }
++
++ switch (system_rev) {
++ case GTA01v4_SYSTEM_REV:
++ case GTA01Bv2_SYSTEM_REV:
++ gta01_gsm_sysfs_entries[ARRAY_SIZE(gta01_gsm_sysfs_entries)-2] =
++ &dev_attr_download.attr;
++ break;
++ default:
++ break;
++ }
++
++ gta01_gsm.con = find_s3c24xx_console();
++ if (!gta01_gsm.con)
++ dev_warn(&pdev->dev, "cannot find S3C24xx console driver\n");
++
++ return sysfs_create_group(&pdev->dev.kobj, >a01_gsm_attr_group);
++}
++
++static int gta01_gsm_remove(struct platform_device *pdev)
++{
++ sysfs_remove_group(&pdev->dev.kobj, >a01_gsm_attr_group);
++
++ return 0;
++}
++
++static struct platform_driver gta01_gsm_driver = {
++ .probe = gta01_gsm_probe,
++ .remove = gta01_gsm_remove,
++ .suspend = gta01_gsm_suspend,
++ .resume = gta01_gsm_resume,
++ .driver = {
++ .name = "gta01-pm-gsm",
++ },
++};
++
++static int __devinit gta01_gsm_init(void)
++{
++ return platform_driver_register(>a01_gsm_driver);
++}
++
++static void gta01_gsm_exit(void)
++{
++ platform_driver_unregister(>a01_gsm_driver);
++}
++
++module_init(gta01_gsm_init);
++module_exit(gta01_gsm_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("FIC GTA01 (Neo1973) GSM Management");
+Index: linux-2.6.21.3-moko/arch/arm/common/gta01_pm_bt.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/arch/arm/common/gta01_pm_bt.c
+@@ -0,0 +1,154 @@
++/*
++ * Bluetooth PM code for the FIC Neo1973 GSM Phone
++ *
++ * (C) 2007 by OpenMoko Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gta01.h>
++
++#define DRVMSG "FIC GTA01 (Neo1973) Bluetooth Power Management"
++
++static ssize_t bt_read(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ if (!strcmp(attr->attr.name, "power_on")) {
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) == 3100)
++ goto out_1;
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ if (s3c2410_gpio_getpin(GTA01_GPIO_BT_EN) == 0)
++ goto out_1;
++ }
++
++ return strlcpy(buf, "0\n", 3);
++out_1:
++ return strlcpy(buf, "1\n", 3);
++}
++
++static ssize_t bt_write(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ unsigned long on = simple_strtoul(buf, NULL, 10);
++
++ if (!strcmp(attr->attr.name, "power_on")) {
++ /* if we are powering up, assert reset, then power, then
++ * release reset */
++ if (on) {
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG,
++ 3100);
++ }
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, on);
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on);
++ } else if (!strcmp(attr->attr.name, "reset")) {
++ /* reset is low-active, so we need to invert */
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on ? 0 : 1 );
++ }
++
++ return count;
++}
++
++static DEVICE_ATTR(power_on, 0644, bt_read, bt_write);
++static DEVICE_ATTR(reset, 0644, bt_read, bt_write);
++
++#ifdef CONFIG_PM
++static int gta01_bt_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ dev_info(&pdev->dev, DRVMSG ": suspending\n");
++ /* FIXME: The PMU should save the PMU status, and the GPIO code should
++ * preserve the GPIO level, so there shouldn't be anything left to do
++ * for us, should there? */
++
++ return 0;
++}
++
++static int gta01_bt_resume(struct platform_device *pdev)
++{
++ dev_info(&pdev->dev, DRVMSG ": resuming\n");
++
++ return 0;
++}
++#else
++#define gta01_bt_suspend NULL
++#define gta01_bt_resume NULL
++#endif
++
++static struct attribute *gta01_bt_sysfs_entries[] = {
++ &dev_attr_power_on.attr,
++ &dev_attr_reset.attr,
++ NULL
++};
++
++static struct attribute_group gta01_bt_attr_group = {
++ .name = NULL,
++ .attrs = gta01_bt_sysfs_entries,
++};
++
++static int __init gta01_bt_probe(struct platform_device *pdev)
++{
++ dev_info(&pdev->dev, DRVMSG ": starting\n");
++
++ /* we make sure that the voltage is off */
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, 0);
++ /* we pull reset to low to make sure that the chip doesn't
++ * drain power through the reset line */
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
++
++ return sysfs_create_group(&pdev->dev.kobj, >a01_bt_attr_group);
++}
++
++static int gta01_bt_remove(struct platform_device *pdev)
++{
++ dev_info(&pdev->dev, DRVMSG ": ending\n");
++
++ sysfs_remove_group(&pdev->dev.kobj, >a01_bt_attr_group);
++
++ return 0;
++}
++
++static struct platform_driver gta01_bt_driver = {
++ .probe = gta01_bt_probe,
++ .remove = gta01_bt_remove,
++ .suspend = gta01_bt_suspend,
++ .resume = gta01_bt_resume,
++ .driver = {
++ .name = "gta01-pm-bt",
++ },
++};
++
++static int __devinit gta01_bt_init(void)
++{
++ return platform_driver_register(>a01_bt_driver);
++}
++
++static void gta01_bt_exit(void)
++{
++ platform_driver_unregister(>a01_bt_driver);
++}
++
++module_init(gta01_bt_init);
++module_exit(gta01_bt_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION(DRVMSG);
Added: developers/nbd/patches-2.6.22/380-gta01-no_nand_partitions.patch
===================================================================
--- developers/nbd/patches-2.6.22/380-gta01-no_nand_partitions.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/380-gta01-no_nand_partitions.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,84 @@
+Index: linux-2.6.20/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- linux-2.6.20.orig/arch/arm/mach-s3c2410/mach-gta01.c 2007-02-15 16:28:11.000000000 +0100
++++ linux-2.6.20/arch/arm/mach-s3c2410/mach-gta01.c 2007-02-15 16:28:11.000000000 +0100
+@@ -186,40 +186,10 @@
+ .devices_count = ARRAY_SIZE(gta01_devices)
+ };
+
+-static struct mtd_partition gta01_nand_part[] = {
+- [0] = {
+- .name = "U-Boot",
+- .size = 0x30000,
+- .offset = 0,
+- },
+- [1] = {
+- .name = "U-Boot environment",
+- .offset = 0x30000,
+- .size = 0x4000,
+- },
+- [2] = {
+- .name = "kernel",
+- .offset = 0x34000,
+- .size = SZ_2M,
+- },
+- [3] = {
+- .name = "initrd",
+- .offset = 0x234000,
+- .size = SZ_4M,
+- },
+- [4] = {
+- .name = "jffs2",
+- .offset = 0x634000,
+- .size = 0x39cc000,
+- },
+-};
+-
+ static struct s3c2410_nand_set gta01_nand_sets[] = {
+ [0] = {
+- .name = "NAND",
++ .name = "neo1973-nand",
+ .nr_chips = 1,
+- .nr_partitions = ARRAY_SIZE(gta01_nand_part),
+- .partitions = gta01_nand_part,
+ },
+ };
+
+Index: linux-2.6.20/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.20.orig/drivers/mtd/nand/s3c2410.c 2007-02-15 16:28:11.000000000 +0100
++++ linux-2.6.20/drivers/mtd/nand/s3c2410.c 2007-02-15 16:28:11.000000000 +0100
+@@ -471,17 +471,31 @@
+ }
+
+ #ifdef CONFIG_MTD_PARTITIONS
++const char *part_probes[] = { "cmdlinepart", NULL };
+ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
+ struct s3c2410_nand_mtd *mtd,
+ struct s3c2410_nand_set *set)
+ {
++ struct mtd_partition *part_info;
++ int nr_part = 0;
++
+ if (set == NULL)
+ return add_mtd_device(&mtd->mtd);
+
+- if (set->nr_partitions > 0 && set->partitions != NULL) {
+- return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
++ if (set->nr_partitions == 0) {
++ mtd->mtd.name = set->name;
++ nr_part = parse_mtd_partitions(&mtd->mtd, part_probes,
++ &part_info, 0);
++ } else {
++ if (set->nr_partitions > 0 && set->partitions != NULL) {
++ nr_part = set->nr_partitions;
++ part_info = set->partitions;
++ }
+ }
+
++ if (nr_part > 0 && part_info)
++ return add_mtd_partitions(&mtd->mtd, part_info, nr_part);
++
+ return add_mtd_device(&mtd->mtd);
+ }
+ #else
Added: developers/nbd/patches-2.6.22/390-input-nots-mousedev.patch
===================================================================
--- developers/nbd/patches-2.6.22/390-input-nots-mousedev.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/390-input-nots-mousedev.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,26 @@
+This patch disables the reporting of touchscreen-like devices via
+/dev/input/mice. In the Neo1973 (much like other handheld devices),
+we need this to distinguish between the touchscreen (which uses tslib)
+and optional additional usb/bluetooth mice that might be attached.
+
+Signed-off-by: Harald Welte <laforge at openmoko.org>
+
+Index: linux-2.6.17.14-fic4.test/drivers/input/mousedev.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/input/mousedev.c 2007-02-09 13:47:15.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/input/mousedev.c 2007-02-09 13:47:40.000000000 +0100
+@@ -689,12 +689,14 @@
+ .evbit = { BIT(EV_KEY) | BIT(EV_REL) },
+ .relbit = { BIT(REL_WHEEL) },
+ }, /* A separate scrollwheel */
++#if 0
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
+ .keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
+ .absbit = { BIT(ABS_X) | BIT(ABS_Y) },
+ }, /* A tablet like device, at least touch detection, two absolute axes */
++#endif
+ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
+ .evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
Added: developers/nbd/patches-2.6.22/400-ts0710.patch
===================================================================
--- developers/nbd/patches-2.6.22/400-ts0710.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/400-ts0710.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,5717 @@
+Index: linux-2.6.17.14-fic2/drivers/char/Kconfig
+===================================================================
+--- linux-2.6.17.14-fic2.orig/drivers/char/Kconfig 2006-12-02 11:20:42.000000000 +0100
++++ linux-2.6.17.14-fic2/drivers/char/Kconfig 2006-12-02 11:23:49.000000000 +0100
+@@ -1034,5 +1034,17 @@
+ sysfs directory, /sys/devices/platform/telco_clock, with a number of
+ files for controlling the behavior of this hardware.
+
++config TS0710_MUX
++ tristate "GSM TS 07.10 Multiplex driver"
++ help
++ This implements the GSM 07.10 multiplex protocol.
++
++config TS0710_MUX_USB_MOTO
++ tristate "Motorola USB support for TS 07.10 Multiplex driver"
++ depends on TS0710_MUX && USB
++ help
++ This addrs support for the TS07.10 over USB, as found in Motorola
++ Smartphones.
++
+ endmenu
+
+Index: linux-2.6.17.14-fic2/drivers/char/Makefile
+===================================================================
+--- linux-2.6.17.14-fic2.orig/drivers/char/Makefile 2006-12-02 11:20:46.000000000 +0100
++++ linux-2.6.17.14-fic2/drivers/char/Makefile 2006-12-02 11:24:55.000000000 +0100
+@@ -97,6 +97,9 @@
+ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
+ obj-$(CONFIG_TCG_TPM) += tpm/
+
++obj-$(CONFIG_TS0710_MUX) += ts0710_mux.o
++obj-$(CONFIG_TS0710_MUX_USB) += ts0710_mux_usb.o
++
+ # Files generated that shall be removed upon make clean
+ clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c
+
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710.h 2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,368 @@
++/*
++ * File: ts0710.h
++ *
++ * Portions derived from rfcomm.c, original header as follows:
++ *
++ * Copyright (C) 2000, 2001 Axis Communications AB
++ *
++ * Author: Mats Friden <mats.friden at axis.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Exceptionally, Axis Communications AB grants discretionary and
++ * conditional permissions for additional use of the text contained
++ * in the company's release of the AXIS OpenBT Stack under the
++ * provisions set forth hereunder.
++ *
++ * Provided that, if you use the AXIS OpenBT Stack with other files,
++ * that do not implement functionality as specified in the Bluetooth
++ * System specification, to produce an executable, this does not by
++ * itself cause the resulting executable to be covered by the GNU
++ * General Public License. Your use of that executable is in no way
++ * restricted on account of using the AXIS OpenBT Stack code with it.
++ *
++ * This exception does not however invalidate any other reasons why
++ * the executable file might be covered by the provisions of the GNU
++ * General Public License.
++ *
++ */
++/*
++ * Copyright (C) 2002 Motorola
++ *
++ * 07/28/2002 Initial version based on rfcomm.c
++ * 11/18/2002 Modified
++ */
++
++#include <linux/config.h>
++#include <linux/module.h>
++
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/fcntl.h>
++#include <linux/string.h>
++#include <linux/major.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/devfs_fs_kernel.h>
++
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++
++#include <asm/byteorder.h>
++#include <asm/types.h>
++
++#define TS0710_MAX_CHN 14
++
++#define SET_PF(ctr) ((ctr) | (1 << 4))
++#define CLR_PF(ctr) ((ctr) & 0xef)
++#define GET_PF(ctr) (((ctr) >> 4) & 0x1)
++
++#define GET_PN_MSG_FRAME_SIZE(pn) ( ((pn)->frame_sizeh << 8) | ((pn)->frame_sizel))
++#define SET_PN_MSG_FRAME_SIZE(pn, size) ({ (pn)->frame_sizel = (size) & 0xff; \
++ (pn)->frame_sizeh = (size) >> 8; })
++
++#define GET_LONG_LENGTH(a) ( ((a).h_len << 7) | ((a).l_len) )
++#define SET_LONG_LENGTH(a, length) ({ (a).ea = 0; \
++ (a).l_len = length & 0x7F; \
++ (a).h_len = (length >> 7) & 0xFF; })
++
++#define SHORT_CRC_CHECK 3
++#define LONG_CRC_CHECK 4
++
++/* FIXME: Should thsi one be define here? */
++#define SHORT_PAYLOAD_SIZE 127
++
++#define EA 1
++#define FCS_SIZE 1
++#define FLAG_SIZE 2
++
++#define TS0710_MAX_HDR_SIZE 5
++#define DEF_TS0710_MTU 256
++
++#define TS0710_BASIC_FLAG 0xF9
++/* the control field */
++#define SABM 0x2f
++#define SABM_SIZE 4
++#define UA 0x63
++#define UA_SIZE 4
++#define DM 0x0f
++#define DISC 0x43
++#define UIH 0xef
++
++/* the type field in a multiplexer command packet */
++#define TEST 0x8
++#define FCON 0x28
++#define FCOFF 0x18
++#define MSC 0x38
++#define RPN 0x24
++#define RLS 0x14
++#define PN 0x20
++#define NSC 0x4
++
++/* V.24 modem control signals */
++#define FC 0x2
++#define RTC 0x4
++#define RTR 0x8
++#define IC 0x40
++#define DV 0x80
++
++#define CTRL_CHAN 0 /* The control channel is defined as DLCI 0 */
++#define MCC_CMD 1 /* Multiplexer command cr */
++#define MCC_RSP 0 /* Multiplexer response cr */
++
++#ifdef __LITTLE_ENDIAN_BITFIELD
++
++typedef struct {
++ __u8 ea:1;
++ __u8 cr:1;
++ __u8 d:1;
++ __u8 server_chn:5;
++} __attribute__ ((packed)) address_field;
++
++typedef struct {
++ __u8 ea:1;
++ __u8 len:7;
++} __attribute__ ((packed)) short_length;
++
++typedef struct {
++ __u8 ea:1;
++ __u8 l_len:7;
++ __u8 h_len;
++} __attribute__ ((packed)) long_length;
++
++typedef struct {
++ address_field addr;
++ __u8 control;
++ short_length length;
++} __attribute__ ((packed)) short_frame_head;
++
++typedef struct {
++ short_frame_head h;
++ __u8 data[0];
++} __attribute__ ((packed)) short_frame;
++
++typedef struct {
++ address_field addr;
++ __u8 control;
++ long_length length;
++ __u8 data[0];
++} __attribute__ ((packed)) long_frame_head;
++
++typedef struct {
++ long_frame_head h;
++ __u8 data[0];
++} __attribute__ ((packed)) long_frame;
++
++/* Typedefinitions for structures used for the multiplexer commands */
++typedef struct {
++ __u8 ea:1;
++ __u8 cr:1;
++ __u8 type:6;
++} __attribute__ ((packed)) mcc_type;
++
++typedef struct {
++ mcc_type type;
++ short_length length;
++ __u8 value[0];
++} __attribute__ ((packed)) mcc_short_frame_head;
++
++typedef struct {
++ mcc_short_frame_head h;
++ __u8 value[0];
++} __attribute__ ((packed)) mcc_short_frame;
++
++typedef struct {
++ mcc_type type;
++ long_length length;
++ __u8 value[0];
++} __attribute__ ((packed)) mcc_long_frame_head;
++
++typedef struct {
++ mcc_long_frame_head h;
++ __u8 value[0];
++} __attribute__ ((packed)) mcc_long_frame;
++
++/* MSC-command */
++typedef struct {
++ __u8 ea:1;
++ __u8 fc:1;
++ __u8 rtc:1;
++ __u8 rtr:1;
++ __u8 reserved:2;
++ __u8 ic:1;
++ __u8 dv:1;
++} __attribute__ ((packed)) v24_sigs;
++
++typedef struct {
++ __u8 ea:1;
++ __u8 b1:1;
++ __u8 b2:1;
++ __u8 b3:1;
++ __u8 len:4;
++} __attribute__ ((packed)) brk_sigs;
++
++typedef struct {
++ short_frame_head s_head;
++ mcc_short_frame_head mcc_s_head;
++ address_field dlci;
++ __u8 v24_sigs;
++ //brk_sigs break_signals;
++ __u8 fcs;
++} __attribute__ ((packed)) msc_msg;
++
++#if 0
++/* conflict with termios.h */
++/* RPN command */
++#define B2400 0
++#define B4800 1
++#define B7200 2
++#define B9600 3
++#define B19200 4
++#define B38400 5
++#define B57600 6
++#define B115200 7
++#define D230400 8
++#endif
++
++/*
++typedef struct{
++ __u8 bit_rate:1;
++ __u8 data_bits:1;
++ __u8 stop_bit:1;
++ __u8 parity:1;
++ __u8 parity_type:1;
++ __u8 xon_u8:1;
++ __u8 xoff_u8:1;
++ __u8 res1:1;
++ __u8 xon_input:1;
++ __u8 xon_output:1;
++ __u8 rtr_input:1;
++ __u8 rtr_output:1;
++ __u8 rtc_input:1;
++ __u8 rtc_output:1;
++ __u8 res2:2;
++} __attribute__((packed)) parameter_mask;
++
++typedef struct{
++ __u8 bit_rate;
++ __u8 data_bits:2;
++ __u8 stop_bit:1;
++ __u8 parity:1;
++ __u8 parity_type:2;
++ __u8 res1:2;
++ __u8 xon_input:1;
++ __u8 xon_output:1;
++ __u8 rtr_input:1;
++ __u8 rtr_output:1;
++ __u8 rtc_input:1;
++ __u8 rtc_output:1;
++ __u8 res2:2;
++ __u8 xon_u8;
++ __u8 xoff_u8;
++ parameter_mask pm;
++} __attribute__((packed)) rpn_values;
++
++typedef struct{
++ short_frame_head s_head;
++ mcc_short_frame_head mcc_s_head;
++ address_field dlci;
++ rpn_values rpn_val;
++ __u8 fcs;
++} __attribute__((packed)) rpn_msg;
++*/
++
++/* RLS-command */
++/*
++typedef struct{
++ short_frame_head s_head;
++ mcc_short_frame_head mcc_s_head;
++ address_field dlci;
++ __u8 error:4;
++ __u8 res:4;
++ __u8 fcs;
++} __attribute__((packed)) rls_msg;
++*/
++
++/* PN-command */
++typedef struct {
++ short_frame_head s_head;
++ mcc_short_frame_head mcc_s_head;
++ __u8 dlci:6;
++ __u8 res1:2;
++ __u8 frame_type:4;
++ __u8 credit_flow:4;
++ __u8 prior:6;
++ __u8 res2:2;
++ __u8 ack_timer;
++ __u8 frame_sizel;
++ __u8 frame_sizeh;
++ __u8 max_nbrof_retrans;
++ __u8 credits;
++ __u8 fcs;
++} __attribute__ ((packed)) pn_msg;
++
++/* NSC-command */
++typedef struct {
++ short_frame_head s_head;
++ mcc_short_frame_head mcc_s_head;
++ mcc_type command_type;
++ __u8 fcs;
++} __attribute__ ((packed)) nsc_msg;
++
++#else
++#error Only littel-endianess supported now!
++#endif
++
++enum {
++ REJECTED = 0,
++ DISCONNECTED,
++ CONNECTING,
++ NEGOTIATING,
++ CONNECTED,
++ DISCONNECTING,
++ FLOW_STOPPED
++};
++
++enum ts0710_events {
++ CONNECT_IND,
++ CONNECT_CFM,
++ DISCONN_CFM
++};
++
++typedef struct {
++ volatile __u8 state;
++ volatile __u8 flow_control;
++ volatile __u8 initiated;
++ volatile __u8 initiator;
++ volatile __u16 mtu;
++ wait_queue_head_t open_wait;
++ wait_queue_head_t close_wait;
++} dlci_struct;
++
++/* user space interfaces */
++typedef struct {
++ volatile __u8 initiator;
++ volatile __u8 c_dlci;
++ volatile __u16 mtu;
++ volatile __u8 be_testing;
++ volatile __u32 test_errs;
++ wait_queue_head_t test_wait;
++
++ dlci_struct dlci[TS0710_MAX_CHN];
++} ts0710_con;
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux.c 2006-12-03 09:20:01.000000000 +0100
+@@ -0,0 +1,3978 @@
++/*
++ * File: mux_driver.c
++ *
++ * Portions derived from rfcomm.c, original header as follows:
++ *
++ * Copyright (C) 2000, 2001 Axis Communications AB
++ *
++ * Author: Mats Friden <mats.friden at axis.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ *
++ * Exceptionally, Axis Communications AB grants discretionary and
++ * conditional permissions for additional use of the text contained
++ * in the company's release of the AXIS OpenBT Stack under the
++ * provisions set forth hereunder.
++ *
++ * Provided that, if you use the AXIS OpenBT Stack with other files,
++ * that do not implement functionality as specified in the Bluetooth
++ * System specification, to produce an executable, this does not by
++ * itself cause the resulting executable to be covered by the GNU
++ * General Public License. Your use of that executable is in no way
++ * restricted on account of using the AXIS OpenBT Stack code with it.
++ *
++ * This exception does not however invalidate any other reasons why
++ * the executable file might be covered by the provisions of the GNU
++ * General Public License.
++ *
++ */
++
++/*
++ * Copyright (C) 2002-2004 Motorola
++ * Copyright (C) 2006 Harald Welte <laforge at openezx.org>
++ *
++ * 07/28/2002 Initial version
++ * 11/18/2002 Second version
++ * 04/21/2004 Add GPRS PROC
++ */
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/types.h>
++
++#include <linux/kernel.h>
++
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/fcntl.h>
++#include <linux/string.h>
++#include <linux/major.h>
++#include <linux/init.h>
++
++
++#if 0
++#include <linux/proc_fs.h>
++
++#define USB_FOR_MUX
++
++#ifndef USB_FOR_MUX
++#include <linux/serial.h>
++#endif
++
++#include <linux/sched.h>
++#include <linux/interrupt.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/devfs_fs_kernel.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/bitops.h>
++
++#endif
++
++#ifdef USB_FOR_MUX
++//#include <linux/usb.h>
++#include "ts0710_mux_usb.h"
++#endif
++
++#include "ts0710.h"
++#include "ts0710_mux.h"
++
++#define TS0710MUX_GPRS_SESSION_MAX 2
++#define TS0710MUX_MAJOR 250
++#define TS0710MUX_MINOR_START 0
++#define NR_MUXS 16
++
++ /*#define TS0710MUX_TIME_OUT 30 *//* 300ms */
++#define TS0710MUX_TIME_OUT 250 /* 2500ms, for BP UART hardware flow control AP UART */
++
++#define TS0710MUX_IO_DLCI_FC_ON 0x54F2
++#define TS0710MUX_IO_DLCI_FC_OFF 0x54F3
++#define TS0710MUX_IO_FC_ON 0x54F4
++#define TS0710MUX_IO_FC_OFF 0x54F5
++
++#define TS0710MUX_MAX_BUF_SIZE 2048
++
++#define TS0710MUX_SEND_BUF_OFFSET 10
++#define TS0710MUX_SEND_BUF_SIZE (DEF_TS0710_MTU + TS0710MUX_SEND_BUF_OFFSET + 34)
++#define TS0710MUX_RECV_BUF_SIZE TS0710MUX_SEND_BUF_SIZE
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++#define ACK_SPACE 66 /* 6 * 11(ACK frame size) */
++#else
++#define ACK_SPACE 42 /* 6 * 7(ACK frame size) */
++#endif
++/*For BP UART problem End*/
++
++ /*#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE)*//* For BP UART problem */
++#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + ACK_SPACE) /* For BP UART problem: ACK_SPACE */
++
++#define TS0710MUX_MAX_TOTAL_FRAME_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + FLAG_SIZE)
++#define TS0710MUX_MAX_CHARS_IN_BUF 65535
++#define TS0710MUX_THROTTLE_THRESHOLD DEF_TS0710_MTU
++
++#define TEST_PATTERN_SIZE 250
++
++#define CMDTAG 0x55
++#define DATATAG 0xAA
++
++#define ACK 0x4F /*For BP UART problem */
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++#define FIRST_BP_SEQ_OFFSET 1 /*offset from start flag */
++#define SECOND_BP_SEQ_OFFSET 2 /*offset from start flag */
++#define FIRST_AP_SEQ_OFFSET 3 /*offset from start flag */
++#define SECOND_AP_SEQ_OFFSET 4 /*offset from start flag */
++#define SLIDE_BP_SEQ_OFFSET 5 /*offset from start flag */
++#define SEQ_FIELD_SIZE 5
++#else
++#define SLIDE_BP_SEQ_OFFSET 1 /*offset from start flag */
++#define SEQ_FIELD_SIZE 1
++#endif
++
++#define ADDRESS_FIELD_OFFSET (1 + SEQ_FIELD_SIZE) /*offset from start flag */
++/*For BP UART problem End*/
++
++#ifndef UNUSED_PARAM
++#define UNUSED_PARAM(v) (void)(v)
++#endif
++
++#define TS0710MUX_GPRS1_DLCI 7
++#define TS0710MUX_GPRS2_DLCI 8
++
++#define TS0710MUX_GPRS1_RECV_COUNT_IDX 0
++#define TS0710MUX_GPRS1_SEND_COUNT_IDX 1
++#define TS0710MUX_GPRS2_RECV_COUNT_IDX 2
++#define TS0710MUX_GPRS2_SEND_COUNT_IDX 3
++#define TS0710MUX_COUNT_MAX_IDX 3
++#define TS0710MUX_COUNT_IDX_NUM (TS0710MUX_COUNT_MAX_IDX + 1)
++
++#if 0
++static volatile int mux_data_count[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
++static volatile int mux_data_count2[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
++static struct semaphore mux_data_count_mutex[TS0710MUX_COUNT_IDX_NUM];
++static volatile __u8 post_recv_count_flag = 0;
++
++/*PROC file*/
++struct proc_dir_entry *gprs_proc_file = NULL;
++ssize_t file_proc_read(struct file *file, char *buf, size_t size,
++ loff_t * ppos);
++ssize_t file_proc_write(struct file *file, const char *buf, size_t count,
++ loff_t * ppos);
++struct file_operations file_proc_operations = {
++ read:file_proc_read,
++ write:file_proc_write,
++};
++typedef struct {
++ int recvBytes;
++ int sentBytes;
++} gprs_bytes;
++
++static __u8 tty2dlci[NR_MUXS] =
++ { 1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 8, 9, 10, 11, 12, 13 };
++static __u8 iscmdtty[NR_MUXS] =
++ { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
++typedef struct {
++ __u8 cmdtty;
++ __u8 datatty;
++} dlci_tty;
++static dlci_tty dlci2tty[] = { {0, 0}, /* DLCI 0 */
++{0, 0}, /* DLCI 1 */
++{1, 1}, /* DLCI 2 */
++{2, 2}, /* DLCI 3 */
++{3, 3}, /* DLCI 4 */
++{4, 4}, /* DLCI 5 */
++{5, 8}, /* DLCI 6 */
++{6, 9}, /* DLCI 7 */
++{7, 10}, /* DLCI 8 */
++{11, 11}, /* DLCI 9 */
++{12, 12}, /* DLCI 10 */
++{13, 13}, /* DLCI 11 */
++{14, 14}, /* DLCI 12 */
++{15, 15}
++}; /* DLCI 13 */
++
++typedef struct {
++ volatile __u8 buf[TS0710MUX_SEND_BUF_SIZE];
++ volatile __u8 *frame;
++ unsigned long flags;
++ volatile __u16 length;
++ volatile __u8 filled;
++ volatile __u8 dummy; /* Allignment to 4*n bytes */
++} mux_send_struct;
++
++/* Bit number in flags of mux_send_struct */
++#define BUF_BUSY 0
++
++struct mux_recv_packet_tag {
++ __u8 *data;
++ __u32 length;
++ struct mux_recv_packet_tag *next;
++};
++typedef struct mux_recv_packet_tag mux_recv_packet;
++
++struct mux_recv_struct_tag {
++ __u8 data[TS0710MUX_RECV_BUF_SIZE];
++ __u32 length;
++ __u32 total;
++ mux_recv_packet *mux_packet;
++ struct mux_recv_struct_tag *next;
++ int no_tty;
++ volatile __u8 post_unthrottle;
++};
++typedef struct mux_recv_struct_tag mux_recv_struct;
++
++#define RECV_RUNNING 0
++static unsigned long mux_recv_flags = 0;
++
++static mux_send_struct *mux_send_info[NR_MUXS];
++static volatile __u8 mux_send_info_flags[NR_MUXS];
++static volatile __u8 mux_send_info_idx = NR_MUXS;
++
++static mux_recv_struct *mux_recv_info[NR_MUXS];
++static volatile __u8 mux_recv_info_flags[NR_MUXS];
++static mux_recv_struct *mux_recv_queue = NULL;
++
++static struct tty_driver mux_driver;
++
++#ifdef USB_FOR_MUX
++#define COMM_FOR_MUX_DRIVER usb_for_mux_driver
++#define COMM_FOR_MUX_TTY usb_for_mux_tty
++#define COMM_MUX_DISPATCHER usb_mux_dispatcher
++#define COMM_MUX_SENDER usb_mux_sender
++#else
++#define COMM_FOR_MUX_DRIVER serial_for_mux_driver
++#define COMM_FOR_MUX_TTY serial_for_mux_tty
++#define COMM_MUX_DISPATCHER serial_mux_dispatcher
++#define COMM_MUX_SENDER serial_mux_sender
++
++extern struct list_head *tq_serial_for_mux;
++#endif
++
++extern struct tty_driver *COMM_FOR_MUX_DRIVER;
++extern struct tty_struct *COMM_FOR_MUX_TTY;
++extern void (*COMM_MUX_DISPATCHER) (struct tty_struct * tty);
++extern void (*COMM_MUX_SENDER) (void);
++
++static struct work_struct send_tqueue;
++static struct work_struct receive_tqueue;
++static struct work_struct post_recv_tqueue;
++
++static struct tty_struct *mux_table[NR_MUXS];
++static struct termios *mux_termios[NR_MUXS];
++static struct termios *mux_termios_locked[NR_MUXS];
++static volatile short int mux_tty[NR_MUXS];
++
++#ifdef min
++#undef min
++#define min(a,b) ( (a)<(b) ? (a):(b) )
++#endif
++
++static int get_count(__u8 idx);
++static int set_count(__u8 idx, int count);
++static int add_count(__u8 idx, int count);
++
++static int send_ua(ts0710_con * ts0710, __u8 dlci);
++static int send_dm(ts0710_con * ts0710, __u8 dlci);
++static int send_sabm(ts0710_con * ts0710, __u8 dlci);
++static int send_disc(ts0710_con * ts0710, __u8 dlci);
++static void queue_uih(mux_send_struct * send_info, __u16 len,
++ ts0710_con * ts0710, __u8 dlci);
++static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
++ __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr);
++static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr);
++static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr);
++
++static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum);
++static __u8 crc_calc(__u8 * data, __u32 length);
++static void create_crctable(__u8 table[]);
++
++static void mux_sched_send(void);
++
++static __u8 crctable[256];
++
++static ts0710_con ts0710_connection;
++/*
++static rpn_values rpn_val;
++*/
++
++static int valid_dlci(__u8 dlci)
++{
++ if ((dlci < TS0710_MAX_CHN) && (dlci > 0))
++ return 1;
++ else
++ return 0;
++}
++
++#ifdef TS0710DEBUG
++
++#ifdef PRINT_OUTPUT_PRINTK
++#define TS0710_DEBUG(fmt, arg...) printk(KERN_INFO "MUX " __FUNCTION__ ": " fmt "\n" , ## arg)
++#else
++#include "ezxlog.h"
++static __u8 strDebug[256];
++#define TS0710_DEBUG(fmt, arg...) ({ snprintf(strDebug, sizeof(strDebug), "MUX " __FUNCTION__ ": " fmt "\n" , ## arg); \
++ /*printk("%s", strDebug)*/ezxlogk("MX", strDebug, strlen(strDebug)); })
++#endif /* End #ifdef PRINT_OUTPUT_PRINTK */
++
++#else
++#define TS0710_DEBUG(fmt...)
++#endif /* End #ifdef TS0710DEBUG */
++
++#ifdef TS0710LOG
++static unsigned char g_tbuf[TS0710MUX_MAX_BUF_SIZE];
++#ifdef PRINT_OUTPUT_PRINTK
++#define TS0710_LOG(fmt, arg...) printk(fmt, ## arg)
++#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
++#else
++#include "ezxlog.h"
++static __u8 strLog[256];
++#define TS0710_LOG(fmt, arg...) ({ snprintf(strLog, sizeof(strLog), fmt, ## arg); \
++ /*printk("%s", strLog)*/ezxlogk("MX", strLog, strlen(strLog)); })
++#define TS0710_PRINTK(fmt, arg...) ({ printk(fmt, ## arg); \
++ TS0710_LOG(fmt, ## arg); })
++#endif /* End #ifdef PRINT_OUTPUT_PRINTK */
++
++#else
++#define TS0710_LOG(fmt...)
++#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
++#endif /* End #ifdef TS0710LOG */
++
++#ifdef TS0710DEBUG
++static void TS0710_DEBUGHEX(__u8 * buf, int len)
++{
++ static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++
++ int i;
++ int c;
++
++ if (len <= 0) {
++ return;
++ }
++
++ c = 0;
++ for (i = 0; (i < len) && (c < (TS0710MUX_MAX_BUF_SIZE - 3)); i++) {
++ sprintf(&tbuf[c], "%02x ", buf[i]);
++ c += 3;
++ }
++ tbuf[c] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++ TS0710_DEBUG("%s", tbuf);
++#else
++ /*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, c);
++#endif
++}
++static void TS0710_DEBUGSTR(__u8 * buf, int len)
++{
++ static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++
++ if (len <= 0) {
++ return;
++ }
++
++ if (len > (TS0710MUX_MAX_BUF_SIZE - 1)) {
++ len = (TS0710MUX_MAX_BUF_SIZE - 1);
++ }
++
++ memcpy(tbuf, buf, len);
++ tbuf[len] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++ /* 0x00 byte in the string pointed by tbuf may truncate the print result */
++ TS0710_DEBUG("%s", tbuf);
++#else
++ /*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, len);
++#endif
++}
++#else
++#define TS0710_DEBUGHEX(buf, len)
++#define TS0710_DEBUGSTR(buf, len)
++#endif /* End #ifdef TS0710DEBUG */
++
++#ifdef TS0710LOG
++static void TS0710_LOGSTR_FRAME(__u8 send, __u8 * data, int len)
++{
++ short_frame *short_pkt;
++ long_frame *long_pkt;
++ __u8 *uih_data_start;
++ __u32 uih_len;
++ __u8 dlci;
++ int pos;
++
++ if (len <= 0) {
++ return;
++ }
++
++ pos = 0;
++ if (send) {
++ pos += sprintf(&g_tbuf[pos], "<");
++ short_pkt = (short_frame *) (data + 1); /*For BP UART problem */
++ } else {
++ /*For BP UART problem */
++ /*pos += sprintf(&g_tbuf[pos], ">"); */
++ pos += sprintf(&g_tbuf[pos], ">%d ", *(data + SLIDE_BP_SEQ_OFFSET)); /*For BP UART problem */
++
++#ifdef TS0710SEQ2
++ pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", *(data + FIRST_BP_SEQ_OFFSET), *(data + SECOND_BP_SEQ_OFFSET), *(data + FIRST_AP_SEQ_OFFSET), *(data + SECOND_AP_SEQ_OFFSET)); /*For BP UART problem */
++#endif
++
++ short_pkt = (short_frame *) (data + ADDRESS_FIELD_OFFSET); /*For BP UART problem */
++ }
++
++ /*For BP UART problem */
++ /*short_pkt = (short_frame *)(data + 1); */
++
++ dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
++ switch (CLR_PF(short_pkt->h.control)) {
++ case SABM:
++ pos += sprintf(&g_tbuf[pos], "C SABM %d ::", dlci);
++ break;
++ case UA:
++ pos += sprintf(&g_tbuf[pos], "C UA %d ::", dlci);
++ break;
++ case DM:
++ pos += sprintf(&g_tbuf[pos], "C DM %d ::", dlci);
++ break;
++ case DISC:
++ pos += sprintf(&g_tbuf[pos], "C DISC %d ::", dlci);
++ break;
++
++ /*For BP UART problem Begin */
++ case ACK:
++ pos += sprintf(&g_tbuf[pos], "C ACK %d ", short_pkt->data[0]);
++
++#ifdef TS0710SEQ2
++ pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", short_pkt->data[1], short_pkt->data[2], short_pkt->data[3], short_pkt->data[4]); /*For BP UART problem */
++#endif
++
++ pos += sprintf(&g_tbuf[pos], "::");
++ break;
++ /*For BP UART problem End */
++
++ case UIH:
++ if (!dlci) {
++ pos += sprintf(&g_tbuf[pos], "C MCC %d ::", dlci);
++ } else {
++
++ if ((short_pkt->h.length.ea) == 0) {
++ long_pkt = (long_frame *) short_pkt;
++ uih_len = GET_LONG_LENGTH(long_pkt->h.length);
++ uih_data_start = long_pkt->h.data;
++ } else {
++ uih_len = short_pkt->h.length.len;
++ uih_data_start = short_pkt->data;
++ }
++ switch (*uih_data_start) {
++ case CMDTAG:
++ pos +=
++ sprintf(&g_tbuf[pos], "I %d A %d ::", dlci,
++ uih_len);
++ break;
++ case DATATAG:
++ default:
++ pos +=
++ sprintf(&g_tbuf[pos], "I %d D %d ::", dlci,
++ uih_len);
++ break;
++ }
++
++ }
++ break;
++ default:
++ pos += sprintf(&g_tbuf[pos], "N!!! %d ::", dlci);
++ break;
++ }
++
++ if (len > (sizeof(g_tbuf) - pos - 1)) {
++ len = (sizeof(g_tbuf) - pos - 1);
++ }
++
++ memcpy(&g_tbuf[pos], data, len);
++ pos += len;
++ g_tbuf[pos] = 0;
++
++#ifdef PRINT_OUTPUT_PRINTK
++ /* 0x00 byte in the string pointed by g_tbuf may truncate the print result */
++ TS0710_LOG("%s\n", g_tbuf);
++#else
++ /*printk("%s\n", g_tbuf) */ ezxlogk("MX", g_tbuf, pos);
++#endif
++}
++#else
++#define TS0710_LOGSTR_FRAME(send, data, len)
++#endif
++
++#ifdef TS0710SIG
++#define my_for_each_task(p) \
++ for ((p) = current; ((p) = (p)->next_task) != current; )
++
++static void TS0710_SIG2APLOGD(void)
++{
++ struct task_struct *p;
++ static __u8 sig = 0;
++
++ if (sig) {
++ return;
++ }
++
++ read_lock(&tasklist_lock);
++ my_for_each_task(p) {
++ if (strncmp(p->comm, "aplogd", 6) == 0) {
++ sig = 1;
++ if (send_sig(SIGUSR2, p, 1) == 0) {
++ TS0710_PRINTK
++ ("MUX: success to send SIGUSR2 to aplogd!\n");
++ } else {
++ TS0710_PRINTK
++ ("MUX: failure to send SIGUSR2 to aplogd!\n");
++ }
++ break;
++ }
++ }
++ read_unlock(&tasklist_lock);
++
++ if (!sig) {
++ TS0710_PRINTK("MUX: not found aplogd!\n");
++ }
++}
++#else
++#define TS0710_SIG2APLOGD()
++#endif
++
++static int basic_write(ts0710_con * ts0710, __u8 * buf, int len)
++{
++ int res;
++
++ UNUSED_PARAM(ts0710);
++
++ buf[0] = TS0710_BASIC_FLAG;
++ buf[len + 1] = TS0710_BASIC_FLAG;
++
++ if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
++ TS0710_PRINTK
++ ("MUX basic_write: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n");
++
++#ifndef USB_FOR_MUX
++ TS0710_PRINTK
++ ("MUX basic_write: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n");
++ TS0710_SIG2APLOGD();
++#endif
++
++ return -1;
++ }
++
++ TS0710_LOGSTR_FRAME(1, buf, len + 2);
++ TS0710_DEBUGHEX(buf, len + 2);
++
++ res = COMM_FOR_MUX_DRIVER->write(COMM_FOR_MUX_TTY, buf, len + 2);
++
++ if (res != len + 2) {
++ TS0710_PRINTK("MUX basic_write: Write Error!\n");
++ return -1;
++ }
++
++ return len + 2;
++}
++
++/* Functions for the crc-check and calculation */
++
++#define CRC_VALID 0xcf
++
++static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum)
++{
++ __u8 fcs = 0xff;
++
++ while (length--) {
++ fcs = crctable[fcs ^ *data++];
++ }
++ fcs = crctable[fcs ^ check_sum];
++ TS0710_DEBUG("fcs : %d\n", fcs);
++ if (fcs == (uint) 0xcf) { /*CRC_VALID) */
++ TS0710_DEBUG("crc_check: CRC check OK\n");
++ return 0;
++ } else {
++ TS0710_PRINTK("MUX crc_check: CRC check failed\n");
++ return 1;
++ }
++}
++
++/* Calculates the checksum according to the ts0710 specification */
++
++static __u8 crc_calc(__u8 * data, __u32 length)
++{
++ __u8 fcs = 0xff;
++
++ while (length--) {
++ fcs = crctable[fcs ^ *data++];
++ }
++
++ return 0xff - fcs;
++}
++
++/* Calulates a reversed CRC table for the FCS check */
++
++static void create_crctable(__u8 table[])
++{
++ int i, j;
++
++ __u8 data;
++ __u8 code_word = (__u8) 0xe0;
++ __u8 sr = (__u8) 0;
++
++ for (j = 0; j < 256; j++) {
++ data = (__u8) j;
++
++ for (i = 0; i < 8; i++) {
++ if ((data & 0x1) ^ (sr & 0x1)) {
++ sr >>= 1;
++ sr ^= code_word;
++ } else {
++ sr >>= 1;
++ }
++
++ data >>= 1;
++ sr &= 0xff;
++ }
++
++ table[j] = sr;
++ sr = 0;
++ }
++}
++
++static void ts0710_reset_dlci(__u8 j)
++{
++ if (j >= TS0710_MAX_CHN)
++ return;
++
++ ts0710_connection.dlci[j].state = DISCONNECTED;
++ ts0710_connection.dlci[j].flow_control = 0;
++ ts0710_connection.dlci[j].mtu = DEF_TS0710_MTU;
++ ts0710_connection.dlci[j].initiated = 0;
++ ts0710_connection.dlci[j].initiator = 0;
++ init_waitqueue_head(&ts0710_connection.dlci[j].open_wait);
++ init_waitqueue_head(&ts0710_connection.dlci[j].close_wait);
++}
++
++static void ts0710_reset_con(void)
++{
++ __u8 j;
++
++ ts0710_connection.initiator = 0;
++ ts0710_connection.mtu = DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE;
++ ts0710_connection.be_testing = 0;
++ ts0710_connection.test_errs = 0;
++ init_waitqueue_head(&ts0710_connection.test_wait);
++
++ for (j = 0; j < TS0710_MAX_CHN; j++) {
++ ts0710_reset_dlci(j);
++ }
++}
++
++static void ts0710_init(void)
++{
++ create_crctable(crctable);
++
++ ts0710_reset_con();
++
++ /* Set the values in the rpn octets */
++/*
++ rpn_val.bit_rate = 7;
++ rpn_val.data_bits = 3;
++ rpn_val.stop_bit = 0;
++ rpn_val.parity = 0;
++ rpn_val.parity_type = 0;
++ rpn_val.res1 = 0;
++ rpn_val.xon_input = 0;
++ rpn_val.xon_output = 0;
++ rpn_val.rtr_input = 0;
++ rpn_val.rtr_output = 0;
++ rpn_val.rtc_input = 0;
++ rpn_val.rtc_output = 0;
++ rpn_val.res2 = 0;
++ rpn_val.xon_u8 = 0x11;
++ rpn_val.xoff_u8 = 0x13;
++ memset(&rpn_val.pm, 0 , 2); *//* Set the mask to zero */
++}
++
++static void ts0710_upon_disconnect(void)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ __u8 j;
++
++ for (j = 0; j < TS0710_MAX_CHN; j++) {
++ ts0710->dlci[j].state = DISCONNECTED;
++ wake_up_interruptible(&ts0710->dlci[j].open_wait);
++ wake_up_interruptible(&ts0710->dlci[j].close_wait);
++ }
++ ts0710->be_testing = 0;
++ wake_up_interruptible(&ts0710->test_wait);
++ ts0710_reset_con();
++}
++
++/* Sending packet functions */
++
++/* Creates a UA packet and puts it at the beginning of the pkt pointer */
++
++static int send_ua(ts0710_con * ts0710, __u8 dlci)
++{
++ __u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++ short_frame *ua;
++
++ TS0710_DEBUG("send_ua: Creating UA packet to DLCI %d\n", dlci);
++
++ ua = (short_frame *) (buf + 1);
++ ua->h.addr.ea = 1;
++ ua->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
++ ua->h.addr.d = (dlci) & 0x1;
++ ua->h.addr.server_chn = (dlci) >> 0x1;
++ ua->h.control = SET_PF(UA);
++ ua->h.length.ea = 1;
++ ua->h.length.len = 0;
++ ua->data[0] = crc_calc((__u8 *) ua, SHORT_CRC_CHECK);
++
++ return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++/* Creates a DM packet and puts it at the beginning of the pkt pointer */
++
++static int send_dm(ts0710_con * ts0710, __u8 dlci)
++{
++ __u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++ short_frame *dm;
++
++ TS0710_DEBUG("send_dm: Creating DM packet to DLCI %d\n", dlci);
++
++ dm = (short_frame *) (buf + 1);
++ dm->h.addr.ea = 1;
++ dm->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
++ dm->h.addr.d = dlci & 0x1;
++ dm->h.addr.server_chn = dlci >> 0x1;
++ dm->h.control = SET_PF(DM);
++ dm->h.length.ea = 1;
++ dm->h.length.len = 0;
++ dm->data[0] = crc_calc((__u8 *) dm, SHORT_CRC_CHECK);
++
++ return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static int send_sabm(ts0710_con * ts0710, __u8 dlci)
++{
++ __u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++ short_frame *sabm;
++
++ TS0710_DEBUG("send_sabm: Creating SABM packet to DLCI %d\n", dlci);
++
++ sabm = (short_frame *) (buf + 1);
++ sabm->h.addr.ea = 1;
++ sabm->h.addr.cr = ((ts0710->initiator) & 0x1);
++ sabm->h.addr.d = dlci & 0x1;
++ sabm->h.addr.server_chn = dlci >> 0x1;
++ sabm->h.control = SET_PF(SABM);
++ sabm->h.length.ea = 1;
++ sabm->h.length.len = 0;
++ sabm->data[0] = crc_calc((__u8 *) sabm, SHORT_CRC_CHECK);
++
++ return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static int send_disc(ts0710_con * ts0710, __u8 dlci)
++{
++ __u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
++ short_frame *disc;
++
++ TS0710_DEBUG("send_disc: Creating DISC packet to DLCI %d\n", dlci);
++
++ disc = (short_frame *) (buf + 1);
++ disc->h.addr.ea = 1;
++ disc->h.addr.cr = ((ts0710->initiator) & 0x1);
++ disc->h.addr.d = dlci & 0x1;
++ disc->h.addr.server_chn = dlci >> 0x1;
++ disc->h.control = SET_PF(DISC);
++ disc->h.length.ea = 1;
++ disc->h.length.len = 0;
++ disc->data[0] = crc_calc((__u8 *) disc, SHORT_CRC_CHECK);
++
++ return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
++}
++
++static void queue_uih(mux_send_struct * send_info, __u16 len,
++ ts0710_con * ts0710, __u8 dlci)
++{
++ __u32 size;
++
++ TS0710_DEBUG
++ ("queue_uih: Creating UIH packet with %d bytes data to DLCI %d\n",
++ len, dlci);
++
++ if (len > SHORT_PAYLOAD_SIZE) {
++ long_frame *l_pkt;
++
++ size = sizeof(long_frame) + len + FCS_SIZE;
++ l_pkt = (long_frame *) (send_info->frame - sizeof(long_frame));
++ set_uih_hdr((void *)l_pkt, dlci, len, ts0710->initiator);
++ l_pkt->data[len] = crc_calc((__u8 *) l_pkt, LONG_CRC_CHECK);
++ send_info->frame = ((__u8 *) l_pkt) - 1;
++ } else {
++ short_frame *s_pkt;
++
++ size = sizeof(short_frame) + len + FCS_SIZE;
++ s_pkt =
++ (short_frame *) (send_info->frame - sizeof(short_frame));
++ set_uih_hdr((void *)s_pkt, dlci, len, ts0710->initiator);
++ s_pkt->data[len] = crc_calc((__u8 *) s_pkt, SHORT_CRC_CHECK);
++ send_info->frame = ((__u8 *) s_pkt) - 1;
++ }
++ send_info->length = size;
++}
++
++/* Multiplexer command packets functions */
++
++/* Turns on the ts0710 flow control */
++
++static int ts0710_fcon_msg(ts0710_con * ts0710, __u8 cr)
++{
++ __u8 buf[30];
++ mcc_short_frame *mcc_pkt;
++ short_frame *uih_pkt;
++ __u32 size;
++
++ size = sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE;
++ uih_pkt = (short_frame *) (buf + 1);
++ set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
++ ts0710->initiator);
++ uih_pkt->data[sizeof(mcc_short_frame)] =
++ crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++ mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
++
++ mcc_pkt->h.type.ea = EA;
++ mcc_pkt->h.type.cr = cr;
++ mcc_pkt->h.type.type = FCON;
++ mcc_pkt->h.length.ea = EA;
++ mcc_pkt->h.length.len = 0;
++
++ return basic_write(ts0710, buf, size);
++}
++
++/* Turns off the ts0710 flow control */
++
++static int ts0710_fcoff_msg(ts0710_con * ts0710, __u8 cr)
++{
++ __u8 buf[30];
++ mcc_short_frame *mcc_pkt;
++ short_frame *uih_pkt;
++ __u32 size;
++
++ size = (sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE);
++ uih_pkt = (short_frame *) (buf + 1);
++ set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
++ ts0710->initiator);
++ uih_pkt->data[sizeof(mcc_short_frame)] =
++ crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++ mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
++
++ mcc_pkt->h.type.ea = 1;
++ mcc_pkt->h.type.cr = cr;
++ mcc_pkt->h.type.type = FCOFF;
++ mcc_pkt->h.length.ea = 1;
++ mcc_pkt->h.length.len = 0;
++
++ return basic_write(ts0710, buf, size);
++}
++
++/*
++static int ts0710_rpn_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 req)
++{
++ char buf[100];
++ rpn_msg* rpn_pkt;
++ __u32 fsize;
++ __u32 psize;
++
++ fsize = sizeof(rpn_msg);
++
++ if (req) {
++ fsize -= sizeof(rpn_values);
++ }
++
++ psize = (fsize - sizeof(short_frame) - FCS_SIZE);
++
++ rpn_pkt = (rpn_msg *) buf;
++
++ set_uih_hdr((short_frame *) rpn_pkt, CTRL_CHAN, psize, ts0710->initiator);
++
++ rpn_pkt->fcs = crc_calc((__u8*) rpn_pkt, SHORT_CRC_CHECK);
++
++ rpn_pkt->mcc_s_head.type.ea = EA;
++ rpn_pkt->mcc_s_head.type.cr = cr;
++ rpn_pkt->mcc_s_head.type.type = RPN;
++ rpn_pkt->mcc_s_head.length.ea = EA;
++
++ rpn_pkt->dlci.ea = EA;
++ rpn_pkt->dlci.cr = 1;
++ rpn_pkt->dlci.d = dlci & 1;
++ rpn_pkt->dlci.server_chn = (dlci >> 1);
++
++ if (req) {
++ rpn_pkt->mcc_s_head.length.len = 1;
++ rpn_pkt->rpn_val.bit_rate = rpn_pkt->fcs;
++ } else {
++ rpn_pkt->mcc_s_head.length.len = 8;
++ memcpy(&(rpn_pkt->rpn_val), &rpn_val, sizeof(rpn_values));
++ }
++ return basic_write(ts0710, buf, fsize);
++}
++*/
++/*
++static int ts0710_rls_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 err_code)
++{
++ char buf[100];
++ rls_msg *rls_pkt;
++ __u32 fsize;
++ __u32 psize;
++
++ fsize = sizeof(rls_msg);
++ psize = fsize - sizeof(short_frame) - FCS_SIZE;
++ rls_pkt = (rls_msg *) buf;
++
++ set_uih_hdr((short_frame *) rls_pkt, CTRL_CHAN, psize, ts0710->initiator);
++ rls_pkt->fcs = crc_calc((__u8*) rls_pkt, SHORT_CRC_CHECK);
++
++ rls_pkt->mcc_s_head.type.ea = EA;
++ rls_pkt->mcc_s_head.type.cr = cr;
++ rls_pkt->mcc_s_head.type.type = RLS;
++ rls_pkt->mcc_s_head.length.ea = EA;
++ rls_pkt->mcc_s_head.length.len = 2;
++
++ rls_pkt->dlci.ea = EA;
++ rls_pkt->dlci.cr = 1;
++ rls_pkt->dlci.d = dlci & 1;
++ rls_pkt->dlci.server_chn = dlci >> 1;
++ rls_pkt->error = err_code;
++ rls_pkt->res = 0;
++
++ return basic_write(ts0710, buf, fsize);
++}
++*/
++
++/* Sends an PN-messages and sets the not negotiable parameters to their
++ default values in ts0710 */
++
++static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
++ __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr)
++{
++ __u8 buf[30];
++ pn_msg *pn_pkt;
++ __u32 size;
++ TS0710_DEBUG
++ ("send_pn_msg: DLCI 0x%02x, prior:0x%02x, frame_size:%d, credit_flow:%x, credits:%d, cr:%x\n",
++ dlci, prior, frame_size, credit_flow, credits, cr);
++
++ size = sizeof(pn_msg);
++ pn_pkt = (pn_msg *) (buf + 1);
++
++ set_uih_hdr((void *)pn_pkt, CTRL_CHAN,
++ size - (sizeof(short_frame) + FCS_SIZE), ts0710->initiator);
++ pn_pkt->fcs = crc_calc((__u8 *) pn_pkt, SHORT_CRC_CHECK);
++
++ pn_pkt->mcc_s_head.type.ea = 1;
++ pn_pkt->mcc_s_head.type.cr = cr;
++ pn_pkt->mcc_s_head.type.type = PN;
++ pn_pkt->mcc_s_head.length.ea = 1;
++ pn_pkt->mcc_s_head.length.len = 8;
++
++ pn_pkt->res1 = 0;
++ pn_pkt->res2 = 0;
++ pn_pkt->dlci = dlci;
++ pn_pkt->frame_type = 0;
++ pn_pkt->credit_flow = credit_flow;
++ pn_pkt->prior = prior;
++ pn_pkt->ack_timer = 0;
++ SET_PN_MSG_FRAME_SIZE(pn_pkt, frame_size);
++ pn_pkt->credits = credits;
++ pn_pkt->max_nbrof_retrans = 0;
++
++ return basic_write(ts0710, buf, size);
++}
++
++/* Send a Not supported command - command, which needs 3 bytes */
++
++static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr)
++{
++ __u8 buf[30];
++ nsc_msg *nsc_pkt;
++ __u32 size;
++
++ size = sizeof(nsc_msg);
++ nsc_pkt = (nsc_msg *) (buf + 1);
++
++ set_uih_hdr((void *)nsc_pkt, CTRL_CHAN,
++ sizeof(nsc_msg) - sizeof(short_frame) - FCS_SIZE,
++ ts0710->initiator);
++
++ nsc_pkt->fcs = crc_calc((__u8 *) nsc_pkt, SHORT_CRC_CHECK);
++
++ nsc_pkt->mcc_s_head.type.ea = 1;
++ nsc_pkt->mcc_s_head.type.cr = cr;
++ nsc_pkt->mcc_s_head.type.type = NSC;
++ nsc_pkt->mcc_s_head.length.ea = 1;
++ nsc_pkt->mcc_s_head.length.len = 1;
++
++ nsc_pkt->command_type.ea = 1;
++ nsc_pkt->command_type.cr = cmd.cr;
++ nsc_pkt->command_type.type = cmd.type;
++
++ return basic_write(ts0710, buf, size);
++}
++
++static int ts0710_msc_msg(ts0710_con * ts0710, __u8 value, __u8 cr, __u8 dlci)
++{
++ __u8 buf[30];
++ msc_msg *msc_pkt;
++ __u32 size;
++
++ size = sizeof(msc_msg);
++ msc_pkt = (msc_msg *) (buf + 1);
++
++ set_uih_hdr((void *)msc_pkt, CTRL_CHAN,
++ sizeof(msc_msg) - sizeof(short_frame) - FCS_SIZE,
++ ts0710->initiator);
++
++ msc_pkt->fcs = crc_calc((__u8 *) msc_pkt, SHORT_CRC_CHECK);
++
++ msc_pkt->mcc_s_head.type.ea = 1;
++ msc_pkt->mcc_s_head.type.cr = cr;
++ msc_pkt->mcc_s_head.type.type = MSC;
++ msc_pkt->mcc_s_head.length.ea = 1;
++ msc_pkt->mcc_s_head.length.len = 2;
++
++ msc_pkt->dlci.ea = 1;
++ msc_pkt->dlci.cr = 1;
++ msc_pkt->dlci.d = dlci & 1;
++ msc_pkt->dlci.server_chn = (dlci >> 1) & 0x1f;
++
++ msc_pkt->v24_sigs = value;
++
++ return basic_write(ts0710, buf, size);
++}
++
++static int ts0710_test_msg(ts0710_con * ts0710, __u8 * test_pattern, __u32 len,
++ __u8 cr, __u8 * f_buf /*Frame buf */ )
++{
++ __u32 size;
++
++ if (len > SHORT_PAYLOAD_SIZE) {
++ long_frame *uih_pkt;
++ mcc_long_frame *mcc_pkt;
++
++ size =
++ (sizeof(long_frame) + sizeof(mcc_long_frame) + len +
++ FCS_SIZE);
++ uih_pkt = (long_frame *) (f_buf + 1);
++
++ set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN, len +
++ sizeof(mcc_long_frame), ts0710->initiator);
++ uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
++ crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
++ mcc_pkt = (mcc_long_frame *) uih_pkt->data;
++
++ mcc_pkt->h.type.ea = EA;
++ /* cr tells whether it is a commmand (1) or a response (0) */
++ mcc_pkt->h.type.cr = cr;
++ mcc_pkt->h.type.type = TEST;
++ SET_LONG_LENGTH(mcc_pkt->h.length, len);
++ memcpy(mcc_pkt->value, test_pattern, len);
++ } else if (len > (SHORT_PAYLOAD_SIZE - sizeof(mcc_short_frame))) {
++ long_frame *uih_pkt;
++ mcc_short_frame *mcc_pkt;
++
++ /* Create long uih packet and short mcc packet */
++ size =
++ (sizeof(long_frame) + sizeof(mcc_short_frame) + len +
++ FCS_SIZE);
++ uih_pkt = (long_frame *) (f_buf + 1);
++
++ set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN,
++ len + sizeof(mcc_short_frame), ts0710->initiator);
++ uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
++ crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
++ mcc_pkt = (mcc_short_frame *) uih_pkt->data;
++
++ mcc_pkt->h.type.ea = EA;
++ mcc_pkt->h.type.cr = cr;
++ mcc_pkt->h.type.type = TEST;
++ mcc_pkt->h.length.ea = EA;
++ mcc_pkt->h.length.len = len;
++ memcpy(mcc_pkt->value, test_pattern, len);
++ } else {
++ short_frame *uih_pkt;
++ mcc_short_frame *mcc_pkt;
++
++ size =
++ (sizeof(short_frame) + sizeof(mcc_short_frame) + len +
++ FCS_SIZE);
++ uih_pkt = (short_frame *) (f_buf + 1);
++
++ set_uih_hdr((void *)uih_pkt, CTRL_CHAN, len
++ + sizeof(mcc_short_frame), ts0710->initiator);
++ uih_pkt->data[uih_pkt->h.length.len] =
++ crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
++ mcc_pkt = (mcc_short_frame *) uih_pkt->data;
++
++ mcc_pkt->h.type.ea = EA;
++ mcc_pkt->h.type.cr = cr;
++ mcc_pkt->h.type.type = TEST;
++ mcc_pkt->h.length.ea = EA;
++ mcc_pkt->h.length.len = len;
++ memcpy(mcc_pkt->value, test_pattern, len);
++
++ }
++ return basic_write(ts0710, f_buf, size);
++}
++
++static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr)
++{
++ uih_pkt->h.addr.ea = 1;
++ uih_pkt->h.addr.cr = cr;
++ uih_pkt->h.addr.d = dlci & 0x1;
++ uih_pkt->h.addr.server_chn = dlci >> 1;
++ uih_pkt->h.control = CLR_PF(UIH);
++
++ if (len > SHORT_PAYLOAD_SIZE) {
++ SET_LONG_LENGTH(((long_frame *) uih_pkt)->h.length, len);
++ } else {
++ uih_pkt->h.length.ea = 1;
++ uih_pkt->h.length.len = len;
++ }
++}
++
++/* Parses a multiplexer control channel packet */
++
++void process_mcc(__u8 * data, __u32 len, ts0710_con * ts0710, int longpkt)
++{
++ __u8 *tbuf = NULL;
++ mcc_short_frame *mcc_short_pkt;
++ int j;
++
++ if (longpkt) {
++ mcc_short_pkt =
++ (mcc_short_frame *) (((long_frame *) data)->data);
++ } else {
++ mcc_short_pkt =
++ (mcc_short_frame *) (((short_frame *) data)->data);
++ }
++
++ switch (mcc_short_pkt->h.type.type) {
++ case TEST:
++ if (mcc_short_pkt->h.type.cr == MCC_RSP) {
++ TS0710_DEBUG("Received test command response\n");
++
++ if (ts0710->be_testing) {
++ if ((mcc_short_pkt->h.length.ea) == 0) {
++ mcc_long_frame *mcc_long_pkt;
++ mcc_long_pkt =
++ (mcc_long_frame *) mcc_short_pkt;
++ if (GET_LONG_LENGTH
++ (mcc_long_pkt->h.length) !=
++ TEST_PATTERN_SIZE) {
++ ts0710->test_errs =
++ TEST_PATTERN_SIZE;
++ TS0710_DEBUG
++ ("Err: received test pattern is %d bytes long, not expected %d\n",
++ GET_LONG_LENGTH
++ (mcc_long_pkt->h.length),
++ TEST_PATTERN_SIZE);
++ } else {
++ ts0710->test_errs = 0;
++ for (j = 0;
++ j < TEST_PATTERN_SIZE;
++ j++) {
++ if (mcc_long_pkt->
++ value[j] !=
++ (j & 0xFF)) {
++ (ts0710->
++ test_errs)++;
++ }
++ }
++ }
++
++ } else {
++
++#if TEST_PATTERN_SIZE < 128
++ if (mcc_short_pkt->h.length.len !=
++ TEST_PATTERN_SIZE) {
++#endif
++
++ ts0710->test_errs =
++ TEST_PATTERN_SIZE;
++ TS0710_DEBUG
++ ("Err: received test pattern is %d bytes long, not expected %d\n",
++ mcc_short_pkt->h.length.
++ len, TEST_PATTERN_SIZE);
++
++#if TEST_PATTERN_SIZE < 128
++ } else {
++ ts0710->test_errs = 0;
++ for (j = 0;
++ j < TEST_PATTERN_SIZE;
++ j++) {
++ if (mcc_short_pkt->
++ value[j] !=
++ (j & 0xFF)) {
++ (ts0710->
++ test_errs)++;
++ }
++ }
++ }
++#endif
++
++ }
++
++ ts0710->be_testing = 0; /* Clear the flag */
++ wake_up_interruptible(&ts0710->test_wait);
++ } else {
++ TS0710_DEBUG
++ ("Err: shouldn't or late to get test cmd response\n");
++ }
++ } else {
++ tbuf = (__u8 *) kmalloc(len + 32, GFP_ATOMIC);
++ if (!tbuf) {
++ break;
++ }
++
++ if ((mcc_short_pkt->h.length.ea) == 0) {
++ mcc_long_frame *mcc_long_pkt;
++ mcc_long_pkt = (mcc_long_frame *) mcc_short_pkt;
++ ts0710_test_msg(ts0710, mcc_long_pkt->value,
++ GET_LONG_LENGTH(mcc_long_pkt->h.
++ length),
++ MCC_RSP, tbuf);
++ } else {
++ ts0710_test_msg(ts0710, mcc_short_pkt->value,
++ mcc_short_pkt->h.length.len,
++ MCC_RSP, tbuf);
++ }
++
++ kfree(tbuf);
++ }
++ break;
++
++ case FCON: /*Flow control on command */
++ TS0710_PRINTK
++ ("MUX Received Flow control(all channels) on command\n");
++ if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++ ts0710->dlci[0].state = CONNECTED;
++ ts0710_fcon_msg(ts0710, MCC_RSP);
++ mux_sched_send();
++ }
++ break;
++
++ case FCOFF: /*Flow control off command */
++ TS0710_PRINTK
++ ("MUX Received Flow control(all channels) off command\n");
++ if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++ for (j = 0; j < TS0710_MAX_CHN; j++) {
++ ts0710->dlci[j].state = FLOW_STOPPED;
++ }
++ ts0710_fcoff_msg(ts0710, MCC_RSP);
++ }
++ break;
++
++ case MSC: /*Modem status command */
++ {
++ __u8 dlci;
++ __u8 v24_sigs;
++
++ dlci = (mcc_short_pkt->value[0]) >> 2;
++ v24_sigs = mcc_short_pkt->value[1];
++
++ if ((ts0710->dlci[dlci].state != CONNECTED)
++ && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++ send_dm(ts0710, dlci);
++ break;
++ }
++ if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++ TS0710_DEBUG("Received Modem status command\n");
++ if (v24_sigs & 2) {
++ if (ts0710->dlci[dlci].state ==
++ CONNECTED) {
++ TS0710_LOG
++ ("MUX Received Flow off on dlci %d\n",
++ dlci);
++ ts0710->dlci[dlci].state =
++ FLOW_STOPPED;
++ }
++ } else {
++ if (ts0710->dlci[dlci].state ==
++ FLOW_STOPPED) {
++ ts0710->dlci[dlci].state =
++ CONNECTED;
++ TS0710_LOG
++ ("MUX Received Flow on on dlci %d\n",
++ dlci);
++ mux_sched_send();
++ }
++ }
++
++ ts0710_msc_msg(ts0710, v24_sigs, MCC_RSP, dlci);
++/*
++ if (!(ts0710->dlci[dlci].initiated) && !(ts0710->dlci[dlci].initiator)) {
++ ts0710_msc_msg(ts0710, EA | RTR | RTC | DV, MCC_CMD, dlci);
++ ts0710->dlci[dlci].initiated = 1;
++ }
++*/
++ } else {
++ TS0710_DEBUG
++ ("Received Modem status response\n");
++
++ if (v24_sigs & 2) {
++ TS0710_DEBUG("Flow stop accepted\n");
++ }
++ }
++ break;
++ }
++
++ /* case RPN: *//*Remote port negotiation command */
++
++/* {
++ __u8 dlci;
++
++ dlci = (mcc_short_pkt->value[0]) >> 2;
++
++ if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++ if (mcc_short_pkt->h.length.len == 1) {
++ TS0710_DEBUG("Received Remote port negotiation command\n");
++ ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
++ } else {
++*/
++ /* Accept the other sides settings (accept all for now) */
++/* TS0710_DEBUG("Received Remote port negotiation respons\n");
++ memcpy(&rpn_val, &mcc_short_pkt->value[1], 8);
++ ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
++*/
++ /* Zero the parametermask after response */
++/* memset(&rpn_val.pm, 0, 2);
++ }
++ }
++ break;
++ }
++*/
++/*
++ case RLS: *//*Remote line status */
++/* {
++ __u8 dlci;
++ __u8 err_code;
++
++ TS0710_DEBUG("Received Remote line status\n");
++ if (mcc_short_pkt->h.type.cr == MCC_CMD) {
++ dlci = mcc_short_pkt->value[0] >> 2;
++ err_code = mcc_short_pkt->value[1];
++
++ ts0710_rls_msg(ts0710, MCC_RSP, dlci, err_code);
++ }
++ break;
++ }
++*/
++ case PN: /*DLC parameter negotiation */
++ {
++ __u8 dlci;
++ __u16 frame_size;
++ pn_msg *pn_pkt;
++
++ pn_pkt = (pn_msg *) data;
++ dlci = pn_pkt->dlci;
++ frame_size = GET_PN_MSG_FRAME_SIZE(pn_pkt);
++ TS0710_DEBUG
++ ("Received DLC parameter negotiation, PN\n");
++ if (pn_pkt->mcc_s_head.type.cr == MCC_CMD) {
++ TS0710_DEBUG("received PN command with:\n");
++ TS0710_DEBUG("Frame size:%d\n", frame_size);
++
++ frame_size =
++ min(frame_size, ts0710->dlci[dlci].mtu);
++ send_pn_msg(ts0710, pn_pkt->prior, frame_size,
++ 0, 0, dlci, MCC_RSP);
++ ts0710->dlci[dlci].mtu = frame_size;
++ TS0710_DEBUG("process_mcc : mtu set to %d\n",
++ ts0710->dlci[dlci].mtu);
++ } else {
++ TS0710_DEBUG("received PN response with:\n");
++ TS0710_DEBUG("Frame size:%d\n", frame_size);
++
++ frame_size =
++ min(frame_size, ts0710->dlci[dlci].mtu);
++ ts0710->dlci[dlci].mtu = frame_size;
++
++ TS0710_DEBUG
++ ("process_mcc : mtu set on dlci:%d to %d\n",
++ dlci, ts0710->dlci[dlci].mtu);
++
++ if (ts0710->dlci[dlci].state == NEGOTIATING) {
++ ts0710->dlci[dlci].state = CONNECTING;
++ wake_up_interruptible(&ts0710->
++ dlci[dlci].
++ open_wait);
++ }
++ }
++ break;
++ }
++
++ case NSC: /*Non supported command resonse */
++ TS0710_LOG("MUX Received Non supported command response\n");
++ break;
++
++ default: /*Non supported command received */
++ TS0710_LOG("MUX Received a non supported command\n");
++ send_nsc_msg(ts0710, mcc_short_pkt->h.type, MCC_RSP);
++ break;
++ }
++}
++
++static mux_recv_packet *get_mux_recv_packet(__u32 size)
++{
++ mux_recv_packet *recv_packet;
++
++ TS0710_DEBUG("Enter into get_mux_recv_packet");
++
++ recv_packet =
++ (mux_recv_packet *) kmalloc(sizeof(mux_recv_packet), GFP_ATOMIC);
++ if (!recv_packet) {
++ return 0;
++ }
++
++ recv_packet->data = (__u8 *) kmalloc(size, GFP_ATOMIC);
++ if (!(recv_packet->data)) {
++ kfree(recv_packet);
++ return 0;
++ }
++ recv_packet->length = 0;
++ recv_packet->next = 0;
++ return recv_packet;
++}
++
++static void free_mux_recv_packet(mux_recv_packet * recv_packet)
++{
++ TS0710_DEBUG("Enter into free_mux_recv_packet");
++
++ if (!recv_packet) {
++ return;
++ }
++
++ if (recv_packet->data) {
++ kfree(recv_packet->data);
++ }
++ kfree(recv_packet);
++}
++
++static void free_mux_recv_struct(mux_recv_struct * recv_info)
++{
++ mux_recv_packet *recv_packet1, *recv_packet2;
++
++ if (!recv_info) {
++ return;
++ }
++
++ recv_packet1 = recv_info->mux_packet;
++ while (recv_packet1) {
++ recv_packet2 = recv_packet1->next;
++ free_mux_recv_packet(recv_packet1);
++ recv_packet1 = recv_packet2;
++ }
++
++ kfree(recv_info);
++}
++
++static inline void add_post_recv_queue(mux_recv_struct ** head,
++ mux_recv_struct * new_item)
++{
++ new_item->next = *head;
++ *head = new_item;
++}
++
++static void ts0710_flow_on(__u8 dlci, ts0710_con * ts0710)
++{
++ int i;
++ __u8 cmdtty;
++ __u8 datatty;
++ struct tty_struct *tty;
++ mux_recv_struct *recv_info;
++
++ if ((ts0710->dlci[0].state != CONNECTED)
++ && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++ return;
++ } else if ((ts0710->dlci[dlci].state != CONNECTED)
++ && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++ return;
++ }
++
++ if (!(ts0710->dlci[dlci].flow_control)) {
++ return;
++ }
++
++ cmdtty = dlci2tty[dlci].cmdtty;
++ datatty = dlci2tty[dlci].datatty;
++
++ if (cmdtty != datatty) {
++ /* Check AT cmd tty */
++ tty = mux_table[cmdtty];
++ if (mux_tty[cmdtty] && tty) {
++ if (test_bit(TTY_THROTTLED, &tty->flags)) {
++ return;
++ }
++ }
++ recv_info = mux_recv_info[cmdtty];
++ if (mux_recv_info_flags[cmdtty] && recv_info) {
++ if (recv_info->total) {
++ return;
++ }
++ }
++
++ /* Check data tty */
++ tty = mux_table[datatty];
++ if (mux_tty[datatty] && tty) {
++ if (test_bit(TTY_THROTTLED, &tty->flags)) {
++ return;
++ }
++ }
++ recv_info = mux_recv_info[datatty];
++ if (mux_recv_info_flags[datatty] && recv_info) {
++ if (recv_info->total) {
++ return;
++ }
++ }
++ }
++
++ for (i = 0; i < 3; i++) {
++ if (ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, dlci) <
++ 0) {
++ continue;
++ } else {
++ TS0710_LOG("MUX send Flow on on dlci %d\n", dlci);
++ ts0710->dlci[dlci].flow_control = 0;
++ break;
++ }
++ }
++}
++
++static void ts0710_flow_off(struct tty_struct *tty, __u8 dlci,
++ ts0710_con * ts0710)
++{
++ int i;
++
++ if (test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
++ return;
++ }
++
++ if ((ts0710->dlci[0].state != CONNECTED)
++ && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++ return;
++ } else if ((ts0710->dlci[dlci].state != CONNECTED)
++ && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++ return;
++ }
++
++ if (ts0710->dlci[dlci].flow_control) {
++ return;
++ }
++
++ for (i = 0; i < 3; i++) {
++ if (ts0710_msc_msg
++ (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
++ continue;
++ } else {
++ TS0710_LOG("MUX send Flow off on dlci %d\n", dlci);
++ ts0710->dlci[dlci].flow_control = 1;
++ break;
++ }
++ }
++}
++
++int ts0710_recv_data(ts0710_con * ts0710, char *data, int len)
++{
++ short_frame *short_pkt;
++ long_frame *long_pkt;
++ __u8 *uih_data_start;
++ __u32 uih_len;
++ __u8 dlci;
++ __u8 be_connecting;
++#ifdef TS0710DEBUG
++ unsigned long t;
++#endif
++
++ short_pkt = (short_frame *) data;
++
++ dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
++ switch (CLR_PF(short_pkt->h.control)) {
++ case SABM:
++ TS0710_DEBUG("SABM-packet received\n");
++
++/*For BP UART problem
++ if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++ break;
++*/
++
++ if (!dlci) {
++ TS0710_DEBUG("server channel == 0\n");
++ ts0710->dlci[0].state = CONNECTED;
++
++ TS0710_DEBUG("sending back UA - control channel\n");
++ send_ua(ts0710, dlci);
++ wake_up_interruptible(&ts0710->dlci[0].open_wait);
++
++ } else if (valid_dlci(dlci)) {
++
++ TS0710_DEBUG("Incomming connect on channel %d\n", dlci);
++
++ TS0710_DEBUG("sending UA, dlci %d\n", dlci);
++ send_ua(ts0710, dlci);
++
++ ts0710->dlci[dlci].state = CONNECTED;
++ wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++
++ } else {
++ TS0710_DEBUG("invalid dlci %d, sending DM\n", dlci);
++ send_dm(ts0710, dlci);
++ }
++
++ break;
++
++ case UA:
++ TS0710_DEBUG("UA packet received\n");
++
++/*For BP UART problem
++ if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++ break;
++*/
++
++ if (!dlci) {
++ TS0710_DEBUG("server channel == 0\n");
++
++ if (ts0710->dlci[0].state == CONNECTING) {
++ ts0710->dlci[0].state = CONNECTED;
++ wake_up_interruptible(&ts0710->dlci[0].
++ open_wait);
++ } else if (ts0710->dlci[0].state == DISCONNECTING) {
++ ts0710_upon_disconnect();
++ } else {
++ TS0710_DEBUG
++ (" Something wrong receiving UA packet\n");
++ }
++ } else if (valid_dlci(dlci)) {
++ TS0710_DEBUG("Incomming UA on channel %d\n", dlci);
++
++ if (ts0710->dlci[dlci].state == CONNECTING) {
++ ts0710->dlci[dlci].state = CONNECTED;
++ wake_up_interruptible(&ts0710->dlci[dlci].
++ open_wait);
++ } else if (ts0710->dlci[dlci].state == DISCONNECTING) {
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ wake_up_interruptible(&ts0710->dlci[dlci].
++ open_wait);
++ wake_up_interruptible(&ts0710->dlci[dlci].
++ close_wait);
++ ts0710_reset_dlci(dlci);
++ } else {
++ TS0710_DEBUG
++ (" Something wrong receiving UA packet\n");
++ }
++ } else {
++ TS0710_DEBUG("invalid dlci %d\n", dlci);
++ }
++
++ break;
++
++ case DM:
++ TS0710_DEBUG("DM packet received\n");
++
++/*For BP UART problem
++ if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++ break;
++*/
++
++ if (!dlci) {
++ TS0710_DEBUG("server channel == 0\n");
++
++ if (ts0710->dlci[0].state == CONNECTING) {
++ be_connecting = 1;
++ } else {
++ be_connecting = 0;
++ }
++ ts0710_upon_disconnect();
++ if (be_connecting) {
++ ts0710->dlci[0].state = REJECTED;
++ }
++ } else if (valid_dlci(dlci)) {
++ TS0710_DEBUG("Incomming DM on channel %d\n", dlci);
++
++ if (ts0710->dlci[dlci].state == CONNECTING) {
++ ts0710->dlci[dlci].state = REJECTED;
++ } else {
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ }
++ wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++ wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
++ ts0710_reset_dlci(dlci);
++ } else {
++ TS0710_DEBUG("invalid dlci %d\n", dlci);
++ }
++
++ break;
++
++ case DISC:
++ TS0710_DEBUG("DISC packet received\n");
++
++/*For BP UART problem
++ if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
++ break;
++*/
++
++ if (!dlci) {
++ TS0710_DEBUG("server channel == 0\n");
++
++ send_ua(ts0710, dlci);
++ TS0710_DEBUG("DISC, sending back UA\n");
++
++ ts0710_upon_disconnect();
++ } else if (valid_dlci(dlci)) {
++ TS0710_DEBUG("Incomming DISC on channel %d\n", dlci);
++
++ send_ua(ts0710, dlci);
++ TS0710_DEBUG("DISC, sending back UA\n");
++
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++ wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
++ ts0710_reset_dlci(dlci);
++ } else {
++ TS0710_DEBUG("invalid dlci %d\n", dlci);
++ }
++
++ break;
++
++ case UIH:
++ TS0710_DEBUG("UIH packet received\n");
++
++ if ((dlci >= TS0710_MAX_CHN)) {
++ TS0710_DEBUG("invalid dlci %d\n", dlci);
++ send_dm(ts0710, dlci);
++ break;
++ }
++
++ if (GET_PF(short_pkt->h.control)) {
++ TS0710_LOG
++ ("MUX Error %s: UIH packet with P/F set, discard it!\n",
++ __FUNCTION__);
++ break;
++ }
++
++ if ((ts0710->dlci[dlci].state != CONNECTED)
++ && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++ TS0710_LOG
++ ("MUX Error %s: DLCI %d not connected, discard it!\n",
++ __FUNCTION__, dlci);
++ send_dm(ts0710, dlci);
++ break;
++ }
++
++ if ((short_pkt->h.length.ea) == 0) {
++ TS0710_DEBUG("Long UIH packet received\n");
++ long_pkt = (long_frame *) data;
++ uih_len = GET_LONG_LENGTH(long_pkt->h.length);
++ uih_data_start = long_pkt->h.data;
++ TS0710_DEBUG("long packet length %d\n", uih_len);
++
++/*For BP UART problem
++ if (crc_check(data, LONG_CRC_CHECK, *(uih_data_start + uih_len)))
++ break;
++*/
++ } else {
++ TS0710_DEBUG("Short UIH pkt received\n");
++ uih_len = short_pkt->h.length.len;
++ uih_data_start = short_pkt->data;
++
++/*For BP UART problem
++ if (crc_check(data, SHORT_CRC_CHECK, *(uih_data_start + uih_len)))
++ break;
++*/
++ }
++
++ if (dlci == 0) {
++ TS0710_DEBUG("UIH on serv_channel 0\n");
++ process_mcc(data, len, ts0710,
++ !(short_pkt->h.length.ea));
++ } else if (valid_dlci(dlci)) {
++ /* do tty dispatch */
++ __u8 tag;
++ __u8 tty_idx;
++ struct tty_struct *tty;
++ __u8 queue_data;
++ __u8 post_recv;
++ __u8 flow_control;
++ mux_recv_struct *recv_info;
++ int recv_room;
++ mux_recv_packet *recv_packet, *recv_packet2;
++
++ TS0710_DEBUG("UIH on channel %d\n", dlci);
++
++ if (uih_len > ts0710->dlci[dlci].mtu) {
++ TS0710_PRINTK
++ ("MUX Error: DLCI:%d, uih_len:%d is bigger than mtu:%d, discard data!\n",
++ dlci, uih_len, ts0710->dlci[dlci].mtu);
++ break;
++ }
++
++ tag = *uih_data_start;
++ uih_data_start++;
++ uih_len--;
++
++ if (!uih_len) {
++ break;
++ }
++
++ switch (tag) {
++ case CMDTAG:
++ tty_idx = dlci2tty[dlci].cmdtty;
++ TS0710_DEBUG("CMDTAG on DLCI:%d, /dev/mux%d\n",
++ dlci, tty_idx);
++ TS0710_DEBUGSTR(uih_data_start, uih_len);
++ if (!(iscmdtty[tty_idx])) {
++ TS0710_PRINTK
++ ("MUX Error: %s: Wrong CMDTAG on DLCI:%d, /dev/mux%d\n",
++ __FUNCTION__, dlci, tty_idx);
++ }
++ break;
++ case DATATAG:
++ default:
++ tty_idx = dlci2tty[dlci].datatty;
++ TS0710_DEBUG
++ ("NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
++ dlci, tty_idx);
++ if (iscmdtty[tty_idx]) {
++ TS0710_PRINTK
++ ("MUX Error: %s: Wrong NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
++ __FUNCTION__, dlci, tty_idx);
++ }
++ break;
++ }
++ tty = mux_table[tty_idx];
++ if ((!mux_tty[tty_idx]) || (!tty)) {
++ TS0710_PRINTK
++ ("MUX: No application waiting for, discard it! /dev/mux%d\n",
++ tty_idx);
++ } else { /* Begin processing received data */
++ if ((!mux_recv_info_flags[tty_idx])
++ || (!mux_recv_info[tty_idx])) {
++ TS0710_PRINTK
++ ("MUX Error: No mux_recv_info, discard it! /dev/mux%d\n",
++ tty_idx);
++ break;
++ }
++
++ recv_info = mux_recv_info[tty_idx];
++ if (recv_info->total > 8192) {
++ TS0710_PRINTK
++ ("MUX : discard data for tty_idx:%d, recv_info->total > 8192 \n",
++ tty_idx);
++ break;
++ }
++
++ queue_data = 0;
++ post_recv = 0;
++ flow_control = 0;
++ recv_room = 65535;
++ if (tty->receive_room)
++ recv_room = tty->receive_room;
++
++ if (test_bit(TTY_THROTTLED, &tty->flags)) {
++ queue_data = 1;
++ } else {
++ if (test_bit
++ (TTY_DONT_FLIP, &tty->flags)) {
++ queue_data = 1;
++ post_recv = 1;
++ } else if (recv_info->total) {
++ queue_data = 1;
++ post_recv = 1;
++ } else if (recv_room < uih_len) {
++ queue_data = 1;
++ flow_control = 1;
++ }
++
++ if ((recv_room -
++ (uih_len + recv_info->total)) <
++ ts0710->dlci[dlci].mtu) {
++ flow_control = 1;
++ }
++ }
++
++ if (!queue_data) {
++ /* Put received data into read buffer of tty */
++ TS0710_DEBUG
++ ("Put received data into read buffer of /dev/mux%d",
++ tty_idx);
++
++#ifdef TS0710DEBUG
++ t = jiffies;
++#endif
++
++ (tty->ldisc.receive_buf) (tty,
++ uih_data_start,
++ NULL,
++ uih_len);
++
++#ifdef TS0710DEBUG
++ TS0710_DEBUG
++ ("tty->ldisc.receive_buf take ticks: %lu",
++ (jiffies - t));
++#endif
++
++ } else { /* Queue data */
++
++ TS0710_DEBUG
++ ("Put received data into recv queue of /dev/mux%d",
++ tty_idx);
++ if (recv_info->total) {
++ /* recv_info is already linked into mux_recv_queue */
++
++ recv_packet =
++ get_mux_recv_packet
++ (uih_len);
++ if (!recv_packet) {
++ TS0710_PRINTK
++ ("MUX %s: no memory\n",
++ __FUNCTION__);
++ break;
++ }
++
++ memcpy(recv_packet->data,
++ uih_data_start, uih_len);
++ recv_packet->length = uih_len;
++ recv_info->total += uih_len;
++ recv_packet->next = NULL;
++
++ if (!(recv_info->mux_packet)) {
++ recv_info->mux_packet =
++ recv_packet;
++ } else {
++ recv_packet2 =
++ recv_info->
++ mux_packet;
++ while (recv_packet2->
++ next) {
++ recv_packet2 =
++ recv_packet2->
++ next;
++ }
++ recv_packet2->next =
++ recv_packet;
++ } /* End if( !(recv_info->mux_packet) ) */
++ } else { /* recv_info->total == 0 */
++ if (uih_len >
++ TS0710MUX_RECV_BUF_SIZE) {
++ TS0710_PRINTK
++ ("MUX Error: tty_idx:%d, uih_len == %d is too big\n",
++ tty_idx, uih_len);
++ uih_len =
++ TS0710MUX_RECV_BUF_SIZE;
++ }
++ memcpy(recv_info->data,
++ uih_data_start, uih_len);
++ recv_info->length = uih_len;
++ recv_info->total = uih_len;
++
++ add_post_recv_queue
++ (&mux_recv_queue,
++ recv_info);
++ } /* End recv_info->total == 0 */
++ } /* End Queue data */
++
++ if (flow_control) {
++ /* Do something for flow control */
++ ts0710_flow_off(tty, dlci, ts0710);
++ }
++
++ if (tty_idx ==
++ dlci2tty[TS0710MUX_GPRS1_DLCI].datatty) {
++ if (add_count
++ (TS0710MUX_GPRS1_RECV_COUNT_IDX,
++ uih_len) < 0) {
++ post_recv_count_flag = 1;
++ post_recv = 1;
++ mux_data_count2
++ [TS0710MUX_GPRS1_RECV_COUNT_IDX]
++ += uih_len;
++ }
++ } else if (tty_idx ==
++ dlci2tty[TS0710MUX_GPRS2_DLCI].
++ datatty) {
++ if (add_count
++ (TS0710MUX_GPRS2_RECV_COUNT_IDX,
++ uih_len) < 0) {
++ post_recv_count_flag = 1;
++ post_recv = 1;
++ mux_data_count2
++ [TS0710MUX_GPRS2_RECV_COUNT_IDX]
++ += uih_len;
++ }
++ }
++
++ if (post_recv)
++ schedule_work(&post_recv_tqueue);
++ } /* End processing received data */
++ } else {
++ TS0710_DEBUG("invalid dlci %d\n", dlci);
++ }
++
++ break;
++
++ default:
++ TS0710_DEBUG("illegal packet\n");
++ break;
++ }
++ return 0;
++}
++
++/*
++int ts0710_send_data(ts0710_con *ts0710, __u8 dlci, __u8 *data, __u32 count)
++{
++ __u32 c, total = 0;
++ __u8 tag, first;
++
++ if( ts0710->dlci[0].state == FLOW_STOPPED ){
++ TS0710_DEBUG("Flow stopped on all channels, returning zero\n");
++*/
++/*
++ return -EFLOWSTOPPED;
++ } else if( ts0710->dlci[dlci].state == FLOW_STOPPED ){
++ TS0710_DEBUG("Flow stopped, returning zero\n");
++*/
++/*
++ return -EFLOWSTOPPED;
++ } else if( ts0710->dlci[dlci].state == CONNECTED ){
++
++ TS0710_DEBUG("trying to send %d bytes\n", count);
++ tag = *data;
++ first = 1;
++*/
++ /* The first byte is always a Cmd/Data tag */
++/*
++ while( count > 1 ){
++
++ c = min(count, ts0710->dlci[dlci].mtu);
++ if( queue_uih(data, c, ts0710, dlci) <= 0 ) {
++ break;
++ }
++
++ total += (c - 1);
++ data += (c - 1);
++ *data = tag;
++ count -= (c - 1);
++
++ if( first ) {
++ first = 0;
++ total++;
++ }
++ }
++ TS0710_DEBUG("sent %d bytes\n", total);
++ return total;
++ } else {
++ TS0710_DEBUG("DLCI %d not connected\n", dlci);
++ return -EDISCONNECTED;
++ }
++}
++*/
++
++/* Close ts0710 channel */
++static void ts0710_close_channel(__u8 dlci)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int try;
++ unsigned long t;
++
++ TS0710_DEBUG("ts0710_disc_command on channel %d\n", dlci);
++
++ if ((ts0710->dlci[dlci].state == DISCONNECTED)
++ || (ts0710->dlci[dlci].state == REJECTED)) {
++ return;
++ } else if (ts0710->dlci[dlci].state == DISCONNECTING) {
++ /* Reentry */
++ return;
++ } else {
++ ts0710->dlci[dlci].state = DISCONNECTING;
++ try = 3;
++ while (try--) {
++ t = jiffies;
++ send_disc(ts0710, dlci);
++ interruptible_sleep_on_timeout(&ts0710->dlci[dlci].
++ close_wait,
++ TS0710MUX_TIME_OUT);
++ if (ts0710->dlci[dlci].state == DISCONNECTED) {
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI %d Send DISC got signal!\n",
++ dlci);
++ break;
++ } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI %d Send DISC timeout!\n", dlci);
++ continue;
++ }
++ }
++
++ if (ts0710->dlci[dlci].state != DISCONNECTED) {
++ if (dlci == 0) { /* Control Channel */
++ ts0710_upon_disconnect();
++ } else { /* Other Channel */
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ wake_up_interruptible(&ts0710->dlci[dlci].
++ close_wait);
++ ts0710_reset_dlci(dlci);
++ }
++ }
++ }
++}
++
++int ts0710_open_channel(__u8 dlci)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int try;
++ int retval;
++ unsigned long t;
++
++ retval = -ENODEV;
++ if (dlci == 0) { // control channel
++ if ((ts0710->dlci[0].state == CONNECTED)
++ || (ts0710->dlci[0].state == FLOW_STOPPED)) {
++ return 0;
++ } else if (ts0710->dlci[0].state == CONNECTING) {
++ /* Reentry */
++ TS0710_PRINTK
++ ("MUX DLCI: 0, reentry to open DLCI 0, pid: %d, %s !\n",
++ current->pid, current->comm);
++ try = 11;
++ while (try--) {
++ t = jiffies;
++ interruptible_sleep_on_timeout(&ts0710->dlci[0].
++ open_wait,
++ TS0710MUX_TIME_OUT);
++ if ((ts0710->dlci[0].state == CONNECTED)
++ || (ts0710->dlci[0].state ==
++ FLOW_STOPPED)) {
++ retval = 0;
++ break;
++ } else if (ts0710->dlci[0].state == REJECTED) {
++ retval = -EREJECTED;
++ break;
++ } else if (ts0710->dlci[0].state ==
++ DISCONNECTED) {
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Wait for connecting got signal!\n",
++ dlci);
++ retval = -EAGAIN;
++ break;
++ } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Wait for connecting timeout!\n",
++ dlci);
++ continue;
++ } else if (ts0710->dlci[0].state == CONNECTING) {
++ continue;
++ }
++ }
++
++ if (ts0710->dlci[0].state == CONNECTING) {
++ ts0710->dlci[0].state = DISCONNECTED;
++ }
++ } else if ((ts0710->dlci[0].state != DISCONNECTED)
++ && (ts0710->dlci[0].state != REJECTED)) {
++ TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
++ return retval;
++ } else {
++ ts0710->initiator = 1;
++ ts0710->dlci[0].state = CONNECTING;
++ ts0710->dlci[0].initiator = 1;
++ try = 10;
++ while (try--) {
++ t = jiffies;
++ send_sabm(ts0710, 0);
++ interruptible_sleep_on_timeout(&ts0710->dlci[0].
++ open_wait,
++ TS0710MUX_TIME_OUT);
++ if ((ts0710->dlci[0].state == CONNECTED)
++ || (ts0710->dlci[0].state ==
++ FLOW_STOPPED)) {
++ retval = 0;
++ break;
++ } else if (ts0710->dlci[0].state == REJECTED) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM got rejected!\n",
++ dlci);
++ retval = -EREJECTED;
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM got signal!\n",
++ dlci);
++ retval = -EAGAIN;
++ break;
++ } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM timeout!\n",
++ dlci);
++ continue;
++ }
++ }
++
++ if (ts0710->dlci[0].state == CONNECTING) {
++ ts0710->dlci[0].state = DISCONNECTED;
++ }
++ wake_up_interruptible(&ts0710->dlci[0].open_wait);
++ }
++ } else { // other channel
++ if ((ts0710->dlci[0].state != CONNECTED)
++ && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++ return retval;
++ } else if ((ts0710->dlci[dlci].state == CONNECTED)
++ || (ts0710->dlci[dlci].state == FLOW_STOPPED)) {
++ return 0;
++ } else if ((ts0710->dlci[dlci].state == NEGOTIATING)
++ || (ts0710->dlci[dlci].state == CONNECTING)) {
++ /* Reentry */
++ try = 8;
++ while (try--) {
++ t = jiffies;
++ interruptible_sleep_on_timeout(&ts0710->
++ dlci[dlci].
++ open_wait,
++ TS0710MUX_TIME_OUT);
++ if ((ts0710->dlci[dlci].state == CONNECTED)
++ || (ts0710->dlci[dlci].state ==
++ FLOW_STOPPED)) {
++ retval = 0;
++ break;
++ } else if (ts0710->dlci[dlci].state == REJECTED) {
++ retval = -EREJECTED;
++ break;
++ } else if (ts0710->dlci[dlci].state ==
++ DISCONNECTED) {
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Wait for connecting got signal!\n",
++ dlci);
++ retval = -EAGAIN;
++ break;
++ } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Wait for connecting timeout!\n",
++ dlci);
++ continue;
++ } else
++ if ((ts0710->dlci[dlci].state ==
++ NEGOTIATING)
++ || (ts0710->dlci[dlci].state ==
++ CONNECTING)) {
++ continue;
++ }
++ }
++
++ if ((ts0710->dlci[dlci].state == NEGOTIATING)
++ || (ts0710->dlci[dlci].state == CONNECTING)) {
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ }
++ } else if ((ts0710->dlci[dlci].state != DISCONNECTED)
++ && (ts0710->dlci[dlci].state != REJECTED)) {
++ TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
++ return retval;
++ } else {
++ ts0710->dlci[dlci].state = NEGOTIATING;
++ ts0710->dlci[dlci].initiator = 1;
++ try = 3;
++ while (try--) {
++ t = jiffies;
++ send_pn_msg(ts0710, 7, ts0710->dlci[dlci].mtu,
++ 0, 0, dlci, 1);
++ interruptible_sleep_on_timeout(&ts0710->
++ dlci[dlci].
++ open_wait,
++ TS0710MUX_TIME_OUT);
++ if (ts0710->dlci[dlci].state == CONNECTING) {
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send pn_msg got signal!\n",
++ dlci);
++ retval = -EAGAIN;
++ break;
++ } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send pn_msg timeout!\n",
++ dlci);
++ continue;
++ }
++ }
++
++ if (ts0710->dlci[dlci].state == CONNECTING) {
++ try = 3;
++ while (try--) {
++ t = jiffies;
++ send_sabm(ts0710, dlci);
++ interruptible_sleep_on_timeout(&ts0710->
++ dlci
++ [dlci].
++ open_wait,
++ TS0710MUX_TIME_OUT);
++ if ((ts0710->dlci[dlci].state ==
++ CONNECTED)
++ || (ts0710->dlci[dlci].state ==
++ FLOW_STOPPED)) {
++ retval = 0;
++ break;
++ } else if (ts0710->dlci[dlci].state ==
++ REJECTED) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM got rejected!\n",
++ dlci);
++ retval = -EREJECTED;
++ break;
++ } else if (signal_pending(current)) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM got signal!\n",
++ dlci);
++ retval = -EAGAIN;
++ break;
++ } else if ((jiffies - t) >=
++ TS0710MUX_TIME_OUT) {
++ TS0710_PRINTK
++ ("MUX DLCI:%d Send SABM timeout!\n",
++ dlci);
++ continue;
++ }
++ }
++ }
++
++ if ((ts0710->dlci[dlci].state == NEGOTIATING)
++ || (ts0710->dlci[dlci].state == CONNECTING)) {
++ ts0710->dlci[dlci].state = DISCONNECTED;
++ }
++ wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
++ }
++ }
++ return retval;
++}
++
++static int ts0710_exec_test_cmd(void)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ __u8 *f_buf; /* Frame buffer */
++ __u8 *d_buf; /* Data buffer */
++ int retval = -EFAULT;
++ int j;
++ unsigned long t;
++
++ if (ts0710->be_testing) {
++ /* Reentry */
++ t = jiffies;
++ interruptible_sleep_on_timeout(&ts0710->test_wait,
++ 3 * TS0710MUX_TIME_OUT);
++ if (ts0710->be_testing == 0) {
++ if (ts0710->test_errs == 0) {
++ retval = 0;
++ } else {
++ retval = -EFAULT;
++ }
++ } else if (signal_pending(current)) {
++ TS0710_DEBUG
++ ("Wait for Test_cmd response got signal!\n");
++ retval = -EAGAIN;
++ } else if ((jiffies - t) >= 3 * TS0710MUX_TIME_OUT) {
++ TS0710_DEBUG("Wait for Test_cmd response timeout!\n");
++ retval = -EFAULT;
++ }
++ } else {
++ ts0710->be_testing = 1; /* Set the flag */
++
++ f_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
++ d_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
++ if ((!f_buf) || (!d_buf)) {
++ if (f_buf) {
++ kfree(f_buf);
++ }
++ if (d_buf) {
++ kfree(d_buf);
++ }
++
++ ts0710->be_testing = 0; /* Clear the flag */
++ ts0710->test_errs = TEST_PATTERN_SIZE;
++ wake_up_interruptible(&ts0710->test_wait);
++ return -ENOMEM;
++ }
++
++ for (j = 0; j < TEST_PATTERN_SIZE; j++) {
++ d_buf[j] = j & 0xFF;
++ }
++
++ t = jiffies;
++ ts0710_test_msg(ts0710, d_buf, TEST_PATTERN_SIZE, MCC_CMD,
++ f_buf);
++ interruptible_sleep_on_timeout(&ts0710->test_wait,
++ 2 * TS0710MUX_TIME_OUT);
++ if (ts0710->be_testing == 0) {
++ if (ts0710->test_errs == 0) {
++ retval = 0;
++ } else {
++ retval = -EFAULT;
++ }
++ } else if (signal_pending(current)) {
++ TS0710_DEBUG("Send Test_cmd got signal!\n");
++ retval = -EAGAIN;
++ } else if ((jiffies - t) >= 2 * TS0710MUX_TIME_OUT) {
++ TS0710_DEBUG("Send Test_cmd timeout!\n");
++ ts0710->test_errs = TEST_PATTERN_SIZE;
++ retval = -EFAULT;
++ }
++
++ ts0710->be_testing = 0; /* Clear the flag */
++ wake_up_interruptible(&ts0710->test_wait);
++
++ /* Release buffer */
++ if (f_buf) {
++ kfree(f_buf);
++ }
++ if (d_buf) {
++ kfree(d_buf);
++ }
++ }
++
++ return retval;
++}
++
++static void mux_sched_send(void)
++{
++
++#ifdef USB_FOR_MUX
++ schedule_work(&send_tqueue);
++#else
++ if (!tq_serial_for_mux) {
++ TS0710_PRINTK("MUX Error: %s: tq_serial_for_mux == 0\n",
++ __FUNCTION__);
++ return;
++ }
++ schedule_work(&send_tqueue);
++ mark_bh(SERIAL_BH);
++#endif
++
++}
++
++/****************************
++ * TTY driver routines
++*****************************/
++
++static void mux_close(struct tty_struct *tty, struct file *filp)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int line;
++ __u8 dlci;
++ __u8 cmdtty;
++ __u8 datatty;
++
++ UNUSED_PARAM(filp);
++
++ if (!tty) {
++ return;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ return;
++ }
++ if (mux_tty[line] > 0)
++ mux_tty[line]--;
++
++ dlci = tty2dlci[line];
++ cmdtty = dlci2tty[dlci].cmdtty;
++ datatty = dlci2tty[dlci].datatty;
++ if ((mux_tty[cmdtty] == 0) && (mux_tty[datatty] == 0)) {
++ if (dlci == 1) {
++ ts0710_close_channel(0);
++ TS0710_PRINTK
++ ("MUX mux_close: tapisrv might be down!!! Close DLCI 1\n");
++ TS0710_SIG2APLOGD();
++ }
++ ts0710_close_channel(dlci);
++ }
++
++ if (mux_tty[line] == 0) {
++ if ((mux_send_info_flags[line])
++ && (mux_send_info[line])
++ /*&& (mux_send_info[line]->filled == 0) */
++ ) {
++ mux_send_info_flags[line] = 0;
++ kfree(mux_send_info[line]);
++ mux_send_info[line] = 0;
++ TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++ }
++
++ if ((mux_recv_info_flags[line])
++ && (mux_recv_info[line])
++ && (mux_recv_info[line]->total == 0)) {
++ mux_recv_info_flags[line] = 0;
++ free_mux_recv_struct(mux_recv_info[line]);
++ mux_recv_info[line] = 0;
++ TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
++ }
++
++ ts0710_flow_on(dlci, ts0710);
++ schedule_work(&post_recv_tqueue);
++
++ wake_up_interruptible(&tty->read_wait);
++ wake_up_interruptible(&tty->write_wait);
++ tty->packet = 0;
++ }
++}
++
++static void mux_throttle(struct tty_struct *tty)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int line;
++ int i;
++ __u8 dlci;
++
++ if (!tty) {
++ return;
++ }
++
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ return;
++ }
++
++ TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
++ line);
++
++ dlci = tty2dlci[line];
++ if ((ts0710->dlci[0].state != CONNECTED)
++ && (ts0710->dlci[0].state != FLOW_STOPPED)) {
++ return;
++ } else if ((ts0710->dlci[dlci].state != CONNECTED)
++ && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
++ return;
++ }
++
++ if (ts0710->dlci[dlci].flow_control) {
++ return;
++ }
++
++ for (i = 0; i < 3; i++) {
++ if (ts0710_msc_msg
++ (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
++ continue;
++ } else {
++ TS0710_LOG("MUX Send Flow off on dlci %d\n", dlci);
++ ts0710->dlci[dlci].flow_control = 1;
++ break;
++ }
++ }
++}
++
++static void mux_unthrottle(struct tty_struct *tty)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int line;
++ __u8 dlci;
++ mux_recv_struct *recv_info;
++
++ if (!tty) {
++ return;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ return;
++ }
++
++ if ((!mux_recv_info_flags[line]) || (!mux_recv_info[line])) {
++ return;
++ }
++
++ TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
++ line);
++
++ recv_info = mux_recv_info[line];
++ dlci = tty2dlci[line];
++
++ if (recv_info->total) {
++ recv_info->post_unthrottle = 1;
++ schedule_work(&post_recv_tqueue);
++ } else {
++ ts0710_flow_on(dlci, ts0710);
++ }
++}
++
++static int mux_chars_in_buffer(struct tty_struct *tty)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int retval;
++ int line;
++ __u8 dlci;
++ mux_send_struct *send_info;
++
++ retval = TS0710MUX_MAX_CHARS_IN_BUF;
++ if (!tty) {
++ goto out;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ goto out;
++ }
++
++ dlci = tty2dlci[line];
++ if (ts0710->dlci[0].state == FLOW_STOPPED) {
++ TS0710_DEBUG
++ ("Flow stopped on all channels, returning MAX chars in buffer\n");
++ goto out;
++ } else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped, returning MAX chars in buffer\n");
++ goto out;
++ } else if (ts0710->dlci[dlci].state != CONNECTED) {
++ TS0710_DEBUG("DLCI %d not connected\n", dlci);
++ goto out;
++ }
++
++ if (!(mux_send_info_flags[line])) {
++ goto out;
++ }
++ send_info = mux_send_info[line];
++ if (!send_info) {
++ goto out;
++ }
++ if (send_info->filled) {
++ goto out;
++ }
++
++ retval = 0;
++
++ out:
++ return retval;
++}
++
++static int mux_chars_in_serial_buffer(struct tty_struct *tty)
++{
++ UNUSED_PARAM(tty);
++
++ if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
++ TS0710_PRINTK
++ ("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n",
++ __FUNCTION__);
++
++#ifndef USB_FOR_MUX
++ TS0710_PRINTK
++ ("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n",
++ __FUNCTION__);
++ TS0710_SIG2APLOGD();
++#endif
++
++ return 0;
++ }
++ return COMM_FOR_MUX_DRIVER->chars_in_buffer(COMM_FOR_MUX_TTY);
++}
++
++static int mux_write(struct tty_struct *tty,
++ const unsigned char *buf, int count)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int line;
++ __u8 dlci;
++ mux_send_struct *send_info;
++ __u8 *d_buf;
++ __u16 c;
++ __u8 post_recv;
++
++ if (count <= 0) {
++ return 0;
++ }
++
++ if (!tty) {
++ return 0;
++ }
++
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS))
++ return -ENODEV;
++
++ dlci = tty2dlci[line];
++ if (ts0710->dlci[0].state == FLOW_STOPPED) {
++ TS0710_DEBUG
++ ("Flow stopped on all channels, returning zero /dev/mux%d\n",
++ line);
++ return 0;
++ } else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped, returning zero /dev/mux%d\n", line);
++ return 0;
++ } else if (ts0710->dlci[dlci].state == CONNECTED) {
++
++ if (!(mux_send_info_flags[line])) {
++ TS0710_PRINTK
++ ("MUX Error: mux_write: mux_send_info_flags[%d] == 0\n",
++ line);
++ return -ENODEV;
++ }
++ send_info = mux_send_info[line];
++ if (!send_info) {
++ TS0710_PRINTK
++ ("MUX Error: mux_write: mux_send_info[%d] == 0\n",
++ line);
++ return -ENODEV;
++ }
++
++ c = min(count, (ts0710->dlci[dlci].mtu - 1));
++ if (c <= 0) {
++ return 0;
++ }
++
++ if (test_and_set_bit(BUF_BUSY, &send_info->flags))
++ return 0;
++
++ if (send_info->filled) {
++ clear_bit(BUF_BUSY, &send_info->flags);
++ return 0;
++ }
++
++ d_buf = ((__u8 *) send_info->buf) + TS0710MUX_SEND_BUF_OFFSET;
++ memcpy(&d_buf[1], buf, c);
++
++ TS0710_DEBUG("Prepare to send %d bytes from /dev/mux%d", c,
++ line);
++ if (iscmdtty[line]) {
++ TS0710_DEBUGSTR(&d_buf[1], c);
++ TS0710_DEBUG("CMDTAG");
++ d_buf[0] = CMDTAG;
++ } else {
++ TS0710_DEBUG("DATATAG");
++ d_buf[0] = DATATAG;
++ }
++
++ TS0710_DEBUGHEX(d_buf, c + 1);
++
++ send_info->frame = d_buf;
++ queue_uih(send_info, c + 1, ts0710, dlci);
++ send_info->filled = 1;
++ clear_bit(BUF_BUSY, &send_info->flags);
++
++ post_recv = 0;
++ if (dlci == TS0710MUX_GPRS1_DLCI) {
++ if (add_count
++ (TS0710MUX_GPRS1_SEND_COUNT_IDX, c) < 0) {
++ post_recv_count_flag = 1;
++ post_recv = 1;
++ mux_data_count2[TS0710MUX_GPRS1_SEND_COUNT_IDX]
++ += c;
++ }
++ } else if (dlci == TS0710MUX_GPRS2_DLCI) {
++ if (add_count
++ (TS0710MUX_GPRS2_SEND_COUNT_IDX, c) < 0) {
++ post_recv_count_flag = 1;
++ post_recv = 1;
++ mux_data_count2[TS0710MUX_GPRS2_SEND_COUNT_IDX]
++ += c;
++ }
++ }
++
++ if (post_recv)
++ schedule_work(&post_recv_tqueue);
++
++ if (mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY) == 0) {
++ /* Sending bottom half should be
++ run after return from this function */
++ mux_sched_send();
++ }
++ return c;
++ } else {
++ TS0710_PRINTK("MUX mux_write: DLCI %d not connected\n", dlci);
++ return -EDISCONNECTED;
++ }
++}
++
++static int mux_write_room(struct tty_struct *tty)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int retval;
++ int line;
++ __u8 dlci;
++ mux_send_struct *send_info;
++
++ retval = 0;
++ if (!tty) {
++ goto out;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ goto out;
++ }
++
++ dlci = tty2dlci[line];
++ if (ts0710->dlci[0].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped on all channels, returning ZERO\n");
++ goto out;
++ } else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped, returning ZERO\n");
++ goto out;
++ } else if (ts0710->dlci[dlci].state != CONNECTED) {
++ TS0710_DEBUG("DLCI %d not connected\n", dlci);
++ goto out;
++ }
++
++ if (!(mux_send_info_flags[line])) {
++ goto out;
++ }
++ send_info = mux_send_info[line];
++ if (!send_info) {
++ goto out;
++ }
++ if (send_info->filled) {
++ goto out;
++ }
++
++ retval = ts0710->dlci[dlci].mtu - 1;
++
++ out:
++ return retval;
++}
++
++static int mux_ioctl(struct tty_struct *tty, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int line;
++ __u8 dlci;
++
++ UNUSED_PARAM(file);
++ UNUSED_PARAM(arg);
++
++ if (!tty) {
++ return -EIO;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ return -ENODEV;
++ }
++
++ dlci = tty2dlci[line];
++ switch (cmd) {
++ case TS0710MUX_IO_MSC_HANGUP:
++ if (ts0710_msc_msg(ts0710, EA | RTR | DV, MCC_CMD, dlci) < 0) {
++ return -EAGAIN;
++ } else {
++ return 0;
++ }
++
++ case TS0710MUX_IO_TEST_CMD:
++ return ts0710_exec_test_cmd();
++/*
++ case TS0710MUX_IO_DLCI_FC_ON:
++ if( line == 0 ) {
++ break;
++ }
++ if( ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
++ return -EAGAIN;
++ } else {
++ return 0;
++ }
++
++ case TS0710MUX_IO_DLCI_FC_OFF:
++ if( line == 0 ) {
++ break;
++ }
++ if( ts0710_msc_msg(ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
++ return -EAGAIN;
++ } else {
++ return 0;
++ }
++
++ case TS0710MUX_IO_FC_ON:
++ if( line != 0 ) {
++ break;
++ }
++ if( ts0710_fcon_msg(ts0710, MCC_CMD) < 0) {
++ return -EAGAIN;
++ } else {
++ return 0;
++ }
++
++ case TS0710MUX_IO_FC_OFF:
++ if( line != 0 ) {
++ break;
++ }
++ if( ts0710_fcoff_msg(ts0710, MCC_CMD) < 0) {
++ return -EAGAIN;
++ } else {
++ return 0;
++ }
++*/
++ default:
++ break;
++ }
++ return -ENOIOCTLCMD;
++}
++
++static void mux_flush_buffer(struct tty_struct *tty)
++{
++ int line;
++
++ if (!tty) {
++ return;
++ }
++
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ return;
++ }
++
++ TS0710_PRINTK("MUX %s: line is:%d\n", __FUNCTION__, line);
++
++ if ((mux_send_info_flags[line])
++ && (mux_send_info[line])
++ && (mux_send_info[line]->filled)) {
++
++ mux_send_info[line]->filled = 0;
++ }
++
++ wake_up_interruptible(&tty->write_wait);
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
++ tty->ldisc.write_wakeup) {
++ (tty->ldisc.write_wakeup) (tty);
++ }
++
++/*
++ if( (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0) ) {
++ TS0710_PRINTK("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n", __FUNCTION__);
++
++#ifndef USB_FOR_MUX
++ TS0710_PRINTK("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n", __FUNCTION__);
++ TS0710_SIG2APLOGD();
++#endif
++
++ return;
++ }
++ return COMM_FOR_MUX_DRIVER->flush_buffer(COMM_FOR_MUX_TTY);
++*/
++}
++
++static int mux_open(struct tty_struct *tty, struct file *filp)
++{
++ int retval;
++ int line;
++ __u8 dlci;
++ __u8 cmdtty;
++ __u8 datatty;
++ mux_send_struct *send_info;
++ mux_recv_struct *recv_info;
++
++ UNUSED_PARAM(filp);
++
++ retval = -ENODEV;
++ if ((COMM_FOR_MUX_DRIVER == NULL) || (COMM_FOR_MUX_TTY == NULL)) {
++
++#ifdef USB_FOR_MUX
++ TS0710_PRINTK("MUX: please install and open IPC-USB first\n");
++#else
++ TS0710_PRINTK("MUX: please install and open ttyS0 first\n");
++#endif
++
++ goto out;
++ }
++
++ if (!tty) {
++ goto out;
++ }
++ line = tty->index;
++ if ((line < 0) || (line >= NR_MUXS)) {
++ goto out;
++ }
++#ifdef TS0710SERVER
++ /* do nothing as a server */
++ mux_tty[line]++;
++ retval = 0;
++#else
++ mux_tty[line]++;
++ dlci = tty2dlci[line];
++
++/* if( dlci == 1 ) { */
++ /* Open server channel 0 first */
++ if ((retval = ts0710_open_channel(0)) != 0) {
++ TS0710_PRINTK("MUX: Can't connect server channel 0!\n");
++ ts0710_init();
++
++ mux_tty[line]--;
++ goto out;
++ }
++/* } */
++
++ /* Allocate memory first. As soon as connection has been established, MUX may receive */
++ if (mux_send_info_flags[line] == 0) {
++ send_info =
++ (mux_send_struct *) kmalloc(sizeof(mux_send_struct),
++ GFP_KERNEL);
++ if (!send_info) {
++ retval = -ENOMEM;
++
++ mux_tty[line]--;
++ goto out;
++ }
++ send_info->length = 0;
++ send_info->flags = 0;
++ send_info->filled = 0;
++ mux_send_info[line] = send_info;
++ mux_send_info_flags[line] = 1;
++ TS0710_DEBUG("Allocate mux_send_info for /dev/mux%d", line);
++ }
++
++ if (mux_recv_info_flags[line] == 0) {
++ recv_info =
++ (mux_recv_struct *) kmalloc(sizeof(mux_recv_struct),
++ GFP_KERNEL);
++ if (!recv_info) {
++ mux_send_info_flags[line] = 0;
++ kfree(mux_send_info[line]);
++ mux_send_info[line] = 0;
++ TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++ retval = -ENOMEM;
++
++ mux_tty[line]--;
++ goto out;
++ }
++ recv_info->length = 0;
++ recv_info->total = 0;
++ recv_info->mux_packet = 0;
++ recv_info->next = 0;
++ recv_info->no_tty = line;
++ recv_info->post_unthrottle = 0;
++ mux_recv_info[line] = recv_info;
++ mux_recv_info_flags[line] = 1;
++ TS0710_DEBUG("Allocate mux_recv_info for /dev/mux%d", line);
++ }
++
++ /* Now establish DLCI connection */
++ cmdtty = dlci2tty[dlci].cmdtty;
++ datatty = dlci2tty[dlci].datatty;
++ if ((mux_tty[cmdtty] > 0) || (mux_tty[datatty] > 0)) {
++ if ((retval = ts0710_open_channel(dlci)) != 0) {
++ TS0710_PRINTK("MUX: Can't connected channel %d!\n",
++ dlci);
++ ts0710_reset_dlci(dlci);
++
++ mux_send_info_flags[line] = 0;
++ kfree(mux_send_info[line]);
++ mux_send_info[line] = 0;
++ TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
++
++ mux_recv_info_flags[line] = 0;
++ free_mux_recv_struct(mux_recv_info[line]);
++ mux_recv_info[line] = 0;
++ TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
++
++ mux_tty[line]--;
++ goto out;
++ }
++ }
++
++ retval = 0;
++#endif
++ out:
++ return retval;
++}
++
++/* mux dispatcher, call from serial.c receiver_chars() */
++void mux_dispatcher(struct tty_struct *tty)
++{
++ UNUSED_PARAM(tty);
++
++ schedule_work(&receive_tqueue);
++}
++
++/*For BP UART problem Begin*/
++#ifdef TS0710SEQ2
++static int send_ack(ts0710_con * ts0710, __u8 seq_num, __u8 bp_seq1,
++ __u8 bp_seq2)
++#else
++static int send_ack(ts0710_con * ts0710, __u8 seq_num)
++#endif
++{
++ __u8 buf[20];
++ short_frame *ack;
++
++#ifdef TS0710SEQ2
++ static __u16 ack_seq = 0;
++#endif
++
++ ack = (short_frame *) (buf + 1);
++ ack->h.addr.ea = 1;
++ ack->h.addr.cr = ((ts0710->initiator) & 0x1);
++ ack->h.addr.d = 0;
++ ack->h.addr.server_chn = 0;
++ ack->h.control = ACK;
++ ack->h.length.ea = 1;
++
++#ifdef TS0710SEQ2
++ ack->h.length.len = 5;
++ ack->data[0] = seq_num;
++ ack->data[1] = bp_seq1;
++ ack->data[2] = bp_seq2;
++ ack->data[3] = (ack_seq & 0xFF);
++ ack->data[4] = (ack_seq >> 8) & 0xFF;
++ ack_seq++;
++ ack->data[5] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
++#else
++ ack->h.length.len = 1;
++ ack->data[0] = seq_num;
++ ack->data[1] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
++#endif
++
++ return basic_write(ts0710, buf,
++ (sizeof(short_frame) + FCS_SIZE +
++ ack->h.length.len));
++}
++
++/*For BP UART problem End*/
++
++static void receive_worker(void *private_)
++{
++ struct tty_struct *tty = COMM_FOR_MUX_TTY;
++ int i, count;
++ static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
++ static unsigned char *tbuf_ptr = &tbuf[0];
++ static unsigned char *start_flag = 0;
++ unsigned char *search, *to, *from;
++ short_frame *short_pkt;
++ long_frame *long_pkt;
++ static int framelen = -1;
++
++ /*For BP UART problem Begin */
++ static __u8 expect_seq = 0;
++ __u32 crc_error;
++ __u8 *uih_data_start;
++ __u32 uih_len;
++ /*For BP UART problem End */
++
++ UNUSED_PARAM(private_);
++
++ if (!tty)
++ return;
++
++#ifdef USB_FOR_MUX
++ TS0710_DEBUG("Receive following bytes from IPC-USB");
++#else
++ TS0710_DEBUG("Receive following bytes from UART");
++#endif
++
++ TS0710_DEBUGHEX(cp, count);
++
++ if (count > (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf))) {
++ TS0710_PRINTK
++ ("MUX receive_worker: !!!!! Exceed buffer boundary !!!!!\n");
++ count = (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf));
++ }
++
++ count = tty_buffer_request_room(tty, count);
++
++ for (i = 0; i < count; i++)
++ tty_insert_flip_char(tty, tbuf_ptr[i], TTY_NORMAL);
++
++ tbuf_ptr += count;
++ search = &tbuf[0];
++
++ if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
++ schedule_work(&receive_tqueue);
++ return;
++ }
++
++ if ((start_flag != 0) && (framelen != -1)) {
++ if ((tbuf_ptr - start_flag) < framelen) {
++ clear_bit(RECV_RUNNING, &mux_recv_flags);
++ return;
++ }
++ }
++
++ while (1) {
++ if (start_flag == 0) { /* Frame Start Flag not found */
++ framelen = -1;
++ while (search < tbuf_ptr) {
++ if (*search == TS0710_BASIC_FLAG) {
++ start_flag = search;
++ break;
++ }
++#ifdef TS0710LOG
++ else {
++ TS0710_LOG(">S %02x %c\n", *search,
++ *search);
++ }
++#endif
++
++ search++;
++ }
++
++ if (start_flag == 0) {
++ tbuf_ptr = &tbuf[0];
++ break;
++ }
++ } else { /* Frame Start Flag found */
++ /* 1 start flag + 1 address + 1 control + 1 or 2 length + lengths data + 1 FCS + 1 end flag */
++ /* For BP UART problem 1 start flag + 1 seq_num + 1 address + ...... */
++ /*if( (framelen == -1) && ((tbuf_ptr - start_flag) > TS0710_MAX_HDR_SIZE) ) */
++ if ((framelen == -1) && ((tbuf_ptr - start_flag) > (TS0710_MAX_HDR_SIZE + SEQ_FIELD_SIZE))) { /*For BP UART problem */
++ /*short_pkt = (short_frame *) (start_flag + 1); */
++ short_pkt = (short_frame *) (start_flag + ADDRESS_FIELD_OFFSET); /*For BP UART problem */
++ if (short_pkt->h.length.ea == 1) { /* short frame */
++ /*framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1; */
++ framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1 + SEQ_FIELD_SIZE; /*For BP UART problem */
++ } else { /* long frame */
++ /*long_pkt = (long_frame *) (start_flag + 1); */
++ long_pkt = (long_frame *) (start_flag + ADDRESS_FIELD_OFFSET); /*For BP UART problem */
++ /*framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH( long_pkt->h.length ) + 2; */
++ framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH(long_pkt->h.length) + 2 + SEQ_FIELD_SIZE; /*For BP UART problem */
++ }
++
++ /*if( framelen > TS0710MUX_MAX_TOTAL_FRAME_SIZE ) { */
++ if (framelen > (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)) { /*For BP UART problem */
++ TS0710_LOGSTR_FRAME(0, start_flag,
++ (tbuf_ptr -
++ start_flag));
++ TS0710_PRINTK
++ ("MUX Error: %s: frame length:%d is bigger than Max total frame size:%d\n",
++ /*__FUNCTION__, framelen, TS0710MUX_MAX_TOTAL_FRAME_SIZE);*/
++ __FUNCTION__, framelen, (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)); /*For BP UART problem */
++ search = start_flag + 1;
++ start_flag = 0;
++ framelen = -1;
++ continue;
++ }
++ }
++
++ if ((framelen != -1)
++ && ((tbuf_ptr - start_flag) >= framelen)) {
++ if (*(start_flag + framelen - 1) == TS0710_BASIC_FLAG) { /* OK, We got one frame */
++
++ /*For BP UART problem Begin */
++ TS0710_LOGSTR_FRAME(0, start_flag,
++ framelen);
++ TS0710_DEBUGHEX(start_flag, framelen);
++
++ short_pkt =
++ (short_frame *) (start_flag +
++ ADDRESS_FIELD_OFFSET);
++ if ((short_pkt->h.length.ea) == 0) {
++ long_pkt =
++ (long_frame *) (start_flag +
++ ADDRESS_FIELD_OFFSET);
++ uih_len =
++ GET_LONG_LENGTH(long_pkt->h.
++ length);
++ uih_data_start =
++ long_pkt->h.data;
++
++ crc_error =
++ crc_check((__u8
++ *) (start_flag +
++ SLIDE_BP_SEQ_OFFSET),
++ LONG_CRC_CHECK +
++ 1,
++ *(uih_data_start +
++ uih_len));
++ } else {
++ uih_len =
++ short_pkt->h.length.len;
++ uih_data_start =
++ short_pkt->data;
++
++ crc_error =
++ crc_check((__u8
++ *) (start_flag +
++ SLIDE_BP_SEQ_OFFSET),
++ SHORT_CRC_CHECK +
++ 1,
++ *(uih_data_start +
++ uih_len));
++ }
++
++ if (!crc_error) {
++ if (expect_seq ==
++ *(start_flag +
++ SLIDE_BP_SEQ_OFFSET)) {
++ expect_seq++;
++ if (expect_seq >= 4) {
++ expect_seq = 0;
++ }
++#ifdef TS0710SEQ2
++ send_ack
++ (&ts0710_connection,
++ expect_seq,
++ *(start_flag +
++ FIRST_BP_SEQ_OFFSET),
++ *(start_flag +
++ SECOND_BP_SEQ_OFFSET));
++#else
++ send_ack
++ (&ts0710_connection,
++ expect_seq);
++#endif
++
++ ts0710_recv_data
++ (&ts0710_connection,
++ start_flag +
++ ADDRESS_FIELD_OFFSET,
++ framelen - 2 -
++ SEQ_FIELD_SIZE);
++ } else {
++
++#ifdef TS0710DEBUG
++ if (*
++ (start_flag +
++ SLIDE_BP_SEQ_OFFSET)
++ != 0x9F) {
++#endif
++
++ TS0710_LOG
++ ("MUX sequence number %d is not expected %d, discard data!\n",
++ *
++ (start_flag
++ +
++ SLIDE_BP_SEQ_OFFSET),
++ expect_seq);
++
++#ifdef TS0710SEQ2
++ send_ack
++ (&ts0710_connection,
++ expect_seq,
++ *
++ (start_flag
++ +
++ FIRST_BP_SEQ_OFFSET),
++ *
++ (start_flag
++ +
++ SECOND_BP_SEQ_OFFSET));
++#else
++ send_ack
++ (&ts0710_connection,
++ expect_seq);
++#endif
++
++#ifdef TS0710DEBUG
++ } else {
++ *(uih_data_start
++ + uih_len) =
++ 0;
++ TS0710_PRINTK
++ ("MUX bp log: %s\n",
++ uih_data_start);
++ }
++#endif
++
++ }
++ } else { /* crc_error */
++ search = start_flag + 1;
++ start_flag = 0;
++ framelen = -1;
++ continue;
++ } /*End if(!crc_error) */
++
++ /*For BP UART problem End */
++
++/*For BP UART problem
++ TS0710_LOGSTR_FRAME(0, start_flag, framelen);
++ TS0710_DEBUGHEX(start_flag, framelen);
++ ts0710_recv_data(&ts0710_connection, start_flag + 1, framelen - 2);
++*/
++ search = start_flag + framelen;
++ } else {
++ TS0710_LOGSTR_FRAME(0, start_flag,
++ framelen);
++ TS0710_DEBUGHEX(start_flag, framelen);
++ TS0710_PRINTK
++ ("MUX: Lost synchronization!\n");
++ search = start_flag + 1;
++ }
++
++ start_flag = 0;
++ framelen = -1;
++ continue;
++ }
++
++ if (start_flag != &tbuf[0]) {
++ to = tbuf;
++ from = start_flag;
++ count = tbuf_ptr - start_flag;
++ while (count--) {
++ *to++ = *from++;
++ }
++
++ tbuf_ptr -= (start_flag - tbuf);
++ start_flag = tbuf;
++ }
++ break;
++ } /* End Frame Start Flag found */
++ } /* End while(1) */
++
++ clear_bit(RECV_RUNNING, &mux_recv_flags);
++}
++
++static void post_recv_worker(void *private_)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ int tty_idx;
++ struct tty_struct *tty;
++ __u8 post_recv;
++ __u8 flow_control;
++ __u8 dlci;
++ mux_recv_struct *recv_info, *recv_info2, *post_recv_q;
++ int recv_room;
++ mux_recv_packet *recv_packet, *recv_packet2;
++ __u8 j;
++
++ UNUSED_PARAM(private_);
++
++ if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
++ schedule_work(&post_recv_tqueue);
++ return;
++ }
++
++ TS0710_DEBUG("Enter into post_recv_worker");
++
++ post_recv = 0;
++ if (!mux_recv_queue) {
++ goto out;
++ }
++
++ post_recv_q = NULL;
++ recv_info2 = mux_recv_queue;
++ while ((recv_info = recv_info2)) {
++ recv_info2 = recv_info->next;
++
++ if (!(recv_info->total)) {
++ TS0710_PRINTK
++ ("MUX Error: %s: Should not get here, recv_info->total == 0 \n",
++ __FUNCTION__);
++ continue;
++ }
++
++ tty_idx = recv_info->no_tty;
++ dlci = tty2dlci[tty_idx];
++ tty = mux_table[tty_idx];
++ if ((!mux_tty[tty_idx]) || (!tty)) {
++ TS0710_PRINTK
++ ("MUX: No application waiting for, free recv_info! tty_idx:%d\n",
++ tty_idx);
++ mux_recv_info_flags[tty_idx] = 0;
++ free_mux_recv_struct(mux_recv_info[tty_idx]);
++ mux_recv_info[tty_idx] = 0;
++ ts0710_flow_on(dlci, ts0710);
++ continue;
++ }
++
++ TS0710_DEBUG("/dev/mux%d recv_info->total is: %d", tty_idx,
++ recv_info->total);
++
++ if (test_bit(TTY_THROTTLED, &tty->flags)) {
++ add_post_recv_queue(&post_recv_q, recv_info);
++ continue;
++ } else if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
++ post_recv = 1;
++ add_post_recv_queue(&post_recv_q, recv_info);
++ continue;
++ }
++
++ flow_control = 0;
++ recv_packet2 = recv_info->mux_packet;
++ while (recv_info->total) {
++ recv_room = 65535;
++ if (tty->receive_room)
++ recv_room = tty->receive_room;
++
++ if (recv_info->length) {
++ if (recv_room < recv_info->length) {
++ flow_control = 1;
++ break;
++ }
++
++ /* Put queued data into read buffer of tty */
++ TS0710_DEBUG
++ ("Put queued recv data into read buffer of /dev/mux%d",
++ tty_idx);
++ TS0710_DEBUGHEX(recv_info->data,
++ recv_info->length);
++ (tty->ldisc.receive_buf) (tty, recv_info->data,
++ NULL,
++ recv_info->length);
++ recv_info->total -= recv_info->length;
++ recv_info->length = 0;
++ } else { /* recv_info->length == 0 */
++ if ((recv_packet = recv_packet2)) {
++ recv_packet2 = recv_packet->next;
++
++ if (recv_room < recv_packet->length) {
++ flow_control = 1;
++ recv_info->mux_packet =
++ recv_packet;
++ break;
++ }
++
++ /* Put queued data into read buffer of tty */
++ TS0710_DEBUG
++ ("Put queued recv data into read buffer of /dev/mux%d",
++ tty_idx);
++ TS0710_DEBUGHEX(recv_packet->data,
++ recv_packet->length);
++ (tty->ldisc.receive_buf) (tty,
++ recv_packet->
++ data, NULL,
++ recv_packet->
++ length);
++ recv_info->total -= recv_packet->length;
++ free_mux_recv_packet(recv_packet);
++ } else {
++ TS0710_PRINTK
++ ("MUX Error: %s: Should not get here, recv_info->total is:%u \n",
++ __FUNCTION__, recv_info->total);
++ }
++ } /* End recv_info->length == 0 */
++ } /* End while( recv_info->total ) */
++
++ if (!(recv_info->total)) {
++ /* Important clear */
++ recv_info->mux_packet = 0;
++
++ if (recv_info->post_unthrottle) {
++ /* Do something for post_unthrottle */
++ ts0710_flow_on(dlci, ts0710);
++ recv_info->post_unthrottle = 0;
++ }
++ } else {
++ add_post_recv_queue(&post_recv_q, recv_info);
++
++ if (flow_control) {
++ /* Do something for flow control */
++ if (recv_info->post_unthrottle) {
++ set_bit(TTY_THROTTLED, &tty->flags);
++ recv_info->post_unthrottle = 0;
++ } else {
++ ts0710_flow_off(tty, dlci, ts0710);
++ }
++ } /* End if( flow_control ) */
++ }
++ } /* End while( (recv_info = recv_info2) ) */
++
++ mux_recv_queue = post_recv_q;
++
++ out:
++ if (post_recv_count_flag) {
++ post_recv_count_flag = 0;
++ for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
++ if (mux_data_count2[j] > 0) {
++ if (add_count(j, mux_data_count2[j]) == 0) {
++ mux_data_count2[j] = 0;
++ } else {
++ post_recv_count_flag = 1;
++ post_recv = 1;
++ }
++ }
++ } /* End for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) */
++ }
++ /* End if( post_recv_count_flag ) */
++ if (post_recv)
++ schedule_work(&post_recv_tqueue);
++ clear_bit(RECV_RUNNING, &mux_recv_flags);
++}
++
++/* mux sender, call from serial.c transmit_chars() */
++void mux_sender(void)
++{
++ mux_send_struct *send_info;
++ int chars;
++ __u8 idx;
++
++ chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
++ if (!chars) {
++ /* chars == 0 */
++ TS0710_LOG("<[]\n");
++ mux_sched_send();
++ return;
++ }
++
++ idx = mux_send_info_idx;
++ if ((idx < NR_MUXS) && (mux_send_info_flags[idx])) {
++ send_info = mux_send_info[idx];
++ if ((send_info)
++ && (send_info->filled)
++ && (send_info->length <=
++ (TS0710MUX_SERIAL_BUF_SIZE - chars))) {
++
++ mux_sched_send();
++ }
++ }
++}
++
++static void send_worker(void *private_)
++{
++ ts0710_con *ts0710 = &ts0710_connection;
++ __u8 j;
++ mux_send_struct *send_info;
++ int chars;
++ struct tty_struct *tty;
++ __u8 dlci;
++
++ UNUSED_PARAM(private_);
++
++ TS0710_DEBUG("Enter into send_worker");
++
++ mux_send_info_idx = NR_MUXS;
++
++ if (ts0710->dlci[0].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped on all channels\n");
++ return;
++ }
++
++ for (j = 0; j < NR_MUXS; j++) {
++
++ if (!(mux_send_info_flags[j])) {
++ continue;
++ }
++
++ send_info = mux_send_info[j];
++ if (!send_info) {
++ continue;
++ }
++
++ if (!(send_info->filled)) {
++ continue;
++ }
++
++ dlci = tty2dlci[j];
++ if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
++ dlci);
++ continue;
++ } else if (ts0710->dlci[dlci].state != CONNECTED) {
++ TS0710_DEBUG("DLCI %d not connected\n", dlci);
++ send_info->filled = 0;
++ continue;
++ }
++
++ chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
++ if (send_info->length <= (TS0710MUX_SERIAL_BUF_SIZE - chars)) {
++ TS0710_DEBUG("Send queued UIH for /dev/mux%d", j);
++ basic_write(ts0710, (__u8 *) send_info->frame,
++ send_info->length);
++ send_info->length = 0;
++ send_info->filled = 0;
++ } else {
++ mux_send_info_idx = j;
++ break;
++ }
++ } /* End for() loop */
++
++ /* Queue UIH data to be transmitted */
++ for (j = 0; j < NR_MUXS; j++) {
++
++ if (!(mux_send_info_flags[j])) {
++ continue;
++ }
++
++ send_info = mux_send_info[j];
++ if (!send_info) {
++ continue;
++ }
++
++ if (send_info->filled) {
++ continue;
++ }
++
++ /* Now queue UIH data to send_info->buf */
++
++ if (!mux_tty[j]) {
++ continue;
++ }
++
++ tty = mux_table[j];
++ if (!tty) {
++ continue;
++ }
++
++ dlci = tty2dlci[j];
++ if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
++ TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
++ dlci);
++ continue;
++ } else if (ts0710->dlci[dlci].state != CONNECTED) {
++ TS0710_DEBUG("DLCI %d not connected\n", dlci);
++ continue;
++ }
++
++ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
++ && tty->ldisc.write_wakeup) {
++ (tty->ldisc.write_wakeup) (tty);
++ }
++ wake_up_interruptible(&tty->write_wait);
++
++#ifdef SERIAL_HAVE_POLL_WAIT
++ wake_up_interruptible(&tty->poll_wait);
++#endif
++
++ if (send_info->filled) {
++ if (j < mux_send_info_idx) {
++ mux_send_info_idx = j;
++ }
++ }
++ } /* End for() loop */
++}
++
++static int get_count(__u8 idx)
++{
++ int ret;
++
++ if (idx > TS0710MUX_COUNT_MAX_IDX) {
++ TS0710_PRINTK("MUX get_count: invalid idx: %d!\n", idx);
++ return -1;
++ }
++
++ down(&mux_data_count_mutex[idx]);
++ ret = mux_data_count[idx];
++ up(&mux_data_count_mutex[idx]);
++
++ return ret;
++}
++
++static int set_count(__u8 idx, int count)
++{
++ if (idx > TS0710MUX_COUNT_MAX_IDX) {
++ TS0710_PRINTK("MUX set_count: invalid idx: %d!\n", idx);
++ return -1;
++ }
++ if (count < 0) {
++ TS0710_PRINTK("MUX set_count: invalid count: %d!\n", count);
++ return -1;
++ }
++
++ down(&mux_data_count_mutex[idx]);
++ mux_data_count[idx] = count;
++ up(&mux_data_count_mutex[idx]);
++
++ return 0;
++}
++
++static int add_count(__u8 idx, int count)
++{
++ if (idx > TS0710MUX_COUNT_MAX_IDX) {
++ TS0710_PRINTK("MUX add_count: invalid idx: %d!\n", idx);
++ return -1;
++ }
++ if (count <= 0) {
++ TS0710_PRINTK("MUX add_count: invalid count: %d!\n", count);
++ return -1;
++ }
++
++ if (down_trylock(&mux_data_count_mutex[idx]))
++ return -1;
++ mux_data_count[idx] += count;
++ up(&mux_data_count_mutex[idx]);
++
++ return 0;
++}
++
++ssize_t file_proc_read(struct file * file, char *buf, size_t size,
++ loff_t * ppos)
++{
++ gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
++ int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
++
++ UNUSED_PARAM(file);
++ UNUSED_PARAM(size);
++ UNUSED_PARAM(ppos);
++
++ gprsData[0].recvBytes = get_count(TS0710MUX_GPRS1_RECV_COUNT_IDX);
++ gprsData[0].sentBytes = get_count(TS0710MUX_GPRS1_SEND_COUNT_IDX);
++ gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes =
++ get_count(TS0710MUX_GPRS2_RECV_COUNT_IDX);
++ gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes =
++ get_count(TS0710MUX_GPRS2_SEND_COUNT_IDX);
++
++ copy_to_user(buf, gprsData, bufLen);
++
++ return bufLen;
++}
++
++ssize_t file_proc_write(struct file * file, const char *buf, size_t count,
++ loff_t * ppos)
++{
++ gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
++ int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
++
++ UNUSED_PARAM(file);
++ UNUSED_PARAM(count);
++ UNUSED_PARAM(ppos);
++
++ memset(gprsData, 0, bufLen);
++
++ copy_from_user(gprsData, buf, bufLen);
++
++ set_count(TS0710MUX_GPRS1_RECV_COUNT_IDX, gprsData[0].recvBytes);
++ set_count(TS0710MUX_GPRS1_SEND_COUNT_IDX, gprsData[0].sentBytes);
++ set_count(TS0710MUX_GPRS2_RECV_COUNT_IDX,
++ gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes);
++ set_count(TS0710MUX_GPRS2_SEND_COUNT_IDX,
++ gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes);
++
++ return bufLen;
++}
++
++static void gprs_proc_init(void)
++{
++ gprs_proc_file =
++ create_proc_entry("gprsbytes", S_IRUSR | S_IWUSR, NULL);
++ gprs_proc_file->proc_fops = &file_proc_operations;
++}
++
++static void gprs_proc_exit(void)
++{
++ remove_proc_entry("gprsbytes", gprs_proc_file);
++}
++#endif
++
++static int __init mux_init(void)
++{
++ unsigned int j;
++
++ ts0710_init();
++#if 0
++ if (COMM_FOR_MUX_DRIVER == NULL) {
++
++#ifdef USB_FOR_MUX
++ panic("please install IPC-USB first\n");
++#else
++ panic("please install ttyS0 first\n");
++#endif
++
++ }
++
++ for (j = 0; j < NR_MUXS; j++) {
++ mux_send_info_flags[j] = 0;
++ mux_send_info[j] = 0;
++ mux_recv_info_flags[j] = 0;
++ mux_recv_info[j] = 0;
++ }
++ mux_send_info_idx = NR_MUXS;
++ mux_recv_queue = NULL;
++ mux_recv_flags = 0;
++
++ for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
++ mux_data_count[j] = 0;
++ mux_data_count2[j] = 0;
++ init_MUTEX(&mux_data_count_mutex[j]);
++ }
++ post_recv_count_flag = 0;
++
++ INIT_WORK(&send_tqueue, send_worker, NULL);
++ INIT_WORK(&receive_tqueue, receive_worker, NULL);
++ INIT_WORK(&post_recv_tqueue, post_recv_worker, NULL);
++#endif
++
++ memset(&mux_driver, 0, sizeof(struct tty_driver));
++ memset(&mux_tty, 0, sizeof(mux_tty));
++ mux_driver.magic = TTY_DRIVER_MAGIC;
++ mux_driver.driver_name = "ts0710mux";
++ mux_driver.name = "ts0710mux";
++ mux_driver.major = TS0710MUX_MAJOR;
++ mux_driver.minor_start = TS0710MUX_MINOR_START;
++ mux_driver.num = NR_MUXS;
++ mux_driver.type = TTY_DRIVER_TYPE_SERIAL;
++ mux_driver.subtype = SERIAL_TYPE_NORMAL;
++ mux_driver.init_termios = tty_std_termios;
++ mux_driver.init_termios.c_iflag = 0;
++ mux_driver.init_termios.c_oflag = 0;
++ mux_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
++ mux_driver.init_termios.c_lflag = 0;
++ mux_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
++
++ mux_driver.ttys = mux_table;
++ mux_driver.termios = mux_termios;
++ mux_driver.termios_locked = mux_termios_locked;
++// mux_driver.driver_state = mux_state;
++ mux_driver.other = NULL;
++
++ mux_driver.open = mux_open;
++ mux_driver.close = mux_close;
++ mux_driver.write = mux_write;
++ mux_driver.write_room = mux_write_room;
++ mux_driver.flush_buffer = mux_flush_buffer;
++ mux_driver.chars_in_buffer = mux_chars_in_buffer;
++ mux_driver.throttle = mux_throttle;
++ mux_driver.unthrottle = mux_unthrottle;
++ mux_driver.ioctl = mux_ioctl;
++ mux_driver.owner = THIS_MODULE;
++
++ if (tty_register_driver(&mux_driver))
++ panic("Couldn't register mux driver");
++
++#if 0
++ COMM_MUX_DISPATCHER = mux_dispatcher;
++ COMM_MUX_SENDER = mux_sender;
++
++ gprs_proc_init();
++#endif
++
++ return 0;
++}
++
++static void __exit mux_exit(void)
++{
++ int j;
++
++#if 0
++ COMM_MUX_DISPATCHER = NULL;
++ COMM_MUX_SENDER = NULL;
++
++ gprs_proc_exit();
++
++ mux_send_info_idx = NR_MUXS;
++ mux_recv_queue = NULL;
++ for (j = 0; j < NR_MUXS; j++) {
++ if ((mux_send_info_flags[j]) && (mux_send_info[j])) {
++ kfree(mux_send_info[j]);
++ }
++ mux_send_info_flags[j] = 0;
++ mux_send_info[j] = 0;
++
++ if ((mux_recv_info_flags[j]) && (mux_recv_info[j])) {
++ free_mux_recv_struct(mux_recv_info[j]);
++ }
++ mux_recv_info_flags[j] = 0;
++ mux_recv_info[j] = 0;
++ }
++#endif
++
++ if (tty_unregister_driver(&mux_driver))
++ panic("Couldn't unregister mux driver");
++}
++
++module_init(mux_init);
++module_exit(mux_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at openezx.org>");
++MODULE_DESCRIPTION("GSM TS 07.10 Multiplexer");
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux.h 2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,103 @@
++/*
++ * mux_macro.h
++ *
++ * Copyright (C) 2002 2005 Motorola
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ *
++ * 11/18/2002 (Motorola) - Initial version
++ *
++ */
++
++/*
++* This header file should be included by both MUX and other applications
++* which access MUX device files. It gives the additional macro definitions
++* shared between MUX and applications.
++*/
++
++/* MUX DLCI(Data Link Connection Identifier) Configuration */
++/*
++* DLCI Service
++* 0 Control Channel
++* 1 Voice Call & Network-related
++* 2 SMS MO
++* 3 SMS MT
++* 4 Phonebook & related
++* 5 MISC
++* 6 CSD/FAX
++* 7 GPRS1
++* 8 GPRS2
++* 9 Logger CMD
++* 10 Logger Data
++* 11 Test CMD
++* 12 AGPS
++* 13 Net Monitor
++*/
++
++/* Mapping between DLCI and MUX device files */
++/*
++* File Name Minor DLCI AT Command/Data
++* /dev/mux0 0 1 AT Command
++* /dev/mux1 1 2 AT Command
++* /dev/mux2 2 3 AT Command
++* /dev/mux3 3 4 AT Command
++* /dev/mux4 4 5 AT Command
++* /dev/mux5 5 6 AT Command
++* /dev/mux6 6 7 AT Command
++* /dev/mux7 7 8 AT Command
++* /dev/mux8 8 6 Data
++* /dev/mux9 9 7 Data
++* /dev/mux10 10 8 Data
++* /dev/mux11 11 9 Data
++* /dev/mux12 12 10 Data
++* /dev/mux13 13 11 Data
++* /dev/mux14 14 12 Data
++* /dev/mux15 15 13 Data
++*/
++
++#define MUX_CMD_FILE_VOICE_CALL "/dev/mux0"
++#define MUX_CMD_FILE_SMS_MO "/dev/mux1"
++#define MUX_CMD_FILE_SMS_MT "/dev/mux2"
++#define MUX_CMD_FILE_PHONEBOOK "/dev/mux3"
++#define MUX_CMD_FILE_MISC "/dev/mux4"
++#define MUX_CMD_FILE_CSD "/dev/mux5"
++#define MUX_CMD_FILE_GPRS1 "/dev/mux6"
++#define MUX_CMD_FILE_GPRS2 "/dev/mux7"
++
++#define MUX_DATA_FILE_CSD "/dev/mux8"
++#define MUX_DATA_FILE_GPRS1 "/dev/mux9"
++#define MUX_DATA_FILE_GPRS2 "/dev/mux10"
++#define MUX_DATA_FILE_LOGGER_CMD "/dev/mux11"
++#define MUX_DATA_FILE_LOGGER_DATA "/dev/mux12"
++#define MUX_DATA_FILE_TEST_CMD "/dev/mux13"
++#define MUX_DATA_FILE_AGPS "/dev/mux14"
++#define MUX_DATA_FILE_NET_MONITOR "/dev/mux15"
++
++#define NUM_MUX_CMD_FILES 8
++#define NUM_MUX_DATA_FILES 8
++#define NUM_MUX_FILES ( NUM_MUX_CMD_FILES + NUM_MUX_DATA_FILES )
++
++/* Special ioctl() upon a MUX device file for hanging up a call */
++#define TS0710MUX_IO_MSC_HANGUP 0x54F0
++
++/* Special ioctl() upon a MUX device file for MUX loopback test */
++#define TS0710MUX_IO_TEST_CMD 0x54F1
++
++/* Special Error code might be return from write() to a MUX device file */
++#define EDISCONNECTED 900 /* Logical data link is disconnected */
++
++/* Special Error code might be return from open() to a MUX device file */
++#define EREJECTED 901 /* Logical data link connection request is rejected */
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.c 2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,868 @@
++/*
++ * linux/drivers/usb/ipcusb.c
++ *
++ * Implementation of a ipc driver based Intel's Bulverde USB Host
++ * Controller.
++ *
++ * Copyright (C) 2003-2005 Motorola
++ * Copyright (C) 2006 Harald Welte <laforge at openezx.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * 2003-Nov-03 - (Motorola) created
++ * 2004-Feb-20 - (Motorola) Add Power Manager codes
++ * 2004-Apr-14 - (Motorola) Update Suspend/Resume codes
++ * 2004-May-10 - (Motorola) Add unlink_urbs codes and do some updates of send
++ * out urb sequence
++ * 2006-Jun-22 - (Harald Welte) port to Linux 2.6.x
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/list.h>
++#include <linux/errno.h>
++#include <asm/uaccess.h>
++#include <asm/hardware.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch-pxa/pxa-regs.h>
++#include <asm/arch-pxa/ezx.h>
++#include <linux/slab.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/tty.h>
++#include <linux/tty_driver.h>
++#include <linux/tty_flip.h>
++#include <linux/circ_buf.h>
++#include <linux/usb.h>
++
++#include "ts0710_mux_usb.h"
++
++/*Macro defined for this driver*/
++#define DRIVER_VERSION "1.0alpha1"
++#define DRIVER_AUTHOR "Motorola / Harald Welte <laforge at openezx.org>"
++#define DRIVER_DESC "USB IPC Driver (TS07.10 lowlevel)"
++#define MOTO_IPC_VID 0x22b8
++#define MOTO_IPC_PID 0x3006
++#define IBUF_SIZE 32 /*urb size*/
++#define IPC_USB_XMIT_SIZE 1024
++#define IPC_URB_SIZE 32
++#define IPC_USB_WRITE_INIT 0
++#define IPC_USB_WRITE_XMIT 1
++#define IPC_USB_PROBE_READY 3
++#define IPC_USB_PROBE_NOT_READY 4
++#define DBG_MAX_BUF_SIZE 1024
++#define ICL_EVENT_INTERVAL (HZ)
++#undef BVD_DEBUG
++
++#define IS_EP_BULK(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
++#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
++#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
++/*End defined macro*/
++
++/*global values defined*/
++static struct usb_driver usb_ipc_driver;
++static struct timer_list ipcusb_timer;
++static struct timer_list suspend_timer;
++static struct timer_list wakeup_timer;
++static struct tty_struct ipcusb_tty; /* the coresponding tty struct, we just use flip buffer here. */
++static struct tty_driver ipcusb_tty_driver; /* the coresponding tty driver, we just use write and chars in buff here*/
++struct tty_driver *usb_for_mux_driver = NULL;
++struct tty_struct *usb_for_mux_tty = NULL;
++void (*usb_mux_dispatcher)(struct tty_struct *tty) = NULL;
++void (*usb_mux_sender)(void) = NULL;
++void (*ipcusb_ap_to_bp)(unsigned char*, int) = NULL;
++void (*ipcusb_bp_to_ap)(unsigned char*, int) = NULL;
++EXPORT_SYMBOL(usb_for_mux_driver);
++EXPORT_SYMBOL(usb_for_mux_tty);
++EXPORT_SYMBOL(usb_mux_dispatcher);
++EXPORT_SYMBOL(usb_mux_sender);
++EXPORT_SYMBOL(ipcusb_ap_to_bp);
++EXPORT_SYMBOL(ipcusb_bp_to_ap);
++static int sumbit_times = 0;
++static int callback_times = 0;
++//static unsigned long last_jiff = 0;
++extern int usbh_finished_resume;
++/*end global values defined*/
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
++
++#ifdef BVD_DEBUG
++#define bvd_dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
++#else
++#define bvd_dbg(format, arg...) do {} while (0)
++#endif
++
++/* USB device context */
++typedef struct {
++ struct list_head list;
++ int size;
++ char *body;
++} buf_list_t;
++
++struct ipc_usb_data {
++ u_int8_t write_finished_flag;
++ u_int8_t write_flag,
++ ipc_flag,
++ suspend_flag;
++ struct usb_device *ipc_dev;
++ struct urb readurb_mux,
++ writeurb_mux,
++ writeurb_dsplog;
++ char *obuf, *ibuf;
++ int writesize; /* max packet size for the
++ output bulk endpoint *
++ transfer buffers */
++
++ struct circ_buf xmit; /* write cric bufffer */
++ struct list_head in_buf_list;
++ char bulk_in_ep_mux,
++ bulk_out_ep_mux,
++ bulk_in_ep_dsplog;
++ unsigned int ifnum;
++
++ struct tasklet_struct bh,
++ bh_bp;
++
++ spinlock_t lock;
++};
++
++struct ipc_usb_data *bvd_ipc;
++
++#ifdef BVD_DEBUG
++static void bvd_dbg_hex(__u8 *buf, int len)
++{
++ static unsigned char tbuf[DBG_MAX_BUF_SIZE];
++ int i, c;
++
++ if (len <= 0)
++ return;
++
++ c = 0;
++ for (i=0; (i < len) && (c < (DBG_MAX_BUF_SIZE - 3)); i++) {
++ sprintf(&tbuf[c], "%02x ",buf[i]);
++ c += 3;
++ }
++ tbuf[c] = 0;
++
++ printk("%s: %s\n", __FUNCTION__, tbuf);
++}
++#else
++#define bvd_dbg_hex(buf, len)
++#endif
++
++static int unlink_urbs(struct urb *urb)
++{
++ unsigned long flags;
++ int retval;
++
++ spin_lock_irqsave(&bvd_ipc->lock, flags);
++
++ retval = usb_unlink_urb(urb);
++ if (retval != -EINPROGRESS && retval != 0)
++ printk("unlink urb err, %d", retval);
++
++ spin_unlock_irqrestore(&bvd_ipc->lock, flags);
++ return retval;
++}
++
++static void append_to_inbuf_list(struct urb *urb)
++{
++ buf_list_t *inbuf;
++ int count = urb->actual_length;
++
++ inbuf = kmalloc(sizeof(buf_list_t), GFP_KERNEL);
++ if (!inbuf) {
++ printk("append_to_inbuf_list: (%d) out of memory!\n",
++ sizeof(buf_list_t));
++ return;
++ }
++
++ inbuf->size = count;
++ inbuf->body = kmalloc(sizeof(char)*count, GFP_KERNEL);
++ if (!inbuf->body) {
++ kfree(inbuf);
++ printk("append_to_inbuf_list: (%d) out of memory!\n",
++ sizeof(char)*count);
++ return;
++ }
++ memcpy(inbuf->body, (unsigned char*)urb->transfer_buffer, count);
++ list_add_tail(&inbuf->list, &bvd_ipc->in_buf_list);
++}
++
++static void ipcusb_timeout(unsigned long data)
++{
++ struct tty_struct *tty = &ipcusb_tty;
++ struct urb *urb = (struct urb *)data;
++
++ bvd_dbg("ipcusb_timeout***");
++
++ while (!(list_empty(&bvd_ipc->in_buf_list))) {
++ int count;
++ buf_list_t *inbuf;
++ struct list_head *ptr = NULL;
++
++ ptr = bvd_ipc->in_buf_list.next;
++ inbuf = list_entry (ptr, buf_list_t, list);
++ count = inbuf->size;
++ if (tty_insert_flip_string(tty, inbuf->body, count) >= count) {
++ list_del(ptr);
++ kfree(inbuf->body);
++ inbuf->body = NULL;
++ kfree(inbuf);
++ } else {
++ bvd_dbg("ipcusb_timeout: bvd_ipc->in_buf_list empty!");
++ break;
++ }
++ }
++
++ if (usb_mux_dispatcher)
++ usb_mux_dispatcher(tty); /**call Liu changhui's func.**/
++
++ if (list_empty(&bvd_ipc->in_buf_list)) {
++ urb->actual_length = 0;
++ urb->dev = bvd_ipc->ipc_dev;
++ if (usb_submit_urb(urb, GFP_ATOMIC))
++ bvd_dbg("ipcusb_timeout: failed resubmitting read urb");
++ bvd_dbg("ipcusb_timeout: resubmited read urb");
++ } else {
++ ipcusb_timer.data = (unsigned long)urb;
++ mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++ }
++}
++
++static void usb_ipc_read_bulk(struct urb *urb, struct pt_regs *regs)
++{
++ buf_list_t *inbuf;
++ int count = urb->actual_length;
++ struct tty_struct *tty = &ipcusb_tty;
++
++ bvd_dbg("usb_ipc_read_bulk: begining!");
++ if (urb->status)
++ printk("nonzero read bulk status received: %d\n", urb->status);
++
++ bvd_dbg("usb_ipc_read_bulk: urb->actual_length=%d", urb->actual_length);
++ bvd_dbg("usb_ipc_read_bulk: urb->transfer_buffer:");
++
++ bvd_dbg_hex((unsigned char*)urb->transfer_buffer, urb->actual_length);
++
++ if (count > 0 && ((*ipcusb_bp_to_ap) != NULL))
++ (*ipcusb_bp_to_ap)(urb->transfer_buffer, urb->actual_length);
++
++ if (!(list_empty(&bvd_ipc->in_buf_list))) {
++ int need_mux = 0;
++
++ bvd_dbg("usb_ipc_read_bulk: some urbs in_buf_list");
++ if (count > 0) {
++ bvd_ipc->suspend_flag = 1;
++ append_to_inbuf_list(urb); /* append the current received urb */
++#if 0
++ if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
++ {
++ last_jiff = jiffies;
++ queue_apm_event(KRNL_ICL, NULL);
++ }
++#endif
++ }
++
++ while (!(list_empty(&bvd_ipc->in_buf_list))) {
++ struct list_head* ptr = NULL;
++ ptr = bvd_ipc->in_buf_list.next;
++ inbuf = list_entry(ptr, buf_list_t, list);
++ count = inbuf->size;
++ need_mux = 1;
++
++ tty_insert_flip_string(tty, inbuf->body, count);
++
++ list_del(ptr);
++ kfree(inbuf->body);
++ inbuf->body = NULL;
++ kfree(inbuf);
++ }
++
++ if (usb_mux_dispatcher && need_mux)
++ usb_mux_dispatcher(tty); /* call Liu changhui's func. */
++
++ if (list_empty(&bvd_ipc->in_buf_list)) {
++ urb->actual_length = 0;
++ urb->dev = bvd_ipc->ipc_dev;
++ if (usb_submit_urb(urb, GFP_ATOMIC))
++ bvd_dbg("usb_ipc_read_bulk: "
++ "failed resubmitting read urb");
++ bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
++ } else {
++ ipcusb_timer.data = (unsigned long)urb;
++ mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++ }
++ } else if (count > 0) {
++ bvd_dbg("usb_ipc_read_bulk: no urbs in_buf_list");
++ bvd_ipc->suspend_flag = 1;
++
++ if (tty_insert_flip_string(tty, urb->transfer_buffer,
++ count) < count) {
++ bvd_ipc->suspend_flag = 1;
++ append_to_inbuf_list(urb);
++ ipcusb_timer.data = (unsigned long)urb;
++ mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
++#if 0
++ if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
++ {
++ last_jiff = jiffies;
++ queue_apm_event(KRNL_ICL, NULL);
++ }
++#endif
++ }
++
++ if (usb_mux_dispatcher)
++ usb_mux_dispatcher(tty); /* call Liu changhui's func. */
++
++ urb->actual_length = 0;
++ urb->dev = bvd_ipc->ipc_dev;
++ if (usb_submit_urb(urb, GFP_ATOMIC))
++ bvd_dbg("failed resubmitting read urb");
++#if 0
++ if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
++ {
++ last_jiff = jiffies;
++ queue_apm_event(KRNL_ICL, NULL);
++ }
++#endif
++ bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
++ }
++
++ bvd_dbg("usb_ipc_read_bulk: completed!!!");
++}
++
++static void usb_ipc_write_bulk(struct urb *urb, struct pt_regs *regs)
++{
++ callback_times++;
++ bvd_ipc->write_finished_flag = 1;
++
++ bvd_dbg("usb_ipc_write_bulk: begining!");
++ //printk("%s: write_finished_flag=%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
++
++ if (urb->status)
++ printk("nonzero write bulk status received: %d\n", urb->status);
++
++ if (usb_mux_sender)
++ usb_mux_sender(); /**call Liu changhui's func**/
++
++ //printk("usb_ipc_write_bulk: mark ipcusb_softint!\n");
++ tasklet_schedule(&bvd_ipc->bh);
++
++ bvd_dbg("usb_ipc_write_bulk: finished!");
++}
++
++static void wakeup_timeout(unsigned long data)
++{
++ GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++ bvd_dbg("wakup_timeout: send GPIO_MCU_INT_SW signal!");
++}
++
++static void suspend_timeout(unsigned long data)
++{
++ if (bvd_ipc->suspend_flag == 1) {
++ bvd_ipc->suspend_flag = 0;
++ mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
++ bvd_dbg("suspend_timeout: add the suspend timer again");
++ } else {
++ unlink_urbs(&bvd_ipc->readurb_mux);
++ UHCRHPS3 = 0x4;
++ mdelay(40);
++ bvd_dbg("suspend_timeout: send SUSPEND signal! UHCRHPS3=0x%x",
++ UHCRHPS3);
++ }
++}
++
++static void ipcusb_xmit_data(void)
++{
++ int c, count = IPC_URB_SIZE;
++ int result = 0;
++ int buf_flag = 0;
++ int buf_num = 0;
++
++ //printk("%s: sumbit_times=%d, callback_times=%d\n", __FUNCTION__, sumbit_times, callback_times);
++ if (bvd_ipc->write_finished_flag == 0)
++ return;
++
++ while (1) {
++ c = CIRC_CNT_TO_END(bvd_ipc->xmit.head, bvd_ipc->xmit.tail,
++ IPC_USB_XMIT_SIZE);
++ if (count < c)
++ c = count;
++ if (c <= 0)
++ break;
++
++ memcpy(bvd_ipc->obuf+buf_num,
++ bvd_ipc->xmit.buf + bvd_ipc->xmit.tail, c);
++ buf_flag = 1;
++ bvd_ipc->xmit.tail = ((bvd_ipc->xmit.tail + c)
++ & (IPC_USB_XMIT_SIZE-1));
++ count -= c;
++ buf_num += c;
++ }
++
++ if (buf_num == 0) {
++ bvd_dbg("ipcusb_xmit_data: buf_num=%d, add suspend_timer",
++ buf_num);
++ bvd_ipc->suspend_flag = 0;
++ mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
++ }
++
++ bvd_dbg("ipcusb_xmit_data: buf_num=%d", buf_num);
++ bvd_dbg("ipcusb_xmit_data: bvd_ipc->obuf: ");
++
++ bvd_dbg_hex((bvd_ipc->obuf)-buf_num, buf_num);
++
++ if (buf_flag) {
++ bvd_ipc->writeurb_mux.transfer_buffer_length = buf_num;
++ bvd_dbg("ipcusb_xmit_data: copy data to write urb finished! ");
++
++ if ((UHCRHPS3 & 0x4) == 0x4) {
++ static int ret;
++ int time = 0;
++
++ /* if BP sleep, wake up BP first */
++ pxa_gpio_mode(GPIO_IN | 41);
++ if (GPIO_is_high(41)) {
++ if (GPIO_is_high(GPIO_MCU_INT_SW))
++ GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++ else
++ GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++
++ time = jiffies;
++ while (GPIO_is_high(41) && (jiffies < (time+HZ)));
++
++ if (GPIO_is_high(41)) {
++ printk("%s: Wakeup BP timeout! BP state is %d\n",
++ __FUNCTION__, GPIO_is_high(41));
++ }
++ if (GPIO_is_high(GPIO_MCU_INT_SW))
++ GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++ else
++ GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
++ }
++
++ /* Resume BP */
++ UHCRHPS3 = 0x8;
++ mdelay(40);
++ bvd_dbg("ipcusb_xmit_data: Send RESUME signal! UHCRHPS3=0x%x",
++ UHCRHPS3);
++ /*send IN token*/
++ bvd_ipc->readurb_mux.actual_length = 0;
++ bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++ if (ret = usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
++ printk("ipcusb_xmit_data: usb_submit_urb(read mux bulk)"
++ "failed! status=%d\n", ret);
++ bvd_dbg("ipcusb_xmit_data: Send a IN token successfully!");
++ }
++
++ sumbit_times++;
++ bvd_ipc->write_finished_flag = 0;
++ //printk("%s: clear write_finished_flag:%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
++ bvd_ipc->writeurb_mux.dev = bvd_ipc->ipc_dev;
++ if (result = usb_submit_urb(&bvd_ipc->writeurb_mux, GFP_ATOMIC))
++ warn("ipcusb_xmit_data: funky result! result=%d\n", result);
++
++ bvd_dbg("ipcusb_xmit_data: usb_submit_urb finished! result:%d", result);
++
++ }
++}
++
++static void usbipc_bh_func(unsigned long param)
++{
++ ipcusb_xmit_data();
++}
++
++extern void get_halted_bit(void);
++
++static void usbipc_bh_bp_func(unsigned long param)
++{
++ if ((UHCRHPS3 & 0x4) == 0x4) {
++ UHCRHPS3 = 0x8;
++ mdelay(40);
++ bvd_dbg("ipcusb_softint_send_readurb: Send RESUME signal! "
++ "UHCRHPS3=0x%x", UHCRHPS3);
++ }
++ if (bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) {
++ get_halted_bit();
++
++ /*send a IN token*/
++ bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++ if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC)) {
++ bvd_dbg("ipcusb_softint_send_readurb: "
++ "usb_submit_urb(read mux bulk) failed!");
++ }
++ bvd_dbg("ipcusb_softint_send_readurb: Send a IN token successfully!");
++ bvd_ipc->suspend_flag = 0;
++ bvd_dbg("ipcusb_softint_send_readurb: add suspend_timer");
++ mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
++ }
++}
++
++static int usb_ipc_write(struct tty_struct *tty,
++ const unsigned char *buf, int count)
++{
++ int c, ret = 0;
++
++ bvd_dbg("usb_ipc_write: count=%d, buf: ", count);
++ bvd_dbg_hex(buf, count);
++
++ if (count <= 0)
++ return 0;
++
++ if (*ipcusb_ap_to_bp != NULL)
++ (*ipcusb_ap_to_bp)(buf, count);
++
++ bvd_ipc->suspend_flag = 1;
++
++ if ((bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) &&
++ (bvd_ipc->xmit.head == bvd_ipc->xmit.tail)) {
++ bvd_dbg("usb_ipc_write: set write_flag");
++ bvd_ipc->write_flag = IPC_USB_WRITE_XMIT;
++ }
++
++ while (1) {
++ c = CIRC_SPACE_TO_END(bvd_ipc->xmit.head,
++ bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
++ if (count < c)
++ c = count;
++ if (c <= 0)
++ break;
++
++ memcpy(bvd_ipc->xmit.buf + bvd_ipc->xmit.head, buf, c);
++ bvd_ipc->xmit.head = ((bvd_ipc->xmit.head + c)
++ & (IPC_USB_XMIT_SIZE-1));
++ buf += c;
++ count -= c;
++ ret += c;
++ }
++ bvd_dbg("usb_ipc_write: ret=%d, bvd_ipc->xmit.buf: ", ret);
++
++ bvd_dbg_hex(bvd_ipc->xmit.buf, ret);
++
++ if (bvd_ipc->write_flag == IPC_USB_WRITE_XMIT) {
++ bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
++ bvd_dbg("usb_ipc_write: mark ipcusb_softint");
++ tasklet_schedule(&bvd_ipc->bh);
++ }
++
++ bvd_dbg("usb_ipc_write: ret=%d\n", ret);
++ return ret;
++}
++
++static int usb_ipc_chars_in_buffer(struct tty_struct *tty)
++{
++ return CIRC_CNT(bvd_ipc->xmit.head, bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
++}
++
++void usb_send_readurb(void)
++{
++ //printk("usb_send_readurb: begining!UHCRHPS3=0x%x, usbh_finished_resume=%d\n", UHCRHPS3, usbh_finished_resume);
++
++ if (usbh_finished_resume == 0)
++ return;
++
++ tasklet_schedule(&bvd_ipc->bh_bp);
++}
++
++static int usb_ipc_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *usbdev = interface_to_usbdev(intf);
++ struct usb_config_descriptor *ipccfg;
++ struct usb_interface_descriptor *interface;
++ struct usb_endpoint_descriptor *endpoint;
++ int ep_cnt, readsize, writesize;
++ char have_bulk_in_mux, have_bulk_out_mux;
++
++ bvd_dbg("usb_ipc_probe: vendor id 0x%x, device id 0x%x",
++ usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
++
++ if ((usbdev->descriptor.idVendor != MOTO_IPC_VID) ||
++ (usbdev->descriptor.idProduct != MOTO_IPC_PID))
++ return -ENODEV;
++
++ /* a2590c : dsplog interface is not supported by this driver */
++ if (intf->minor == 2) /* dsplog interface number is 2 */
++ return -1;
++
++ bvd_dbg("usb_ipc_probe: USB dev address:%p", usbdev);
++ bvd_dbg("usb_ipc_probe: ifnum:%u", intf->minor);
++
++ ipccfg = &usbdev->actconfig->desc;
++ bvd_dbg("usb_ipc_prob: config%d", ipccfg->bConfigurationValue);
++ bvd_dbg("usb_ipc_prob: bNumInterfaces = %d", ipccfg->bNumInterfaces);
++
++ /* After this point we can be a little noisy about what we are trying
++ * to configure, hehe. */
++ if (usbdev->descriptor.bNumConfigurations != 1) {
++ info("usb_ipc_probe: Only one device configuration "
++ "is supported.");
++ return -1;
++ }
++
++ if (usbdev->config[0].desc.bNumInterfaces != 3) {
++ info("usb_ipc_probe: Only three device interfaces are "
++ "supported.");
++ return -1;
++ }
++
++ interface = &intf->cur_altsetting->desc;
++ endpoint = &intf->cur_altsetting->endpoint[0].desc;
++ /* Start checking for two bulk endpoints or ... FIXME: This is a future
++ * enhancement...*/
++ bvd_dbg("usb_ipc_probe: Number of Endpoints:%d",
++ (int) interface->bNumEndpoints);
++ if (interface->bNumEndpoints != 2) {
++ info("usb_ipc_probe: Only two endpoints supported.");
++ return -1;
++ }
++
++ ep_cnt = have_bulk_in_mux = have_bulk_out_mux = 0;
++
++ bvd_dbg("usb_ipc_probe: endpoint[0] is:%x",
++ (&endpoint[0])->bEndpointAddress);
++ bvd_dbg("usb_ipc_probe: endpoint[1] is:%x ",
++ (&endpoint[1])->bEndpointAddress);
++
++ while (ep_cnt < interface->bNumEndpoints) {
++
++ if (!have_bulk_in_mux && IS_EP_BULK_IN(endpoint[ep_cnt])) {
++ bvd_dbg("usb_ipc_probe: bEndpointAddress(IN) is:%x ",
++ (&endpoint[ep_cnt])->bEndpointAddress);
++ have_bulk_in_mux =
++ (&endpoint[ep_cnt])->bEndpointAddress;
++ readsize = (&endpoint[ep_cnt])->wMaxPacketSize;
++ bvd_dbg("usb_ipc_probe: readsize=%d", readsize);
++ ep_cnt++;
++ continue;
++ }
++
++ if (!have_bulk_out_mux && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
++ bvd_dbg("usb_ipc_probe: bEndpointAddress(OUT) is:%x ",
++ (&endpoint[ep_cnt])->bEndpointAddress);
++ have_bulk_out_mux =
++ (&endpoint[ep_cnt])->bEndpointAddress;
++ writesize = (&endpoint[ep_cnt])->wMaxPacketSize;
++ bvd_dbg("usb_ipc_probe: writesize=%d", writesize);
++ ep_cnt++;
++ continue;
++ }
++
++ info("usb_ipc_probe: Undetected endpoint ^_^ ");
++ /* Shouldn't ever get here unless we have something weird */
++ return -1;
++ }
++
++ /* Perform a quick check to make sure that everything worked as it
++ * should have. */
++
++ switch (interface->bNumEndpoints) {
++ case 2:
++ if (!have_bulk_in_mux || !have_bulk_out_mux) {
++ info("usb_ipc_probe: Two bulk endpoints required.");
++ return -1;
++ }
++ break;
++ default:
++ info("usb_ipc_probe: Endpoint determination failed ^_^ ");
++ return -1;
++ }
++
++ /* Ok, now initialize all the relevant values */
++ if (!(bvd_ipc->obuf = (char *)kmalloc(writesize, GFP_KERNEL))) {
++ err("usb_ipc_probe: Not enough memory for the output buffer.");
++ kfree(bvd_ipc);
++ return -1;
++ }
++ bvd_dbg("usb_ipc_probe: obuf address:%p", bvd_ipc->obuf);
++
++ if (!(bvd_ipc->ibuf = (char *)kmalloc(readsize, GFP_KERNEL))) {
++ err("usb_ipc_probe: Not enough memory for the input buffer.");
++ kfree(bvd_ipc->obuf);
++ kfree(bvd_ipc);
++ return -1;
++ }
++ bvd_dbg("usb_ipc_probe: ibuf address:%p", bvd_ipc->ibuf);
++
++ bvd_ipc->ipc_flag = IPC_USB_PROBE_READY;
++ bvd_ipc->write_finished_flag = 1;
++ bvd_ipc->suspend_flag = 1;
++ bvd_ipc->bulk_in_ep_mux= have_bulk_in_mux;
++ bvd_ipc->bulk_out_ep_mux= have_bulk_out_mux;
++ bvd_ipc->ipc_dev = usbdev;
++ bvd_ipc->writesize = writesize;
++ INIT_LIST_HEAD (&bvd_ipc->in_buf_list);
++
++ bvd_ipc->bh.func = usbipc_bh_func;
++ bvd_ipc->bh.data = (unsigned long) bvd_ipc;
++
++ bvd_ipc->bh_bp.func = usbipc_bh_bp_func;
++ bvd_ipc->bh_bp.data = (unsigned long) bvd_ipc;
++
++ /*Build a write urb*/
++ usb_fill_bulk_urb(&bvd_ipc->writeurb_mux, usbdev,
++ usb_sndbulkpipe(bvd_ipc->ipc_dev,
++ bvd_ipc->bulk_out_ep_mux),
++ bvd_ipc->obuf, writesize, usb_ipc_write_bulk,
++ bvd_ipc);
++ //bvd_ipc->writeurb_mux.transfer_flags |= USB_ASYNC_UNLINK;
++
++ /*Build a read urb and send a IN token first time*/
++ usb_fill_bulk_urb(&bvd_ipc->readurb_mux, usbdev,
++ usb_rcvbulkpipe(usbdev, bvd_ipc->bulk_in_ep_mux),
++ bvd_ipc->ibuf, readsize, usb_ipc_read_bulk, bvd_ipc);
++ //bvd_ipc->readurb_mux.transfer_flags |= USB_ASYNC_UNLINK;
++
++ usb_driver_claim_interface(&usb_ipc_driver, intf, bvd_ipc);
++ //usb_driver_claim_interface(&usb_ipc_driver, &ipccfg->interface[1], bvd_ipc);
++
++ // a2590c: dsplog is not supported by this driver
++ // usb_driver_claim_interface(&usb_ipc_driver,
++ // &ipccfg->interface[2], bvd_ipc);
++ /*send a IN token first time*/
++ bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
++ if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
++ printk("usb_ipc_prob: usb_submit_urb(read mux bulk) failed!\n");
++
++ bvd_dbg("usb_ipc_prob: Send a IN token successfully!");
++
++ if (bvd_ipc->xmit.head != bvd_ipc->xmit.tail) {
++ printk("usb_ipc_probe: mark ipcusb_softint!\n");
++ tasklet_schedule(&bvd_ipc->bh);
++ }
++
++ printk("usb_ipc_probe: completed probe!");
++ usb_set_intfdata(intf, &bvd_ipc);
++ return 0;
++}
++
++static void usb_ipc_disconnect(struct usb_interface *intf)
++{
++ //struct usb_device *usbdev = interface_to_usbdev(intf);
++ struct ipc_usb_data *bvd_ipc_disconnect = usb_get_intfdata(intf);
++
++ printk("usb_ipc_disconnect:*** \n");
++
++ if ((UHCRHPS3 & 0x4) == 0)
++ usb_unlink_urb(&bvd_ipc_disconnect->readurb_mux);
++
++ usb_unlink_urb(&bvd_ipc_disconnect->writeurb_mux);
++
++ bvd_ipc_disconnect->ipc_flag = IPC_USB_PROBE_NOT_READY;
++ kfree(bvd_ipc_disconnect->ibuf);
++ kfree(bvd_ipc_disconnect->obuf);
++
++ usb_driver_release_interface(&usb_ipc_driver,
++ bvd_ipc_disconnect->ipc_dev->actconfig->interface[0]);
++ usb_driver_release_interface(&usb_ipc_driver,
++ bvd_ipc_disconnect->ipc_dev->actconfig->interface[1]);
++
++ //a2590c: dsplog interface is not supported by this driver
++ //usb_driver_release_interface(&usb_ipc_driver, &bvd_ipc_disconnect->ipc_dev->actconfig->interface[2]);
++
++ bvd_ipc_disconnect->ipc_dev = NULL;
++
++ usb_set_intfdata(intf, NULL);
++
++ printk("usb_ipc_disconnect completed!\n");
++}
++
++static struct usb_device_id usb_ipc_id_table[] = {
++ { USB_DEVICE(MOTO_IPC_VID, MOTO_IPC_PID) },
++ { } /* Terminating entry */
++};
++
++static struct usb_driver usb_ipc_driver = {
++ .name = "usb ipc",
++ .probe = usb_ipc_probe,
++ .disconnect = usb_ipc_disconnect,
++ .id_table = usb_ipc_id_table,
++};
++
++static int __init usb_ipc_init(void)
++{
++ int result;
++
++ bvd_dbg("init usb_ipc");
++ /* register driver at the USB subsystem */
++ result = usb_register(&usb_ipc_driver);
++ if (result < 0) {
++ err ("usb ipc driver could not be registered");
++ return result;
++ }
++
++ /*init the related mux interface*/
++ if (!(bvd_ipc = kzalloc(sizeof(struct ipc_usb_data), GFP_KERNEL))) {
++ err("usb_ipc_init: Out of memory.");
++ usb_deregister(&usb_ipc_driver);
++ return -ENOMEM;
++ }
++ bvd_dbg("usb_ipc_init: Address of bvd_ipc:%p", bvd_ipc);
++
++ if (!(bvd_ipc->xmit.buf = kmalloc(IPC_USB_XMIT_SIZE, GFP_KERNEL))) {
++ err("usb_ipc_init: Not enough memory for the input buffer.");
++ kfree(bvd_ipc);
++ usb_deregister(&usb_ipc_driver);
++ return -ENOMEM;
++ }
++ bvd_dbg("usb_ipc_init: bvd_ipc->xmit.buf address:%p",
++ bvd_ipc->xmit.buf);
++ bvd_ipc->ipc_dev = NULL;
++ bvd_ipc->xmit.head = bvd_ipc->xmit.tail = 0;
++ bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
++
++ ipcusb_tty_driver.write = usb_ipc_write;
++ ipcusb_tty_driver.chars_in_buffer = usb_ipc_chars_in_buffer;
++
++ usb_for_mux_driver = &ipcusb_tty_driver;
++ usb_for_mux_tty = &ipcusb_tty;
++
++ /* init timers for ipcusb read process and usb suspend */
++ init_timer(&ipcusb_timer);
++ ipcusb_timer.function = ipcusb_timeout;
++
++ init_timer(&suspend_timer);
++ suspend_timer.function = suspend_timeout;
++
++ init_timer(&wakeup_timer);
++ wakeup_timer.function = wakeup_timeout;
++
++ info("USB Host(Bulverde) IPC driver registered.");
++ info(DRIVER_VERSION ":" DRIVER_DESC);
++
++ return 0;
++}
++
++static void __exit usb_ipc_exit(void)
++{
++ bvd_dbg("cleanup bvd_ipc");
++
++ kfree(bvd_ipc->xmit.buf);
++ kfree(bvd_ipc);
++ usb_deregister(&usb_ipc_driver);
++
++ info("USB Host(Bulverde) IPC driver deregistered.");
++}
++
++module_init(usb_ipc_init);
++module_exit(usb_ipc_exit);
++EXPORT_SYMBOL(usb_send_readurb);
+Index: linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/ts0710_mux_usb.h 2006-12-02 11:21:56.000000000 +0100
+@@ -0,0 +1,29 @@
++/*
++ * linux/drivers/usb/ipcusb.h
++ *
++ * Implementation of a ipc driver based Intel's Bulverde USB Host
++ * Controller.
++ *
++ * Copyright (C) 2003-2005 Motorola
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ * 2003-Nov-18 - (Motorola) created
++ *
++ */
++extern struct tty_driver *usb_for_mux_driver;
++extern struct tty_struct *usb_for_mux_tty;
++extern void (*usb_mux_dispatcher)(struct tty_struct *tty);
++extern void (*usb_mux_sender)(void);
+Index: linux-2.6.17.14-fic2/drivers/char/gsmmux/gsmmux_ldisc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.17.14-fic2/drivers/char/gsmmux/gsmmux_ldisc.c 2006-12-04 16:50:05.000000000 +0100
+@@ -0,0 +1,291 @@
++/* Linux kernel GSM multiplex implementation
++ *
++ * (C) 2006 by Harald Welte <hwelte at gnumonks.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/poll.h>
++#include <linux/ioctl.h>
++#include <linux/list.h>
++#include <linux/tty.h>
++#include <linux/string.h>
++#include <linux/tty_ldisc.h>
++
++/* this structure resembles a (de)multiplexer implementation */
++struct gsm_muxer {
++ struct list_head list;
++ char *name;
++ struct module *owner;
++ int (input)(struct gsmmux_instance *mi, const unsigned char *buf,
++ char *flags, int count);
++};
++
++/* one instance of gsm_muxer */
++struct gsmmux_instance {
++ struct gsm_muxer *muxer;
++}
++
++
++/* muxer core */
++
++static list_head gsm_muxers;
++spinlock_t gsm_muxers_lock;
++
++int gsmmux_register(struct gsm_muxer *muxer)
++{
++ spin_lock(&gsm_muxers_lock);
++ list_add(&muxer->list, &gsm_muxers);
++ spin_unlock(&gsm_muxers_lock);
++}
++EXPORT_SYMBOL_GPL(gsmmux_register);
++
++void gsmmux_unregister(struct gsm_muxer *muxer)
++{
++ spin_lock(&gsm_muxers_lock);
++ list_del(&muxer->list);
++ spin_unlock(&gsm_muxers_lock);
++}
++EXPORT_SYMBOL_GPL(gsmmux_unregister);
++
++/* called by lower layer (e.g. ldisc) if new data arrives */
++int gsmmmux_input(struct gsmmux_instance *mi, const unsigned char *buf,
++ char *flags, int count)
++{
++ return mi->muxer->input(mi, buf, flags, count);
++}
++EXPORT_SYMBOL_GPL(gsmmux_input);
++
++/* UART ops */
++
++static void gsmmux_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++}
++
++static void gsmmux_uart_break_ctl(struct uart_port *port, int break_state)
++{
++
++}
++
++static void gsmmux_uart_shutdown(struct uart_port *)
++{
++
++}
++
++static int gsmmux_uart_startup(struct uart_port *port)
++{
++ int ret = 0;
++
++ dbg("gsmmux_uart_startup: port=%s\n", port);
++
++ return ret;
++}
++
++static void gsmmux_uart_pm(struct uart_port *port, unsigned int level,
++ unsigned int old)
++{
++ switch (level) {
++ case 3:
++ break;
++ case 0:
++ break;
++ default:
++ }
++}
++
++static const char *gsmmux_uart_type(struct uart_port *port)
++{
++ return "gsmmux";
++}
++
++static void gsmmux_uart_release_port(struct uart_port *port)
++{
++}
++
++static int gsmmux_uart_request_port(struct uart_port *port)
++{
++ return 0;
++}
++
++static void gsmmux_uart_config_port(struct uart_port *port, int flags)
++{
++}
++
++static int gsmmux_uart_verify_port(struct uart_port *port,
++ struct serial_struct *ser)
++{
++ return 0;
++}
++
++static struct uart_ops gsmmux_uart_ops = {
++ .pm = gsmmux_uart_pm,
++ .tx_empty = gsmmux_uart_tx_empty,
++ .get_mctrl = gsmmux_uart_get_mctrl,
++ .set_mctrl = gsmmux_uart_set_mctrl,
++ .stop_tx = gsmmux_uart_stop_tx,
++ .enable_ms = gsmmux_uart_enable_ms,
++ .break_ctl = gsmmux_uart_break_ctl,
++ .startup = gsmmux_uart_startup,
++ .shutdown = gsmmux_uart_shutdown,
++ .set_termios = gsmmux_uart_set_termios,
++ .type = gsmmux_uart_type,
++ .release_port = gsmmux_uart_release_port,
++ .request_port = gsmmux_uart_request_port,
++ .config_port = gsmmux_uart_config_port,
++ .verify_port = gsmmux_uart_verify_port,
++};
++
++static struct uart_driver gsmmux_uart_drv = {
++ .owner = THIS_MODULE,
++ .driver_name = "ttyGSM",
++ .devfs_name = "gsm/",
++ .dev_name = "gsmmux_serial",
++ .major = 240, /* local/unassigned range */
++ .nr = NUM_MUX,
++};
++
++static struct uart_port gsmmux_uart_ports[NUM_MUX] = {
++ {
++ .type = PORT_GSMMUX,
++ },
++};
++
++
++/* This is the gsmmux ldisc instance-specific data structure */
++struct gsm_ldisc {
++ struct gsmmux_instance mux;
++ spinlock_t recv_lock;
++};
++
++#define tty2gsm_ldisc(tty) ((struct gsm_ldisc *) ((tty)->disc_data))
++#define gsm_ldisc2tty(gsm_ldisc) ((gsm_ldisc)->tty)
++
++static ssize_t gsm_ldisc_read(struct tty_struct *tty, struct file *file,
++ __u8 __user *buf, size_t nr)
++{
++ /* reading by the userspace process on the 'master' tty is generally
++ * not supported while this line discipline is in presence */
++ return -EAGAIN;
++}
++
++static ssize_t gsm_ldisc_write(struct tty_struct *tty, struct file *file,
++ const unsigned char *buf, size_t nr)
++{
++ /* writing by the userspace process on the 'master' tty is generally
++ * not supported while this line discipline is in presence */
++ return -EAGAIN;
++}
++
++static int gsm_ldisc_ioctl(struct tty_struct *tty, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++ int err;
++
++ if (gsm_ldisc == NULL)
++ return -ENXIO;
++ err = -EFAULT;
++
++ switch (cmd) {
++ default:
++ err = -ENOIOCTLCMD;
++ }
++
++ return err;
++}
++
++static unsigned int gsm_ldisc_poll(struct tty_struct *tty, struct file *filp,
++ poll_table *wait)
++{
++ return 0;
++}
++
++static int gsm_ldisc_open(struct tty_struct *tty)
++{
++ struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++ /* Attach the line discipline ... */
++}
++
++static void gsm_ldisc_close(struct tty_struct *tty)
++{
++ struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++ /* Disconnect the line discipline */
++}
++
++/* this is called by the lower level (serial) driver when received data is
++ * available */
++
++static void gsm_ldisc_recv_buf(struct tty_struct *tty, const __u8 *data,
++ char *flags, int count)
++{
++ struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++ unsigned long flags;
++
++ if (!gsm_ldisc)
++ return;
++
++ spin_lock_irqsave(&gsm_ldisc->recv_lock, flags);
++ gsmmux_input(gsm_ldisc->mux, buf, cflags, count);
++ spin_unlock_irqrestore(&gsm_ldisc->recv_lock, flags);
++
++}
++
++/* this gets called by the lower level (serial) driver when it wants
++ * us to send some [more] data */
++static void gsm_ldisc_write_wakeup(struct tty_struct *tty)
++{
++ struct gsm_ldisc *gsm_ldisc = tty2gsm_ldisc(tty);
++
++ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
++ if (!gsm_ldisc)
++ return;
++}
++
++static struct tty_ldisc gsmmux_ldisc = {
++ .owner = THIS_MODULE,
++ .magic = TTY_LDISC_MAGIC,
++ .name = "gsmmux",
++ .open = gsm_ldisc_open,
++ .close = gsm_ldisc_close,
++ .read = gsm_ldisc_read,
++ .write = gsm_ldics_write,
++ .ioctl = gsm_ldisc_ioctl,
++ .poll = gsm_ldisc_poll,
++ .receive_buf = gsm_ldisc_recv_buf,
++ .write_wakeup = gsm_ldisc_write_wakeup,
++};
++
++static int __init gsm_ldisc_init(void)
++{
++ return tty_register_ldisc(N_GSMMUX, &gsmmux_ldisc);
++}
++
++static void __exit gsm_ldisc_exit(void)
++{
++ tty_unregister_ldisc(N_GSMMUX);
++}
++
++module_init(gsm_ldisc_init);
++module_exit(gsm_ldisc_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Harald Welte <laforge at gnumonks.org>");
++MODULE_ALIAS_LDISC(N_GSMMUX);
+Index: linux-2.6.17.14-fic2/include/linux/serial_core.h
+===================================================================
+--- linux-2.6.17.14-fic2.orig/include/linux/serial_core.h 2006-12-03 18:21:27.000000000 +0100
++++ linux-2.6.17.14-fic2/include/linux/serial_core.h 2006-12-03 18:23:55.000000000 +0100
+@@ -130,6 +130,9 @@
+ /* SUN4V Hypervisor Console */
+ #define PORT_SUNHV 72
+
++/* GSM Multiplexer Virtual Port */
++#define PORT_GSMMUX 73
++
+ #ifdef __KERNEL__
+
+ #include <linux/config.h>
Added: developers/nbd/patches-2.6.22/410-s3c2410-qt2410-buttons.patch
===================================================================
--- developers/nbd/patches-2.6.22/410-s3c2410-qt2410-buttons.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/410-s3c2410-qt2410-buttons.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,279 @@
+Index: linux-2.6.21-moko/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/mach-s3c2410/mach-qt2410.c
++++ linux-2.6.21-moko/arch/arm/mach-s3c2410/mach-qt2410.c
+@@ -408,6 +408,24 @@
+ .ocr_avail = MMC_VDD_32_33,
+ };
+
++static struct resource qt2410_button_resources[] = {
++ [0] = {
++ .start = S3C2410_GPF0,
++ .end = S3C2410_GPF0,
++ },
++ [1] = {
++ .start = S3C2410_GPF2,
++ .end = S3C2410_GPF2,
++ },
++};
++
++struct platform_device qt2410_button_dev = {
++ .name ="qt2410-button",
++ .num_resources = ARRAY_SIZE(qt2410_button_resources),
++ .resource = qt2410_button_resources,
++};
++
++
+ static void __init qt2410_map_io(void)
+ {
+ s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
+Index: linux-2.6.21-moko/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/keyboard/Kconfig
++++ linux-2.6.21-moko/drivers/input/keyboard/Kconfig
+@@ -240,5 +240,10 @@
+ To compile this driver as a module, choose M here: the
+ module will be called gta01kbd.
+
++config KEYBOARD_QT2410
++ tristate "QT2410 buttons"
++ depends on MACH_QT2410
++ default y
++
+
+ endif
+Index: linux-2.6.21-moko/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/input/keyboard/Makefile
++++ linux-2.6.21-moko/drivers/input/keyboard/Makefile
+@@ -14,6 +14,7 @@
+ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+ obj-$(CONFIG_KEYBOARD_GTA01) += gta01kbd.o
++obj-$(CONFIG_KEYBOARD_QT2410) += qt2410kbd.o
+ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
+ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+Index: linux-2.6.21-moko/drivers/input/keyboard/qt2410kbd.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/input/keyboard/qt2410kbd.c
+@@ -0,0 +1,218 @@
++/*
++ * Keyboard driver for Armzone QT2410
++ *
++ * (C) 2006 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/gta01.h>
++
++struct gta01kbd {
++ struct input_dev *input;
++ unsigned int suspended;
++ unsigned long suspend_jiffies;
++};
++
++static irqreturn_t gta01kbd_interrupt(int irq, void *dev_id)
++{
++ struct gta01kbd *gta01kbd_data = dev_id;
++
++ /* FIXME: use GPIO from platform_dev resources */
++ if (s3c2410_gpio_getpin(S3C2410_GPF0))
++ input_report_key(gta01kbd_data->input, KEY_PHONE, 1);
++ else
++ input_report_key(gta01kbd_data->input, KEY_PHONE, 0);
++
++ input_sync(gta01kbd_data->input);
++
++ return IRQ_HANDLED;
++}
++
++
++#ifdef CONFIG_PM
++static int gta01kbd_suspend(struct platform_device *dev, pm_message_t state)
++{
++ int i;
++ struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++ gta01kbd->suspended = 1;
++
++ return 0;
++}
++
++static int gta01kbd_resume(struct platform_device *dev)
++{
++ int i;
++ struct gta01kbd *gta01kbd = platform_get_drvdata(dev);
++
++ gta01kbd->suspended = 0;
++
++ return 0;
++}
++#else
++#define gta01kbd_suspend NULL
++#define gta01kbd_resume NULL
++#endif
++
++static int gta01kbd_probe(struct platform_device *pdev)
++{
++ struct gta01kbd *gta01kbd;
++ struct input_dev *input_dev;
++ int irq_911, irq_hold;
++
++ gta01kbd = kzalloc(sizeof(struct gta01kbd), GFP_KERNEL);
++ input_dev = input_allocate_device();
++ if (!gta01kbd || !input_dev) {
++ kfree(gta01kbd);
++ input_free_device(input_dev);
++ return -ENOMEM;
++ }
++
++ if (pdev->resource[0].flags != 0)
++ return -EINVAL;
++
++ irq_911 = s3c2410_gpio_getirq(pdev->resource[0].start);
++ if (irq_911 < 0)
++ return -EINVAL;
++
++ platform_set_drvdata(pdev, gta01kbd);
++
++ gta01kbd->input = input_dev;
++
++#if 0
++ spin_lock_init(>a01kbd->lock);
++ /* Init Keyboard rescan timer */
++ init_timer(&corgikbd->timer);
++ corgikbd->timer.function = corgikbd_timer_callback;
++ corgikbd->timer.data = (unsigned long) corgikbd;
++
++ /* Init Hinge Timer */
++ init_timer(&corgikbd->htimer);
++ corgikbd->htimer.function = corgikbd_hinge_timer;
++ corgikbd->htimer.data = (unsigned long) corgikbd;
++
++ corgikbd->suspend_jiffies=jiffies;
++
++ memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
++#endif
++
++ input_dev->name = "QT2410 Buttons";
++ input_dev->phys = "qt2410kbd/input0";
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++ input_dev->cdev.dev = &pdev->dev;
++ input_dev->private = gta01kbd;
++
++ input_dev->evbit[0] = BIT(EV_KEY);
++#if 0
++ input_dev->keycode = gta01kbd->keycode;
++ input_dev->keycodesize = sizeof(unsigned char);
++ input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode);
++
++ for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
++ set_bit(corgikbd->keycode[i], input_dev->keybit);
++ clear_bit(0, input_dev->keybit);
++ set_bit(SW_LID, input_dev->swbit);
++ set_bit(SW_TABLET_MODE, input_dev->swbit);
++ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++#endif
++
++ input_register_device(gta01kbd->input);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPF0, S3C2410_GPF0_EINT0);
++ if (request_irq(irq_911, gta01kbd_interrupt,
++ SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING,
++ "qt2410kbd_eint0", gta01kbd))
++ printk(KERN_WARNING "gta01kbd: Can't get IRQ\n");
++ enable_irq_wake(irq_911);
++
++ /* FIXME: headphone insert */
++
++#if 0
++ mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
++
++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
++ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
++ pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
++ if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
++ SA_INTERRUPT | SA_TRIGGER_RISING,
++ "corgikbd", corgikbd))
++ printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
++ }
++
++ /* Set Strobe lines as outputs - set high */
++ for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
++ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
++
++ /* Setup the headphone jack as an input */
++ pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
++#endif
++
++ return 0;
++}
++
++static int gta01kbd_remove(struct platform_device *pdev)
++{
++ struct gta01kbd *gta01kbd = platform_get_drvdata(pdev);
++
++ free_irq(s3c2410_gpio_getirq(pdev->resource[0].start), gta01kbd);
++#if 0
++ int i;
++
++ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
++ free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
++
++ del_timer_sync(&corgikbd->htimer);
++ del_timer_sync(&corgikbd->timer);
++#endif
++ input_unregister_device(gta01kbd->input);
++
++ kfree(gta01kbd);
++
++ return 0;
++}
++
++static struct platform_driver gta01kbd_driver = {
++ .probe = gta01kbd_probe,
++ .remove = gta01kbd_remove,
++ .suspend = gta01kbd_suspend,
++ .resume = gta01kbd_resume,
++ .driver = {
++ .name = "qt2410-button",
++ },
++};
++
++static int __devinit gta01kbd_init(void)
++{
++ return platform_driver_register(>a01kbd_driver);
++}
++
++static void __exit gta01kbd_exit(void)
++{
++ platform_driver_unregister(>a01kbd_driver);
++}
++
++module_init(gta01kbd_init);
++module_exit(gta01kbd_exit);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("Armzone QT2410 Buttons Driver");
++MODULE_LICENSE("GPL");
Added: developers/nbd/patches-2.6.22/420-config-nr-tty-devices.patch
===================================================================
--- developers/nbd/patches-2.6.22/420-config-nr-tty-devices.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/420-config-nr-tty-devices.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,47 @@
+Index: linux-2.6.20.1/drivers/char/Kconfig
+===================================================================
+--- linux-2.6.20.1.orig/drivers/char/Kconfig 2007-02-26 00:46:56.000000000 +0100
++++ linux-2.6.20.1/drivers/char/Kconfig 2007-03-01 16:00:01.000000000 +0100
+@@ -57,6 +57,18 @@
+
+ If unsure, say Y.
+
++config NR_TTY_DEVICES
++ int "Maximum tty device number"
++ depends on VT
++ default 63
++ ---help---
++ This is the highest numbered device created in /dev. You will actually have
++ NR_TTY_DEVICES+1 devices in /dev. The default is 63, which will result in
++ 64 /dev entries. The lowest number you can set is 11, anything below that,
++ and it will default to 11. 63 is also the upper limit so we don't overrun
++ the serial consoles.
++
++
+ config HW_CONSOLE
+ bool
+ depends on VT && !S390 && !UML
+Index: linux-2.6.20.1/include/linux/vt.h
+===================================================================
+--- linux-2.6.20.1.orig/include/linux/vt.h 2007-03-01 16:04:03.000000000 +0100
++++ linux-2.6.20.1/include/linux/vt.h 2007-03-01 16:03:50.000000000 +0100
+@@ -6,8 +6,19 @@
+ * resizing).
+ */
+ #define MIN_NR_CONSOLES 1 /* must be at least 1 */
++#if (CONFIG_NR_TTY_DEVICES < 4)
++/* Lower Limit */
++#define MAX_NR_CONSOLES 4 /* serial lines start at 64 */
++#define MAX_NR_USER_CONSOLES 4 /* must be root to allocate above this */
++#elif (CONFIG_NR_TTY_DEVICES > 63)
++/* Upper Limit */
+ #define MAX_NR_CONSOLES 63 /* serial lines start at 64 */
+ #define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */
++#else
++/* They chose a sensible number */
++#define MAX_NR_CONSOLES CONFIG_NR_TTY_DEVICES
++#define MAX_NR_USER_CONSOLES CONFIG_NR_TTY_DEVICES
++#endif
+ /* Note: the ioctl VT_GETSTATE does not work for
+ consoles 16 and higher (since it returns a short) */
+
Added: developers/nbd/patches-2.6.22/430-hxd8-core.patch
===================================================================
--- developers/nbd/patches-2.6.22/430-hxd8-core.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/430-hxd8-core.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,456 @@
+This patch adds another machine, the FIC HXD8
+Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/mach-s3c2440/Kconfig
++++ linux-2.6.21-moko/arch/arm/mach-s3c2440/Kconfig
+@@ -66,6 +66,12 @@
+ default y if ARCH_S3C2440
+ select CPU_S3C2440
+
++config MACH_HXD8
++ bool "FIC HXD8"
++ select CPU_S3C2440
++ select SENSORS_PCF50606
++ help
++ Say Y here if you are using the FIC Neo1973 GSM Phone
+
+ endmenu
+
+Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/arch/arm/mach-s3c2440/Makefile
++++ linux-2.6.21-moko/arch/arm/mach-s3c2440/Makefile
+@@ -21,3 +21,4 @@
+ obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o
+ obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o
+ obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o
++obj-$(CONFIG_MACH_HXD8) += mach-hxd8.o
+Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/mach-hxd8.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/arch/arm/mach-s3c2440/mach-hxd8.c
+@@ -0,0 +1,403 @@
++/* linux/arch/arm/mach-s3c2440/mach-hxd8.c
++ *
++ * S3C2440 Machine Support for the FIC HXD8
++ *
++ * Copyright (c) 2007 OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/workqueue.h>
++#include <linux/serial_core.h>
++#include <linux/platform_device.h>
++#include <linux/mmc/protocol.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <linux/pcf50606.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/hardware/iomd.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++//#include <asm/debug-ll.h>
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-lcd.h>
++#include <asm/arch/idle.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/mci.h>
++#include <asm/arch/ts.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++#include <asm/arch/usb-control.h>
++
++#include <asm/arch-s3c2440/hxd8.h>
++#include <asm/arch/gta01.h>
++
++//#include "s3c2410.h"
++//#include "s3c2440.h"
++//#include "clock.h"
++#include <asm/plat-s3c24xx/devs.h>
++#include <asm/plat-s3c24xx/cpu.h>
++#include <asm/plat-s3c24xx/pm.h>
++
++static struct map_desc hxd8_iodesc[] __initdata = {
++ /* ISA IO Space map (memory space selected by A24) */
++
++ {
++ .virtual = (u32)S3C24XX_VA_ISA_WORD,
++ .pfn = __phys_to_pfn(S3C2410_CS2),
++ .length = 0x10000,
++ .type = MT_DEVICE,
++ }, {
++ .virtual = (u32)S3C24XX_VA_ISA_WORD + 0x10000,
++ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
++ .length = SZ_4M,
++ .type = MT_DEVICE,
++ }, {
++ .virtual = (u32)S3C24XX_VA_ISA_BYTE,
++ .pfn = __phys_to_pfn(S3C2410_CS2),
++ .length = 0x10000,
++ .type = MT_DEVICE,
++ }, {
++ .virtual = (u32)S3C24XX_VA_ISA_BYTE + 0x10000,
++ .pfn = __phys_to_pfn(S3C2410_CS2 + (1<<24)),
++ .length = SZ_4M,
++ .type = MT_DEVICE,
++ }
++};
++
++#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg hxd8_uartcfgs[] __initdata = {
++ [0] = {
++ .hwport = 0,
++ .flags = 0,
++ .ucon = 0x3c5,
++ .ulcon = 0x03,
++ .ufcon = 0x51,
++ },
++ [1] = {
++ .hwport = 1,
++ .flags = 0,
++ .ucon = 0x3c5,
++ .ulcon = 0x03,
++ .ufcon = 0x51,
++ },
++ [2] = {
++ .hwport = 2,
++ .flags = 0,
++ .ucon = 0x3c5,
++ .ulcon = 0x03,
++ .ufcon = 0x51,
++ }
++};
++
++static struct s3c2410_nand_set hxd8_nand_sets[] = {
++ [0] = {
++ .name = "hxd8-nand",
++ .nr_chips = 1,
++ },
++ [1] = {
++ .name = "hxd8-nand-1",
++ .nr_chips = 1,
++ },
++ [2] = {
++ .name = "hxd8-nand-2",
++ .nr_chips = 1,
++ },
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++*/
++
++static struct s3c2410_platform_nand hxd8_nand_info = {
++ .tacls = 20,
++ .twrph0 = 60,
++ .twrph1 = 20,
++ .nr_sets = ARRAY_SIZE(hxd8_nand_sets),
++ .sets = hxd8_nand_sets,
++};
++
++/* PMU configuration */
++
++static struct pcf50606_platform_data hxd8_pcf_pdata = {
++ .used_features = PCF50606_FEAT_EXTON |
++ PCF50606_FEAT_BBC |
++ PCF50606_FEAT_WDT |
++ PCF50606_FEAT_RTC |
++ PCF50606_FEAT_PWM |
++ PCF50606_FEAT_PWM_BL |
++ PCF50606_FEAT_BATVOLT,
++ .onkey_seconds_required = 5,
++ .init_brightness = 8,
++ .rails = {
++ [PCF50606_REGULATOR_D1REG] = {
++ .name = "rc_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_D2REG] = {
++ .name = "gps_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_D3REG] = {
++ .name = "io2_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_DCD] = {
++ .name = "core_1v3",
++ .voltage = {
++ .init = 1300,
++ .max = 1500,
++ },
++ },
++ [PCF50606_REGULATOR_DCDE] = {
++ .name = "io1_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_DCUD] = {
++ .name = "rf_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_IOREG] = {
++ .name = "audio_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50606_REGULATOR_LPREG] = {
++ .name = "lcm_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ },
++};
++
++static struct resource hxd8_pmu_resources[] = {
++ [0] = {
++ .flags = IORESOURCE_IRQ,
++ .start = HXD8_IRQ_PCF50606,
++ .end = HXD8_IRQ_PCF50606,
++ },
++};
++
++static struct platform_device hxd8_pmu_dev = {
++ .name = "pcf50606",
++ .num_resources = ARRAY_SIZE(hxd8_pmu_resources),
++ .resource = hxd8_pmu_resources,
++ .dev = {
++ .platform_data = &hxd8_pcf_pdata,
++ },
++};
++
++/* LCD driver info */
++
++static struct s3c2410fb_mach_info hxd8_lcd_cfg __initdata = {
++ .regs = {
++
++ .lcdcon1 = S3C2410_LCDCON1_TFT24BPP |
++ S3C2410_LCDCON1_TFT |
++ S3C2410_LCDCON1_CLKVAL(0x05),
++
++ .lcdcon2 = S3C2410_LCDCON2_VBPD(1) |
++ S3C2410_LCDCON2_LINEVAL(271) |
++ S3C2410_LCDCON2_VFPD(1) |
++ S3C2410_LCDCON2_VSPW(9),
++
++ .lcdcon3 = S3C2410_LCDCON3_HBPD(1) |
++ S3C2410_LCDCON3_HOZVAL(479) |
++ S3C2410_LCDCON3_HFPD(1),
++
++ .lcdcon4 = S3C2410_LCDCON4_MVAL(0) |
++ S3C2410_LCDCON4_HSPW(40),
++
++ .lcdcon5 = S3C2410_LCDCON5_FRM565 |
++ S3C2410_LCDCON5_INVVLINE |
++ S3C2410_LCDCON5_INVVFRAME,
++ },
++
++ .lpcsel = ((0xCE6) & ~7),
++ .type = S3C2410_LCDCON1_TFT,
++
++ .width = 480,
++ .height = 272,
++
++ .xres = {
++ .min = 480,
++ .max = 480,
++ .defval = 480,
++ },
++
++ .yres = {
++ .min = 272,
++ .max = 272,
++ .defval = 272,
++ },
++
++ .bpp = {
++ .min = 16,
++ .max = 32,
++ .defval = 32,
++ },
++};
++
++static struct platform_device hxd8_pm_gsm_dev = {
++ .name = "gta01-pm-gsm",
++};
++
++static void gta01_udc_command(enum s3c2410_udc_cmd_e cmd)
++{
++ printk(KERN_DEBUG "%s(%d)\n", __func__, cmd);
++
++ switch (cmd) {
++ case S3C2410_UDC_P_ENABLE:
++ s3c2410_gpio_setpin(HXD8_GPIO_USB_PULLUP, 1);
++ break;
++ case S3C2410_UDC_P_DISABLE:
++ s3c2410_gpio_setpin(HXD8_GPIO_USB_PULLUP, 0);
++ break;
++ case S3C2410_UDC_P_RESET:
++ /* FIXME! */
++ break;
++ default:
++ break;
++ }
++}
++
++/* USB Charger */
++
++static int hxd8_udc_vbus_draw(unsigned int ma)
++{
++ if (ma >= 500) {
++ /* enable fast charge */
++ printk(KERN_DEBUG "udc: enabling fast charge\n");
++ s3c2410_gpio_setpin(HXD8_GPIO_USB_CUR_SEL, 1);
++ } else {
++ /* disable fast charge */
++ printk(KERN_DEBUG "udc: disabling fast charge\n");
++ s3c2410_gpio_setpin(HXD8_GPIO_USB_CUR_SEL, 0);
++ }
++
++ return 0;
++}
++
++static struct s3c2410_udc_mach_info hxd8_udc_cfg = {
++ .vbus_draw = hxd8_udc_vbus_draw,
++};
++
++/* Touch Screen */
++static struct s3c2410_ts_mach_info hxd8_ts_cfg = {
++ .delay = 10000,
++ .presc = 49,
++ .oversampling_shift = 4,
++};
++
++static struct platform_device *hxd8_devices[] __initdata = {
++ &s3c_device_usb,
++ &s3c_device_lcd,
++ &s3c_device_wdt,
++ &s3c_device_i2c,
++ &s3c_device_iis,
++ &s3c_device_sdi,
++ &s3c_device_usbgadget,
++ &s3c_device_nand,
++ &s3c_device_ts,
++};
++
++static struct s3c24xx_board hxd8_board __initdata = {
++ .devices = hxd8_devices,
++ .devices_count = ARRAY_SIZE(hxd8_devices)
++};
++
++static void __init hxd8_map_io(void)
++{
++ s3c24xx_init_io(hxd8_iodesc, ARRAY_SIZE(hxd8_iodesc));
++ s3c24xx_init_clocks(16934400);
++ s3c24xx_init_uarts(hxd8_uartcfgs, ARRAY_SIZE(hxd8_uartcfgs));
++ s3c24xx_set_board(&hxd8_board);
++}
++
++static void __init hxd8_machine_init(void)
++{
++ hxd8_udc_cfg.udc_command = gta01_udc_command;
++ s3c_device_nand.dev.platform_data = &hxd8_nand_info;
++
++ s3c24xx_fb_set_platdata(&hxd8_lcd_cfg);
++
++ s3c24xx_udc_set_platdata(&hxd8_udc_cfg);
++ set_s3c2410ts_info(&hxd8_ts_cfg);
++
++ //platform_device_register(>a01_button_dev);
++ platform_device_register(&hxd8_pm_gsm_dev);
++
++ platform_device_register(&hxd8_pmu_dev);
++
++ s3c2410_pm_init();
++}
++
++MACHINE_START(HXD8, "HXD8")
++ /* Maintainer: Harald Welte <laforge at openmoko.org> */
++ .phys_io = S3C2410_PA_UART,
++ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++ .boot_params = S3C2410_SDRAM_PA + 0x100,
++
++ .init_irq = s3c24xx_init_irq,
++ .map_io = hxd8_map_io,
++ .init_machine = hxd8_machine_init,
++ .timer = &s3c24xx_timer,
++MACHINE_END
+Index: linux-2.6.21-moko/include/asm-arm/arch-s3c2440/hxd8.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/include/asm-arm/arch-s3c2440/hxd8.h
+@@ -0,0 +1,16 @@
++#ifndef _HXD8_H
++#define _HXD8_H
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/irqs.h>
++
++#define HXD8v1_SYSTEM_REV 0x00000110
++
++#define HXD8_GPIO_USB_CUR_SEL S3C2410_GPA0
++#define HXD8_GPIO_BACKLIGHT S3C2410_GPB0
++#define HXD8_GPIO_USB_PULLUP S3C2410_GPB9
++#define HXD8_GPIO_PCF50606 S3C2410_GPF6
++
++#define HXD8_IRQ_PCF50606 IRQ_EINT6
++
++#endif
Added: developers/nbd/patches-2.6.22/440-s3c2410_fb-truecolor.patch
===================================================================
--- developers/nbd/patches-2.6.22/440-s3c2410_fb-truecolor.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/440-s3c2410_fb-truecolor.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,67 @@
+Patch to enable 24bit truecolor support (32bit per pixel) support to
+s3c2410fb.c
+Index: linux-2.6.20.4-moko/arch/arm/mach-s3c2410/mach-gta01.c
+===================================================================
+--- linux-2.6.20.4-moko.orig/arch/arm/mach-s3c2410/mach-gta01.c 2007-04-08 14:01:47.000000000 +0200
++++ linux-2.6.20.4-moko/arch/arm/mach-s3c2410/mach-gta01.c 2007-04-08 14:02:09.000000000 +0200
+@@ -321,7 +321,7 @@
+
+ .bpp = {
+ .min = 1,
+- .max = 16,
++ .max = 32,
+ .defval = 16,
+ },
+ };
+Index: linux-2.6.20.4-moko/drivers/video/s3c2410fb.c
+===================================================================
+--- linux-2.6.20.4-moko.orig/drivers/video/s3c2410fb.c 2007-04-08 12:59:39.000000000 +0200
++++ linux-2.6.20.4-moko/drivers/video/s3c2410fb.c 2007-04-08 14:02:09.000000000 +0200
+@@ -263,6 +263,7 @@
+ }
+ break;
+ case 24:
++ case 32:
+ /* 24 bpp 888 */
+ var->red.length = 8;
+ var->red.offset = 16;
+@@ -315,6 +316,12 @@
+ break;
+ case 16:
+ fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT16BPP;
++ fbi->regs.lcdcon5 |= S3C2410_LCDCON5_HWSWP;
++ break;
++ case 24:
++ case 32:
++ fbi->regs.lcdcon1 |= S3C2410_LCDCON1_TFT24BPP;
++ fbi->regs.lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
+ break;
+
+ default:
+@@ -450,6 +457,8 @@
+
+ switch (var->bits_per_pixel)
+ {
++ case 32:
++ case 24:
+ case 16:
+ fbi->fb->fix.visual = FB_VISUAL_TRUECOLOR;
+ break;
+@@ -515,9 +524,17 @@
+ if (regno < 16) {
+ u32 *pal = fbi->fb->pseudo_palette;
+
++ switch (info->var.bits_per_pixel) {
++ case 16:
+ val = chan_to_field(red, &fbi->fb->var.red);
+ val |= chan_to_field(green, &fbi->fb->var.green);
+ val |= chan_to_field(blue, &fbi->fb->var.blue);
++ break;
++ case 32:
++ red >>= 8; green >>= 8; blue >>= 8; transp >>= 8;
++ val = (transp << 24) | (red << 16) | (green << 8) | blue;
++ break;
++ }
+
+ pal[regno] = val;
+ }
Added: developers/nbd/patches-2.6.22/450-s3c2440-nand-disable-hwecc.patch
===================================================================
--- developers/nbd/patches-2.6.22/450-s3c2440-nand-disable-hwecc.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/450-s3c2440-nand-disable-hwecc.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,17 @@
+Disable the hardware ECC checking on S3C2440 based platforms (HXD8, SMDK2440,
+GTA02) for the time being, since our u-boot doesn't yet support it for 2k page
+size NAND
+
+Index: linux-2.6.21-moko/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/mtd/nand/s3c2410.c
++++ linux-2.6.21-moko/drivers/mtd/nand/s3c2410.c
+@@ -644,7 +644,7 @@
+ nmtd->mtd.owner = THIS_MODULE;
+ nmtd->set = set;
+
+- if (hardware_ecc) {
++ if (info->cpu_type == TYPE_S3C2410 && hardware_ecc) {
+ chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ chip->ecc.correct = s3c2410_nand_correct_data;
+ chip->ecc.mode = NAND_ECC_HW;
Added: developers/nbd/patches-2.6.22/460-hxd8-tsl256x.patch
===================================================================
--- developers/nbd/patches-2.6.22/460-hxd8-tsl256x.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/460-hxd8-tsl256x.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,517 @@
+Index: linux-2.6.21-moko/drivers/i2c/chips/tsl256x.c
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/i2c/chips/tsl256x.c
+@@ -0,0 +1,310 @@
++/*
++ * tsl256x.c -- TSL256x Light Sensor driver
++ *
++ * Copyright 2007 by Fiwin.
++ * Author: Alec Tsai <alec_tsai at fiwin.com.tw>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This I2C client driver refers to pcf50606.c.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/i2c.h>
++#include <linux/types.h>
++#include <linux/input.h>
++
++#include "tsl256x.h"
++
++static unsigned short normal_i2c[] = { 0x39, I2C_CLIENT_END };
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++struct tsl256x_data {
++ struct i2c_client client;
++ struct mutex lock;
++ struct input_dev *input_dev;
++};
++
++static struct i2c_driver tsl256x_driver;
++
++/******************************************************************************
++ * Low-Level routines
++ *****************************************************************************/
++static inline int __reg_write(struct tsl256x_data *tsl, u_int8_t reg,
++ u_int8_t val)
++{
++ return i2c_smbus_write_byte_data(&tsl->client, reg, val);
++}
++
++static int reg_write(struct tsl256x_data *tsl, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++
++ mutex_lock(&tsl->lock);
++ ret = __reg_write(tsl, reg, val);
++ mutex_unlock(&tsl->lock);
++
++ return ret;
++}
++
++static inline int32_t __reg_read(struct tsl256x_data *tsl, u_int8_t reg)
++{
++ int32_t ret;
++
++ ret = i2c_smbus_read_byte_data(&tsl->client, reg);
++
++ return ret;
++}
++
++static u_int8_t reg_read(struct tsl256x_data *tsl, u_int8_t reg)
++{
++ int32_t ret;
++
++ mutex_lock(&tsl->lock);
++ ret = __reg_read(tsl, reg);
++ mutex_unlock(&tsl->lock);
++
++ return ret & 0xff;
++}
++
++u_int32_t calculate_lux(u_int32_t iGain, u_int32_t iType, u_int32_t ch0,
++ u_int32_t ch1)
++{
++ u_int32_t channel0 = ch0 * 636 / 10;
++ u_int32_t channel1 = ch1 * 636 / 10;
++ u_int32_t lux_value = 0;
++ u_int32_t ratio = (channel1 * (2^RATIO_SCALE)) / channel0;
++ u_int32_t b = 0, m = 0;
++
++ if (0 == ch0)
++ return 0;
++ else {
++ if (ratio > (13 * (2^RATIO_SCALE) / 10))
++ return 0;
++ }
++
++ switch (iType) {
++ case 0: // T package
++ if ((ratio >= 0) && (ratio <= K1T)) {
++ b = B1T;
++ m = M1T;
++ } else if (ratio <= K2T) {
++ b = B2T;
++ m = M2T;
++ } else if (ratio <= K3T) {
++ b = B3T;
++ m = M3T;
++ } else if (ratio <= K4T) {
++ b = B4T;
++ m = M4T;
++ } else if (ratio <= K5T) {
++ b = B5T;
++ m = M5T;
++ } else if (ratio <= K6T) {
++ b = B6T;
++ m = M6T;
++ } else if (ratio <= K7T) {
++ b = B7T;
++ m = M7T;
++ } else if (ratio > K8T) {
++ b = B8T;
++ m = M8T;
++ }
++ break;
++ case 1:// CS package
++ if ((ratio >= 0) && (ratio <= K1C)) {
++ b = B1C;
++ m = M1C;
++ } else if (ratio <= K2C) {
++ b = B2C;
++ m = M2C;
++ } else if (ratio <= K3C) {
++ b = B3C;
++ m = M3C;
++ } else if (ratio <= K4C) {
++ b = B4C;
++ m = M4C;
++ } else if (ratio <= K5C) {
++ b = B5C;
++ m = M5C;
++ } else if (ratio <= K6C) {
++ b = B6C;
++ m = M6C;
++ } else if (ratio <= K7C) {
++ b = B7C;
++ m = M7C;
++ } else if (ratio > K8C) {
++ b = B8C;
++ m = M8C;
++ }
++ break;
++ default:
++ return 0;
++ break;
++ }
++
++ lux_value = ((channel0 * b) - (channel1 * m)) / 16384;
++ return(lux_value);
++}
++
++static ssize_t tsl256x_show_light_lux(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct tsl256x_data *tsl = i2c_get_clientdata(client);
++ u_int8_t low_byte_of_ch0 = 0, high_byte_of_ch0 = 0;
++ u_int8_t low_byte_of_ch1 = 0, high_byte_of_ch1 = 0;
++ u_int32_t adc_value_ch0, adc_value_ch1, adc_value;
++
++ low_byte_of_ch0 = reg_read(tsl, TSL256X_REG_DATA0LOW);
++ high_byte_of_ch0 = reg_read(tsl, TSL256X_REG_DATA0HIGH);
++ low_byte_of_ch1 = reg_read(tsl, TSL256X_REG_DATA1LOW);
++ high_byte_of_ch1 = reg_read(tsl, TSL256X_REG_DATA1HIGH);
++
++ adc_value_ch0 = (high_byte_of_ch0 * 256 + low_byte_of_ch0) * 16;
++ adc_value_ch1 = (high_byte_of_ch1 * 256 + low_byte_of_ch1) * 16;
++
++ adc_value = calculate_lux(0, 0, adc_value_ch0, adc_value_ch1);
++ return sprintf(buf, "%d\n", adc_value);
++}
++
++static DEVICE_ATTR(light_lux, S_IRUGO, tsl256x_show_light_lux, NULL);
++
++static int tsl256x_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *new_client = NULL;
++ struct tsl256x_data *tsl256x = NULL;
++ u_int8_t id = 0;
++ int res = 0;
++
++ if (!(tsl256x = kzalloc(sizeof(*tsl256x), GFP_KERNEL)))
++ return -ENOMEM;
++
++ mutex_init(&tsl256x->lock);
++ new_client = &tsl256x->client;
++ i2c_set_clientdata(new_client, tsl256x);
++ new_client->addr = address;
++ new_client->adapter = adapter;
++ new_client->driver = &tsl256x_driver;
++ new_client->flags = 0;
++ strlcpy(new_client->name, "tsl256x", I2C_NAME_SIZE);
++
++ /* now we try to detect the chip */
++ /* register with i2c core */
++ res = i2c_attach_client(new_client);
++ if (res) {
++ printk(KERN_DEBUG "[%s]Error: during i2c_attach_client()\n",
++ new_client->name);
++ goto exit_free;
++ } else {
++ printk(KERN_INFO "TSL256X is attached to I2C bus.\n");
++ }
++
++ /* Configure TSL256X. */
++ {
++ /* Power up TSL256X. */
++ reg_write(tsl256x, TSL256X_REG_CONTROL, 0x03);
++
++ /* Check TSL256X ID. */
++ id = reg_read(tsl256x, TSL256X_REG_ID);
++ if (TSL2561_ID == (id & 0xF0)) {
++ /* Configuring the Timing Register.
++ High Gain (16x), integration time of 101ms. */
++ reg_write(tsl256x, TSL256X_REG_TIMING, 0x11);
++ } else {
++ goto exit_free;
++ }
++ }
++
++ res = device_create_file(&new_client->dev, &dev_attr_light_lux);
++ if (res)
++ goto exit_detach;
++
++ return 0;
++
++exit_free:
++ kfree(tsl256x);
++ return res;
++exit_detach:
++ i2c_detach_client(new_client);
++ return res;
++}
++
++static int tsl256x_attach_adapter(struct i2c_adapter *adapter)
++{
++ return i2c_probe(adapter, &addr_data, &tsl256x_detect);
++}
++
++static int tsl256x_detach_client(struct i2c_client *client)
++{
++ struct tsl256x_data *tsl256x = i2c_get_clientdata(client);
++
++ printk(KERN_INFO "Detach TSL256X from I2C bus.\n");
++
++ /* Power down TSL256X. */
++ reg_write(tsl256x, TSL256X_REG_CONTROL, 0x00);
++
++ device_remove_file(&client->dev, &dev_attr_light_lux);
++ kfree(tsl256x);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int tsl256x_suspend(struct device *dev, pm_message_t state)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct tsl256x_data *tsl256x = i2c_get_clientdata(client);
++
++ /* Power down TSL256X. */
++ reg_write(tsl256x, TSL256X_REG_CONTROL, 0x00);
++
++ return 0;
++}
++
++static int tsl256x_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct tsl256x_data *tsl256x = i2c_get_clientdata(client);
++
++ /* Power up TSL256X. */
++ reg_write(tsl256x, TSL256X_REG_CONTROL, 0x03);
++
++ return 0;
++}
++#endif
++
++static struct i2c_driver tsl256x_driver = {
++ .driver = {
++ .name = "tsl256x",
++ .owner = THIS_MODULE,
++#ifdef CONFIG_PM
++ .suspend = &tsl256x_suspend,
++ .resume = &tsl256x_resume,
++#endif
++ },
++ .id = I2C_DRIVERID_TSL256X,
++ .attach_adapter = &tsl256x_attach_adapter,
++ .detach_client = &tsl256x_detach_client,
++};
++
++static int __init tsl256x_init(void)
++{
++ return i2c_add_driver(&tsl256x_driver);
++}
++
++static void __exit tsl256x_exit(void)
++{
++ i2c_del_driver(&tsl256x_driver);
++}
++
++MODULE_AUTHOR("Alec Tsai <alec_tsai at fiwin.com.tw>");
++MODULE_LICENSE("GPL");
++
++module_init(tsl256x_init);
++module_exit(tsl256x_exit);
++
+Index: linux-2.6.21-moko/drivers/i2c/chips/tsl256x.h
+===================================================================
+--- /dev/null
++++ linux-2.6.21-moko/drivers/i2c/chips/tsl256x.h
+@@ -0,0 +1,154 @@
++/*
++ * tsl256x.h -- TSL256x Light Sensor driver
++ *
++ * Copyright 2007 by Fiwin.
++ * Author: Alec Tsai <alec_tsai at fiwin.com.tw>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * The contents of header file is copied from TSL256x Datasheet.
++ */
++
++#ifndef _TSL256X_H
++#define _TSL256X_H
++
++#define TSL2560_ID 0x00
++#define TSL2561_ID 0x10
++
++#define LUX_SCALE 14 /* scale by 2^14 */
++#define RATIO_SCALE 9 /*scale ratio by 2^9 */
++
++/******************************************************************************
++ * Integration time scaling factors
++ *****************************************************************************/
++#define CH_SCALE 10 /* scale channel values by 2^10 */
++#define CHSCALE_TINT0 0x7517 /* 322/11 * 2^CH_SCALE */
++#define CHSCALE_TINT1 0x0fe7 /* 322/81 * 2^CH_SCALE */
++
++/******************************************************************************
++ * T Package coefficients
++ *****************************************************************************/
++/*
++ * For Ch1/Ch0=0.00 to 0.50
++ * Lux/Ch0=0.0304.0.062*((Ch1/Ch0)^1.4)
++ * piecewise approximation
++ * For Ch1/Ch0=0.00 to 0.125:
++ * Lux/Ch0=0.0304.0.0272*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.125 to 0.250:
++ * Lux/Ch0=0.0325.0.0440*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.250 to 0.375:
++ * Lux/Ch0=0.0351.0.0544*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.375 to 0.50:
++ * Lux/Ch0=0.0381.0.0624*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.50 to 0.61:
++ * Lux/Ch0=0.0224.0.031*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.61 to 0.80:
++ * Lux/Ch0=0.0128.0.0153*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0=0.80 to 1.30:
++ * Lux/Ch0=0.00146.0.00112*(Ch1/Ch0)
++ *
++ * For Ch1/Ch0>1.3:
++ * Lux/Ch0=0
++ */
++#define K1T 0x0040 /* 0.125 * 2^RATIO_SCALE */
++#define B1T 0x01f2 /* 0.0304 * 2^LUX_SCALE */
++#define M1T 0x01be /* 0.0272 * 2^LUX_SCALE */
++#define K2T 0x0080 /* 0.250 * 2^RATIO_SCALE */
++#define B2T 0x0214 /* 0.0325 * 2^LUX_SCALE */
++#define M2T 0x02d1 /* 0.0440 * 2^LUX_SCALE */
++#define K3T 0x00c0 /* 0.375 * 2^RATIO_SCALE */
++#define B3T 0x023f /* 0.0351 * 2^LUX_SCALE */
++#define M3T 0x037b /* 0.0544 * 2^LUX_SCALE */
++#define K4T 0x0100 /* 0.50 * 2^RATIO_SCALE */
++#define B4T 0x0270 /* 0.0381 * 2^LUX_SCALE */
++#define M4T 0x03fe /* 0.0624 * 2^LUX_SCALE */
++#define K5T 0x0138 /* 0.61 * 2^RATIO_SCALE */
++#define B5T 0x016f /* 0.0224 * 2^LUX_SCALE */
++#define M5T 0x01fc /* 0.0310 * 2^LUX_SCALE */
++#define K6T 0x019a /* 0.80 * 2^RATIO_SCALE */
++#define B6T 0x00d2 /* 0.0128 * 2^LUX_SCALE */
++#define M6T 0x00fb /* 0.0153 * 2^LUX_SCALE */
++#define K7T 0x029a /* 1.3 * 2^RATIO_SCALE */
++#define B7T 0x0018 /* 0.00146 * 2^LUX_SCALE */
++#define M7T 0x0012 /* 0.00112 * 2^LUX_SCALE */
++#define K8T 0x029a /* 1.3 * 2^RATIO_SCALE */
++#define B8T 0x0000 /* 0.000 * 2^LUX_SCALE */
++#define M8T 0x0000 /* 0.000 * 2^LUX_SCALE */
++
++/******************************************************************************
++ * CS package coefficients
++ *****************************************************************************/
++/*
++ * For 0 <= Ch1/Ch0 <= 0.52
++ * Lux/Ch0 = 0.0315.0.0593*((Ch1/Ch0)^1.4)
++ * piecewise approximation
++ * For 0 <= Ch1/Ch0 <= 0.13
++ * Lux/Ch0 = 0.0315.0.0262*(Ch1/Ch0)
++ * For 0.13 <= Ch1/Ch0 <= 0.26
++ * Lux/Ch0 = 0.0337.0.0430*(Ch1/Ch0)
++ * For 0.26 <= Ch1/Ch0 <= 0.39
++ * Lux/Ch0 = 0.0363.0.0529*(Ch1/Ch0)
++ * For 0.39 <= Ch1/Ch0 <= 0.52
++ * Lux/Ch0 = 0.0392.0.0605*(Ch1/Ch0)
++ * For 0.52 < Ch1/Ch0 <= 0.65
++ * Lux/Ch0 = 0.0229.0.0291*(Ch1/Ch0)
++ * For 0.65 < Ch1/Ch0 <= 0.80
++ * Lux/Ch0 = 0.00157.0.00180*(Ch1/Ch0)
++ * For 0.80 < Ch1/Ch0 <= 1.30
++ * Lux/Ch0 = 0.00338.0.00260*(Ch1/Ch0)
++ * For Ch1/Ch0 > 1.30
++ * Lux = 0
++ */
++#define K1C 0x0043 /* 0.130 * 2^RATIO_SCALE */
++#define B1C 0x0204 /* 0.0315 * 2^LUX_SCALE */
++#define M1C 0x01ad /* 0.0262 * 2^LUX_SCALE */
++#define K2C 0x0085 /* 0.260 * 2^RATIO_SCALE */
++#define B2C 0x0228 /* 0.0337 * 2^LUX_SCALE */
++#define M2C 0x02c1 /* 0.0430 * 2^LUX_SCALE */
++#define K3C 0x00c8 /* 0.390 * 2^RATIO_SCALE */
++#define B3C 0x0253 /* 0.0363 * 2^LUX_SCALE */
++#define M3C 0x0363 /* 0.0529 * 2^LUX_SCALE */
++#define K4C 0x010a /* 0.520 * 2^RATIO_SCALE */
++#define B4C 0x0282 /* 0.0392 * 2^LUX_SCALE */
++#define M4C 0x03df /* 0.0605 * 2^LUX_SCALE */
++#define K5C 0x014d /* 0.65 * 2^RATIO_SCALE */
++#define B5C 0x0177 /* 0.0229 * 2^LUX_SCALE */
++#define M5C 0x01dd /* 0.0291 * 2^LUX_SCALE */
++#define K6C 0x019a /* 0.80 * 2^RATIO_SCALE */
++#define B6C 0x0101 /* 0.0157 * 2^LUX_SCALE */
++#define M6C 0x0127 /* 0.0180 * 2^LUX_SCALE */
++#define K7C 0x029a /* 1.3 * 2^RATIO_SCALE */
++#define B7C 0x0037 /* 0.00338 * 2^LUX_SCALE */
++#define M7C 0x002b /* 0.00260 * 2^LUX_SCALE */
++#define K8C 0x029a /* 1.3 * 2^RATIO_SCALE */
++#define B8C 0x0000 /* 0.000 * 2^LUX_SCALE */
++#define M8C 0x0000 /* 0.000 * 2^LUX_SCALE */
++
++/* TSL256x registers definition . */
++enum tsl256x_regs {
++ TSL256X_REG_CONTROL = 0x80, /* Control of basic functions */
++ TSL256X_REG_TIMING = 0x81, /* Integration time/gain control */
++ TSL256X_REG_THRESHLOWLOW = 0x82, /* Low byte of low interrupt threshold */
++ TSL256X_REG_THRESHLOWHIGH = 0x83, /* High byte of low interrupt threshold */
++ TSL256X_REG_THRESHHIGHLOW = 0x84, /* Low byte of high interrupt threshold */
++ TSL256X_REG_THRESHHIGHHIGH = 0x85, /* High byte of high interrupt threshold */
++ TSL256X_REG_INTERRUPT = 0x86, /* Interrupt control */
++ TSL256X_REG_CRC = 0x88, /* Factory test - not a user register */
++ TSL256X_REG_ID = 0x8A, /* Part number/ Rev ID */
++ TSL256X_REG_DATA0LOW = 0x8C, /* Low byte of ADC channel 0 */
++ TSL256X_REG_DATA0HIGH = 0x8D, /* High byte of ADC channel 0 */
++ TSL256X_REG_DATA1LOW = 0x8E, /* Low byte of ADC channel 1 */
++ TSL256X_REG_DATA1HIGH = 0x8F, /* High byte of ADC channel 1 */
++ __NUM_TSL256X_REGS
++};
++
++#endif /* _TSL256X_H */
++
+Index: linux-2.6.21-moko/drivers/i2c/chips/Kconfig
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/i2c/chips/Kconfig
++++ linux-2.6.21-moko/drivers/i2c/chips/Kconfig
+@@ -136,4 +136,14 @@
+ This driver can also be built as a module. If so, the module
+ will be called max6875.
+
++config SENSORS_TSL256X
++ tristate "Texas TSL256X Ambient Light Sensor"
++ depends on I2C
++ help
++ If you say yes here you get support for the Texas TSL256X
++ ambient light sensor chip.
++
++ This driver can also be built as a module. If so, the module
++ will be called tsl256x.
++
+ endmenu
+Index: linux-2.6.21-moko/drivers/i2c/chips/Makefile
+===================================================================
+--- linux-2.6.21-moko.orig/drivers/i2c/chips/Makefile
++++ linux-2.6.21-moko/drivers/i2c/chips/Makefile
+@@ -13,6 +13,7 @@
+ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+ obj-$(CONFIG_TPS65010) += tps65010.o
++obj-$(CONFIG_SENSORS_TSL256X) += tsl256x.o
+
+ ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
+ EXTRA_CFLAGS += -DDEBUG
+Index: linux-2.6.21-moko/include/linux/i2c-id.h
+===================================================================
+--- linux-2.6.21-moko.orig/include/linux/i2c-id.h
++++ linux-2.6.21-moko/include/linux/i2c-id.h
+@@ -160,6 +160,7 @@
+ #define I2C_DRIVERID_W83L785TS 1047
+ #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
+ #define I2C_DRIVERID_PCF50606 1049
++#define I2C_DRIVERID_TSL256X 1050
+
+ /*
+ * ---- Adapter types ----------------------------------------------------
Added: developers/nbd/patches-2.6.22/470-pcf50633.patch
===================================================================
--- developers/nbd/patches-2.6.22/470-pcf50633.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/470-pcf50633.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2255 @@
+Index: linux-2.6.22.1/drivers/i2c/chips/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Kconfig 2007-07-16 16:01:27.968568185 +0200
++++ linux-2.6.22.1/drivers/i2c/chips/Kconfig 2007-07-16 16:01:28.356590298 +0200
+@@ -45,6 +45,15 @@
+ This driver can also be built as a module. If so, the module
+ will be called pcf50606.
+
++config SENSORS_PCF50633
++ tristate "Philips PCF50633"
++ depends on I2C
++ help
++ If you say yes here you get support for Philips PCF50633
++ PMU (Power Management Unit) chips.
++
++ This driver can also be built as a module. If so, the module
++ will be called pcf50633.
+
+ config SENSORS_PCF8574
+ tristate "Philips PCF8574 and PCF8574A"
+Index: linux-2.6.22.1/drivers/i2c/chips/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Makefile 2007-07-16 16:01:27.992569554 +0200
++++ linux-2.6.22.1/drivers/i2c/chips/Makefile 2007-07-16 16:01:28.360590524 +0200
+@@ -9,6 +9,7 @@
+ obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
+ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
+ obj-$(CONFIG_SENSORS_PCF50606) += pcf50606.o
++obj-$(CONFIG_SENSORS_PCF50633) += pcf50633.o
+ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
+ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
+Index: linux-2.6.22.1/drivers/i2c/chips/pcf50633.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50633.c 2007-07-16 16:02:12.659114953 +0200
+@@ -0,0 +1,1691 @@
++/* Philips PCF50633 Power Management Unit (PMU) driver
++ *
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ * This driver is a monster ;) It provides the following features
++ * - voltage control for a dozen different voltage domains
++ * - charging control for main and backup battery
++ * - rtc / alarm
++ * - adc driver (hw_sensors like)
++ * - backlight
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/i2c.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/workqueue.h>
++#include <linux/rtc.h>
++#include <linux/bcd.h>
++#include <linux/watchdog.h>
++#include <linux/miscdevice.h>
++#include <linux/input.h>
++#include <linux/fb.h>
++#include <linux/backlight.h>
++#include <linux/sched.h>
++#include <linux/platform_device.h>
++#include <linux/pcf50633.h>
++#include <linux/apm-emulation.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch/gta02.h>
++
++#include "pcf50633.h"
++
++#if 1
++#define DEBUGP(x, args ...) printk("%s: " x, __FUNCTION__, ## args)
++#define DEBUGPC(x, args ...) printk(x, ## args)
++#else
++#define DEBUGP(x, args ...)
++#define DEBUGPC(x, args ...)
++#endif
++
++/***********************************************************************
++ * Static data / structures
++ ***********************************************************************/
++
++static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
++
++I2C_CLIENT_INSMOD_1(pcf50633);
++
++#define PCF50633_F_CHG_FAST 0x00000001 /* Charger Fast allowed */
++#define PCF50633_F_CHG_PRESENT 0x00000002 /* Charger present */
++#define PCF50633_F_CHG_FOK 0x00000004 /* Fast OK for battery */
++#define PCF50633_F_CHG_ERR 0x00000008 /* Charger Error */
++#define PCF50633_F_CHG_PROT 0x00000010 /* Charger Protection */
++#define PCF50633_F_CHG_READY 0x00000020 /* Charging completed */
++#define PCF50633_F_CHG_MASK 0x000000fc
++
++#define PCF50633_F_PWR_PRESSED 0x00000100
++#define PCF50633_F_RTC_SECOND 0x00000200
++#define PCF50633_F_USB_PRESENT 0x00000400
++
++enum close_state {
++ CLOSE_STATE_NOT,
++ CLOSE_STATE_ALLOW = 0x2342,
++};
++
++struct pcf50633_data {
++ struct i2c_client client;
++ struct pcf50633_platform_data *pdata;
++ struct backlight_device *backlight;
++ struct mutex lock;
++ unsigned int flags;
++ unsigned int working;
++ struct work_struct work;
++ struct rtc_device *rtc;
++ struct input_dev *input_dev;
++ int allow_close;
++ int onkey_seconds;
++ int irq;
++#ifdef CONFIG_PM
++ struct {
++ u_int8_t int1m, int2m, int3m, int4m, int5m;
++ u_int8_t ooctim2;
++ u_int8_t autoout, autoena, automxc;
++ u_int8_t down1out, down1mxc;
++ u_int8_t down2out, down2ena;
++ u_int8_t memldoout, memldoena;
++ u_int8_t ledout, ledena, leddim;
++ struct {
++ u_int8_t out;
++ u_int8_t ena;
++ } ldo[__NUM_PCF50633_REGS];
++ } standby_regs;
++#endif
++};
++
++static struct i2c_driver pcf50633_driver;
++
++struct pcf50633_data *pcf50633_global;
++EXPORT_SYMBOL(pcf50633_global);
++
++static struct platform_device *pcf50633_pdev;
++
++/* This is a mitsubishi TN11-3H103J T,B NTC Thermistor -10..79 centigrade */
++static const u_int16_t ntc_table_tn11_3h103j[] = {
++ /* -10 */
++ 40260, 38560, 36950, 35410, 33950, 32550, 31220, 29960, 28750, 27590,
++ 26490, 25440, 24440, 23480, 22560, 21680, 20830, 20020, 19240, 18500,
++ 17780, 17710, 16440, 15810, 15210, 14630, 14070, 13540, 13030, 12540,
++ 12070, 11620, 11190, 10780, 10380, 10000, 9635, 9286, 8950, 8629,
++ 8320, 8024, 7740, 7467, 7205, 6954, 6713, 6481, 6258, 6044,
++ 5839, 5641, 5451, 5269, 5093, 4924, 4762, 4605, 4455, 4310,
++ 4171, 4037, 3908, 3784, 3664, 3549, 3438, 3313, 3227, 3128,
++ 3032, 2939, 2850, 2763, 2680, 2600, 2522, 2448, 2375, 2306,
++ 2239, 2174, 2111, 2050, 1922, 1935, 1881, 1828, 1776, 1727,
++};
++
++
++/***********************************************************************
++ * Low-Level routines
++ ***********************************************************************/
++
++static inline int __reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ return i2c_smbus_write_byte_data(&pcf->client, reg, val);
++}
++
++static int reg_write(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_write(pcf, reg, val);
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static inline int32_t __reg_read(struct pcf50633_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ ret = i2c_smbus_read_byte_data(&pcf->client, reg);
++
++ return ret;
++}
++
++static u_int8_t reg_read(struct pcf50633_data *pcf, u_int8_t reg)
++{
++ int32_t ret;
++
++ mutex_lock(&pcf->lock);
++ ret = __reg_read(pcf, reg);
++ mutex_unlock(&pcf->lock);
++
++ return ret & 0xff;
++}
++
++static int reg_set_bit_mask(struct pcf50633_data *pcf,
++ u_int8_t reg, u_int8_t mask, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ val &= mask;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~mask;
++ tmp |= val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++static int reg_clear_bits(struct pcf50633_data *pcf, u_int8_t reg, u_int8_t val)
++{
++ int ret;
++ u_int8_t tmp;
++
++ mutex_lock(&pcf->lock);
++
++ tmp = __reg_read(pcf, reg);
++ tmp &= ~val;
++ ret = __reg_write(pcf, reg, tmp);
++
++ mutex_unlock(&pcf->lock);
++
++ return ret;
++}
++
++/* synchronously read one ADC channel (busy-wait for result to be complete) */
++static u_int16_t adc_read(struct pcf50633_data *pcf, int channel, int avg,
++ u_int16_t *data2)
++{
++ u_int8_t adcs3, adcs2, adcs1;
++ u_int16_t ret;
++
++ DEBUGP("entering (pcf=%p, channel=%u, data2=%p)\n",
++ pcf, channel, data2);
++
++ channel &= PCF50633_ADCC1_ADCMUX_MASK;
++
++ mutex_lock(&pcf->lock);
++
++ /* start ADC conversion of selected channel */
++ __reg_write(pcf, PCF50633_REG_ADCC1, channel | avg |
++ PCF50633_ADCC1_ADCSTART | PCF50633_ADCC1_RES_10BIT);
++
++ do {
++ adcs3 = __reg_read(pcf, PCF50633_REG_ADCS3);
++ } while (!(adcs3 & PCF50633_ADCS3_ADCRDY));
++
++ adcs1 = __reg_read(pcf, PCF50633_REG_ADCS1);
++ ret = (adcs1 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT1L_MASK);
++
++ if (data2) {
++ adcs2 = __reg_read(pcf, PCF50633_REG_ADCS2);
++ *data2 = (adcs2 << 2) | (adcs3 & PCF50633_ADCS3_ADCDAT2L_MASK)
++ >> PCF50633_ADCS3_ADCDAT2L_SHIFT;
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("returning %u %u\n", ret, data2 ? *data2 : 0);
++
++ return ret;
++}
++
++/***********************************************************************
++ * Voltage / ADC
++ ***********************************************************************/
++
++static u_int8_t auto_voltage(unsigned int millivolts)
++{
++ if (millivolts < 1800)
++ return 0;
++ if (millivolts > 3800)
++ return 0xff;
++
++ millivolts -= 625;
++ return millivolts/25;
++}
++
++static unsigned int auto_2voltage(u_int8_t bits)
++{
++ if (bits < 0x2f)
++ return 0;
++ return 625 + (bits * 25);
++}
++
++static u_int8_t down_voltage(unsigned int millivolts)
++{
++ if (millivolts < 625)
++ return 0;
++ else if (millivolts > 3000)
++ return 0xff;
++
++ millivolts -= 625;
++ return millivolts/25;
++}
++
++static unsigned int down_2voltage(u_int8_t bits)
++{
++ return 625 + (bits*25);
++}
++
++static u_int8_t ldo_voltage(unsigned int millivolts)
++{
++ if (millivolts < 900)
++ return 0;
++ else if (millivolts > 3600)
++ return 0x1f;
++
++ millivolts -= 900;
++ return millivolts/100;
++}
++
++static unsigned int ldo_2voltage(u_int8_t bits)
++{
++ bits &= 0x1f;
++ return 900 + (bits * 100);
++}
++
++static const u_int8_t regulator_registers[__NUM_PCF50633_REGULATORS] = {
++ [PCF50633_REGULATOR_AUTO] = PCF50633_REG_AUTOOUT,
++ [PCF50633_REGULATOR_DOWN1] = PCF50633_REG_DOWN1OUT,
++ [PCF50633_REGULATOR_DOWN2] = PCF50633_REG_DOWN2OUT,
++ [PCF50633_REGULATOR_MEMLDO] = PCF50633_REG_MEMLDOOUT,
++ [PCF50633_REGULATOR_LDO1] = PCF50633_REG_LDO1OUT,
++ [PCF50633_REGULATOR_LDO2] = PCF50633_REG_LDO2OUT,
++ [PCF50633_REGULATOR_LDO3] = PCF50633_REG_LDO3OUT,
++ [PCF50633_REGULATOR_LDO4] = PCF50633_REG_LDO4OUT,
++ [PCF50633_REGULATOR_LDO5] = PCF50633_REG_LDO5OUT,
++ [PCF50633_REGULATOR_LDO6] = PCF50633_REG_LDO6OUT,
++ [PCF50633_REGULATOR_HCLDO] = PCF50633_REG_HCLDOOUT,
++};
++
++int pcf50633_onoff_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg, int on)
++{
++ u_int8_t addr;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ /* the *ENA register is always one after the *OUT register */
++ addr = regulator_registers[reg] + 1;
++
++ if (on == 0)
++ reg_set_bit_mask(pcf, addr, PCF50633_REGULATOR_ON, 0);
++ else
++ reg_set_bit_mask(pcf, addr, PCF50633_REGULATOR_ON,
++ PCF50633_REGULATOR_ON);
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50633_onoff_set);
++
++int pcf50633_onoff_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg)
++{
++ u_int8_t val, addr;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ /* the *ENA register is always one after the *OUT register */
++ addr = regulator_registers[reg] + 1;
++ val = reg_read(pcf, addr) & PCF50633_REGULATOR_ON;
++
++ return val;
++}
++EXPORT_SYMBOL(pcf50633_onoff_get);
++
++int pcf50633_voltage_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg,
++ unsigned int millivolts)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++
++ DEBUGP("pcf=%p, reg=%d, mvolts=%d\n", pcf, reg, millivolts);
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ regnr = regulator_registers[reg];
++
++ if (millivolts > pcf->pdata->rails[reg].voltage.max)
++ return -EINVAL;
++
++ switch (reg) {
++ case PCF50633_REGULATOR_AUTO:
++ volt_bits = auto_voltage(millivolts);
++ break;
++ case PCF50633_REGULATOR_DOWN1:
++ volt_bits = down_voltage(millivolts);
++ break;
++ case PCF50633_REGULATOR_DOWN2:
++ volt_bits = down_voltage(millivolts);
++ break;
++ case PCF50633_REGULATOR_LDO1:
++ case PCF50633_REGULATOR_LDO2:
++ case PCF50633_REGULATOR_LDO3:
++ case PCF50633_REGULATOR_LDO4:
++ case PCF50633_REGULATOR_LDO5:
++ case PCF50633_REGULATOR_LDO6:
++ case PCF50633_REGULATOR_HCLDO:
++ volt_bits = ldo_voltage(millivolts);
++ DEBUGP("ldo_voltage(0x%x)=%u\n", millivolts, volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return reg_write(pcf, regnr, volt_bits);
++}
++EXPORT_SYMBOL(pcf50633_voltage_set);
++
++unsigned int pcf50633_voltage_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg)
++{
++ u_int8_t volt_bits;
++ u_int8_t regnr;
++ unsigned int rc = 0;
++
++ if (reg >= __NUM_PCF50633_REGULATORS)
++ return -EINVAL;
++
++ regnr = regulator_registers[reg];
++ volt_bits = reg_read(pcf, regnr);
++
++ switch (reg) {
++ case PCF50633_REGULATOR_AUTO:
++ rc = auto_2voltage(volt_bits);
++ break;
++ case PCF50633_REGULATOR_DOWN1:
++ rc = down_2voltage(volt_bits);
++ break;
++ case PCF50633_REGULATOR_DOWN2:
++ rc = down_2voltage(volt_bits);
++ break;
++ case PCF50633_REGULATOR_LDO1:
++ case PCF50633_REGULATOR_LDO2:
++ case PCF50633_REGULATOR_LDO3:
++ case PCF50633_REGULATOR_LDO4:
++ case PCF50633_REGULATOR_LDO5:
++ case PCF50633_REGULATOR_LDO6:
++ case PCF50633_REGULATOR_HCLDO:
++ rc = ldo_2voltage(volt_bits);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return rc;
++}
++EXPORT_SYMBOL(pcf50633_voltage_get);
++
++/* go into 'STANDBY' mode, i.e. power off the main CPU and peripherals */
++void pcf50633_go_standby(void)
++{
++ reg_write(pcf50633_global, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_GOSTDBY);
++}
++EXPORT_SYMBOL(pcf50633_go_standby);
++
++void pcf50633_gpo0_set(struct pcf50633_data *pcf, int on)
++{
++ u_int8_t val;
++
++ if (on)
++ val = PCF50633_GPOCFG_GPOSEL_1;
++ else
++ val = PCF50633_GPOCFG_GPOSEL_0;
++
++ reg_set_bit_mask(pcf, PCF50633_REG_GPOCFG, 0x0f, val);
++}
++EXPORT_SYMBOL(pcf50633_gpo0_set);
++
++int pcf50633_gpo0_get(struct pcf50633_data *pcf)
++{
++ u_int8_t reg = reg_read(pcf, PCF50633_REG_GPOCFG) & 0x0f;
++
++ if (reg == PCF50633_GPOCFG_GPOSEL_1 ||
++ reg == (PCF50633_GPOCFG_GPOSEL_0|PCF50633_GPOCFG_GPOSEL_INVERSE))
++ return 1;
++
++ return 0;
++}
++EXPORT_SYMBOL(pcf50633_gpo0_get);
++
++static void pcf50633_work(struct work_struct *work)
++{
++ struct pcf50633_data *pcf =
++ container_of(work, struct pcf50633_data, work);
++ u_int8_t int1, int2, int3, int4, int5;
++
++ pcf->working = 1;
++
++ /* FIXME: read in one i2c transaction */
++ int1 = __reg_read(pcf, PCF50633_REG_INT1);
++ int2 = __reg_read(pcf, PCF50633_REG_INT2);
++ int3 = __reg_read(pcf, PCF50633_REG_INT3);
++ int4 = __reg_read(pcf, PCF50633_REG_INT4);
++ int5 = __reg_read(pcf, PCF50633_REG_INT5);
++
++ DEBUGP("INT1=0x%02x INT2=0x%02x INT3=0x%02x INT4=0x%02x INT5=0x%02x\n",
++ int1, int2, int3, int4, int5);
++
++ if (int1 & PCF50633_INT1_ADPINS) {
++ /* Charger inserted */
++ DEBUGPC("ADPINS ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 1);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags |= PCF50633_F_CHG_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_INSERT);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_ADD);
++ }
++ if (int1 & PCF50633_INT1_ADPREM) {
++ /* Charger removed */
++ DEBUGPC("ADPREM ");
++ input_report_key(pcf->input_dev, KEY_BATTERY, 0);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags &= ~PCF50633_F_CHG_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_REMOVE);
++ /* FIXME: signal this to userspace */
++ //kobject_uevent( ,KOBJ_ADD);
++ }
++ if (int1 & PCF50633_INT1_USBINS) {
++ DEBUGPC("USBINS ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 1);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags |= PCF50633_F_USB_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_USB_INSERT);
++
++ }
++ if (int1 & PCF50633_INT1_USBREM) {
++ DEBUGPC("USBREM ");
++ input_report_key(pcf->input_dev, KEY_POWER2, 0);
++ apm_queue_event(APM_POWER_STATUS_CHANGE);
++ pcf->flags &= ~PCF50633_F_USB_PRESENT;
++ if (pcf->pdata->cb)
++ pcf->pdata->cb(&pcf->client.dev,
++ PCF50633_FEAT_MBC, PMU_EVT_USB_REMOVE);
++ }
++ if (int1 & PCF50633_INT1_ALARM) {
++ DEBUGPC("ALARM ");
++ if (pcf->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_update_irq(pcf->rtc, 1,
++ RTC_AF | RTC_IRQF);
++ }
++ if (int1 & PCF50633_INT1_SECOND) {
++ DEBUGPC("SECOND ");
++ if (pcf->flags & PCF50633_F_RTC_SECOND)
++ rtc_update_irq(pcf->rtc, 1,
++ RTC_PF | RTC_IRQF);
++
++ if (pcf->onkey_seconds >= 0 &&
++ pcf->flags & PCF50633_F_PWR_PRESSED) {
++ DEBUGP("ONKEY_SECONDS(%u, OOCSTAT=0x%02x) ",
++ pcf->onkey_seconds,
++ reg_read(pcf, PCF50633_REG_OOCSTAT));
++ pcf->onkey_seconds++;
++ if (pcf->onkey_seconds >=
++ pcf->pdata->onkey_seconds_required) {
++ /* Ask init to do 'ctrlaltdel' */
++ DEBUGPC("SIGINT(init) ");
++ kill_proc(1, SIGINT, 1);
++ /* FIXME: what if userspace doesn't shut down? */
++ }
++ }
++ }
++
++ if (int2 & PCF50633_INT2_ONKEYF) {
++ /* ONKEY falling edge (start of button press) */
++ DEBUGPC("ONKEYF ");
++ pcf->flags |= PCF50633_F_PWR_PRESSED;
++ input_report_key(pcf->input_dev, KEY_POWER, 1);
++ }
++ if (int2 & PCF50633_INT2_ONKEYR) {
++ /* ONKEY rising edge (end of button press) */
++ DEBUGPC("ONKEYR ");
++ pcf->flags &= ~PCF50633_F_PWR_PRESSED;
++ pcf->onkey_seconds = -1;
++ input_report_key(pcf->input_dev, KEY_POWER, 0);
++ /* disable SECOND interrupt in case RTC didn't
++ * request it */
++ if (!(pcf->flags & PCF50633_F_RTC_SECOND))
++ reg_set_bit_mask(pcf, PCF50633_REG_INT1M,
++ PCF50633_INT1_SECOND,
++ PCF50633_INT1_SECOND);
++ }
++ /* FIXME: we don't use EXTON1/2/3. thats why we skip it */
++
++ if (int3 & PCF50633_INT3_BATFULL) {
++ DEBUGPC("BATFULL ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_CHGHALT) {
++ DEBUGPC("CHGHALT ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_THLIMON) {
++ DEBUGPC("THLIMON ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_THLIMOFF) {
++ DEBUGPC("THLIMOFF ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_USBLIMON) {
++ DEBUGPC("USBLIMON ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_USBLIMOFF) {
++ DEBUGPC("USBLIMOFF ");
++ /* FIXME: signal this to userspace */
++ }
++ if (int3 & PCF50633_INT3_ADCRDY) {
++ /* ADC result ready */
++ DEBUGPC("ADCRDY ");
++ }
++ if (int3 & PCF50633_INT3_ONKEY1S) {
++ /* ONKEY pressed for more than 1 second */
++ pcf->onkey_seconds = 0;
++ DEBUGPC("ONKEY1S ");
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_TOTRST,
++ PCF50633_OOCSHDWN_TOTRST);
++ /* enable SECOND interrupt (hz tick) */
++ reg_clear_bits(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ }
++
++ if (int4 & (PCF50633_INT4_LOWBAT|PCF50633_INT4_LOWSYS)) {
++ /* Really low battery voltage, we have 8 seconds left */
++ DEBUGPC("LOWBAT ");
++ apm_queue_event(APM_LOW_BATTERY);
++ DEBUGPC("SIGPWR(init) ");
++ kill_proc(1, SIGPWR, 1);
++ /* Tell PMU we are taking care of this */
++ reg_set_bit_mask(pcf, PCF50633_REG_OOCSHDWN,
++ PCF50633_OOCSHDWN_TOTRST,
++ PCF50633_OOCSHDWN_TOTRST);
++ }
++ if (int4 & PCF50633_INT4_HIGHTMP) {
++ /* High temperature */
++ DEBUGPC("HIGHTMP ");
++ apm_queue_event(APM_CRITICAL_SUSPEND);
++ }
++ if (int4 & (PCF50633_INT4_AUTOPWRFAIL|PCF50633_INT4_DWN1PWRFAIL|
++ PCF50633_INT4_DWN2PWRFAIL|PCF50633_INT4_LEDPWRFAIL|
++ PCF50633_INT4_LEDOVP)) {
++ /* Some regulator failed */
++ DEBUGPC("REGULATOR_FAIL ");
++ /* FIXME: deal with this */
++ }
++
++ DEBUGPC("\n");
++
++ pcf->working = 0;
++ input_sync(pcf->input_dev);
++ put_device(&pcf->client.dev);
++
++ enable_irq(pcf->irq);
++}
++
++static void pcf50633_schedule_work(struct pcf50633_data *pcf)
++{
++ int status;
++
++ get_device(&pcf->client.dev);
++ status = schedule_work(&pcf->work);
++ if (!status && !pcf->working)
++ dev_dbg(&pcf->client.dev, "work item may be lost\n");
++}
++
++
++static irqreturn_t pcf50633_irq(int irq, void *_pcf)
++{
++ struct pcf50633_data *pcf = _pcf;
++
++ DEBUGP("entering(irq=%u, pcf=%p): scheduling work\n",
++ irq, _pcf);
++ pcf50633_schedule_work(pcf);
++
++ /* Disable any further interrupts until we have processed
++ * the current one */
++ disable_irq(irq);
++
++ return IRQ_HANDLED;
++}
++
++static u_int16_t adc_to_batt_millivolts(u_int16_t adc)
++{
++ u_int16_t mvolts;
++
++ mvolts = (adc * 6000) / 1024;
++
++ return mvolts;
++}
++
++#define BATTVOLT_SCALE_START 2800
++#define BATTVOLT_SCALE_END 4200
++#define BATTVOLT_SCALE_DIVIDER ((BATTVOLT_SCALE_END - BATTVOLT_SCALE_START)/100)
++
++static u_int8_t battvolt_scale(u_int16_t battvolt)
++{
++ /* FIXME: this linear scale is completely bogus */
++ u_int16_t battvolt_relative = battvolt - BATTVOLT_SCALE_START;
++ unsigned int percent = battvolt_relative / BATTVOLT_SCALE_DIVIDER;
++
++ return percent;
++}
++
++u_int16_t pcf50633_battvolt(struct pcf50633_data *pcf)
++{
++ u_int16_t adc;
++ adc = adc_read(pcf, PCF50633_ADCC1_MUX_BATSNS_RES, 0, NULL);
++
++ return adc_to_batt_millivolts(adc);
++}
++EXPORT_SYMBOL(pcf50633_battvolt);
++
++static ssize_t show_battvolt(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++
++ return sprintf(buf, "%u\n", pcf50633_battvolt(pcf));
++}
++static DEVICE_ATTR(battvolt, S_IRUGO | S_IWUSR, show_battvolt, NULL);
++
++static int reg_id_by_name(const char *name)
++{
++ int reg_id;
++
++ if (!strcmp(name, "voltage_auto"))
++ reg_id = PCF50633_REGULATOR_AUTO;
++ else if (!strcmp(name, "voltage_down1"))
++ reg_id = PCF50633_REGULATOR_DOWN1;
++ else if (!strcmp(name, "voltage_down2"))
++ reg_id = PCF50633_REGULATOR_DOWN2;
++ else if (!strcmp(name, "voltage_memldo"))
++ reg_id = PCF50633_REGULATOR_MEMLDO;
++ else if (!strcmp(name, "voltage_ldo1"))
++ reg_id = PCF50633_REGULATOR_LDO1;
++ else if (!strcmp(name, "voltage_ldo2"))
++ reg_id = PCF50633_REGULATOR_LDO2;
++ else if (!strcmp(name, "voltage_ldo3"))
++ reg_id = PCF50633_REGULATOR_LDO3;
++ else if (!strcmp(name, "voltage_ldo4"))
++ reg_id = PCF50633_REGULATOR_LDO4;
++ else if (!strcmp(name, "voltage_ldo5"))
++ reg_id = PCF50633_REGULATOR_LDO5;
++ else if (!strcmp(name, "voltage_ldo6"))
++ reg_id = PCF50633_REGULATOR_LDO6;
++ else if (!strcmp(name, "voltage_hcldo"))
++ reg_id = PCF50633_REGULATOR_HCLDO;
++ else
++ reg_id = -1;
++
++ return reg_id;
++}
++
++static ssize_t show_vreg(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return 0;
++
++ if (pcf50633_onoff_get(pcf, reg_id) > 0)
++ return sprintf(buf, "%u\n", pcf50633_voltage_get(pcf, reg_id));
++ else
++ return strlcpy(buf, "0\n", PAGE_SIZE);
++}
++
++static ssize_t set_vreg(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ unsigned long mvolts = simple_strtoul(buf, NULL, 10);
++ unsigned int reg_id;
++
++ reg_id = reg_id_by_name(attr->attr.name);
++ if (reg_id < 0)
++ return -EIO;
++
++ DEBUGP("attempting to set %s(%d) to %lu mvolts\n", attr->attr.name,
++ reg_id, mvolts);
++
++ if (mvolts == 0) {
++ pcf50633_onoff_set(pcf, reg_id, 0);
++ } else {
++ if (pcf50633_voltage_set(pcf, reg_id, mvolts) < 0) {
++ dev_warn(dev, "refusing to set %s(%d) to %lu mvolts "
++ "(max=%u)\n", attr->attr.name, reg_id, mvolts,
++ pcf->pdata->rails[reg_id].voltage.max);
++ return -EINVAL;
++ }
++ pcf50633_onoff_set(pcf, reg_id, 1);
++ }
++
++ return count;
++}
++
++static DEVICE_ATTR(voltage_auto, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_down1, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_down2, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_memldo, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo1, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo2, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo3, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo4, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo5, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_ldo6, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++static DEVICE_ATTR(voltage_hcldo, S_IRUGO | S_IWUSR, show_vreg, set_vreg);
++
++/***********************************************************************
++ * Charger Control
++ ***********************************************************************/
++
++/* Enable/disable fast charging (500mA in the GTA02) */
++void pcf50633_charge_fast(struct pcf50633_data *pcf, int on)
++{
++#if 0
++ if (!(pcf->pdata->used_features & PCF50606_FEAT_MBC))
++ return;
++
++ if (on) {
++ /* We can allow PCF to automatically charge
++ * using Ifast */
++ pcf->flags |= PCF50606_F_CHG_FAST;
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST,
++ PCF50606_MBCC1_AUTOFST);
++ } else {
++ pcf->flags &= ~PCF50606_F_CHG_FAST;
++ /* disable automatic fast-charge */
++ reg_clear_bits(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_AUTOFST);
++ /* switch to idle mode to abort existing charge
++ * process */
++ reg_set_bit_mask(pcf, PCF50606_REG_MBCC1,
++ PCF50606_MBCC1_CHGMOD_MASK,
++ PCF50606_MBCC1_CHGMOD_IDLE);
++ }
++#endif
++}
++EXPORT_SYMBOL(pcf50633_charge_fast);
++
++#define ONE 1000000
++static inline u_int16_t adc_to_rntc(struct pcf50633_data *pcf, u_int16_t adc)
++{
++ u_int32_t r_batt = (adc * pcf->pdata->r_fix_batt) / (1023 - adc);
++ u_int16_t r_ntc;
++
++ /* The battery NTC has a parallell 10kOhms resistor */
++ r_ntc = ONE / ((ONE/r_batt) - (ONE/pcf->pdata->r_fix_batt_par));
++
++ return r_ntc;
++}
++
++static inline int16_t rntc_to_temp(u_int16_t rntc)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(ntc_table_tn11_3h103j); i++) {
++ if (rntc > ntc_table_tn11_3h103j[i])
++ return i - 10;
++ }
++ return 2342;
++}
++
++static ssize_t show_battemp(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int16_t adc;
++
++ adc = adc_read(pcf, PCF50633_ADCC1_MUX_BATTEMP, 0, NULL);
++
++ return sprintf(buf, "%d\n", rntc_to_temp(adc_to_rntc(pcf, adc)));
++#endif
++ return sprintf(buf, "\n");
++}
++static DEVICE_ATTR(battemp, S_IRUGO | S_IWUSR, show_battemp, NULL);
++
++static inline u_int16_t adc_to_chg_milliamps(struct pcf50633_data *pcf,
++ u_int16_t adc_adcin1,
++ u_int16_t adc_batvolt)
++{
++ u_int32_t res = ((adc_adcin1 - adc_batvolt) * 6000);
++ return res / (pcf->pdata->r_sense_milli * 1024 / 1000);
++}
++
++static ssize_t show_chgcur(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int16_t adc_batvolt, adc_adcin1;
++ u_int16_t ma;
++
++ adc_batvolt = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_ADCIN1,
++ &adc_adcin1);
++ ma = adc_to_chg_milliamps(pcf, adc_adcin1, adc_batvolt);
++
++ return sprintf(buf, "%u\n", ma);
++#endif
++ return sprintf(buf, "\n");
++}
++static DEVICE_ATTR(chgcur, S_IRUGO | S_IWUSR, show_chgcur, NULL);
++
++static const char *chgmode_names[] = {
++ [PCF50633_MBCS2_MBC_PLAY] = "play-only",
++ [PCF50633_MBCS2_MBC_USB_PRE] = "pre",
++ [PCF50633_MBCS2_MBC_ADP_PRE] = "pre",
++ [PCF50633_MBCS2_MBC_USB_PRE_WAIT] = "pre-wait",
++ [PCF50633_MBCS2_MBC_ADP_PRE_WAIT] = "pre-wait",
++ [PCF50633_MBCS2_MBC_USB_FAST] = "fast",
++ [PCF50633_MBCS2_MBC_ADP_FAST] = "fast",
++ [PCF50633_MBCS2_MBC_USB_FAST_WAIT] = "fast-wait",
++ [PCF50633_MBCS2_MBC_ADP_FAST_WAIT] = "fast-wait",
++ [PCF50633_MBCS2_MBC_ADP_FAST_WAIT] = "bat-full",
++};
++
++static ssize_t show_chgmode(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ u_int8_t mbcs2 = reg_read(pcf, PCF50633_REG_MBCS2);
++ u_int8_t chgmod = (mbcs2 & PCF50633_MBCS2_MBC_MASK);
++
++ return sprintf(buf, "%s\n", chgmode_names[chgmod]);
++}
++
++static ssize_t set_chgmode(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ u_int8_t mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1);
++
++ mbcc1 &= ~PCF50606_MBCC1_CHGMOD_MASK;
++
++ if (!strcmp(buf, "qualification"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_QUAL;
++ else if (!strcmp(buf, "pre"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_PRE;
++ else if (!strcmp(buf, "trickle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_TRICKLE;
++ else if (!strcmp(buf, "fast_cccv"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_FAST_CCCV;
++ /* We don't allow the other fast modes for security reasons */
++ else if (!strcmp(buf, "idle"))
++ mbcc1 |= PCF50606_MBCC1_CHGMOD_IDLE;
++ else
++ return -EINVAL;
++
++ reg_write(pcf, PCF50606_REG_MBCC1, mbcc1);
++#endif
++ return count;
++}
++
++static DEVICE_ATTR(chgmode, S_IRUGO | S_IWUSR, show_chgmode, set_chgmode);
++
++static const char *chgstate_names[] = {
++ [PCF50633_F_CHG_FAST] = "fast_enabled",
++ [PCF50633_F_CHG_PRESENT] = "present",
++ [PCF50633_F_CHG_FOK] = "fast_ok",
++ [PCF50633_F_CHG_ERR] = "error",
++ [PCF50633_F_CHG_PROT] = "protection",
++ [PCF50633_F_CHG_READY] = "ready",
++};
++
++static ssize_t show_chgstate(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++#if 0
++ char *b = buf;
++ int i;
++
++ for (i = 0; i < 32; i++)
++ if (pcf->flags & (1 << i) && i < ARRAY_SIZE(chgstate_names))
++ b += sprintf(b, "%s ", chgstate_names[i]);
++
++ if (b > buf)
++ b += sprintf(b, "\n");
++
++ return b - buf;
++#endif
++ return 0;
++}
++static DEVICE_ATTR(chgstate, S_IRUGO | S_IWUSR, show_chgstate, NULL);
++
++/***********************************************************************
++ * APM emulation
++ ***********************************************************************/
++
++extern void (*apm_get_power_status)(struct apm_power_info *);
++
++static void pcf50633_get_power_status(struct apm_power_info *info)
++{
++ struct pcf50633_data *pcf = pcf50633_global;
++ u_int8_t chgmod = reg_read(pcf, PCF50633_REG_MBCS2) &
++ PCF50633_MBCS2_MBC_MASK;
++ u_int16_t battvolt = 0; /* FIXME */
++ //u_int16_t battvolt = pcf50606_battvolt(pcf);
++
++ if (reg_read(pcf, PCF50633_REG_MBCS1) &
++ (PCF50633_MBCS1_USBPRES|PCF50633_MBCS1_ADAPTPRES))
++ info->ac_line_status = APM_AC_ONLINE;
++ else
++ info->ac_line_status = APM_AC_OFFLINE;
++
++ switch (chgmod) {
++ case PCF50633_MBCS2_MBC_USB_PRE:
++ case PCF50633_MBCS2_MBC_USB_PRE_WAIT:
++ case PCF50633_MBCS2_MBC_USB_FAST_WAIT:
++ case PCF50633_MBCS2_MBC_ADP_PRE:
++ case PCF50633_MBCS2_MBC_ADP_PRE_WAIT:
++ case PCF50633_MBCS2_MBC_ADP_FAST_WAIT:
++ case PCF50633_MBCS2_MBC_BAT_FULL:
++ case PCF50633_MBCS2_MBC_HALT:
++ info->battery_life = battvolt_scale(battvolt);
++ break;
++ case PCF50633_MBCS2_MBC_USB_FAST:
++ case PCF50633_MBCS2_MBC_ADP_FAST:
++ info->battery_status = APM_BATTERY_STATUS_CHARGING;
++ info->battery_flag = APM_BATTERY_FLAG_CHARGING;
++ default:
++ break;
++ }
++}
++
++/***********************************************************************
++ * RTC
++ ***********************************************************************/
++
++struct pcf50633_time {
++ u_int8_t sec;
++ u_int8_t min;
++ u_int8_t hour;
++ u_int8_t wkday;
++ u_int8_t day;
++ u_int8_t month;
++ u_int8_t year;
++};
++
++static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
++{
++ rtc->tm_sec = BCD2BIN(pcf->sec);
++ rtc->tm_min = BCD2BIN(pcf->min);
++ rtc->tm_hour = BCD2BIN(pcf->hour);
++ rtc->tm_wday = BCD2BIN(pcf->wkday);
++ rtc->tm_mday = BCD2BIN(pcf->day);
++ rtc->tm_mon = BCD2BIN(pcf->month);
++ rtc->tm_year = BCD2BIN(pcf->year) + 100;
++}
++
++static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
++{
++ pcf->sec = BIN2BCD(rtc->tm_sec);
++ pcf->min = BIN2BCD(rtc->tm_min);
++ pcf->hour = BIN2BCD(rtc->tm_hour);
++ pcf->wkday = BIN2BCD(rtc->tm_wday);
++ pcf->day = BIN2BCD(rtc->tm_mday);
++ pcf->month = BIN2BCD(rtc->tm_mon);
++ pcf->year = BIN2BCD(rtc->tm_year - 100);
++}
++
++static int pcf50633_rtc_ioctl(struct device *dev, unsigned int cmd,
++ unsigned long arg)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ switch (cmd) {
++ case RTC_PIE_OFF:
++ /* disable periodic interrupt (hz tick) */
++ pcf->flags &= ~PCF50633_F_RTC_SECOND;
++ reg_set_bit_mask(pcf, PCF50633_REG_INT1M,
++ PCF50633_INT1_SECOND, PCF50633_INT1_SECOND);
++ return 0;
++ case RTC_PIE_ON:
++ /* ensable periodic interrupt (hz tick) */
++ pcf->flags |= PCF50633_F_RTC_SECOND;
++ reg_clear_bits(pcf, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ return 0;
++ }
++ return -ENOIOCTLCMD;
++}
++
++static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50633_REG_RTCSC);
++ pcf_tm.min = __reg_read(pcf, PCF50633_REG_RTCMN);
++ pcf_tm.hour = __reg_read(pcf, PCF50633_REG_RTCHR);
++ pcf_tm.wkday = __reg_read(pcf, PCF50633_REG_RTCWD);
++ pcf_tm.day = __reg_read(pcf, PCF50633_REG_RTCDT);
++ pcf_tm.month = __reg_read(pcf, PCF50633_REG_RTCMT);
++ pcf_tm.year = __reg_read(pcf, PCF50633_REG_RTCYR);
++ mutex_unlock(&pcf->lock);
++
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ pcf2rtc_time(tm, &pcf_tm);
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++
++ return 0;
++}
++
++static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ DEBUGP("RTC_TIME: %u.%u.%u %u:%u:%u\n",
++ tm->tm_mday, tm->tm_mon, tm->tm_year,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++ rtc2pcf_time(&pcf_tm, tm);
++ DEBUGP("PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
++ pcf_tm.day, pcf_tm.month, pcf_tm.year,
++ pcf_tm.hour, pcf_tm.min, pcf_tm.sec);
++
++ mutex_lock(&pcf->lock);
++ /* FIXME: disable second interrupt */
++ __reg_write(pcf, PCF50633_REG_RTCSC, pcf_tm.sec);
++ __reg_write(pcf, PCF50633_REG_RTCMN, pcf_tm.min);
++ __reg_write(pcf, PCF50633_REG_RTCHR, pcf_tm.hour);
++ __reg_write(pcf, PCF50633_REG_RTCWD, pcf_tm.wkday);
++ __reg_write(pcf, PCF50633_REG_RTCDT, pcf_tm.day);
++ __reg_write(pcf, PCF50633_REG_RTCMT, pcf_tm.month);
++ __reg_write(pcf, PCF50633_REG_RTCYR, pcf_tm.year);
++ /* FIXME: re-enable second interrupt */
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++
++ mutex_lock(&pcf->lock);
++ pcf_tm.sec = __reg_read(pcf, PCF50633_REG_RTCSCA);
++ pcf_tm.min = __reg_read(pcf, PCF50633_REG_RTCMNA);
++ pcf_tm.hour = __reg_read(pcf, PCF50633_REG_RTCHRA);
++ pcf_tm.wkday = __reg_read(pcf, PCF50633_REG_RTCWDA);
++ pcf_tm.day = __reg_read(pcf, PCF50633_REG_RTCDTA);
++ pcf_tm.month = __reg_read(pcf, PCF50633_REG_RTCMTA);
++ pcf_tm.year = __reg_read(pcf, PCF50633_REG_RTCYRA);
++ mutex_unlock(&pcf->lock);
++
++ pcf2rtc_time(&alrm->time, &pcf_tm);
++
++ return 0;
++}
++
++static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ struct pcf50633_time pcf_tm;
++ u_int8_t irqmask;
++
++ rtc2pcf_time(&pcf_tm, &alrm->time);
++
++ mutex_lock(&pcf->lock);
++
++ /* disable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50633_REG_INT1M);
++ irqmask |= PCF50633_INT1_ALARM;
++ __reg_write(pcf, PCF50633_REG_INT1M, irqmask);
++
++ __reg_write(pcf, PCF50633_REG_RTCSCA, pcf_tm.sec);
++ __reg_write(pcf, PCF50633_REG_RTCMNA, pcf_tm.min);
++ __reg_write(pcf, PCF50633_REG_RTCHRA, pcf_tm.hour);
++ __reg_write(pcf, PCF50633_REG_RTCWDA, pcf_tm.wkday);
++ __reg_write(pcf, PCF50633_REG_RTCDTA, pcf_tm.day);
++ __reg_write(pcf, PCF50633_REG_RTCMTA, pcf_tm.month);
++ __reg_write(pcf, PCF50633_REG_RTCYRA, pcf_tm.year);
++
++ if (alrm->enabled) {
++ /* (re-)enaable alarm interrupt */
++ irqmask = __reg_read(pcf, PCF50633_REG_INT1M);
++ irqmask &= ~PCF50633_INT1_ALARM;
++ __reg_write(pcf, PCF50633_REG_INT1M, irqmask);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ /* FIXME */
++ return 0;
++}
++
++static struct rtc_class_ops pcf50633_rtc_ops = {
++ .ioctl = pcf50633_rtc_ioctl,
++ .read_time = pcf50633_rtc_read_time,
++ .set_time = pcf50633_rtc_set_time,
++ .read_alarm = pcf50633_rtc_read_alarm,
++ .set_alarm = pcf50633_rtc_set_alarm,
++};
++
++/***********************************************************************
++ * Backlight device
++ ***********************************************************************/
++
++static int pcf50633bl_get_intensity(struct backlight_device *bd)
++{
++ struct pcf50633_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = reg_read(pcf, PCF50633_REG_LEDOUT);
++
++ return intensity & 0x3f;
++}
++
++static int pcf50633bl_set_intensity(struct backlight_device *bd)
++{
++ struct pcf50633_data *pcf = class_get_devdata(&bd->class_dev);
++ int intensity = bd->props.brightness;
++
++ if (bd->props.power != FB_BLANK_UNBLANK)
++ intensity = 0;
++ if (bd->props.fb_blank != FB_BLANK_UNBLANK)
++ intensity = 0;
++
++ return reg_set_bit_mask(pcf, PCF50633_REG_LEDOUT, 0x3f,
++ intensity);
++}
++
++static struct backlight_ops pcf50633bl_ops = {
++ .get_brightness = pcf50633bl_get_intensity,
++ .update_status = pcf50633bl_set_intensity,
++};
++
++/***********************************************************************
++ * Driver initialization
++ ***********************************************************************/
++
++#ifdef CONFIG_MACH_NEO1973_GTA02
++/* We currently place those platform devices here to make sure the device
++ * suspend/resume order is correct */
++static struct platform_device gta01_pm_gps_dev = {
++ .name ="gta01-pm-gps",
++};
++
++static struct platform_device gta01_pm_bt_dev = {
++ .name ="gta01-pm-bt",
++};
++#endif
++
++static struct attribute *pcf_sysfs_entries[17] = {
++ &dev_attr_voltage_auto.attr,
++ &dev_attr_voltage_down1.attr,
++ &dev_attr_voltage_down2.attr,
++ &dev_attr_voltage_memldo.attr,
++ &dev_attr_voltage_ldo1.attr,
++ &dev_attr_voltage_ldo2.attr,
++ &dev_attr_voltage_ldo3.attr,
++ &dev_attr_voltage_ldo4.attr,
++ &dev_attr_voltage_ldo5.attr,
++ &dev_attr_voltage_ldo6.attr,
++ &dev_attr_voltage_hcldo.attr,
++ NULL
++};
++
++static struct attribute_group pcf_attr_group = {
++ .name = NULL, /* put in device directory */
++ .attrs = pcf_sysfs_entries,
++};
++
++static void populate_sysfs_group(struct pcf50633_data *pcf)
++{
++ int i = 0;
++ struct attribute **attr;
++
++ for (attr = pcf_sysfs_entries; *attr; attr++)
++ i++;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_MBC) {
++ pcf_sysfs_entries[i++] = &dev_attr_chgstate.attr;
++ pcf_sysfs_entries[i++] = &dev_attr_chgmode.attr;
++ }
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_CHGCUR)
++ pcf_sysfs_entries[i++] = &dev_attr_chgcur.attr;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_BATVOLT)
++ pcf_sysfs_entries[i++] = &dev_attr_battvolt.attr;
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_BATTEMP)
++ pcf_sysfs_entries[i++] = &dev_attr_battemp.attr;
++
++}
++
++static int pcf50633_detect(struct i2c_adapter *adapter, int address, int kind)
++{
++ struct i2c_client *new_client;
++ struct pcf50633_data *data;
++ int err = 0;
++ int irq;
++
++ DEBUGP("entering\n");
++ if (!pcf50633_pdev) {
++ printk(KERN_ERR "pcf50633: driver needs a platform_device!\n");
++ return -EIO;
++ }
++
++ irq = platform_get_irq(pcf50633_pdev, 0);
++ if (irq < 0) {
++ dev_err(&pcf50633_pdev->dev, "no irq in platform resources!\n");
++ return -EIO;
++ }
++
++ /* At the moment, we only support one PCF50633 in a system */
++ if (pcf50633_global) {
++ dev_err(&pcf50633_pdev->dev,
++ "currently only one chip supported\n");
++ return -EBUSY;
++ }
++
++ if (!(data = kzalloc(sizeof(*data), GFP_KERNEL)))
++ return -ENOMEM;
++
++ mutex_init(&data->lock);
++ INIT_WORK(&data->work, pcf50633_work);
++ data->irq = irq;
++ data->working = 0;
++ data->onkey_seconds = -1;
++ data->pdata = pcf50633_pdev->dev.platform_data;
++
++ new_client = &data->client;
++ i2c_set_clientdata(new_client, data);
++ new_client->addr = address;
++ new_client->adapter = adapter;
++ new_client->driver = &pcf50633_driver;
++ new_client->flags = 0;
++ strlcpy(new_client->name, "pcf50633", I2C_NAME_SIZE);
++
++ /* now we try to detect the chip */
++
++ /* register with i2c core */
++ if ((err = i2c_attach_client(new_client))) {
++ dev_err(&new_client->dev,
++ "error during i2c_attach_client()\n");
++ goto exit_free;
++ }
++
++ pcf50633_global = data;
++
++ populate_sysfs_group(data);
++
++ err = sysfs_create_group(&new_client->dev.kobj, &pcf_attr_group);
++ if (err) {
++ dev_err(&new_client->dev, "error creating sysfs group\n");
++ goto exit_detach;
++ }
++
++ /* create virtual charger 'device' */
++
++ /* register power off handler with core power management */
++ pm_power_off = &pcf50633_go_standby;
++
++ /* configure interrupt mask */
++ reg_write(data, PCF50633_REG_INT1M, PCF50633_INT1_SECOND);
++ reg_write(data, PCF50633_REG_INT2M, 0x00);
++ reg_write(data, PCF50633_REG_INT3M, 0x00);
++ reg_write(data, PCF50633_REG_INT4M, 0x00);
++ reg_write(data, PCF50633_REG_INT5M, 0x00);
++
++ err = request_irq(irq, pcf50633_irq, SA_INTERRUPT,
++ "pcf50633", data);
++ if (err < 0)
++ goto exit_sysfs;
++
++ set_irq_type(irq, IRQT_FALLING);
++
++ if (enable_irq_wake(irq) < 0)
++ dev_err(&new_client->dev, "IRQ %u cannot be enabled as wake-up"
++ "source in this hardware revision!", irq);
++
++ if (data->pdata->used_features & PCF50633_FEAT_RTC) {
++ data->rtc = rtc_device_register("pcf50633", &new_client->dev,
++ &pcf50633_rtc_ops, THIS_MODULE);
++ if (IS_ERR(data->rtc)) {
++ err = PTR_ERR(data->rtc);
++ goto exit_irq;
++ }
++ }
++
++ if (data->pdata->used_features & PCF50633_FEAT_PWM_BL) {
++ data->backlight = backlight_device_register("pcf50633-bl",
++ &new_client->dev,
++ data,
++ &pcf50633bl_ops);
++ if (!data->backlight)
++ goto exit_rtc;
++ /* FIXME: are we sure we want default == off? */
++ data->backlight->props.max_brightness = 16;
++ data->backlight->props.power = FB_BLANK_UNBLANK;
++ data->backlight->props.brightness = 0;
++ backlight_update_status(data->backlight);
++ }
++
++ data->input_dev = input_allocate_device();
++ if (!data->input_dev)
++ goto exit_pwm;
++
++ data->input_dev->name = "FIC Neo1973 PMU events";
++ data->input_dev->phys = "FIXME";
++ data->input_dev->id.bustype = BUS_I2C;
++ data->input_dev->cdev.dev = &new_client->dev;
++
++ data->input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
++ set_bit(KEY_POWER, data->input_dev->keybit);
++ set_bit(KEY_POWER2, data->input_dev->keybit);
++ set_bit(KEY_BATTERY, data->input_dev->keybit);
++
++ input_register_device(data->input_dev);
++
++ apm_get_power_status = pcf50633_get_power_status;
++
++#ifdef CONFIG_MACH_NEO1973_GTA02
++ if (machine_is_neo1973_gta02()) {
++ gta01_pm_gps_dev.dev.parent = &new_client->dev;
++ switch (system_rev) {
++ case GTA02v1_SYSTEM_REV:
++ gta01_pm_bt_dev.dev.parent = &new_client->dev;
++ platform_device_register(>a01_pm_bt_dev);
++ break;
++ }
++ platform_device_register(>a01_pm_gps_dev);
++ }
++#endif
++
++ return 0;
++exit_pwm:
++ if (data->pdata->used_features & PCF50633_FEAT_PWM_BL)
++ backlight_device_unregister(data->backlight);
++exit_rtc:
++ if (data->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_device_unregister(pcf50633_global->rtc);
++exit_irq:
++ free_irq(pcf50633_global->irq, pcf50633_global);
++exit_sysfs:
++ pm_power_off = NULL;
++ sysfs_remove_group(&new_client->dev.kobj, &pcf_attr_group);
++exit_detach:
++ i2c_detach_client(new_client);
++exit_free:
++ kfree(data);
++ pcf50633_global = NULL;
++ return err;
++}
++
++static int pcf50633_attach_adapter(struct i2c_adapter *adapter)
++{
++ DEBUGP("entering, calling i2c_probe\n");
++ return i2c_probe(adapter, &addr_data, &pcf50633_detect);
++}
++
++static int pcf50633_detach_client(struct i2c_client *client)
++{
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++
++ DEBUGP("entering\n");
++
++ apm_get_power_status = NULL;
++ input_unregister_device(pcf->input_dev);
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_PWM_BL)
++ backlight_device_unregister(pcf->backlight);
++
++ if (pcf->pdata->used_features & PCF50633_FEAT_RTC)
++ rtc_device_unregister(pcf->rtc);
++
++ free_irq(pcf->irq, pcf);
++
++ sysfs_remove_group(&client->dev.kobj, &pcf_attr_group);
++
++ pm_power_off = NULL;
++
++ kfree(pcf);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++#define INT1M_RESUMERS (PCF50633_INT1_ADPINS | \
++ PCF50633_INT1_ADPREM | \
++ PCF50633_INT1_USBINS | \
++ PCF50633_INT1_USBREM | \
++ PCF50633_INT1_ALARM)
++#define INT2M_RESUMERS (PCF50633_INT2_ONKEYF)
++#define INT3M_RESUMERS (PCF50633_INT3_BATFULL | \
++ PCF50633_INT3_CHGHALT | \
++ PCF50633_INT3_THLIMON | \
++ PCF50633_INT3_THLIMOFF | \
++ PCF50633_INT3_USBLIMON | \
++ PCF50633_INT3_USBLIMOFF | \
++ PCF50633_INT3_ONKEY1S)
++#define INT4M_RESUMERS (PCF50633_INT4_LOWSYS | \
++ PCF50633_INT4_LOWBAT | \
++ PCF50633_INT4_HIGHTMP)
++#define INT5M_RESUMERS (0)
++
++static int pcf50633_suspend(struct device *dev, pm_message_t state)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ int i;
++
++ /* The general idea is to power down all unused power supplies,
++ * and then mask all PCF50606 interrup sources but EXTONR, ONKEYF
++ * and ALARM */
++
++ mutex_lock(&pcf->lock);
++
++ /* Save all registers that don't "survive" standby state */
++ pcf->standby_regs.ooctim2 = __reg_read(pcf, PCF50633_REG_OOCTIM2);
++ pcf->standby_regs.autoout = __reg_read(pcf, PCF50633_REG_AUTOOUT);
++ pcf->standby_regs.autoena = __reg_read(pcf, PCF50633_REG_AUTOENA);
++ pcf->standby_regs.automxc = __reg_read(pcf, PCF50633_REG_AUTOMXC);
++ pcf->standby_regs.down1out = __reg_read(pcf, PCF50633_REG_DOWN1OUT);
++ pcf->standby_regs.down1mxc = __reg_read(pcf, PCF50633_REG_DOWN1MXC);
++ pcf->standby_regs.down2out = __reg_read(pcf, PCF50633_REG_DOWN2OUT);
++ pcf->standby_regs.down2ena = __reg_read(pcf, PCF50633_REG_DOWN2ENA);
++ pcf->standby_regs.memldoout = __reg_read(pcf, PCF50633_REG_MEMLDOOUT);
++ pcf->standby_regs.memldoena = __reg_read(pcf, PCF50633_REG_MEMLDOENA);
++ pcf->standby_regs.ledout = __reg_read(pcf, PCF50633_REG_LEDOUT);
++ pcf->standby_regs.ledena = __reg_read(pcf, PCF50633_REG_LEDENA);
++ pcf->standby_regs.leddim = __reg_read(pcf, PCF50633_REG_LEDDIM);
++ /* FIXME: one big read? */
++ for (i = 0; i < 7; i++) {
++ u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
++ pcf->standby_regs.ldo[i].out = __reg_read(pcf, reg_out);
++ pcf->standby_regs.ldo[i].ena = __reg_read(pcf, reg_out+1);
++ }
++
++ /* switch off power supplies that are not needed during suspend */
++ for (i = 0; i < __NUM_PCF50633_REGULATORS; i++) {
++ if (!(pcf->pdata->rails[i].flags & PMU_VRAIL_F_SUSPEND_ON)) {
++ u_int8_t tmp;
++
++ DEBUGP("disabling pcf50633 regulator %u\n", i);
++ /* we cannot use pcf50633_onoff_set() because we're
++ * already under the mutex */
++ tmp = __reg_read(pcf, regulator_registers[i]+1);
++ tmp &= 0xfe;
++ __reg_write(pcf, regulator_registers[i]+1, tmp);
++ }
++ }
++
++ pcf->standby_regs.int1m = __reg_read(pcf, PCF50633_REG_INT1M);
++ pcf->standby_regs.int2m = __reg_read(pcf, PCF50633_REG_INT2M);
++ pcf->standby_regs.int3m = __reg_read(pcf, PCF50633_REG_INT3M);
++ pcf->standby_regs.int4m = __reg_read(pcf, PCF50633_REG_INT4M);
++ pcf->standby_regs.int5m = __reg_read(pcf, PCF50633_REG_INT5M);
++ __reg_write(pcf, PCF50633_REG_INT1M, ~INT1M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT2M, ~INT2M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT3M, ~INT3M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT4M, ~INT4M_RESUMERS & 0xff);
++ __reg_write(pcf, PCF50633_REG_INT5M, ~INT5M_RESUMERS & 0xff);
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++
++static int pcf50633_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct pcf50633_data *pcf = i2c_get_clientdata(client);
++ int i;
++
++ mutex_lock(&pcf->lock);
++
++ /* Resume all saved registers that don't "survive" standby state */
++ __reg_write(pcf, PCF50633_REG_INT1M, pcf->standby_regs.int1m);
++ __reg_write(pcf, PCF50633_REG_INT2M, pcf->standby_regs.int2m);
++ __reg_write(pcf, PCF50633_REG_INT3M, pcf->standby_regs.int3m);
++ __reg_write(pcf, PCF50633_REG_INT4M, pcf->standby_regs.int4m);
++ __reg_write(pcf, PCF50633_REG_INT5M, pcf->standby_regs.int5m);
++
++ __reg_write(pcf, PCF50633_REG_OOCTIM2, pcf->standby_regs.ooctim2);
++ __reg_write(pcf, PCF50633_REG_AUTOOUT, pcf->standby_regs.autoout);
++ __reg_write(pcf, PCF50633_REG_AUTOMXC, pcf->standby_regs.automxc);
++ __reg_write(pcf, PCF50633_REG_DOWN1OUT, pcf->standby_regs.down1out);
++ __reg_write(pcf, PCF50633_REG_DOWN1MXC, pcf->standby_regs.down1mxc);
++ __reg_write(pcf, PCF50633_REG_DOWN2OUT, pcf->standby_regs.down2out);
++ __reg_write(pcf, PCF50633_REG_DOWN2ENA, pcf->standby_regs.down2ena);
++ __reg_write(pcf, PCF50633_REG_MEMLDOOUT, pcf->standby_regs.memldoout);
++ __reg_write(pcf, PCF50633_REG_MEMLDOENA, pcf->standby_regs.memldoena);
++ __reg_write(pcf, PCF50633_REG_LEDOUT, pcf->standby_regs.ledout);
++ __reg_write(pcf, PCF50633_REG_LEDENA, pcf->standby_regs.ledena);
++ __reg_write(pcf, PCF50633_REG_LEDDIM, pcf->standby_regs.leddim);
++ /* FIXME: one big read? */
++ for (i = 0; i < 7; i++) {
++ u_int8_t reg_out = PCF50633_REG_LDO1OUT + 2*i;
++ __reg_write(pcf, reg_out, pcf->standby_regs.ldo[i].out);
++ __reg_write(pcf, reg_out+1, pcf->standby_regs.ldo[i].ena);
++ }
++
++ mutex_unlock(&pcf->lock);
++
++ return 0;
++}
++#else
++#define pcf50633_suspend NULL
++#define pcf50633_resume NULL
++#endif
++
++static struct i2c_driver pcf50633_driver = {
++ .driver = {
++ .name = "pcf50633",
++ .suspend= &pcf50633_suspend,
++ .resume = &pcf50633_resume,
++ },
++ .id = I2C_DRIVERID_PCF50633,
++ .attach_adapter = &pcf50633_attach_adapter,
++ .detach_client = &pcf50633_detach_client,
++};
++
++/* platform driver, since i2c devices don't have platform_data */
++static int __init pcf50633_plat_probe(struct platform_device *pdev)
++{
++ struct pcf50633_platform_data *pdata = pdev->dev.platform_data;
++
++ if (!pdata)
++ return -ENODEV;
++
++ pcf50633_pdev = pdev;
++
++ return 0;
++}
++
++static int pcf50633_plat_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct platform_driver pcf50633_plat_driver = {
++ .probe = pcf50633_plat_probe,
++ .remove = pcf50633_plat_remove,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "pcf50633",
++ },
++};
++
++static int __init pcf50633_init(void)
++{
++ int rc;
++
++ if (!(rc = platform_driver_register(&pcf50633_plat_driver)))
++ rc = i2c_add_driver(&pcf50633_driver);
++
++ return rc;
++}
++
++static void pcf50633_exit(void)
++{
++ i2c_del_driver(&pcf50633_driver);
++ platform_driver_unregister(&pcf50633_plat_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_LICENSE("GPL");
++
++module_init(pcf50633_init);
++module_exit(pcf50633_exit);
+Index: linux-2.6.22.1/include/linux/pcf50633.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/pcf50633.h 2007-07-16 16:01:28.400592804 +0200
+@@ -0,0 +1,103 @@
++#ifndef _LINUX_PCF50633_H
++#define _LINUX_PCF50633_H
++
++/* public in-kernel pcf50633 api */
++enum pcf50633_regulator_id {
++ PCF50633_REGULATOR_AUTO,
++ PCF50633_REGULATOR_DOWN1,
++ PCF50633_REGULATOR_DOWN2,
++ PCF50633_REGULATOR_MEMLDO,
++ PCF50633_REGULATOR_LDO1,
++ PCF50633_REGULATOR_LDO2,
++ PCF50633_REGULATOR_LDO3,
++ PCF50633_REGULATOR_LDO4,
++ PCF50633_REGULATOR_LDO5,
++ PCF50633_REGULATOR_LDO6,
++ PCF50633_REGULATOR_HCLDO,
++ __NUM_PCF50633_REGULATORS
++};
++
++struct pcf50633_data;
++extern struct pcf50633_data *pcf50633_global;
++
++extern void
++pcf50633_go_standby(void);
++
++extern void
++pcf50633_gpo0_set(struct pcf50633_data *pcf, int on);
++
++extern int
++pcf50633_gpo0_get(struct pcf50633_data *pcf);
++
++extern int
++pcf50633_voltage_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg,
++ unsigned int millivolts);
++extern unsigned int
++pcf50633_voltage_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg);
++extern int
++pcf50633_onoff_get(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg);
++
++extern int
++pcf50633_onoff_set(struct pcf50633_data *pcf,
++ enum pcf50633_regulator_id reg, int on);
++
++extern void
++pcf50633_charge_fast(struct pcf50633_data *pcf, int on);
++
++/* FIXME: sharded with pcf50606 */
++#define PMU_VRAIL_F_SUSPEND_ON 0x00000001 /* Remains on during suspend */
++#define PMU_VRAIL_F_UNUSED 0x00000002 /* This rail is not used */
++struct pmu_voltage_rail {
++ char *name;
++ unsigned int flags;
++ struct {
++ unsigned int init;
++ unsigned int max;
++ } voltage;
++};
++
++enum pmu_event {
++ PMU_EVT_NONE,
++ PMU_EVT_INSERT,
++ PMU_EVT_REMOVE,
++ PMU_EVT_USB_INSERT,
++ PMU_EVT_USB_REMOVE,
++ __NUM_PMU_EVTS
++};
++
++typedef int pmu_cb(struct device *dev, unsigned int feature,
++ enum pmu_event event);
++
++#define PCF50633_FEAT_EXTON 0x00000001 /* not yet supported */
++#define PCF50633_FEAT_MBC 0x00000002
++#define PCF50633_FEAT_BBC 0x00000004 /* not yet supported */
++#define PCF50633_FEAT_RTC 0x00000040
++#define PCF50633_FEAT_CHGCUR 0x00000100
++#define PCF50633_FEAT_BATVOLT 0x00000200
++#define PCF50633_FEAT_BATTEMP 0x00000400
++#define PCF50633_FEAT_PWM_BL 0x00000800
++
++struct pcf50633_platform_data {
++ /* general */
++ unsigned int used_features;
++ unsigned int onkey_seconds_required;
++
++ /* voltage regulator related */
++ struct pmu_voltage_rail rails[__NUM_PCF50633_REGULATORS];
++ unsigned int used_regulators;
++
++ /* charger related */
++ unsigned int r_fix_batt;
++ unsigned int r_fix_batt_par;
++ unsigned int r_sense_milli;
++
++ struct {
++ u_int8_t mbcc3; /* charger voltage / current */
++ } charger;
++ pmu_cb *cb;
++};
++
++#endif /* _PCF50633_H */
+Index: linux-2.6.22.1/include/linux/i2c-id.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/i2c-id.h 2007-07-16 16:01:28.076574340 +0200
++++ linux-2.6.22.1/include/linux/i2c-id.h 2007-07-16 16:01:28.420593944 +0200
+@@ -162,6 +162,7 @@
+ #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
+ #define I2C_DRIVERID_PCF50606 1049
+ #define I2C_DRIVERID_TSL256X 1050
++#define I2C_DRIVERID_PCF50633 1051
+
+ /*
+ * ---- Adapter types ----------------------------------------------------
+Index: linux-2.6.22.1/drivers/i2c/chips/pcf50633.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50633.h 2007-07-16 16:01:28.464596453 +0200
+@@ -0,0 +1,402 @@
++#ifndef _PCF50633_H
++#define _PCF50633_H
++
++/* Philips PCF50633 Power Managemnt Unit (PMU) driver
++ * (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ */
++
++enum pfc50633_regs {
++ PCF50633_REG_VERSION = 0x00,
++ PCF50633_REG_VARIANT = 0x01,
++ PCF50633_REG_INT1 = 0x02, /* Interrupt Status */
++ PCF50633_REG_INT2 = 0x03, /* Interrupt Status */
++ PCF50633_REG_INT3 = 0x04, /* Interrupt Status */
++ PCF50633_REG_INT4 = 0x05, /* Interrupt Status */
++ PCF50633_REG_INT5 = 0x06, /* Interrupt Status */
++ PCF50633_REG_INT1M = 0x07, /* Interrupt Mask */
++ PCF50633_REG_INT2M = 0x08, /* Interrupt Mask */
++ PCF50633_REG_INT3M = 0x09, /* Interrupt Mask */
++ PCF50633_REG_INT4M = 0x0a, /* Interrupt Mask */
++ PCF50633_REG_INT5M = 0x0b, /* Interrupt Mask */
++ PCF50633_REG_OOCSHDWN = 0x0c,
++ PCF50633_REG_OOCWAKE = 0x0d,
++ PCF50633_REG_OOCTIM1 = 0x0e,
++ PCF50633_REG_OOCTIM2 = 0x0f,
++ PCF50633_REG_OOCMODE = 0x10,
++ PCF50633_REG_OOCCTL = 0x11,
++ PCF50633_REG_OOCSTAT = 0x12,
++ PCF50633_REG_GPIOCTL = 0x13,
++ PCF50633_REG_GPIO1CFG = 0x14,
++ PCF50633_REG_GPIO2CFG = 0x15,
++ PCF50633_REG_GPIO3CFG = 0x16,
++ PCF50633_REG_GPOCFG = 0x17,
++ PCF50633_REG_BVMCTL = 0x18,
++ PCF50633_REG_SVMCTL = 0x19,
++ PCF50633_REG_AUTOOUT = 0x1a,
++ PCF50633_REG_AUTOENA = 0x1b,
++ PCF50633_REG_AUTOCTL = 0x1c,
++ PCF50633_REG_AUTOMXC = 0x1d,
++ PCF50633_REG_DOWN1OUT = 0x1e,
++ PCF50633_REG_DOWN1ENA = 0x1f,
++ PCF50633_REG_DOWN1CTL = 0x20,
++ PCF50633_REG_DOWN1MXC = 0x21,
++ PCF50633_REG_DOWN2OUT = 0x22,
++ PCF50633_REG_DOWN2ENA = 0x23,
++ PCF50633_REG_DOWN2CTL = 0x24,
++ PCF50633_REG_DOWN2MXC = 0x25,
++ PCF50633_REG_MEMLDOOUT = 0x26,
++ PCF50633_REG_MEMLDOENA = 0x27,
++ PCF50633_REG_LEDOUT = 0x28,
++ PCF50633_REG_LEDENA = 0x29,
++ PCF50633_REG_LEDCTL = 0x2a,
++ PCF50633_REG_LEDDIM = 0x2b,
++ /* reserved */
++ PCF50633_REG_LDO1OUT = 0x2d,
++ PCF50633_REG_LDO1ENA = 0x2e,
++ PCF50633_REG_LDO2OUT = 0x2f,
++ PCF50633_REG_LDO2ENA = 0x30,
++ PCF50633_REG_LDO3OUT = 0x31,
++ PCF50633_REG_LDO3ENA = 0x32,
++ PCF50633_REG_LDO4OUT = 0x33,
++ PCF50633_REG_LDO4ENA = 0x34,
++ PCF50633_REG_LDO5OUT = 0x35,
++ PCF50633_REG_LDO5ENA = 0x36,
++ PCF50633_REG_LDO6OUT = 0x37,
++ PCF50633_REG_LDO6ENA = 0x38,
++ PCF50633_REG_HCLDOOUT = 0x39,
++ PCF50633_REG_HCLDOENA = 0x3a,
++ PCF50633_REG_STBYCTL1 = 0x3b,
++ PCF50633_REG_STBYCTL2 = 0x3c,
++ PCF50633_REG_DEBPF1 = 0x3d,
++ PCF50633_REG_DEBPF2 = 0x3e,
++ PCF50633_REG_DEBPF3 = 0x3f,
++ PCF50633_REG_HCLDOOVL = 0x40,
++ PCF50633_REG_DCDCSTAT = 0x41,
++ PCF50633_REG_LDOSTAT = 0x42,
++ PCF50633_REG_MBCC1 = 0x43,
++ PCF50633_REG_MBCC2 = 0x44,
++ PCF50633_REG_MBCC3 = 0x45,
++ PCF50633_REG_MBCC4 = 0x46,
++ PCF50633_REG_MBCC5 = 0x47,
++ PCF50633_REG_MBCC6 = 0x48,
++ PCF50633_REG_MBCC7 = 0x49,
++ PCF50633_REG_MBCC8 = 0x4a,
++ PCF50633_REG_MBCS1 = 0x4b,
++ PCF50633_REG_MBCS2 = 0x4c,
++ PCF50633_REG_MBCS3 = 0x4d,
++ PCF50633_REG_BBCCTL = 0x4e,
++ PCF50633_REG_ALMGAIN = 0x4f,
++ PCF50633_REG_ALMDATA = 0x50,
++ /* reserved */
++ PCF50633_REG_ADCC3 = 0x52,
++ PCF50633_REG_ADCC2 = 0x53,
++ PCF50633_REG_ADCC1 = 0x54,
++ PCF50633_REG_ADCS1 = 0x55,
++ PCF50633_REG_ADCS2 = 0x56,
++ PCF50633_REG_ADCS3 = 0x57,
++ /* reserved */
++ PCF50633_REG_RTCSC = 0x59, /* Second */
++ PCF50633_REG_RTCMN = 0x5a, /* Minute */
++ PCF50633_REG_RTCHR = 0x5b, /* Hour */
++ PCF50633_REG_RTCWD = 0x5c, /* Weekday */
++ PCF50633_REG_RTCDT = 0x5d, /* Day */
++ PCF50633_REG_RTCMT = 0x5e, /* Month */
++ PCF50633_REG_RTCYR = 0x5f, /* Year */
++ PCF50633_REG_RTCSCA = 0x60, /* Alarm Second */
++ PCF50633_REG_RTCMNA = 0x61, /* Alarm Minute */
++ PCF50633_REG_RTCHRA = 0x62, /* Alarm Hour */
++ PCF50633_REG_RTCWDA = 0x63, /* Alarm Weekday */
++ PCF50633_REG_RTCDTA = 0x64, /* Alarm Day */
++ PCF50633_REG_RTCMTA = 0x65, /* Alarm Month */
++ PCF50633_REG_RTCYRA = 0x66, /* Alarm Year */
++
++ PCF50633_REG_MEMBYTE0 = 0x67,
++ PCF50633_REG_MEMBYTE1 = 0x68,
++ PCF50633_REG_MEMBYTE2 = 0x69,
++ PCF50633_REG_MEMBYTE3 = 0x6a,
++ PCF50633_REG_MEMBYTE4 = 0x6b,
++ PCF50633_REG_MEMBYTE5 = 0x6c,
++ PCF50633_REG_MEMBYTE6 = 0x6d,
++ PCF50633_REG_MEMBYTE7 = 0x6e,
++ /* reserved */
++ PCF50633_REG_DCDCPFM = 0x84,
++ __NUM_PCF50633_REGS
++};
++
++enum pcf50633_reg_int1 {
++ PCF50633_INT1_ADPINS = 0x01, /* Adapter inserted */
++ PCF50633_INT1_ADPREM = 0x02, /* Adapter removed */
++ PCF50633_INT1_USBINS = 0x04, /* USB inserted */
++ PCF50633_INT1_USBREM = 0x08, /* USB removed */
++ /* reserved */
++ PCF50633_INT1_ALARM = 0x40, /* RTC alarm time is reached */
++ PCF50633_INT1_SECOND = 0x80, /* RTC periodic second interrupt */
++};
++
++enum pcf50633_reg_int2 {
++ PCF50633_INT2_ONKEYR = 0x01, /* ONKEY rising edge */
++ PCF50633_INT2_ONKEYF = 0x02, /* ONKEY falling edge */
++ PCF50633_INT2_EXTON1R = 0x04, /* EXTON1 rising edge */
++ PCF50633_INT2_EXTON1F = 0x08, /* EXTON1 falling edge */
++ PCF50633_INT2_EXTON2R = 0x10, /* EXTON2 rising edge */
++ PCF50633_INT2_EXTON2F = 0x20, /* EXTON2 falling edge */
++ PCF50633_INT2_EXTON3R = 0x40, /* EXTON3 rising edge */
++ PCF50633_INT2_EXTON3F = 0x80, /* EXTON3 falling edge */
++};
++
++enum pcf50633_reg_int3 {
++ PCF50633_INT3_BATFULL = 0x01, /* Battery full */
++ PCF50633_INT3_CHGHALT = 0x02, /* Charger halt */
++ PCF50633_INT3_THLIMON = 0x04,
++ PCF50633_INT3_THLIMOFF = 0x08,
++ PCF50633_INT3_USBLIMON = 0x10,
++ PCF50633_INT3_USBLIMOFF = 0x20,
++ PCF50633_INT3_ADCRDY = 0x40, /* ADC conversion finished */
++ PCF50633_INT3_ONKEY1S = 0x80, /* ONKEY pressed 1 second */
++};
++
++enum pcf50633_reg_int4 {
++ PCF50633_INT4_LOWSYS = 0x01,
++ PCF50633_INT4_LOWBAT = 0x02,
++ PCF50633_INT4_HIGHTMP = 0x04,
++ PCF50633_INT4_AUTOPWRFAIL = 0x08,
++ PCF50633_INT4_DWN1PWRFAIL = 0x10,
++ PCF50633_INT4_DWN2PWRFAIL = 0x20,
++ PCF50633_INT4_LEDPWRFAIL = 0x40,
++ PCF50633_INT4_LEDOVP = 0x80,
++};
++
++enum pcf50633_reg_int5 {
++ PCF50633_INT5_LDO1PWRFAIL = 0x01,
++ PCF50633_INT5_LDO2PWRFAIL = 0x02,
++ PCF50633_INT5_LDO3PWRFAIL = 0x04,
++ PCF50633_INT5_LDO4PWRFAIL = 0x08,
++ PCF50633_INT5_LDO5PWRFAIL = 0x10,
++ PCF50633_INT5_LDO6PWRFAIL = 0x20,
++ PCF50633_INT5_HCLDOPWRFAIL = 0x40,
++ PCF50633_INT5_HCLDOOVL = 0x80,
++};
++
++enum pcf50633_reg_oocshdwn {
++ PCF50633_OOCSHDWN_GOSTDBY = 0x01,
++ PCF50633_OOCSHDWN_TOTRST = 0x04,
++ PCF50633_OOCSHDWN_COLDBOOT = 0x08,
++};
++
++enum pcf50633_reg_oocwake {
++ PCF50633_OOCWAKE_ONKEY = 0x01,
++ PCF50633_OOCWAKE_EXTON1 = 0x02,
++ PCF50633_OOCWAKE_EXTON2 = 0x04,
++ PCF50633_OOCWAKE_EXTON3 = 0x08,
++ PCF50633_OOCWAKE_RTC = 0x10,
++ /* reserved */
++ PCF50633_OOCWAKE_USB = 0x40,
++ PCF50633_OOCWAKE_ADP = 0x80,
++};
++
++enum pcf50633_reg_mbcc1 {
++ PCF50633_MBCC1_CHGENA = 0x01, /* Charger enable */
++ PCF50633_MBCC1_AUTOSTOP = 0x02,
++ PCF50633_MBCC1_AUTORES = 0x04, /* automatic resume */
++ PCF50633_MBCC1_RESUME = 0x08, /* explicit resume cmd */
++ PCF50633_MBCC1_RESTART = 0x10, /* restart charging */
++ PCF50633_MBCC1_PREWDTIME_60M = 0x20, /* max. precharging time */
++ PCF50633_MBCC1_WDTIME_1H = 0x00,
++ PCF50633_MBCC1_WDTIME_2H = 0x40,
++ PCF50633_MBCC1_WDTIME_4H = 0x80,
++ PCF50633_MBCC1_WDTIME_6H = 0xc0,
++};
++#define PCF50633_MBCC1_WDTIME_MASK 0xc0
++
++enum pcf50633_reg_mbcc2 {
++ PCF50633_MBCC2_VBATCOND_2V7 = 0x00,
++ PCF50633_MBCC2_VBATCOND_2V85 = 0x01,
++ PCF50633_MBCC2_VBATCOND_3V0 = 0x02,
++ PCF50633_MBCC2_VBATCOND_3V15 = 0x03,
++ PCF50633_MBCC2_VMAX_4V = 0x00,
++ PCF50633_MBCC2_VMAX_4V20 = 0x28,
++ PCF50633_MBCC2_VRESDEBTIME_64S = 0x80, /* debounce time (32/64sec) */
++};
++#define PCF50633_MBCC2_VBATCOND_MASK 0x03
++#define PCF50633_MBCC2_VMAX_MASK 0x3c
++
++enum pcf50633_reg_adcc1 {
++ PCF50633_ADCC1_ADCSTART = 0x01,
++ PCF50633_ADCC1_RES_10BIT = 0x02,
++ PCF50633_ADCC1_AVERAGE_NO = 0x00,
++ PCF50633_ADCC1_AVERAGE_4 = 0x04,
++ PCF50633_ADCC1_AVERAGE_8 = 0x08,
++ PCF50633_ADCC1_AVERAGE_16 = 0x0c,
++
++ PCF50633_ADCC1_MUX_BATSNS_RES = 0x00,
++ PCF50633_ADCC1_MUX_BATSNS_SUBTR = 0x10,
++ PCF50633_ADCC1_MUX_ADCIN2_RES = 0x20,
++ PCF50633_ADCC1_MUX_ADCIN2_SUBTR = 0x30,
++ PCF50633_ADCC1_MUX_BATTEMP = 0x60,
++ PCF50633_ADCC1_MUX_ADCIN1 = 0x70,
++};
++#define PCF50633_ADCC1_AVERAGE_MASK 0x0c
++#define PCF50633_ADCC1_ADCMUX_MASK 0xf0
++
++enum pcf50633_reg_adcc2 {
++ PCF50633_ADCC2_RATIO_NONE = 0x00,
++ PCF50633_ADCC2_RATIO_BATTEMP = 0x01,
++ PCF50633_ADCC2_RATIO_ADCIN1 = 0x02,
++ PCF50633_ADCC2_RATIO_BOTH = 0x03,
++ PCF50633_ADCC2_RATIOSETTL_100US = 0x04,
++};
++#define PCF50633_ADCC2_RATIO_MASK 0x03
++
++enum pcf50633_reg_adcc3 {
++ PCF50633_ADCC3_ACCSW_EN = 0x01,
++ PCF50633_ADCC3_NTCSW_EN = 0x04,
++ PCF50633_ADCC3_RES_DIV_TWO = 0x10,
++ PCF50633_ADCC3_RES_DIV_THREE = 0x00,
++};
++
++enum pcf50633_reg_adcs3 {
++ PCF50633_ADCS3_REF_NTCSW = 0x00,
++ PCF50633_ADCS3_REF_ACCSW = 0x10,
++ PCF50633_ADCS3_REF_2V0 = 0x20,
++ PCF50633_ADCS3_REF_VISA = 0x30,
++ PCF50633_ADCS3_REF_2V0_2 = 0x70,
++ PCF50633_ADCS3_ADCRDY = 0x80,
++};
++#define PCF50633_ADCS3_ADCDAT1L_MASK 0x03
++#define PCF50633_ADCS3_ADCDAT2L_MASK 0x0c
++#define PCF50633_ADCS3_ADCDAT2L_SHIFT 2
++#define PCF50633_ASCS3_REF_MASK 0x70
++
++enum pcf50633_regulator_enable {
++ PCF50633_REGULATOR_ON = 0x01,
++ PCF50633_REGULATOR_ON_GPIO1 = 0x02,
++ PCF50633_REGULATOR_ON_GPIO2 = 0x04,
++ PCF50633_REGULATOR_ON_GPIO3 = 0x08,
++};
++#define PCF50633_REGULATOR_ON_MASK 0x0f
++
++enum pcf50633_regulator_phase {
++ PCF50633_REGULATOR_ACTPH1 = 0x00,
++ PCF50633_REGULATOR_ACTPH2 = 0x10,
++ PCF50633_REGULATOR_ACTPH3 = 0x20,
++ PCF50633_REGULATOR_ACTPH4 = 0x30,
++};
++#define PCF50633_REGULATOR_ACTPH_MASK 0x30
++
++enum pcf50633_reg_gpocfg {
++ PCF50633_GPOCFG_GPOSEL_0 = 0x00,
++ PCF50633_GPOCFG_GPOSEL_LED_NFET = 0x01,
++ PCF50633_GPOCFG_GPOSEL_SYSxOK = 0x02,
++ PCF50633_GPOCFG_GPOSEL_CLK32K = 0x03,
++ PCF50633_GPOCFG_GPOSEL_ADAPUSB = 0x04,
++ PCF50633_GPOCFG_GPOSEL_USBxOK = 0x05,
++ PCF50633_GPOCFG_GPOSEL_ACTPH4 = 0x06,
++ PCF50633_GPOCFG_GPOSEL_1 = 0x07,
++ PCF50633_GPOCFG_GPOSEL_INVERSE = 0x08,
++};
++#define PCF50633_GPOCFG_GPOSEL_MASK 0x07
++
++#if 0
++enum pcf50633_reg_mbcc1 {
++ PCF50633_MBCC1_CHGENA = 0x01,
++ PCF50633_MBCC1_AUTOSTOP = 0x02,
++ PCF50633_MBCC1_AUTORES = 0x04,
++ PCF50633_MBCC1_RESUME = 0x08,
++ PCF50633_MBCC1_RESTART = 0x10,
++ PCF50633_MBCC1_PREWDTIME_30MIN = 0x00,
++ PCF50633_MBCC1_PREWDTIME_60MIN = 0x20,
++ PCF50633_MBCC1_WDTIME_2HRS = 0x40,
++ PCF50633_MBCC1_WDTIME_4HRS = 0x80,
++ PCF50633_MBCC1_WDTIME_6HRS = 0xc0,
++};
++
++enum pcf50633_reg_mbcc2 {
++ PCF50633_MBCC2_VBATCOND_2V7 = 0x00,
++ PCF50633_MBCC2_VBATCOND_2V85 = 0x01,
++ PCF50633_MBCC2_VBATCOND_3V0 = 0x02,
++ PCF50633_MBCC2_VBATCOND_3V15 = 0x03,
++ PCF50633_MBCC2_VRESDEBTIME_64S = 0x80,
++};
++#define PCF50633_MBCC2_VMAX_MASK 0x3c
++#endif
++
++enum pcf50633_reg_mbcc7 {
++ PCF50633_MBCC7_USB_100mA = 0x00,
++ PCF50633_MBCC7_USB_500mA = 0x01,
++ PCF50633_MBCC7_USB_1000mA = 0x02,
++ PCF50633_MBCC7_USB_SUSPEND = 0x03,
++ PCF50633_MBCC7_BATTEMP_EN = 0x04,
++ PCF50633_MBCC7_BATSYSIMAX_1A6 = 0x00,
++ PCF50633_MBCC7_BATSYSIMAX_1A8 = 0x40,
++ PCF50633_MBCC7_BATSYSIMAX_2A0 = 0x80,
++ PCF50633_MBCC7_BATSYSIMAX_2A2 = 0xc0,
++};
++#define PCF56033_MBCC7_USB_MASK 0x03
++
++enum pcf50633_reg_mbcc8 {
++ PCF50633_MBCC8_USBENASUS = 0x10,
++};
++
++enum pcf50633_reg_mbcs1 {
++ PCF50633_MBCS1_USBPRES = 0x01,
++ PCF50633_MBCS1_USBOK = 0x02,
++ PCF50633_MBCS1_ADAPTPRES = 0x04,
++ PCF50633_MBCS1_ADAPTOK = 0x08,
++ PCF50633_MBCS1_TBAT_OK = 0x00,
++ PCF50633_MBCS1_TBAT_ABOVE = 0x10,
++ PCF50633_MBCS1_TBAT_BELOW = 0x20,
++ PCF50633_MBCS1_TBAT_UNDEF = 0x30,
++ PCF50633_MBCS1_PREWDTEXP = 0x40,
++ PCF50633_MBCS1_WDTEXP = 0x80,
++};
++
++enum pcf50633_reg_mbcs2_mbcmod {
++ PCF50633_MBCS2_MBC_PLAY = 0x00,
++ PCF50633_MBCS2_MBC_USB_PRE = 0x01,
++ PCF50633_MBCS2_MBC_USB_PRE_WAIT = 0x02,
++ PCF50633_MBCS2_MBC_USB_FAST = 0x03,
++ PCF50633_MBCS2_MBC_USB_FAST_WAIT= 0x04,
++ PCF50633_MBCS2_MBC_USB_SUSPEND = 0x05,
++ PCF50633_MBCS2_MBC_ADP_PRE = 0x06,
++ PCF50633_MBCS2_MBC_ADP_PRE_WAIT = 0x07,
++ PCF50633_MBCS2_MBC_ADP_FAST = 0x08,
++ PCF50633_MBCS2_MBC_ADP_FAST_WAIT= 0x09,
++ PCF50633_MBCS2_MBC_BAT_FULL = 0x0a,
++ PCF50633_MBCS2_MBC_HALT = 0x0b,
++};
++#define PCF50633_MBCS2_MBC_MASK 0x0f
++enum pcf50633_reg_mbcs2_chgstat {
++ PCF50633_MBCS2_CHGS_NONE = 0x00,
++ PCF50633_MBCS2_CHGS_ADAPTER = 0x10,
++ PCF50633_MBCS2_CHGS_USB = 0x20,
++ PCF50633_MBCS2_CHGS_BOTH = 0x30,
++};
++#define PCF50633_MBCS2_RESSTAT_AUTO 0x40
++
++enum pcf50633_reg_mbcs3 {
++ PCF50633_MBCS3_USBLIM_PLAY = 0x01,
++ PCF50633_MBCS3_USBLIM_CGH = 0x02,
++ PCF50633_MBCS3_TLIM_PLAY = 0x04,
++ PCF50633_MBCS3_TLIM_CHG = 0x08,
++ PCF50633_MBCS3_ILIM = 0x10, /* 1: Ibat > Icutoff */
++ PCF50633_MBCS3_VLIM = 0x20, /* 1: Vbat == Vmax */
++ PCF50633_MBCS3_VBATSTAT = 0x40, /* 1: Vbat > Vbatcond */
++ PCF50633_MBCS3_VRES = 0x80, /* 1: Vbat > Vth(RES) */
++};
++
++/* this is to be provided by the board implementation */
++extern const u_int8_t pcf50633_initial_regs[__NUM_PCF50633_REGS];
++
++void pcf50633_reg_write(u_int8_t reg, u_int8_t val);
++
++u_int8_t pcf50633_reg_read(u_int8_t reg);
++
++void pcf50633_reg_set_bit_mask(u_int8_t reg, u_int8_t mask, u_int8_t val);
++void pcf50633_reg_clear_bits(u_int8_t reg, u_int8_t bits);
++
++void pcf50633_charge_autofast(int on);
++
++#endif /* _PCF50606_H */
++
Added: developers/nbd/patches-2.6.22/480-smedia-glamo.patch
===================================================================
--- developers/nbd/patches-2.6.22/480-smedia-glamo.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/480-smedia-glamo.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,2955 @@
+Index: linux-2.6.22.1/drivers/video/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/video/Kconfig 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/drivers/video/Kconfig 2007-07-16 15:18:37.046059661 +0200
+@@ -1820,6 +1820,38 @@
+ framebuffer. ML300 carries a 640*480 LCD display on the board,
+ ML403 uses a standard DB15 VGA connector.
+
++config GLAMO
++ bool
++
++config FB_GLAMO
++ tristate "Smedia Glamo 336x/337x framebuffer support"
++ depends on FB
++ select GLAMO
++ help
++ Frame buffer driver for the LCD controller in the Smedia Glamo
++ 336x/337x.
++
++ This driver is also available as a module ( = code which can be
++ inserted and removed from the running kernel whenever you want). The
++ module will be called glamofb. If you want to compile it as a module,
++ say M here and read <file:Documentation/modules.txt>.
++
++ If unsure, say N.
++
++config GLAMO_SPI_GPIO
++ bool "Glamo GPIO SPI bitbang support"
++ depends on GLAMO
++ help
++ Enable a bitbanging SPI adapter driver for the Smedia Glamo.
++
++config FB_GLAMO_SPI
++ bool "Glamo LCM control channel SPI support"
++ depends on FB_GLAMO
++ help
++ Enable a bitbanging SPI adapter driver for the Smedia Glamo LCM
++ control channel. This SPI interface is frequently used to
++ interconnect the LCM control interface.
++
+ config FB_VIRTUAL
+ tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
+ depends on FB
+Index: linux-2.6.22.1/drivers/video/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/video/Makefile 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/drivers/video/Makefile 2007-07-16 15:19:37.713516901 +0200
+@@ -113,6 +113,7 @@
+ obj-$(CONFIG_FB_PS3) += ps3fb.o
+ obj-$(CONFIG_FB_SM501) += sm501fb.o
+ obj-$(CONFIG_FB_XILINX) += xilinxfb.o
++obj-$(CONFIG_GLAMO) += glamo/
+
+ # Platform or fallback drivers go here
+ obj-$(CONFIG_FB_VESA) += vesafb.o
+Index: linux-2.6.22.1/drivers/video/glamo/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/Makefile 2007-07-16 15:18:37.078061486 +0200
+@@ -0,0 +1,11 @@
++#
++# Makefile for the Smedia Glamo framebuffer driver
++#
++
++obj-$(CONFIG_GLAMO) += glamo-core.o glamo-gpio.o
++obj-$(CONFIG_GLAMO_SPI) += glamo-spi.o
++obj-$(CONFIG_GLAMO_SPI_GPIO) += glamo-spi-gpio.o
++
++obj-$(CONFIG_FB_GLAMO) += glamo-fb.o
++obj-$(CONFIG_FB_GLAMO_SPI) += glamo-lcm-spi.o
++
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-regs.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-regs.h 2007-07-16 15:18:37.098062625 +0200
+@@ -0,0 +1,466 @@
++#ifndef _GLAMO_REGS_H
++#define _GLAMO_REGS_H
++
++/* Smedia Glamo 336x/337x driver
++ *
++ * (C) 2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++enum glamo_regster_offsets {
++ GLAMO_REGOFS_GENERIC = 0x0000,
++ GLAMO_REGOFS_HOSTBUS = 0x0200,
++ GLAMO_REGOFS_MEMORY = 0x0300,
++ GLAMO_REGOFS_VIDCAP = 0x0400,
++ GLAMO_REGOFS_ISP = 0x0500,
++ GLAMO_REGOFS_JPEG = 0x0800,
++ GLAMO_REGOFS_MPEG = 0x0c00,
++ GLAMO_REGOFS_LCD = 0x1100,
++ GLAMO_REGOFS_MMC = 0x1400,
++ GLAMO_REGOFS_MPROC0 = 0x1500,
++ GLAMO_REGOFS_MPROC1 = 0x1580,
++ GLAMO_REGOFS_CMDQUEUE = 0x1600,
++ GLAMO_REGOFS_RISC = 0x1680,
++ GLAMO_REGOFS_2D = 0x1700,
++ GLAMO_REGOFS_3D = 0x1b00,
++};
++
++
++enum glamo_register_generic {
++ GLAMO_REG_GCONF1 = 0x0000,
++ GLAMO_REG_GCONF2 = 0x0002,
++#define GLAMO_REG_DEVICE_ID GLAMO_REG_GCONF2
++ GLAMO_REG_GCONF3 = 0x0004,
++#define GLAMO_REG_REVISION_ID GLAMO_REG_GCONF3
++ GLAMO_REG_IRQ_GEN1 = 0x0006,
++#define GLAMO_REG_IRQ_ENABLE GLAMO_REG_IRQ_GEN1
++ GLAMO_REG_IRQ_GEN2 = 0x0008,
++#define GLAMO_REG_IRQ_SET GLAMO_REG_IRQ_GEN2
++ GLAMO_REG_IRQ_GEN3 = 0x000a,
++#define GLAMO_REG_IRQ_CLEAR GLAMO_REG_IRQ_GEN3
++ GLAMO_REG_IRQ_GEN4 = 0x000c,
++#define GLAMO_REG_IRQ_STATUS GLAMO_REG_IRQ_GEN4
++ GLAMO_REG_CLOCK_HOST = 0x0010,
++ GLAMO_REG_CLOCK_MEMORY = 0x0012,
++ GLAMO_REG_CLOCK_LCD = 0x0014,
++ GLAMO_REG_CLOCK_MMC = 0x0016,
++ GLAMO_REG_CLOCK_ISP = 0x0018,
++ GLAMO_REG_CLOCK_JPEG = 0x001a,
++ GLAMO_REG_CLOCK_3D = 0x001c,
++ GLAMO_REG_CLOCK_2D = 0x001e,
++ GLAMO_REG_CLOCK_RISC1 = 0x0020, /* 3365 only? */
++ GLAMO_REG_CLOCK_RISC2 = 0x0022, /* 3365 only? */
++ GLAMO_REG_CLOCK_MPEG = 0x0024,
++ GLAMO_REG_CLOCK_MPROC = 0x0026,
++
++ GLAMO_REG_CLOCK_GEN5_1 = 0x0030,
++ GLAMO_REG_CLOCK_GEN5_2 = 0x0032,
++ GLAMO_REG_CLOCK_GEN6 = 0x0034,
++ GLAMO_REG_CLOCK_GEN7 = 0x0036,
++ GLAMO_REG_CLOCK_GEN8 = 0x0038,
++ GLAMO_REG_CLOCK_GEN9 = 0x003a,
++ GLAMO_REG_CLOCK_GEN10 = 0x003c,
++ GLAMO_REG_CLOCK_GEN11 = 0x003e,
++ GLAMO_REG_PLL_GEN1 = 0x0040,
++ GLAMO_REG_PLL_GEN2 = 0x0042,
++ GLAMO_REG_PLL_GEN3 = 0x0044,
++ GLAMO_REG_PLL_GEN4 = 0x0046,
++ GLAMO_REG_PLL_GEN5 = 0x0048,
++ GLAMO_REG_GPIO_GEN1 = 0x0050,
++ GLAMO_REG_GPIO_GEN2 = 0x0052,
++ GLAMO_REG_GPIO_GEN3 = 0x0054,
++ GLAMO_REG_GPIO_GEN4 = 0x0056,
++ GLAMO_REG_GPIO_GEN5 = 0x0058,
++ GLAMO_REG_GPIO_GEN6 = 0x005a,
++ GLAMO_REG_GPIO_GEN7 = 0x005c,
++ GLAMO_REG_GPIO_GEN8 = 0x005e,
++ GLAMO_REG_GPIO_GEN9 = 0x0060,
++ GLAMO_REG_GPIO_GEN10 = 0x0062,
++ GLAMO_REG_DFT_GEN1 = 0x0070,
++ GLAMO_REG_DFT_GEN2 = 0x0072,
++ GLAMO_REG_DFT_GEN3 = 0x0074,
++ GLAMO_REG_DFT_GEN4 = 0x0076,
++
++ GLAMO_REG_DFT_GEN5 = 0x01e0,
++ GLAMO_REG_DFT_GEN6 = 0x01f0,
++};
++
++#define GLAMO_REG_HOSTBUS(x) (GLAMO_REGOFS_HOSTBUS-2+(x*2))
++
++#define REG_MEM(x) (GLAMO_REGOFS_MEMORY+(x))
++#define GLAMO_REG_MEM_TIMING(x) (GLAMO_REG_MEM_TIMING1-2+(x*2))
++
++enum glamo_register_mem {
++ GLAMO_REG_MEM_TYPE = REG_MEM(0x00),
++ GLAMO_REG_MEM_GEN = REG_MEM(0x02),
++ GLAMO_REG_MEM_TIMING1 = REG_MEM(0x04),
++ GLAMO_REG_MEM_TIMING2 = REG_MEM(0x06),
++ GLAMO_REG_MEM_TIMING3 = REG_MEM(0x08),
++ GLAMO_REG_MEM_TIMING4 = REG_MEM(0x0a),
++ GLAMO_REG_MEM_TIMING5 = REG_MEM(0x0c),
++ GLAMO_REG_MEM_TIMING6 = REG_MEM(0x0e),
++ GLAMO_REG_MEM_TIMING7 = REG_MEM(0x10),
++ GLAMO_REG_MEM_TIMING8 = REG_MEM(0x12),
++ GLAMO_REG_MEM_TIMING9 = REG_MEM(0x14),
++ GLAMO_REG_MEM_TIMING10 = REG_MEM(0x16),
++ GLAMO_REG_MEM_TIMING11 = REG_MEM(0x18),
++ GLAMO_REG_MEM_POWER1 = REG_MEM(0x1a),
++ GLAMO_REG_MEM_POWER2 = REG_MEM(0x1c),
++ GLAMO_REG_MEM_LCD_BUF1 = REG_MEM(0x1e),
++ GLAMO_REG_MEM_LCD_BUF2 = REG_MEM(0x20),
++ GLAMO_REG_MEM_LCD_BUF3 = REG_MEM(0x22),
++ GLAMO_REG_MEM_LCD_BUF4 = REG_MEM(0x24),
++ GLAMO_REG_MEM_BIST1 = REG_MEM(0x26),
++ GLAMO_REG_MEM_BIST2 = REG_MEM(0x28),
++ GLAMO_REG_MEM_BIST3 = REG_MEM(0x2a),
++ GLAMO_REG_MEM_BIST4 = REG_MEM(0x2c),
++ GLAMO_REG_MEM_BIST5 = REG_MEM(0x2e),
++ GLAMO_REG_MEM_MAH1 = REG_MEM(0x30),
++ GLAMO_REG_MEM_MAH2 = REG_MEM(0x32),
++ GLAMO_REG_MEM_DRAM1 = REG_MEM(0x34),
++ GLAMO_REG_MEM_DRAM2 = REG_MEM(0x36),
++ GLAMO_REG_MEM_CRC = REG_MEM(0x38),
++};
++
++#define GLAMO_MEM_TYPE_MASK 0x03
++
++enum glamo_reg_mem_dram1 {
++ GLAMO_MEM_DRAM1_EN_SDRAM_CLK = (1 << 11),
++ GLAMO_MEM_DRAM1_SELF_REFRESH = (1 << 12),
++};
++
++enum glamo_reg_mem_dram2 {
++ GLAMO_MEM_DRAM2_DEEP_PWRDOWN = (1 << 12),
++};
++
++enum glamo_irq {
++ GLAMO_IRQ_HOSTBUS = 0x0001,
++ GLAMO_IRQ_JPEG = 0x0002,
++ GLAMO_IRQ_MPEG = 0x0004,
++ GLAMO_IRQ_MPROC1 = 0x0008,
++ GLAMO_IRQ_MPROC0 = 0x0010,
++ GLAMO_IRQ_CMDQUEUE = 0x0020,
++ GLAMO_IRQ_2D = 0x0040,
++ GLAMO_IRQ_MMC = 0x0080,
++ GLAMO_IRQ_RISC = 0x0100,
++};
++
++enum glamo_reg_clock_host {
++ GLAMO_CLOCK_HOST_DG_BCLK = 0x0001,
++ GLAMO_CLOCK_HOST_DG_M0CLK = 0x0004,
++ GLAMO_CLOCK_HOST_RESET = 0x1000,
++};
++
++enum glamo_reg_clock_mem {
++ GLAMO_CLOCK_MEM_DG_M1CLK = 0x0001,
++ GLAMO_CLOCK_MEM_EN_M1CLK = 0x0002,
++ GLAMO_CLOCK_MEM_DG_MOCACLK = 0x0004,
++ GLAMO_CLOCK_MEM_EN_MOCACLK = 0x0008,
++ GLAMO_CLOCK_MEM_RESET = 0x1000,
++ GLAMO_CLOCK_MOCA_RESET = 0x2000,
++};
++
++enum glamo_reg_clock_lcd {
++ GLAMO_CLOCK_LCD_DG_DCLK = 0x0001,
++ GLAMO_CLOCK_LCD_EN_DCLK = 0x0002,
++ GLAMO_CLOCK_LCD_DG_DMCLK = 0x0004,
++ GLAMO_CLOCK_LCD_EN_DMCLK = 0x0008,
++ //
++ GLAMO_CLOCK_LCD_EN_DHCLK = 0x0020,
++ GLAMO_CLOCK_LCD_DG_M5CLK = 0x0040,
++ GLAMO_CLOCK_LCD_EN_M5CLK = 0x0080,
++ GLAMO_CLOCK_LCD_RESET = 0x1000,
++};
++
++enum glamo_reg_clock_mmc {
++ GLAMO_CLOCK_MMC_DG_TCLK = 0x0001,
++ GLAMO_CLOCK_MMC_EN_TCLK = 0x0002,
++ GLAMO_CLOCK_MMC_DG_M9CLK = 0x0004,
++ GLAMO_CLOCK_MMC_EN_M9CLK = 0x0008,
++ GLAMO_CLOCK_MMC_RESET = 0x1000,
++};
++
++enum glamo_reg_clock_isp {
++ GLAMO_CLOCK_ISP_DG_I1CLK = 0x0001,
++ GLAMO_CLOCK_ISP_EN_I1CLK = 0x0002,
++ GLAMO_CLOCK_ISP_DG_CCLK = 0x0004,
++ GLAMO_CLOCK_ISP_EN_CCLK = 0x0008,
++ //
++ GLAMO_CLOCK_ISP_EN_SCLK = 0x0020,
++ GLAMO_CLOCK_ISP_DG_M2CLK = 0x0040,
++ GLAMO_CLOCK_ISP_EN_M2CLK = 0x0080,
++ GLAMO_CLOCK_ISP_DG_M15CLK = 0x0100,
++ GLAMO_CLOCK_ISP_EN_M15CLK = 0x0200,
++ GLAMO_CLOCK_ISP1_RESET = 0x1000,
++ GLAMO_CLOCK_ISP2_RESET = 0x2000,
++};
++
++enum glamo_reg_clock_jpeg {
++ GLAMO_CLOCK_JPEG_DG_JCLK = 0x0001,
++ GLAMO_CLOCK_JPEG_EN_JCLK = 0x0002,
++ GLAMO_CLOCK_JPEG_DG_M3CLK = 0x0004,
++ GLAMO_CLOCK_JPEG_EN_M3CLK = 0x0008,
++ GLAMO_CLOCK_JPEG_RESET = 0x1000,
++};
++
++enum glamo_reg_clock_2d {
++ GLAMO_CLOCK_2D_DG_GCLK = 0x0001,
++ GLAMO_CLOCK_2D_EN_GCLK = 0x0002,
++ GLAMO_CLOCK_2D_DG_M7CLK = 0x0004,
++ GLAMO_CLOCK_2D_EN_M7CLK = 0x0008,
++ GLAMO_CLOCK_2D_DG_M6CLK = 0x0010,
++ GLAMO_CLOCK_2D_EN_M6CLK = 0x0020,
++ GLAMO_CLOCK_2D_RESET = 0x1000,
++ GLAMO_CLOCK_2D_CQ_RESET = 0x2000,
++};
++
++enum glamo_reg_clock_3d {
++ GLAMO_CLOCK_3D_DG_ECLK = 0x0001,
++ GLAMO_CLOCK_3D_EN_ECLK = 0x0002,
++ GLAMO_CLOCK_3D_DG_RCLK = 0x0004,
++ GLAMO_CLOCK_3D_EN_RCLK = 0x0008,
++ GLAMO_CLOCK_3D_DG_M8CLK = 0x0010,
++ GLAMO_CLOCK_3D_EN_M8CLK = 0x0020,
++ GLAMO_CLOCK_3D_BACK_RESET = 0x1000,
++ GLAMO_CLOCK_3D_FRONT_RESET = 0x2000,
++};
++
++enum glamo_reg_clock_mpeg {
++ GLAMO_CLOCK_MPEG_DG_X0CLK = 0x0001,
++ GLAMO_CLOCK_MPEG_EN_X0CLK = 0x0002,
++ GLAMO_CLOCK_MPEG_DG_X1CLK = 0x0004,
++ GLAMO_CLOCK_MPEG_EN_X1CLK = 0x0008,
++ GLAMO_CLOCK_MPEG_DG_X2CLK = 0x0010,
++ GLAMO_CLOCK_MPEG_EN_X2CLK = 0x0020,
++ GLAMO_CLOCK_MPEG_DG_X3CLK = 0x0040,
++ GLAMO_CLOCK_MPEG_EN_X3CLK = 0x0080,
++ GLAMO_CLOCK_MPEG_DG_X4CLK = 0x0100,
++ GLAMO_CLOCK_MPEG_EN_X4CLK = 0x0200,
++ GLAMO_CLOCK_MPEG_DG_X6CLK = 0x0400,
++ GLAMO_CLOCK_MPEG_EN_X6CLK = 0x0800,
++ GLAMO_CLOCK_MPEG_ENC_RESET = 0x1000,
++ GLAMO_CLOCK_MPEG_DEC_RESET = 0x2000,
++};
++
++enum glamo_reg_clock51 {
++ GLAMO_CLOCK_GEN51_EN_DIV_MCLK = 0x0001,
++ GLAMO_CLOCK_GEN51_EN_DIV_SCLK = 0x0002,
++ GLAMO_CLOCK_GEN51_EN_DIV_JCLK = 0x0004,
++ GLAMO_CLOCK_GEN51_EN_DIV_DCLK = 0x0008,
++ GLAMO_CLOCK_GEN51_EN_DIV_DMCLK = 0x0010,
++ GLAMO_CLOCK_GEN51_EN_DIV_DHCLK = 0x0020,
++ GLAMO_CLOCK_GEN51_EN_DIV_GCLK = 0x0040,
++ GLAMO_CLOCK_GEN51_EN_DIV_TCLK = 0x0080,
++ /* FIXME: higher bits */
++};
++
++enum glamo_reg_hostbus2 {
++ GLAMO_HOSTBUS2_MMIO_EN_ISP = 0x0001,
++ GLAMO_HOSTBUS2_MMIO_EN_JPEG = 0x0002,
++ GLAMO_HOSTBUS2_MMIO_EN_MPEG = 0x0004,
++ GLAMO_HOSTBUS2_MMIO_EN_LCD = 0x0008,
++ GLAMO_HOSTBUS2_MMIO_EN_MMC = 0x0010,
++ GLAMO_HOSTBUS2_MMIO_EN_MICROP0 = 0x0020,
++ GLAMO_HOSTBUS2_MMIO_EN_MICROP1 = 0x0040,
++ GLAMO_HOSTBUS2_MMIO_EN_CQ = 0x0080,
++ GLAMO_HOSTBUS2_MMIO_EN_RISC = 0x0100,
++ GLAMO_HOSTBUS2_MMIO_EN_2D = 0x0200,
++ GLAMO_HOSTBUS2_MMIO_EN_3D = 0x0400,
++};
++
++/* LCD Controller */
++
++#define REG_LCD(x) (x)
++enum glamo_reg_lcd {
++ GLAMO_REG_LCD_MODE1 = REG_LCD(0x00),
++ GLAMO_REG_LCD_MODE2 = REG_LCD(0x02),
++ GLAMO_REG_LCD_MODE3 = REG_LCD(0x04),
++ GLAMO_REG_LCD_WIDTH = REG_LCD(0x06),
++ GLAMO_REG_LCD_HEIGHT = REG_LCD(0x08),
++ GLAMO_REG_LCD_POLARITY = REG_LCD(0x0a),
++ GLAMO_REG_LCD_A_BASE1 = REG_LCD(0x0c),
++ GLAMO_REG_LCD_A_BASE2 = REG_LCD(0x0e),
++ GLAMO_REG_LCD_B_BASE1 = REG_LCD(0x10),
++ GLAMO_REG_LCD_B_BASE2 = REG_LCD(0x12),
++ GLAMO_REG_LCD_C_BASE1 = REG_LCD(0x14),
++ GLAMO_REG_LCD_C_BASE2 = REG_LCD(0x16),
++ GLAMO_REG_LCD_PITCH = REG_LCD(0x18),
++ /* RES */
++ GLAMO_REG_LCD_HORIZ_TOTAL = REG_LCD(0x1c),
++ /* RES */
++ GLAMO_REG_LCD_HORIZ_RETR_START = REG_LCD(0x20),
++ /* RES */
++ GLAMO_REG_LCD_HORIZ_RETR_END = REG_LCD(0x24),
++ /* RES */
++ GLAMO_REG_LCD_HORIZ_DISP_START = REG_LCD(0x28),
++ /* RES */
++ GLAMO_REG_LCD_HORIZ_DISP_END = REG_LCD(0x2c),
++ /* RES */
++ GLAMO_REG_LCD_VERT_TOTAL = REG_LCD(0x30),
++ /* RES */
++ GLAMO_REG_LCD_VERT_RETR_START = REG_LCD(0x34),
++ /* RES */
++ GLAMO_REG_LCD_VERT_RETR_END = REG_LCD(0x38),
++ /* RES */
++ GLAMO_REG_LCD_VERT_DISP_START = REG_LCD(0x3c),
++ /* RES */
++ GLAMO_REG_LCD_VERT_DISP_END = REG_LCD(0x40),
++ /* RES */
++ GLAMO_REG_LCD_POL = REG_LCD(0x44),
++ GLAMO_REG_LCD_DATA_START = REG_LCD(0x46),
++ GLAMO_REG_LCD_FRATE_CONTRO = REG_LCD(0x48),
++ GLAMO_REG_LCD_DATA_CMD_HDR = REG_LCD(0x4a),
++ GLAMO_REG_LCD_SP_START = REG_LCD(0x4c),
++ GLAMO_REG_LCD_SP_END = REG_LCD(0x4e),
++ GLAMO_REG_LCD_CURSOR_BASE1 = REG_LCD(0x50),
++ GLAMO_REG_LCD_CURSOR_BASE2 = REG_LCD(0x52),
++ GLAMO_REG_LCD_CURSOR_PITCH = REG_LCD(0x54),
++ GLAMO_REG_LCD_CURSOR_X_SIZE = REG_LCD(0x56),
++ GLAMO_REG_LCD_CURSOR_Y_SIZE = REG_LCD(0x58),
++ GLAMO_REG_LCD_CURSOR_X_POS = REG_LCD(0x5a),
++ GLAMO_REG_LCD_CURSOR_Y_POS = REG_LCD(0x5c),
++ GLAMO_REG_LCD_CURSOR_PRESET = REG_LCD(0x5e),
++ GLAMO_REG_LCD_CURSOR_FG_COLOR = REG_LCD(0x60),
++ /* RES */
++ GLAMO_REG_LCD_CURSOR_BG_COLOR = REG_LCD(0x64),
++ /* RES */
++ GLAMO_REG_LCD_CURSOR_DST_COLOR = REG_LCD(0x68),
++ /* RES */
++ GLAMO_REG_LCD_STATUS1 = REG_LCD(0x80),
++ GLAMO_REG_LCD_STATUS2 = REG_LCD(0x82),
++ GLAMO_REG_LCD_STATUS3 = REG_LCD(0x84),
++ GLAMO_REG_LCD_STATUS4 = REG_LCD(0x86),
++ /* RES */
++ GLAMO_REG_LCD_COMMAND1 = REG_LCD(0xa0),
++ GLAMO_REG_LCD_COMMAND2 = REG_LCD(0xa2),
++ /* RES */
++ GLAMO_REG_LCD_WFORM_DELAY1 = REG_LCD(0xb0),
++ GLAMO_REG_LCD_WFORM_DELAY2 = REG_LCD(0xb2),
++ /* RES */
++ GLAMO_REG_LCD_GAMMA_CORR = REG_LCD(0x100),
++ /* RES */
++ GLAMO_REG_LCD_GAMMA_R_ENTRY01 = REG_LCD(0x110),
++ GLAMO_REG_LCD_GAMMA_R_ENTRY23 = REG_LCD(0x112),
++ GLAMO_REG_LCD_GAMMA_R_ENTRY45 = REG_LCD(0x114),
++ GLAMO_REG_LCD_GAMMA_R_ENTRY67 = REG_LCD(0x116),
++ GLAMO_REG_LCD_GAMMA_R_ENTRY8 = REG_LCD(0x118),
++ /* RES */
++ GLAMO_REG_LCD_GAMMA_G_ENTRY01 = REG_LCD(0x130),
++ GLAMO_REG_LCD_GAMMA_G_ENTRY23 = REG_LCD(0x132),
++ GLAMO_REG_LCD_GAMMA_G_ENTRY45 = REG_LCD(0x134),
++ GLAMO_REG_LCD_GAMMA_G_ENTRY67 = REG_LCD(0x136),
++ GLAMO_REG_LCD_GAMMA_G_ENTRY8 = REG_LCD(0x138),
++ /* RES */
++ GLAMO_REG_LCD_GAMMA_B_ENTRY01 = REG_LCD(0x150),
++ GLAMO_REG_LCD_GAMMA_B_ENTRY23 = REG_LCD(0x152),
++ GLAMO_REG_LCD_GAMMA_B_ENTRY45 = REG_LCD(0x154),
++ GLAMO_REG_LCD_GAMMA_B_ENTRY67 = REG_LCD(0x156),
++ GLAMO_REG_LCD_GAMMA_B_ENTRY8 = REG_LCD(0x158),
++ /* RES */
++ GLAMO_REG_LCD_SRAM_DRIVING1 = REG_LCD(0x160),
++ GLAMO_REG_LCD_SRAM_DRIVING2 = REG_LCD(0x162),
++ GLAMO_REG_LCD_SRAM_DRIVING3 = REG_LCD(0x164),
++};
++
++enum glamo_reg_lcd_mode1 {
++ GLAMO_LCD_MODE1_PWRSAVE = 0x0001,
++ GLAMO_LCD_MODE1_PARTIAL_PRT = 0x0002,
++ GLAMO_LCD_MODE1_HWFLIP = 0x0004,
++ GLAMO_LCD_MODE1_LCD2 = 0x0008,
++ /* RES */
++ GLAMO_LCD_MODE1_PARTIAL_MODE = 0x0020,
++ GLAMO_LCD_MODE1_CURSOR_DSTCOLOR = 0x0040,
++ GLAMO_LCD_MODE1_PARTIAL_ENABLE = 0x0080,
++ GLAMO_LCD_MODE1_TVCLK_IN_ENABLE = 0x0100,
++ GLAMO_LCD_MODE1_HSYNC_HIGH_ACT = 0x0200,
++ GLAMO_LCD_MODE1_VSYNC_HIGH_ACT = 0x0400,
++ GLAMO_LCD_MODE1_HSYNC_FLIP = 0x0800,
++ GLAMO_LCD_MODE1_GAMMA_COR_EN = 0x1000,
++ GLAMO_LCD_MODE1_DITHER_EN = 0x2000,
++ GLAMO_LCD_MODE1_CURSOR_EN = 0x4000,
++ GLAMO_LCD_MODE1_ROTATE_EN = 0x8000,
++};
++
++enum glamo_reg_lcd_mode2 {
++ GLAMO_LCD_MODE2_CRC_CHECK_EN = 0x0001,
++ GLAMO_LCD_MODE2_DCMD_PER_LINE = 0x0002,
++ GLAMO_LCD_MODE2_NOUSE_BDEF = 0x0004,
++ GLAMO_LCD_MODE2_OUT_POS_MODE = 0x0008,
++ GLAMO_LCD_MODE2_FRATE_CTRL_EN = 0x0010,
++ GLAMO_LCD_MODE2_SINGLE_BUFFER = 0x0020,
++ GLAMO_LCD_MODE2_SER_LSB_TO_MSB = 0x0040,
++ /* FIXME */
++};
++
++enum glamo_reg_lcd_mode3 {
++ /* LCD color source data format */
++ GLAMO_LCD_SRC_RGB565 = 0x0000,
++ GLAMO_LCD_SRC_ARGB1555 = 0x4000,
++ GLAMO_LCD_SRC_ARGB4444 = 0x8000,
++ /* interface type */
++ GLAMO_LCD_MODE3_LCD = 0x1000,
++ GLAMO_LCD_MODE3_RGB = 0x0800,
++ GLAMO_LCD_MODE3_CPU = 0x0000,
++ /* mode */
++ GLAMO_LCD_MODE3_RGB332 = 0x0000,
++ GLAMO_LCD_MODE3_RGB444 = 0x0100,
++ GLAMO_LCD_MODE3_RGB565 = 0x0200,
++ GLAMO_LCD_MODE3_RGB666 = 0x0300,
++ /* depth */
++ GLAMO_LCD_MODE3_6BITS = 0x0000,
++ GLAMO_LCD_MODE3_8BITS = 0x0010,
++ GLAMO_LCD_MODE3_9BITS = 0x0020,
++ GLAMO_LCD_MODE3_16BITS = 0x0030,
++ GLAMO_LCD_MODE3_18BITS = 0x0040,
++};
++
++enum glamo_lcd_cmd_type {
++ GLAMO_LCD_CMD_TYPE_DISP = 0x0000,
++ GLAMO_LCD_CMD_TYPE_PARALLEL = 0x4000,
++ GLAMO_LCD_CMD_TYPE_SERIAL = 0x8000,
++ GLAMO_LCD_CMD_TYPE_SERIAL_DIRECT= 0xc000,
++};
++#define GLAMO_LCD_CMD_TYPE_MASK 0xc000
++
++enum glamo_lcd_cmds {
++ GLAMO_LCD_CMD_DATA_DISP_FIRE = 0x00,
++ GLAMO_LCD_CMD_DATA_DISP_SYNC = 0x01, /* RGB only */
++ /* switch to command mode, no display */
++ GLAMO_LCD_CMD_DATA_FIRE_NO_DISP = 0x02,
++ /* display until VSYNC, switch to command */
++ GLAMO_LCD_CMD_DATA_FIRE_VSYNC = 0x11,
++ /* display until HSYNC, switch to command */
++ GLAMO_LCD_CMD_DATA_FIRE_HSYNC = 0x12,
++ /* display until VSYNC, 1 black frame, VSYNC, switch to command */
++ GLAMO_LCD_CMD_DATA_FIRE_VSYNC_B = 0x13,
++ /* don't care about display and switch to command */
++ GLAMO_LCD_CMD_DATA_FIRE_FREE = 0x14, /* RGB only */
++ /* don't care about display, keep data display but disable data,
++ * and switch to command */
++ GLAMO_LCD_CMD_DATA_FIRE_FREE_D = 0x15, /* RGB only */
++};
++
++enum glamo_core_revisions {
++ GLAMO_CORE_REV_A0 = 0x0000,
++ GLAMO_CORE_REV_A1 = 0x0001,
++ GLAMO_CORE_REV_A2 = 0x0002,
++ GLAMO_CORE_REV_A3 = 0x0003,
++};
++
++#endif /* _GLAMO_REGS_H */
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-core.c 2007-07-16 15:18:37.118063765 +0200
+@@ -0,0 +1,1017 @@
++/* Smedia Glamo 336x/337x driver
++ *
++ * (C) 2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/workqueue.h>
++#include <linux/wait.h>
++#include <linux/platform_device.h>
++#include <linux/kernel_stat.h>
++#include <linux/spinlock.h>
++#include <linux/glamofb.h>
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/div64.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include "glamo-regs.h"
++#include "glamo-core.h"
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++static struct glamo_core *glamo_handle;
++
++static inline void __reg_write(struct glamo_core *glamo,
++ u_int16_t reg, u_int16_t val)
++{
++ writew(val, glamo->base + reg);
++}
++
++static inline u_int16_t __reg_read(struct glamo_core *glamo,
++ u_int16_t reg)
++{
++ return readw(glamo->base + reg);
++}
++
++static void __reg_set_bit_mask(struct glamo_core *glamo,
++ u_int16_t reg, u_int16_t mask,
++ u_int16_t val)
++{
++ u_int16_t tmp;
++
++ val &= mask;
++
++ tmp = __reg_read(glamo, reg);
++ tmp &= ~mask;
++ tmp |= val;
++ __reg_write(glamo, reg, tmp);
++}
++
++static void reg_set_bit_mask(struct glamo_core *glamo,
++ u_int16_t reg, u_int16_t mask,
++ u_int16_t val)
++{
++ spin_lock(&glamo->lock);
++ __reg_set_bit_mask(glamo, reg, mask, val);
++ spin_unlock(&glamo->lock);
++}
++
++static inline void __reg_set_bit(struct glamo_core *glamo,
++ u_int16_t reg, u_int16_t bit)
++{
++ __reg_set_bit_mask(glamo, reg, bit, 0xffff);
++}
++
++static inline void __reg_clear_bit(struct glamo_core *glamo,
++ u_int16_t reg, u_int16_t bit)
++{
++ __reg_set_bit_mask(glamo, reg, bit, 0);
++}
++
++static inline void glamo_vmem_write(struct glamo_core *glamo, u_int32_t addr,
++ u_int16_t *src, int len)
++{
++ if (addr & 0x0001 || (unsigned long)src & 0x0001 || len & 0x0001) {
++ dev_err(&glamo->pdev->dev, "unaligned write(0x%08x, 0x%p, "
++ "0x%x)!!\n", addr, src, len);
++ }
++
++}
++
++static inline void glamo_vmem_read(struct glamo_core *glamo, u_int16_t *buf,
++ u_int32_t addr, int len)
++{
++ if (addr & 0x0001 || (unsigned long) buf & 0x0001 || len & 0x0001) {
++ dev_err(&glamo->pdev->dev, "unaligned read(0x%p, 0x08%x, "
++ "0x%x)!!\n", buf, addr, len);
++ }
++
++
++}
++
++/***********************************************************************
++ * resources of sibling devices
++ ***********************************************************************/
++
++#if 0
++static struct resource glamo_core_resources[] = {
++ {
++ .start = GLAMO_REGOFS_GENERIC,
++ .end = GLAMO_REGOFS_GENERIC + 0x400,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .start = 0,
++ .end = 0,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device glamo_core_dev = {
++ .name = "glamo-core",
++ .resource = &glamo_core_resources,
++ .num_resources = ARRAY_SIZE(glamo_core_resources),
++};
++#endif
++
++static struct resource glamo_mmc_resources[] = {
++ {
++ /* FIXME: those need to be incremented by parent base */
++ .start = GLAMO_REGOFS_MMC,
++ .end = GLAMO_REGOFS_MMC + 0x100,
++ .flags = IORESOURCE_MEM
++ }, {
++ .start = IRQ_GLAMO_MMC,
++ .end = IRQ_GLAMO_MMC,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device glamo_mmc_dev = {
++ .name = "glamo-mmc",
++ .resource = glamo_mmc_resources,
++ .num_resources = ARRAY_SIZE(glamo_mmc_resources),
++};
++
++static struct resource glamo_jpeg_resources[] = {
++ {
++ .start = GLAMO_REGOFS_JPEG,
++ .end = GLAMO_REGOFS_MPEG,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .start = IRQ_GLAMO_JPEG,
++ .end = IRQ_GLAMO_JPEG,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device glamo_jpeg_dev = {
++ .name = "glamo-jpeg",
++ .resource = glamo_jpeg_resources,
++ .num_resources = ARRAY_SIZE(glamo_jpeg_resources),
++};
++
++static struct resource glamo_mpeg_resources[] = {
++ {
++ .start = GLAMO_REGOFS_MPEG,
++ .end = GLAMO_REGOFS_LCD,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .start = IRQ_GLAMO_MPEG,
++ .end = IRQ_GLAMO_MPEG,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device glamo_mpeg_dev = {
++ .name = "glamo-mpeg",
++ .resource = glamo_mpeg_resources,
++ .num_resources = ARRAY_SIZE(glamo_mpeg_resources),
++};
++
++static struct resource glamo_2d_resources[] = {
++ {
++ .start = GLAMO_REGOFS_2D,
++ .end = GLAMO_REGOFS_3D,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .start = IRQ_GLAMO_2D,
++ .end = IRQ_GLAMO_2D,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct platform_device glamo_2d_dev = {
++ .name = "glamo-2d",
++ .resource = glamo_2d_resources,
++ .num_resources = ARRAY_SIZE(glamo_2d_resources),
++};
++
++static struct resource glamo_3d_resources[] = {
++ {
++ .start = GLAMO_REGOFS_3D,
++ .end = GLAMO_REGOFS_3D + 0x600,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device glamo_3d_dev = {
++ .name = "glamo-3d",
++ .resource = glamo_2d_resources,
++ .num_resources = ARRAY_SIZE(glamo_2d_resources),
++};
++
++static struct platform_device glamo_spigpio_dev = {
++ .name = "glamo-spi-gpio",
++};
++
++/* for the time being, we put the on-screen framebuffer into the lowest
++ * VRAM space. This should make the code easily compatible with the various
++ * 2MB/4MB/8MB variants of the Smedia chips */
++#define GLAMO_OFFSET_VRAM 0x800000
++#define GLAMO_OFFSET_FB (GLAMO_OFFSET_VRAM)
++
++/* we only allocate the minimum possible size for the framebuffer to make
++ * sure we have sufficient memory for other functions of the chip */
++//#define GLAMO_FB_SIZE (640*480*4) /* == 0x12c000 */
++#define GLAMO_FB_SIZE 0x800000
++
++static struct resource glamo_fb_resources[] = {
++ /* FIXME: those need to be incremented by parent base */
++ {
++ .name = "glamo-fb-regs",
++ .start = GLAMO_REGOFS_LCD,
++ .end = GLAMO_REGOFS_LCD + 0x100,
++ .flags = IORESOURCE_MEM,
++ }, {
++ .name = "glamo-fb-mem",
++ .start = GLAMO_OFFSET_FB,
++ .end = GLAMO_OFFSET_FB + GLAMO_FB_SIZE,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static struct platform_device glamo_fb_dev = {
++ .name = "glamo-fb",
++ .resource = glamo_fb_resources,
++ .num_resources = ARRAY_SIZE(glamo_fb_resources),
++};
++
++static void mangle_mem_resources(struct resource *res, int num_res,
++ struct resource *parent)
++{
++ int i;
++
++ for (i = 0; i < num_res; i++) {
++ if (res[i].flags != IORESOURCE_MEM)
++ continue;
++ res[i].start += parent->start;
++ res[i].end += parent->start;
++ res[i].parent = parent;
++ }
++}
++
++/***********************************************************************
++ * IRQ demultiplexer
++ ***********************************************************************/
++#define irq2glamo(x) (x - IRQ_GLAMO(0))
++
++static void glamo_ack_irq(unsigned int irq)
++{
++ /* clear interrupt source */
++ __reg_write(glamo_handle, GLAMO_REG_IRQ_CLEAR,
++ 1 << irq2glamo(irq));
++}
++
++static void glamo_mask_irq(unsigned int irq)
++{
++ u_int16_t tmp;
++
++ /* clear bit in enable register */
++ tmp = __reg_read(glamo_handle, GLAMO_REG_IRQ_ENABLE);
++ tmp &= ~(1 << irq2glamo(irq));
++ __reg_write(glamo_handle, GLAMO_REG_IRQ_ENABLE, tmp);
++}
++
++static void glamo_unmask_irq(unsigned int irq)
++{
++ u_int16_t tmp;
++
++ /* set bit in enable register */
++ tmp = __reg_read(glamo_handle, GLAMO_REG_IRQ_ENABLE);
++ tmp |= (1 << irq2glamo(irq));
++ __reg_write(glamo_handle, GLAMO_REG_IRQ_ENABLE, tmp);
++}
++
++static struct irq_chip glamo_irq_chip = {
++ .ack = glamo_ack_irq,
++ .mask = glamo_mask_irq,
++ .unmask = glamo_unmask_irq,
++};
++
++static void glamo_irq_demux_handler(unsigned int irq, struct irq_desc *desc)
++{
++ const unsigned int cpu = smp_processor_id();
++
++ spin_lock(&desc->lock);
++
++ desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
++
++ if (unlikely(desc->status & IRQ_INPROGRESS)) {
++ desc->status |= (IRQ_PENDING | IRQ_MASKED);
++ desc->chip->mask(irq);
++ desc->chip->ack(irq);
++ goto out_unlock;
++ }
++
++ kstat_cpu(cpu).irqs[irq]++;
++ desc->chip->ack(irq);
++ desc->status |= IRQ_INPROGRESS;
++
++ do {
++ u_int16_t irqstatus;
++ int i;
++
++ if (unlikely((desc->status &
++ (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
++ (IRQ_PENDING | IRQ_MASKED))) {
++ /* dealing with pending IRQ, unmasking */
++ desc->chip->unmask(irq);
++ desc->status &= ~IRQ_MASKED;
++ }
++
++ desc->status &= ~IRQ_PENDING;
++
++ /* read IRQ status register */
++ irqstatus = __reg_read(glamo_handle, GLAMO_REG_IRQ_STATUS);
++ for (i = 0; i < 9; i++) {
++ if (irqstatus & (1 << i)) {
++ irq = IRQ_GLAMO(0) + i;
++ desc = irq_desc + irq;
++ desc_handle_irq(irq, desc);
++ }
++ }
++
++ } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
++
++ desc->status &= ~IRQ_INPROGRESS;
++
++out_unlock:
++ spin_unlock(&desc->lock);
++}
++
++/***********************************************************************
++ * 'engine' support
++ ***********************************************************************/
++
++int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine)
++{
++ spin_lock(&glamo->lock);
++ switch (engine) {
++ case GLAMO_ENGINE_LCD:
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_LCD,
++ GLAMO_CLOCK_LCD_EN_M5CLK |
++ GLAMO_CLOCK_LCD_EN_DHCLK |
++ GLAMO_CLOCK_LCD_EN_DMCLK |
++ GLAMO_CLOCK_LCD_EN_DCLK |
++ GLAMO_CLOCK_LCD_DG_M5CLK |
++ GLAMO_CLOCK_LCD_DG_DMCLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_GEN5_1,
++ GLAMO_CLOCK_GEN51_EN_DIV_DHCLK |
++ GLAMO_CLOCK_GEN51_EN_DIV_DMCLK |
++ GLAMO_CLOCK_GEN51_EN_DIV_DCLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
++ GLAMO_HOSTBUS2_MMIO_EN_LCD,
++ 0xffff);
++ break;
++ case GLAMO_ENGINE_MMC:
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MMC,
++ GLAMO_CLOCK_MMC_EN_M9CLK |
++ GLAMO_CLOCK_MMC_EN_TCLK |
++ GLAMO_CLOCK_MMC_DG_M9CLK |
++ GLAMO_CLOCK_MMC_DG_TCLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
++ GLAMO_HOSTBUS2_MMIO_EN_MMC,
++ GLAMO_HOSTBUS2_MMIO_EN_MMC);
++ break;
++ case GLAMO_ENGINE_2D:
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_2D,
++ GLAMO_CLOCK_2D_EN_M7CLK |
++ GLAMO_CLOCK_2D_EN_GCLK |
++ GLAMO_CLOCK_2D_DG_M7CLK |
++ GLAMO_CLOCK_2D_DG_GCLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
++ GLAMO_HOSTBUS2_MMIO_EN_2D,
++ GLAMO_HOSTBUS2_MMIO_EN_2D);
++ break;
++ case GLAMO_ENGINE_CMDQ:
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_2D,
++ GLAMO_CLOCK_2D_EN_M6CLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_HOSTBUS(2),
++ GLAMO_HOSTBUS2_MMIO_EN_CQ,
++ GLAMO_HOSTBUS2_MMIO_EN_CQ);
++ break;
++ /* FIXME: Implementation */
++ }
++ spin_unlock(&glamo->lock);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(glamo_engine_enable);
++
++int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine)
++{
++ spin_lock(&glamo->lock);
++ switch (engine) {
++ /* FIXME: Implementation */
++ default:
++ break;
++ }
++ spin_unlock(&glamo->lock);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(glamo_engine_disable);
++
++struct glamo_script reset_regs[] = {
++ [GLAMO_ENGINE_LCD] = {
++ GLAMO_REG_CLOCK_LCD, GLAMO_CLOCK_LCD_RESET
++ },
++#if 0
++ [GLAMO_ENGINE_HOST] = {
++ GLAMO_REG_CLOCK_HOST, GLAMO_CLOCK_HOST_RESET
++ },
++ [GLAMO_ENGINE_MEM] = {
++ GLAMO_REG_CLOCK_MEM, GLAMO_CLOCK_MEM_RESET
++ },
++#endif
++ [GLAMO_ENGINE_MMC] = {
++ GLAMO_REG_CLOCK_MMC, GLAMO_CLOCK_MMC_RESET
++ },
++ [GLAMO_ENGINE_2D] = {
++ GLAMO_REG_CLOCK_2D, GLAMO_CLOCK_2D_RESET
++ },
++ [GLAMO_ENGINE_JPEG] = {
++ GLAMO_REG_CLOCK_JPEG, GLAMO_CLOCK_JPEG_RESET
++ },
++};
++
++void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine)
++{
++ struct glamo_script *rst;
++
++ if (engine >= ARRAY_SIZE(reset_regs)) {
++ dev_warn(&glamo->pdev->dev, "unknown engine %u ", engine);
++ return;
++ }
++
++ rst = &reset_regs[engine];
++
++ spin_lock(&glamo->lock);
++ __reg_set_bit(glamo, rst->reg, rst->val);
++ spin_unlock(&glamo->lock);
++
++ msleep(1);
++
++ spin_lock(&glamo->lock);
++ __reg_clear_bit(glamo, rst->reg, rst->val);
++ spin_unlock(&glamo->lock);
++}
++EXPORT_SYMBOL_GPL(glamo_engine_reset);
++
++/***********************************************************************
++ * script support
++ ***********************************************************************/
++
++int glamo_run_script(struct glamo_core *glamo, struct glamo_script *script, int len)
++{
++ int i;
++
++ for (i = 0; i < len; i++) {
++ struct glamo_script *line = &script[i];
++
++ if (line->reg == 0xffff)
++ return 0;
++ else if (line->reg == 0xfffe)
++ msleep(line->val);
++ else
++ __reg_write(glamo, script[i].reg, script[i].val);
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL(glamo_run_script);
++
++static struct glamo_script glamo_init_script[] = {
++ { GLAMO_REG_CLOCK_HOST, 0x1000 },
++ { 0xfffe, 1 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x1000 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x2000 },
++ { GLAMO_REG_CLOCK_LCD, 0x1000 },
++ { GLAMO_REG_CLOCK_MMC, 0x1000 },
++ { GLAMO_REG_CLOCK_ISP, 0x1000 },
++ { GLAMO_REG_CLOCK_ISP, 0x2000 },
++ { GLAMO_REG_CLOCK_JPEG, 0x1000 },
++ { GLAMO_REG_CLOCK_3D, 0x1000 },
++ { GLAMO_REG_CLOCK_3D, 0x2000 },
++ { GLAMO_REG_CLOCK_2D, 0x1000 },
++ { GLAMO_REG_CLOCK_2D, 0x2000 },
++ { GLAMO_REG_CLOCK_RISC1, 0x1000 },
++ { GLAMO_REG_CLOCK_MPEG, 0x3000 },
++ { GLAMO_REG_CLOCK_MPEG, 0x3000 },
++ { GLAMO_REG_CLOCK_MPROC, 0x1000 },
++ { 0xfffe, 1 },
++ { GLAMO_REG_CLOCK_HOST, 0x0000 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x0000 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x0000 },
++ { GLAMO_REG_CLOCK_LCD, 0x0000 },
++ { GLAMO_REG_CLOCK_MMC, 0x0000 },
++ { GLAMO_REG_CLOCK_ISP, 0x0000 },
++ { GLAMO_REG_CLOCK_ISP, 0x0000 },
++ { GLAMO_REG_CLOCK_JPEG, 0x0000 },
++ { GLAMO_REG_CLOCK_3D, 0x0000 },
++ { GLAMO_REG_CLOCK_3D, 0x0000 },
++ { GLAMO_REG_CLOCK_2D, 0x0000 },
++ { GLAMO_REG_CLOCK_2D, 0x0000 },
++ { GLAMO_REG_CLOCK_RISC1, 0x0000 },
++ { GLAMO_REG_CLOCK_MPEG, 0x0000 },
++ { GLAMO_REG_CLOCK_MPEG, 0x0000 },
++ { GLAMO_REG_CLOCK_MPROC, 0x0000 },
++ { 0xfffe, 1 },
++ { GLAMO_REG_PLL_GEN1, 0x05db }, /* 48MHz */
++ { GLAMO_REG_PLL_GEN3, 0x09c3 }, /* 80MHz */
++ { 0xfffe, 300 },
++ { GLAMO_REG_IRQ_ENABLE, 0xffff },
++ { GLAMO_REG_CLOCK_GEN6, 0x2000 },
++ { GLAMO_REG_CLOCK_GEN7, 0x0102 },
++ { GLAMO_REG_CLOCK_GEN8, 0x0100 },
++ { GLAMO_REG_CLOCK_HOST, 0x000d },
++ { 0x200, 0x0ef0 },
++ { 0x202, 0x7fff },
++ { 0x212, 0x0000 },
++ { 0x214, 0x4000 },
++ { 0x216, 0xf00e },
++ { GLAMO_REG_MEM_GEN, 0xafaf },
++ { GLAMO_REG_MEM_TIMING1, 0x0108 },
++ { GLAMO_REG_MEM_TIMING2, 0x0010 },
++ { GLAMO_REG_MEM_TIMING3, 0x0000 },
++ { GLAMO_REG_MEM_TIMING4, 0x0000 },
++ { GLAMO_REG_MEM_TIMING5, 0x0000 },
++ { GLAMO_REG_MEM_TIMING6, 0x0000 },
++ { GLAMO_REG_MEM_TIMING7, 0x0000 },
++ { GLAMO_REG_MEM_TIMING8, 0x1002 },
++ { GLAMO_REG_MEM_TIMING9, 0x6006 },
++ { GLAMO_REG_MEM_TIMING10, 0x00ff },
++ { GLAMO_REG_MEM_TIMING11, 0x0001 },
++ { GLAMO_REG_MEM_POWER1, 0x0020 },
++ { GLAMO_REG_MEM_POWER2, 0x0000 },
++ { GLAMO_REG_MEM_DRAM2, 0x01d6 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x000a },
++ { GLAMO_REG_CLOCK_MEMORY, 0x000b },
++ { GLAMO_REG_MEM_TYPE, 0x0873 },
++};
++
++#if 0 /* MM370 */
++static const struct glamo_script regs_vram_2mb = {
++ { GLAMO_REG_CLOCK_MEMORY, 0x3aaa },
++ { 0xfffe, 50 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x0aaa },
++ { 0xfffe, 3 },
++ { GLAMO_REG_MEM_POWER1, 0x0020 },
++ { 0x033a, 0x0000 },
++ { 0x033c, 0x0000 },
++ { 0x033e, 0x0000 },
++ { 0x0340, 0x0000 },
++ { 0x0342, 0x0000 },
++ { 0x0344, 0x0000 },
++ { 0x0346, 0x0240 },
++ { GLAMO_REG_MEM_TIMING8, 0x1016 },
++ { GLAMO_REG_MEM_TIMING9, 0x6067 },
++ { GLAMO_REG_MEM_TIMING10, 0x00ff },
++ { GLAMO_REG_MEM_TIMING11, 0x0030 },
++ { GLAMO_REG_MEM_GEN, 0x3fff },
++ { GLAMO_REG_MEM_GEN, 0xafaf },
++ { GLAMO_REG_MEM_TIMING1, 0x0108 },
++ { GLAMO_REG_MEM_TIMING2, 0x0010 },
++ { GLAMO_REG_MEM_DRAM1, 0x0a00 },
++ { 0xfffe, 3 },
++ { GLAMO_REG_MEM_DRAM1, 0xe200 },
++ { 0xfffe, 1 },
++};
++
++static const struct glamo_script regs_vram_8mb = {
++ { GLAMO_REG_CLOCK_MEMORY, 0x3aaa },
++ { 0xfffe, 50 },
++ { GLAMO_REG_CLOCK_MEMORY, 0x0aaa },
++ { 0xfffe, 3 },
++ { GLAMO_REG_MEM_POWER1, 0x0020 },
++ { 0x033a, 0x45cf },
++ { 0x033c, 0x4240 },
++ { 0x033e, 0x53e0 },
++ { 0x0340, 0x1401 },
++ { 0x0342, 0x0c44 },
++ { 0x0344, 0x1d0b },
++ { 0x0346, 0x25ac },
++ { 0x0348, 0x1953 },
++ { 0xfffe, 1 },
++ { GLAMO_REG_MEM_TYPE, 0x087a },
++ { GLAMO_REG_MEM_DRAM2, 0x01d6 },
++ { GLAMO_REG_MEM_TIMING8, 0x1060 },
++ { GLAMO_REG_MEM_TIMING9, 0x6067 },
++ { GLAMO_REG_MEM_TIMING10, 0x00ff },
++ { GLAMO_REG_MEM_TIMING11, 0x0030 },
++ { GLAMO_REG_MEM_GEN, 0x3fff },
++ { GLAMO_REG_MEM_GEN, 0xafaf },
++ { GLAMO_REG_MEM_TIMING1, 0x3108 },
++ { GLAMO_REG_MEM_TIMING2, 0x0010 },
++ { GLAMO_REG_MEM_DRAM1, 0x0a00 },
++ { 0xfffe, 3 },
++ { GLAMO_REG_MEM_DRAM1, 0xe200 },
++ { 0xfffe, 1 },
++};
++#endif
++
++enum glamo_pll {
++ GLAMO_PLL1,
++ GLAMO_PLL2,
++};
++
++static int glamo_pll_rate(struct glamo_core *glamo,
++ enum glamo_pll pll)
++{
++ u_int16_t reg;
++ unsigned int div = 512;
++ /* FIXME: move osci into platform_data */
++ unsigned int osci = 32768;
++
++ if (osci == 32768)
++ div = 1;
++
++ switch (pll) {
++ case GLAMO_PLL1:
++ reg = __reg_read(glamo, GLAMO_REG_PLL_GEN1);
++ break;
++ case GLAMO_PLL2:
++ reg = __reg_read(glamo, GLAMO_REG_PLL_GEN3);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return (osci/div)*reg;
++}
++
++enum glamo_power {
++ GLAMO_POWER_ON,
++ GLAMO_POWER_STANDBY,
++ GLAMO_POWER_SUSPEND,
++};
++
++static void glamo_power(struct glamo_core *glamo,
++ enum glamo_power new_state)
++{
++ spin_lock(&glamo->lock);
++
++ switch (new_state) {
++ case GLAMO_POWER_ON:
++ /* power up PLL1 and PLL2 */
++ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN6, 0x0001, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0x0000);
++ /* enable memory clock and get it out of deep pwrdown */
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
++ GLAMO_CLOCK_MEM_EN_MOCACLK, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM2,
++ GLAMO_MEM_DRAM2_DEEP_PWRDOWN, 0x0000);
++ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM1,
++ GLAMO_MEM_DRAM1_SELF_REFRESH, 0x0000);
++ /* FIXME: reset pll's */
++ break;
++ case GLAMO_POWER_STANDBY:
++ /* enable memory self-refresh */
++ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM1,
++ GLAMO_MEM_DRAM1_SELF_REFRESH, 0xffff);
++ /* stop memory clock */
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
++ GLAMO_CLOCK_MEM_EN_MOCACLK, 0x0000);
++ /* power down PLL2 and then PLL1 */
++ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN5, 0x0001, 0xffff);
++ break;
++ case GLAMO_POWER_SUSPEND:
++ __reg_set_bit_mask(glamo, GLAMO_REG_MEM_DRAM2,
++ GLAMO_MEM_DRAM2_DEEP_PWRDOWN, 0xffff);
++ /* stop memory clock */
++ __reg_set_bit_mask(glamo, GLAMO_REG_CLOCK_MEMORY,
++ GLAMO_CLOCK_MEM_EN_MOCACLK, 0x0000);
++ /* power down PLL2 and then PLL1 */
++ __reg_set_bit_mask(glamo, GLAMO_REG_PLL_GEN3, 0x2000, 0xffff);
++ __reg_set_bit_mask(glamo, GLAMO_REG_DFT_GEN5, 0x0001, 0xffff);
++ break;
++ }
++
++ spin_unlock(&glamo->lock);
++}
++
++#define MEMDETECT_RETRY 6
++static unsigned int detect_memsize(struct glamo_core *glamo)
++{
++ int i;
++
++ static const u_int16_t pattern[] = {
++ 0x1111, 0x8a8a, 0x2222, 0x7a7a,
++ 0x3333, 0x6a6a, 0x4444, 0x5a5a,
++ 0x5555, 0x4a4a, 0x6666, 0x3a3a,
++ 0x7777, 0x2a2a, 0x8888, 0x1a1a
++ };
++
++ for (i = 0; i < MEMDETECT_RETRY; i++) {
++ switch (glamo->type) {
++ case 3600:
++ __reg_write(glamo, GLAMO_REG_MEM_TYPE, 0x0072);
++ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xc100);
++ break;
++ case 3650:
++ switch (glamo->revision) {
++ case GLAMO_CORE_REV_A0:
++ if (i & 1)
++ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
++ 0x097a);
++ else
++ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
++ 0x0173);
++
++ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0x0000);
++ msleep(1);
++ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xc100);
++ break;
++ default:
++ if (i & 1)
++ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
++ 0x0972);
++ else
++ __reg_write(glamo, GLAMO_REG_MEM_TYPE,
++ 0x0872);
++
++ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0x0000);
++ msleep(1);
++ __reg_write(glamo, GLAMO_REG_MEM_DRAM1, 0xe100);
++ break;
++ }
++ break;
++ case 3700:
++ /* FIXME */
++ default:
++ break;
++ }
++
++#if 0
++ /* FIXME: finish implementation */
++ for (j = 0; j < 8; j++) {
++ __
++#endif
++ }
++
++ return 0;
++}
++
++/* Find out if we can support this version of the Glamo chip */
++static int glamo_supported(struct glamo_core *glamo)
++{
++ u_int16_t dev_id, rev_id, memsize;
++
++ dev_id = __reg_read(glamo, GLAMO_REG_DEVICE_ID);
++ rev_id = __reg_read(glamo, GLAMO_REG_REVISION_ID);
++
++ switch (dev_id) {
++ case 0x3650:
++ switch (rev_id) {
++ case GLAMO_CORE_REV_A2:
++ break;
++ case GLAMO_CORE_REV_A0:
++ case GLAMO_CORE_REV_A1:
++ case GLAMO_CORE_REV_A3:
++ dev_warn(&glamo->pdev->dev, "untested core revision "
++ "%04x, your mileage may vary\n", rev_id);
++ break;
++ default:
++ dev_warn(&glamo->pdev->dev, "unknown glamo revision "
++ "%04x, your mileage may vary\n", rev_id);
++ /* maybe should abort ? */
++ }
++ break;
++ case 0x3600:
++ case 0x3700:
++ default:
++ dev_err(&glamo->pdev->dev, "unsupported Glamo device %04x\n",
++ dev_id);
++ return 0;
++ }
++
++ dev_info(&glamo->pdev->dev, "Detected Glamo core %04x Revision %04x "
++ "(%uHz CPU / %uHz Memory)\n", dev_id, rev_id,
++ glamo_pll_rate(glamo, GLAMO_PLL1),
++ glamo_pll_rate(glamo, GLAMO_PLL2));
++
++ return 1;
++}
++
++
++static int __init glamo_probe(struct platform_device *pdev)
++{
++ int rc, irq;
++ struct glamo_core *glamo;
++
++ if (glamo_handle) {
++ dev_err(&pdev->dev,
++ "This driver supports only one instance\n");
++ return -EBUSY;
++ }
++
++ glamo = kmalloc(GFP_KERNEL, sizeof(*glamo));
++ if (!glamo)
++ return -ENOMEM;
++
++ glamo_handle = glamo;
++ glamo->pdev = pdev;
++ glamo->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ glamo->irq = platform_get_irq(pdev, 0);
++ glamo->pdata = pdev->dev.platform_data;
++ if (!glamo->irq || !glamo->mem || !glamo->pdata) {
++ dev_err(&pdev->dev, "platform device with no IRQ/MEM ?\n");
++ rc = -ENOENT;
++ goto out_free;
++ }
++
++ /* register a number of sibling devices whoise IOMEM resources
++ * are siblings of pdev's IOMEM resource */
++#if 0
++ glamo_core_dev.dev.parent = &pdev.dev;
++ mangle_mem_resources(glamo_core_dev.resources,
++ glamo_core_dev.num_resources, glamo->mem);
++ glamo_core_dev.resources[1].start = glamo->irq;
++ glamo_core_dev.resources[1].end = glamo->irq;
++ platform_device_register(&glamo_core_dev);
++#endif
++
++ glamo_mmc_dev.dev.parent = &pdev->dev;
++ mangle_mem_resources(glamo_mmc_dev.resource,
++ glamo_mmc_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_mmc_dev);
++
++ glamo_2d_dev.dev.parent = &pdev->dev;
++ mangle_mem_resources(glamo_2d_dev.resource,
++ glamo_2d_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_2d_dev);
++
++ glamo_3d_dev.dev.parent = &pdev->dev;
++ mangle_mem_resources(glamo_3d_dev.resource,
++ glamo_3d_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_3d_dev);
++
++ glamo_jpeg_dev.dev.parent = &pdev->dev;
++ mangle_mem_resources(glamo_jpeg_dev.resource,
++ glamo_jpeg_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_jpeg_dev);
++
++ glamo_mpeg_dev.dev.parent = &pdev->dev;
++ mangle_mem_resources(glamo_mpeg_dev.resource,
++ glamo_mpeg_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_mpeg_dev);
++
++ glamo->pdata->glamo = glamo;
++ glamo_fb_dev.dev.parent = &pdev->dev;
++ glamo_fb_dev.dev.platform_data = glamo->pdata;
++ mangle_mem_resources(glamo_fb_dev.resource,
++ glamo_fb_dev.num_resources, glamo->mem);
++ platform_device_register(&glamo_fb_dev);
++
++ glamo->pdata->spigpio_info->glamo = glamo;
++ glamo_spigpio_dev.dev.parent = &pdev->dev;
++ glamo_spigpio_dev.dev.platform_data = glamo->pdata->spigpio_info;
++ platform_device_register(&glamo_spigpio_dev);
++
++ /* only request the generic, hostbus and memory controller MMIO */
++ glamo->mem = request_mem_region(glamo->mem->start,
++ GLAMO_REGOFS_VIDCAP, "glamo-core");
++ if (!glamo->mem) {
++ dev_err(&pdev->dev, "failed to request memory region\n");
++ goto out_free;
++ }
++
++ /* only remap the generic, hostbus and memory controller registers */
++ glamo->base = ioremap(glamo->mem->start, GLAMO_REGOFS_VIDCAP);
++ if (!glamo->base) {
++ dev_err(&pdev->dev, "failed to ioremap() memory region\n");
++ goto out_free;
++ }
++
++ if (!glamo_supported(glamo)) {
++ dev_err(&pdev->dev, "This Glamo is not supported\n");
++ goto out_free;
++ }
++
++ platform_set_drvdata(pdev, glamo);
++
++ printk("running init script\n");
++ glamo_run_script(glamo, glamo_init_script, ARRAY_SIZE(glamo_init_script));
++
++ dev_info(&glamo->pdev->dev, "Glamo core now %uHz CPU / %uHz Memory)\n",
++ glamo_pll_rate(glamo, GLAMO_PLL1),
++ glamo_pll_rate(glamo, GLAMO_PLL2));
++
++ printk("interrupts\n");
++
++ /* FIXME: do we need to request_irq() it ? */
++ for (irq = IRQ_GLAMO(0); irq <= IRQ_GLAMO(8); irq++) {
++ set_irq_chip(irq, &glamo_irq_chip);
++ set_irq_handler(irq, handle_level_irq);
++ set_irq_flags(irq, IRQF_VALID);
++ }
++ set_irq_chained_handler(glamo->irq, glamo_irq_demux_handler);
++ set_irq_type(glamo->irq, IRQT_FALLING);
++
++ return 0;
++
++out_free:
++ glamo_handle = NULL;
++ kfree(glamo);
++ return rc;
++}
++
++static int glamo_remove(struct platform_device *pdev)
++{
++ struct glamo_core *glamo = platform_get_drvdata(pdev);
++ int irq;
++
++ disable_irq(glamo->irq);
++ set_irq_chained_handler(glamo->irq, NULL);
++
++ for (irq = IRQ_GLAMO(0); irq <= IRQ_GLAMO(8); irq++) {
++ set_irq_flags(irq, 0);
++ set_irq_chip(irq, NULL);
++ }
++
++ platform_set_drvdata(pdev, NULL);
++ platform_device_unregister(&glamo_fb_dev);
++ platform_device_unregister(&glamo_mmc_dev);
++ iounmap(glamo->base);
++ release_mem_region(glamo->mem->start, GLAMO_REGOFS_VIDCAP);
++ glamo_handle = NULL;
++ kfree(glamo);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int glamo_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int glamo_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++#else
++#define glamo_suspend NULL
++#define glamo_resume NULL
++#endif
++
++static struct platform_driver glamo_driver = {
++ .probe = glamo_probe,
++ .remove = glamo_remove,
++ .suspend = glamo_suspend,
++ .resume = glamo_resume,
++ .driver = {
++ .name = "glamo3362",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __devinit glamo_init(void)
++{
++ return platform_driver_register(&glamo_driver);
++}
++
++static void __exit glamo_cleanup(void)
++{
++ platform_driver_unregister(&glamo_driver);
++}
++
++module_init(glamo_init);
++module_exit(glamo_cleanup);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("Smedia Glamo 336x/337x core/resource driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/include/asm-arm/arch-s3c2410/irqs.h
+===================================================================
+--- linux-2.6.22.1.orig/include/asm-arm/arch-s3c2410/irqs.h 2007-07-10 20:56:30.000000000 +0200
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/irqs.h 2007-07-16 15:18:37.142065131 +0200
+@@ -148,9 +148,37 @@
+ #define IRQ_S3C2443_AC97 S3C2410_IRQSUB(28)
+
+ #ifdef CONFIG_CPU_S3C2443
+-#define NR_IRQS (IRQ_S3C2443_AC97+1)
++#define _NR_IRQS (IRQ_S3C2443_AC97+1)
+ #else
+-#define NR_IRQS (IRQ_S3C2440_AC97+1)
++#define _NR_IRQS (IRQ_S3C2440_AC97+1)
+ #endif
+
++/*
++ * The next 16 interrupts are for board specific purposes. Since
++ * the kernel can only run on one machine at a time, we can re-use
++ * these. If you need more, increase IRQ_BOARD_END, but keep it
++ * within sensible limits.
++ */
++#define IRQ_BOARD_START _NR_IRQS
++#define IRQ_BOARD_END (_NR_IRQS + 10)
++
++#if defined(CONFIG_MACH_NEO1973_GTA02)
++#define NR_IRQS (IRQ_BOARD_END)
++#else
++#define NR_IRQS (IRQ_BOARD_START)
++#endif
++
++/* Neo1973 GTA02 interrupts */
++#define NEO1973_GTA02_IRQ(x) (IRQ_BOARD_START + (x))
++#define IRQ_GLAMO(x) NEO1973_GTA02_IRQ(x)
++#define IRQ_GLAMO_HOSTBUS IRQ_GLAMO(0)
++#define IRQ_GLAMO_JPEG IRQ_GLAMO(1)
++#define IRQ_GLAMO_MPEG IRQ_GLAMO(2)
++#define IRQ_GLAMO_MPROC1 IRQ_GLAMO(3)
++#define IRQ_GLAMO_MPROC0 IRQ_GLAMO(4)
++#define IRQ_GLAMO_CMDQUEUE IRQ_GLAMO(5)
++#define IRQ_GLAMO_2D IRQ_GLAMO(6)
++#define IRQ_GLAMO_MMC IRQ_GLAMO(7)
++#define IRQ_GLAMO_RISC IRQ_GLAMO(8)
++
+ #endif /* __ASM_ARCH_IRQ_H */
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-fb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-fb.c 2007-07-16 15:18:37.182067411 +0200
+@@ -0,0 +1,547 @@
++/* Smedia Glamo 336x/337x driver
++ *
++ * (C) 2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ */
++
++#define DEBUG
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/vmalloc.h>
++#include <linux/dma-mapping.h>
++#include <linux/interrupt.h>
++#include <linux/workqueue.h>
++#include <linux/wait.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/div64.h>
++
++#ifdef CONFIG_PM
++#include <linux/pm.h>
++#endif
++
++#include <linux/glamofb.h>
++
++#include "glamo-regs.h"
++#include "glamo-core.h"
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++#define GLAMO_FB_ALLOC (640*480*2)
++
++struct glamofb_handle {
++ struct fb_info *fb;
++ struct device *dev;
++ struct resource *reg;
++ struct resource *fb_res;
++ char __iomem *base;
++ struct glamofb_platform_data *mach_info;
++ struct glamo_core *glamo;
++ u_int32_t pseudo_pal[16];
++};
++
++/* 'sibling' spi device for lcm init */
++static struct platform_device glamo_spi_dev = {
++ .name = "glamo-lcm-spi",
++};
++
++static inline int reg_read(struct glamofb_handle *glamo,
++ u_int16_t reg)
++{
++ return readw(glamo->base + reg);
++}
++
++static inline void reg_write(struct glamofb_handle *glamo,
++ u_int16_t reg, u_int16_t val)
++{
++ writew(val, glamo->base + reg);
++}
++
++static struct glamo_script glamo_regs[] = {
++ { GLAMO_REG_LCD_MODE1, 0x0020 },
++ /* no display rotation, no hardware cursor, no dither, no gamma,
++ * no retrace flip, vsync low-active, hsync low active,
++ * no TVCLK, no partial display, hw dest color from fb,
++ * no partial display mode, LCD1, software flip, */
++ { GLAMO_REG_LCD_MODE2, 0x1020 },
++ /* no video flip, no ptr, no ptr, dhclk,
++ * normal mode, no cpuif,
++ * res, serial msb first, single fb, no fr ctrl,
++ * cpu if bits all zero, no crc
++ * 0000 0000 0010 0000 */
++ { GLAMO_REG_LCD_MODE3, 0x0b40 },
++ /* src data rgb565, res, 18bit rgb666
++ * 000 01 011 0100 0000 */
++ { GLAMO_REG_LCD_WIDTH, 480 },
++ { GLAMO_REG_LCD_HEIGHT, 640 },
++ { GLAMO_REG_LCD_POLARITY, 0x440c },
++ /* DE high active, no cpu/lcd if, cs0 force low, a0 low active,
++ * np cpu if, 9bit serial data, sclk rising edge latch data
++ * 01 00 0 100 0 000 01 0 0 */
++ { GLAMO_REG_LCD_A_BASE1, 0x0000 }, /* display A base address 15:0 */
++ { GLAMO_REG_LCD_A_BASE2, 0x0000 }, /* display A base address 22:16 */
++ { GLAMO_REG_LCD_PITCH, 480*2 },
++ { GLAMO_REG_LCD_HORIZ_TOTAL, 480 + 8 + 8 + 104 }, /* 600 */
++ { GLAMO_REG_LCD_HORIZ_RETR_START, 0 },
++ { GLAMO_REG_LCD_HORIZ_RETR_END, 8 },
++ { GLAMO_REG_LCD_HORIZ_DISP_START, 8 + 104 },
++ { GLAMO_REG_LCD_HORIZ_DISP_END, 8 + 104 + 480 },
++ { GLAMO_REG_LCD_VERT_TOTAL, 640 + 2 + 2 + 16 }, /* 660 */
++ { GLAMO_REG_LCD_VERT_RETR_START, 0 },
++ { GLAMO_REG_LCD_VERT_RETR_END, 2 },
++ { GLAMO_REG_LCD_VERT_DISP_START, 2 + 2 },
++ { GLAMO_REG_LCD_VERT_DISP_END, 2 + 2 + 640 },
++};
++
++static int glamofb_run_script(struct glamofb_handle *glamo,
++ struct glamo_script *script, int len)
++{
++ int i;
++
++ for (i = 0; i < len; i++) {
++ struct glamo_script *line = &script[i];
++
++ if (line->reg == 0xffff)
++ return 0;
++ else if (line->reg == 0xfffe)
++ msleep(line->val);
++ else
++ reg_write(glamo, script[i].reg, script[i].val);
++ }
++
++ return 0;
++}
++
++static int glamofb_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info)
++{
++ struct glamofb_handle *glamo = info->par;
++ u_int16_t mode;
++
++ if (var->yres > glamo->mach_info->yres.max)
++ var->yres = glamo->mach_info->yres.max;
++ else if (var->yres < glamo->mach_info->yres.min)
++ var->yres = glamo->mach_info->yres.min;
++
++ if (var->xres > glamo->mach_info->xres.max)
++ var->xres = glamo->mach_info->xres.max;
++ else if (var->xres < glamo->mach_info->xres.min)
++ var->xres = glamo->mach_info->xres.min;
++
++ if (var->bits_per_pixel > glamo->mach_info->bpp.max)
++ var->bits_per_pixel = glamo->mach_info->bpp.max;
++ else if (var->bits_per_pixel < glamo->mach_info->bpp.min)
++ var->bits_per_pixel = glamo->mach_info->bpp.min;
++
++ /* FIXME: set rgb positions */
++ switch (var->bits_per_pixel) {
++ case 16:
++ switch (reg_read(glamo, GLAMO_REG_LCD_MODE3) & 0xc000) {
++ case GLAMO_LCD_SRC_RGB565:
++ var->red.offset = 11;
++ var->green.offset = 5;
++ var->blue.offset = 0;
++ var->red.length = 5;
++ var->green.length = 6;
++ var->blue.length = 5;
++ var->transp.length = 0;
++ break;
++ case GLAMO_LCD_SRC_ARGB1555:
++ var->transp.offset = 15;
++ var->red.offset = 10;
++ var->green.offset = 5;
++ var->blue.offset = 0;
++ var->transp.length = 1;
++ var->red.length = 5;
++ var->green.length = 5;
++ var->blue.length = 5;
++ break;
++ case GLAMO_LCD_SRC_ARGB4444:
++ var->transp.offset = 12;
++ var->red.offset = 8;
++ var->green.offset = 4;
++ var->blue.offset = 0;
++ var->transp.length = 4;
++ var->red.length = 4;
++ var->green.length = 4;
++ var->blue.length = 4;
++ break;
++ }
++ case 24:
++ case 32:
++ default:
++ /* The Smedia Glamo doesn't support anything but 16bit color */
++ return -EINVAL;
++ break;
++ }
++
++ return 0;
++}
++
++static int glamofb_set_par(struct fb_info *info)
++{
++ struct glamofb_handle *glamo = info->par;
++ struct fb_var_screeninfo *var = &info->var;
++
++ /* FIXME */
++
++ switch (var->bits_per_pixel) {
++ case 16:
++ glamo->fb->fix.visual = FB_VISUAL_DIRECTCOLOR;
++ break;
++ case 32:
++ glamo->fb->fix.visual = FB_VISUAL_TRUECOLOR;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ glamo->fb->fix.line_length = (var->width * var->bits_per_pixel) / 8;
++ glamo->fb->fix.smem_len = info->fix.line_length * var->yres_virtual;
++
++ return 0;
++}
++
++static int glamofb_blank(int blank_mode, struct fb_info *info)
++{
++ /* FIXME */
++ return 0;
++}
++
++static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
++{
++ chan &= 0xffff;
++ chan >>= 16 - bf->length;
++ return chan << bf->offset;
++}
++
++static int glamofb_setcolreg(unsigned regno,
++ unsigned red, unsigned green, unsigned blue,
++ unsigned transp, struct fb_info *info)
++{
++ struct glamofb_handle *glamo = info->par;
++ unsigned int val;
++
++ switch (glamo->fb->fix.visual) {
++ case FB_VISUAL_TRUECOLOR:
++ case FB_VISUAL_DIRECTCOLOR:
++ /* true-colour, use pseuo-palette */
++
++ if (regno < 16) {
++ u32 *pal = glamo->fb->pseudo_palette;
++
++ val = chan_to_field(red, &glamo->fb->var.red);
++ val |= chan_to_field(green, &glamo->fb->var.green);
++ val |= chan_to_field(blue, &glamo->fb->var.blue);
++
++ pal[regno] = val;
++ };
++ break;
++ default:
++ return 1; /* unknown type */
++ }
++
++ return 0;
++}
++
++static inline int glamofb_cmdq_empty(struct glamofb_handle *gfb)
++{
++ return reg_read(gfb, GLAMO_REG_LCD_STATUS1) & (1 << 15);
++}
++
++void glamofb_cmd_mode(struct glamofb_handle *gfb, int on)
++{
++ dev_dbg(gfb->dev, "glamofb_cmd_mode(gfb=%p, on=%d)\n", gfb, on);
++ if (on) {
++ dev_dbg(gfb->dev, "%s: waiting for cmdq empty: ",
++ __FUNCTION__);
++ while (!glamofb_cmdq_empty(gfb))
++ yield();
++ dev_dbg(gfb->dev, "empty!\n");
++
++ /* display the entire frame then switch to command */
++ reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
++ GLAMO_LCD_CMD_TYPE_DISP |
++ GLAMO_LCD_CMD_DATA_FIRE_VSYNC);
++
++ /* wait until LCD is idle */
++ dev_dbg(gfb->dev, "waiting for LCD idle: ");
++ while (!reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12))
++ yield();
++ dev_dbg(gfb->dev, "idle!\n");
++ } else {
++ /* RGB interface needs vsync/hsync */
++ if (reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB)
++ reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
++ GLAMO_LCD_CMD_TYPE_DISP |
++ GLAMO_LCD_CMD_DATA_DISP_SYNC);
++
++ reg_write(gfb, GLAMO_REG_LCD_COMMAND1,
++ GLAMO_LCD_CMD_TYPE_DISP |
++ GLAMO_LCD_CMD_DATA_DISP_FIRE);
++ }
++}
++EXPORT_SYMBOL_GPL(glamofb_cmd_mode);
++
++int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val)
++{
++
++ dev_dbg(gfb->dev, "%s: waiting for cmdq empty\n",
++ __FUNCTION__);
++ while (!glamofb_cmdq_empty(gfb))
++ yield();
++ dev_dbg(gfb->dev, "idle, writing 0x%04x\n", val);
++
++ reg_write(gfb, GLAMO_REG_LCD_COMMAND1, val);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(glamofb_cmd_write);
++
++static struct fb_ops glamofb_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = glamofb_check_var,
++ .fb_set_par = glamofb_set_par,
++ .fb_blank = glamofb_blank,
++ .fb_setcolreg = glamofb_setcolreg,
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++};
++
++static int __init glamofb_probe(struct platform_device *pdev)
++{
++ int rc = -EIO;
++ struct fb_info *fbinfo;
++ struct glamofb_handle *glamofb;
++ struct glamofb_platform_data *mach_info = pdev->dev.platform_data;
++
++ printk(KERN_INFO "SMEDIA Glamo frame buffer driver (C) 2007 "
++ "OpenMoko, Inc.\n");
++
++ fbinfo = framebuffer_alloc(sizeof(struct glamofb_handle), &pdev->dev);
++ if (!fbinfo)
++ return -ENOMEM;
++
++ glamofb = fbinfo->par;
++ glamofb->fb = fbinfo;
++ glamofb->dev = &pdev->dev;
++
++ strcpy(fbinfo->fix.id, "SMedia Glamo");
++
++ glamofb->reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "glamo-fb-regs");
++ if (!glamofb->reg) {
++ dev_err(&pdev->dev, "platform device with no registers?\n");
++ rc = -ENOENT;
++ goto out_free;
++ }
++
++ glamofb->fb_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
++ "glamo-fb-mem");
++ if (!glamofb->fb_res) {
++ dev_err(&pdev->dev, "platform device with no memory ?\n");
++ rc = -ENOENT;
++ goto out_free;
++ }
++
++ glamofb->reg = request_mem_region(glamofb->reg->start,
++ RESSIZE(glamofb->reg), pdev->name);
++ if (!glamofb->reg) {
++ dev_err(&pdev->dev, "failed to request mmio region\n");
++ goto out_free;
++ }
++
++ glamofb->fb_res = request_mem_region(glamofb->fb_res->start,
++ GLAMO_FB_ALLOC, pdev->name);
++ if (!glamofb->fb_res) {
++ dev_err(&pdev->dev, "failed to request vram region\n");
++ goto out_release_reg;
++ }
++
++ /* we want to remap only the registers required for this core
++ * driver. */
++ glamofb->base = ioremap(glamofb->reg->start, RESSIZE(glamofb->reg));
++ if (!glamofb->base) {
++ dev_err(&pdev->dev, "failed to ioremap() mmio memory\n");
++ goto out_release_fb;
++ }
++ fbinfo->fix.smem_start = (unsigned long) glamofb->fb_res->start;
++
++ fbinfo->screen_base = ioremap(glamofb->fb_res->start,
++ RESSIZE(glamofb->fb_res));
++ if (!fbinfo->screen_base) {
++ dev_err(&pdev->dev, "failed to ioremap() vram memory\n");
++ goto out_release_fb;
++ }
++
++ platform_set_drvdata(pdev, fbinfo);
++
++ glamofb->mach_info = pdev->dev.platform_data;
++
++ fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
++ fbinfo->fix.type_aux = 0;
++ fbinfo->fix.xpanstep = 0;
++ fbinfo->fix.ypanstep = 0;
++ fbinfo->fix.ywrapstep = 0;
++ fbinfo->fix.accel = FB_ACCEL_NONE; /* FIXME */
++
++ fbinfo->var.nonstd = 0;
++ fbinfo->var.activate = FB_ACTIVATE_NOW;
++ fbinfo->var.height = mach_info->height;
++ fbinfo->var.width = mach_info->width;
++ fbinfo->var.accel_flags = 0;
++ fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
++
++ fbinfo->fbops = &glamofb_ops;
++ fbinfo->flags = FBINFO_FLAG_DEFAULT;
++ fbinfo->pseudo_palette = &glamofb->pseudo_pal;
++
++ fbinfo->var.xres = mach_info->xres.defval;
++ fbinfo->var.xres_virtual = mach_info->xres.defval;
++ fbinfo->var.yres = mach_info->yres.defval;
++ fbinfo->var.yres_virtual = mach_info->yres.defval;
++ fbinfo->var.bits_per_pixel = mach_info->bpp.defval;
++
++#if 0
++ fbinfo->var.upper_margin =
++ fbinfo->var.lower_margin =
++ fbinfo->var.vsync_len = 2;
++
++ fbinfo->var.left_margin =
++ fbinfo->var.right_margin =
++ fbinfo->var.hsync_len = 8;
++#endif
++
++ fbinfo->var.red.offset = 11;
++ fbinfo->var.green.offset = 5;
++ fbinfo->var.blue.offset = 0;
++ fbinfo->var.transp.offset = 0;
++ fbinfo->var.red.length = 5;
++ fbinfo->var.green.length = 6;
++ fbinfo->var.blue.length = 5;
++ fbinfo->var.transp.length = 0;
++ fbinfo->fix.smem_len = mach_info->xres.max *
++ mach_info->yres.max *
++ mach_info->bpp.max / 8;
++
++ memset(fbinfo->screen_base, 0, fbinfo->fix.smem_len);
++
++ glamo_engine_enable(mach_info->glamo, GLAMO_ENGINE_LCD);
++ glamo_engine_reset(mach_info->glamo, GLAMO_ENGINE_LCD);
++ glamofb_run_script(glamofb, glamo_regs, ARRAY_SIZE(glamo_regs));
++ glamofb_cmd_mode(glamofb, 0);
++
++ rc = register_framebuffer(fbinfo);
++ if (rc < 0) {
++ dev_err(&pdev->dev, "failed to register framebuffer\n");
++ goto out_unmap_fb;
++ }
++
++ if (mach_info->spi_info) {
++ /* register the sibling spi device */
++ mach_info->spi_info->glamofb_handle = glamofb;
++ glamo_spi_dev.dev.parent = &pdev->dev;
++ glamo_spi_dev.dev.platform_data = mach_info->spi_info;
++ platform_device_register(&glamo_spi_dev);
++ }
++
++ printk(KERN_INFO "fb%d: %s frame buffer device\n",
++ fbinfo->node, fbinfo->fix.id);
++
++ return 0;
++
++out_unmap_fb:
++ iounmap(fbinfo->screen_base);
++out_unmap:
++ iounmap(glamofb->base);
++out_release_fb:
++ release_mem_region(glamofb->fb_res->start, RESSIZE(glamofb->fb_res));
++out_release_reg:
++ release_mem_region(glamofb->reg->start, RESSIZE(glamofb->reg));
++out_free:
++ framebuffer_release(fbinfo);
++ return rc;
++}
++
++static int glamofb_remove(struct platform_device *pdev)
++{
++ struct glamofb_handle *glamofb = platform_get_drvdata(pdev);
++
++ platform_set_drvdata(pdev, NULL);
++ iounmap(glamofb->base);
++ release_mem_region(glamofb->reg->start, RESSIZE(glamofb->reg));
++ kfree(glamofb);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int glamofb_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ return 0;
++}
++
++static int glamofb_resume(struct platform_device *pdev)
++{
++ return 0;
++}
++#else
++#define glamofb_suspend NULL
++#define glamofb_resume NULL
++#endif
++
++static struct platform_driver glamofb_driver = {
++ .probe = glamofb_probe,
++ .remove = glamofb_remove,
++ .suspend = glamofb_suspend,
++ .resume = glamofb_resume,
++ .driver = {
++ .name = "glamo-fb",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __devinit glamofb_init(void)
++{
++ return platform_driver_register(&glamofb_driver);
++}
++
++static void __exit glamofb_cleanup(void)
++{
++ platform_driver_unregister(&glamofb_driver);
++}
++
++module_init(glamofb_init);
++module_exit(glamofb_cleanup);
++
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
++MODULE_DESCRIPTION("Smedia Glamo 336x/337x framebuffer driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/include/linux/glamofb.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/glamofb.h 2007-07-16 15:18:37.206068780 +0200
+@@ -0,0 +1,29 @@
++#ifndef _LINUX_GLAMOFB_H
++#define _LINUX_GLAMOFB_H
++
++#include <linux/spi/glamo.h>
++
++struct glamofb_val {
++ unsigned int defval;
++ unsigned int min;
++ unsigned int max;
++};
++
++struct glamo_core;
++
++struct glamofb_platform_data {
++ int width, height;
++
++ struct glamofb_val xres;
++ struct glamofb_val yres;
++ struct glamofb_val bpp;
++
++ struct glamo_spi_info *spi_info;
++ struct glamo_spigpio_info *spigpio_info;
++ struct glamo_core *glamo;
++};
++
++void glamofb_cmd_mode(struct glamofb_handle *gfb, int on);
++int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val);
++
++#endif
+Index: linux-2.6.22.1/include/linux/spi/glamo.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/spi/glamo.h 2007-07-16 15:18:37.230070146 +0200
+@@ -0,0 +1,28 @@
++#ifndef __GLAMO_SPI_H
++#define __GLAMO_SPI_H
++
++#include <linux/glamo-gpio.h>
++
++struct spi_board_info;
++struct glamofb_handle;
++struct glamo_core;
++
++struct glamo_spi_info {
++ unsigned long board_size;
++ struct spi_board_info *board_info;
++ struct glamofb_handle *glamofb_handle;
++};
++
++struct glamo_spigpio_info {
++ unsigned int pin_clk;
++ unsigned int pin_mosi;
++ unsigned int pin_miso;
++ unsigned int pin_cs;
++
++ unsigned int board_size;
++ struct spi_board_info* board_info;
++ struct glamo_core *glamo;
++};
++
++
++#endif
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-core.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-core.h 2007-07-16 15:18:37.254071515 +0200
+@@ -0,0 +1,51 @@
++#ifndef __GLAMO_CORE_H
++#define __GLAMO_CORE_H
++
++struct glamo_core {
++ int irq;
++ struct resource *mem;
++ struct resource *mem_core;
++ void __iomem *base;
++ struct platform_device *pdev;
++ struct glamofb_platform_data *pdata;
++ u_int16_t type;
++ u_int16_t revision;
++ spinlock_t lock;
++};
++
++struct glamo_script {
++ u_int16_t reg;
++ u_int16_t val;
++};
++
++int glamo_run_script(struct glamo_core *glamo,
++ struct glamo_script *script, int len);
++
++enum glamo_engine {
++ GLAMO_ENGINE_CAPTURE,
++ GLAMO_ENGINE_ISP,
++ GLAMO_ENGINE_JPEG,
++ GLAMO_ENGINE_MPEG_ENC,
++ GLAMO_ENGINE_MPEG_DEC,
++ GLAMO_ENGINE_LCD,
++ GLAMO_ENGINE_CMDQ,
++ GLAMO_ENGINE_2D,
++ GLAMO_ENGINE_3D,
++ GLAMO_ENGINE_MMC,
++ GLAMO_ENGINE_MICROP0,
++ GLAMO_ENGINE_RISC,
++ GLAMO_ENGINE_MICROP1_MPEG_ENC,
++ GLAMO_ENGINE_MICROP1_MPEG_DEC,
++#if 0
++ GLAMO_ENGINE_H264_DEC,
++ GLAMO_ENGINE_RISC1,
++ GLAMO_ENGINE_SPI,
++#endif
++ __NUM_GLAMO_ENGINES
++};
++
++int glamo_engine_enable(struct glamo_core *glamo, enum glamo_engine engine);
++int glamo_engine_disable(struct glamo_core *glamo, enum glamo_engine engine);
++void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine);
++
++#endif /* __GLAMO_CORE_H */
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-gpio.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-gpio.c 2007-07-16 15:18:37.278072884 +0200
+@@ -0,0 +1,62 @@
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include <linux/io.h>
++
++#include <linux/glamo-gpio.h>
++
++#include "glamo-core.h"
++#include "glamo-regs.h"
++
++void glamo_gpio_setpin(struct glamo_core *glamo, unsigned int pin,
++ unsigned int value)
++{
++ unsigned int reg = REG_OF_GPIO(pin);
++ u_int16_t tmp;
++
++ spin_lock(&glamo->lock);
++ tmp = readw(glamo->base + reg);
++ if (value)
++ tmp |= OUTPUT_BIT(pin);
++ else
++ tmp &= ~OUTPUT_BIT(pin);
++ writew(tmp, glamo->base + reg);
++ spin_unlock(&glamo->lock);
++}
++EXPORT_SYMBOL(glamo_gpio_setpin);
++
++int glamo_gpio_getpin(struct glamo_core *glamo, unsigned int pin)
++{
++ return readw(REG_OF_GPIO(pin)) & INPUT_BIT(pin) ? 1 : 0;
++}
++EXPORT_SYMBOL(glamo_gpio_getpin);
++
++void glamo_gpio_cfgpin(struct glamo_core *glamo, unsigned int pinfunc)
++{
++ unsigned int reg = REG_OF_GPIO(pinfunc);
++ u_int16_t tmp;
++
++ spin_lock(&glamo->lock);
++ tmp = readw(glamo->base + reg);
++
++ if ((pinfunc & 0x00f0) == GLAMO_GPIO_F_FUNC) {
++ /* pin is a function pin: clear gpio bit */
++ tmp &= ~FUNC_BIT(pinfunc);
++ } else {
++ /* pin is gpio: set gpio bit */
++ tmp |= FUNC_BIT(pinfunc);
++
++ if (pinfunc & GLAMO_GPIO_F_IN) {
++ /* gpio input: set bit to disable output mode */
++ tmp |= GPIO_OUT_BIT(pinfunc);
++ } else if (pinfunc & GLAMO_GPIO_F_OUT) {
++ /* gpio output: clear bit to enable output mode */
++ tmp &= ~GPIO_OUT_BIT(pinfunc);
++ }
++ }
++ writew(tmp, glamo->base + reg);
++ spin_unlock(&glamo->lock);
++}
++EXPORT_SYMBOL(glamo_gpio_cfgpin);
++
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-lcm-spi.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-lcm-spi.c 2007-07-16 15:18:37.294073795 +0200
+@@ -0,0 +1,241 @@
++/*
++ * Copyright (C) 2007 OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ * Smedia Glamo GPIO based SPI driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This driver currently only implements a minimum subset of the hardware
++ * features, esp. those features that are required to drive the jbt6k74
++ * LCM controller asic in the TD028TTEC1 LCM.
++ *
++*/
++
++#define DEBUG
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/spinlock.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/spi/glamo.h>
++
++#include <linux/glamofb.h>
++
++#include <asm/hardware.h>
++
++#include "glamo-core.h"
++#include "glamo-regs.h"
++
++struct glamo_spi {
++ struct spi_bitbang bitbang;
++ struct spi_master *master;
++ struct glamo_spi_info *info;
++ struct device *dev;
++};
++
++static inline struct glamo_spi *to_gs(struct spi_device *spi)
++{
++ return spi->controller_data;
++}
++
++static int glamo_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct glamo_spi *gs = to_gs(spi);
++ unsigned int bpw;
++
++ bpw = t ? t->bits_per_word : spi->bits_per_word;
++
++ if (bpw != 9 && bpw != 8) {
++ dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void glamo_spi_chipsel(struct spi_device *spi, int value)
++{
++#if 0
++ struct glamo_spi *gs = to_gs(spi);
++
++ dev_dbg(&spi->dev, "chipsel %d: spi=%p, gs=%p, info=%p, handle=%p\n",
++ value, spi, gs, gs->info, gs->info->glamofb_handle);
++
++ glamofb_cmd_mode(gs->info->glamofb_handle, value);
++#endif
++}
++
++static int glamo_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct glamo_spi *gs = to_gs(spi);
++ const u_int16_t *ui16 = (const u_int16_t *) t->tx_buf;
++ u_int16_t nine_bits;
++ int i;
++
++ dev_dbg(&spi->dev, "txrx: tx %p, rx %p, bpw %d, len %d\n",
++ t->tx_buf, t->rx_buf, t->bits_per_word, t->len);
++
++ if (spi->bits_per_word == 9)
++ nine_bits = (1 << 9);
++ else
++ nine_bits = 0;
++
++ if (t->len > 3 * sizeof(u_int16_t)) {
++ dev_err(&spi->dev, "this driver doesn't support "
++ "%u sized xfers\n", t->len);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < t->len/sizeof(u_int16_t); i++) {
++ /* actually transfer the data */
++#if 1
++ glamofb_cmd_write(gs->info->glamofb_handle,
++ GLAMO_LCD_CMD_TYPE_SERIAL | nine_bits |
++ (1 << 10) | (1 << 11) | (ui16[i] & 0x1ff));
++#endif
++ /* FIXME: fire ?!? */
++ if (i == 0 && (ui16[i] & 0x1ff) == 0x29) {
++ dev_dbg(&spi->dev, "leaving command mode\n");
++ glamofb_cmd_mode(gs->info->glamofb_handle, 0);
++ }
++ }
++
++ return t->len;
++}
++
++static int glamo_spi_setup(struct spi_device *spi)
++{
++ int ret;
++
++ if (!spi->bits_per_word)
++ spi->bits_per_word = 9;
++
++ /* FIXME: hardware can do this */
++ if (spi->mode & SPI_LSB_FIRST)
++ return -EINVAL;
++
++ ret = glamo_spi_setupxfer(spi, NULL);
++ if (ret < 0) {
++ dev_err(&spi->dev, "setupxfer returned %d\n", ret);
++ return ret;
++ }
++
++ dev_dbg(&spi->dev, "%s: mode %d, %u bpw\n",
++ __FUNCTION__, spi->mode, spi->bits_per_word);
++
++ return 0;
++}
++
++static int glamo_spi_probe(struct platform_device *pdev)
++{
++ struct spi_master *master;
++ struct glamo_spi *sp;
++ int ret;
++ int i;
++
++ master = spi_alloc_master(&pdev->dev, sizeof(struct glamo_spi));
++ if (master == NULL) {
++ dev_err(&pdev->dev, "failed to allocate spi master\n");
++ ret = -ENOMEM;
++ goto err;
++ }
++
++ sp = spi_master_get_devdata(master);
++ memset(sp, 0, sizeof(struct glamo_spi));
++
++ sp->master = spi_master_get(master);
++ sp->info = pdev->dev.platform_data;
++ if (!sp->info) {
++ dev_err(&pdev->dev, "can't operate without platform data\n");
++ ret = -EIO;
++ goto err_no_pdev;
++ }
++ dev_dbg(&pdev->dev, "sp->info(pdata) = %p\n", sp->info);
++
++ sp->dev = &pdev->dev;
++
++ platform_set_drvdata(pdev, sp);
++
++ sp->bitbang.master = sp->master;
++ sp->bitbang.setup_transfer = glamo_spi_setupxfer;
++ sp->bitbang.chipselect = glamo_spi_chipsel;
++ sp->bitbang.txrx_bufs = glamo_spi_txrx;
++ sp->bitbang.master->setup = glamo_spi_setup;
++
++ ret = spi_bitbang_start(&sp->bitbang);
++ if (ret)
++ goto err_no_bitbang;
++
++ /* register the chips to go with the board */
++
++ glamofb_cmd_mode(sp->info->glamofb_handle, 1);
++
++ for (i = 0; i < sp->info->board_size; i++) {
++ dev_info(&pdev->dev, "registering %p: %s\n",
++ &sp->info->board_info[i],
++ sp->info->board_info[i].modalias);
++
++ sp->info->board_info[i].controller_data = sp;
++ spi_new_device(master, sp->info->board_info + i);
++ }
++
++ return 0;
++
++err_no_bitbang:
++ platform_set_drvdata(pdev, NULL);
++err_no_pdev:
++ spi_master_put(sp->bitbang.master);
++err:
++ return ret;
++
++}
++
++static int glamo_spi_remove(struct platform_device *pdev)
++{
++ struct glamo_spi *sp = platform_get_drvdata(pdev);
++
++ spi_bitbang_stop(&sp->bitbang);
++ spi_master_put(sp->bitbang.master);
++
++ return 0;
++}
++
++#define glamo_spi_suspend NULL
++#define glamo_spi_resume NULL
++
++static struct platform_driver glamo_spi_drv = {
++ .probe = glamo_spi_probe,
++ .remove = glamo_spi_remove,
++ .suspend = glamo_spi_suspend,
++ .resume = glamo_spi_resume,
++ .driver = {
++ .name = "glamo-lcm-spi",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init glamo_spi_init(void)
++{
++ return platform_driver_register(&glamo_spi_drv);
++}
++
++static void __exit glamo_spi_exit(void)
++{
++ platform_driver_unregister(&glamo_spi_drv);
++}
++
++module_init(glamo_spi_init);
++module_exit(glamo_spi_exit);
++
++MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>")
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/drivers/video/glamo/glamo-spi-gpio.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/video/glamo/glamo-spi-gpio.c 2007-07-16 15:18:37.310074706 +0200
+@@ -0,0 +1,250 @@
++/*
++ * Copyright (C) 2007 OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ *
++ * Smedia Glamo GPIO based SPI driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * This driver currently only implements a minimum subset of the hardware
++ * features, esp. those features that are required to drive the jbt6k74
++ * LCM controller asic in the TD028TTEC1 LCM.
++ *
++*/
++
++#define DEBUG
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/spinlock.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++
++#include <linux/spi/spi.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/spi/glamo.h>
++
++#include <linux/glamofb.h>
++
++#include <asm/hardware.h>
++
++#include "glamo-core.h"
++#include "glamo-regs.h"
++
++struct glamo_spigpio {
++ struct spi_bitbang bitbang;
++ struct spi_master *master;
++ struct glamo_spigpio_info *info;
++ struct glamo_core *glamo;
++ //struct device *dev;
++};
++
++static inline struct glamo_spigpio *to_sg(struct spi_device *spi)
++{
++ return spi->controller_data;
++}
++
++static inline void setsck(struct spi_device *dev, int on)
++{
++ struct glamo_spigpio *sg = to_sg(dev);
++ glamo_gpio_setpin(sg->glamo, sg->info->pin_clk, on ? 1 : 0);
++}
++
++static inline void setmosi(struct spi_device *dev, int on)
++{
++ struct glamo_spigpio *sg = to_sg(dev);
++ glamo_gpio_setpin(sg->glamo, sg->info->pin_mosi, on ? 1 : 0);
++}
++
++static inline u32 getmiso(struct spi_device *dev)
++{
++ struct glamo_spigpio *sg = to_sg(dev);
++ if (sg->info->pin_miso)
++ return glamo_gpio_getpin(sg->glamo, sg->info->pin_miso) ? 1 : 0;
++ else
++ return 0;
++}
++
++#define spidelay(x) ndelay(x)
++
++#define EXPAND_BITBANG_TXRX
++#include <linux/spi/spi_bitbang.h>
++
++static u32 glamo_spigpio_txrx_mode0(struct spi_device *spi,
++ unsigned nsecs, u32 word, u8 bits)
++{
++ return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
++}
++
++static u32 glamo_spigpio_txrx_mode1(struct spi_device *spi,
++ unsigned nsecs, u32 word, u8 bits)
++{
++ return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
++}
++
++static u32 glamo_spigpio_txrx_mode2(struct spi_device *spi,
++ unsigned nsecs, u32 word, u8 bits)
++{
++ return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
++}
++
++static u32 glamo_spigpio_txrx_mode3(struct spi_device *spi,
++ unsigned nsecs, u32 word, u8 bits)
++{
++ return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
++}
++
++
++#if 0
++static int glamo_spigpio_setupxfer(struct spi_device *spi, struct spi_transfer *t)
++{
++ struct glamo_spi *gs = to_sg(spi);
++ unsigned int bpw;
++
++ bpw = t ? t->bits_per_word : spi->bits_per_word;
++
++ if (bpw != 9 && bpw != 8) {
++ dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++#endif
++
++static void glamo_spigpio_chipsel(struct spi_device *spi, int value)
++{
++ struct glamo_spigpio *gs = to_sg(spi);
++#if 0
++ dev_dbg(&spi->dev, "chipsel %d: spi=%p, gs=%p, info=%p, handle=%p\n",
++ value, spi, gs, gs->info, gs->info->glamo);
++#endif
++ glamo_gpio_setpin(gs->glamo, gs->info->pin_cs, value ? 0 : 1);
++}
++
++
++static int glamo_spigpio_probe(struct platform_device *pdev)
++{
++ struct spi_master *master;
++ struct glamo_spigpio *sp;
++ int ret;
++ int i;
++
++ master = spi_alloc_master(&pdev->dev, sizeof(struct glamo_spigpio));
++ if (master == NULL) {
++ dev_err(&pdev->dev, "failed to allocate spi master\n");
++ ret = -ENOMEM;
++ goto err;
++ }
++
++ sp = spi_master_get_devdata(master);
++ platform_set_drvdata(pdev, sp);
++ sp->info = pdev->dev.platform_data;
++ if (!sp->info) {
++ dev_err(&pdev->dev, "can't operate without platform data\n");
++ ret = -EIO;
++ goto err_no_pdev;
++ }
++
++
++ //memset(sp, 0, sizeof(struct glamo_spigpio));
++
++ sp->master = spi_master_get(master);
++ sp->glamo = sp->info->glamo;
++
++ sp->bitbang.master = sp->master;
++ sp->bitbang.chipselect = glamo_spigpio_chipsel;
++ sp->bitbang.txrx_word[SPI_MODE_0] = glamo_spigpio_txrx_mode0;
++ sp->bitbang.txrx_word[SPI_MODE_1] = glamo_spigpio_txrx_mode1;
++ sp->bitbang.txrx_word[SPI_MODE_2] = glamo_spigpio_txrx_mode2;
++ sp->bitbang.txrx_word[SPI_MODE_3] = glamo_spigpio_txrx_mode3;
++
++ /* set state of spi pins */
++ glamo_gpio_setpin(sp->glamo, sp->info->pin_clk, 0);
++ glamo_gpio_setpin(sp->glamo, sp->info->pin_mosi, 0);
++ glamo_gpio_setpin(sp->glamo, sp->info->pin_cs, 1);
++
++ glamo_gpio_cfgpin(sp->glamo, sp->info->pin_clk);
++ glamo_gpio_cfgpin(sp->glamo, sp->info->pin_mosi);
++ glamo_gpio_cfgpin(sp->glamo, sp->info->pin_cs);
++ if (sp->info->pin_miso)
++ glamo_gpio_cfgpin(sp->glamo, sp->info->pin_miso);
++
++#if 0
++ sp->dev = &pdev->dev;
++
++ sp->bitbang.setup_transfer = glamo_spi_setupxfer;
++ sp->bitbang.txrx_bufs = glamo_spi_txrx;
++ sp->bitbang.master->setup = glamo_spi_setup;
++#endif
++
++ ret = spi_bitbang_start(&sp->bitbang);
++ if (ret)
++ goto err_no_bitbang;
++
++ /* register the chips to go with the board */
++
++ for (i = 0; i < sp->info->board_size; i++) {
++ dev_info(&pdev->dev, "registering %p: %s\n",
++ &sp->info->board_info[i],
++ sp->info->board_info[i].modalias);
++
++ sp->info->board_info[i].controller_data = sp;
++ spi_new_device(master, sp->info->board_info + i);
++ }
++
++ return 0;
++
++err_no_bitbang:
++ platform_set_drvdata(pdev, NULL);
++err_no_pdev:
++ spi_master_put(sp->bitbang.master);
++err:
++ return ret;
++
++}
++
++static int glamo_spigpio_remove(struct platform_device *pdev)
++{
++ struct glamo_spigpio *sp = platform_get_drvdata(pdev);
++
++ spi_bitbang_stop(&sp->bitbang);
++ spi_master_put(sp->bitbang.master);
++
++ return 0;
++}
++
++#define glamo_spigpio_suspend NULL
++#define glamo_spigpio_resume NULL
++
++static struct platform_driver glamo_spi_drv = {
++ .probe = glamo_spigpio_probe,
++ .remove = glamo_spigpio_remove,
++ .suspend = glamo_spigpio_suspend,
++ .resume = glamo_spigpio_resume,
++ .driver = {
++ .name = "glamo-spi-gpio",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init glamo_spi_init(void)
++{
++ return platform_driver_register(&glamo_spi_drv);
++}
++
++static void __exit glamo_spi_exit(void)
++{
++ platform_driver_unregister(&glamo_spi_drv);
++}
++
++module_init(glamo_spi_init);
++module_exit(glamo_spi_exit);
++
++MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver");
++MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>")
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/include/linux/glamo-gpio.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/glamo-gpio.h 2007-07-16 15:18:37.362077670 +0200
+@@ -0,0 +1,99 @@
++#ifndef __GLAMO_GPIO_H
++#define __GLAMO_GPIO_H
++
++struct glamo_core;
++
++#define GLAMO_GPIO_BANKA 0x0000
++#define GLAMO_GPIO_BANKB 0x1000
++#define GLAMO_GPIO_BANKC 0x2000
++#define GLAMO_GPIO_BANKD 0x3000
++
++#define GLAMO_GPIONO(bank, pin) ((bank & 0xf000) | ((pin & 0xf) << 8))
++
++#define GLAMO_GPIO_F_IN 0x0010
++#define GLAMO_GPIO_F_OUT 0x0020
++#define GLAMO_GPIO_F_FUNC 0x0030
++
++#define GLAMO_GPIO0 GLAMO_GPIONO(GLAMO_GPIO_BANKA, 0)
++#define GLAMO_GPIO0_INPUT (GLAMO_GPIO0 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO0_OUTPUT (GLAMO_GPIO0 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO0_HA20 (GLAMO_GPIO0 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO1 GLAMO_GPIONO(GLAMO_GPIO_BANKA, 1)
++#define GLAMO_GPIO1_INPUT (GLAMO_GPIO1 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO1_OUTPUT (GLAMO_GPIO1 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO1_HA21 (GLAMO_GPIO1 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO2 GLAMO_GPIONO(GLAMO_GPIO_BANKA, 2)
++#define GLAMO_GPIO2_INPUT (GLAMO_GPIO2 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO2_OUTPUT (GLAMO_GPIO2 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO2_HA22 (GLAMO_GPIO2 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO3 GLAMO_GPIONO(GLAMO_GPIO_BANKA, 3)
++#define GLAMO_GPIO3_INPUT (GLAMO_GPIO3 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO3_OUTPUT (GLAMO_GPIO3 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO3_HA23 (GLAMO_GPIO3 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO4 GLAMO_GPIONO(GLAMO_GPIO_BANKB, 0)
++#define GLAMO_GPIO4_INPUT (GLAMO_GPIO4 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO4_OUTPUT (GLAMO_GPIO4 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO4_nLCS0 (GLAMO_GPIO4 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO5 GLAMO_GPIONO(GLAMO_GPIO_BANKB, 1)
++#define GLAMO_GPIO5_INPUT (GLAMO_GPIO5 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO5_OUTPUT (GLAMO_GPIO5 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO5_nLCS1 (GLAMO_GPIO5 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO6 GLAMO_GPIONO(GLAMO_GPIO_BANKB, 2)
++#define GLAMO_GPIO6_INPUT
++#define GLAMO_GPIO6_OUTPUT
++#define GLAMO_GPIO6_LDCLK
++
++#define GLAMO_GPIO7 GLAMO_GPIONO(GLAMO_GPIO_BANKB, 3)
++#define GLAMO_GPIO7_INPUT
++#define GLAMO_GPIO7_OUTPUT
++#define GLAMO_GPIO7_nLDE
++
++#define GLAMO_GPIO8 GLAMO_GPIONO(GLAMO_GPIO_BANKC, 0)
++#define GLAMO_GPIO8_INPUT
++#define GLAMO_GPIO8_OUTPUT
++#define GLAMO_GPIO8_LD16
++
++#define GLAMO_GPIO9 GLAMO_GPIONO(GLAMO_GPIO_BANKC, 1)
++#define GLAMO_GPIO9_INPUT
++#define GLAMO_GPIO9_OUTPUT
++#define GLAMO_GPIO9_LD17
++
++#define GLAMO_GPIO10 GLAMO_GPIONO(GLAMO_GPIO_BANKC, 2)
++#define GLAMO_GPIO10_INPUT (GLAMO_GPIO10 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO10_OUTPUT (GLAMO_GPIO10 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO10_LSCK (GLAMO_GPIO10 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO11 GLAMO_GPIONO(GLAMO_GPIO_BANKC, 3)
++#define GLAMO_GPIO11_INPUT (GLAMO_GPIO11 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO11_OUTPUT (GLAMO_GPIO11 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO11_LSDA (GLAMO_GPIO11 | GLAMO_GPIO_F_FUNC)
++
++#define GLAMO_GPIO12 GLAMO_GPIONO(GLAMO_GPIO_BANKD, 0)
++#define GLAMO_GPIO12_INPUT (GLAMO_GPIO12 | GLAMO_GPIO_F_IN)
++#define GLAMO_GPIO12_OUTPUT (GLAMO_GPIO12 | GLAMO_GPIO_F_OUT)
++#define GLAMO_GPIO12_LSA0 (GLAMO_GPIO12 | GLAMO_GPIO_F_FUNC)
++
++
++/* shift by 11 is implicit of shift by 12, multiplied by 2 */
++#define REG_OF_GPIO(gpio) (((gpio & 0xf000) >> 11) + GLAMO_REG_GPIO_GEN1)
++#define NUM_OF_GPIO(gpio) ((gpio & 0x0f00) >> 8)
++#define GPIO_OUT_BIT(gpio) (1 << (NUM_OF_GPIO(gpio) + 0))
++#define OUTPUT_BIT(gpio) (1 << (NUM_OF_GPIO(gpio) + 4))
++#define INPUT_BIT(gpio) (1 << (NUM_OF_GPIO(gpio) + 8))
++#define FUNC_BIT(gpio) (1 << (NUM_OF_GPIO(gpio) + 12))
++
++void glamo_gpio_setpin(struct glamo_core *glamo, unsigned int pin,
++ unsigned int value);
++
++int glamo_gpio_getpin(struct glamo_core *glamo, unsigned int pin);
++
++void glamo_gpio_cfgpin(struct glamo_core *glamo, unsigned int pinfunc);
++
++
++#endif /* _GLAMO_GPIO */
Added: developers/nbd/patches-2.6.22/490-s3c24xx-nand-largepage.patch
===================================================================
--- developers/nbd/patches-2.6.22/490-s3c24xx-nand-largepage.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/490-s3c24xx-nand-largepage.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,129 @@
+MTD: S3C24XX large page NAND support
+
+This adds support for using large page NAND devices
+with the S3C24XX NAND controller. This also adds the
+file Documentation/arm/Samsung-S3C24XX/NAND.txt to
+describe the differences.
+
+Signed-off-by: Ben Dooks <ben-linux at fluff.org>
+
+Index: linux-2.6.21.3-moko/drivers/mtd/nand/s3c2410.c
+===================================================================
+--- linux-2.6.21.3-moko.orig/drivers/mtd/nand/s3c2410.c
++++ linux-2.6.21.3-moko/drivers/mtd/nand/s3c2410.c
+@@ -473,7 +473,7 @@
+ ecc_code[1] = ecc >> 8;
+ ecc_code[2] = ecc >> 16;
+
+- pr_debug("%s: returning ecc %06lx\n", __func__, ecc);
++ pr_debug("%s: returning ecc %06lx\n", __func__, ecc & 0xffffff);
+
+ return 0;
+ }
+@@ -648,9 +648,6 @@
+ chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ chip->ecc.correct = s3c2410_nand_correct_data;
+ chip->ecc.mode = NAND_ECC_HW;
+- chip->ecc.size = 512;
+- chip->ecc.bytes = 3;
+- chip->ecc.layout = &nand_hw_eccoob;
+
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+@@ -674,6 +671,34 @@
+ }
+ }
+
++/* s3c2410_nand_update_chip
++ *
++ * post-probe chip update, to change any items, such as the
++ * layout for large page nand
++ */
++
++static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,
++ struct s3c2410_nand_mtd *nmtd)
++{
++ struct nand_chip *chip = &nmtd->chip;
++
++ printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift);
++
++ if (hardware_ecc) {
++ /* change the behaviour depending on wether we are using
++ * the large or small page nand device */
++
++ if (chip->page_shift > 10) {
++ chip->ecc.size = 256;
++ chip->ecc.bytes = 3;
++ } else {
++ chip->ecc.size = 512;
++ chip->ecc.bytes = 3;
++ chip->ecc.layout = &nand_hw_eccoob;
++ }
++ }
++}
++
+ /* s3c2410_nand_probe
+ *
+ * called by device layer when it finds a device matching
+@@ -780,9 +805,12 @@
+
+ s3c2410_nand_init_chip(info, nmtd, sets);
+
+- nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
++ nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
++ (sets) ? sets->nr_chips : 1);
+
+ if (nmtd->scan_res == 0) {
++ s3c2410_nand_update_chip(info, nmtd);
++ nand_scan_tail(&nmtd->mtd);
+ s3c2410_nand_add_partition(info, nmtd, sets);
+ }
+
+Index: linux-2.6.21.3-moko/Documentation/arm/Samsung-S3C24XX/NAND.txt
+===================================================================
+--- /dev/null
++++ linux-2.6.21.3-moko/Documentation/arm/Samsung-S3C24XX/NAND.txt
+@@ -0,0 +1,30 @@
++ S3C24XX NAND Support
++ ====================
++
++Introduction
++------------
++
++Small Page NAND
++---------------
++
++The driver uses a 512 byte (1 page) ECC code for this setup. The
++ECC code is not directly compatible with the default kernel ECC
++code, so the driver enforces its own OOB layout and ECC parameters
++
++Large Page NAND
++---------------
++
++The driver is capable of handling NAND flash with a 2KiB page
++size, with support for hardware ECC generation and correction.
++
++Unlike the 512byte page mode, the driver generates ECC data for
++each 256 byte block in an 2KiB page. This means that more than
++one error in a page can be rectified. It also means that the
++OOB layout remains the default kernel layout for these flashes.
++
++
++Document Author
++---------------
++
++Ben Dooks, Copyright 2007 Simtec Electronics
++
+Index: linux-2.6.21.3-moko/Documentation/arm/Samsung-S3C24XX/Overview.txt
+===================================================================
+--- linux-2.6.21.3-moko.orig/Documentation/arm/Samsung-S3C24XX/Overview.txt
++++ linux-2.6.21.3-moko/Documentation/arm/Samsung-S3C24XX/Overview.txt
+@@ -156,6 +156,8 @@
+ controller. If there are any problems the latest linux-mtd
+ code can be found from http://www.linux-mtd.infradead.org/
+
++ For more information see Documentation/arm/Samsung-S3C24XX/NAND.txt
++
+
+ Serial
+ ------
Added: developers/nbd/patches-2.6.22/500-gta02-core.patch
===================================================================
--- developers/nbd/patches-2.6.22/500-gta02-core.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/500-gta02-core.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,844 @@
+Index: linux-2.6.22.1/arch/arm/mach-s3c2440/mach-gta02.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/mach-gta02.c 2007-07-16 15:53:59.459009082 +0200
+@@ -0,0 +1,620 @@
++/*
++ * linux/arch/arm/mach-s3c2440/mach-gta02.c
++ *
++ * S3C2440 Machine Support for the FIC GTA02 (Neo1973)
++ *
++ * Copyright (C) 2006-2007 by OpenMoko, Inc.
++ * Author: Harald Welte <laforge at openmoko.org>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of
++ * the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
++ * MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/interrupt.h>
++#include <linux/list.h>
++#include <linux/timer.h>
++#include <linux/init.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++#include <linux/serial_core.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/glamo.h>
++#include <linux/spi/spi_bitbang.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/nand_ecc.h>
++#include <linux/mtd/partitions.h>
++
++#include <linux/pcf50633.h>
++
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/mach/irq.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach-types.h>
++
++#include <asm/arch/regs-serial.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-gpioj.h>
++#include <asm/arch/fb.h>
++#include <asm/arch/udc.h>
++#include <asm/arch/nand.h>
++#include <asm/arch/mci.h>
++#include <asm/arch/ts.h>
++#include <asm/arch/spi.h>
++#include <asm/arch/spi-gpio.h>
++#include <asm/arch/usb-control.h>
++
++#include <asm/arch/gta01.h>
++#include <asm/arch/gta02.h>
++
++#include <asm/plat-s3c24xx/devs.h>
++#include <asm/plat-s3c24xx/cpu.h>
++#include <asm/plat-s3c24xx/pm.h>
++
++#include <linux/glamofb.h>
++
++static struct map_desc gta02_iodesc[] __initdata = {
++ {
++ .virtual = 0xe0000000,
++ .pfn = __phys_to_pfn(S3C2410_CS3+0x01000000),
++ .length = SZ_1M,
++ .type = MT_DEVICE
++ },
++};
++
++#define UCON S3C2410_UCON_DEFAULT
++#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
++#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE
++
++static struct s3c2410_uartcfg gta02_uartcfgs[] = {
++ [0] = {
++ .hwport = 0,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++ [1] = {
++ .hwport = 1,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++ [2] = {
++ .hwport = 2,
++ .flags = 0,
++ .ucon = UCON,
++ .ulcon = ULCON,
++ .ufcon = UFCON,
++ },
++
++};
++
++/* PMU driver info */
++
++static int pmu_callback(struct device *dev, unsigned int feature,
++ enum pmu_event event)
++{
++ switch (feature) {
++ case PCF50633_FEAT_MBC:
++ switch (event) {
++ case PMU_EVT_INSERT:
++ case PMU_EVT_USB_INSERT:
++ pcf50633_charge_fast(pcf50633_global, 1);
++ break;
++ case PMU_EVT_REMOVE:
++ case PMU_EVT_USB_REMOVE:
++ pcf50633_charge_fast(pcf50633_global, 0);
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static struct pcf50633_platform_data gta02_pcf_pdata = {
++ .used_features = PCF50633_FEAT_MBC |
++ PCF50633_FEAT_BBC |
++ PCF50633_FEAT_RTC |
++ PCF50633_FEAT_CHGCUR |
++ PCF50633_FEAT_BATVOLT |
++ PCF50633_FEAT_BATTEMP,
++ .onkey_seconds_required = 3,
++ .cb = &pmu_callback,
++ .r_fix_batt = 10000,
++ .r_fix_batt_par = 10000,
++ .r_sense_milli = 220,
++ .rails = {
++ [PCF50633_REGULATOR_AUTO] = {
++ .name = "io_3v3",
++ .flags = PMU_VRAIL_F_SUSPEND_ON,
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50633_REGULATOR_DOWN1] = {
++ .name = "core_1v3",
++ .voltage = {
++ .init = 1300,
++ .max = 1600,
++ },
++ },
++ [PCF50633_REGULATOR_DOWN2] = {
++ .name = "core_1v8",
++ .voltage = {
++ .init = 1800,
++ .max = 1800,
++ },
++ },
++ [PCF50633_REGULATOR_HCLDO] = {
++ .name = "sd_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50633_REGULATOR_LDO1] = {
++ .name = "stby_1v3",
++ .flags = PMU_VRAIL_F_SUSPEND_ON,
++ .voltage = {
++ .init = 1300,
++ .max = 1330,
++ },
++ },
++ [PCF50633_REGULATOR_LDO2] = {
++ .name = "codec_3v3",
++ .voltage = {
++ .init = 3300,
++ .max = 3300,
++ },
++ },
++ [PCF50633_REGULATOR_LDO3] = {
++ .name = "lcm_3v",
++ .voltage = {
++ .init = 3000,
++ .max = 3000,
++ },
++ },
++ [PCF50633_REGULATOR_LDO4] = {
++ .name = "gl_2v5",
++ .voltage = {
++ .init = 2500,
++ .max = 2500,
++ },
++ },
++ [PCF50633_REGULATOR_LDO5] = {
++ .name = "gl_1v5",
++ .voltage = {
++ .init = 1500,
++ .max = 1500,
++ },
++ },
++ [PCF50633_REGULATOR_LDO6] = {
++ .name = "user1",
++ .voltage = {
++ .init = 0,
++ .max = 3300,
++ },
++ },
++ },
++};
++
++static void cfg_pmu_vrail(struct pmu_voltage_rail *vrail, char *name,
++ unsigned int flags, unsigned int init,
++ unsigned int max)
++{
++ vrail->name = name;
++ vrail->flags = flags;
++ vrail->voltage.init = init;
++ vrail->voltage.max = max;
++}
++
++static void mangle_pmu_pdata_by_system_rev(void)
++{
++ switch (system_rev) {
++ case GTA02v1_SYSTEM_REV:
++ /* FIXME: this is only in v1 due to wrong PMU variant */
++ gta02_pcf_pdata.rails[PCF50633_REGULATOR_DOWN2].flags =
++ PMU_VRAIL_F_SUSPEND_ON;
++ break;
++ default:
++ break;
++ }
++}
++
++static struct resource gta02_pmu_resources[] = {
++ [0] = {
++ .flags = IORESOURCE_IRQ,
++ .start = GTA02_IRQ_PCF50633,
++ .end = GTA02_IRQ_PCF50633,
++ },
++};
++
++struct platform_device gta02_pmu_dev = {
++ .name = "pcf50633",
++ .num_resources = ARRAY_SIZE(gta02_pmu_resources),
++ .resource = gta02_pmu_resources,
++ .dev = {
++ .platform_data = >a02_pcf_pdata,
++ },
++};
++
++static struct platform_device *gta02_devices[] __initdata = {
++ &s3c_device_usb,
++ &s3c_device_wdt,
++ &s3c_device_i2c,
++ &s3c_device_iis,
++ // &s3c_device_sdi, /* FIXME: temporary disable to avoid s3cmci bind */
++ &s3c_device_usbgadget,
++ &s3c_device_nand,
++ &s3c_device_ts,
++};
++
++static struct s3c2410_nand_set gta02_nand_sets[] = {
++ [0] = {
++ .name = "neo1973-nand",
++ .nr_chips = 1,
++ },
++};
++
++/* choose a set of timings which should suit most 512Mbit
++ * chips and beyond.
++ */
++
++static struct s3c2410_platform_nand gta02_nand_info = {
++ .tacls = 20,
++ .twrph0 = 60,
++ .twrph1 = 20,
++ .nr_sets = ARRAY_SIZE(gta02_nand_sets),
++ .sets = gta02_nand_sets,
++};
++
++static void gta02_mmc_set_power(unsigned char power_mode, unsigned short vdd)
++{
++ printk(KERN_DEBUG "mmc_set_power(power_mode=%u, vdd=%u\n",
++ power_mode, vdd);
++
++ switch (system_rev) {
++ case GTA02v1_SYSTEM_REV:
++ /* FIXME */
++ break;
++ }
++}
++
++static struct s3c24xx_mci_pdata gta02_mmc_cfg = {
++ .gpio_detect = GTA02_GPIO_nSD_DETECT,
++ .set_power = >a02_mmc_set_power,
++ .ocr_avail = MMC_VDD_32_33,
++};
++
++static void gta02_udc_command(enum s3c2410_udc_cmd_e cmd)
++{
++ printk(KERN_DEBUG "%s(%d)\n", __func__, cmd);
++
++ switch (cmd) {
++ case S3C2410_UDC_P_ENABLE:
++ s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 1);
++ break;
++ case S3C2410_UDC_P_DISABLE:
++ s3c2410_gpio_setpin(GTA02_GPIO_USB_PULLUP, 0);
++ break;
++ case S3C2410_UDC_P_RESET:
++ /* FIXME! */
++ break;
++ default:
++ break;
++ }
++}
++
++/* use a work queue, since I2C API inherently schedules
++ * and we get called in hardirq context from UDC driver */
++
++struct vbus_draw {
++ struct work_struct work;
++ int ma;
++};
++static struct vbus_draw gta02_udc_vbus_drawer;
++
++static void __gta02_udc_vbus_draw(struct work_struct *work)
++{
++ if (gta02_udc_vbus_drawer.ma >= 500) {
++ /* enable fast charge */
++ printk(KERN_DEBUG "udc: enabling fast charge\n");
++ pcf50633_charge_fast(pcf50633_global, 1);
++ } else {
++ /* disable fast charge */
++ printk(KERN_DEBUG "udc: disabling fast charge\n");
++ pcf50633_charge_fast(pcf50633_global, 0);
++ }
++}
++
++static int gta02_udc_vbus_draw(unsigned int ma)
++{
++ gta02_udc_vbus_drawer.ma = ma;
++ schedule_work(>a02_udc_vbus_drawer.work);
++
++ return 0;
++}
++
++static struct s3c2410_udc_mach_info gta02_udc_cfg = {
++ .vbus_draw = gta02_udc_vbus_draw,
++ .udc_command = gta02_udc_command,
++
++};
++
++static struct s3c2410_ts_mach_info gta02_ts_cfg = {
++ .delay = 10000,
++ .presc = 49,
++ .oversampling_shift = 2,
++};
++
++/* SPI */
++
++static struct spi_board_info gta02_spi_board_info[] __initdata = {
++ {
++ .modalias = "jbt6k74",
++ /* platform_data */
++ /* controller_data */
++ /* irq */
++ .max_speed_hz = 10 * 1000 * 1000,
++ .bus_num = 1,
++ /* chip_select */
++ },
++};
++
++static struct glamo_spi_info glamo_spi_cfg = {
++ .board_size = ARRAY_SIZE(gta02_spi_board_info),
++ .board_info = gta02_spi_board_info,
++};
++
++static struct glamo_spigpio_info glamo_spigpio_cfg = {
++ .pin_clk = GLAMO_GPIO10_OUTPUT,
++ .pin_mosi = GLAMO_GPIO11_OUTPUT,
++ .pin_cs = GLAMO_GPIO12_OUTPUT,
++ .pin_miso = 0,
++ .board_size = ARRAY_SIZE(gta02_spi_board_info),
++ .board_info = gta02_spi_board_info,
++};
++
++#if 0
++#ifdef SPI_HARD
++static struct s3c2410_spi_info spi_cfg = {
++ .pin_cs = S3C2410_GPG3,
++ .board_size = ARRAY_SIZE(gta01_spi_board_info),
++ .board_info = gta01_spi_board_info,
++};
++#else
++static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
++{
++ switch (cs) {
++ case BITBANG_CS_ACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPG3, 0);
++ break;
++ case BITBANG_CS_INACTIVE:
++ s3c2410_gpio_setpin(S3C2410_GPG3, 1);
++ break;
++ }
++}
++
++static struct s3c2410_spigpio_info spi_gpio_cfg = {
++ .pin_clk = S3C2410_GPG7,
++ .pin_mosi = S3C2410_GPG6,
++ .pin_miso = S3C2410_GPG5,
++ .board_size = ARRAY_SIZE(gta01_spi_board_info),
++ .board_info = gta01_spi_board_info,
++ .chip_select = &spi_gpio_cs,
++};
++
++static struct resource s3c_spi_lcm_resource[] = {
++ [0] = {
++ .start = S3C2410_GPG3,
++ .end = S3C2410_GPG3,
++ },
++ [1] = {
++ .start = S3C2410_GPG5,
++ .end = S3C2410_GPG5,
++ },
++ [2] = {
++ .start = S3C2410_GPG6,
++ .end = S3C2410_GPG6,
++ },
++ [3] = {
++ .start = S3C2410_GPG7,
++ .end = S3C2410_GPG7,
++ },
++};
++
++struct platform_device s3c_device_spi_lcm = {
++ .name = "s3c24xx-spi-gpio",
++ .id = 1,
++ .num_resources = ARRAY_SIZE(s3c_spi_lcm_resource),
++ .resource = s3c_spi_lcm_resource,
++ .dev = {
++ .platform_data = &spi_gpio_cfg,
++ },
++};
++#endif
++#endif
++
++static struct resource gta02_led_resources[] = {
++ [0] = {
++ .start = GTA02_GPIO_VIBRATOR_ON,
++ .end = GTA02_GPIO_VIBRATOR_ON,
++ },
++ /* FIXME */
++};
++
++struct platform_device gta02_led_dev = {
++ .name = "gta01-led",
++ .num_resources = ARRAY_SIZE(gta02_led_resources),
++ .resource = gta02_led_resources,
++};
++
++static struct resource gta01_button_resources[] = {
++ [0] = {
++ .start = GTA02_GPIO_AUX_KEY,
++ .end = GTA02_GPIO_AUX_KEY,
++ },
++ [1] = {
++ .start = GTA02_GPIO_HOLD_KEY,
++ .end = GTA02_GPIO_HOLD_KEY,
++ },
++ [2] = {
++ .start = GTA02_GPIO_JACK_INSERT,
++ .end = GTA02_GPIO_JACK_INSERT,
++ },
++};
++
++static struct platform_device gta01_button_dev = {
++ .name = "gta01-button",
++ .num_resources = ARRAY_SIZE(gta01_button_resources),
++ .resource = gta01_button_resources,
++};
++
++static struct platform_device gta01_pm_gsm_dev = {
++ .name = "gta01-pm-gsm",
++};
++
++/* USB */
++static struct s3c2410_hcd_info gta02_usb_info = {
++ .port[0] = {
++ .flags = S3C_HCDFLG_USED,
++ },
++ .port[1] = {
++ .flags = 0,
++ },
++};
++
++/* Smedia Glamo 3362 */
++
++static struct glamofb_platform_data gta02_glamo_pdata = {
++ .width = 480,
++ .height = 640,
++ .xres = {
++ .min = 240,
++ .max = 480,
++ .defval = 480,
++ },
++ .yres = {
++ .min = 320,
++ .max = 640,
++ .defval = 640,
++ },
++ .bpp = {
++ .min = 16,
++ .max = 16,
++ .defval = 16,
++ },
++ //.spi_info = &glamo_spi_cfg,
++ .spigpio_info = &glamo_spigpio_cfg,
++};
++
++static struct resource gta02_glamo_resources[] = {
++ [0] = {
++ .start = S3C2410_CS1,
++ .end = S3C2410_CS1 + 0x1000000,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = GTA02_IRQ_3D,
++ .end = GTA02_IRQ_3D,
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ .start = GTA02_GPIO_3D_RESET,
++ .end = GTA02_GPIO_3D_RESET,
++ },
++};
++
++static struct platform_device gta02_glamo_dev = {
++ .name = "glamo3362",
++ .num_resources = ARRAY_SIZE(gta02_glamo_resources),
++ .resource = gta02_glamo_resources,
++ .dev = {
++ .platform_data = >a02_glamo_pdata,
++ },
++};
++
++static void __init gta02_map_io(void)
++{
++ s3c24xx_init_io(gta02_iodesc, ARRAY_SIZE(gta02_iodesc));
++ s3c24xx_init_clocks(12000000);
++ s3c24xx_init_uarts(gta02_uartcfgs, ARRAY_SIZE(gta02_uartcfgs));
++ platform_add_devices(gta02_devices, ARRAY_SIZE(gta02_devices));
++}
++
++static irqreturn_t gta02_modem_irq(int irq, void *param)
++{
++ printk(KERN_DEBUG "modem wakeup interrupt\n");
++ return IRQ_HANDLED;
++}
++
++static void __init gta02_machine_init(void)
++{
++ s3c_device_usb.dev.platform_data = >a02_usb_info;
++ s3c_device_nand.dev.platform_data = >a02_nand_info;
++ s3c_device_sdi.dev.platform_data = >a02_mmc_cfg;
++
++ INIT_WORK(>a02_udc_vbus_drawer.work, __gta02_udc_vbus_draw);
++ s3c24xx_udc_set_platdata(>a02_udc_cfg);
++ set_s3c2410ts_info(>a02_ts_cfg);
++
++ platform_device_register(>a01_button_dev);
++ platform_device_register(>a01_pm_gsm_dev);
++
++ mangle_pmu_pdata_by_system_rev();
++ platform_device_register(>a02_pmu_dev);
++ platform_device_register(>a02_led_dev);
++ platform_device_register(>a02_glamo_dev);
++
++ s3c2410_pm_init();
++
++ /* Set LCD_RESET / XRES to high */
++ s3c2410_gpio_cfgpin(GTA01_GPIO_LCD_RESET, S3C2410_GPIO_OUTPUT);
++ s3c2410_gpio_setpin(GTA01_GPIO_LCD_RESET, 1);
++
++ set_irq_type(GTA02_IRQ_MODEM, IRQT_RISING);
++ request_irq(GTA02_IRQ_MODEM, gta02_modem_irq,
++ SA_INTERRUPT, "modem", NULL);
++ enable_irq_wake(GTA02_IRQ_MODEM);
++}
++
++MACHINE_START(NEO1973_GTA02, "GTA02")
++ .phys_io = S3C2410_PA_UART,
++ .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
++ .boot_params = S3C2410_SDRAM_PA + 0x100,
++ .map_io = gta02_map_io,
++ .init_irq = s3c24xx_init_irq,
++ .init_machine = gta02_machine_init,
++ .timer = &s3c24xx_timer,
++MACHINE_END
+Index: linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta02.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta02.h 2007-07-16 15:52:19.877334245 +0200
+@@ -0,0 +1,24 @@
++#ifndef _GTA02_H
++#define _GTA02_H
++
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/irqs.h>
++
++/* Different hardware revisions, passed in ATAG_REVISION by u-boot */
++#define GTA02v1_SYSTEM_REV 0x00000310
++#define GTA02v2_SYSTEM_REV 0x00000320
++
++#define GTA02_GPIO_3D_IRQ S3C2410_GPG4
++#define GTA02_GPIO_3D_RESET S3C2440_GPJ0
++#define GTA02_GPIO_nSD_DETECT S3C2410_GPF5
++#define GTA02_GPIO_USB_PULLUP S3C2410_GPB9
++#define GTA02_GPIO_VIBRATOR_ON S3C2410_GPB3
++#define GTA02_GPIO_HOLD_KEY S3C2410_GPF7
++#define GTA02_GPIO_AUX_KEY S3C2410_GPF6
++#define GTA02_GPIO_JACK_INSERT S3C2410_GPF4
++
++#define GTA02_IRQ_MODEM IRQ_EINT1
++#define GTA02_IRQ_3D IRQ_EINT12
++#define GTA02_IRQ_PCF50633 IRQ_EINT9
++
++#endif /* _GTA02_H */
+Index: linux-2.6.22.1/arch/arm/mach-s3c2440/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Kconfig 2007-07-16 15:52:18.773271328 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Kconfig 2007-07-16 15:52:19.905335841 +0200
+@@ -73,5 +73,12 @@
+ help
+ Say Y here if you are using the FIC Neo1973 GSM Phone
+
++config MACH_NEO1973_GTA02
++ bool "FIC Neo1973 GSM Phone (GTA02 Hardware)"
++ select CPU_S3C2440
++ select SENSORS_PCF50633
++ help
++ Say Y here if you are using the FIC Neo1973 GSM Phone
++
+ endmenu
+
+Index: linux-2.6.22.1/arch/arm/mach-s3c2440/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Makefile 2007-07-16 15:52:18.777271558 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Makefile 2007-07-16 15:52:19.933337439 +0200
+@@ -22,3 +22,4 @@
+ obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o
+ obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o
+ obj-$(CONFIG_MACH_HXD8) += mach-hxd8.o
++obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o
+Index: linux-2.6.22.1/arch/arm/common/gta01_pm_bt.c
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/common/gta01_pm_bt.c 2007-07-16 15:52:18.289243745 +0200
++++ linux-2.6.22.1/arch/arm/common/gta01_pm_bt.c 2007-07-16 15:52:19.953338579 +0200
+@@ -19,6 +19,7 @@
+ #include <linux/pcf50606.h>
+
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+ #include <asm/arch/gta01.h>
+
+ #define DRVMSG "FIC GTA01 (Neo1973) Bluetooth Power Management"
+@@ -27,11 +28,18 @@
+ char *buf)
+ {
+ if (!strcmp(attr->attr.name, "power_on")) {
+- if (pcf50606_onoff_get(pcf50606_global,
+- PCF50606_REGULATOR_D1REG) &&
+- pcf50606_voltage_get(pcf50606_global,
+- PCF50606_REGULATOR_D1REG) == 3100)
+- goto out_1;
++ switch (machine_arch_type) {
++ case MACH_TYPE_NEO1973_GTA01:
++ if (pcf50606_onoff_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) &&
++ pcf50606_voltage_get(pcf50606_global,
++ PCF50606_REGULATOR_D1REG) == 3100)
++ goto out_1;
++ break;
++ case MACH_TYPE_NEO1973_GTA02:
++ /* FIXME: implementation using PCF50633 */
++ break;
++ }
+ } else if (!strcmp(attr->attr.name, "reset")) {
+ if (s3c2410_gpio_getpin(GTA01_GPIO_BT_EN) == 0)
+ goto out_1;
+@@ -48,17 +56,24 @@
+ unsigned long on = simple_strtoul(buf, NULL, 10);
+
+ if (!strcmp(attr->attr.name, "power_on")) {
+- /* if we are powering up, assert reset, then power, then
+- * release reset */
+- if (on) {
+- s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
+- pcf50606_voltage_set(pcf50606_global,
+- PCF50606_REGULATOR_D1REG,
+- 3100);
++ switch (machine_arch_type) {
++ case MACH_TYPE_NEO1973_GTA01:
++ /* if we are powering up, assert reset, then power, then
++ * release reset */
++ if (on) {
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
++ pcf50606_voltage_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG,
++ 3100);
++ }
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, on);
++ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on);
++ break;
++ case MACH_TYPE_NEO1973_GTA02:
++ /* FIXME: implementation */
++ break;
+ }
+- pcf50606_onoff_set(pcf50606_global,
+- PCF50606_REGULATOR_D1REG, on);
+- s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on);
+ } else if (!strcmp(attr->attr.name, "reset")) {
+ /* reset is low-active, so we need to invert */
+ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, on ? 0 : 1 );
+@@ -107,9 +122,16 @@
+ {
+ dev_info(&pdev->dev, DRVMSG ": starting\n");
+
+- /* we make sure that the voltage is off */
+- pcf50606_onoff_set(pcf50606_global,
+- PCF50606_REGULATOR_D1REG, 0);
++ switch (machine_arch_type) {
++ case MACH_TYPE_NEO1973_GTA01:
++ /* we make sure that the voltage is off */
++ pcf50606_onoff_set(pcf50606_global,
++ PCF50606_REGULATOR_D1REG, 0);
++ break;
++ case MACH_TYPE_NEO1973_GTA02:
++ /* FIXME: implementation */
++ break;
++ }
+ /* we pull reset to low to make sure that the chip doesn't
+ * drain power through the reset line */
+ s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
+Index: linux-2.6.22.1/arch/arm/common/gta01_pm_gsm.c
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/common/gta01_pm_gsm.c 2007-07-16 15:52:18.257241924 +0200
++++ linux-2.6.22.1/arch/arm/common/gta01_pm_gsm.c 2007-07-16 15:52:20.037343364 +0200
+@@ -19,7 +19,9 @@
+ #include <linux/errno.h>
+
+ #include <asm/hardware.h>
++#include <asm/mach-types.h>
+ #include <asm/arch/gta01.h>
++#include <asm/arch/gta02.h>
+
+ struct gta01pm_priv {
+ int gpio_ngsm_en;
+@@ -70,11 +72,12 @@
+
+ if (!strcmp(attr->attr.name, "power_on")) {
+ if (on) {
+- dev_info(dev, "powering up GSM, thus disconnecting "
+- "serial console\n");
++ if (gta01_gsm.con) {
++ dev_info(dev, "powering up GSM, thus "
++ "disconnecting serial console\n");
+
+- if (gta01_gsm.con)
+ console_stop(gta01_gsm.con);
++ }
+
+ if (gta01_gsm.gpio_ngsm_en)
+ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 0);
+@@ -86,11 +89,12 @@
+ if (gta01_gsm.gpio_ngsm_en)
+ s3c2410_gpio_setpin(gta01_gsm.gpio_ngsm_en, 1);
+
+- if (gta01_gsm.con)
++ if (gta01_gsm.con) {
+ console_start(gta01_gsm.con);
+
+- dev_info(dev, "powered down GSM, thus enabling "
+- "serial console\n");
++ dev_info(dev, "powered down GSM, thus enabling "
++ "serial console\n");
++ }
+ }
+ } else if (!strcmp(attr->attr.name, "reset")) {
+ s3c2410_gpio_setpin(GTA01_GPIO_MODEM_RST, on);
+@@ -158,6 +162,9 @@
+ gta01_gsm.gpio_ngsm_en = GTA01Bv2_GPIO_nGSM_EN;
+ s3c2410_gpio_setpin(GTA01v3_GPIO_nGSM_EN, 0);
+ break;
++ case GTA02v1_SYSTEM_REV:
++ gta01_gsm.gpio_ngsm_en = 0;
++ break;
+ default:
+ dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
+ "some PM features not available!!!\n",
+@@ -175,9 +182,13 @@
+ break;
+ }
+
+- gta01_gsm.con = find_s3c24xx_console();
+- if (!gta01_gsm.con)
+- dev_warn(&pdev->dev, "cannot find S3C24xx console driver\n");
++ if (machine_is_neo1973_gta01()) {
++ gta01_gsm.con = find_s3c24xx_console();
++ if (!gta01_gsm.con)
++ dev_warn(&pdev->dev,
++ "cannot find S3C24xx console driver\n");
++ } else
++ gta01_gsm.con = NULL;
+
+ return sysfs_create_group(&pdev->dev.kobj, >a01_gsm_attr_group);
+ }
Added: developers/nbd/patches-2.6.22/800-sdio_merge.patch
===================================================================
--- developers/nbd/patches-2.6.22/800-sdio_merge.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/800-sdio_merge.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,5674 @@
+Index: linux-2.6.22.1/drivers/mmc/card/block.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/card/block.c 2007-07-19 00:16:57.054043020 +0200
++++ linux-2.6.22.1/drivers/mmc/card/block.c 2007-07-19 00:18:11.438281932 +0200
+@@ -262,7 +262,9 @@
+ }
+
+ brq.data.sg = mq->sg;
+- brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);
++ brq.data.sg_len = mmc_queue_map_sg(mq);
++
++ mmc_queue_bounce_pre(mq);
+
+ if (brq.data.blocks !=
+ (req->nr_sectors >> (md->block_bits - 9))) {
+@@ -279,6 +281,9 @@
+ }
+
+ mmc_wait_for_req(card->host, &brq.mrq);
++
++ mmc_queue_bounce_post(mq);
++
+ if (brq.cmd.error) {
+ printk(KERN_ERR "%s: error %d sending read/write command\n",
+ req->rq_disk->disk_name, brq.cmd.error);
+Index: linux-2.6.22.1/drivers/mmc/card/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/card/Kconfig 2007-07-19 00:17:03.710422342 +0200
++++ linux-2.6.22.1/drivers/mmc/card/Kconfig 2007-07-19 00:18:11.438281932 +0200
+@@ -14,3 +14,28 @@
+ mount the filesystem. Almost everyone wishing MMC support
+ should say Y or M here.
+
++config MMC_BLOCK_BOUNCE
++ bool "Use bounce buffer for simple hosts"
++ depends on MMC_BLOCK
++ default y
++ help
++ SD/MMC is a high latency protocol where it is crucial to
++ send large requests in order to get high performance. Many
++ controllers, however, are restricted to continuous memory
++ (i.e. they can't do scatter-gather), something the kernel
++ rarely can provide.
++
++ Say Y here to help these restricted hosts by bouncing
++ requests back and forth from a large buffer. You will get
++ a big performance gain at the cost of up to 64 KiB of
++ physical memory.
++
++ If unsure, say Y here.
++
++config SDIO_UART
++ tristate "SDIO UART/GPS class support"
++ depends on MMC
++ help
++ SDIO function driver for SDIO cards that implements the UART
++ class, as well as the GPS class which appears like a UART.
++
+Index: linux-2.6.22.1/drivers/mmc/card/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/card/Makefile 2007-07-19 00:17:03.746424393 +0200
++++ linux-2.6.22.1/drivers/mmc/card/Makefile 2007-07-19 00:18:11.470283760 +0200
+@@ -9,3 +9,5 @@
+ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
+ mmc_block-objs := block.o queue.o
+
++obj-$(CONFIG_SDIO_UART) += sdio_uart.o
++
+Index: linux-2.6.22.1/drivers/mmc/card/queue.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/card/queue.c 2007-07-19 00:16:57.074044160 +0200
++++ linux-2.6.22.1/drivers/mmc/card/queue.c 2007-07-19 00:18:11.498285352 +0200
+@@ -17,6 +17,8 @@
+ #include <linux/mmc/host.h>
+ #include "queue.h"
+
++#define MMC_QUEUE_BOUNCESZ 65536
++
+ #define MMC_QUEUE_SUSPENDED (1 << 0)
+
+ /*
+@@ -118,6 +120,7 @@
+ struct mmc_host *host = card->host;
+ u64 limit = BLK_BOUNCE_HIGH;
+ int ret;
++ unsigned int bouncesz;
+
+ if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
+ limit = *mmc_dev(host)->dma_mask;
+@@ -127,21 +130,61 @@
+ if (!mq->queue)
+ return -ENOMEM;
+
+- blk_queue_prep_rq(mq->queue, mmc_prep_request);
+- blk_queue_bounce_limit(mq->queue, limit);
+- blk_queue_max_sectors(mq->queue, host->max_req_size / 512);
+- blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
+- blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
+- blk_queue_max_segment_size(mq->queue, host->max_seg_size);
+-
+ mq->queue->queuedata = mq;
+ mq->req = NULL;
+
+- mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs,
+- GFP_KERNEL);
+- if (!mq->sg) {
+- ret = -ENOMEM;
+- goto cleanup_queue;
++ blk_queue_prep_rq(mq->queue, mmc_prep_request);
++
++#ifdef CONFIG_MMC_BLOCK_BOUNCE
++ if (host->max_hw_segs == 1) {
++ bouncesz = MMC_QUEUE_BOUNCESZ;
++
++ if (bouncesz > host->max_req_size)
++ bouncesz = host->max_req_size;
++ if (bouncesz > host->max_seg_size)
++ bouncesz = host->max_seg_size;
++
++ mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
++ if (!mq->bounce_buf) {
++ printk(KERN_WARNING "%s: unable to allocate "
++ "bounce buffer\n", mmc_card_name(card));
++ } else {
++ blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH);
++ blk_queue_max_sectors(mq->queue, bouncesz / 512);
++ blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
++ blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
++ blk_queue_max_segment_size(mq->queue, bouncesz);
++
++ mq->sg = kmalloc(sizeof(struct scatterlist),
++ GFP_KERNEL);
++ if (!mq->sg) {
++ ret = -ENOMEM;
++ goto free_bounce_buf;
++ }
++
++ mq->bounce_sg = kmalloc(sizeof(struct scatterlist) *
++ bouncesz / 512, GFP_KERNEL);
++ if (!mq->bounce_sg) {
++ ret = -ENOMEM;
++ goto free_sg;
++ }
++ }
++ }
++#endif
++
++ if (!mq->bounce_buf) {
++ blk_queue_bounce_limit(mq->queue, limit);
++ blk_queue_max_sectors(mq->queue, host->max_req_size / 512);
++ blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);
++ blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);
++ blk_queue_max_segment_size(mq->queue, host->max_seg_size);
++
++ mq->sg = kmalloc(sizeof(struct scatterlist) *
++ host->max_phys_segs, GFP_KERNEL);
++ if (!mq->sg) {
++ ret = -ENOMEM;
++ goto cleanup_queue;
++ }
+ }
+
+ init_MUTEX(&mq->thread_sem);
+@@ -149,14 +192,21 @@
+ mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd");
+ if (IS_ERR(mq->thread)) {
+ ret = PTR_ERR(mq->thread);
+- goto free_sg;
++ goto free_bounce_sg;
+ }
+
+ return 0;
+-
++ free_bounce_sg:
++ if (mq->bounce_sg)
++ kfree(mq->bounce_sg);
++ mq->bounce_sg = NULL;
+ free_sg:
+ kfree(mq->sg);
+ mq->sg = NULL;
++ free_bounce_buf:
++ if (mq->bounce_buf)
++ kfree(mq->bounce_buf);
++ mq->bounce_buf = NULL;
+ cleanup_queue:
+ blk_cleanup_queue(mq->queue);
+ return ret;
+@@ -178,9 +228,17 @@
+ /* Then terminate our worker thread */
+ kthread_stop(mq->thread);
+
++ if (mq->bounce_sg)
++ kfree(mq->bounce_sg);
++ mq->bounce_sg = NULL;
++
+ kfree(mq->sg);
+ mq->sg = NULL;
+
++ if (mq->bounce_buf)
++ kfree(mq->bounce_buf);
++ mq->bounce_buf = NULL;
++
+ blk_cleanup_queue(mq->queue);
+
+ mq->card = NULL;
+@@ -231,3 +289,108 @@
+ }
+ }
+
++static void copy_sg(struct scatterlist *dst, unsigned int dst_len,
++ struct scatterlist *src, unsigned int src_len)
++{
++ unsigned int chunk;
++ char *dst_buf, *src_buf;
++ unsigned int dst_size, src_size;
++
++ dst_buf = NULL;
++ src_buf = NULL;
++ dst_size = 0;
++ src_size = 0;
++
++ while (src_len) {
++ BUG_ON(dst_len == 0);
++
++ if (dst_size == 0) {
++ dst_buf = page_address(dst->page) + dst->offset;
++ dst_size = dst->length;
++ }
++
++ if (src_size == 0) {
++ src_buf = page_address(src->page) + src->offset;
++ src_size = src->length;
++ }
++
++ chunk = min(dst_size, src_size);
++
++ memcpy(dst_buf, src_buf, chunk);
++
++ dst_buf += chunk;
++ src_buf += chunk;
++ dst_size -= chunk;
++ src_size -= chunk;
++
++ if (dst_size == 0) {
++ dst++;
++ dst_len--;
++ }
++
++ if (src_size == 0) {
++ src++;
++ src_len--;
++ }
++ }
++}
++
++unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
++{
++ unsigned int sg_len;
++
++ if (!mq->bounce_buf)
++ return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
++
++ BUG_ON(!mq->bounce_sg);
++
++ sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg);
++
++ mq->bounce_sg_len = sg_len;
++
++ /*
++ * Shortcut in the event we only get a single entry.
++ */
++ if (sg_len == 1) {
++ memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist));
++ return 1;
++ }
++
++ mq->sg[0].page = virt_to_page(mq->bounce_buf);
++ mq->sg[0].offset = offset_in_page(mq->bounce_buf);
++ mq->sg[0].length = 0;
++
++ while (sg_len) {
++ mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
++ sg_len--;
++ }
++
++ return 1;
++}
++
++void mmc_queue_bounce_pre(struct mmc_queue *mq)
++{
++ if (!mq->bounce_buf)
++ return;
++
++ if (mq->bounce_sg_len == 1)
++ return;
++ if (rq_data_dir(mq->req) != WRITE)
++ return;
++
++ copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len);
++}
++
++void mmc_queue_bounce_post(struct mmc_queue *mq)
++{
++ if (!mq->bounce_buf)
++ return;
++
++ if (mq->bounce_sg_len == 1)
++ return;
++ if (rq_data_dir(mq->req) != READ)
++ return;
++
++ copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1);
++}
++
+Index: linux-2.6.22.1/drivers/mmc/card/queue.h
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/card/queue.h 2007-07-19 00:17:55.881395399 +0200
++++ linux-2.6.22.1/drivers/mmc/card/queue.h 2007-07-19 00:18:11.518286492 +0200
+@@ -14,6 +14,9 @@
+ void *data;
+ struct request_queue *queue;
+ struct scatterlist *sg;
++ char *bounce_buf;
++ struct scatterlist *bounce_sg;
++ unsigned int bounce_sg_len;
+ };
+
+ extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *);
+@@ -21,4 +24,8 @@
+ extern void mmc_queue_suspend(struct mmc_queue *);
+ extern void mmc_queue_resume(struct mmc_queue *);
+
++extern unsigned int mmc_queue_map_sg(struct mmc_queue *);
++extern void mmc_queue_bounce_pre(struct mmc_queue *);
++extern void mmc_queue_bounce_post(struct mmc_queue *);
++
+ #endif
+Index: linux-2.6.22.1/drivers/mmc/card/sdio_uart.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/card/sdio_uart.c 2007-07-19 00:18:11.546288090 +0200
+@@ -0,0 +1,1139 @@
++/*
++ * linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
++ *
++ * Based on drivers/serial/8250.c and drivers/serial/serial_core.c
++ * by Russell King.
++ *
++ * Author: Nicolas Pitre
++ * Created: June 15, 2007
++ * Copyright: MontaVista Software, Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++/*
++ * Note: Although this driver assumes a 16550A-like UART implementation,
++ * it is not possible to leverage the common 8250/16550 driver, nor the
++ * core UART infrastructure, as they assumes direct access to the hardware
++ * registers, often under a spinlock. This is not possible in the SDIO
++ * context as SDIO access functions must be able to sleep.
++ *
++ * Because we need to lock the SDIO host to ensure an exclusive access to
++ * the card, we simply rely on that lock to also prevent and serialize
++ * concurrent access to the same port.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/mutex.h>
++#include <linux/serial_reg.h>
++#include <linux/circ_buf.h>
++#include <linux/gfp.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++
++#include <linux/mmc/core.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++
++
++#define UART_NR 8 /* Number of UARTs this driver can handle */
++
++
++#define UART_XMIT_SIZE PAGE_SIZE
++#define WAKEUP_CHARS 256
++
++#define circ_empty(circ) ((circ)->head == (circ)->tail)
++#define circ_clear(circ) ((circ)->head = (circ)->tail = 0)
++
++#define circ_chars_pending(circ) \
++ (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++#define circ_chars_free(circ) \
++ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
++
++
++struct uart_icount {
++ __u32 cts;
++ __u32 dsr;
++ __u32 rng;
++ __u32 dcd;
++ __u32 rx;
++ __u32 tx;
++ __u32 frame;
++ __u32 overrun;
++ __u32 parity;
++ __u32 brk;
++};
++
++struct sdio_uart_port {
++ struct kref kref;
++ struct tty_struct *tty;
++ unsigned int index;
++ unsigned int opened;
++ struct mutex open_lock;
++ struct sdio_func *func;
++ struct mutex func_lock;
++ unsigned int regs_offset;
++ struct circ_buf xmit;
++ spinlock_t write_lock;
++ struct uart_icount icount;
++ unsigned int uartclk;
++ unsigned int mctrl;
++ unsigned int read_status_mask;
++ unsigned int ignore_status_mask;
++ unsigned char x_char;
++ unsigned char ier;
++ unsigned char lcr;
++};
++
++static struct sdio_uart_port *sdio_uart_table[UART_NR];
++static DEFINE_SPINLOCK(sdio_uart_table_lock);
++
++static int sdio_uart_add_port(struct sdio_uart_port *port)
++{
++ int index, ret = -EBUSY;
++
++ kref_init(&port->kref);
++ mutex_init(&port->open_lock);
++ mutex_init(&port->func_lock);
++ spin_lock_init(&port->write_lock);
++
++ spin_lock(&sdio_uart_table_lock);
++ for (index = 0; index < UART_NR; index++) {
++ if (!sdio_uart_table[index]) {
++ port->index = index;
++ sdio_uart_table[index] = port;
++ ret = 0;
++ break;
++ }
++ }
++ spin_unlock(&sdio_uart_table_lock);
++
++ return ret;
++}
++
++static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
++{
++ struct sdio_uart_port *port;
++
++ if (index >= UART_NR)
++ return NULL;
++
++ spin_lock(&sdio_uart_table_lock);
++ port = sdio_uart_table[index];
++ if (port)
++ kref_get(&port->kref);
++ spin_unlock(&sdio_uart_table_lock);
++
++ return port;
++}
++
++static void sdio_uart_port_destroy(struct kref *kref)
++{
++ struct sdio_uart_port *port =
++ container_of(kref, struct sdio_uart_port, kref);
++ kfree(port);
++}
++
++static void sdio_uart_port_put(struct sdio_uart_port *port)
++{
++ kref_put(&port->kref, sdio_uart_port_destroy);
++}
++
++static void sdio_uart_port_remove(struct sdio_uart_port *port)
++{
++ struct sdio_func *func;
++
++ BUG_ON(sdio_uart_table[port->index] != port);
++
++ spin_lock(&sdio_uart_table_lock);
++ sdio_uart_table[port->index] = NULL;
++ spin_unlock(&sdio_uart_table_lock);
++
++ /*
++ * We're killing a port that potentially still is in use by
++ * the tty layer. Be careful to prevent any further access
++ * to the SDIO function and arrange for the tty layer to
++ * give up on that port ASAP.
++ * Beware: the lock ordering is critical.
++ */
++ mutex_lock(&port->open_lock);
++ mutex_lock(&port->func_lock);
++ func = port->func;
++ sdio_claim_host(func);
++ port->func = NULL;
++ mutex_unlock(&port->func_lock);
++ if (port->opened)
++ tty_hangup(port->tty);
++ mutex_unlock(&port->open_lock);
++ sdio_release_irq(func);
++ sdio_disable_func(func);
++ sdio_release_host(func);
++
++ sdio_uart_port_put(port);
++}
++
++static int sdio_uart_claim_func(struct sdio_uart_port *port)
++{
++ mutex_lock(&port->func_lock);
++ if (unlikely(!port->func)) {
++ mutex_unlock(&port->func_lock);
++ return -ENODEV;
++ }
++ sdio_claim_host(port->func);
++ mutex_unlock(&port->func_lock);
++ return 0;
++}
++
++static inline void sdio_uart_release_func(struct sdio_uart_port *port)
++{
++ sdio_release_host(port->func);
++}
++
++static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset)
++{
++ unsigned char c;
++ c = sdio_readb(port->func, port->regs_offset + offset, NULL);
++ return c;
++}
++
++static inline void sdio_out(struct sdio_uart_port *port, int offset, int value)
++{
++ sdio_writeb(port->func, value, port->regs_offset + offset, NULL);
++}
++
++static unsigned int sdio_uart_get_mctrl(struct sdio_uart_port *port)
++{
++ unsigned char status;
++ unsigned int ret;
++
++ status = sdio_in(port, UART_MSR);
++
++ ret = 0;
++ if (status & UART_MSR_DCD)
++ ret |= TIOCM_CAR;
++ if (status & UART_MSR_RI)
++ ret |= TIOCM_RNG;
++ if (status & UART_MSR_DSR)
++ ret |= TIOCM_DSR;
++ if (status & UART_MSR_CTS)
++ ret |= TIOCM_CTS;
++ return ret;
++}
++
++static void sdio_uart_write_mctrl(struct sdio_uart_port *port, unsigned int mctrl)
++{
++ unsigned char mcr = 0;
++
++ if (mctrl & TIOCM_RTS)
++ mcr |= UART_MCR_RTS;
++ if (mctrl & TIOCM_DTR)
++ mcr |= UART_MCR_DTR;
++ if (mctrl & TIOCM_OUT1)
++ mcr |= UART_MCR_OUT1;
++ if (mctrl & TIOCM_OUT2)
++ mcr |= UART_MCR_OUT2;
++ if (mctrl & TIOCM_LOOP)
++ mcr |= UART_MCR_LOOP;
++
++ sdio_out(port, UART_MCR, mcr);
++}
++
++static inline void sdio_uart_update_mctrl(struct sdio_uart_port *port,
++ unsigned int set, unsigned int clear)
++{
++ unsigned int old;
++
++ old = port->mctrl;
++ port->mctrl = (old & ~clear) | set;
++ if (old != port->mctrl)
++ sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++#define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0)
++#define sdio_uart_clear_mctrl(port, x) sdio_uart_update_mctrl(port, 0, x)
++
++static void sdio_uart_change_speed(struct sdio_uart_port *port,
++ struct ktermios *termios,
++ struct ktermios *old)
++{
++ unsigned char cval, fcr = 0;
++ unsigned int baud, quot;
++
++ switch (termios->c_cflag & CSIZE) {
++ case CS5:
++ cval = UART_LCR_WLEN5;
++ break;
++ case CS6:
++ cval = UART_LCR_WLEN6;
++ break;
++ case CS7:
++ cval = UART_LCR_WLEN7;
++ break;
++ default:
++ case CS8:
++ cval = UART_LCR_WLEN8;
++ break;
++ }
++
++ if (termios->c_cflag & CSTOPB)
++ cval |= UART_LCR_STOP;
++ if (termios->c_cflag & PARENB)
++ cval |= UART_LCR_PARITY;
++ if (!(termios->c_cflag & PARODD))
++ cval |= UART_LCR_EPAR;
++
++ for (;;) {
++ baud = tty_termios_baud_rate(termios);
++ if (baud == 0)
++ baud = 9600; /* Special case: B0 rate. */
++ if (baud <= port->uartclk)
++ break;
++ /*
++ * Oops, the quotient was zero. Try again with the old
++ * baud rate if possible, otherwise default to 9600.
++ */
++ termios->c_cflag &= ~CBAUD;
++ if (old) {
++ termios->c_cflag |= old->c_cflag & CBAUD;
++ old = NULL;
++ } else
++ termios->c_cflag |= B9600;
++ }
++ quot = (2 * port->uartclk + baud) / (2 * baud);
++
++ if (baud < 2400)
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
++ else
++ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10;
++
++ port->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
++ if (termios->c_iflag & INPCK)
++ port->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
++ if (termios->c_iflag & (BRKINT | PARMRK))
++ port->read_status_mask |= UART_LSR_BI;
++
++ /*
++ * Characters to ignore
++ */
++ port->ignore_status_mask = 0;
++ if (termios->c_iflag & IGNPAR)
++ port->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
++ if (termios->c_iflag & IGNBRK) {
++ port->ignore_status_mask |= UART_LSR_BI;
++ /*
++ * If we're ignoring parity and break indicators,
++ * ignore overruns too (for real raw support).
++ */
++ if (termios->c_iflag & IGNPAR)
++ port->ignore_status_mask |= UART_LSR_OE;
++ }
++
++ /*
++ * ignore all characters if CREAD is not set
++ */
++ if ((termios->c_cflag & CREAD) == 0)
++ port->ignore_status_mask |= UART_LSR_DR;
++
++ /*
++ * CTS flow control flag and modem status interrupts
++ */
++ port->ier &= ~UART_IER_MSI;
++ if ((termios->c_cflag & CRTSCTS) || !(termios->c_cflag & CLOCAL))
++ port->ier |= UART_IER_MSI;
++
++ port->lcr = cval;
++
++ sdio_out(port, UART_IER, port->ier);
++ sdio_out(port, UART_LCR, cval | UART_LCR_DLAB);
++ sdio_out(port, UART_DLL, quot & 0xff);
++ sdio_out(port, UART_DLM, quot >> 8);
++ sdio_out(port, UART_LCR, cval);
++ sdio_out(port, UART_FCR, fcr);
++
++ sdio_uart_write_mctrl(port, port->mctrl);
++}
++
++static void sdio_uart_start_tx(struct sdio_uart_port *port)
++{
++ if (!(port->ier & UART_IER_THRI)) {
++ port->ier |= UART_IER_THRI;
++ sdio_out(port, UART_IER, port->ier);
++ }
++}
++
++static void sdio_uart_stop_tx(struct sdio_uart_port *port)
++{
++ if (port->ier & UART_IER_THRI) {
++ port->ier &= ~UART_IER_THRI;
++ sdio_out(port, UART_IER, port->ier);
++ }
++}
++
++static void sdio_uart_stop_rx(struct sdio_uart_port *port)
++{
++ port->ier &= ~UART_IER_RLSI;
++ port->read_status_mask &= ~UART_LSR_DR;
++ sdio_out(port, UART_IER, port->ier);
++}
++
++static void sdio_uart_receive_chars(struct sdio_uart_port *port, int *status)
++{
++ struct tty_struct *tty = port->tty;
++ unsigned int ch, flag;
++ int max_count = 256;
++
++ do {
++ ch = sdio_in(port, UART_RX);
++ flag = TTY_NORMAL;
++ port->icount.rx++;
++
++ if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
++ UART_LSR_FE | UART_LSR_OE))) {
++ /*
++ * For statistics only
++ */
++ if (*status & UART_LSR_BI) {
++ *status &= ~(UART_LSR_FE | UART_LSR_PE);
++ port->icount.brk++;
++ } else if (*status & UART_LSR_PE)
++ port->icount.parity++;
++ else if (*status & UART_LSR_FE)
++ port->icount.frame++;
++ if (*status & UART_LSR_OE)
++ port->icount.overrun++;
++
++ /*
++ * Mask off conditions which should be ignored.
++ */
++ *status &= port->read_status_mask;
++ if (*status & UART_LSR_BI) {
++ flag = TTY_BREAK;
++ } else if (*status & UART_LSR_PE)
++ flag = TTY_PARITY;
++ else if (*status & UART_LSR_FE)
++ flag = TTY_FRAME;
++ }
++
++ if ((*status & port->ignore_status_mask & ~UART_LSR_OE) == 0)
++ tty_insert_flip_char(tty, ch, flag);
++
++ /*
++ * Overrun is special. Since it's reported immediately,
++ * it doesn't affect the current character.
++ */
++ if (*status & ~port->ignore_status_mask & UART_LSR_OE)
++ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
++
++ *status = sdio_in(port, UART_LSR);
++ } while ((*status & UART_LSR_DR) && (max_count-- > 0));
++ tty_flip_buffer_push(tty);
++}
++
++static void sdio_uart_transmit_chars(struct sdio_uart_port *port)
++{
++ struct circ_buf *xmit = &port->xmit;
++ int count;
++
++ if (port->x_char) {
++ sdio_out(port, UART_TX, port->x_char);
++ port->icount.tx++;
++ port->x_char = 0;
++ return;
++ }
++ if (circ_empty(xmit) || port->tty->stopped || port->tty->hw_stopped) {
++ sdio_uart_stop_tx(port);
++ return;
++ }
++
++ count = 16;
++ do {
++ sdio_out(port, UART_TX, xmit->buf[xmit->tail]);
++ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++ port->icount.tx++;
++ if (circ_empty(xmit))
++ break;
++ } while (--count > 0);
++
++ if (circ_chars_pending(xmit) < WAKEUP_CHARS)
++ tty_wakeup(port->tty);
++
++ if (circ_empty(xmit))
++ sdio_uart_stop_tx(port);
++}
++
++static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
++{
++ int status;
++
++ status = sdio_in(port, UART_MSR);
++
++ if ((status & UART_MSR_ANY_DELTA) == 0)
++ return;
++
++ if (status & UART_MSR_TERI)
++ port->icount.rng++;
++ if (status & UART_MSR_DDSR)
++ port->icount.dsr++;
++ if (status & UART_MSR_DDCD)
++ port->icount.dcd++;
++ if (status & UART_MSR_DCTS) {
++ port->icount.cts++;
++ if (port->tty->termios->c_cflag & CRTSCTS) {
++ int cts = (status & UART_MSR_CTS);
++ if (port->tty->hw_stopped) {
++ if (cts) {
++ port->tty->hw_stopped = 0;
++ sdio_uart_start_tx(port);
++ tty_wakeup(port->tty);
++ }
++ } else {
++ if (!cts) {
++ port->tty->hw_stopped = 1;
++ sdio_uart_stop_tx(port);
++ }
++ }
++ }
++ }
++}
++
++/*
++ * This handles the interrupt from one port.
++ */
++static void sdio_uart_irq(struct sdio_func *func)
++{
++ struct sdio_uart_port *port = sdio_get_drvdata(func);
++ unsigned int iir, lsr;
++
++ iir = sdio_in(port, UART_IIR);
++ if (iir & UART_IIR_NO_INT)
++ return;
++ lsr = sdio_in(port, UART_LSR);
++ if (lsr & UART_LSR_DR)
++ sdio_uart_receive_chars(port, &lsr);
++ sdio_uart_check_modem_status(port);
++ if (lsr & UART_LSR_THRE)
++ sdio_uart_transmit_chars(port);
++}
++
++static int sdio_uart_startup(struct sdio_uart_port *port)
++{
++ unsigned long page;
++ int ret;
++
++ /*
++ * Set the TTY IO error marker - we will only clear this
++ * once we have successfully opened the port.
++ */
++ set_bit(TTY_IO_ERROR, &port->tty->flags);
++
++ /* Initialise and allocate the transmit buffer. */
++ page = __get_free_page(GFP_KERNEL);
++ if (!page)
++ return -ENOMEM;
++ port->xmit.buf = (unsigned char *)page;
++ circ_clear(&port->xmit);
++
++ ret = sdio_uart_claim_func(port);
++ if (ret)
++ goto err1;
++ ret = sdio_enable_func(port->func);
++ if (ret)
++ goto err2;
++ ret = sdio_claim_irq(port->func, sdio_uart_irq);
++ if (ret)
++ goto err3;
++
++ /*
++ * Clear the FIFO buffers and disable them.
++ * (they will be reenabled in sdio_change_speed())
++ */
++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
++ sdio_out(port, UART_FCR, 0);
++
++ /*
++ * Clear the interrupt registers.
++ */
++ (void) sdio_in(port, UART_LSR);
++ (void) sdio_in(port, UART_RX);
++ (void) sdio_in(port, UART_IIR);
++ (void) sdio_in(port, UART_MSR);
++
++ /*
++ * Now, initialize the UART
++ */
++ sdio_out(port, UART_LCR, UART_LCR_WLEN8);
++
++ port->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
++ port->mctrl = TIOCM_OUT2;
++
++ sdio_uart_change_speed(port, port->tty->termios, NULL);
++
++ if (port->tty->termios->c_cflag & CBAUD)
++ sdio_uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++ if (port->tty->termios->c_cflag & CRTSCTS)
++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS))
++ port->tty->hw_stopped = 1;
++
++ clear_bit(TTY_IO_ERROR, &port->tty->flags);
++
++ /* Kick the IRQ handler once while we're still holding the host lock */
++ sdio_uart_irq(port->func);
++
++ sdio_uart_release_func(port);
++ return 0;
++
++err3:
++ sdio_disable_func(port->func);
++err2:
++ sdio_uart_release_func(port);
++err1:
++ free_page((unsigned long)port->xmit.buf);
++ return ret;
++}
++
++static void sdio_uart_shutdown(struct sdio_uart_port *port)
++{
++ int ret;
++
++ ret = sdio_uart_claim_func(port);
++ if (ret)
++ goto skip;
++
++ sdio_uart_stop_rx(port);
++
++ /* TODO: wait here for TX FIFO to drain */
++
++ /* Turn off DTR and RTS early. */
++ if (port->tty->termios->c_cflag & HUPCL)
++ sdio_uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
++
++ /* Disable interrupts from this port */
++ sdio_release_irq(port->func);
++ port->ier = 0;
++ sdio_out(port, UART_IER, 0);
++
++ sdio_uart_clear_mctrl(port, TIOCM_OUT2);
++
++ /* Disable break condition and FIFOs. */
++ port->lcr &= ~UART_LCR_SBC;
++ sdio_out(port, UART_LCR, port->lcr);
++ sdio_out(port, UART_FCR, UART_FCR_ENABLE_FIFO |
++ UART_FCR_CLEAR_RCVR |
++ UART_FCR_CLEAR_XMIT);
++ sdio_out(port, UART_FCR, 0);
++
++ sdio_disable_func(port->func);
++
++ sdio_uart_release_func(port);
++
++skip:
++ /* Free the transmit buffer page. */
++ free_page((unsigned long)port->xmit.buf);
++}
++
++static int sdio_uart_open (struct tty_struct *tty, struct file * filp)
++{
++ struct sdio_uart_port *port;
++ int ret;
++
++ port = sdio_uart_port_get(tty->index);
++ if (!port)
++ return -ENODEV;
++
++ mutex_lock(&port->open_lock);
++
++ /*
++ * Make sure not to mess up with a dead port
++ * which has not been closed yet.
++ */
++ if (tty->driver_data && tty->driver_data != port) {
++ mutex_unlock(&port->open_lock);
++ sdio_uart_port_put(port);
++ return -EBUSY;
++ }
++
++ if (!port->opened) {
++ tty->driver_data = port;
++ port->tty = tty;
++ ret = sdio_uart_startup(port);
++ if (ret) {
++ tty->driver_data = NULL;
++ port->tty = NULL;
++ mutex_unlock(&port->open_lock);
++ sdio_uart_port_put(port);
++ return ret;
++ }
++ }
++ port->opened++;
++ mutex_unlock(&port->open_lock);
++ return 0;
++}
++
++static void sdio_uart_close(struct tty_struct *tty, struct file * filp)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++
++ if (!port)
++ return;
++
++ mutex_lock(&port->open_lock);
++ BUG_ON(!port->opened);
++
++ /*
++ * This is messy. The tty layer calls us even when open()
++ * returned an error. Ignore this close request if tty->count
++ * is larger than port->count.
++ */
++ if (tty->count > port->opened) {
++ mutex_unlock(&port->open_lock);
++ return;
++ }
++
++ if (--port->opened == 0) {
++ tty->closing = 1;
++ sdio_uart_shutdown(port);
++ tty_ldisc_flush(tty);
++ port->tty = NULL;
++ tty->driver_data = NULL;
++ tty->closing = 0;
++ }
++ mutex_unlock(&port->open_lock);
++ sdio_uart_port_put(port);
++}
++
++static int sdio_uart_write(struct tty_struct * tty, const unsigned char *buf,
++ int count)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ struct circ_buf *circ = &port->xmit;
++ int c, ret = 0;
++
++ if (!port->func)
++ return -ENODEV;
++
++ spin_lock(&port->write_lock);
++ while (1) {
++ c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
++ if (count < c)
++ c = count;
++ if (c <= 0)
++ break;
++ memcpy(circ->buf + circ->head, buf, c);
++ circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
++ buf += c;
++ count -= c;
++ ret += c;
++ }
++ spin_unlock(&port->write_lock);
++
++ if ( !(port->ier & UART_IER_THRI)) {
++ int err = sdio_uart_claim_func(port);
++ if (!err) {
++ sdio_uart_start_tx(port);
++ sdio_uart_irq(port->func);
++ sdio_uart_release_func(port);
++ } else
++ ret = err;
++ }
++
++ return ret;
++}
++
++static int sdio_uart_write_room(struct tty_struct *tty)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ return port ? circ_chars_free(&port->xmit) : 0;
++}
++
++static int sdio_uart_chars_in_buffer(struct tty_struct *tty)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ return port ? circ_chars_pending(&port->xmit) : 0;
++}
++
++static void sdio_uart_send_xchar(struct tty_struct *tty, char ch)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++
++ port->x_char = ch;
++ if (ch && !(port->ier & UART_IER_THRI)) {
++ if (sdio_uart_claim_func(port) != 0)
++ return;
++ sdio_uart_start_tx(port);
++ sdio_uart_irq(port->func);
++ sdio_uart_release_func(port);
++ }
++}
++
++static void sdio_uart_throttle(struct tty_struct *tty)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++
++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++ return;
++
++ if (sdio_uart_claim_func(port) != 0)
++ return;
++
++ if (I_IXOFF(tty)) {
++ port->x_char = STOP_CHAR(tty);
++ sdio_uart_start_tx(port);
++ }
++
++ if (tty->termios->c_cflag & CRTSCTS)
++ sdio_uart_clear_mctrl(port, TIOCM_RTS);
++
++ sdio_uart_irq(port->func);
++ sdio_uart_release_func(port);
++}
++
++static void sdio_uart_unthrottle(struct tty_struct *tty)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++
++ if (!I_IXOFF(tty) && !(tty->termios->c_cflag & CRTSCTS))
++ return;
++
++ if (sdio_uart_claim_func(port) != 0)
++ return;
++
++ if (I_IXOFF(tty)) {
++ if (port->x_char) {
++ port->x_char = 0;
++ } else {
++ port->x_char = START_CHAR(tty);
++ sdio_uart_start_tx(port);
++ }
++ }
++
++ if (tty->termios->c_cflag & CRTSCTS)
++ sdio_uart_set_mctrl(port, TIOCM_RTS);
++
++ sdio_uart_irq(port->func);
++ sdio_uart_release_func(port);
++}
++
++static void sdio_uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ unsigned int cflag = tty->termios->c_cflag;
++
++#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
++
++ if ((cflag ^ old_termios->c_cflag) == 0 &&
++ RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
++ return;
++
++ if (sdio_uart_claim_func(port) != 0)
++ return;
++
++ sdio_uart_change_speed(port, tty->termios, old_termios);
++
++ /* Handle transition to B0 status */
++ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
++ sdio_uart_clear_mctrl(port, TIOCM_RTS | TIOCM_DTR);
++
++ /* Handle transition away from B0 status */
++ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
++ unsigned int mask = TIOCM_DTR;
++ if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags))
++ mask |= TIOCM_RTS;
++ sdio_uart_set_mctrl(port, mask);
++ }
++
++ /* Handle turning off CRTSCTS */
++ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
++ tty->hw_stopped = 0;
++ sdio_uart_start_tx(port);
++ }
++
++ /* Handle turning on CRTSCTS */
++ if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
++ if (!(sdio_uart_get_mctrl(port) & TIOCM_CTS)) {
++ tty->hw_stopped = 1;
++ sdio_uart_stop_tx(port);
++ }
++ }
++
++ sdio_uart_release_func(port);
++}
++
++static void sdio_uart_break_ctl(struct tty_struct *tty, int break_state)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++
++ if (sdio_uart_claim_func(port) != 0)
++ return;
++
++ if (break_state == -1)
++ port->lcr |= UART_LCR_SBC;
++ else
++ port->lcr &= ~UART_LCR_SBC;
++ sdio_out(port, UART_LCR, port->lcr);
++
++ sdio_uart_release_func(port);
++}
++
++static int sdio_uart_tiocmget(struct tty_struct *tty, struct file *file)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ int result;
++
++ result = sdio_uart_claim_func(port);
++ if (!result) {
++ result = port->mctrl | sdio_uart_get_mctrl(port);
++ sdio_uart_release_func(port);
++ }
++
++ return result;
++}
++
++static int sdio_uart_tiocmset(struct tty_struct *tty, struct file *file,
++ unsigned int set, unsigned int clear)
++{
++ struct sdio_uart_port *port = tty->driver_data;
++ int result;
++
++ result =sdio_uart_claim_func(port);
++ if(!result) {
++ sdio_uart_update_mctrl(port, set, clear);
++ sdio_uart_release_func(port);
++ }
++
++ return result;
++}
++
++static int sdio_uart_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int i, len = 0;
++ off_t begin = 0;
++
++ len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n",
++ "", "", "");
++ for (i = 0; i < UART_NR && len < PAGE_SIZE - 96; i++) {
++ struct sdio_uart_port *port = sdio_uart_port_get(i);
++ if (port) {
++ len += sprintf(page+len, "%d: uart:SDIO", i);
++ if(capable(CAP_SYS_ADMIN)) {
++ len += sprintf(page + len, " tx:%d rx:%d",
++ port->icount.tx, port->icount.rx);
++ if (port->icount.frame)
++ len += sprintf(page + len, " fe:%d",
++ port->icount.frame);
++ if (port->icount.parity)
++ len += sprintf(page + len, " pe:%d",
++ port->icount.parity);
++ if (port->icount.brk)
++ len += sprintf(page + len, " brk:%d",
++ port->icount.brk);
++ if (port->icount.overrun)
++ len += sprintf(page + len, " oe:%d",
++ port->icount.overrun);
++ if (port->icount.cts)
++ len += sprintf(page + len, " cts:%d",
++ port->icount.cts);
++ if (port->icount.dsr)
++ len += sprintf(page + len, " dsr:%d",
++ port->icount.dsr);
++ if (port->icount.rng)
++ len += sprintf(page + len, " rng:%d",
++ port->icount.rng);
++ if (port->icount.dcd)
++ len += sprintf(page + len, " dcd:%d",
++ port->icount.dcd);
++ }
++ strcat(page, "\n");
++ len++;
++ sdio_uart_port_put(port);
++ }
++
++ if (len + begin > off + count)
++ goto done;
++ if (len + begin < off) {
++ begin += len;
++ len = 0;
++ }
++ }
++ *eof = 1;
++
++done:
++ if (off >= len + begin)
++ return 0;
++ *start = page + (off - begin);
++ return (count < begin + len - off) ? count : (begin + len - off);
++}
++
++static const struct tty_operations sdio_uart_ops = {
++ .open = sdio_uart_open,
++ .close = sdio_uart_close,
++ .write = sdio_uart_write,
++ .write_room = sdio_uart_write_room,
++ .chars_in_buffer = sdio_uart_chars_in_buffer,
++ .send_xchar = sdio_uart_send_xchar,
++ .throttle = sdio_uart_throttle,
++ .unthrottle = sdio_uart_unthrottle,
++ .set_termios = sdio_uart_set_termios,
++ .break_ctl = sdio_uart_break_ctl,
++ .tiocmget = sdio_uart_tiocmget,
++ .tiocmset = sdio_uart_tiocmset,
++ .read_proc = sdio_uart_read_proc,
++};
++
++static struct tty_driver *sdio_uart_tty_driver;
++
++static int sdio_uart_probe(struct sdio_func *func,
++ const struct sdio_device_id *id)
++{
++ struct sdio_uart_port *port;
++ int ret;
++
++ port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
++ if (!port)
++ return -ENOMEM;
++
++ if (func->class == SDIO_CLASS_UART) {
++ printk(KERN_WARNING "%s: need info on UART class basic setup\n",
++ sdio_func_id(func));
++ kfree(port);
++ return -ENOSYS;
++ } else if (func->class == SDIO_CLASS_GPS) {
++ /*
++ * We need tuple 0x91. It contains SUBTPL_SIOREG
++ * and SUBTPL_RCVCAPS.
++ */
++ struct sdio_func_tuple *tpl;
++ for (tpl = func->tuples; tpl; tpl = tpl->next) {
++ if (tpl->code != 0x91)
++ continue;
++ if (tpl->size < 10)
++ continue;
++ if (tpl->data[1] == 0) /* SUBTPL_SIOREG */
++ break;
++ }
++ if (!tpl) {
++ printk(KERN_WARNING
++ "%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
++ sdio_func_id(func));
++ kfree(port);
++ return -EINVAL;
++ }
++ printk(KERN_DEBUG "%s: Register ID = 0x%02x, Exp ID = 0x%02x\n",
++ sdio_func_id(func), tpl->data[2], tpl->data[3]);
++ port->regs_offset = (tpl->data[4] << 0) |
++ (tpl->data[5] << 8) |
++ (tpl->data[6] << 16);
++ printk(KERN_DEBUG "%s: regs offset = 0x%x\n",
++ sdio_func_id(func), port->regs_offset);
++ port->uartclk = tpl->data[7] * 115200;
++ if (port->uartclk == 0)
++ port->uartclk = 115200;
++ printk(KERN_DEBUG "%s: clk %d baudcode %u 4800-div %u\n",
++ sdio_func_id(func), port->uartclk,
++ tpl->data[7], tpl->data[8] | (tpl->data[9] << 8));
++ } else {
++ kfree(port);
++ return -EINVAL;
++ }
++
++ port->func = func;
++ sdio_set_drvdata(func, port);
++
++ ret = sdio_uart_add_port(port);
++ if (ret) {
++ kfree(port);
++ } else {
++ struct device *dev;
++ dev = tty_register_device(sdio_uart_tty_driver, port->index, &func->dev);
++ if (IS_ERR(dev)) {
++ sdio_uart_port_remove(port);
++ ret = PTR_ERR(dev);
++ }
++ }
++
++ return ret;
++}
++
++static void sdio_uart_remove(struct sdio_func *func)
++{
++ struct sdio_uart_port *port = sdio_get_drvdata(func);
++
++ tty_unregister_device(sdio_uart_tty_driver, port->index);
++ sdio_uart_port_remove(port);
++}
++
++static const struct sdio_device_id sdio_uart_ids[] = {
++ { SDIO_DEVICE_CLASS(SDIO_CLASS_UART) },
++ { SDIO_DEVICE_CLASS(SDIO_CLASS_GPS) },
++ { /* end: all zeroes */ },
++};
++
++MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
++
++static struct sdio_driver sdio_uart_driver = {
++ .probe = sdio_uart_probe,
++ .remove = sdio_uart_remove,
++ .name = "sdio_uart",
++ .id_table = sdio_uart_ids,
++};
++
++static int __init sdio_uart_init(void)
++{
++ int ret;
++ struct tty_driver *tty_drv;
++
++ sdio_uart_tty_driver = tty_drv = alloc_tty_driver(UART_NR);
++ if (!tty_drv)
++ return -ENOMEM;
++
++ tty_drv->owner = THIS_MODULE;
++ tty_drv->driver_name = "sdio_uart";
++ tty_drv->name = "ttySDIO";
++ tty_drv->major = 0; /* dynamically allocated */
++ tty_drv->minor_start = 0;
++ tty_drv->type = TTY_DRIVER_TYPE_SERIAL;
++ tty_drv->subtype = SERIAL_TYPE_NORMAL;
++ tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
++ tty_drv->init_termios = tty_std_termios;
++ tty_drv->init_termios.c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL;
++ tty_set_operations(tty_drv, &sdio_uart_ops);
++
++ ret = tty_register_driver(tty_drv);
++ if (ret)
++ goto err1;
++
++ ret = sdio_register_driver(&sdio_uart_driver);
++ if (ret)
++ goto err2;
++
++ return 0;
++
++err2:
++ tty_unregister_driver(tty_drv);
++err1:
++ put_tty_driver(tty_drv);
++ return ret;
++}
++
++static void __exit sdio_uart_exit(void)
++{
++ sdio_unregister_driver(&sdio_uart_driver);
++ tty_unregister_driver(sdio_uart_tty_driver);
++ put_tty_driver(sdio_uart_tty_driver);
++}
++
++module_init(sdio_uart_init);
++module_exit(sdio_uart_exit);
++
++MODULE_AUTHOR("Nicolas Pitre");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/drivers/mmc/core/bus.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/bus.c 2007-07-19 00:19:51.203967255 +0200
+@@ -0,0 +1,261 @@
++/*
++ * linux/drivers/mmc/core/bus.c
++ *
++ * Copyright (C) 2003 Russell King, All Rights Reserved.
++ * Copyright (C) 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * MMC card bus driver model
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++
++#include <linux/mmc/card.h>
++#include <linux/mmc/host.h>
++
++#include "sysfs.h"
++#include "core.h"
++#include "bus.h"
++
++#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
++#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
++
++static ssize_t mmc_type_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct mmc_card *card = dev_to_mmc_card(dev);
++
++ switch (card->type) {
++ case MMC_TYPE_MMC:
++ return sprintf(buf, "MMC\n");
++ case MMC_TYPE_SD:
++ return sprintf(buf, "SD\n");
++ case MMC_TYPE_SDIO:
++ return sprintf(buf, "SDIO\n");
++ default:
++ return -EFAULT;
++ }
++}
++
++static struct device_attribute mmc_dev_attrs[] = {
++ MMC_ATTR_RO(type),
++ __ATTR_NULL,
++};
++
++/*
++ * This currently matches any MMC driver to any MMC card - drivers
++ * themselves make the decision whether to drive this card in their
++ * probe method.
++ */
++static int mmc_bus_match(struct device *dev, struct device_driver *drv)
++{
++ return 1;
++}
++
++static int
++mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
++ int buf_size)
++{
++ struct mmc_card *card = dev_to_mmc_card(dev);
++ const char *type;
++ int i = 0, length = 0;
++
++ switch (card->type) {
++ case MMC_TYPE_MMC:
++ type = "MMC";
++ break;
++ case MMC_TYPE_SD:
++ type = "SD";
++ break;
++ case MMC_TYPE_SDIO:
++ type = "SDIO";
++ break;
++ default:
++ type = NULL;
++ }
++
++ if (type) {
++ if (add_uevent_var(envp, num_envp, &i,
++ buf, buf_size, &length,
++ "MMC_TYPE=%s", type))
++ return -ENOMEM;
++ }
++
++ if (add_uevent_var(envp, num_envp, &i,
++ buf, buf_size, &length,
++ "MMC_NAME=%s", mmc_card_name(card)))
++ return -ENOMEM;
++
++ envp[i] = NULL;
++
++ return 0;
++}
++
++static int mmc_bus_probe(struct device *dev)
++{
++ struct mmc_driver *drv = to_mmc_driver(dev->driver);
++ struct mmc_card *card = dev_to_mmc_card(dev);
++
++ return drv->probe(card);
++}
++
++static int mmc_bus_remove(struct device *dev)
++{
++ struct mmc_driver *drv = to_mmc_driver(dev->driver);
++ struct mmc_card *card = dev_to_mmc_card(dev);
++
++ drv->remove(card);
++
++ return 0;
++}
++
++static int mmc_bus_suspend(struct device *dev, pm_message_t state)
++{
++ struct mmc_driver *drv = to_mmc_driver(dev->driver);
++ struct mmc_card *card = dev_to_mmc_card(dev);
++ int ret = 0;
++
++ if (dev->driver && drv->suspend)
++ ret = drv->suspend(card, state);
++ return ret;
++}
++
++static int mmc_bus_resume(struct device *dev)
++{
++ struct mmc_driver *drv = to_mmc_driver(dev->driver);
++ struct mmc_card *card = dev_to_mmc_card(dev);
++ int ret = 0;
++
++ if (dev->driver && drv->resume)
++ ret = drv->resume(card);
++ return ret;
++}
++
++static struct bus_type mmc_bus_type = {
++ .name = "mmc",
++ .dev_attrs = mmc_dev_attrs,
++ .match = mmc_bus_match,
++ .uevent = mmc_bus_uevent,
++ .probe = mmc_bus_probe,
++ .remove = mmc_bus_remove,
++ .suspend = mmc_bus_suspend,
++ .resume = mmc_bus_resume,
++};
++
++int mmc_register_bus(void)
++{
++ return bus_register(&mmc_bus_type);
++}
++
++void mmc_unregister_bus(void)
++{
++ bus_unregister(&mmc_bus_type);
++}
++
++/**
++ * mmc_register_driver - register a media driver
++ * @drv: MMC media driver
++ */
++int mmc_register_driver(struct mmc_driver *drv)
++{
++ drv->drv.bus = &mmc_bus_type;
++ return driver_register(&drv->drv);
++}
++
++EXPORT_SYMBOL(mmc_register_driver);
++
++/**
++ * mmc_unregister_driver - unregister a media driver
++ * @drv: MMC media driver
++ */
++void mmc_unregister_driver(struct mmc_driver *drv)
++{
++ drv->drv.bus = &mmc_bus_type;
++ driver_unregister(&drv->drv);
++}
++
++EXPORT_SYMBOL(mmc_unregister_driver);
++
++static void mmc_release_card(struct device *dev)
++{
++ struct mmc_card *card = dev_to_mmc_card(dev);
++
++ kfree(card);
++}
++
++/*
++ * Allocate and initialise a new MMC card structure.
++ */
++struct mmc_card *mmc_alloc_card(struct mmc_host *host)
++{
++ struct mmc_card *card;
++
++ card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
++ if (!card)
++ return ERR_PTR(-ENOMEM);
++
++ memset(card, 0, sizeof(struct mmc_card));
++
++ card->host = host;
++
++ device_initialize(&card->dev);
++
++ card->dev.parent = mmc_classdev(host);
++ card->dev.bus = &mmc_bus_type;
++ card->dev.release = mmc_release_card;
++
++ return card;
++}
++
++/*
++ * Register a new MMC card with the driver model.
++ */
++int mmc_add_card(struct mmc_card *card)
++{
++ int ret;
++
++ snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
++ "%s:%04x", mmc_hostname(card->host), card->rca);
++
++ card->dev.uevent_suppress = 1;
++
++ ret = device_add(&card->dev);
++ if (ret)
++ return ret;
++
++ if (card->host->bus_ops->sysfs_add) {
++ ret = card->host->bus_ops->sysfs_add(card->host, card);
++ if (ret) {
++ device_del(&card->dev);
++ return ret;
++ }
++ }
++
++ card->dev.uevent_suppress = 0;
++
++ kobject_uevent(&card->dev.kobj, KOBJ_ADD);
++
++ mmc_card_set_present(card);
++
++ return 0;
++}
++
++/*
++ * Unregister a new MMC card with the driver model, and
++ * (eventually) free it.
++ */
++void mmc_remove_card(struct mmc_card *card)
++{
++ if (mmc_card_present(card)) {
++ if (card->host->bus_ops->sysfs_remove)
++ card->host->bus_ops->sysfs_remove(card->host, card);
++ device_del(&card->dev);
++ }
++
++ put_device(&card->dev);
++}
++
+Index: linux-2.6.22.1/drivers/mmc/core/bus.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/bus.h 2007-07-19 00:19:51.203967255 +0200
+@@ -0,0 +1,22 @@
++/*
++ * linux/drivers/mmc/core/bus.h
++ *
++ * Copyright (C) 2003 Russell King, All Rights Reserved.
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#ifndef _MMC_CORE_BUS_H
++#define _MMC_CORE_BUS_H
++
++struct mmc_card *mmc_alloc_card(struct mmc_host *host);
++int mmc_add_card(struct mmc_card *card);
++void mmc_remove_card(struct mmc_card *card);
++
++int mmc_register_bus(void);
++void mmc_unregister_bus(void);
++
++#endif
++
+Index: linux-2.6.22.1/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/core.c 2007-07-19 00:18:26.731153423 +0200
++++ linux-2.6.22.1/drivers/mmc/core/core.c 2007-07-19 00:19:51.203967255 +0200
+@@ -27,13 +27,36 @@
+ #include <linux/mmc/sd.h>
+
+ #include "core.h"
+-#include "sysfs.h"
++#include "bus.h"
++#include "host.h"
++#include "sdio_bus.h"
+
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
++#include "sdio_ops.h"
+
+ extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
+ extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
++extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
++
++static struct workqueue_struct *workqueue;
++
++/*
++ * Internal function. Schedule delayed work in the MMC work queue.
++ */
++static int mmc_schedule_delayed_work(struct delayed_work *work,
++ unsigned long delay)
++{
++ return queue_delayed_work(workqueue, work, delay);
++}
++
++/*
++ * Internal function. Flush all scheduled work from the MMC work queue.
++ */
++static void mmc_flush_scheduled_work(void)
++{
++ flush_workqueue(workqueue);
++}
+
+ /**
+ * mmc_request_done - finish processing an MMC request
+@@ -222,36 +245,41 @@
+ /**
+ * __mmc_claim_host - exclusively claim a host
+ * @host: mmc host to claim
+- * @card: mmc card to claim host for
++ * @abort: whether or not the operation should be aborted
+ *
+- * Claim a host for a set of operations. If a valid card
+- * is passed and this wasn't the last card selected, select
+- * the card before returning.
+- *
+- * Note: you should use mmc_card_claim_host or mmc_claim_host.
++ * Claim a host for a set of operations. If @abort is non null and
++ * dereference a non-zero value then this will return prematurely with
++ * that non-zero value without acquiring the lock. Returns zero
++ * with the lock held otherwise.
+ */
+-void mmc_claim_host(struct mmc_host *host)
++int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
+ {
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
++ int stop;
+
+ add_wait_queue(&host->wq, &wait);
+ spin_lock_irqsave(&host->lock, flags);
+ while (1) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+- if (!host->claimed)
++ stop = abort ? atomic_read(abort) : 0;
++ if (stop || !host->claimed)
+ break;
+ spin_unlock_irqrestore(&host->lock, flags);
+ schedule();
+ spin_lock_irqsave(&host->lock, flags);
+ }
+ set_current_state(TASK_RUNNING);
+- host->claimed = 1;
++ if (!stop)
++ host->claimed = 1;
++ else
++ wake_up(&host->wq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ remove_wait_queue(&host->wq, &wait);
++ return stop;
+ }
+
+-EXPORT_SYMBOL(mmc_claim_host);
++EXPORT_SYMBOL(__mmc_claim_host);
+
+ /**
+ * mmc_release_host - release a host
+@@ -369,22 +397,6 @@
+ }
+
+ /*
+- * Allocate a new MMC card
+- */
+-struct mmc_card *mmc_alloc_card(struct mmc_host *host)
+-{
+- struct mmc_card *card;
+-
+- card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL);
+- if (!card)
+- return ERR_PTR(-ENOMEM);
+-
+- mmc_init_card(card, host);
+-
+- return card;
+-}
+-
+-/*
+ * Apply power to the MMC stack. This is a two-stage process.
+ * First, we enable power to the card without the clock running.
+ * We then wait a bit for the power to stabilise. Finally,
+@@ -512,7 +524,7 @@
+ EXPORT_SYMBOL(mmc_detect_change);
+
+
+-static void mmc_rescan(struct work_struct *work)
++void mmc_rescan(struct work_struct *work)
+ {
+ struct mmc_host *host =
+ container_of(work, struct mmc_host, detect.work);
+@@ -535,24 +547,38 @@
+
+ mmc_send_if_cond(host, host->ocr_avail);
+
++ /*
++ * First we search for SDIO...
++ */
++ err = mmc_send_io_op_cond(host, 0, &ocr);
++ if (err == MMC_ERR_NONE) {
++ if (mmc_attach_sdio(host, ocr))
++ mmc_power_off(host);
++ return;
++ }
++
++ /*
++ * ...then normal SD...
++ */
+ err = mmc_send_app_op_cond(host, 0, &ocr);
+ if (err == MMC_ERR_NONE) {
+ if (mmc_attach_sd(host, ocr))
+ mmc_power_off(host);
+- } else {
+- /*
+- * If we fail to detect any SD cards then try
+- * searching for MMC cards.
+- */
+- err = mmc_send_op_cond(host, 0, &ocr);
+- if (err == MMC_ERR_NONE) {
+- if (mmc_attach_mmc(host, ocr))
+- mmc_power_off(host);
+- } else {
++ return;
++ }
++
++ /*
++ * ...and finally MMC.
++ */
++ err = mmc_send_op_cond(host, 0, &ocr);
++ if (err == MMC_ERR_NONE) {
++ if (mmc_attach_mmc(host, ocr))
+ mmc_power_off(host);
+- mmc_release_host(host);
+- }
++ return;
+ }
++
++ mmc_release_host(host);
++ mmc_power_off(host);
+ } else {
+ if (host->bus_ops->detect && !host->bus_dead)
+ host->bus_ops->detect(host);
+@@ -561,69 +587,13 @@
+ }
+ }
+
+-
+-/**
+- * mmc_alloc_host - initialise the per-host structure.
+- * @extra: sizeof private data structure
+- * @dev: pointer to host device model structure
+- *
+- * Initialise the per-host structure.
+- */
+-struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
+-{
+- struct mmc_host *host;
+-
+- host = mmc_alloc_host_sysfs(extra, dev);
+- if (host) {
+- spin_lock_init(&host->lock);
+- init_waitqueue_head(&host->wq);
+- INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+-
+- /*
+- * By default, hosts do not support SGIO or large requests.
+- * They have to set these according to their abilities.
+- */
+- host->max_hw_segs = 1;
+- host->max_phys_segs = 1;
+- host->max_seg_size = PAGE_CACHE_SIZE;
+-
+- host->max_req_size = PAGE_CACHE_SIZE;
+- host->max_blk_size = 512;
+- host->max_blk_count = PAGE_CACHE_SIZE / 512;
+- }
+-
+- return host;
+-}
+-
+-EXPORT_SYMBOL(mmc_alloc_host);
+-
+-/**
+- * mmc_add_host - initialise host hardware
+- * @host: mmc host
+- */
+-int mmc_add_host(struct mmc_host *host)
++void mmc_start_host(struct mmc_host *host)
+ {
+- int ret;
+-
+- ret = mmc_add_host_sysfs(host);
+- if (ret == 0) {
+- mmc_power_off(host);
+- mmc_detect_change(host, 0);
+- }
+-
+- return ret;
++ mmc_power_off(host);
++ mmc_detect_change(host, 0);
+ }
+
+-EXPORT_SYMBOL(mmc_add_host);
+-
+-/**
+- * mmc_remove_host - remove host hardware
+- * @host: mmc host
+- *
+- * Unregister and remove all cards associated with this host,
+- * and power down the MMC bus.
+- */
+-void mmc_remove_host(struct mmc_host *host)
++void mmc_stop_host(struct mmc_host *host)
+ {
+ #ifdef CONFIG_MMC_DEBUG
+ unsigned long flags;
+@@ -648,24 +618,8 @@
+ BUG_ON(host->card);
+
+ mmc_power_off(host);
+- mmc_remove_host_sysfs(host);
+-}
+-
+-EXPORT_SYMBOL(mmc_remove_host);
+-
+-/**
+- * mmc_free_host - free the host structure
+- * @host: mmc host
+- *
+- * Free the host once all references to it have been dropped.
+- */
+-void mmc_free_host(struct mmc_host *host)
+-{
+- mmc_free_host_sysfs(host);
+ }
+
+-EXPORT_SYMBOL(mmc_free_host);
+-
+ #ifdef CONFIG_PM
+
+ /**
+@@ -726,4 +680,47 @@
+
+ #endif
+
++static int __init mmc_init(void)
++{
++ int ret;
++
++ workqueue = create_singlethread_workqueue("kmmcd");
++ if (!workqueue)
++ return -ENOMEM;
++
++ ret = mmc_register_bus();
++ if (ret)
++ goto destroy_workqueue;
++
++ ret = mmc_register_host_class();
++ if (ret)
++ goto unregister_bus;
++
++ ret = sdio_register_bus();
++ if (ret)
++ goto unregister_host_class;
++
++ return 0;
++
++unregister_host_class:
++ mmc_unregister_host_class();
++unregister_bus:
++ mmc_unregister_bus();
++destroy_workqueue:
++ destroy_workqueue(workqueue);
++
++ return ret;
++}
++
++static void __exit mmc_exit(void)
++{
++ sdio_unregister_bus();
++ mmc_unregister_host_class();
++ mmc_unregister_bus();
++ destroy_workqueue(workqueue);
++}
++
++subsys_initcall(mmc_init);
++module_exit(mmc_exit);
++
+ MODULE_LICENSE("GPL");
+Index: linux-2.6.22.1/drivers/mmc/core/core.h
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/core.h 2007-07-19 00:18:26.751154563 +0200
++++ linux-2.6.22.1/drivers/mmc/core/core.h 2007-07-19 00:19:51.203967255 +0200
+@@ -18,6 +18,8 @@
+ struct mmc_bus_ops {
+ void (*remove)(struct mmc_host *);
+ void (*detect)(struct mmc_host *);
++ int (*sysfs_add)(struct mmc_host *, struct mmc_card *card);
++ void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card);
+ void (*suspend)(struct mmc_host *);
+ void (*resume)(struct mmc_host *);
+ };
+@@ -54,8 +56,6 @@
+ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
+ void mmc_set_timing(struct mmc_host *host, unsigned int timing);
+
+-struct mmc_card *mmc_alloc_card(struct mmc_host *host);
+-
+ static inline void mmc_delay(unsigned int ms)
+ {
+ if (ms < 1000 / HZ) {
+@@ -66,5 +66,9 @@
+ }
+ }
+
++void mmc_rescan(struct work_struct *work);
++void mmc_start_host(struct mmc_host *host);
++void mmc_stop_host(struct mmc_host *host);
++
+ #endif
+
+Index: linux-2.6.22.1/drivers/mmc/core/host.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/host.c 2007-07-19 00:19:51.239969309 +0200
+@@ -0,0 +1,156 @@
++/*
++ * linux/drivers/mmc/core/host.c
++ *
++ * Copyright (C) 2003 Russell King, All Rights Reserved.
++ * Copyright (C) 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * MMC host class device management
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/idr.h>
++#include <linux/pagemap.h>
++
++#include <linux/mmc/host.h>
++
++#include "core.h"
++#include "host.h"
++
++#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
++
++static void mmc_host_classdev_release(struct device *dev)
++{
++ struct mmc_host *host = cls_dev_to_mmc_host(dev);
++ kfree(host);
++}
++
++static struct class mmc_host_class = {
++ .name = "mmc_host",
++ .dev_release = mmc_host_classdev_release,
++};
++
++int mmc_register_host_class(void)
++{
++ return class_register(&mmc_host_class);
++}
++
++void mmc_unregister_host_class(void)
++{
++ class_unregister(&mmc_host_class);
++}
++
++static DEFINE_IDR(mmc_host_idr);
++static DEFINE_SPINLOCK(mmc_host_lock);
++
++/**
++ * mmc_alloc_host - initialise the per-host structure.
++ * @extra: sizeof private data structure
++ * @dev: pointer to host device model structure
++ *
++ * Initialise the per-host structure.
++ */
++struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
++{
++ struct mmc_host *host;
++
++ host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
++ if (!host)
++ return NULL;
++
++ memset(host, 0, sizeof(struct mmc_host) + extra);
++
++ host->parent = dev;
++ host->class_dev.parent = dev;
++ host->class_dev.class = &mmc_host_class;
++ device_initialize(&host->class_dev);
++
++ spin_lock_init(&host->lock);
++ init_waitqueue_head(&host->wq);
++ INIT_DELAYED_WORK(&host->detect, mmc_rescan);
++
++ /*
++ * By default, hosts do not support SGIO or large requests.
++ * They have to set these according to their abilities.
++ */
++ host->max_hw_segs = 1;
++ host->max_phys_segs = 1;
++ host->max_seg_size = PAGE_CACHE_SIZE;
++
++ host->max_req_size = PAGE_CACHE_SIZE;
++ host->max_blk_size = 512;
++ host->max_blk_count = PAGE_CACHE_SIZE / 512;
++
++ return host;
++}
++
++EXPORT_SYMBOL(mmc_alloc_host);
++
++/**
++ * mmc_add_host - initialise host hardware
++ * @host: mmc host
++ */
++int mmc_add_host(struct mmc_host *host)
++{
++ int err;
++
++ if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
++ return -ENOMEM;
++
++ spin_lock(&mmc_host_lock);
++ err = idr_get_new(&mmc_host_idr, host, &host->index);
++ spin_unlock(&mmc_host_lock);
++ if (err)
++ return err;
++
++ snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
++ "mmc%d", host->index);
++
++ err = device_add(&host->class_dev);
++ if (err)
++ return err;
++
++ mmc_start_host(host);
++
++ return 0;
++}
++
++EXPORT_SYMBOL(mmc_add_host);
++
++/**
++ * mmc_remove_host - remove host hardware
++ * @host: mmc host
++ *
++ * Unregister and remove all cards associated with this host,
++ * and power down the MMC bus.
++ */
++void mmc_remove_host(struct mmc_host *host)
++{
++ mmc_stop_host(host);
++
++ device_del(&host->class_dev);
++
++ spin_lock(&mmc_host_lock);
++ idr_remove(&mmc_host_idr, host->index);
++ spin_unlock(&mmc_host_lock);
++}
++
++EXPORT_SYMBOL(mmc_remove_host);
++
++/**
++ * mmc_free_host - free the host structure
++ * @host: mmc host
++ *
++ * Free the host once all references to it have been dropped.
++ */
++void mmc_free_host(struct mmc_host *host)
++{
++ put_device(&host->class_dev);
++}
++
++EXPORT_SYMBOL(mmc_free_host);
++
+Index: linux-2.6.22.1/drivers/mmc/core/host.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/host.h 2007-07-19 00:19:51.239969309 +0200
+@@ -0,0 +1,18 @@
++/*
++ * linux/drivers/mmc/core/host.h
++ *
++ * Copyright (C) 2003 Russell King, All Rights Reserved.
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#ifndef _MMC_CORE_HOST_H
++#define _MMC_CORE_HOST_H
++
++int mmc_register_host_class(void);
++void mmc_unregister_host_class(void);
++
++#endif
++
+Index: linux-2.6.22.1/drivers/mmc/core/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/Makefile 2007-07-19 00:18:26.979167557 +0200
++++ linux-2.6.22.1/drivers/mmc/core/Makefile 2007-07-19 00:19:51.263970674 +0200
+@@ -7,5 +7,8 @@
+ endif
+
+ obj-$(CONFIG_MMC) += mmc_core.o
+-mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o
++mmc_core-y := core.o sysfs.o bus.o host.o \
++ mmc.o mmc_ops.o sd.o sd_ops.o \
++ sdio.o sdio_ops.o sdio_bus.o \
++ sdio_cis.o sdio_io.o sdio_irq.o
+
+Index: linux-2.6.22.1/drivers/mmc/core/mmc.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/mmc.c 2007-07-19 00:18:26.771155703 +0200
++++ linux-2.6.22.1/drivers/mmc/core/mmc.c 2007-07-19 00:19:51.291972273 +0200
+@@ -18,6 +18,7 @@
+
+ #include "core.h"
+ #include "sysfs.h"
++#include "bus.h"
+ #include "mmc_ops.h"
+
+ static const unsigned int tran_exp[] = {
+@@ -236,7 +237,7 @@
+ * In the case of a resume, "curcard" will contain the card
+ * we're trying to reinitialise.
+ */
+-static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
++static int mmc_init_card(struct mmc_host *host, u32 ocr,
+ struct mmc_card *oldcard)
+ {
+ struct mmc_card *card;
+@@ -413,8 +414,7 @@
+ mmc_release_host(host);
+
+ if (err != MMC_ERR_NONE) {
+- mmc_remove_card(host->card);
+- host->card = NULL;
++ mmc_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+@@ -422,6 +422,53 @@
+ }
+ }
+
++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
++ card->raw_cid[2], card->raw_cid[3]);
++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
++ card->raw_csd[2], card->raw_csd[3]);
++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year);
++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev);
++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev);
++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid);
++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name);
++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid);
++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial);
++
++static struct device_attribute mmc_dev_attrs[] = {
++ MMC_ATTR_RO(cid),
++ MMC_ATTR_RO(csd),
++ MMC_ATTR_RO(date),
++ MMC_ATTR_RO(fwrev),
++ MMC_ATTR_RO(hwrev),
++ MMC_ATTR_RO(manfid),
++ MMC_ATTR_RO(name),
++ MMC_ATTR_RO(oemid),
++ MMC_ATTR_RO(serial),
++ __ATTR_NULL,
++};
++
++/*
++ * Adds sysfs entries as relevant.
++ */
++static int mmc_sysfs_add(struct mmc_host *host, struct mmc_card *card)
++{
++ int ret;
++
++ ret = mmc_add_attrs(card, mmc_dev_attrs);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/*
++ * Removes the sysfs entries added by mmc_sysfs_add().
++ */
++static void mmc_sysfs_remove(struct mmc_host *host, struct mmc_card *card)
++{
++ mmc_remove_attrs(card, mmc_dev_attrs);
++}
++
+ #ifdef CONFIG_MMC_UNSAFE_RESUME
+
+ /*
+@@ -453,11 +500,9 @@
+
+ mmc_claim_host(host);
+
+- err = mmc_sd_init_card(host, host->ocr, host->card);
++ err = mmc_init_card(host, host->ocr, host->card);
+ if (err != MMC_ERR_NONE) {
+- mmc_remove_card(host->card);
+- host->card = NULL;
+-
++ mmc_remove(host);
+ mmc_detach_bus(host);
+ }
+
+@@ -474,6 +519,8 @@
+ static const struct mmc_bus_ops mmc_ops = {
+ .remove = mmc_remove,
+ .detect = mmc_detect,
++ .sysfs_add = mmc_sysfs_add,
++ .sysfs_remove = mmc_sysfs_remove,
+ .suspend = mmc_suspend,
+ .resume = mmc_resume,
+ };
+@@ -512,13 +559,13 @@
+ /*
+ * Detect and init the card.
+ */
+- err = mmc_sd_init_card(host, host->ocr, NULL);
++ err = mmc_init_card(host, host->ocr, NULL);
+ if (err != MMC_ERR_NONE)
+ goto err;
+
+ mmc_release_host(host);
+
+- err = mmc_register_card(host->card);
++ err = mmc_add_card(host->card);
+ if (err)
+ goto reclaim_host;
+
+Index: linux-2.6.22.1/drivers/mmc/core/sd.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sd.c 2007-07-19 00:18:26.835159351 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sd.c 2007-07-19 00:19:51.375977058 +0200
+@@ -19,11 +19,10 @@
+
+ #include "core.h"
+ #include "sysfs.h"
++#include "bus.h"
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
+
+-#include "core.h"
+-
+ static const unsigned int tran_exp[] = {
+ 10000, 100000, 1000000, 10000000,
+ 0, 0, 0, 0
+@@ -487,8 +486,7 @@
+ mmc_release_host(host);
+
+ if (err != MMC_ERR_NONE) {
+- mmc_remove_card(host->card);
+- host->card = NULL;
++ mmc_sd_remove(host);
+
+ mmc_claim_host(host);
+ mmc_detach_bus(host);
+@@ -496,6 +494,55 @@
+ }
+ }
+
++MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
++ card->raw_cid[2], card->raw_cid[3]);
++MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
++ card->raw_csd[2], card->raw_csd[3]);
++MMC_ATTR_FN(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
++MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year);
++MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev);
++MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev);
++MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid);
++MMC_ATTR_FN(name, "%s\n", card->cid.prod_name);
++MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid);
++MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial);
++
++static struct device_attribute mmc_sd_dev_attrs[] = {
++ MMC_ATTR_RO(cid),
++ MMC_ATTR_RO(csd),
++ MMC_ATTR_RO(scr),
++ MMC_ATTR_RO(date),
++ MMC_ATTR_RO(fwrev),
++ MMC_ATTR_RO(hwrev),
++ MMC_ATTR_RO(manfid),
++ MMC_ATTR_RO(name),
++ MMC_ATTR_RO(oemid),
++ MMC_ATTR_RO(serial),
++ __ATTR_NULL,
++};
++
++/*
++ * Adds sysfs entries as relevant.
++ */
++static int mmc_sd_sysfs_add(struct mmc_host *host, struct mmc_card *card)
++{
++ int ret;
++
++ ret = mmc_add_attrs(card, mmc_sd_dev_attrs);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++/*
++ * Removes the sysfs entries added by mmc_sysfs_add().
++ */
++static void mmc_sd_sysfs_remove(struct mmc_host *host, struct mmc_card *card)
++{
++ mmc_remove_attrs(card, mmc_sd_dev_attrs);
++}
++
+ #ifdef CONFIG_MMC_UNSAFE_RESUME
+
+ /*
+@@ -529,9 +576,7 @@
+
+ err = mmc_sd_init_card(host, host->ocr, host->card);
+ if (err != MMC_ERR_NONE) {
+- mmc_remove_card(host->card);
+- host->card = NULL;
+-
++ mmc_sd_remove(host);
+ mmc_detach_bus(host);
+ }
+
+@@ -548,6 +593,8 @@
+ static const struct mmc_bus_ops mmc_sd_ops = {
+ .remove = mmc_sd_remove,
+ .detect = mmc_sd_detect,
++ .sysfs_add = mmc_sd_sysfs_add,
++ .sysfs_remove = mmc_sd_sysfs_remove,
+ .suspend = mmc_sd_suspend,
+ .resume = mmc_sd_resume,
+ };
+@@ -599,7 +646,7 @@
+
+ mmc_release_host(host);
+
+- err = mmc_register_card(host->card);
++ err = mmc_add_card(host->card);
+ if (err)
+ goto reclaim_host;
+
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_bus.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_bus.c 2007-07-19 00:39:19.490544115 +0200
+@@ -0,0 +1,251 @@
++/*
++ * linux/drivers/mmc/core/sdio_bus.c
++ *
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ *
++ * SDIO function driver model
++ */
++
++#include <linux/device.h>
++#include <linux/err.h>
++
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_bus.h"
++
++#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
++#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
++
++/* show configuration fields */
++#define sdio_config_attr(field, format_string) \
++static ssize_t \
++field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct sdio_func *func; \
++ \
++ func = dev_to_sdio_func (dev); \
++ return sprintf (buf, format_string, func->field); \
++}
++
++sdio_config_attr(class, "0x%02x\n");
++sdio_config_attr(vendor, "0x%04x\n");
++sdio_config_attr(device, "0x%04x\n");
++
++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct sdio_func *func = dev_to_sdio_func (dev);
++
++ return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
++ func->class, func->vendor, func->device);
++}
++
++struct device_attribute sdio_dev_attrs[] = {
++ __ATTR_RO(class),
++ __ATTR_RO(vendor),
++ __ATTR_RO(device),
++ __ATTR_RO(modalias),
++ __ATTR_NULL,
++};
++
++static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
++ const struct sdio_device_id *id)
++{
++ if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
++ return NULL;
++ if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
++ return NULL;
++ if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
++ return NULL;
++ return id;
++}
++
++static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
++ struct sdio_driver *sdrv)
++{
++ const struct sdio_device_id *ids;
++
++ ids = sdrv->id_table;
++
++ if (ids) {
++ while (ids->class || ids->vendor || ids->device) {
++ if (sdio_match_one(func, ids))
++ return ids;
++ ids++;
++ }
++ }
++
++ return NULL;
++}
++
++static int sdio_bus_match(struct device *dev, struct device_driver *drv)
++{
++ struct sdio_func *func = dev_to_sdio_func(dev);
++ struct sdio_driver *sdrv = to_sdio_driver(drv);
++
++ if (sdio_match_device(func, sdrv))
++ return 1;
++
++ return 0;
++}
++
++static int
++sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
++ int buf_size)
++{
++ struct sdio_func *func = dev_to_sdio_func(dev);
++ int i = 0, length = 0;
++
++ if (add_uevent_var(envp, num_envp, &i,
++ buf, buf_size, &length,
++ "SDIO_CLASS=%02X", func->class))
++ return -ENOMEM;
++
++ if (add_uevent_var(envp, num_envp, &i,
++ buf, buf_size, &length,
++ "SDIO_ID=%04X:%04X", func->vendor, func->device))
++ return -ENOMEM;
++
++ if (add_uevent_var(envp, num_envp, &i,
++ buf, buf_size, &length,
++ "MODALIAS=sdio:c%02Xv%04Xd%04X",
++ func->class, func->vendor, func->device))
++ return -ENOMEM;
++
++ envp[i] = NULL;
++
++ return 0;
++}
++
++static int sdio_bus_probe(struct device *dev)
++{
++ struct sdio_driver *drv = to_sdio_driver(dev->driver);
++ struct sdio_func *func = dev_to_sdio_func(dev);
++ const struct sdio_device_id *id;
++
++ id = sdio_match_device(func, drv);
++ if (!id)
++ return -ENODEV;
++
++ return drv->probe(func, id);
++}
++
++static int sdio_bus_remove(struct device *dev)
++{
++ struct sdio_driver *drv = to_sdio_driver(dev->driver);
++ struct sdio_func *func = dev_to_sdio_func(dev);
++
++ drv->remove(func);
++
++ return 0;
++}
++
++static struct bus_type sdio_bus_type = {
++ .name = "sdio",
++ .dev_attrs = sdio_dev_attrs,
++ .match = sdio_bus_match,
++ .uevent = sdio_bus_uevent,
++ .probe = sdio_bus_probe,
++ .remove = sdio_bus_remove,
++};
++
++int sdio_register_bus(void)
++{
++ return bus_register(&sdio_bus_type);
++}
++
++void sdio_unregister_bus(void)
++{
++ bus_unregister(&sdio_bus_type);
++}
++
++/**
++ * sdio_register_driver - register a function driver
++ * @drv: SDIO function driver
++ */
++int sdio_register_driver(struct sdio_driver *drv)
++{
++ drv->drv.name = drv->name;
++ drv->drv.bus = &sdio_bus_type;
++ return driver_register(&drv->drv);
++}
++
++EXPORT_SYMBOL_GPL(sdio_register_driver);
++
++/**
++ * sdio_unregister_driver - unregister a function driver
++ * @drv: SDIO function driver
++ */
++void sdio_unregister_driver(struct sdio_driver *drv)
++{
++ drv->drv.bus = &sdio_bus_type;
++ driver_unregister(&drv->drv);
++}
++
++EXPORT_SYMBOL_GPL(sdio_unregister_driver);
++
++static void sdio_release_func(struct device *dev)
++{
++ struct sdio_func *func = dev_to_sdio_func(dev);
++
++ kfree(func);
++}
++
++/*
++ * Allocate and initialise a new SDIO function structure.
++ */
++struct sdio_func *sdio_alloc_func(struct mmc_card *card)
++{
++ struct sdio_func *func;
++
++ func = kmalloc(sizeof(struct sdio_func), GFP_KERNEL);
++ if (!func)
++ return ERR_PTR(-ENOMEM);
++
++ memset(func, 0, sizeof(struct sdio_func));
++
++ func->card = card;
++
++ device_initialize(&func->dev);
++
++ func->dev.parent = &card->dev;
++ func->dev.bus = &sdio_bus_type;
++ func->dev.release = sdio_release_func;
++
++ return func;
++}
++
++/*
++ * Register a new SDIO function with the driver model.
++ */
++int sdio_add_func(struct sdio_func *func)
++{
++ int ret;
++
++ snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
++ "%s:%d", mmc_card_id(func->card), func->num);
++
++ ret = device_add(&func->dev);
++ if (ret == 0)
++ sdio_func_set_present(func);
++
++ return ret;
++}
++
++/*
++ * Unregister a SDIO function with the driver model, and
++ * (eventually) free it.
++ */
++void sdio_remove_func(struct sdio_func *func)
++{
++ if (sdio_func_present(func))
++ device_del(&func->dev);
++
++ put_device(&func->dev);
++}
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_bus.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_bus.h 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,22 @@
++/*
++ * linux/drivers/mmc/core/sdio_bus.h
++ *
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++#ifndef _MMC_CORE_SDIO_BUS_H
++#define _MMC_CORE_SDIO_BUS_H
++
++struct sdio_func *sdio_alloc_func(struct mmc_card *card);
++int sdio_add_func(struct sdio_func *func);
++void sdio_remove_func(struct sdio_func *func);
++
++int sdio_register_bus(void);
++void sdio_unregister_bus(void);
++
++#endif
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio.c 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,330 @@
++/*
++ * linux/drivers/mmc/sdio.c
++ *
++ * Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/err.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "core.h"
++#include "bus.h"
++#include "sdio_bus.h"
++#include "mmc_ops.h"
++#include "sd_ops.h"
++#include "sdio_ops.h"
++#include "sdio_cis.h"
++
++static int sdio_read_fbr(struct sdio_func *func)
++{
++ int ret;
++ unsigned char data;
++
++ ret = mmc_io_rw_direct(func->card, 0, 0,
++ func->num * 0x100 + SDIO_FBR_STD_IF, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++
++ data &= 0x0f;
++
++ if (data == 0x0f) {
++ ret = mmc_io_rw_direct(func->card, 0, 0,
++ func->num * 0x100 + SDIO_FBR_STD_IF_EXT, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++ }
++
++ func->class = data;
++
++out:
++ return ret;
++}
++
++static int sdio_init_func(struct mmc_card *card, unsigned int fn)
++{
++ int ret;
++ struct sdio_func *func;
++
++ BUG_ON(fn > SDIO_MAX_FUNCS);
++
++ func = sdio_alloc_func(card);
++ if (IS_ERR(func))
++ return PTR_ERR(func);
++
++ func->num = fn;
++
++ ret = sdio_read_fbr(func);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ goto fail;
++
++ ret = sdio_read_cis(func, fn);
++ if (ret)
++ goto fail;
++
++ card->sdio_func[fn - 1] = func;
++
++ return 0;
++
++fail:
++ /*
++ * It is okay to remove the function here even though we hold
++ * the host lock as we haven't registered the device yet.
++ */
++ sdio_remove_func(func);
++ return ret;
++}
++
++static int sdio_read_cccr(struct mmc_card *card)
++{
++ int ret;
++ int cccr_vsn;
++ unsigned char data;
++
++ memset(&card->cccr, 0, sizeof(struct sdio_cccr));
++
++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++
++ cccr_vsn = data & 0x0f;
++
++ if (cccr_vsn > SDIO_CCCR_REV_1_20) {
++ printk("%s: unrecognised CCCR structure version %d\n",
++ mmc_hostname(card->host), cccr_vsn);
++ return MMC_ERR_INVALID;
++ }
++
++ card->cccr.sdio_vsn = (data & 0xf0) >> 4;
++
++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++
++ if (data & SDIO_CCCR_CAP_SMB)
++ card->cccr.multi_block = 1;
++ if (data & SDIO_CCCR_CAP_LSC)
++ card->cccr.low_speed = 1;
++ if (data & SDIO_CCCR_CAP_4BLS)
++ card->cccr.wide_bus = 1;
++
++ if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++
++ if (data & SDIO_POWER_SMPC)
++ card->cccr.high_power = 1;
++ }
++
++ if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
++ if (ret != MMC_ERR_NONE)
++ goto out;
++
++ if (data & SDIO_SPEED_SHS)
++ card->cccr.high_speed = 1;
++ }
++
++out:
++ return ret;
++}
++
++/*
++ * Host is being removed. Free up the current card.
++ */
++static void mmc_sdio_remove(struct mmc_host *host)
++{
++ int i;
++
++ BUG_ON(!host);
++ BUG_ON(!host->card);
++
++ for (i = 0;i < host->card->sdio_funcs;i++) {
++ if (host->card->sdio_func[i]) {
++ sdio_remove_func(host->card->sdio_func[i]);
++ host->card->sdio_func[i] = NULL;
++ }
++ }
++
++ mmc_remove_card(host->card);
++ host->card = NULL;
++}
++
++/*
++ * Card detection callback from host.
++ */
++static void mmc_sdio_detect(struct mmc_host *host)
++{
++ int err;
++
++ BUG_ON(!host);
++ BUG_ON(!host->card);
++
++ mmc_claim_host(host);
++
++ /*
++ * Just check if our card has been removed.
++ */
++ err = mmc_select_card(host->card);
++
++ mmc_release_host(host);
++
++ if (err != MMC_ERR_NONE) {
++ mmc_sdio_remove(host);
++
++ mmc_claim_host(host);
++ mmc_detach_bus(host);
++ mmc_release_host(host);
++ }
++}
++
++
++static const struct mmc_bus_ops mmc_sdio_ops = {
++ .remove = mmc_sdio_remove,
++ .detect = mmc_sdio_detect,
++};
++
++
++/*
++ * Starting point for SDIO card init.
++ */
++int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
++{
++ int err;
++ int i, funcs;
++ struct mmc_card *card;
++
++ BUG_ON(!host);
++ BUG_ON(!host->claimed);
++
++ mmc_attach_bus(host, &mmc_sdio_ops);
++
++ /*
++ * Sanity check the voltages that the card claims to
++ * support.
++ */
++ if (ocr & 0x7F) {
++ printk(KERN_WARNING "%s: card claims to support voltages "
++ "below the defined range. These will be ignored.\n",
++ mmc_hostname(host));
++ ocr &= ~0x7F;
++ }
++
++ if (ocr & MMC_VDD_165_195) {
++ printk(KERN_WARNING "%s: SDIO card claims to support the "
++ "incompletely defined 'low voltage range'. This "
++ "will be ignored.\n", mmc_hostname(host));
++ ocr &= ~MMC_VDD_165_195;
++ }
++
++ host->ocr = mmc_select_voltage(host, ocr);
++
++ /*
++ * Can we support the voltage(s) of the card(s)?
++ */
++ if (!host->ocr)
++ goto err;
++
++ /*
++ * Inform the card of the voltage
++ */
++ err = mmc_send_io_op_cond(host, host->ocr, &ocr);
++ if (err != MMC_ERR_NONE)
++ goto err;
++
++ /*
++ * The number of functions on the card is encoded inside
++ * the ocr.
++ */
++ funcs = (ocr & 0x70000000) >> 28;
++
++ /*
++ * Allocate card structure.
++ */
++ card = mmc_alloc_card(host);
++ if (IS_ERR(card))
++ goto err;
++
++ card->type = MMC_TYPE_SDIO;
++ card->sdio_funcs = funcs;
++
++ host->card = card;
++
++ /*
++ * Set card RCA.
++ */
++ err = mmc_send_relative_addr(host, &card->rca);
++ if (err != MMC_ERR_NONE)
++ goto remove;
++
++ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
++
++ /*
++ * Select card, as all following commands rely on that.
++ */
++ err = mmc_select_card(card);
++ if (err != MMC_ERR_NONE)
++ goto remove;
++
++ /*
++ * Read the common registers.
++ */
++ err = sdio_read_cccr(card);
++ if (err != MMC_ERR_NONE)
++ goto remove;
++
++ /*
++ * Initialize (but don't add) all present functions.
++ */
++ for (i = 0;i < funcs;i++) {
++ err = sdio_init_func(host->card, i + 1);
++ if (err)
++ goto remove;
++ }
++
++ mmc_release_host(host);
++
++ /*
++ * First add the card to the driver model...
++ */
++ err = mmc_add_card(host->card);
++ if (err)
++ goto remove_added;
++
++ /*
++ * ...then the SDIO functions.
++ */
++ for (i = 0;i < funcs;i++) {
++ err = sdio_add_func(host->card->sdio_func[i]);
++ if (err)
++ goto remove_added;
++ }
++
++ return 0;
++
++
++remove_added:
++ /* Remove without lock if the device has been added. */
++ mmc_sdio_remove(host);
++ mmc_claim_host(host);
++remove:
++ /* And with lock if it hasn't been added. */
++ if (host->card)
++ mmc_sdio_remove(host);
++err:
++ mmc_detach_bus(host);
++ mmc_release_host(host);
++
++ return 0;
++}
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_cis.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_cis.c 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,289 @@
++/*
++ * linux/drivers/mmc/core/sdio_cis.c
++ *
++ * Author: Nicolas Pitre
++ * Created: June 11, 2007
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/kernel.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_cis.h"
++#include "sdio_ops.h"
++
++
++static int cistpl_vers_1(struct sdio_func *func, unsigned fn,
++ const unsigned char *buf, unsigned size)
++{
++ unsigned i, nr_strings = 0;
++
++ printk(KERN_DEBUG "%s: CISTPL_VERS_1 = %u.%u\n",
++ sdio_func_id(func), buf[0], buf[1]);
++ buf += 2;
++
++ for (i = 0; i < size - 2; i++) {
++ if (buf[i] == 0xff)
++ break;
++ if (buf[i] == 0)
++ nr_strings++;
++ }
++ printk(KERN_INFO "%s:", sdio_func_id(func));
++ for (i = 0; i < nr_strings; i++) {
++ printk(" \"%s\"", buf);
++ while (*buf++);
++ }
++ printk("\n");
++
++ return 0;
++}
++
++static int cistpl_manfid(struct sdio_func *func, unsigned fn,
++ const unsigned char *buf, unsigned size)
++{
++ /* TPLMID_MANF */
++ func->vendor = buf[0] | (buf[1] << 8);
++ printk(KERN_DEBUG "%s: TPLMID_MANF = 0x%04x\n",
++ sdio_func_id(func), func->vendor);
++
++ /* TPLMID_CARD */
++ func->device = buf[2] | (buf[3] << 8);
++ printk(KERN_DEBUG "%s: TPLMID_CARD = 0x%04x\n",
++ sdio_func_id(func), func->device);
++
++ return 0;
++}
++
++static int cistpl_funcid(struct sdio_func *func, unsigned fn,
++ const unsigned char *buf, unsigned size)
++{
++ /* TPLFID_FUNCTION */
++ printk(KERN_DEBUG "%s: TPLFID_FUNCTION = 0x%02x\n",
++ sdio_func_id(func), buf[0]);
++
++ return 0;
++}
++
++static const unsigned char speed_val[16] =
++ { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
++static const unsigned int speed_unit[8] =
++ { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
++
++static int cistpl_funce(struct sdio_func *func, unsigned fn,
++ const unsigned char *buf, unsigned size)
++{
++ unsigned val;
++
++ /*
++ * There should be two versions of the CISTPL_FUNCE tuple,
++ * one for the common CIS (function 0) and a version used by
++ * the individual function's CIS (1-7). Yet, the later has a
++ * different length depending on the SDIO spec version.
++ */
++ if (fn == 0) {
++ if (size < 0x04 || buf[0] != 0)
++ goto bad;
++ /* TPLFE_FN0_BLK_SIZE */
++ val = buf[1] | (buf[2] << 8);
++ printk(KERN_DEBUG "%s: TPLFE_FN0_BLK_SIZE = %u\n",
++ sdio_func_id(func), val);
++ /* TPLFE_MAX_TRAN_SPEED */
++ val = speed_val[(buf[3] >> 3) & 15] * speed_unit[buf[3] & 7];
++ printk(KERN_DEBUG "%s: max speed = %u kbps\n",
++ sdio_func_id(func), val/1000);
++ } else {
++ unsigned vsn = func->card->cccr.sdio_vsn;
++ unsigned min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
++ if (size < min_size || buf[0] != 1)
++ goto bad;
++ /* TPLFE_FUNCTION_INFO */
++ val = buf[1];
++ printk(KERN_DEBUG "%s: TPLFE_FUNCTION_INFO = 0x%02x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_STD_IO_REV */
++ val = buf[2];
++ printk(KERN_DEBUG "%s: TPLFE_STD_IO_REV = 0x%02x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_CARD_PSN */
++ val = buf[3] | (buf[4] << 8) | (buf[5] << 16) | (buf[6] << 24);
++ printk(KERN_DEBUG "%s: TPLFE_CARD_PSN = 0x%08x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_CSA_SIZE */
++ val = buf[7] | (buf[8] << 8) | (buf[9] << 16) | (buf[10] << 24);
++ printk(KERN_DEBUG "%s: TPLFE_CSA_SIZE = 0x%08x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_CSA_PROPERTY */
++ val = buf[11];
++ printk(KERN_DEBUG "%s: TPLFE_CSA_PROPERTY = 0x%02x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_MAX_BLK_SIZE */
++ val = buf[12] | (buf[13] << 8);
++ printk(KERN_DEBUG "%s: TPLFE_MAX_BLK_SIZE = %u\n",
++ sdio_func_id(func), val);
++ /* TPLFE_OCR */
++ val = buf[14] | (buf[15] << 8) | (buf[16] << 16) | (buf[17] << 24);
++ printk(KERN_DEBUG "%s: TPLFE_OCR = 0x%08x\n",
++ sdio_func_id(func), val);
++ /* TPLFE_OP_MIN_PWR, TPLFE_OP_AVG_PWR, TPLFE_OP_MAX_PWR */
++ printk(KERN_DEBUG "%s: operating current (min/avg/max) = %u/%u/%u mA\n",
++ sdio_func_id(func), buf[18], buf[19], buf[20]);
++ /* TPLFE_SB_MIN_PWR, TPLFE_SB_AVG_PWR, TPLFE_SB_MAX_PWR */
++ printk(KERN_DEBUG "%s: standby current (min/avg/max) = %u/%u/%u mA\n",
++ sdio_func_id(func), buf[21], buf[22], buf[23]);
++ /* TPLFE_MIN_BW */
++ val = buf[24] | (buf[25] << 8);
++ printk(KERN_DEBUG "%s: minimum data bandwidth = %u KB/sec\n",
++ sdio_func_id(func), val);
++ /* TPLFE_OPT_BW */
++ val = buf[26] | (buf[27] << 8);
++ printk(KERN_DEBUG "%s: optimum data bandwidth = %u KB/sec\n",
++ sdio_func_id(func), val);
++ if (vsn > SDIO_SDIO_REV_1_00) {
++ /* TPLFE_ENABLE_TIMEOUT_VAL */
++ val = buf[28] | (buf[29] << 8);
++ printk(KERN_DEBUG "%s: TPLFE_ENABLE_TIMEOUT_VAL = %u\n",
++ sdio_func_id(func), val);
++ /* TPLFE_SP_AVG_PWR_3.3V */
++ val = buf[30] | (buf[31] << 8);
++ /* TPLFE_SP_MAX_PWR_3.3V */
++ val = buf[32] | (buf[33] << 8);
++ /* TPLFE_HP_AVG_PWR_3.3V */
++ val = buf[34] | (buf[35] << 8);
++ /* TPLFE_HP_MAX_PWR_3.3V */
++ val = buf[36] | (buf[37] << 8);
++ /* TPLFE_LP_AVG_PWR_3.3V */
++ val = buf[38] | (buf[39] << 8);
++ /* TPLFE_LP_MAX_PWR_3.3V */
++ val = buf[40] | (buf[41] << 8);
++ }
++ }
++ return 0;
++
++bad:
++ printk(KERN_WARNING "%s: bad CISTPL_FUNCE size %u type %u for function %u\n",
++ sdio_func_id(func), size, buf[0], fn);
++ return -EINVAL;
++}
++
++typedef int (tpl_parse_t)(struct sdio_func *, unsigned,
++ const unsigned char *, unsigned);
++
++struct cis_tpl {
++ unsigned char code;
++ unsigned char min_size;
++ tpl_parse_t *parse;
++};
++
++static const struct cis_tpl cis_tpl_list[] = {
++ { 0x15, 3, cistpl_vers_1 },
++ { 0x20, 4, cistpl_manfid },
++ { 0x21, 2, cistpl_funcid },
++ { 0x22, 0, cistpl_funce },
++};
++
++int sdio_read_cis(struct sdio_func *func, unsigned int fn)
++{
++ int ret;
++ struct sdio_func_tuple *this, **prev;
++ unsigned i, ptr = 0;
++
++ /*
++ * The common CIS provides information global to all functions,
++ * and the function specific CIS is allowed to override the
++ * common CIS. Therefore, we always read the common CIS first.
++ */
++ if (fn != 0) {
++ ret = sdio_read_cis(func, 0);
++ if (ret)
++ return ret;
++ }
++
++ /*
++ * Note that this works for the common CIS (function number 0) as
++ * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
++ * have the same offset.
++ */
++ for (i = 0; i < 3; i++) {
++ unsigned char x;
++ ret = mmc_io_rw_direct(func->card, 0, 0,
++ fn * 0x100 + SDIO_FBR_CIS + i, 0, &x);
++ if (ret != MMC_ERR_NONE)
++ return -EIO;
++ ptr |= x << (i * 8);
++ }
++
++ /* find the list tail */
++ for (prev = &func->tuples; *prev; prev = &(*prev)->next);
++
++ do {
++ unsigned char tpl_code, tpl_link;
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ break;
++
++ /* 0xff means we're done */
++ if (tpl_code == 0xff)
++ break;
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_link);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ break;
++
++ this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
++ if (!this)
++ return -ENOMEM;
++
++ for (i = 0; i < tpl_link; i++) {
++ ret = mmc_io_rw_direct(func->card, 0, 0,
++ ptr + i, 0, &this->data[i]);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ break;
++ }
++ if (ret) {
++ kfree(this);
++ break;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
++ if (cis_tpl_list[i].code == tpl_code)
++ break;
++ if (i >= ARRAY_SIZE(cis_tpl_list)) {
++ /* this tuple is unknown to the core */
++ this->next = NULL;
++ this->code = tpl_code;
++ this->size = tpl_link;
++ *prev = this;
++ prev = &this->next;
++ printk(KERN_DEBUG
++ "%s: queuing CIS tuple 0x%02x length %u\n",
++ sdio_func_id(func), tpl_code, tpl_link);
++ } else {
++ const struct cis_tpl *tpl = cis_tpl_list + i;
++ if (tpl_link < tpl->min_size) {
++ printk(KERN_WARNING
++ "%s: bad CIS tuple 0x%02x (length = %u, expected >= %u\n",
++ sdio_func_id(func), tpl_code, tpl_link, tpl->min_size);
++ ret = -EINVAL;
++ } else
++ ret = tpl->parse(func, fn, this->data, tpl_link);
++ kfree(this);
++ }
++
++ ptr += tpl_link;
++ } while (!ret);
++
++ return ret;
++}
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_cis.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_cis.h 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,19 @@
++/*
++ * linux/drivers/mmc/core/sdio_cis.h
++ *
++ * Author: Nicolas Pitre
++ * Created: June 11, 2007
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef _MMC_SDIO_CIS_H
++#define _MMC_SDIO_CIS_H
++
++int sdio_read_cis(struct sdio_func *func, unsigned int fn);
++
++#endif
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_io.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_io.c 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,398 @@
++/*
++ * linux/drivers/mmc/core/sdio_io.c
++ *
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_ops.h"
++
++/**
++ * sdio_claim_host - exclusively claim a bus for a certain SDIO function
++ * @func: SDIO function that will be accessed
++ *
++ * Claim a bus for a set of operations. The SDIO function given
++ * is used to figure out which bus is relevant.
++ */
++void sdio_claim_host(struct sdio_func *func)
++{
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ mmc_claim_host(func->card->host);
++}
++
++EXPORT_SYMBOL_GPL(sdio_claim_host);
++
++/**
++ * sdio_release_host - release a bus for a certain SDIO function
++ * @func: SDIO function that was accessed
++ *
++ * Release a bus, allowing others to claim the bus for their
++ * operations.
++ */
++void sdio_release_host(struct sdio_func *func)
++{
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ mmc_release_host(func->card->host);
++}
++
++EXPORT_SYMBOL_GPL(sdio_release_host);
++
++/**
++ * sdio_enable_func - enables a SDIO function for usage
++ * @func: SDIO function to enable
++ *
++ * Powers up and activates a SDIO function so that register
++ * access is possible.
++ */
++int sdio_enable_func(struct sdio_func *func)
++{
++ int ret;
++ unsigned char reg;
++ unsigned long timeout;
++
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ goto err;
++
++ reg |= 1 << func->num;
++
++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ goto err;
++
++ /*
++ * FIXME: This should timeout based on information in the CIS,
++ * but we don't have card to parse that yet.
++ */
++ timeout = jiffies + HZ;
++
++ while (1) {
++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ goto err;
++ if (reg & (1 << func->num))
++ break;
++ ret = -ETIME;
++ if (time_after(jiffies, timeout))
++ goto err;
++ }
++
++ pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
++
++ return 0;
++
++err:
++ pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_enable_func);
++
++/**
++ * sdio_disable_func - disable a SDIO function
++ * @func: SDIO function to disable
++ *
++ * Powers down and deactivates a SDIO function. Register access
++ * to this function will fail until the function is reenabled.
++ */
++int sdio_disable_func(struct sdio_func *func)
++{
++ int ret;
++ unsigned char reg;
++
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
++ if (ret != MMC_ERR_NONE)
++ goto err;
++
++ reg &= ~(1 << func->num);
++
++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
++ if (ret != MMC_ERR_NONE)
++ goto err;
++
++ pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
++
++ return 0;
++
++err:
++ pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
++ return -EIO;
++}
++
++EXPORT_SYMBOL_GPL(sdio_disable_func);
++
++/**
++ * sdio_readb - read a single byte from a SDIO function
++ * @func: SDIO function to access
++ * @addr: address to read
++ * @err_ret: optional status value from transfer
++ *
++ * Reads a single byte from the address space of a given SDIO
++ * function. If there is a problem reading the address, 0xff
++ * is returned and @err_ret will contain the error code.
++ */
++unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++ unsigned char val;
++
++ BUG_ON(!func);
++
++ if (err_ret)
++ *err_ret = MMC_ERR_NONE;
++
++ ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
++ if (ret != MMC_ERR_NONE) {
++ if (err_ret)
++ *err_ret = ret;
++ return 0xFF;
++ }
++
++ return val;
++}
++
++EXPORT_SYMBOL_GPL(sdio_readb);
++
++/**
++ * sdio_writeb - write a single byte to a SDIO function
++ * @func: SDIO function to access
++ * @b: byte to write
++ * @addr: address to write to
++ * @err_ret: optional status value from transfer
++ *
++ * Writes a single byte to the address space of a given SDIO
++ * function. @err_ret will contain the status of the actual
++ * transfer.
++ */
++void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++
++ BUG_ON(!func);
++
++ ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
++ if (err_ret)
++ *err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writeb);
++
++/**
++ * sdio_memcpy_fromio - read a chunk of memory from a SDIO function
++ * @func: SDIO function to access
++ * @dst: buffer to store the data
++ * @addr: address to begin reading from
++ * @count: number of bytes to read
++ *
++ * Reads up to 512 bytes from the address space of a given SDIO
++ * function. Return value indicates if the transfer succeeded or
++ * not.
++ */
++int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
++ unsigned int addr, int count)
++{
++ return mmc_io_rw_extended(func->card, 0, func->num, addr, 0, dst,
++ count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
++
++/**
++ * sdio_memcpy_toio - write a chunk of memory to a SDIO function
++ * @func: SDIO function to access
++ * @addr: address to start writing to
++ * @src: buffer that contains the data to write
++ * @count: number of bytes to write
++ *
++ * Writes up to 512 bytes to the address space of a given SDIO
++ * function. Return value indicates if the transfer succeeded or
++ * not.
++ */
++int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
++ void *src, int count)
++{
++ return mmc_io_rw_extended(func->card, 1, func->num, addr, 0, src,
++ count);
++}
++
++/**
++ * sdio_readsb - read from a FIFO on a SDIO function
++ * @func: SDIO function to access
++ * @dst: buffer to store the data
++ * @addr: address of (single byte) FIFO
++ * @count: number of bytes to read
++ *
++ * Reads up to 512 bytes from the specified FIFO of a given SDIO
++ * function. Return value indicates if the transfer succeeded or
++ * not.
++ */
++int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
++ int count)
++{
++ return mmc_io_rw_extended(func->card, 0, func->num, addr, 1, dst,
++ count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readsb);
++
++/**
++ * sdio_writesb - write to a FIFO of a SDIO function
++ * @func: SDIO function to access
++ * @addr: address of (single byte) FIFO
++ * @src: buffer that contains the data to write
++ * @count: number of bytes to write
++ *
++ * Writes up to 512 bytes to the specified FIFO of a given SDIO
++ * function. Return value indicates if the transfer succeeded or
++ * not.
++ */
++int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
++ int count)
++{
++ return mmc_io_rw_extended(func->card, 1, func->num, addr, 1, src,
++ count);
++}
++
++EXPORT_SYMBOL_GPL(sdio_writesb);
++
++/**
++ * sdio_readw - read a 16 bit integer from a SDIO function
++ * @func: SDIO function to access
++ * @addr: address to read
++ * @err_ret: optional status value from transfer
++ *
++ * Reads a 16 bit integer from the address space of a given SDIO
++ * function. If there is a problem reading the address, 0xffff
++ * is returned and @err_ret will contain the error code.
++ */
++unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++ unsigned short value;
++
++ if (err_ret)
++ *err_ret = MMC_ERR_NONE;
++
++ ret = sdio_memcpy_fromio(func, &value, addr, 2);
++ if (ret != MMC_ERR_NONE) {
++ if (err_ret)
++ *err_ret = ret;
++ return 0xFFFF;
++ }
++
++ return le16_to_cpu(value);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readw);
++
++/**
++ * sdio_writew - write a 16 bit integer to a SDIO function
++ * @func: SDIO function to access
++ * @b: integer to write
++ * @addr: address to write to
++ * @err_ret: optional status value from transfer
++ *
++ * Writes a 16 bit integer to the address space of a given SDIO
++ * function. @err_ret will contain the status of the actual
++ * transfer.
++ */
++void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++
++ b = cpu_to_le16(b);
++
++ ret = sdio_memcpy_toio(func, addr, &b, 2);
++ if (err_ret)
++ *err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writew);
++
++/**
++ * sdio_readl - read a 32 bit integer from a SDIO function
++ * @func: SDIO function to access
++ * @addr: address to read
++ * @err_ret: optional status value from transfer
++ *
++ * Reads a 32 bit integer from the address space of a given SDIO
++ * function. If there is a problem reading the address,
++ * 0xffffffff is returned and @err_ret will contain the error
++ * code.
++ */
++unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++ unsigned long value;
++
++ if (err_ret)
++ *err_ret = MMC_ERR_NONE;
++
++ ret = sdio_memcpy_fromio(func, &value, addr, 4);
++ if (ret != MMC_ERR_NONE) {
++ if (err_ret)
++ *err_ret = ret;
++ return 0xFFFFFFFF;
++ }
++
++ return le32_to_cpu(value);
++}
++
++EXPORT_SYMBOL_GPL(sdio_readl);
++
++/**
++ * sdio_writel - write a 32 bit integer to a SDIO function
++ * @func: SDIO function to access
++ * @b: integer to write
++ * @addr: address to write to
++ * @err_ret: optional status value from transfer
++ *
++ * Writes a 32 bit integer to the address space of a given SDIO
++ * function. @err_ret will contain the status of the actual
++ * transfer.
++ */
++void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
++ int *err_ret)
++{
++ int ret;
++
++ b = cpu_to_le32(b);
++
++ ret = sdio_memcpy_toio(func, addr, &b, 4);
++ if (err_ret)
++ *err_ret = ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_writel);
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_irq.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_irq.c 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,231 @@
++/*
++ * linux/drivers/mmc/core/sdio_irq.c
++ *
++ * Author: Nicolas Pitre
++ * Created: June 18, 2007
++ * Copyright: MontaVista Software Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/kthread.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++
++#include <linux/mmc/core.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio.h>
++#include <linux/mmc/sdio_func.h>
++
++#include "sdio_ops.h"
++
++static int process_sdio_pending_irqs(struct mmc_card *card)
++{
++ int i, ret;
++ unsigned char pending;
++
++ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
++ if (ret != MMC_ERR_NONE) {
++ printk(KERN_WARNING "%s: error %d reading SDIO_CCCR_INTx\n",
++ mmc_card_id(card), ret);
++ return -EIO;
++ }
++
++ for (i = 1; i <= 7; i++) {
++ if (pending & (1 << i)) {
++ struct sdio_func *func = card->sdio_func[i - 1];
++ if (func->irq_handler) {
++ func->irq_handler(func);
++ } else
++ printk(KERN_WARNING "%s: pending IRQ with no handler\n",
++ sdio_func_id(func));
++ }
++ }
++
++ return 0;
++}
++
++static int sdio_irq_thread(void *_host)
++{
++ struct mmc_host *host = _host;
++ struct sched_param param = { .sched_priority = 1 };
++ unsigned long period;
++ int ret;
++
++ sched_setscheduler(current, SCHED_FIFO, ¶m);
++
++ /*
++ * We want to allow for SDIO cards to work even on non SDIO
++ * aware hosts. One thing that non SDIO host cannot do is
++ * asynchronous notification of pending SDIO card interrupts
++ * hence we poll for them in that case.
++ */
++ period = msecs_to_jiffies(10);
++
++ pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
++ mmc_hostname(host), period);
++
++ do {
++ /*
++ * We claim the host here on drivers behalf for a couple
++ * reasons:
++ *
++ * 1) it is already needed to retrieve the CCCR_INTx;
++ * 2) we want the driver(s) to clear the IRQ condition ASAP;
++ * 3) we need to control the abort condition locally.
++ *
++ * Just like traditional hard IRQ handlers, we expect SDIO
++ * IRQ handlers to be quick and to the point, so that the
++ * holding of the host lock does not cover too much work
++ * that doesn't require that lock to be held.
++ */
++ ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
++ if (ret)
++ break;
++ ret = process_sdio_pending_irqs(host->card);
++ mmc_release_host(host);
++
++ /*
++ * Give other threads a chance to run in the presence of
++ * errors. FIXME: determine if due to card removal and
++ * possibly exit this thread if so.
++ */
++ if (ret)
++ ssleep(1);
++
++ set_task_state(current, TASK_INTERRUPTIBLE);
++ if (!kthread_should_stop())
++ schedule_timeout(period);
++ set_task_state(current, TASK_RUNNING);
++ } while (!kthread_should_stop());
++
++ pr_debug("%s: IRQ thread exiting with code %d\n",
++ mmc_hostname(host), ret);
++
++ return ret;
++}
++
++static int sdio_card_irq_get(struct mmc_card *card)
++{
++ struct mmc_host *host = card->host;
++
++ BUG_ON(!host->claimed);
++
++ if (!host->sdio_irqs++) {
++ atomic_set(&host->sdio_irq_thread_abort, 0);
++ host->sdio_irq_thread =
++ kthread_run(sdio_irq_thread, host, "sdio_irqd");
++ if (IS_ERR(host->sdio_irq_thread)) {
++ int err = PTR_ERR(host->sdio_irq_thread);
++ host->sdio_irqs--;
++ return err;
++ }
++ }
++
++ return 0;
++}
++
++static int sdio_card_irq_put(struct mmc_card *card)
++{
++ struct mmc_host *host = card->host;
++
++ BUG_ON(!host->claimed);
++ BUG_ON(host->sdio_irqs < 1);
++
++ if (!--host->sdio_irqs) {
++ atomic_set(&host->sdio_irq_thread_abort, 1);
++ kthread_stop(host->sdio_irq_thread);
++ }
++
++ return 0;
++}
++
++/**
++ * sdio_claim_irq - claim the IRQ for a SDIO function
++ * @func: SDIO function
++ * @handler: IRQ handler callback
++ *
++ * Claim and activate the IRQ for the given SDIO function. The provided
++ * handler will be called when that IRQ is asserted. The host is always
++ * claimed already when the handler is called so the handler must not
++ * call sdio_claim_host() nor sdio_release_host().
++ */
++int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
++{
++ int ret;
++ unsigned char reg;
++
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
++
++ if (func->irq_handler) {
++ pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
++ return -EBUSY;
++ }
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ return ret;
++
++ reg |= 1 << func->num;
++
++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
++ ret = (ret != MMC_ERR_NONE) ? -EIO : 0;
++ if (ret)
++ return ret;
++
++ func->irq_handler = handler;
++ ret = sdio_card_irq_get(func->card);
++ if (ret)
++ func->irq_handler = NULL;
++
++ return ret;
++}
++
++EXPORT_SYMBOL_GPL(sdio_claim_irq);
++
++/**
++ * sdio_release_irq - release the IRQ for a SDIO function
++ * @func: SDIO function
++ *
++ * Disable and release the IRQ for the given SDIO function.
++ */
++int sdio_release_irq(struct sdio_func *func)
++{
++ int ret;
++ unsigned char reg;
++
++ BUG_ON(!func);
++ BUG_ON(!func->card);
++
++ pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
++
++ if (func->irq_handler) {
++ func->irq_handler = NULL;
++ sdio_card_irq_put(func->card);
++ }
++
++ ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
++ if (ret != MMC_ERR_NONE)
++ return -EIO;
++
++ reg &= ~(1 << func->num);
++
++ ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
++ if (ret != MMC_ERR_NONE)
++ return -EIO;
++
++ return 0;
++}
++
++EXPORT_SYMBOL_GPL(sdio_release_irq);
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_ops.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_ops.c 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,151 @@
++/*
++ * linux/drivers/mmc/sdio_ops.c
++ *
++ * Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#include <asm/scatterlist.h>
++#include <linux/scatterlist.h>
++
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/sdio.h>
++
++#include "core.h"
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
++{
++ struct mmc_command cmd;
++ int i, err = 0;
++
++ BUG_ON(!host);
++
++ memset(&cmd, 0, sizeof(struct mmc_command));
++
++ cmd.opcode = SD_IO_SEND_OP_COND;
++ cmd.arg = ocr;
++ cmd.flags = MMC_RSP_R4 | MMC_CMD_BCR;
++
++ for (i = 100; i; i--) {
++ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
++ if (err != MMC_ERR_NONE)
++ break;
++
++ if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
++ break;
++
++ err = MMC_ERR_TIMEOUT;
++
++ mmc_delay(10);
++ }
++
++ if (rocr)
++ *rocr = cmd.resp[0];
++
++ return err;
++}
++
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, u8 in, u8* out)
++{
++ struct mmc_command cmd;
++ int err;
++
++ BUG_ON(!card);
++ BUG_ON(fn > 7);
++
++ memset(&cmd, 0, sizeof(struct mmc_command));
++
++ cmd.opcode = SD_IO_RW_DIRECT;
++ cmd.arg = write ? 0x80000000 : 0x00000000;
++ cmd.arg |= fn << 28;
++ cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
++ cmd.arg |= addr << 9;
++ cmd.arg |= in;
++ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
++
++ err = mmc_wait_for_cmd(card->host, &cmd, 0);
++ if (err != MMC_ERR_NONE)
++ return err;
++
++ if (cmd.resp[0] & R5_COM_CRC_ERROR)
++ return MMC_ERR_BADCRC;
++ if (cmd.resp[0] & R5_ILLEGAL_COMMAND)
++ return MMC_ERR_INVALID;
++ if (cmd.resp[0] & R5_ERROR)
++ return MMC_ERR_FAILED;
++ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++ return MMC_ERR_INVALID;
++ if (cmd.resp[0] & R5_OUT_OF_RANGE)
++ return MMC_ERR_INVALID;
++
++ if (out)
++ *out = cmd.resp[0] & 0xFF;
++
++ return MMC_ERR_NONE;
++}
++
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, int bang, u8 *buf, unsigned size)
++{
++ struct mmc_request mrq;
++ struct mmc_command cmd;
++ struct mmc_data data;
++ struct scatterlist sg;
++
++ BUG_ON(!card);
++ BUG_ON(fn > 7);
++ BUG_ON(size > 512);
++
++ memset(&mrq, 0, sizeof(struct mmc_request));
++ memset(&cmd, 0, sizeof(struct mmc_command));
++ memset(&data, 0, sizeof(struct mmc_data));
++
++ mrq.cmd = &cmd;
++ mrq.data = &data;
++
++ cmd.opcode = SD_IO_RW_EXTENDED;
++ cmd.arg = write ? 0x80000000 : 0x00000000;
++ cmd.arg |= fn << 28;
++ cmd.arg |= bang ? 0x00000000 : 0x04000000;
++ cmd.arg |= addr << 9;
++ cmd.arg |= (size == 512) ? 0 : size;
++ cmd.flags = MMC_RSP_R5 | MMC_CMD_ADTC;
++
++ data.blksz = size;
++ data.blocks = 1;
++ data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
++ data.sg = &sg;
++ data.sg_len = 1;
++
++ sg_init_one(&sg, buf, size);
++
++ mmc_set_data_timeout(&data, card, 0);
++
++ mmc_wait_for_req(card->host, &mrq);
++
++ if (cmd.error != MMC_ERR_NONE)
++ return cmd.error;
++ if (data.error != MMC_ERR_NONE)
++ return data.error;
++
++ if (cmd.resp[0] & R5_COM_CRC_ERROR)
++ return MMC_ERR_BADCRC;
++ if (cmd.resp[0] & R5_ILLEGAL_COMMAND)
++ return MMC_ERR_INVALID;
++ if (cmd.resp[0] & R5_ERROR)
++ return MMC_ERR_FAILED;
++ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
++ return MMC_ERR_INVALID;
++ if (cmd.resp[0] & R5_OUT_OF_RANGE)
++ return MMC_ERR_INVALID;
++
++ return MMC_ERR_NONE;
++}
++
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_ops.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/core/sdio_ops.h 2007-07-19 00:19:51.403978656 +0200
+@@ -0,0 +1,22 @@
++/*
++ * linux/drivers/mmc/sdio_ops.c
++ *
++ * Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef _MMC_SDIO_OPS_H
++#define _MMC_SDIO_OPS_H
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, u8 in, u8* out);
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, int bang, u8 *data, unsigned size);
++
++#endif
++
+Index: linux-2.6.22.1/drivers/mmc/core/sysfs.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sysfs.c 2007-07-19 00:18:26.899162998 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sysfs.c 2007-07-19 00:19:51.459981844 +0200
+@@ -2,6 +2,7 @@
+ * linux/drivers/mmc/core/sysfs.c
+ *
+ * Copyright (C) 2003 Russell King, All Rights Reserved.
++ * Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+@@ -9,352 +10,34 @@
+ *
+ * MMC sysfs/driver model support.
+ */
+-#include <linux/module.h>
+-#include <linux/init.h>
+ #include <linux/device.h>
+-#include <linux/idr.h>
+-#include <linux/workqueue.h>
+
+ #include <linux/mmc/card.h>
+-#include <linux/mmc/host.h>
+
+ #include "sysfs.h"
+
+-#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
+-#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
+-#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
+-
+-#define MMC_ATTR(name, fmt, args...) \
+-static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
+-{ \
+- struct mmc_card *card = dev_to_mmc_card(dev); \
+- return sprintf(buf, fmt, args); \
+-}
+-
+-MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
+- card->raw_cid[2], card->raw_cid[3]);
+-MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
+- card->raw_csd[2], card->raw_csd[3]);
+-MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]);
+-MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year);
+-MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev);
+-MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev);
+-MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid);
+-MMC_ATTR(name, "%s\n", card->cid.prod_name);
+-MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid);
+-MMC_ATTR(serial, "0x%08x\n", card->cid.serial);
+-
+-#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+-
+-static struct device_attribute mmc_dev_attrs[] = {
+- MMC_ATTR_RO(cid),
+- MMC_ATTR_RO(csd),
+- MMC_ATTR_RO(date),
+- MMC_ATTR_RO(fwrev),
+- MMC_ATTR_RO(hwrev),
+- MMC_ATTR_RO(manfid),
+- MMC_ATTR_RO(name),
+- MMC_ATTR_RO(oemid),
+- MMC_ATTR_RO(serial),
+- __ATTR_NULL
+-};
+-
+-static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr);
+-
+-
+-static void mmc_release_card(struct device *dev)
+-{
+- struct mmc_card *card = dev_to_mmc_card(dev);
+-
+- kfree(card);
+-}
+-
+-/*
+- * This currently matches any MMC driver to any MMC card - drivers
+- * themselves make the decision whether to drive this card in their
+- * probe method.
+- */
+-static int mmc_bus_match(struct device *dev, struct device_driver *drv)
+-{
+- return 1;
+-}
+-
+-static int
+-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
+- int buf_size)
+-{
+- struct mmc_card *card = dev_to_mmc_card(dev);
+- char ccc[13];
+- int retval = 0, i = 0, length = 0;
+-
+-#define add_env(fmt,val) do { \
+- retval = add_uevent_var(envp, num_envp, &i, \
+- buf, buf_size, &length, \
+- fmt, val); \
+- if (retval) \
+- return retval; \
+-} while (0);
+-
+- for (i = 0; i < 12; i++)
+- ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0';
+- ccc[12] = '\0';
+-
+- add_env("MMC_CCC=%s", ccc);
+- add_env("MMC_MANFID=%06x", card->cid.manfid);
+- add_env("MMC_NAME=%s", mmc_card_name(card));
+- add_env("MMC_OEMID=%04x", card->cid.oemid);
+-#undef add_env
+- envp[i] = NULL;
+-
+- return 0;
+-}
+-
+-static int mmc_bus_suspend(struct device *dev, pm_message_t state)
++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs)
+ {
+- struct mmc_driver *drv = to_mmc_driver(dev->driver);
+- struct mmc_card *card = dev_to_mmc_card(dev);
+- int ret = 0;
+-
+- if (dev->driver && drv->suspend)
+- ret = drv->suspend(card, state);
+- return ret;
+-}
++ int error = 0;
++ int i;
+
+-static int mmc_bus_resume(struct device *dev)
+-{
+- struct mmc_driver *drv = to_mmc_driver(dev->driver);
+- struct mmc_card *card = dev_to_mmc_card(dev);
+- int ret = 0;
+-
+- if (dev->driver && drv->resume)
+- ret = drv->resume(card);
+- return ret;
+-}
+-
+-static int mmc_bus_probe(struct device *dev)
+-{
+- struct mmc_driver *drv = to_mmc_driver(dev->driver);
+- struct mmc_card *card = dev_to_mmc_card(dev);
+-
+- return drv->probe(card);
+-}
+-
+-static int mmc_bus_remove(struct device *dev)
+-{
+- struct mmc_driver *drv = to_mmc_driver(dev->driver);
+- struct mmc_card *card = dev_to_mmc_card(dev);
+-
+- drv->remove(card);
+-
+- return 0;
+-}
+-
+-static struct bus_type mmc_bus_type = {
+- .name = "mmc",
+- .dev_attrs = mmc_dev_attrs,
+- .match = mmc_bus_match,
+- .uevent = mmc_bus_uevent,
+- .probe = mmc_bus_probe,
+- .remove = mmc_bus_remove,
+- .suspend = mmc_bus_suspend,
+- .resume = mmc_bus_resume,
+-};
+-
+-/**
+- * mmc_register_driver - register a media driver
+- * @drv: MMC media driver
+- */
+-int mmc_register_driver(struct mmc_driver *drv)
+-{
+- drv->drv.bus = &mmc_bus_type;
+- return driver_register(&drv->drv);
+-}
+-
+-EXPORT_SYMBOL(mmc_register_driver);
+-
+-/**
+- * mmc_unregister_driver - unregister a media driver
+- * @drv: MMC media driver
+- */
+-void mmc_unregister_driver(struct mmc_driver *drv)
+-{
+- drv->drv.bus = &mmc_bus_type;
+- driver_unregister(&drv->drv);
+-}
+-
+-EXPORT_SYMBOL(mmc_unregister_driver);
+-
+-
+-/*
+- * Internal function. Initialise a MMC card structure.
+- */
+-void mmc_init_card(struct mmc_card *card, struct mmc_host *host)
+-{
+- memset(card, 0, sizeof(struct mmc_card));
+- card->host = host;
+- device_initialize(&card->dev);
+- card->dev.parent = mmc_classdev(host);
+- card->dev.bus = &mmc_bus_type;
+- card->dev.release = mmc_release_card;
+-}
+-
+-/*
+- * Internal function. Register a new MMC card with the driver model.
+- */
+-int mmc_register_card(struct mmc_card *card)
+-{
+- int ret;
+-
+- snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
+- "%s:%04x", mmc_hostname(card->host), card->rca);
+-
+- ret = device_add(&card->dev);
+- if (ret == 0) {
+- if (mmc_card_sd(card)) {
+- ret = device_create_file(&card->dev, &mmc_dev_attr_scr);
+- if (ret)
+- device_del(&card->dev);
++ for (i = 0; attr_name(attrs[i]); i++) {
++ error = device_create_file(&card->dev, &attrs[i]);
++ if (error) {
++ while (--i >= 0)
++ device_remove_file(&card->dev, &attrs[i]);
++ break;
+ }
+ }
+- if (ret == 0)
+- mmc_card_set_present(card);
+- return ret;
+-}
+-
+-/*
+- * Internal function. Unregister a new MMC card with the
+- * driver model, and (eventually) free it.
+- */
+-void mmc_remove_card(struct mmc_card *card)
+-{
+- if (mmc_card_present(card)) {
+- if (mmc_card_sd(card))
+- device_remove_file(&card->dev, &mmc_dev_attr_scr);
+-
+- device_del(&card->dev);
+- }
+-
+- put_device(&card->dev);
+-}
+-
+-
+-static void mmc_host_classdev_release(struct device *dev)
+-{
+- struct mmc_host *host = cls_dev_to_mmc_host(dev);
+- kfree(host);
+-}
+-
+-static struct class mmc_host_class = {
+- .name = "mmc_host",
+- .dev_release = mmc_host_classdev_release,
+-};
+-
+-static DEFINE_IDR(mmc_host_idr);
+-static DEFINE_SPINLOCK(mmc_host_lock);
+-
+-/*
+- * Internal function. Allocate a new MMC host.
+- */
+-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev)
+-{
+- struct mmc_host *host;
+-
+- host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
+- if (host) {
+- memset(host, 0, sizeof(struct mmc_host) + extra);
+-
+- host->parent = dev;
+- host->class_dev.parent = dev;
+- host->class_dev.class = &mmc_host_class;
+- device_initialize(&host->class_dev);
+- }
+-
+- return host;
+-}
+-
+-/*
+- * Internal function. Register a new MMC host with the MMC class.
+- */
+-int mmc_add_host_sysfs(struct mmc_host *host)
+-{
+- int err;
+-
+- if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
+- return -ENOMEM;
+-
+- spin_lock(&mmc_host_lock);
+- err = idr_get_new(&mmc_host_idr, host, &host->index);
+- spin_unlock(&mmc_host_lock);
+- if (err)
+- return err;
+-
+- snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
+- "mmc%d", host->index);
+-
+- return device_add(&host->class_dev);
+-}
+-
+-/*
+- * Internal function. Unregister a MMC host with the MMC class.
+- */
+-void mmc_remove_host_sysfs(struct mmc_host *host)
+-{
+- device_del(&host->class_dev);
+
+- spin_lock(&mmc_host_lock);
+- idr_remove(&mmc_host_idr, host->index);
+- spin_unlock(&mmc_host_lock);
++ return error;
+ }
+
+-/*
+- * Internal function. Free a MMC host.
+- */
+-void mmc_free_host_sysfs(struct mmc_host *host)
++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs)
+ {
+- put_device(&host->class_dev);
+-}
++ int i;
+
+-static struct workqueue_struct *workqueue;
+-
+-/*
+- * Internal function. Schedule delayed work in the MMC work queue.
+- */
+-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
+-{
+- return queue_delayed_work(workqueue, work, delay);
+-}
+-
+-/*
+- * Internal function. Flush all scheduled work from the MMC work queue.
+- */
+-void mmc_flush_scheduled_work(void)
+-{
+- flush_workqueue(workqueue);
+-}
+-
+-static int __init mmc_init(void)
+-{
+- int ret;
+-
+- workqueue = create_singlethread_workqueue("kmmcd");
+- if (!workqueue)
+- return -ENOMEM;
+-
+- ret = bus_register(&mmc_bus_type);
+- if (ret == 0) {
+- ret = class_register(&mmc_host_class);
+- if (ret)
+- bus_unregister(&mmc_bus_type);
+- }
+- return ret;
+-}
+-
+-static void __exit mmc_exit(void)
+-{
+- class_unregister(&mmc_host_class);
+- bus_unregister(&mmc_bus_type);
+- destroy_workqueue(workqueue);
++ for (i = 0; attr_name(attrs[i]); i++)
++ device_remove_file(&card->dev, &attrs[i]);
+ }
+
+-module_init(mmc_init);
+-module_exit(mmc_exit);
+Index: linux-2.6.22.1/drivers/mmc/core/sysfs.h
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sysfs.h 2007-07-19 00:18:26.919164137 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sysfs.h 2007-07-19 00:19:51.491983671 +0200
+@@ -11,17 +11,16 @@
+ #ifndef _MMC_CORE_SYSFS_H
+ #define _MMC_CORE_SYSFS_H
+
+-void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
+-int mmc_register_card(struct mmc_card *card);
+-void mmc_remove_card(struct mmc_card *card);
++#define MMC_ATTR_FN(name, fmt, args...) \
++static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
++{ \
++ struct mmc_card *card = container_of(dev, struct mmc_card, dev);\
++ return sprintf(buf, fmt, args); \
++}
+
+-struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev);
+-int mmc_add_host_sysfs(struct mmc_host *host);
+-void mmc_remove_host_sysfs(struct mmc_host *host);
+-void mmc_free_host_sysfs(struct mmc_host *host);
++#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+
+-int mmc_schedule_work(struct work_struct *work);
+-int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay);
+-void mmc_flush_scheduled_work(void);
++int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs);
++void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs);
+
+ #endif
+Index: linux-2.6.22.1/drivers/mmc/host/at91_mci.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/at91_mci.c 2007-07-19 00:20:07.700907362 +0200
++++ linux-2.6.22.1/drivers/mmc/host/at91_mci.c 2007-07-19 00:20:27.118013880 +0200
+@@ -78,8 +78,6 @@
+
+ #define DRIVER_NAME "at91_mci"
+
+-#undef SUPPORT_4WIRE
+-
+ #define FL_SENT_COMMAND (1 << 0)
+ #define FL_SENT_STOP (1 << 1)
+
+@@ -131,7 +129,7 @@
+ /*
+ * Copy from sg to a dma block - used for transfers
+ */
+-static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
++static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data)
+ {
+ unsigned int len, i, size;
+ unsigned *dmabuf = host->buffer;
+@@ -180,7 +178,7 @@
+ /*
+ * Prepare a dma read
+ */
+-static void at91mci_pre_dma_read(struct at91mci_host *host)
++static void at91_mci_pre_dma_read(struct at91mci_host *host)
+ {
+ int i;
+ struct scatterlist *sg;
+@@ -248,7 +246,7 @@
+ /*
+ * Handle after a dma read
+ */
+-static void at91mci_post_dma_read(struct at91mci_host *host)
++static void at91_mci_post_dma_read(struct at91mci_host *host)
+ {
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+@@ -268,8 +266,6 @@
+ }
+
+ while (host->in_use_index < host->transfer_index) {
+- unsigned int *buffer;
+-
+ struct scatterlist *sg;
+
+ pr_debug("finishing index %d\n", host->in_use_index);
+@@ -280,29 +276,31 @@
+
+ dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE);
+
+- /* Swap the contents of the buffer */
+- buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
+- pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
+-
+ data->bytes_xfered += sg->length;
+
+ if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */
++ unsigned int *buffer;
+ int index;
+
++ /* Swap the contents of the buffer */
++ buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset;
++ pr_debug("buffer = %p, length = %d\n", buffer, sg->length);
++
+ for (index = 0; index < (sg->length / 4); index++)
+ buffer[index] = swab32(buffer[index]);
++
++ kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ }
+
+- kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
+ flush_dcache_page(sg->page);
+ }
+
+ /* Is there another transfer to trigger? */
+ if (host->transfer_index < data->sg_len)
+- at91mci_pre_dma_read(host);
++ at91_mci_pre_dma_read(host);
+ else {
++ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX);
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF);
+- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+ }
+
+ pr_debug("post dma read done\n");
+@@ -323,7 +321,6 @@
+
+ /* Now wait for cmd ready */
+ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE);
+- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
+
+ cmd = host->cmd;
+ if (!cmd) return;
+@@ -331,18 +328,53 @@
+ data = cmd->data;
+ if (!data) return;
+
++ if (cmd->data->flags & MMC_DATA_MULTI) {
++ pr_debug("multiple write : wait for BLKE...\n");
++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
++ } else
++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
++
+ data->bytes_xfered = host->total_length;
+ }
+
++/*Handle after command sent ready*/
++static int at91_mci_handle_cmdrdy(struct at91mci_host *host)
++{
++ if (!host->cmd)
++ return 1;
++ else if (!host->cmd->data) {
++ if (host->flags & FL_SENT_STOP) {
++ /*After multi block write, we must wait for NOTBUSY*/
++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY);
++ } else return 1;
++ } else if (host->cmd->data->flags & MMC_DATA_WRITE) {
++ /*After sendding multi-block-write command, start DMA transfer*/
++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE);
++ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
++ }
++
++ /* command not completed, have to wait */
++ return 0;
++}
++
++
+ /*
+ * Enable the controller
+ */
+ static void at91_mci_enable(struct at91mci_host *host)
+ {
++ unsigned int mr;
++
+ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN);
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+ at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC);
+- at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a);
++ mr = AT91_MCI_PDCMODE | 0x34a;
++
++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++ mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF;
++
++ at91_mci_write(host, AT91_MCI_MR, mr);
+
+ /* use Slot A or B (only one at same time) */
+ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b);
+@@ -358,9 +390,8 @@
+
+ /*
+ * Send a command
+- * return the interrupts to enable
+ */
+-static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
++static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd)
+ {
+ unsigned int cmdr, mr;
+ unsigned int block_length;
+@@ -371,8 +402,7 @@
+
+ host->cmd = cmd;
+
+- /* Not sure if this is needed */
+-#if 0
++ /* Needed for leaving busy state before CMD1 */
+ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) {
+ pr_debug("Clearing timeout\n");
+ at91_mci_write(host, AT91_MCI_ARGR, 0);
+@@ -382,7 +412,7 @@
+ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR));
+ }
+ }
+-#endif
++
+ cmdr = cmd->opcode;
+
+ if (mmc_resp_type(cmd) == MMC_RSP_NONE)
+@@ -439,50 +469,48 @@
+ at91_mci_write(host, ATMEL_PDC_TCR, 0);
+ at91_mci_write(host, ATMEL_PDC_TNPR, 0);
+ at91_mci_write(host, ATMEL_PDC_TNCR, 0);
++ ier = AT91_MCI_CMDRDY;
++ } else {
++ /* zero block length and PDC mode */
++ mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff;
++ at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
++
++ /*
++ * Disable the PDC controller
++ */
++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+
+- at91_mci_write(host, AT91_MCI_ARGR, cmd->arg);
+- at91_mci_write(host, AT91_MCI_CMDR, cmdr);
+- return AT91_MCI_CMDRDY;
+- }
+-
+- mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */
+- at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE);
+-
+- /*
+- * Disable the PDC controller
+- */
+- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
+-
+- if (cmdr & AT91_MCI_TRCMD_START) {
+- data->bytes_xfered = 0;
+- host->transfer_index = 0;
+- host->in_use_index = 0;
+- if (cmdr & AT91_MCI_TRDIR) {
+- /*
+- * Handle a read
+- */
+- host->buffer = NULL;
+- host->total_length = 0;
++ if (cmdr & AT91_MCI_TRCMD_START) {
++ data->bytes_xfered = 0;
++ host->transfer_index = 0;
++ host->in_use_index = 0;
++ if (cmdr & AT91_MCI_TRDIR) {
++ /*
++ * Handle a read
++ */
++ host->buffer = NULL;
++ host->total_length = 0;
+
+- at91mci_pre_dma_read(host);
+- ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
+- }
+- else {
+- /*
+- * Handle a write
+- */
+- host->total_length = block_length * blocks;
+- host->buffer = dma_alloc_coherent(NULL,
+- host->total_length,
+- &host->physical_address, GFP_KERNEL);
+-
+- at91mci_sg_to_dma(host, data);
+-
+- pr_debug("Transmitting %d bytes\n", host->total_length);
+-
+- at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
+- at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
+- ier = AT91_MCI_TXBUFE;
++ at91_mci_pre_dma_read(host);
++ ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */;
++ }
++ else {
++ /*
++ * Handle a write
++ */
++ host->total_length = block_length * blocks;
++ host->buffer = dma_alloc_coherent(NULL,
++ host->total_length,
++ &host->physical_address, GFP_KERNEL);
++
++ at91_mci_sg_to_dma(host, data);
++
++ pr_debug("Transmitting %d bytes\n", host->total_length);
++
++ at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address);
++ at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4);
++ ier = AT91_MCI_CMDRDY;
++ }
+ }
+ }
+
+@@ -497,39 +525,24 @@
+ if (cmdr & AT91_MCI_TRCMD_START) {
+ if (cmdr & AT91_MCI_TRDIR)
+ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN);
+- else
+- at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN);
+ }
+- return ier;
+-}
+-
+-/*
+- * Wait for a command to complete
+- */
+-static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd)
+-{
+- unsigned int ier;
+-
+- ier = at91_mci_send_command(host, cmd);
+-
+- pr_debug("setting ier to %08X\n", ier);
+
+- /* Stop on errors or the required value */
++ /* Enable selected interrupts */
+ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);
+ }
+
+ /*
+ * Process the next step in the request
+ */
+-static void at91mci_process_next(struct at91mci_host *host)
++static void at91_mci_process_next(struct at91mci_host *host)
+ {
+ if (!(host->flags & FL_SENT_COMMAND)) {
+ host->flags |= FL_SENT_COMMAND;
+- at91mci_process_command(host, host->request->cmd);
++ at91_mci_send_command(host, host->request->cmd);
+ }
+ else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) {
+ host->flags |= FL_SENT_STOP;
+- at91mci_process_command(host, host->request->stop);
++ at91_mci_send_command(host, host->request->stop);
+ }
+ else
+ mmc_request_done(host->mmc, host->request);
+@@ -538,7 +551,7 @@
+ /*
+ * Handle a command that has been completed
+ */
+-static void at91mci_completed_command(struct at91mci_host *host)
++static void at91_mci_completed_command(struct at91mci_host *host)
+ {
+ struct mmc_command *cmd = host->cmd;
+ unsigned int status;
+@@ -583,7 +596,7 @@
+ else
+ cmd->error = MMC_ERR_NONE;
+
+- at91mci_process_next(host);
++ at91_mci_process_next(host);
+ }
+
+ /*
+@@ -595,7 +608,7 @@
+ host->request = mrq;
+ host->flags = 0;
+
+- at91mci_process_next(host);
++ at91_mci_process_next(host);
+ }
+
+ /*
+@@ -698,29 +711,33 @@
+ at91_mci_handle_transmitted(host);
+ }
+
++ if (int_status & AT91_MCI_ENDRX) {
++ pr_debug("ENDRX\n");
++ at91_mci_post_dma_read(host);
++ }
++
+ if (int_status & AT91_MCI_RXBUFF) {
+ pr_debug("RX buffer full\n");
+- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
++ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS);
++ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX);
++ completed = 1;
+ }
+
+ if (int_status & AT91_MCI_ENDTX)
+ pr_debug("Transmit has ended\n");
+
+- if (int_status & AT91_MCI_ENDRX) {
+- pr_debug("Receive has ended\n");
+- at91mci_post_dma_read(host);
+- }
+-
+ if (int_status & AT91_MCI_NOTBUSY) {
+ pr_debug("Card is ready\n");
+- at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY);
++ completed = 1;
+ }
+
+ if (int_status & AT91_MCI_DTIP)
+ pr_debug("Data transfer in progress\n");
+
+- if (int_status & AT91_MCI_BLKE)
++ if (int_status & AT91_MCI_BLKE) {
+ pr_debug("Block transfer has ended\n");
++ completed = 1;
++ }
+
+ if (int_status & AT91_MCI_TXRDY)
+ pr_debug("Ready to transmit\n");
+@@ -730,14 +747,14 @@
+
+ if (int_status & AT91_MCI_CMDRDY) {
+ pr_debug("Command ready\n");
+- completed = 1;
++ completed = at91_mci_handle_cmdrdy(host);
+ }
+ }
+
+ if (completed) {
+ pr_debug("Completed command\n");
+ at91_mci_write(host, AT91_MCI_IDR, 0xffffffff);
+- at91mci_completed_command(host);
++ at91_mci_completed_command(host);
+ } else
+ at91_mci_write(host, AT91_MCI_IDR, int_status);
+
+@@ -830,11 +847,11 @@
+ host->bus_mode = 0;
+ host->board = pdev->dev.platform_data;
+ if (host->board->wire4) {
+-#ifdef SUPPORT_4WIRE
+- mmc->caps |= MMC_CAP_4_BIT_DATA;
+-#else
+- printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");
+-#endif
++ if (cpu_is_at91sam9260() || cpu_is_at91sam9263())
++ mmc->caps |= MMC_CAP_4_BIT_DATA;
++ else
++ printk("AT91 MMC: 4 wire bus mode not supported"
++ " - using 1 wire\n");
+ }
+
+ /*
+Index: linux-2.6.22.1/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/Kconfig 2007-07-19 00:20:08.056927650 +0200
++++ linux-2.6.22.1/drivers/mmc/host/Kconfig 2007-07-19 00:20:27.242020948 +0200
+@@ -100,16 +100,3 @@
+ To compile this driver as a module, choose M here: the
+ module will be called tifm_sd.
+
+-config MMC_S3C
+- tristate "Samsung S3C SD/MMC Card Interface support"
+- depends on ARCH_S3C2410 && MMC
+- help
+- This selects a driver for the MCI interface found in
+- Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+- If you have a board based on one of those and a MMC/SD
+- slot, say Y or M here.
+-
+- If unsure, say N.
+-
+-
+-
+Index: linux-2.6.22.1/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/Makefile 2007-07-19 00:20:08.036926511 +0200
++++ linux-2.6.22.1/drivers/mmc/host/Makefile 2007-07-19 00:20:27.266022319 +0200
+@@ -4,7 +4,6 @@
+
+ ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+- obj-$(CONFIG_MMC) += mmc_debug.o
+ endif
+
+ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
+@@ -16,5 +15,4 @@
+ obj-$(CONFIG_MMC_OMAP) += omap.o
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
+-obj-$(CONFIG_MMC_S3C) += s3cmci.o
+
+Index: linux-2.6.22.1/drivers/mmc/host/sdhci.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/sdhci.c 2007-07-19 00:20:07.928920359 +0200
++++ linux-2.6.22.1/drivers/mmc/host/sdhci.c 2007-07-19 00:20:27.426031434 +0200
+@@ -70,6 +70,14 @@
+ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE,
+ },
+
++ {
++ .vendor = PCI_VENDOR_ID_ENE,
++ .device = PCI_DEVICE_ID_ENE_CB712_SD_2,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE,
++ },
++
+ { /* Generic SD host controller */
+ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
+ },
+@@ -1022,7 +1030,7 @@
+ writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
+ }
+
+- intmask &= SDHCI_INT_BUS_POWER;
++ intmask &= ~SDHCI_INT_BUS_POWER;
+
+ if (intmask) {
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
+Index: linux-2.6.22.1/include/linux/mmc/card.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/card.h 2007-07-19 00:15:45.869986470 +0200
++++ linux-2.6.22.1/include/linux/mmc/card.h 2007-07-19 00:16:26.864322603 +0200
+@@ -55,7 +55,20 @@
+ unsigned int hs_max_dtr;
+ };
+
++struct sdio_cccr {
++ unsigned int sdio_vsn;
++ unsigned int sd_vsn;
++ unsigned int multi_block:1,
++ low_speed:1,
++ wide_bus:1,
++ high_power:1,
++ high_speed:1;
++};
++
+ struct mmc_host;
++struct sdio_func;
++
++#define SDIO_MAX_FUNCS 7
+
+ /*
+ * MMC device
+@@ -67,11 +80,13 @@
+ unsigned int type; /* card type */
+ #define MMC_TYPE_MMC 0 /* MMC card */
+ #define MMC_TYPE_SD 1 /* SD card */
++#define MMC_TYPE_SDIO 2 /* SDIO card */
+ unsigned int state; /* (our) card state */
+ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
+ #define MMC_STATE_READONLY (1<<1) /* card is read-only */
+ #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
+ #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
++
+ u32 raw_cid[4]; /* raw card CID */
+ u32 raw_csd[4]; /* raw card CSD */
+ u32 raw_scr[2]; /* raw card SCR */
+@@ -80,10 +95,15 @@
+ struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
+ struct sd_scr scr; /* extra SD information */
+ struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
++
++ unsigned int sdio_funcs; /* number of SDIO functions */
++ struct sdio_cccr cccr; /* common card info */
++ struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
+ };
+
+ #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
+ #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
++#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
+
+ #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
+ #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
+Index: linux-2.6.22.1/include/linux/mmc/core.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/core.h 2007-07-19 00:15:45.909988749 +0200
++++ linux-2.6.22.1/include/linux/mmc/core.h 2007-07-19 00:39:23.226757028 +0200
+@@ -41,6 +41,8 @@
+ #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
+ #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
+ #define MMC_RSP_R3 (MMC_RSP_PRESENT)
++#define MMC_RSP_R4 (MMC_RSP_PRESENT)
++#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+ #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+ #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
+
+@@ -106,7 +108,18 @@
+
+ extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int);
+
+-extern void mmc_claim_host(struct mmc_host *host);
++extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
+ extern void mmc_release_host(struct mmc_host *host);
+
++/**
++ * mmc_claim_host - exclusively claim a host
++ * @host: mmc host to claim
++ *
++ * Claim a host for a set of operations.
++ */
++static inline void mmc_claim_host(struct mmc_host *host)
++{
++ __mmc_claim_host(host, NULL);
++}
++
+ #endif
+Index: linux-2.6.22.1/include/linux/mmc/host.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/host.h 2007-07-19 00:15:45.929989885 +0200
++++ linux-2.6.22.1/include/linux/mmc/host.h 2007-07-19 00:16:26.912325341 +0200
+@@ -124,6 +124,10 @@
+ unsigned int bus_refs; /* reference counter */
+ unsigned int bus_dead:1; /* bus has been released */
+
++ unsigned int sdio_irqs;
++ struct task_struct *sdio_irq_thread;
++ atomic_t sdio_irq_thread_abort;
++
+ unsigned long private[0] ____cacheline_aligned;
+ };
+
+Index: linux-2.6.22.1/include/linux/mmc/sdio_func.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/mmc/sdio_func.h 2007-07-19 00:16:27.012331037 +0200
+@@ -0,0 +1,138 @@
++/*
++ * include/linux/mmc/sdio_func.h
++ *
++ * Copyright 2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef MMC_SDIO_FUNC_H
++#define MMC_SDIO_FUNC_H
++
++#include <linux/device.h>
++#include <linux/mod_devicetable.h>
++
++struct mmc_card;
++struct sdio_func;
++
++typedef void (sdio_irq_handler_t)(struct sdio_func *);
++
++/*
++ * SDIO function CIS tuple (unknown to the core)
++ */
++struct sdio_func_tuple {
++ struct sdio_func_tuple *next;
++ unsigned char code;
++ unsigned char size;
++ unsigned char data[0];
++};
++
++/*
++ * SDIO function devices
++ */
++struct sdio_func {
++ struct mmc_card *card; /* the card this device belongs to */
++ struct device dev; /* the device */
++ sdio_irq_handler_t *irq_handler; /* IRQ callback */
++ unsigned int num; /* function number */
++
++ unsigned char class; /* standard interface class */
++ unsigned short vendor; /* vendor id */
++ unsigned short device; /* device id */
++
++ unsigned int state; /* function state */
++#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
++
++ struct sdio_func_tuple *tuples;
++};
++
++#define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT)
++
++#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
++
++#define sdio_func_id(f) ((f)->dev.bus_id)
++
++#define sdio_get_drvdata(f) dev_get_drvdata(&(f)->dev)
++#define sdio_set_drvdata(f,d) dev_set_drvdata(&(f)->dev, d)
++
++/*
++ * SDIO function device driver
++ */
++struct sdio_driver {
++ char *name;
++ const struct sdio_device_id *id_table;
++
++ int (*probe)(struct sdio_func *, const struct sdio_device_id *);
++ void (*remove)(struct sdio_func *);
++
++ struct device_driver drv;
++};
++
++/**
++ * SDIO_DEVICE - macro used to describe a specific SDIO device
++ * @vend: the 16 bit manufacturer code
++ * @dev: the 16 bit function id
++ *
++ * This macro is used to create a struct sdio_device_id that matches a
++ * specific device. The class field will be set to SDIO_ANY_ID.
++ */
++#define SDIO_DEVICE(vend,dev) \
++ .class = SDIO_ANY_ID, \
++ .vendor = (vend), .device = (dev)
++
++/**
++ * SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class
++ * @dev_class: the 8 bit standard interface code
++ *
++ * This macro is used to create a struct sdio_device_id that matches a
++ * specific standard SDIO function type. The vendor and device fields will
++ * be set to SDIO_ANY_ID.
++ */
++#define SDIO_DEVICE_CLASS(dev_class) \
++ .class = (dev_class), \
++ .vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID
++
++extern int sdio_register_driver(struct sdio_driver *);
++extern void sdio_unregister_driver(struct sdio_driver *);
++
++/*
++ * SDIO I/O operations
++ */
++extern void sdio_claim_host(struct sdio_func *func);
++extern void sdio_release_host(struct sdio_func *func);
++
++extern int sdio_enable_func(struct sdio_func *func);
++extern int sdio_disable_func(struct sdio_func *func);
++
++extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
++extern int sdio_release_irq(struct sdio_func *func);
++
++extern unsigned char sdio_readb(struct sdio_func *func,
++ unsigned int addr, int *err_ret);
++extern unsigned short sdio_readw(struct sdio_func *func,
++ unsigned int addr, int *err_ret);
++extern unsigned long sdio_readl(struct sdio_func *func,
++ unsigned int addr, int *err_ret);
++
++extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
++ unsigned int addr, int count);
++extern int sdio_readsb(struct sdio_func *func, void *dst,
++ unsigned int addr, int count);
++
++extern void sdio_writeb(struct sdio_func *func, unsigned char b,
++ unsigned int addr, int *err_ret);
++extern void sdio_writew(struct sdio_func *func, unsigned short b,
++ unsigned int addr, int *err_ret);
++extern void sdio_writel(struct sdio_func *func, unsigned long b,
++ unsigned int addr, int *err_ret);
++
++extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
++ void *src, int count);
++extern int sdio_writesb(struct sdio_func *func, unsigned int addr,
++ void *src, int count);
++
++#endif
++
+Index: linux-2.6.22.1/include/linux/mmc/sdio.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/mmc/sdio.h 2007-07-19 00:16:27.012331037 +0200
+@@ -0,0 +1,157 @@
++/*
++ * include/linux/mmc/sdio.h
++ *
++ * Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef MMC_SDIO_H
++#define MMC_SDIO_H
++
++/* SDIO commands type argument response */
++#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */
++#define SD_IO_RW_DIRECT 52 /* ac [31:0] See below R5 */
++#define SD_IO_RW_EXTENDED 53 /* adtc [31:0] See below R5 */
++
++/*
++ * SD_IO_RW_DIRECT argument format:
++ *
++ * [31] R/W flag
++ * [30:28] Function number
++ * [27] RAW flag
++ * [25:9] Register address
++ * [7:0] Data
++ */
++
++/*
++ * SD_IO_RW_EXTENDED argument format:
++ *
++ * [31] R/W flag
++ * [30:28] Function number
++ * [27] Block mode
++ * [26] Increment address
++ * [25:9] Register address
++ * [8:0] Byte/block count
++ */
++
++/*
++ SDIO status in R5
++ Type
++ e : error bit
++ s : status bit
++ r : detected and set for the actual command response
++ x : detected and set during command execution. the host must poll
++ the card by sending status command in order to read these bits.
++ Clear condition
++ a : according to the card state
++ b : always related to the previous command. Reception of
++ a valid command will clear it (with a delay of one command)
++ c : clear by read
++ */
++
++#define R5_COM_CRC_ERROR (1 << 15) /* er, b */
++#define R5_ILLEGAL_COMMAND (1 << 14) /* er, b */
++#define R5_ERROR (1 << 11) /* erx, c */
++#define R5_FUNCTION_NUMBER (1 << 9) /* er, c */
++#define R5_OUT_OF_RANGE (1 << 8) /* er, c */
++#define R5_STATUS(x) (x & 0xCB00)
++#define R5_IO_CURRENT_STATE(x) ((x & 0x3000) >> 12) /* s, b */
++
++/*
++ * Card Common Control Registers (CCCR)
++ */
++
++#define SDIO_CCCR_CCCR 0x00
++
++#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */
++#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
++#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
++
++#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
++#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */
++#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */
++#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */
++
++#define SDIO_CCCR_SD 0x01
++
++#define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */
++#define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */
++#define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */
++
++#define SDIO_CCCR_IOEx 0x02
++#define SDIO_CCCR_IORx 0x03
++
++#define SDIO_CCCR_IENx 0x04 /* Function/Master Interrupt Enable */
++#define SDIO_CCCR_INTx 0x05 /* Function Interrupt Pending */
++
++#define SDIO_CCCR_ABORT 0x06 /* function abort/card reset */
++
++#define SDIO_CCCR_IF 0x07 /* bus interface controls */
++
++#define SDIO_BUS_WIDTH_1BIT 0x00
++#define SDIO_BUS_WIDTH_4BIT 0x02
++
++#define SDIO_BUS_CD_DISABLE 0x80 /* disable pull-up on DAT3 (pin 1) */
++
++#define SDIO_CCCR_CAPS 0x08
++
++#define SDIO_CCCR_CAP_SDC 0x01 /* can do CMD52 while data transfer */
++#define SDIO_CCCR_CAP_SMB 0x02 /* can do multi-block xfers (CMD53) */
++#define SDIO_CCCR_CAP_SRW 0x04 /* supports read-wait protocol */
++#define SDIO_CCCR_CAP_SBS 0x08 /* supports suspend/resume */
++#define SDIO_CCCR_CAP_S4MI 0x10 /* interrupt during 4-bit CMD53 */
++#define SDIO_CCCR_CAP_E4MI 0x20 /* enable ints during 4-bit CMD53 */
++#define SDIO_CCCR_CAP_LSC 0x40 /* low speed card */
++#define SDIO_CCCR_CAP_4BLS 0x80 /* 4 bit low speed card */
++
++#define SDIO_CCCR_CIS 0x09 /* common CIS pointer (3 bytes) */
++
++/* Following 4 regs are valid only if SBS is set */
++#define SDIO_CCCR_SUSPEND 0x0c
++#define SDIO_CCCR_SELx 0x0d
++#define SDIO_CCCR_EXECx 0x0e
++#define SDIO_CCCR_READYx 0x0f
++
++#define SDIO_CCCR_BLKSIZE 0x10
++
++#define SDIO_CCCR_POWER 0x12
++
++#define SDIO_POWER_SMPC 0x01 /* Supports Master Power Control */
++#define SDIO_POWER_EMPC 0x02 /* Enable Master Power Control */
++
++#define SDIO_CCCR_SPEED 0x13
++
++#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */
++#define SDIO_SPEED_EHS 0x02 /* Enable High-Speed mode */
++
++/*
++ * Function Basic Registers (FBR)
++ */
++
++#define SDIO_FBR_STD_IF 0x00
++
++#define SDIO_FBR_SUPPORTS_CSA 0x40 /* supports Code Storage Area */
++#define SDIO_FBR_ENABLE_CSA 0x80 /* enable Code Storage Area */
++
++#define SDIO_FBR_STD_IF_EXT 0x01
++
++#define SDIO_FBR_POWER 0x02
++
++#define SDIO_FBR_POWER_SPS 0x01 /* Supports Power Selection */
++#define SDIO_FBR_POWER_EPS 0x02 /* Enable (low) Power Selection */
++
++#define SDIO_FBR_CIS 0x09 /* CIS pointer (3 bytes) */
++
++
++#define SDIO_FBR_CSA 0x0C /* CSA pointer (3 bytes) */
++
++#define SDIO_FBR_CSA_DATA 0x0F
++
++#define SDIO_FBR_BLKSIZE 0x10 /* block size (2 bytes) */
++
++#endif
++
+Index: linux-2.6.22.1/include/linux/mmc/sdio_ids.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/mmc/sdio_ids.h 2007-07-19 00:16:27.012331037 +0200
+@@ -0,0 +1,23 @@
++/*
++ * SDIO Classes, Interface Types, Manufacturer IDs, etc.
++ */
++
++#ifndef MMC_SDIO_IDS_H
++#define MMC_SDIO_IDS_H
++
++/*
++ * Standard SDIO Function Interfaces
++ */
++
++#define SDIO_CLASS_NONE 0x00 /* Not a SDIO standard interface */
++#define SDIO_CLASS_UART 0x01 /* standard UART interface */
++#define SDIO_CLASS_BT_A 0x02 /* Type-A BlueTooth std interface */
++#define SDIO_CLASS_BT_B 0x03 /* Type-B BlueTooth std interface */
++#define SDIO_CLASS_GPS 0x04 /* GPS standard interface */
++#define SDIO_CLASS_CAMERA 0x05 /* Camera standard interface */
++#define SDIO_CLASS_PHS 0x06 /* PHS standard interface */
++#define SDIO_CLASS_WLAN 0x07 /* WLAN interface */
++#define SDIO_CLASS_ATA 0x08 /* Embedded SDIO-ATA std interface */
++
++
++#endif
+Index: linux-2.6.22.1/include/linux/mod_devicetable.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mod_devicetable.h 2007-07-19 00:39:45.880047969 +0200
++++ linux-2.6.22.1/include/linux/mod_devicetable.h 2007-07-19 00:39:55.784612397 +0200
+@@ -333,4 +333,15 @@
+ #define PA_HVERSION_ANY_ID 0xffff
+ #define PA_SVERSION_ANY_ID 0xffffffff
+
++/* SDIO */
++
++#define SDIO_ANY_ID (~0)
++
++struct sdio_device_id {
++ __u8 class; /* Standard interface or SDIO_ANY_ID */
++ __u16 vendor; /* Vendor or SDIO_ANY_ID */
++ __u16 device; /* Device ID or SDIO_ANY_ID */
++ kernel_ulong_t driver_data; /* Data private to the driver */
++};
++
+ #endif /* LINUX_MOD_DEVICETABLE_H */
Added: developers/nbd/patches-2.6.22/810-s3c_mci.patch
===================================================================
--- developers/nbd/patches-2.6.22/810-s3c_mci.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/810-s3c_mci.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,1603 @@
+This is the latest S3C MMC/SD driver by Thomas Kleffel
+
+Index: linux-2.6.22.1/include/asm-arm/arch-s3c2410/regs-sdi.h
+===================================================================
+--- linux-2.6.22.1.orig/include/asm-arm/arch-s3c2410/regs-sdi.h 2007-07-19 01:10:32.537282965 +0200
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/regs-sdi.h 2007-07-19 01:10:38.565626502 +0200
+@@ -28,9 +28,15 @@
+ #define S3C2410_SDIDCNT (0x30)
+ #define S3C2410_SDIDSTA (0x34)
+ #define S3C2410_SDIFSTA (0x38)
++
+ #define S3C2410_SDIDATA (0x3C)
+ #define S3C2410_SDIIMSK (0x40)
+
++#define S3C2440_SDIDATA (0x40)
++#define S3C2440_SDIIMSK (0x3C)
++
++#define S3C2440_SDICON_SDRESET (1<<8)
++#define S3C2440_SDICON_MMCCLOCK (1<<5)
+ #define S3C2410_SDICON_BYTEORDER (1<<4)
+ #define S3C2410_SDICON_SDIOIRQ (1<<3)
+ #define S3C2410_SDICON_RWAITEN (1<<2)
+@@ -42,7 +48,8 @@
+ #define S3C2410_SDICMDCON_LONGRSP (1<<10)
+ #define S3C2410_SDICMDCON_WAITRSP (1<<9)
+ #define S3C2410_SDICMDCON_CMDSTART (1<<8)
+-#define S3C2410_SDICMDCON_INDEX (0xff)
++#define S3C2410_SDICMDCON_SENDERHOST (1<<6)
++#define S3C2410_SDICMDCON_INDEX (0x3f)
+
+ #define S3C2410_SDICMDSTAT_CRCFAIL (1<<12)
+ #define S3C2410_SDICMDSTAT_CMDSENT (1<<11)
+@@ -51,6 +58,9 @@
+ #define S3C2410_SDICMDSTAT_XFERING (1<<8)
+ #define S3C2410_SDICMDSTAT_INDEX (0xff)
+
++#define S3C2440_SDIDCON_DS_BYTE (0<<22)
++#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)
++#define S3C2440_SDIDCON_DS_WORD (2<<22)
+ #define S3C2410_SDIDCON_IRQPERIOD (1<<21)
+ #define S3C2410_SDIDCON_TXAFTERRESP (1<<20)
+ #define S3C2410_SDIDCON_RXAFTERCMD (1<<19)
+@@ -59,6 +69,7 @@
+ #define S3C2410_SDIDCON_WIDEBUS (1<<16)
+ #define S3C2410_SDIDCON_DMAEN (1<<15)
+ #define S3C2410_SDIDCON_STOP (1<<14)
++#define S3C2440_SDIDCON_DATSTART (1<<14)
+ #define S3C2410_SDIDCON_DATMODE (3<<12)
+ #define S3C2410_SDIDCON_BLKNUM (0x7ff)
+
+@@ -68,6 +79,7 @@
+ #define S3C2410_SDIDCON_XFER_RXSTART (2<<12)
+ #define S3C2410_SDIDCON_XFER_TXSTART (3<<12)
+
++#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
+ #define S3C2410_SDIDCNT_BLKNUM_SHIFT (12)
+
+ #define S3C2410_SDIDSTA_RDYWAITREQ (1<<10)
+@@ -82,10 +94,12 @@
+ #define S3C2410_SDIDSTA_TXDATAON (1<<1)
+ #define S3C2410_SDIDSTA_RXDATAON (1<<0)
+
++#define S3C2440_SDIFSTA_FIFORESET (1<<16)
++#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
+ #define S3C2410_SDIFSTA_TFDET (1<<13)
+ #define S3C2410_SDIFSTA_RFDET (1<<12)
+-#define S3C2410_SDIFSTA_TXHALF (1<<11)
+-#define S3C2410_SDIFSTA_TXEMPTY (1<<10)
++#define S3C2410_SDIFSTA_TFHALF (1<<11)
++#define S3C2410_SDIFSTA_TFEMPTY (1<<10)
+ #define S3C2410_SDIFSTA_RFLAST (1<<9)
+ #define S3C2410_SDIFSTA_RFFULL (1<<8)
+ #define S3C2410_SDIFSTA_RFHALF (1<<7)
+Index: linux-2.6.22.1/drivers/mmc/host/mmc_debug.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/host/mmc_debug.c 2007-07-19 01:15:25.233962797 +0200
+@@ -0,0 +1,61 @@
++/*
++ * linux/drivers/mmc/mmc_debug.c
++ *
++ * Copyright (C) 2003 maintech GmbH, Thomas Kleffel <tk at maintech.de>
++ *
++ * This file contains debug helper functions for the MMC/SD stack
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/mmc/core.h>
++#include <linux/mmc/mmc.h>
++#include "mmc_debug.h"
++
++char *mmc_cmd2str(int cmd)
++{
++ switch(cmd) {
++ case 0: return "GO_IDLE_STATE";
++ case 1: return "ALL_SEND_OCR";
++ case 2: return "ALL_SEND_CID";
++ case 3: return "ALL_SEND_RELATIVE_ADD";
++ case 6: return "ACMD: SD_SET_BUSWIDTH";
++ case 7: return "SEL_DESEL_CARD";
++ case 9: return "SEND_CSD";
++ case 10: return "SEND_CID";
++ case 11: return "READ_UNTIL_STOP";
++ case 12: return "STOP_TRANSMISSION";
++ case 13: return "SEND_STATUS";
++ case 15: return "GO_INACTIVE_STATE";
++ case 16: return "SET_BLOCKLEN";
++ case 17: return "READ_SINGLE_BLOCK";
++ case 18: return "READ_MULTIPLE_BLOCK";
++ case 24: return "WRITE_SINGLE_BLOCK";
++ case 25: return "WRITE_MULTIPLE_BLOCK";
++ case 41: return "ACMD: SD_APP_OP_COND";
++ case 55: return "APP_CMD";
++ default: return "UNKNOWN";
++ }
++}
++EXPORT_SYMBOL(mmc_cmd2str);
++
++char *mmc_err2str(int err)
++{
++ switch(err) {
++ case MMC_ERR_NONE: return "OK";
++ case MMC_ERR_TIMEOUT: return "TIMEOUT";
++ case MMC_ERR_BADCRC: return "BADCRC";
++ case MMC_ERR_FIFO: return "FIFO";
++ case MMC_ERR_FAILED: return "FAILED";
++ case MMC_ERR_INVALID: return "INVALID";
++ case MMC_ERR_BUSY: return "BUSY";
++ case MMC_ERR_DMA: return "DMA";
++ case MMC_ERR_CANCELED: return "CANCELED";
++ default: return "UNKNOWN";
++ }
++}
++EXPORT_SYMBOL(mmc_err2str);
+Index: linux-2.6.22.1/drivers/mmc/host/mmc_debug.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/host/mmc_debug.h 2007-07-19 01:10:38.597628323 +0200
+@@ -0,0 +1,7 @@
++#ifndef MMC_DEBUG_H
++#define MMC_DEBUG_H
++
++char *mmc_cmd2str(int err);
++char *mmc_err2str(int err);
++
++#endif /* MMC_DEBUG_H */
+Index: linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c 2007-07-19 01:13:22.862989265 +0200
+@@ -0,0 +1,1341 @@
++/*
++ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
++ *
++ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk at maintech.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/module.h>
++#include <linux/dma-mapping.h>
++#include <linux/clk.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++
++#include <asm/dma.h>
++#include <asm/dma-mapping.h>
++
++#include <asm/io.h>
++#include <asm/arch/regs-sdi.h>
++#include <asm/arch/regs-gpio.h>
++
++#include "mmc_debug.h"
++#include "s3cmci.h"
++
++#define DRIVER_NAME "s3c-mci"
++
++enum dbg_channels {
++ dbg_err = (1 << 0),
++ dbg_debug = (1 << 1),
++ dbg_info = (1 << 2),
++ dbg_irq = (1 << 3),
++ dbg_sg = (1 << 4),
++ dbg_dma = (1 << 5),
++ dbg_pio = (1 << 6),
++ dbg_fail = (1 << 7),
++ dbg_conf = (1 << 8),
++};
++
++static const int dbgmap_err = dbg_err | dbg_fail;
++static const int dbgmap_info = dbg_info | dbg_conf;
++static const int dbgmap_debug = dbg_debug;
++
++#define dbg(host, channels, args...) \
++ if (dbgmap_err & channels) \
++ dev_err(&host->pdev->dev, args); \
++ else if (dbgmap_info & channels) \
++ dev_info(&host->pdev->dev, args);\
++ else if (dbgmap_debug & channels) \
++ dev_dbg(&host->pdev->dev, args);
++
++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
++
++static struct s3c2410_dma_client s3cmci_dma_client = {
++ .name = "s3c-mci",
++};
++
++static void finalize_request(struct s3cmci_host *host);
++static void s3cmci_send_request(struct mmc_host *mmc);
++static void s3cmci_reset(struct s3cmci_host *host);
++
++#ifdef CONFIG_MMC_DEBUG
++
++static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
++{
++ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
++ u32 datcon, datcnt, datsta, fsta, imask;
++
++ con = readl(host->base + S3C2410_SDICON);
++ pre = readl(host->base + S3C2410_SDIPRE);
++ cmdarg = readl(host->base + S3C2410_SDICMDARG);
++ cmdcon = readl(host->base + S3C2410_SDICMDCON);
++ cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
++ r0 = readl(host->base + S3C2410_SDIRSP0);
++ r1 = readl(host->base + S3C2410_SDIRSP1);
++ r2 = readl(host->base + S3C2410_SDIRSP2);
++ r3 = readl(host->base + S3C2410_SDIRSP3);
++ timer = readl(host->base + S3C2410_SDITIMER);
++ bsize = readl(host->base + S3C2410_SDIBSIZE);
++ datcon = readl(host->base + S3C2410_SDIDCON);
++ datcnt = readl(host->base + S3C2410_SDIDCNT);
++ datsta = readl(host->base + S3C2410_SDIDSTA);
++ fsta = readl(host->base + S3C2410_SDIFSTA);
++ imask = readl(host->base + host->sdiimsk);
++
++ dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
++ prefix, con, pre, timer);
++
++ dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
++ prefix, cmdcon, cmdarg, cmdsta);
++
++ dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
++ " DSTA:[%08x] DCNT:[%08x]\n",
++ prefix, datcon, fsta, datsta, datcnt);
++
++ dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
++ " R2:[%08x] R3:[%08x]\n",
++ prefix, r0, r1, r2, r3);
++}
++
++static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
++ int stop)
++{
++ snprintf(host->dbgmsg_cmd, 300,
++ "#%u%s op:%s(%i) arg:0x%08x flags:0x08%x retries:%u",
++ host->ccnt, (stop?" (STOP)":""), mmc_cmd2str(cmd->opcode),
++ cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
++
++ if (cmd->data) {
++ snprintf(host->dbgmsg_dat, 300,
++ "#%u bsize:%u blocks:%u bytes:%u",
++ host->dcnt, cmd->data->blksz,
++ cmd->data->blocks,
++ cmd->data->blocks * cmd->data->blksz);
++ } else {
++ host->dbgmsg_dat[0] = '\0';
++ }
++}
++
++static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
++ int fail)
++{
++ unsigned int dbglvl = fail?dbg_fail:dbg_debug;
++
++ if (!cmd)
++ return;
++
++ if (cmd->error == MMC_ERR_NONE) {
++
++ dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
++ host->dbgmsg_cmd, cmd->resp[0]);
++ } else {
++ dbg(host, dbglvl, "CMD[%s] %s Status:%s\n",
++ mmc_err2str(cmd->error), host->dbgmsg_cmd,
++ host->status);
++ }
++
++ if (!cmd->data)
++ return;
++
++ if (cmd->data->error == MMC_ERR_NONE) {
++ dbg(host, dbglvl, "DAT[%s] %s\n",
++ mmc_err2str(cmd->data->error), host->dbgmsg_dat);
++ } else {
++ dbg(host, dbglvl, "DAT[%s] %s DCNT:0x%08x\n",
++ mmc_err2str(cmd->data->error), host->dbgmsg_dat,
++ readl(host->base + S3C2410_SDIDCNT));
++ }
++}
++#endif
++
++static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
++{
++ u32 newmask;
++
++ newmask = readl(host->base + host->sdiimsk);
++ newmask|= imask;
++
++ writel(newmask, host->base + host->sdiimsk);
++
++ return newmask;
++}
++
++static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
++{
++ u32 newmask;
++
++ newmask = readl(host->base + host->sdiimsk);
++ newmask&= ~imask;
++
++ writel(newmask, host->base + host->sdiimsk);
++
++ return newmask;
++}
++
++static inline void clear_imask(struct s3cmci_host *host)
++{
++ writel(0, host->base + host->sdiimsk);
++}
++
++static inline int get_data_buffer(struct s3cmci_host *host,
++ volatile u32 *words, volatile u32 **pointer)
++{
++ struct scatterlist *sg;
++
++ if (host->pio_active == XFER_NONE)
++ return -EINVAL;
++
++ if ((!host->mrq) || (!host->mrq->data))
++ return -EINVAL;
++
++ if (host->pio_sgptr >= host->mrq->data->sg_len) {
++ dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
++ host->pio_sgptr, host->mrq->data->sg_len);
++ return -EBUSY;
++ }
++ sg = &host->mrq->data->sg[host->pio_sgptr];
++
++ *words = sg->length >> 2;
++ *pointer= page_address(sg->page) + sg->offset;
++
++ host->pio_sgptr++;
++
++ dbg(host, dbg_sg, "new buffer (%i/%i)\n",
++ host->pio_sgptr, host->mrq->data->sg_len);
++
++ return 0;
++}
++
++#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
++#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
++
++static inline void do_pio_read(struct s3cmci_host *host)
++{
++ int res;
++ u32 fifo;
++ void __iomem *from_ptr;
++
++ //write real prescaler to host, it might be set slow to fix
++ writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++ from_ptr = host->base + host->sdidata;
++
++ while ((fifo = FIFO_FILL(host))) {
++ if (!host->pio_words) {
++ res = get_data_buffer(host, &host->pio_words,
++ &host->pio_ptr);
++ if (res) {
++ host->pio_active = XFER_NONE;
++ host->complete_what = COMPLETION_FINALIZE;
++
++ dbg(host, dbg_pio, "pio_read(): "
++ "complete (no more data).\n");
++ return;
++ }
++
++ dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
++ host->pio_words, host->pio_ptr);
++ }
++
++ dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
++ "buffer:[%03i] dcnt:[%08X]\n",
++ fifo, host->pio_words,
++ readl(host->base + S3C2410_SDIDCNT));
++
++ if (fifo > host->pio_words)
++ fifo = host->pio_words;
++
++ host->pio_words-= fifo;
++ host->pio_count+= fifo;
++
++ while(fifo--) {
++ *(host->pio_ptr++) = readl(from_ptr);
++ }
++ }
++
++ if (!host->pio_words) {
++ res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
++ if (res) {
++ dbg(host, dbg_pio, "pio_read(): "
++ "complete (no more buffers).\n");
++ host->pio_active = XFER_NONE;
++ host->complete_what = COMPLETION_FINALIZE;
++
++ return;
++ }
++ }
++
++ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
++}
++
++static inline void do_pio_write(struct s3cmci_host *host)
++{
++ int res;
++ u32 fifo;
++
++ void __iomem *to_ptr;
++
++ to_ptr = host->base + host->sdidata;
++
++ while ((fifo = FIFO_FREE(host))) {
++ if (!host->pio_words) {
++ res = get_data_buffer(host, &host->pio_words,
++ &host->pio_ptr);
++ if (res) {
++ dbg(host, dbg_pio, "pio_write(): "
++ "complete (no more data).\n");
++ host->pio_active = XFER_NONE;
++
++ return;
++ }
++
++ dbg(host, dbg_pio, "pio_write(): "
++ "new source: [%i]@[%p]\n",
++ host->pio_words, host->pio_ptr);
++
++ }
++
++ if (fifo > host->pio_words)
++ fifo = host->pio_words;
++
++ host->pio_words-= fifo;
++ host->pio_count+= fifo;
++
++ while(fifo--) {
++ writel(*(host->pio_ptr++), to_ptr);
++ }
++ }
++
++ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++}
++
++static void pio_tasklet(unsigned long data)
++{
++ struct s3cmci_host *host = (struct s3cmci_host *) data;
++
++
++ if (host->pio_active == XFER_WRITE)
++ do_pio_write(host);
++
++ if (host->pio_active == XFER_READ)
++ do_pio_read(host);
++
++ if (host->complete_what == COMPLETION_FINALIZE) {
++ clear_imask(host);
++ if (host->pio_active != XFER_NONE) {
++ dbg(host, dbg_err, "unfinished %s "
++ "- pio_count:[%u] pio_words:[%u]\n",
++ (host->pio_active == XFER_READ)?"read":"write",
++ host->pio_count, host->pio_words);
++
++ host->mrq->data->error = MMC_ERR_DMA;
++ }
++
++ disable_irq(host->irq);
++ finalize_request(host);
++ }
++}
++
++/*
++ * ISR for SDI Interface IRQ
++ * Communication between driver and ISR works as follows:
++ * host->mrq points to current request
++ * host->complete_what tells the ISR when the request is considered done
++ * COMPLETION_CMDSENT when the command was sent
++ * COMPLETION_RSPFIN when a response was received
++ * COMPLETION_XFERFINISH when the data transfer is finished
++ * COMPLETION_XFERFINISH_RSPFIN both of the above.
++ * host->complete_request is the completion-object the driver waits for
++ *
++ * 1) Driver sets up host->mrq and host->complete_what
++ * 2) Driver prepares the transfer
++ * 3) Driver enables interrupts
++ * 4) Driver starts transfer
++ * 5) Driver waits for host->complete_rquest
++ * 6) ISR checks for request status (errors and success)
++ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
++ * 7) ISR completes host->complete_request
++ * 8) ISR disables interrupts
++ * 9) Driver wakes up and takes care of the request
++ *
++ * Note: "->error"-fields are expected to be set to 0 before the request
++ * was issued by mmc.c - therefore they are only set, when an error
++ * contition comes up
++ */
++
++static irqreturn_t s3cmci_irq(int irq, void *dev_id)
++{
++ struct s3cmci_host *host;
++ struct mmc_command *cmd;
++ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
++ u32 mci_cclear, mci_dclear;
++ unsigned long iflags;
++
++ host = (struct s3cmci_host *)dev_id;
++
++ spin_lock_irqsave(&host->complete_lock, iflags);
++
++ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
++ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
++ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
++ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
++ mci_imsk = readl(host->base + host->sdiimsk);
++ mci_cclear = 0;
++ mci_dclear = 0;
++
++ if ((host->complete_what == COMPLETION_NONE) ||
++ (host->complete_what == COMPLETION_FINALIZE)) {
++ host->status = "nothing to complete";
++ clear_imask(host);
++ goto irq_out;
++ }
++
++ if (!host->mrq) {
++ host->status = "no active mrq";
++ clear_imask(host);
++ goto irq_out;
++ }
++
++ cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd;
++
++ if (!cmd) {
++ host->status = "no active cmd";
++ clear_imask(host);
++ goto irq_out;
++ }
++
++ if (!host->dodma) {
++ if ((host->pio_active == XFER_WRITE) &&
++ (mci_fsta & S3C2410_SDIFSTA_TFDET)) {
++
++ disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++ tasklet_schedule(&host->pio_tasklet);
++ host->status = "pio tx";
++ }
++
++ if ((host->pio_active == XFER_READ) &&
++ (mci_fsta & S3C2410_SDIFSTA_RFDET)) {
++
++ disable_imask(host,
++ S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
++
++ tasklet_schedule(&host->pio_tasklet);
++ host->status = "pio rx";
++ }
++ }
++
++ if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
++ cmd->error = MMC_ERR_TIMEOUT;
++ host->status = "error: command timeout";
++ goto fail_transfer;
++ }
++
++ if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
++ if (host->complete_what == COMPLETION_CMDSENT) {
++ host->status = "ok: command sent";
++ goto close_transfer;
++ }
++
++ mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
++ }
++
++ if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
++ if (cmd->flags & MMC_RSP_CRC) {
++ cmd->error = MMC_ERR_BADCRC;
++ host->status = "error: bad command crc";
++ goto fail_transfer;
++ }
++
++ mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
++ }
++
++ if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
++ if (host->complete_what == COMPLETION_RSPFIN) {
++ host->status = "ok: command response received";
++ goto close_transfer;
++ }
++
++ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
++ host->complete_what = COMPLETION_XFERFINISH;
++
++ mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
++ }
++
++ /* errors handled after this point are only relevant
++ when a data transfer is in progress */
++
++ if (!cmd->data)
++ goto clear_status_bits;
++
++ /* Check for FIFO failure */
++ if (host->is2440) {
++ if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
++ host->mrq->data->error = MMC_ERR_FIFO;
++ host->status = "error: 2440 fifo failure";
++ goto fail_transfer;
++ }
++ } else {
++ if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
++ cmd->data->error = MMC_ERR_FIFO;
++ host->status = "error: fifo failure";
++ goto fail_transfer;
++ }
++ }
++
++ if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
++ cmd->data->error = MMC_ERR_BADCRC;
++ host->status = "error: bad data crc (outgoing)";
++ goto fail_transfer;
++ }
++
++ if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
++ cmd->data->error = MMC_ERR_BADCRC;
++ host->status = "error: bad data crc (incoming)";
++ goto fail_transfer;
++ }
++
++ if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
++ cmd->data->error = MMC_ERR_TIMEOUT;
++ host->status = "error: data timeout";
++ goto fail_transfer;
++ }
++
++ if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
++ if (host->complete_what == COMPLETION_XFERFINISH) {
++ host->status = "ok: data transfer completed";
++ goto close_transfer;
++ }
++
++ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
++ host->complete_what = COMPLETION_RSPFIN;
++ }
++
++ mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
++ }
++
++clear_status_bits:
++ writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
++ writel(mci_dclear, host->base + S3C2410_SDIDSTA);
++
++ goto irq_out;
++
++fail_transfer:
++ host->pio_active = XFER_NONE;
++
++close_transfer:
++ host->complete_what = COMPLETION_FINALIZE;
++
++ clear_imask(host);
++ tasklet_schedule(&host->pio_tasklet);
++
++ goto irq_out;
++
++irq_out:
++ dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x "
++ "fsta:0x%08x dcnt:0x%08x status:%s.\n",
++ mci_csta, mci_dsta, mci_fsta,
++ mci_dcnt, host->status);
++
++ spin_unlock_irqrestore(&host->complete_lock, iflags);
++ return IRQ_HANDLED;
++
++}
++
++/*
++ * ISR for the CardDetect Pin
++*/
++
++static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
++{
++ struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
++
++ dbg(host, dbg_irq, "card detect\n");
++
++ mmc_detect_change(host->mmc, 500);
++
++ return IRQ_HANDLED;
++}
++
++void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
++ int size, enum s3c2410_dma_buffresult result)
++{
++ unsigned long iflags;
++ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
++ struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
++
++ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
++ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
++ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
++ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
++
++ if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
++ return;
++
++ if (!host->dmatogo)
++ return;
++
++ spin_lock_irqsave(&host->complete_lock, iflags);
++
++ if (result != S3C2410_RES_OK) {
++ dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
++ "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
++ mci_csta, mci_dsta, mci_fsta,
++ mci_dcnt, result, host->dmatogo);
++
++ goto fail_request;
++ }
++
++ host->dmatogo--;
++ if (host->dmatogo) {
++ dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
++ "DCNT:[%08x] toGo:%u\n",
++ size, mci_dsta, mci_dcnt, host->dmatogo);
++
++ goto out;
++ }
++
++ dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
++ size, mci_dsta, mci_dcnt);
++
++ host->complete_what = COMPLETION_FINALIZE;
++
++out:
++ tasklet_schedule(&host->pio_tasklet);
++ spin_unlock_irqrestore(&host->complete_lock, iflags);
++ return;
++
++
++fail_request:
++ host->mrq->data->error = MMC_ERR_DMA;
++ host->complete_what = COMPLETION_FINALIZE;
++ writel(0, host->base + host->sdiimsk);
++ goto out;
++
++}
++
++static void finalize_request(struct s3cmci_host *host)
++{
++ struct mmc_request *mrq = host->mrq;
++ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
++ int debug_as_failure = 0;
++
++ if (host->complete_what != COMPLETION_FINALIZE)
++ return;
++
++ if (!mrq)
++ return;
++
++ if (cmd->data && (cmd->error == MMC_ERR_NONE) &&
++ (cmd->data->error == MMC_ERR_NONE)) {
++
++ if (host->dodma && (!host->dma_complete)) {
++ dbg(host, dbg_dma, "DMA Missing!\n");
++ return;
++ }
++ }
++
++ // Read response
++ cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
++ cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
++ cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
++ cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
++
++ // reset clock speed, as it could still be set low for
++ writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++ if (cmd->error)
++ debug_as_failure = 1;
++
++ if (cmd->data && cmd->data->error)
++ debug_as_failure = 1;
++
++ //if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
++
++#ifdef CONFIG_MMC_DEBUG
++ dbg_dumpcmd(host, cmd, debug_as_failure);
++#endif
++ //Cleanup controller
++ writel(0, host->base + S3C2410_SDICMDARG);
++ writel(0, host->base + S3C2410_SDIDCON);
++ writel(0, host->base + S3C2410_SDICMDCON);
++ writel(0, host->base + host->sdiimsk);
++
++ if (cmd->data && cmd->error)
++ cmd->data->error = cmd->error;
++
++ if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
++ host->cmd_is_stop = 1;
++ s3cmci_send_request(host->mmc);
++ return;
++ }
++
++ // If we have no data transfer we are finished here
++ if (!mrq->data)
++ goto request_done;
++
++ // Calulate the amout of bytes transfer, but only if there was
++ // no error
++ if (mrq->data->error == MMC_ERR_NONE) {
++ mrq->data->bytes_xfered =
++ (mrq->data->blocks * mrq->data->blksz);
++ } else {
++ mrq->data->bytes_xfered = 0;
++ }
++
++ // If we had an error while transfering data we flush the
++ // DMA channel and the fifo to clear out any garbage
++ if (mrq->data->error != MMC_ERR_NONE) {
++ if (host->dodma)
++ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++
++ if (host->is2440) {
++ //Clear failure register and reset fifo
++ writel(S3C2440_SDIFSTA_FIFORESET |
++ S3C2440_SDIFSTA_FIFOFAIL,
++ host->base + S3C2410_SDIFSTA);
++ } else {
++ u32 mci_con;
++
++ //reset fifo
++ mci_con = readl(host->base + S3C2410_SDICON);
++ mci_con|= S3C2410_SDICON_FIFORESET;
++
++ writel(mci_con, host->base + S3C2410_SDICON);
++ }
++ }
++
++request_done:
++ host->complete_what = COMPLETION_NONE;
++ host->mrq = NULL;
++ mmc_request_done(host->mmc, mrq);
++}
++
++
++void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source)
++{
++ static int setup_ok = 0;
++ static enum s3c2410_dmasrc last_source = -1;
++
++ if (last_source == source)
++ return;
++
++ last_source = source;
++
++ s3c2410_dma_devconfig(host->dma, source, 3,
++ host->mem->start + host->sdidata);
++
++ if (!setup_ok) {
++ s3c2410_dma_config(host->dma, 4,
++ (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
++ s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);
++ s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
++ setup_ok = 1;
++ }
++}
++
++static void s3cmci_send_command(struct s3cmci_host *host,
++ struct mmc_command *cmd)
++{
++ u32 ccon, imsk;
++
++ imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
++ S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
++ S3C2410_SDIIMSK_RESPONSECRC;
++
++ enable_imask(host, imsk);
++
++ if (cmd->data) {
++ host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
++ } else if (cmd->flags & MMC_RSP_PRESENT) {
++ host->complete_what = COMPLETION_RSPFIN;
++ } else {
++ host->complete_what = COMPLETION_CMDSENT;
++ }
++
++ writel(cmd->arg, host->base + S3C2410_SDICMDARG);
++
++ ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
++ ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
++
++ if (cmd->flags & MMC_RSP_PRESENT)
++ ccon |= S3C2410_SDICMDCON_WAITRSP;
++
++ if (cmd->flags & MMC_RSP_136)
++ ccon|= S3C2410_SDICMDCON_LONGRSP;
++
++ writel(ccon, host->base + S3C2410_SDICMDCON);
++}
++
++static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
++{
++ u32 dcon, imsk, stoptries=3;
++
++ /* write DCON register */
++
++ if (!data) {
++ writel(0, host->base + S3C2410_SDIDCON);
++ return 0;
++ }
++
++ while(readl(host->base + S3C2410_SDIDSTA) &
++ (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
++
++ dbg(host, dbg_err,
++ "mci_setup_data() transfer stillin progress.\n");
++
++ writel(0, host->base + S3C2410_SDIDCON);
++ s3cmci_reset(host);
++
++ if (0 == (stoptries--)) {
++#ifdef CONFIG_MMC_DEBUG
++ dbg_dumpregs(host, "DRF");
++#endif
++
++ return -EINVAL;
++ }
++ }
++
++ dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
++
++ if (host->dodma) {
++ dcon |= S3C2410_SDIDCON_DMAEN;
++ }
++
++ if (host->bus_width == MMC_BUS_WIDTH_4) {
++ dcon |= S3C2410_SDIDCON_WIDEBUS;
++ }
++
++ if (!(data->flags & MMC_DATA_STREAM)) {
++ dcon |= S3C2410_SDIDCON_BLOCKMODE;
++ }
++
++ if (data->flags & MMC_DATA_WRITE) {
++ dcon |= S3C2410_SDIDCON_TXAFTERRESP;
++ dcon |= S3C2410_SDIDCON_XFER_TXSTART;
++ }
++
++ if (data->flags & MMC_DATA_READ) {
++ dcon |= S3C2410_SDIDCON_RXAFTERCMD;
++ dcon |= S3C2410_SDIDCON_XFER_RXSTART;
++ }
++
++ if (host->is2440) {
++ dcon |= S3C2440_SDIDCON_DS_WORD;
++ dcon |= S3C2440_SDIDCON_DATSTART;
++ }
++
++ writel(dcon, host->base + S3C2410_SDIDCON);
++
++ /* write BSIZE register */
++
++ writel(data->blksz, host->base + S3C2410_SDIBSIZE);
++
++ /* add to IMASK register */
++ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
++ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
++
++ enable_imask(host, imsk);
++
++ /* write TIMER register */
++
++ if (host->is2440) {
++ writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
++ } else {
++ writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
++
++ //FIX: set slow clock to prevent timeouts on read
++ if (data->flags & MMC_DATA_READ) {
++ writel(0xFF, host->base + S3C2410_SDIPRE);
++ }
++ }
++
++ //debug_dump_registers(host, "Data setup:");
++
++ return 0;
++}
++
++static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
++{
++ int rw = (data->flags & MMC_DATA_WRITE)?1:0;
++
++ if (rw != ((data->flags & MMC_DATA_READ)?0:1))
++ return -EINVAL;
++
++ host->pio_sgptr = 0;
++ host->pio_words = 0;
++ host->pio_count = 0;
++ host->pio_active = rw?XFER_WRITE:XFER_READ;
++
++ if (rw) {
++ do_pio_write(host);
++ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
++ } else {
++ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
++ | S3C2410_SDIIMSK_RXFIFOLAST);
++ }
++
++ return 0;
++}
++
++static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
++{
++ int dma_len, i;
++
++ int rw = (data->flags & MMC_DATA_WRITE)?1:0;
++
++ if (rw != ((data->flags & MMC_DATA_READ)?0:1))
++ return -EINVAL;
++
++ s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
++ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++
++ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
++ (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
++
++
++ if (dma_len == 0)
++ return -ENOMEM;
++
++ host->dma_complete = 0;
++ host->dmatogo = dma_len;
++
++ for (i = 0; i < dma_len; i++) {
++ int res;
++
++ dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
++ sg_dma_address(&data->sg[i]),
++ sg_dma_len(&data->sg[i]));
++
++ res = s3c2410_dma_enqueue(host->dma, (void *) host,
++ sg_dma_address(&data->sg[i]),
++ sg_dma_len(&data->sg[i]));
++
++ if (res) {
++ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
++ return -EBUSY;
++ }
++ }
++
++ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
++
++ return 0;
++}
++
++static void s3cmci_send_request(struct mmc_host *mmc)
++{
++ struct s3cmci_host *host = mmc_priv(mmc);
++ struct mmc_request *mrq = host->mrq;
++ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
++
++ host->ccnt++;
++#ifdef CONFIG_MMC_DEBUG
++ prepare_dbgmsg(host, cmd, host->cmd_is_stop);
++#endif
++ //Clear command, data and fifo status registers
++ //Fifo clear only necessary on 2440, but doesn't hurt on 2410
++ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
++ writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
++ writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
++
++ if (cmd->data) {
++ int res;
++ res = s3cmci_setup_data(host, cmd->data);
++
++ host->dcnt++;
++
++ if (res) {
++ cmd->error = MMC_ERR_DMA;
++ cmd->data->error = MMC_ERR_DMA;
++
++ mmc_request_done(mmc, mrq);
++ return;
++ }
++
++
++ if (host->dodma) {
++ res = s3cmci_prepare_dma(host, cmd->data);
++ } else {
++ res = s3cmci_prepare_pio(host, cmd->data);
++ }
++
++ if (res) {
++ cmd->error = MMC_ERR_DMA;
++ cmd->data->error = MMC_ERR_DMA;
++
++ mmc_request_done(mmc, mrq);
++ return;
++ }
++
++ }
++
++ // Send command
++ s3cmci_send_command(host, cmd);
++
++ // Enable Interrupt
++ enable_irq(host->irq);
++}
++
++static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++ struct s3cmci_host *host = mmc_priv(mmc);
++
++ host->cmd_is_stop = 0;
++ host->mrq = mrq;
++
++ s3cmci_send_request(mmc);
++}
++
++static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++ struct s3cmci_host *host = mmc_priv(mmc);
++ u32 mci_psc, mci_con;
++
++ //Set power
++ mci_con = readl(host->base + S3C2410_SDICON);
++ switch(ios->power_mode) {
++ case MMC_POWER_ON:
++ case MMC_POWER_UP:
++ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
++ s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
++ s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
++ s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
++ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
++ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
++
++ if (!host->is2440)
++ mci_con|=S3C2410_SDICON_FIFORESET;
++
++ break;
++
++ case MMC_POWER_OFF:
++ default:
++ s3c2410_gpio_setpin(S3C2410_GPE5, 0);
++ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
++
++ if (host->is2440)
++ mci_con|=S3C2440_SDICON_SDRESET;
++
++ break;
++ }
++
++ //Set clock
++ for (mci_psc=0; mci_psc<255; mci_psc++) {
++ host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
++
++ if (host->real_rate <= ios->clock)
++ break;
++ }
++
++ if(mci_psc > 255) mci_psc = 255;
++ host->prescaler = mci_psc;
++
++ writel(host->prescaler, host->base + S3C2410_SDIPRE);
++
++ //If requested clock is 0, real_rate will be 0, too
++ if (ios->clock == 0)
++ host->real_rate = 0;
++
++ //Set CLOCK_ENABLE
++ if (ios->clock)
++ mci_con |= S3C2410_SDICON_CLOCKTYPE;
++ else
++ mci_con &=~S3C2410_SDICON_CLOCKTYPE;
++
++ writel(mci_con, host->base + S3C2410_SDICON);
++
++ if ((ios->power_mode==MMC_POWER_ON)
++ || (ios->power_mode==MMC_POWER_UP)) {
++
++ dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
++ host->real_rate/1000, ios->clock/1000);
++ } else {
++ dbg(host, dbg_conf, "powered down.\n");
++ }
++
++ host->bus_width = ios->bus_width;
++
++}
++
++static void s3cmci_reset(struct s3cmci_host *host)
++{
++ u32 con = readl(host->base + S3C2410_SDICON);
++
++ con |= S3C2440_SDICON_SDRESET;
++
++ writel(con, host->base + S3C2410_SDICON);
++}
++
++static struct mmc_host_ops s3cmci_ops = {
++ .request = s3cmci_request,
++ .set_ios = s3cmci_set_ios,
++};
++
++static int s3cmci_probe(struct platform_device *pdev, int is2440)
++{
++ struct mmc_host *mmc;
++ struct s3cmci_host *host;
++
++ int ret;
++
++ mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
++ if (!mmc) {
++ ret = -ENOMEM;
++ goto probe_out;
++ }
++
++ host = mmc_priv(mmc);
++ host->mmc = mmc;
++ host->pdev = pdev;
++
++ spin_lock_init(&host->complete_lock);
++ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
++ if (is2440) {
++ host->is2440 = 1;
++ host->sdiimsk = S3C2440_SDIIMSK;
++ host->sdidata = S3C2440_SDIDATA;
++ host->clk_div = 1;
++ } else {
++ host->is2440 = 0;
++ host->sdiimsk = S3C2410_SDIIMSK;
++ host->sdidata = S3C2410_SDIDATA;
++ host->clk_div = 2;
++ }
++ host->dodma = 0;
++ host->complete_what = COMPLETION_NONE;
++ host->pio_active = XFER_NONE;
++
++ host->dma = S3CMCI_DMA;
++ host->irq_cd = IRQ_EINT2;
++
++ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!host->mem) {
++ dev_err(&pdev->dev,
++ "failed to get io memory region resouce.\n");
++
++ ret = -ENOENT;
++ goto probe_free_host;
++ }
++
++ host->mem = request_mem_region(host->mem->start,
++ RESSIZE(host->mem), pdev->name);
++
++ if (!host->mem) {
++ dev_err(&pdev->dev, "failed to request io memory region.\n");
++ ret = -ENOENT;
++ goto probe_free_host;
++ }
++
++ host->base = ioremap(host->mem->start, RESSIZE(host->mem));
++ if (host->base == 0) {
++ dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
++ ret = -EINVAL;
++ goto probe_free_mem_region;
++ }
++
++ host->irq = platform_get_irq(pdev, 0);
++ if (host->irq == 0) {
++ dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
++ ret = -EINVAL;
++ goto probe_iounmap;
++ }
++
++ if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
++ dev_err(&pdev->dev, "failed to request mci interrupt.\n");
++ ret = -ENOENT;
++ goto probe_iounmap;
++ }
++
++ disable_irq(host->irq);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
++ set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
++
++ if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
++ dev_err(&pdev->dev,
++ "failed to request card detect interrupt.\n");
++
++ ret = -ENOENT;
++ goto probe_free_irq;
++ }
++
++ if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
++ dev_err(&pdev->dev, "unable to get DMA channel.\n");
++ ret = -EBUSY;
++ goto probe_free_irq_cd;
++ }
++
++ host->clk = clk_get(&pdev->dev, "sdi");
++ if (IS_ERR(host->clk)) {
++ dev_err(&pdev->dev, "failed to find clock source.\n");
++ ret = PTR_ERR(host->clk);
++ host->clk = NULL;
++ goto probe_free_host;
++ }
++
++ if ((ret = clk_enable(host->clk))) {
++ dev_err(&pdev->dev, "failed to enable clock source.\n");
++ goto clk_free;
++ }
++
++ host->clk_rate = clk_get_rate(host->clk);
++
++ mmc->ops = &s3cmci_ops;
++ mmc->ocr_avail = MMC_VDD_32_33;
++ mmc->caps = MMC_CAP_4_BIT_DATA;
++ mmc->f_min = host->clk_rate / (host->clk_div * 256);
++ mmc->f_max = host->clk_rate / host->clk_div;
++
++ mmc->max_blk_count = 4095;
++ mmc->max_blk_size = 4095;
++ mmc->max_req_size = 4095 * 512;
++ mmc->max_seg_size = mmc->max_req_size;
++
++ mmc->max_phys_segs = 128;
++ mmc->max_hw_segs = 128;
++
++ dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
++ (host->is2440?"2440":""),
++ host->base, host->irq, host->irq_cd, host->dma);
++
++ if ((ret = mmc_add_host(mmc))) {
++ dev_err(&pdev->dev, "failed to add mmc host.\n");
++ goto free_dmabuf;
++ }
++
++ platform_set_drvdata(pdev, mmc);
++
++ dev_info(&pdev->dev,"initialisation done.\n");
++ return 0;
++
++ free_dmabuf:
++ clk_disable(host->clk);
++
++ clk_free:
++ clk_put(host->clk);
++
++ probe_free_irq_cd:
++ free_irq(host->irq_cd, host);
++
++ probe_free_irq:
++ free_irq(host->irq, host);
++
++ probe_iounmap:
++ iounmap(host->base);
++
++ probe_free_mem_region:
++ release_mem_region(host->mem->start, RESSIZE(host->mem));
++
++ probe_free_host:
++ mmc_free_host(mmc);
++ probe_out:
++ return ret;
++}
++
++static int s3cmci_remove(struct platform_device *pdev)
++{
++ struct mmc_host *mmc = platform_get_drvdata(pdev);
++ struct s3cmci_host *host = mmc_priv(mmc);
++
++ mmc_remove_host(mmc);
++ clk_disable(host->clk);
++ clk_put(host->clk);
++ free_irq(host->irq_cd, host);
++ free_irq(host->irq, host);
++ iounmap(host->base);
++ release_mem_region(host->mem->start, RESSIZE(host->mem));
++ mmc_free_host(mmc);
++
++ return 0;
++}
++
++static int s3cmci_probe_2410(struct platform_device *dev)
++{
++ return s3cmci_probe(dev, 0);
++}
++
++static int s3cmci_probe_2412(struct platform_device *dev)
++{
++ return s3cmci_probe(dev, 1);
++}
++
++static int s3cmci_probe_2440(struct platform_device *dev)
++{
++ return s3cmci_probe(dev, 1);
++}
++
++#ifdef CONFIG_PM
++
++static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct mmc_host *mmc = platform_get_drvdata(dev);
++
++ return mmc_suspend_host(mmc, state);
++}
++
++static int s3cmci_resume(struct platform_device *dev)
++{
++ struct mmc_host *mmc = platform_get_drvdata(dev);
++
++ return mmc_resume_host(mmc);
++}
++
++#else /* CONFIG_PM */
++#define s3cmci_suspend NULL
++#define s3cmci_resume NULL
++#endif /* CONFIG_PM */
++
++
++static struct platform_driver s3cmci_driver_2410 =
++{
++ .driver.name = "s3c2410-sdi",
++ .probe = s3cmci_probe_2410,
++ .remove = s3cmci_remove,
++ .suspend = s3cmci_suspend,
++ .resume = s3cmci_resume,
++};
++
++static struct platform_driver s3cmci_driver_2412 =
++{
++ .driver.name = "s3c2412-sdi",
++ .probe = s3cmci_probe_2412,
++ .remove = s3cmci_remove,
++ .suspend = s3cmci_suspend,
++ .resume = s3cmci_resume,
++};
++
++static struct platform_driver s3cmci_driver_2440 =
++{
++ .driver.name = "s3c2440-sdi",
++ .probe = s3cmci_probe_2440,
++ .remove = s3cmci_remove,
++ .suspend = s3cmci_suspend,
++ .resume = s3cmci_resume,
++};
++
++
++static int __init s3cmci_init(void)
++{
++ platform_driver_register(&s3cmci_driver_2410);
++ platform_driver_register(&s3cmci_driver_2412);
++ platform_driver_register(&s3cmci_driver_2440);
++ return 0;
++}
++
++static void __exit s3cmci_exit(void)
++{
++ platform_driver_unregister(&s3cmci_driver_2410);
++ platform_driver_unregister(&s3cmci_driver_2412);
++ platform_driver_unregister(&s3cmci_driver_2440);
++}
++
++module_init(s3cmci_init);
++module_exit(s3cmci_exit);
++
++MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Thomas Kleffel <tk at maintech.de>");
++
+Index: linux-2.6.22.1/drivers/mmc/host/s3cmci.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.h 2007-07-19 01:13:22.334959182 +0200
+@@ -0,0 +1,71 @@
++/*
++ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
++ *
++ * Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++//FIXME: DMA Resource management ?!
++#define S3CMCI_DMA 0
++
++enum s3cmci_waitfor {
++ COMPLETION_NONE,
++ COMPLETION_FINALIZE,
++ COMPLETION_CMDSENT,
++ COMPLETION_RSPFIN,
++ COMPLETION_XFERFINISH,
++ COMPLETION_XFERFINISH_RSPFIN,
++};
++
++struct s3cmci_host {
++ struct platform_device *pdev;
++ struct mmc_host *mmc;
++ struct resource *mem;
++ struct clk *clk;
++ void __iomem *base;
++ int irq;
++ int irq_cd;
++ int dma;
++
++ unsigned long clk_rate;
++ unsigned long clk_div;
++ unsigned long real_rate;
++ u8 prescaler;
++
++ int is2440;
++ unsigned sdiimsk;
++ unsigned sdidata;
++ int dodma;
++
++ volatile int dmatogo;
++
++ struct mmc_request *mrq;
++ int cmd_is_stop;
++
++ spinlock_t complete_lock;
++ volatile enum s3cmci_waitfor
++ complete_what;
++
++ volatile int dma_complete;
++
++ volatile u32 pio_sgptr;
++ volatile u32 pio_words;
++ volatile u32 pio_count;
++ volatile u32 *pio_ptr;
++#define XFER_NONE 0
++#define XFER_READ 1
++#define XFER_WRITE 2
++ volatile u32 pio_active;
++
++ int bus_width;
++
++ char dbgmsg_cmd[301];
++ char dbgmsg_dat[301];
++ volatile char *status;
++
++ unsigned int ccnt, dcnt;
++ struct tasklet_struct pio_tasklet;
++};
+Index: linux-2.6.22.1/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/Kconfig 2007-07-19 01:10:43.497907574 +0200
++++ linux-2.6.22.1/drivers/mmc/host/Kconfig 2007-07-19 01:11:57.214108422 +0200
+@@ -100,3 +100,14 @@
+ To compile this driver as a module, choose M here: the
+ module will be called tifm_sd.
+
++config MMC_S3C
++ tristate "Samsung S3C SD/MMC Card Interface support"
++ depends on ARCH_S3C2410 && MMC
++ help
++ This selects a driver for the MCI interface found in
++ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
++ If you have a board based on one of those and a MMC/SD
++ slot, say Y or M here.
++
++ If unsure, say N.
++
+Index: linux-2.6.22.1/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/Makefile 2007-07-19 01:10:43.517908714 +0200
++++ linux-2.6.22.1/drivers/mmc/host/Makefile 2007-07-19 01:11:07.043249347 +0200
+@@ -15,4 +15,6 @@
+ obj-$(CONFIG_MMC_OMAP) += omap.o
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
++obj-$(CONFIG_MMC_S3C) += s3cmci.o
++obj-$(CONFIG_MMC) += mmc_debug.o
+
Added: developers/nbd/patches-2.6.22/820-s3cmci_dbg.patch
===================================================================
--- developers/nbd/patches-2.6.22/820-s3cmci_dbg.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/820-s3cmci_dbg.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,28 @@
+This patch is a workaround of some S3C2410 MMC chip bug
+
+Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mmc/host/s3cmci.c 2007-01-24 12:17:51.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mmc/host/s3cmci.c 2007-01-24 12:17:52.000000000 +0100
+@@ -445,11 +445,17 @@
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+ if (cmd->flags & MMC_RSP_CRC) {
+- cmd->error = MMC_ERR_BADCRC;
+- host->status = "error: bad command crc";
+- goto fail_transfer;
++ if (host->mrq->cmd->flags & MMC_RSP_136) {
++ dbg(host, dbg_irq,
++ "fixup: ignore CRC fail with long rsp\n");
++ } else {
++#if 0
++ cmd->error = MMC_ERR_BADCRC;
++ host->status = "error: bad command crc";
++ goto fail_transfer;
++#endif
++ }
+ }
+-
+ mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+ }
+
Added: developers/nbd/patches-2.6.22/830-s3cmci-dma-free.patch
===================================================================
--- developers/nbd/patches-2.6.22/830-s3cmci-dma-free.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/830-s3cmci-dma-free.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,12 @@
+Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mmc/host/s3cmci.c 2007-01-24 12:17:52.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mmc/host/s3cmci.c 2007-01-24 12:17:57.000000000 +0100
+@@ -1247,6 +1247,7 @@
+ mmc_remove_host(mmc);
+ clk_disable(host->clk);
+ clk_put(host->clk);
++ s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
+ free_irq(host->irq_cd, host);
+ free_irq(host->irq, host);
+ iounmap(host->base);
Added: developers/nbd/patches-2.6.22/840-s3cmci-stop-fix.patch
===================================================================
--- developers/nbd/patches-2.6.22/840-s3cmci-stop-fix.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/840-s3cmci-stop-fix.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,27 @@
+This patch from Thomas Kleffel (the author of s3cmci) should fix our
+SD/MMC instability problems.
+
+Signed-off-by: Harald Welte <laforge at openmoko.org>
+
+Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
+===================================================================
+--- linux-2.6.17.14-fic4.test.orig/drivers/mmc/host/s3cmci.c 2007-02-07 20:39:12.000000000 +0100
++++ linux-2.6.17.14-fic4.test/drivers/mmc/host/s3cmci.c 2007-02-10 02:31:13.000000000 +0100
+@@ -667,7 +667,7 @@
+ #endif
+ //Cleanup controller
+ writel(0, host->base + S3C2410_SDICMDARG);
+- writel(0, host->base + S3C2410_SDIDCON);
++ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+ writel(0, host->base + S3C2410_SDICMDCON);
+ writel(0, host->base + host->sdiimsk);
+
+@@ -794,7 +794,7 @@
+ dbg(host, dbg_err,
+ "mci_setup_data() transfer stillin progress.\n");
+
+- writel(0, host->base + S3C2410_SDIDCON);
++ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
+ s3cmci_reset(host);
+
+ if (0 == (stoptries--)) {
Added: developers/nbd/patches-2.6.22/850-s3c_mci_platform.patch
===================================================================
--- developers/nbd/patches-2.6.22/850-s3c_mci_platform.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/850-s3c_mci_platform.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,143 @@
+This patch adds platform data support to the s3mci driver. This allows
+flexible board-specific configuration of set_power, card detect and read only
+pins.
+Index: linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.c 2007-07-19 00:27:24.937824075 +0200
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c 2007-07-19 00:27:45.823014255 +0200
+@@ -22,6 +22,7 @@
+ #include <asm/io.h>
+ #include <asm/arch/regs-sdi.h>
+ #include <asm/arch/regs-gpio.h>
++#include <asm/arch/mci.h>
+
+ #include "mmc_debug.h"
+ #include "s3cmci.h"
+@@ -1012,6 +1013,9 @@
+ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
++ if (host->pdata->set_power)
++ host->pdata->set_power(ios->power_mode, ios->vdd);
++
+ if (!host->is2440)
+ mci_con|=S3C2410_SDICON_FIFORESET;
+
+@@ -1022,6 +1026,9 @@
+ s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+
++ if (host->pdata->set_power)
++ host->pdata->set_power(ios->power_mode, ios->vdd);
++
+ if (host->is2440)
+ mci_con|=S3C2440_SDICON_SDRESET;
+
+@@ -1075,9 +1082,26 @@
+ writel(con, host->base + S3C2410_SDICON);
+ }
+
++static int s3cmci_get_ro(struct mmc_host *mmc)
++{
++ struct s3cmci_host *host = mmc_priv(mmc);
++
++ if (host->pdata->gpio_wprotect == 0)
++ return 0;
++
++ return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
++}
++
+ static struct mmc_host_ops s3cmci_ops = {
+ .request = s3cmci_request,
+ .set_ios = s3cmci_set_ios,
++ .get_ro = s3cmci_get_ro,
++};
++
++static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
++ .gpio_detect = 0,
++ .set_power = NULL,
++ .ocr_avail = MMC_VDD_32_33,
+ };
+
+ static int s3cmci_probe(struct platform_device *pdev, int is2440)
+@@ -1097,6 +1121,12 @@
+ host->mmc = mmc;
+ host->pdev = pdev;
+
++ host->pdata = pdev->dev.platform_data;
++ if (!host->pdata) {
++ pdev->dev.platform_data = &s3cmci_def_pdata;
++ host->pdata = &s3cmci_def_pdata;
++ }
++
+ spin_lock_init(&host->complete_lock);
+ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ if (is2440) {
+@@ -1115,7 +1145,8 @@
+ host->pio_active = XFER_NONE;
+
+ host->dma = S3CMCI_DMA;
+- host->irq_cd = IRQ_EINT2;
++ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
++ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->mem) {
+@@ -1157,7 +1188,7 @@
+
+ disable_irq(host->irq);
+
+- s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
++ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
+ set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+
+ if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
+@@ -1168,6 +1199,10 @@
+ goto probe_free_irq;
+ }
+
++ if (host->pdata->gpio_wprotect)
++ s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
++ S3C2410_GPIO_INPUT);
++
+ if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+ dev_err(&pdev->dev, "unable to get DMA channel.\n");
+ ret = -EBUSY;
+@@ -1190,7 +1225,7 @@
+ host->clk_rate = clk_get_rate(host->clk);
+
+ mmc->ops = &s3cmci_ops;
+- mmc->ocr_avail = MMC_VDD_32_33;
++ mmc->ocr_avail = host->pdata->ocr_avail;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->f_min = host->clk_rate / (host->clk_div * 256);
+ mmc->f_max = host->clk_rate / host->clk_div;
+Index: linux-2.6.22.1/drivers/mmc/host/s3cmci.h
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.h 2007-07-19 00:25:34.647538991 +0200
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.h 2007-07-19 00:27:45.831014710 +0200
+@@ -22,6 +22,7 @@
+
+ struct s3cmci_host {
+ struct platform_device *pdev;
++ struct s3c24xx_mci_pdata *pdata;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct clk *clk;
+Index: linux-2.6.22.1/include/asm-arm/arch-s3c2410/mci.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/mci.h 2007-07-19 00:27:45.859016308 +0200
+@@ -0,0 +1,12 @@
++#ifndef _ARCH_MCI_H
++#define _ARCH_MCI_H
++
++struct s3c24xx_mci_pdata {
++ unsigned int gpio_detect;
++ unsigned int gpio_wprotect;
++ unsigned long ocr_avail;
++ void (*set_power)(unsigned char power_mode,
++ unsigned short vdd);
++};
++
++#endif /* _ARCH_NCI_H */
Added: developers/nbd/patches-2.6.22/860-missing_defs.patch
===================================================================
--- developers/nbd/patches-2.6.22/860-missing_defs.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/860-missing_defs.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,134 @@
+Index: linux-2.6.22.1/include/linux/mmc/core.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/core.h 2007-07-19 00:30:53.705721081 +0200
++++ linux-2.6.22.1/include/linux/mmc/core.h 2007-07-19 00:30:57.097914392 +0200
+@@ -56,12 +56,15 @@
+ unsigned int retries; /* max number of retries */
+ unsigned int error; /* command error */
+
+-#define MMC_ERR_NONE 0
+-#define MMC_ERR_TIMEOUT 1
+-#define MMC_ERR_BADCRC 2
+-#define MMC_ERR_FIFO 3
+-#define MMC_ERR_FAILED 4
+-#define MMC_ERR_INVALID 5
++#define MMC_ERR_NONE 0
++#define MMC_ERR_TIMEOUT 1
++#define MMC_ERR_BADCRC 2
++#define MMC_ERR_FIFO 3
++#define MMC_ERR_DMA 4
++#define MMC_ERR_BUSY 5
++#define MMC_ERR_FAILED 6
++#define MMC_ERR_INVALID 7
++#define MMC_ERR_CANCELED 8
+
+ struct mmc_data *data; /* data segment associated with cmd */
+ struct mmc_request *mrq; /* associated request */
+Index: linux-2.6.22.1/include/linux/mmc/mmc.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/mmc.h 2007-07-19 00:30:50.197521158 +0200
++++ linux-2.6.22.1/include/linux/mmc/mmc.h 2007-07-19 00:30:57.133916442 +0200
+@@ -78,6 +78,21 @@
+ #define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
+ #define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
+
++/* SD commands type argument response */
++ /* class 0 */
++/* This is basically the same command as for MMC with some quirks. */
++#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
++#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
++
++ /* class 10 */
++#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
++
++ /* Application commands */
++#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */
++#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */
++#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
++#define SD_APP_SEND_SCR 51 /* adtc R1 */
++
+ /*
+ * MMC_SWITCH argument format:
+ *
+@@ -90,6 +105,27 @@
+ */
+
+ /*
++ * SD_SWITCH argument format:
++ *
++ * [31] Check (0) or switch (1)
++ * [30:24] Reserved (0)
++ * [23:20] Function group 6
++ * [19:16] Function group 5
++ * [15:12] Function group 4
++ * [11:8] Function group 3
++ * [7:4] Function group 2
++ * [3:0] Function group 1
++ */
++
++/*
++ * SD_SEND_IF_COND argument format:
++ *
++ * [31:12] Reserved (0)
++ * [11:8] Host Voltage Supply Flags
++ * [7:0] Check Pattern (0xAA)
++ */
++
++/*
+ MMC status in R1
+ Type
+ e : error bit
+@@ -172,9 +208,30 @@
+ u8 ecc;
+ };
+
+-/*
+- * OCR bits are mostly in host.h
+- */
++#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */
++#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */
++#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */
++#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */
++#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */
++#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */
++#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */
++#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */
++#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
++#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
++#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
++#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
++#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
++#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
++#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
++#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
++#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
++#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
++#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
++#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
++#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
++#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
++#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
++#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
+ #define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */
+
+ /*
+@@ -253,5 +310,19 @@
+ #define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */
+ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */
+
++/*
++ * SCR field definitions
++ */
++
++#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
++#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
++#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
++
++/*
++ * SD bus widths
++ */
++#define SD_BUS_WIDTH_1 0
++#define SD_BUS_WIDTH_4 2
++
+ #endif /* MMC_MMC_PROTOCOL_H */
+
Added: developers/nbd/patches-2.6.22/870-missing_export.patch
===================================================================
--- developers/nbd/patches-2.6.22/870-missing_export.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/870-missing_export.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,12 @@
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_io.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_io.c 2007-07-19 11:00:35.214766577 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio_io.c 2007-07-19 11:00:50.079613676 +0200
+@@ -242,6 +242,7 @@
+ return mmc_io_rw_extended(func->card, 1, func->num, addr, 0, src,
+ count);
+ }
++EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
+
+ /**
+ * sdio_readsb - read from a FIFO on a SDIO function
Added: developers/nbd/patches-2.6.22/880-sdio_ops.patch
===================================================================
--- developers/nbd/patches-2.6.22/880-sdio_ops.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/880-sdio_ops.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,258 @@
+Index: linux-2.6.22.1/drivers/mmc/core/core.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/core.c 2007-07-20 14:39:27.222772468 +0200
++++ linux-2.6.22.1/drivers/mmc/core/core.c 2007-07-20 14:39:43.679710296 +0200
+@@ -25,6 +25,7 @@
+ #include <linux/mmc/host.h>
+ #include <linux/mmc/mmc.h>
+ #include <linux/mmc/sd.h>
++#include <linux/mmc/sdio_ops.h>
+
+ #include "core.h"
+ #include "bus.h"
+@@ -33,7 +34,6 @@
+
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
+-#include "sdio_ops.h"
+
+ extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
+ extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
+Index: linux-2.6.22.1/drivers/mmc/core/sdio.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio.c 2007-07-20 14:42:35.705513484 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio.c 2007-07-20 14:42:49.714311802 +0200
+@@ -15,13 +15,13 @@
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/sdio.h>
+ #include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ops.h>
+
+ #include "core.h"
+ #include "bus.h"
+ #include "sdio_bus.h"
+ #include "mmc_ops.h"
+ #include "sd_ops.h"
+-#include "sdio_ops.h"
+ #include "sdio_cis.h"
+
+ static int sdio_read_fbr(struct sdio_func *func)
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_cis.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_cis.c 2007-07-20 14:42:38.981700184 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio_cis.c 2007-07-20 14:43:08.475380931 +0200
+@@ -17,9 +17,9 @@
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/sdio.h>
+ #include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ops.h>
+
+ #include "sdio_cis.h"
+-#include "sdio_ops.h"
+
+
+ static int cistpl_vers_1(struct sdio_func *func, unsigned fn,
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_io.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_io.c 2007-07-20 14:42:39.001701324 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio_io.c 2007-07-20 14:43:28.064497256 +0200
+@@ -13,8 +13,7 @@
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/sdio.h>
+ #include <linux/mmc/sdio_func.h>
+-
+-#include "sdio_ops.h"
++#include <linux/mmc/sdio_ops.h>
+
+ /**
+ * sdio_claim_host - exclusively claim a bus for a certain SDIO function
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_irq.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_irq.c 2007-07-20 14:42:39.021702464 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio_irq.c 2007-07-20 14:43:40.577210315 +0200
+@@ -22,8 +22,7 @@
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/sdio.h>
+ #include <linux/mmc/sdio_func.h>
+-
+-#include "sdio_ops.h"
++#include <linux/mmc/sdio_ops.h>
+
+ static int process_sdio_pending_irqs(struct mmc_card *card)
+ {
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_ops.c
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_ops.c 2007-07-20 14:26:28.418390933 +0200
++++ linux-2.6.22.1/drivers/mmc/core/sdio_ops.c 2007-07-20 14:48:21.093196008 +0200
+@@ -50,6 +50,7 @@
+
+ return err;
+ }
++EXPORT_SYMBOL_GPL(mmc_send_io_op_cond);
+
+ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, u8 in, u8* out)
+@@ -90,6 +91,7 @@
+
+ return MMC_ERR_NONE;
+ }
++EXPORT_SYMBOL_GPL(mmc_io_rw_direct);
+
+ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
+ unsigned addr, int bang, u8 *buf, unsigned size)
+@@ -148,4 +150,86 @@
+
+ return MMC_ERR_NONE;
+ }
++EXPORT_SYMBOL_GPL(mmc_io_rw_extended);
+
++
++int mmc_async_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, int bang, u8 *buf, unsigned size,
++ void (*done)(struct mmc_request *), void *done_data)
++{
++ struct mmc_request *mrq;
++ struct mmc_command *cmd;
++ struct mmc_data *data;
++ struct scatterlist *sg;
++
++ BUG_ON(!card);
++ BUG_ON(fn > 7);
++ BUG_ON(size > 512);
++
++ mrq = kzalloc(sizeof(struct mmc_request), GFP_KERNEL);
++ if (!mrq)
++ goto err1;
++
++ cmd = kzalloc(sizeof(struct mmc_command), GFP_KERNEL);
++ if (!cmd)
++ goto err2;
++
++ data = kzalloc(sizeof(struct mmc_data), GFP_KERNEL);
++ if (!data)
++ goto err3;
++
++ sg = kzalloc(sizeof(struct scatterlist), GFP_KERNEL);
++ if (!sg)
++ goto err4;
++
++ mrq->cmd = cmd;
++ mrq->data = data;
++
++ cmd->opcode = SD_IO_RW_EXTENDED;
++ cmd->arg = write ? 0x80000000 : 0x00000000;
++ cmd->arg |= fn << 28;
++ cmd->arg |= bang ? 0x00000000 : 0x04000000;
++ cmd->arg |= addr << 9;
++ cmd->arg |= (size == 512) ? 0 : size;
++ cmd->flags = MMC_RSP_R5 | MMC_CMD_ADTC;
++
++ data->blksz = size;
++ data->blocks = 1;
++ data->flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
++ data->sg = sg;
++ data->sg_len = 1;
++
++ sg_init_one(sg, buf, size);
++
++ mmc_set_data_timeout(data, card, 0);
++
++ mmc_start_request(card->host, mrq);
++
++ if (cmd->error != MMC_ERR_NONE)
++ return cmd->error;
++ if (data->error != MMC_ERR_NONE)
++ return data->error;
++
++ if (cmd->resp[0] & R5_COM_CRC_ERROR)
++ return MMC_ERR_BADCRC;
++ if (cmd->resp[0] & R5_ILLEGAL_COMMAND)
++ return MMC_ERR_INVALID;
++ if (cmd->resp[0] & R5_ERROR)
++ return MMC_ERR_FAILED;
++ if (cmd->resp[0] & R5_FUNCTION_NUMBER)
++ return MMC_ERR_INVALID;
++ if (cmd->resp[0] & R5_OUT_OF_RANGE)
++ return MMC_ERR_INVALID;
++
++ return MMC_ERR_NONE;
++
++err4:
++ kfree(data);
++err3:
++ kfree(cmd);
++err2:
++ kfree(mrq);
++err1:
++ return MMC_ERR_FAILED;
++}
++EXPORT_SYMBOL_GPL(mmc_async_io_rw_extended);
+Index: linux-2.6.22.1/drivers/mmc/core/sdio_ops.h
+===================================================================
+--- linux-2.6.22.1.orig/drivers/mmc/core/sdio_ops.h 2007-07-20 14:35:52.962562473 +0200
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,22 +0,0 @@
+-/*
+- * linux/drivers/mmc/sdio_ops.c
+- *
+- * Copyright 2006-2007 Pierre Ossman
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or (at
+- * your option) any later version.
+- */
+-
+-#ifndef _MMC_SDIO_OPS_H
+-#define _MMC_SDIO_OPS_H
+-
+-int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
+-int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
+- unsigned addr, u8 in, u8* out);
+-int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
+- unsigned addr, int bang, u8 *data, unsigned size);
+-
+-#endif
+-
+Index: linux-2.6.22.1/include/linux/mmc/sdio_ops.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22.1/include/linux/mmc/sdio_ops.h 2007-07-20 14:36:59.062329284 +0200
+@@ -0,0 +1,25 @@
++/*
++ * linux/drivers/mmc/sdio_ops.c
++ *
++ * Copyright 2006-2007 Pierre Ossman
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ */
++
++#ifndef _MMC_SDIO_OPS_H
++#define _MMC_SDIO_OPS_H
++
++int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
++int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, u8 in, u8* out);
++int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, int bang, u8 *data, unsigned size);
++int mmc_async_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
++ unsigned addr, int bang, u8 *buf, unsigned size,
++ void (*done)(struct mmc_request *), void *done_data);
++
++#endif
++
+Index: linux-2.6.22.1/include/linux/mmc/core.h
+===================================================================
+--- linux-2.6.22.1.orig/include/linux/mmc/core.h 2007-07-20 14:47:08.677069250 +0200
++++ linux-2.6.22.1/include/linux/mmc/core.h 2007-07-20 14:47:19.737699558 +0200
+@@ -104,6 +104,7 @@
+ struct mmc_host;
+ struct mmc_card;
+
++extern void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq);
+ extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
+ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
+ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
Added: developers/nbd/patches-2.6.22/900-pcf50606_fix.patch
===================================================================
--- developers/nbd/patches-2.6.22/900-pcf50606_fix.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/900-pcf50606_fix.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,21 @@
+Index: linux-2.6.21.5/drivers/i2c/chips/pcf50606.c
+===================================================================
+--- linux-2.6.21.5.orig/drivers/i2c/chips/pcf50606.c 2007-07-16 12:21:35.724785054 +0200
++++ linux-2.6.21.5/drivers/i2c/chips/pcf50606.c 2007-07-16 12:21:57.510026525 +0200
+@@ -1871,12 +1871,12 @@
+ static struct i2c_driver pcf50606_driver = {
+ .driver = {
+ .name = "pcf50606",
+- .suspend= &pcf50606_suspend,
+- .resume = &pcf50606_resume,
++ .suspend= pcf50606_suspend,
++ .resume = pcf50606_resume,
+ },
+ .id = I2C_DRIVERID_PCF50606,
+- .attach_adapter = &pcf50606_attach_adapter,
+- .detach_client = &pcf50606_detach_client,
++ .attach_adapter = pcf50606_attach_adapter,
++ .detach_client = pcf50606_detach_client,
+ };
+
+ /* platform driver, since i2c devices don't have platform_data */
Added: developers/nbd/patches-2.6.22/901-pcf50633_fix.patch
===================================================================
--- developers/nbd/patches-2.6.22/901-pcf50633_fix.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/901-pcf50633_fix.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,21 @@
+Index: linux-2.6.21.5/drivers/i2c/chips/pcf50633.c
+===================================================================
+--- linux-2.6.21.5.orig/drivers/i2c/chips/pcf50633.c 2007-07-16 12:22:46.480817213 +0200
++++ linux-2.6.21.5/drivers/i2c/chips/pcf50633.c 2007-07-16 12:23:02.773745695 +0200
+@@ -1633,12 +1633,12 @@
+ static struct i2c_driver pcf50633_driver = {
+ .driver = {
+ .name = "pcf50633",
+- .suspend= &pcf50633_suspend,
+- .resume = &pcf50633_resume,
++ .suspend= pcf50633_suspend,
++ .resume = pcf50633_resume,
+ },
+ .id = I2C_DRIVERID_PCF50633,
+- .attach_adapter = &pcf50633_attach_adapter,
+- .detach_client = &pcf50633_detach_client,
++ .attach_adapter = pcf50633_attach_adapter,
++ .detach_client = pcf50633_detach_client,
+ };
+
+ /* platform driver, since i2c devices don't have platform_data */
Added: developers/nbd/patches-2.6.22/902-qt2410_lcd.patch
===================================================================
--- developers/nbd/patches-2.6.22/902-qt2410_lcd.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/902-qt2410_lcd.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,28 @@
+Index: linux-2.6.21.5/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.21.5.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 12:39:13.341055171 +0200
++++ linux-2.6.21.5/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 12:39:39.594551274 +0200
+@@ -123,6 +123,7 @@
+ S3C2410_LCDCON5_HWSWP,
+ },
+
++ .type = S3C2410_LCDCON1_TFT,
+ .lpcsel = ((0xCE6) & ~7) | 1<<4,
+
+ .width = 640,
+@@ -174,6 +175,7 @@
+ S3C2410_LCDCON5_HWSWP,
+ },
+
++ .type = S3C2410_LCDCON1_TFT,
+ .lpcsel = ((0xCE6) & ~7) | 1<<4,
+
+ .width = 480,
+@@ -225,6 +227,7 @@
+ S3C2410_LCDCON5_HWSWP,
+ },
+
++ .type = S3C2410_LCDCON1_TFT,
+ .lpcsel = ((0xCE6) & ~7) | 1<<4,
+
+ .width = 240,
Added: developers/nbd/patches-2.6.22/903-qt2410_nand.patch
===================================================================
--- developers/nbd/patches-2.6.22/903-qt2410_nand.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/903-qt2410_nand.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,12 @@
+Index: linux-2.6.21.5/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.21.5.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 12:42:30.308279693 +0200
++++ linux-2.6.21.5/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 12:43:05.846304894 +0200
+@@ -323,6 +323,7 @@
+ /* Board devices */
+
+ static struct platform_device *qt2410_devices[] __initdata = {
++ &s3c_device_nand,
+ &s3c_device_usb,
+ &s3c_device_lcd,
+ &s3c_device_wdt,
Added: developers/nbd/patches-2.6.22/904-partitions.patch
===================================================================
--- developers/nbd/patches-2.6.22/904-partitions.patch 2007-07-27 16:41:13 UTC (rev 2539)
+++ developers/nbd/patches-2.6.22/904-partitions.patch 2007-07-27 18:59:01 UTC (rev 2540)
@@ -0,0 +1,54 @@
+Index: linux-2.6.22.1/arch/arm/mach-s3c2410/mach-qt2410.c
+===================================================================
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-16 16:03:20.770996429 +0200
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-qt2410.c 2007-07-17 00:55:11.481484184 +0200
+@@ -37,7 +37,6 @@
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+-#include <linux/mtd/partitions.h>
+
+ #include <asm/mach/arch.h>
+ #include <asm/mach/map.h>
+@@ -337,40 +336,10 @@
+ &qt2410_led,
+ };
+
+-static struct mtd_partition qt2410_nand_part[] = {
+- [0] = {
+- .name = "U-Boot",
+- .size = 0x30000,
+- .offset = 0,
+- },
+- [1] = {
+- .name = "U-Boot environment",
+- .offset = 0x30000,
+- .size = 0x4000,
+- },
+- [2] = {
+- .name = "kernel",
+- .offset = 0x34000,
+- .size = SZ_2M,
+- },
+- [3] = {
+- .name = "initrd",
+- .offset = 0x234000,
+- .size = SZ_4M,
+- },
+- [4] = {
+- .name = "jffs2",
+- .offset = 0x634000,
+- .size = 0x39cc000,
+- },
+-};
+-
+ static struct s3c2410_nand_set qt2410_nand_sets[] = {
+ [0] = {
+- .name = "NAND",
++ .name = "qt2410-nand",
+ .nr_chips = 1,
+- .nr_partitions = ARRAY_SIZE(qt2410_nand_part),
+- .partitions = qt2410_nand_part,
+ },
+ };
+
More information about the commitlog
mailing list