symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/etraxfs_timer.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU ETRAX Timers
       
     3  *
       
     4  * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
       
     5  *
       
     6  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
     7  * of this software and associated documentation files (the "Software"), to deal
       
     8  * in the Software without restriction, including without limitation the rights
       
     9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    10  * copies of the Software, and to permit persons to whom the Software is
       
    11  * furnished to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in
       
    14  * all copies or substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    22  * THE SOFTWARE.
       
    23  */
       
    24 #include <stdio.h>
       
    25 #include <sys/time.h>
       
    26 #include "hw.h"
       
    27 #include "sysemu.h"
       
    28 #include "qemu-timer.h"
       
    29 
       
    30 #define D(x)
       
    31 
       
    32 #define RW_TMR0_DIV   0x00
       
    33 #define R_TMR0_DATA   0x04
       
    34 #define RW_TMR0_CTRL  0x08
       
    35 #define RW_TMR1_DIV   0x10
       
    36 #define R_TMR1_DATA   0x14
       
    37 #define RW_TMR1_CTRL  0x18
       
    38 #define R_TIME        0x38
       
    39 #define RW_WD_CTRL    0x40
       
    40 #define R_WD_STAT     0x44
       
    41 #define RW_INTR_MASK  0x48
       
    42 #define RW_ACK_INTR   0x4c
       
    43 #define R_INTR        0x50
       
    44 #define R_MASKED_INTR 0x54
       
    45 
       
    46 struct fs_timer_t {
       
    47 	CPUState *env;
       
    48 	qemu_irq *irq;
       
    49 	qemu_irq *nmi;
       
    50 
       
    51 	QEMUBH *bh_t0;
       
    52 	QEMUBH *bh_t1;
       
    53 	QEMUBH *bh_wd;
       
    54 	ptimer_state *ptimer_t0;
       
    55 	ptimer_state *ptimer_t1;
       
    56 	ptimer_state *ptimer_wd;
       
    57 	struct timeval last;
       
    58 
       
    59 	int wd_hits;
       
    60 
       
    61 	/* Control registers.  */
       
    62 	uint32_t rw_tmr0_div;
       
    63 	uint32_t r_tmr0_data;
       
    64 	uint32_t rw_tmr0_ctrl;
       
    65 
       
    66 	uint32_t rw_tmr1_div;
       
    67 	uint32_t r_tmr1_data;
       
    68 	uint32_t rw_tmr1_ctrl;
       
    69 
       
    70 	uint32_t rw_wd_ctrl;
       
    71 
       
    72 	uint32_t rw_intr_mask;
       
    73 	uint32_t rw_ack_intr;
       
    74 	uint32_t r_intr;
       
    75 	uint32_t r_masked_intr;
       
    76 };
       
    77 
       
    78 static uint32_t timer_rinvalid (void *opaque, target_phys_addr_t addr)
       
    79 {
       
    80 	struct fs_timer_t *t = opaque;
       
    81 	CPUState *env = t->env;
       
    82 	cpu_abort(env, "Unsupported short access. reg=" TARGET_FMT_plx "\n",
       
    83 		  addr);
       
    84 	return 0;
       
    85 }
       
    86 
       
    87 static uint32_t timer_readl (void *opaque, target_phys_addr_t addr)
       
    88 {
       
    89 	struct fs_timer_t *t = opaque;
       
    90 	uint32_t r = 0;
       
    91 
       
    92 	switch (addr) {
       
    93 	case R_TMR0_DATA:
       
    94 		break;
       
    95 	case R_TMR1_DATA:
       
    96 		D(printf ("R_TMR1_DATA\n"));
       
    97 		break;
       
    98 	case R_TIME:
       
    99 		r = qemu_get_clock(vm_clock) / 10;
       
   100 		break;
       
   101 	case RW_INTR_MASK:
       
   102 		r = t->rw_intr_mask;
       
   103 		break;
       
   104 	case R_MASKED_INTR:
       
   105 		r = t->r_intr & t->rw_intr_mask;
       
   106 		break;
       
   107 	default:
       
   108 		D(printf ("%s %x\n", __func__, addr));
       
   109 		break;
       
   110 	}
       
   111 	return r;
       
   112 }
       
   113 
       
   114 static void
       
   115 timer_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value)
       
   116 {
       
   117 	struct fs_timer_t *t = opaque;
       
   118 	CPUState *env = t->env;
       
   119 	cpu_abort(env, "Unsupported short access. reg=" TARGET_FMT_plx "\n",
       
   120 		  addr);
       
   121 }
       
   122 
       
   123 #define TIMER_SLOWDOWN 1
       
   124 static void update_ctrl(struct fs_timer_t *t, int tnum)
       
   125 {
       
   126 	unsigned int op;
       
   127 	unsigned int freq;
       
   128 	unsigned int freq_hz;
       
   129 	unsigned int div;
       
   130 	uint32_t ctrl;
       
   131 
       
   132 	ptimer_state *timer;
       
   133 
       
   134 	if (tnum == 0) {
       
   135 		ctrl = t->rw_tmr0_ctrl;
       
   136 		div = t->rw_tmr0_div;
       
   137 		timer = t->ptimer_t0;
       
   138 	} else {
       
   139 		ctrl = t->rw_tmr1_ctrl;
       
   140 		div = t->rw_tmr1_div;
       
   141 		timer = t->ptimer_t1;
       
   142 	}
       
   143 
       
   144 
       
   145 	op = ctrl & 3;
       
   146 	freq = ctrl >> 2;
       
   147 	freq_hz = 32000000;
       
   148 
       
   149 	switch (freq)
       
   150 	{
       
   151 	case 0:
       
   152 	case 1:
       
   153 		D(printf ("extern or disabled timer clock?\n"));
       
   154 		break;
       
   155 	case 4: freq_hz =  29493000; break;
       
   156 	case 5: freq_hz =  32000000; break;
       
   157 	case 6: freq_hz =  32768000; break;
       
   158 	case 7: freq_hz = 100001000; break;
       
   159 	default:
       
   160 		abort();
       
   161 		break;
       
   162 	}
       
   163 
       
   164 	D(printf ("freq_hz=%d div=%d\n", freq_hz, div));
       
   165 	div = div * TIMER_SLOWDOWN;
       
   166 	div >>= 10;
       
   167 	freq_hz >>= 10;
       
   168 	ptimer_set_freq(timer, freq_hz);
       
   169 	ptimer_set_limit(timer, div, 0);
       
   170 
       
   171 	switch (op)
       
   172 	{
       
   173 		case 0:
       
   174 			/* Load.  */
       
   175 			ptimer_set_limit(timer, div, 1);
       
   176 			break;
       
   177 		case 1:
       
   178 			/* Hold.  */
       
   179 			ptimer_stop(timer);
       
   180 			break;
       
   181 		case 2:
       
   182 			/* Run.  */
       
   183 			ptimer_run(timer, 0);
       
   184 			break;
       
   185 		default:
       
   186 			abort();
       
   187 			break;
       
   188 	}
       
   189 }
       
   190 
       
   191 static void timer_update_irq(struct fs_timer_t *t)
       
   192 {
       
   193 	t->r_intr &= ~(t->rw_ack_intr);
       
   194 	t->r_masked_intr = t->r_intr & t->rw_intr_mask;
       
   195 
       
   196 	D(printf("%s: masked_intr=%x\n", __func__, t->r_masked_intr));
       
   197 	if (t->r_masked_intr)
       
   198 		qemu_irq_raise(t->irq[0]);
       
   199 	else
       
   200 		qemu_irq_lower(t->irq[0]);
       
   201 }
       
   202 
       
   203 static void timer0_hit(void *opaque)
       
   204 {
       
   205 	struct fs_timer_t *t = opaque;
       
   206 	t->r_intr |= 1;
       
   207 	timer_update_irq(t);
       
   208 }
       
   209 
       
   210 static void timer1_hit(void *opaque)
       
   211 {
       
   212 	struct fs_timer_t *t = opaque;
       
   213 	t->r_intr |= 2;
       
   214 	timer_update_irq(t);
       
   215 }
       
   216 
       
   217 static void watchdog_hit(void *opaque)
       
   218 {
       
   219 	struct fs_timer_t *t = opaque;
       
   220 	if (t->wd_hits == 0) {
       
   221 		/* real hw gives a single tick before reseting but we are
       
   222 		   a bit friendlier to compensate for our slower execution.  */
       
   223 		ptimer_set_count(t->ptimer_wd, 10);
       
   224 		ptimer_run(t->ptimer_wd, 1);
       
   225 		qemu_irq_raise(t->nmi[0]);
       
   226 	}
       
   227 	else
       
   228 		qemu_system_reset_request();
       
   229 
       
   230 	t->wd_hits++;
       
   231 }
       
   232 
       
   233 static inline void timer_watchdog_update(struct fs_timer_t *t, uint32_t value)
       
   234 {
       
   235 	unsigned int wd_en = t->rw_wd_ctrl & (1 << 8);
       
   236 	unsigned int wd_key = t->rw_wd_ctrl >> 9;
       
   237 	unsigned int wd_cnt = t->rw_wd_ctrl & 511;
       
   238 	unsigned int new_key = value >> 9 & ((1 << 7) - 1);
       
   239 	unsigned int new_cmd = (value >> 8) & 1;
       
   240 
       
   241 	/* If the watchdog is enabled, they written key must match the
       
   242 	   complement of the previous.  */
       
   243 	wd_key = ~wd_key & ((1 << 7) - 1);
       
   244 
       
   245 	if (wd_en && wd_key != new_key)
       
   246 		return;
       
   247 
       
   248 	D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", 
       
   249 		 wd_en, new_key, wd_key, new_cmd, wd_cnt));
       
   250 
       
   251 	if (t->wd_hits)
       
   252 		qemu_irq_lower(t->nmi[0]);
       
   253 
       
   254 	t->wd_hits = 0;
       
   255 
       
   256 	ptimer_set_freq(t->ptimer_wd, 760);
       
   257 	if (wd_cnt == 0)
       
   258 		wd_cnt = 256;
       
   259 	ptimer_set_count(t->ptimer_wd, wd_cnt);
       
   260 	if (new_cmd)
       
   261 		ptimer_run(t->ptimer_wd, 1);
       
   262 	else
       
   263 		ptimer_stop(t->ptimer_wd);
       
   264 
       
   265 	t->rw_wd_ctrl = value;
       
   266 }
       
   267 
       
   268 static void
       
   269 timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
       
   270 {
       
   271 	struct fs_timer_t *t = opaque;
       
   272 
       
   273 	switch (addr)
       
   274 	{
       
   275 		case RW_TMR0_DIV:
       
   276 			t->rw_tmr0_div = value;
       
   277 			break;
       
   278 		case RW_TMR0_CTRL:
       
   279 			D(printf ("RW_TMR0_CTRL=%x\n", value));
       
   280 			t->rw_tmr0_ctrl = value;
       
   281 			update_ctrl(t, 0);
       
   282 			break;
       
   283 		case RW_TMR1_DIV:
       
   284 			t->rw_tmr1_div = value;
       
   285 			break;
       
   286 		case RW_TMR1_CTRL:
       
   287 			D(printf ("RW_TMR1_CTRL=%x\n", value));
       
   288 			t->rw_tmr1_ctrl = value;
       
   289 			update_ctrl(t, 1);
       
   290 			break;
       
   291 		case RW_INTR_MASK:
       
   292 			D(printf ("RW_INTR_MASK=%x\n", value));
       
   293 			t->rw_intr_mask = value;
       
   294 			timer_update_irq(t);
       
   295 			break;
       
   296 		case RW_WD_CTRL:
       
   297 			timer_watchdog_update(t, value);
       
   298 			break;
       
   299 		case RW_ACK_INTR:
       
   300 			t->rw_ack_intr = value;
       
   301 			timer_update_irq(t);
       
   302 			t->rw_ack_intr = 0;
       
   303 			break;
       
   304 		default:
       
   305 			printf ("%s " TARGET_FMT_plx " %x\n",
       
   306 				__func__, addr, value);
       
   307 			break;
       
   308 	}
       
   309 }
       
   310 
       
   311 static CPUReadMemoryFunc *timer_read[] = {
       
   312 	&timer_rinvalid,
       
   313 	&timer_rinvalid,
       
   314 	&timer_readl,
       
   315 };
       
   316 
       
   317 static CPUWriteMemoryFunc *timer_write[] = {
       
   318 	&timer_winvalid,
       
   319 	&timer_winvalid,
       
   320 	&timer_writel,
       
   321 };
       
   322 
       
   323 static void etraxfs_timer_reset(void *opaque)
       
   324 {
       
   325 	struct fs_timer_t *t = opaque;
       
   326 
       
   327 	ptimer_stop(t->ptimer_t0);
       
   328 	ptimer_stop(t->ptimer_t1);
       
   329 	ptimer_stop(t->ptimer_wd);
       
   330 	t->rw_wd_ctrl = 0;
       
   331 	t->r_intr = 0;
       
   332 	t->rw_intr_mask = 0;
       
   333 	qemu_irq_lower(t->irq[0]);
       
   334 }
       
   335 
       
   336 void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, qemu_irq *nmi,
       
   337 			target_phys_addr_t base)
       
   338 {
       
   339 	static struct fs_timer_t *t;
       
   340 	int timer_regs;
       
   341 
       
   342 	t = qemu_mallocz(sizeof *t);
       
   343 	if (!t)
       
   344 		return;
       
   345 
       
   346 	t->bh_t0 = qemu_bh_new(timer0_hit, t);
       
   347 	t->bh_t1 = qemu_bh_new(timer1_hit, t);
       
   348 	t->bh_wd = qemu_bh_new(watchdog_hit, t);
       
   349 	t->ptimer_t0 = ptimer_init(t->bh_t0);
       
   350 	t->ptimer_t1 = ptimer_init(t->bh_t1);
       
   351 	t->ptimer_wd = ptimer_init(t->bh_wd);
       
   352 	t->irq = irqs;
       
   353 	t->nmi = nmi;
       
   354 	t->env = env;
       
   355 
       
   356 	timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t);
       
   357 	cpu_register_physical_memory (base, 0x5c, timer_regs);
       
   358 
       
   359 	qemu_register_reset(etraxfs_timer_reset, t);
       
   360 }