diff options
Diffstat (limited to 'drivers/omap_hsi/hsi_driver.h')
-rw-r--r-- | drivers/omap_hsi/hsi_driver.h | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/drivers/omap_hsi/hsi_driver.h b/drivers/omap_hsi/hsi_driver.h new file mode 100644 index 0000000..0991d98 --- /dev/null +++ b/drivers/omap_hsi/hsi_driver.h @@ -0,0 +1,398 @@ +/* + * hsi_driver.h + * + * Header file for the HSI driver low level interface. + * + * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved. + * Copyright (C) 2009 Texas Instruments, Inc. + * + * Author: Carlos Chinea <carlos.chinea@nokia.com> + * Author: Sebastien JAN <s-jan@ti.com> + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef __HSI_DRIVER_H__ +#define __HSI_DRIVER_H__ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/notifier.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/pm_runtime.h> + +#include <linux/hsi_driver_if.h> +#include <plat/omap_hsi.h> + +/* Channel states */ +#define HSI_CH_OPEN 0x01 +#define HSI_CH_RX_POLL 0x10 +#define HSI_CH_ACWAKE 0x02 /* ACWAKE line status */ + +#define HSI_CH_NUMBER_NONE 0xFF +/* + * The number of channels handled by the driver in the ports, or the highest + * port channel number (+1) used. (MAX:8 for SSI; 16 for HSI) + * Reducing this value optimizes the driver memory footprint. + */ +#define HSI_PORT_MAX_CH HSI_CHANNELS_MAX + +/* Number of DMA channels when nothing is defined for the device */ +#define HSI_DMA_CHANNEL_DEFAULT 8 + + +#define LOG_NAME "OMAP HSI: " + +/* SW strategies for HSI FIFO mapping */ +enum { + HSI_FIFO_MAPPING_UNDEF = 0, + HSI_FIFO_MAPPING_SSI, /* 8 FIFOs per port (SSI compatible mode) */ + HSI_FIFO_MAPPING_ALL_PORT1, /* ALL FIFOs mapped on 1st port */ +}; +#define HSI_FIFO_MAPPING_DEFAULT HSI_FIFO_MAPPING_ALL_PORT1 + +/* Device identifying constants */ +enum { + HSI_DRV_DEVICE_HSI, + HSI_DRV_DEVICE_SSI +}; + +/** + * struct hsi_data - HSI buffer descriptor + * @addr: pointer to the buffer where to send or receive data + * @size: size in words (32 bits) of the buffer + * @lch: associated GDD (DMA) logical channel number, if any + */ +struct hsi_data { + u32 *addr; + unsigned int size; + int lch; +}; + +/** + * struct hsi_channel - HSI channel data + * @read_data: Incoming HSI buffer descriptor + * @write_data: Outgoing HSI buffer descriptor + * @hsi_port: Reference to port where the channel belongs to + * @flags: Tracks if channel has been open + * @channel_number: HSI channel number + * @rw_lock: Read/Write lock to serialize access to callback and hsi_device + * @dev: Reference to the associated hsi_device channel + * @write_done: Callback to signal TX completed. + * @read_done: Callback to signal RX completed. + * @port_event: Callback to signal port events (RX Error, HWBREAK, CAWAKE ...) + */ +struct hsi_channel { + struct hsi_data read_data; + struct hsi_data write_data; + struct hsi_port *hsi_port; + u8 flags; + u8 channel_number; + rwlock_t rw_lock; + struct hsi_device *dev; + void (*write_done) (struct hsi_device *dev, unsigned int size); + void (*read_done) (struct hsi_device *dev, unsigned int size); + void (*port_event) (struct hsi_device *dev, unsigned int event, + void *arg); +}; + +/** + * struct hsi_port - hsi port driver data + * @hsi_channel: Array of channels in the port + * @hsi_controller: Reference to the HSI controller + * @port_number: port number + * @max_ch: maximum number of channels supported on the port + * @n_irq: HSI irq line use to handle interrupts (0 or 1) + * @irq: IRQ number + * @cawake_gpio: GPIO number for cawake line (-1 if none) + * @cawake_gpio_irq: IRQ number for cawake gpio events + * @cawake_status: Tracks CAWAKE line status + * @cawake_off_event: True if CAWAKE event was detected from OFF mode + * @acwake_status: Bitmap to track ACWAKE line status per channel + * @in_int_tasklet: True if interrupt tasklet for this port is currently running + * @in_cawake_tasklet: True if CAWAKE tasklet for this port is currently running + * @counters_on: indicates if the HSR counters are in use or not + * @reg_counters: stores the previous counters values when deactivated + * @lock: Serialize access to the port registers and internal data + * @hsi_tasklet: Bottom half for interrupts when clocks are enabled + * @cawake_tasklet: Bottom half for cawake events + */ +struct hsi_port { + struct hsi_channel hsi_channel[HSI_PORT_MAX_CH]; + struct hsi_dev *hsi_controller; + u8 flags; + u8 port_number; /* Range [1,2] */ + u8 max_ch; + u8 n_irq; + int irq; + int cawake_gpio; + int cawake_gpio_irq; + int cawake_status; + bool cawake_off_event; + unsigned int acwake_status; /* HSI_TODO : fine tune init values */ + bool in_int_tasklet; + bool in_cawake_tasklet; + int counters_on; + unsigned long reg_counters; + spinlock_t lock; /* access to the port registers and internal data */ + struct tasklet_struct hsi_tasklet; + struct tasklet_struct cawake_tasklet; /* SSI_TODO : need to replace */ + /* by a workqueue */ +}; + +/** + * struct hsi_dev - hsi controller driver data + * This structure is saved into platform_device->dev->p->driver_data + * + * @hsi_port: Array of hsi ports enabled in the controller + * @id: HSI controller platform id number + * @max_p: Number of ports enabled in the controller + * @hsi_clk: Reference to the HSI custom clock + * @base: HSI registers base virtual address + * @phy_base: HSI registers base physical address + * @lock: Serializes access to internal data and regs + * @clock_enabled: Indicates if HSI Clocks are ON + * @gdd_irq: GDD (DMA) irq number + * @fifo_mapping_strategy: Selected strategy for fifo to ports/channels mapping + * @gdd_usecount: Holds the number of ongoning DMA transfers + * @last_gdd_lch: Last used GDD logical channel + * @gdd_chan_count: Number of available DMA channels on the device (must be ^2) + * @in_dma_tasklet: True if DMA tasklet for the controller is currently running + * @set_min_bus_tput: (PM) callback to set minimun bus throuput + * @clk_notifier_register: (PM) callabck for DVFS support + * @clk_notifier_unregister: (PM) callabck for DVFS support + * @hsi_nb: (PM) Notification block for DVFS notification chain + * @hsi_gdd_tasklet: Bottom half for DMA Interrupts when clocks are enabled + * @dir: debugfs base directory + * @dev: Reference to the HSI platform device + */ +struct hsi_dev { /* HSI_TODO: should be later renamed into hsi_controller*/ + struct hsi_port hsi_port[HSI_MAX_PORTS]; + int id; + u8 max_p; + void __iomem *base; + unsigned long phy_base; + spinlock_t lock; /* Serializes access to internal data and regs */ + bool clock_enabled; + int gdd_irq; + unsigned int fifo_mapping_strategy; + unsigned int gdd_usecount; + unsigned int last_gdd_lch; + unsigned int gdd_chan_count; + bool in_dma_tasklet; + void (*set_min_bus_tput) (struct device *dev, u8 agent_id, + unsigned long r); + struct notifier_block hsi_nb; + struct tasklet_struct hsi_gdd_tasklet; +#ifdef CONFIG_DEBUG_FS + struct dentry *dir; +#endif + struct device *dev; +}; + +/** + * struct hsi_platform_data - Board specific data +*/ +struct hsi_platform_data { + void (*set_min_bus_tput) (struct device *dev, u8 agent_id, + unsigned long r); + int (*device_enable) (struct platform_device *pdev); + int (*device_shutdown) (struct platform_device *pdev); + int (*device_idle) (struct platform_device *pdev); + int (*wakeup_enable) (int hsi_port); + int (*wakeup_disable) (int hsi_port); + int (*wakeup_is_from_hsi) (void); + int (*board_suspend)(int hsi_port, bool dev_may_wakeup); + int (*board_resume)(int hsi_port); + u8 num_ports; + struct ctrl_ctx *ctx; + u8 hsi_gdd_chan_count; + unsigned long default_hsi_fclk; +}; + +/* HSI Bus */ +extern struct bus_type hsi_bus_type; + +int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg); +int hsi_bus_init(void); +void hsi_bus_exit(void); +/* End HSI Bus */ + +void hsi_reset_ch_read(struct hsi_channel *ch); +void hsi_reset_ch_write(struct hsi_channel *ch); +bool hsi_is_channel_busy(struct hsi_channel *ch); +bool hsi_is_hsi_port_busy(struct hsi_port *pport); +bool hsi_is_hsi_controller_busy(struct hsi_dev *hsi_ctrl); +bool hsi_is_hst_port_busy(struct hsi_port *pport); +bool hsi_is_hst_controller_busy(struct hsi_dev *hsi_ctrl); + +int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag); +int hsi_driver_enable_read_interrupt(struct hsi_channel *hsi_channel, + u32 *data); +int hsi_driver_enable_write_interrupt(struct hsi_channel *hsi_channel, + u32 *data); +bool hsi_is_dma_read_int_pending(struct hsi_dev *hsi_ctrl); +int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 * data, + unsigned int count); +int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 * data, + unsigned int count); + +int hsi_driver_cancel_read_interrupt(struct hsi_channel *ch); +int hsi_driver_cancel_write_interrupt(struct hsi_channel *ch); +void hsi_driver_disable_read_interrupt(struct hsi_channel *ch); +void hsi_driver_disable_write_interrupt(struct hsi_channel *ch); +int hsi_driver_cancel_write_dma(struct hsi_channel *ch); +int hsi_driver_cancel_read_dma(struct hsi_channel *ch); +int hsi_do_cawake_process(struct hsi_port *pport); + +int hsi_driver_device_is_hsi(struct platform_device *dev); + +int hsi_mpu_init(struct hsi_port *hsi_p, const char *irq_name); +void hsi_mpu_exit(struct hsi_port *hsi_p); + +int hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name); +void hsi_gdd_exit(struct hsi_dev *hsi_ctrl); + +int hsi_cawake_init(struct hsi_port *port, const char *irq_name); +void hsi_cawake_exit(struct hsi_port *port); + +int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel, + unsigned int port); +int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo, + unsigned int *channel, unsigned int *port); +int hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype); +long hsi_hst_bufstate_f_reg(struct hsi_dev *hsi_ctrl, + unsigned int port, unsigned int channel); +long hsi_hsr_bufstate_f_reg(struct hsi_dev *hsi_ctrl, + unsigned int port, unsigned int channel); +long hsi_hst_buffer_reg(struct hsi_dev *hsi_ctrl, + unsigned int port, unsigned int channel); +long hsi_hsr_buffer_reg(struct hsi_dev *hsi_ctrl, + unsigned int port, unsigned int channel); +u8 hsi_get_rx_fifo_occupancy(struct hsi_dev *hsi_ctrl, u8 fifo); +void hsi_set_pm_force_hsi_on(struct hsi_dev *hsi_ctrl); +void hsi_set_pm_default(struct hsi_dev *hsi_ctrl); +int hsi_softreset(struct hsi_dev *hsi_ctrl); +void hsi_softreset_driver(struct hsi_dev *hsi_ctrl); + +void hsi_clocks_disable_channel(struct device *dev, u8 channel_number, + const char *s); +int hsi_clocks_enable_channel(struct device *dev, u8 channel_number, + const char *s); +#ifdef CONFIG_PM_RUNTIME +extern int hsi_runtime_resume(struct device *dev); +extern int hsi_runtime_suspend(struct device *dev); +#else +static inline int hsi_runtime_resume(struct device *dev) { return -ENOSYS; } +static inline int hsi_runtime_suspend(struct device *dev) { return -ENOSYS; } +#endif +void hsi_save_ctx(struct hsi_dev *hsi_ctrl); +void hsi_restore_ctx(struct hsi_dev *hsi_ctrl); + + +#ifdef CONFIG_DEBUG_FS +int hsi_debug_init(void); +void hsi_debug_exit(void); +int hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl); +void hsi_debug_remove_ctrl(struct hsi_dev *hsi_ctrl); +#else +#define hsi_debug_add_ctrl(hsi_ctrl) 0 +#define hsi_debug_remove_ctrl(hsi_ctrl) +#define hsi_debug_init() 0 +#define hsi_debug_exit() +#endif /* CONFIG_DEBUG_FS */ + +static inline struct hsi_channel *hsi_ctrl_get_ch(struct hsi_dev *hsi_ctrl, + unsigned int port, + unsigned int channel) +{ + return &hsi_ctrl->hsi_port[port - 1].hsi_channel[channel]; +} + +/* HSI IO access */ +static inline u32 hsi_inl(void __iomem *base, u32 offset) +{ + return inl((unsigned int)base + offset); +} + +static inline void hsi_outl(u32 data, void __iomem *base, u32 offset) +{ + outl(data, (unsigned int)base + offset); +} + +static inline void hsi_outl_or(u32 data, void __iomem *base, u32 offset) +{ + u32 tmp = hsi_inl(base, offset); + hsi_outl((tmp | data), base, offset); +} + +static inline void hsi_outl_and(u32 data, void __iomem *base, u32 offset) +{ + u32 tmp = hsi_inl(base, offset); + hsi_outl((tmp & data), base, offset); +} + +static inline u16 hsi_inw(void __iomem *base, u32 offset) +{ + return inw((unsigned int)base + offset); +} + +static inline void hsi_outw(u16 data, void __iomem *base, u32 offset) +{ + outw(data, (unsigned int)base + offset); +} + +static inline void hsi_outw_or(u16 data, void __iomem *base, u32 offset) +{ + u16 tmp = hsi_inw(base, offset); + hsi_outw((tmp | data), base, offset); +} + +static inline void hsi_outw_and(u16 data, void __iomem *base, u32 offset) +{ + u16 tmp = hsi_inw(base, offset); + hsi_outw((tmp & data), base, offset); +} + +static inline int hsi_get_cawake(struct hsi_port *port) +{ + struct platform_device *pdev = + to_platform_device(port->hsi_controller->dev); + + if (hsi_driver_device_is_hsi(pdev)) + return (HSI_HSR_MODE_WAKE_STATUS == + (hsi_inl(port->hsi_controller->base, + HSI_HSR_MODE_REG(port->port_number)) & + HSI_HSR_MODE_WAKE_STATUS)); + else if (port->cawake_gpio >= 0) + return gpio_get_value(port->cawake_gpio); + else + return -ENXIO; +} + +static inline void hsi_clocks_disable(struct device *dev, const char *s) +{ + hsi_clocks_disable_channel(dev, HSI_CH_NUMBER_NONE, s); +} + +static inline int hsi_clocks_enable(struct device *dev, const char *s) +{ + return hsi_clocks_enable_channel(dev, HSI_CH_NUMBER_NONE, s); +} + +#endif /* __HSI_DRIVER_H__ */ |