aboutsummaryrefslogtreecommitdiff
path: root/sim/bfin/devices.c
blob: d3fa95faa059b04398342ad1a12666379421fd23 (plain)
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
/* Blackfin device support.

   Copyright (C) 2010-2012 Free Software Foundation, Inc.
   Contributed by Analog Devices, Inc.

   This file is part of simulators.

   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 3 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, see <http://www.gnu.org/licenses/>.  */

#include "config.h"

#include "sim-main.h"
#include "sim-hw.h"
#include "hw-device.h"
#include "dv-bfin_cec.h"
#include "dv-bfin_mmu.h"

static void
bfin_mmr_invalid (struct hw *me, SIM_CPU *cpu, address_word addr,
		  unsigned nr_bytes, bool write)
{
  if (!cpu)
    cpu = hw_system_cpu (me);

  /* Only throw a fit if the cpu is doing the access.  DMA/GDB simply
     go unnoticed.  Not exactly hardware behavior, but close enough.  */
  if (!cpu)
    {
      sim_io_eprintf (hw_system (me), "%s: invalid MMR access @ %#x\n",
		      hw_path (me), addr);
      return;
    }

  HW_TRACE ((me, "invalid MMR %s to 0x%08lx length %u",
	     write ? "write" : "read", (unsigned long) addr, nr_bytes));

  /* XXX: is this what hardware does ?  */
  if (addr >= BFIN_CORE_MMR_BASE)
    /* XXX: This should be setting up CPLB fault addrs ?  */
    mmu_process_fault (cpu, addr, write, false, false, true);
  else
    /* XXX: Newer parts set up an interrupt from EBIU and program
            EBIU_ERRADDR with the address.  */
    cec_hwerr (cpu, HWERR_SYSTEM_MMR);
}

void
dv_bfin_mmr_invalid (struct hw *me, address_word addr, unsigned nr_bytes,
		     bool write)
{
  bfin_mmr_invalid (me, NULL, addr, nr_bytes, write);
}

void
dv_bfin_mmr_require (struct hw *me, address_word addr, unsigned nr_bytes,
		     unsigned size, bool write)
{
  if (nr_bytes != size)
    dv_bfin_mmr_invalid (me, addr, nr_bytes, write);
}

static bool
bfin_mmr_check (struct hw *me, SIM_CPU *cpu, address_word addr,
		unsigned nr_bytes, bool write)
{
  if (addr >= BFIN_CORE_MMR_BASE)
    {
      /* All Core MMRs are aligned 32bits.  */
      if ((addr & 3) == 0 && nr_bytes == 4)
	return true;
    }
  else if (addr >= BFIN_SYSTEM_MMR_BASE)
    {
      /* All System MMRs are 32bit aligned, but can be 16bits or 32bits.  */
      if ((addr & 0x3) == 0 && (nr_bytes == 2 || nr_bytes == 4))
	return true;
    }
  else
    return true;

  /* Still here ?  Must be crap.  */
  bfin_mmr_invalid (me, cpu, addr, nr_bytes, write);

  return false;
}

bool
dv_bfin_mmr_check (struct hw *me, address_word addr, unsigned nr_bytes,
		   bool write)
{
  return bfin_mmr_check (me, NULL, addr, nr_bytes, write);
}

int
device_io_read_buffer (device *me, void *source, int space,
		       address_word addr, unsigned nr_bytes,
		       SIM_DESC sd, SIM_CPU *cpu, sim_cia cia)
{
  struct hw *dv_me = (struct hw *) me;

  if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
    return nr_bytes;

  if (bfin_mmr_check (dv_me, cpu, addr, nr_bytes, false))
    if (cpu)
      {
	sim_cpu_hw_io_read_buffer (cpu, cia, dv_me, source, space,
				   addr, nr_bytes);
	return nr_bytes;
      }
    else
      return sim_hw_io_read_buffer (sd, dv_me, source, space, addr, nr_bytes);
  else
    return 0;
}

int
device_io_write_buffer (device *me, const void *source, int space,
			address_word addr, unsigned nr_bytes,
                        SIM_DESC sd, SIM_CPU *cpu, sim_cia cia)
{
  struct hw *dv_me = (struct hw *) me;

  if (STATE_ENVIRONMENT (sd) != OPERATING_ENVIRONMENT)
    return nr_bytes;

  if (bfin_mmr_check (dv_me, cpu, addr, nr_bytes, true))
    if (cpu)
      {
	sim_cpu_hw_io_write_buffer (cpu, cia, dv_me, source, space,
				    addr, nr_bytes);
	return nr_bytes;
      }
    else
      return sim_hw_io_write_buffer (sd, dv_me, source, space, addr, nr_bytes);
  else
    return 0;
}

void device_error (device *me, const char *message, ...)
{
  /* Don't bother doing anything here -- any place in common code that
     calls device_error() follows it with sim_hw_abort().  Since the
     device isn't bound to the system yet, we can't call any common
     hardware error funcs on it or we'll hit a NULL pointer.  */
}

unsigned int dv_get_bus_num (struct hw *me)
{
  const hw_unit *unit = hw_unit_address (me);
  return unit->cells[unit->nr_cells - 1];
}