1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
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__ */
|