static void attach_bfin_uart_regs (struct hw *me, struct bfin_uart *uart) { address_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); if (attach_size != BFIN_MMR_UART2_SIZE) hw_abort (me, "\"reg\" size must be %#x", BFIN_MMR_UART2_SIZE); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); uart->base = attach_address; }
int hw_find_integer_array_property (struct hw *me, const char *property, unsigned index, signed_cell *integer) { const struct hw_property *node; int sizeof_integer = sizeof (*integer); signed_cell *cell; TRACE (trace_devices, ("hw_find_integer(me=0x%lx, property=%s)\n", (long)me, property)); /* check things sane */ node = hw_find_property (me, property); if (node == NULL) hw_abort (me, "property \"%s\" not found", property); if (node->type != integer_property && node->type != array_property) hw_abort (me, "property \"%s\" of wrong type (integer or array)", property); if ((node->sizeof_array % sizeof_integer) != 0) hw_abort (me, "property \"%s\" contains an incomplete number of cells", property); if (node->sizeof_array <= sizeof_integer * index) return 0; /* Find and convert the value */ cell = ((signed_cell*)node->array) + index; *integer = BE2H_cell (*cell); return node->sizeof_array / sizeof_integer; }
static void attach_tx3904irc_regs (struct hw *me, struct tx3904irc *controller) { unsigned_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain one addr/size entry"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); controller->base_address = attach_address; }
/* Helper function to easily add CFI erase regions to the existing set. */ static void cfi_add_erase_region (struct hw *me, struct cfi *cfi, unsigned blocks, unsigned size) { unsigned num_regions = cfi->query.num_erase_regions; struct cfi_erase_region *region; unsigned char *qry_region; /* Store for our own usage. */ region = &cfi->erase_regions[num_regions]; region->blocks = blocks; region->size = size; if (num_regions == 0) region->start = 0; else region->start = region[-1].end; region->end = region->start + (blocks * size); /* Regions are 4 bytes long. */ qry_region = cfi->erase_region_info + 4 * num_regions; /* [0][1] = number erase blocks - 1 */ if (blocks > 0xffff + 1) hw_abort (me, "erase blocks %u too big to fit into region info", blocks); cfi_encode_16bit (&qry_region[0], blocks - 1); /* [2][3] = block size / 256 bytes */ if (size > 0xffff * 256) hw_abort (me, "erase size %u too big to fit into region info", size); cfi_encode_16bit (&qry_region[2], size / 256); /* Yet another region. */ cfi->query.num_erase_regions = num_regions + 1; }
static void write_intmode_reg (struct hw *me, struct mn103ser *serial, unsigned_word serial_reg, const void *source, unsigned nr_bytes) { unsigned8 val = *(unsigned8 *)source; if ( nr_bytes == 1 ) { /* Check for attempt to write to read-only bits of register. */ if ( ( serial_reg == 2 && (val & 0xCA) != 0 ) || ( serial_reg != 2 && (val & 0x4A) != 0 ) ) { hw_abort(me, "Cannot write to read-only bits of SC%dICR.", serial_reg); } else { serial->device[serial_reg].intmode = val; } } else { hw_abort (me, "bad write size of %d bytes to SC%dICR.", nr_bytes, serial_reg); } }
static void attach_bfin_cec_regs (struct hw *me, struct bfin_cec *cec) { address_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); if (attach_size != BFIN_COREMMR_CEC_SIZE) hw_abort (me, "\"reg\" size must be %#x", BFIN_COREMMR_CEC_SIZE); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); cec->base = attach_address; /* XXX: should take from the device tree. */ cec->cpu = STATE_CPU (hw_system (me), 0); cec->me = me; }
static void attach_mn103ser_regs (struct hw *me, struct mn103ser *serial) { unsigned_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); serial->block.base = attach_address; hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); serial->block.bound = attach_address + (attach_size - 1); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); }
static void write_control_reg (struct hw *me, struct mn103ser *serial, unsigned_word serial_reg, const void *source, unsigned nr_bytes) { unsigned16 val = LE2H_2 (*(unsigned16 *)source); /* really allow 1 byte write, too */ if ( nr_bytes == 2 ) { if ( serial_reg == 2 && (val & 0x0C04) != 0 ) { hw_abort(me, "Cannot write to read-only bits of SC2CTR."); } else { serial->device[serial_reg].control = val; } } else { hw_abort (me, "bad read size of %d bytes from SC%dSTR.", nr_bytes, serial_reg); } }
static void write_control_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { if ( io_port_reg == 3 && (buf & 0xfc) != 0 ) { hw_abort(me, "Cannot write to read-only bits of P3DIR."); } else { io_port->port[io_port_reg].control = buf; } } else { hw_abort (me, "bad write size of %d bytes to P%dDIR.", nr_bytes, io_port_reg); } }
static void write_output_mode_reg (struct hw *me, struct mn103iop *io_port, unsigned_word io_port_reg, const void *source, unsigned nr_bytes) { unsigned8 buf = *(unsigned8 *)source; if ( nr_bytes == 1 ) { /* check if there are fields which can't be written and take appropriate action depending what bits are set */ if ( ( io_port_reg == 3 && (buf & 0xfc) != 0 ) || ( (io_port_reg == 0 || io_port_reg == 1) && (buf & 0xfe) != 0 ) ) { hw_abort(me, "Cannot write to read-only bits of output mode register."); } else { io_port->port[io_port_reg].output_mode = buf; } } else { hw_abort (me, "bad write size of %d bytes to P%dMD.", nr_bytes, io_port_reg); } }
static void attach_mn103int_regs (struct hw *me, struct mn103int *controller) { int i; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); for (i = 0; i < NR_BLOCKS; i++) { unsigned_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (!hw_find_reg_array_property (me, "reg", i, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); controller->block[i].base = attach_address; hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); controller->block[i].bound = attach_address + (attach_size - 1); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); } }
static void attach_mn103cpu_regs (struct hw *me, struct mn103cpu *controller) { unsigned_word attach_address; int attach_space; unsigned attach_size; reg_property_spec reg; if (hw_find_property (me, "reg") == NULL) hw_abort (me, "Missing \"reg\" property"); if (!hw_find_reg_array_property (me, "reg", 0, ®)) hw_abort (me, "\"reg\" property must contain three addr/size entries"); hw_unit_address_to_attach_address (hw_parent (me), ®.address, &attach_space, &attach_address, me); controller->block.base = attach_address; hw_unit_size_to_attach_size (hw_parent (me), ®.size, &attach_size, me); controller->block.bound = attach_address + (attach_size - 1); if ((controller->block.base & 3) != 0) hw_abort (me, "cpu register block must be 4 byte aligned"); hw_attach_address (hw_parent (me), 0, attach_space, attach_address, attach_size, me); }
static unsigned mn103iop_io_write_buffer (struct hw *me, const void *source, int space, unsigned_word base, unsigned nr_bytes) { struct mn103iop *io_port = hw_data (me); enum io_port_register_types io_port_reg; HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); io_port_reg = decode_addr (me, io_port, base); switch (io_port_reg) { /* Port output registers */ case P0OUT: case P1OUT: case P2OUT: case P3OUT: write_output_reg(me, io_port, io_port_reg-P0OUT, source, nr_bytes); break; /* Port output mode registers */ case P0MD: case P1MD: case P2MD: case P3MD: write_output_mode_reg(me, io_port, io_port_reg-P0MD, source, nr_bytes); break; /* Port control registers */ case P0DIR: case P1DIR: case P2DIR: case P3DIR: write_control_reg(me, io_port, io_port_reg-P0DIR, source, nr_bytes); break; /* Port pin registers */ case P0IN: case P1IN: case P2IN: hw_abort(me, "Cannot write to pin register."); break; case P2SS: case P4SS: write_dedicated_control_reg(me, io_port, io_port_reg, source, nr_bytes); break; default: hw_abort(me, "invalid address"); } return nr_bytes; }
static void read_special_timer6_reg (struct hw *me, struct mn103tim *timers, int timer_nr, void *dest, unsigned nr_bytes) { unsigned32 val; switch (nr_bytes) { case 1: { switch ( timer_nr ) { case TM6MDA: *(unsigned8 *)dest = timers->tm6mda; break; case TM6MDB: *(unsigned8 *)dest = timers->tm6mdb; break; case TM6CA: *(unsigned8 *)dest = timers->tm6ca; break; case TM6CB: *(unsigned8 *)dest = timers->tm6cb; break; default: break; } break; } case 2: if ( timer_nr == TM6CA ) { *(unsigned16 *)dest = timers->tm6ca; } else if ( timer_nr == TM6CB ) { *(unsigned16 *)dest = timers->tm6cb; } else { hw_abort(me, "bad read size for timer 6 mode A/B register"); } break; default: hw_abort(me, "bad read size for timer 6 register"); } }
static void write_special_timer6_reg (struct hw *me, struct mn103tim *timers, int timer_nr, const void *source, unsigned nr_bytes) { unsigned32 val; switch (nr_bytes) { case 1: { switch ( timer_nr ) { case TM6MDA: timers->tm6mda = *(unsigned8 *)source; break; case TM6MDB: timers->tm6mdb = *(unsigned8 *)source; break; case TM6CA: timers->tm6ca = *(unsigned8 *)source; break; case TM6CB: timers->tm6cb = *(unsigned8 *)source; break; default: break; } break; } case 2: if ( timer_nr == TM6CA ) { timers->tm6ca = *(unsigned16 *)source; } else if ( timer_nr == TM6CB ) { timers->tm6cb = *(unsigned16 *)source; } else { hw_abort(me, "bad read size for timer 6 mode A/B register"); } break; default: hw_abort(me, "bad read size for timer 6 register"); } }
const struct hw_property * hw_find_array_property (struct hw *me, const char *property) { const struct hw_property *node; node = hw_find_property (me, property); if (node == NULL) hw_abort (me, "property \"%s\" not found", property); if (node->type != array_property) hw_abort (me, "property \"%s\" of wrong type (array)", property); return node; }
const char * hw_tree_find_string_property (struct hw *root, const char *path_to_property) { name_specifier spec; if (!split_property_specifier (root, path_to_property, &spec)) hw_abort (root, "Invalid property path %s", path_to_property); root = split_find_device (root, &spec); if (spec.name != NULL) hw_abort (root, "device \"%s\" not found (property \"%s\")", spec.name, path_to_property); return hw_find_string_property (root, spec.property); }
const char * hw_find_string_property (struct hw *me, const char *property) { const struct hw_property *node; const char *string; node = hw_find_property (me, property); if (node == NULL) hw_abort (me, "property \"%s\" not found", property); if (node->type != string_property) hw_abort (me, "property \"%s\" of wrong type (string)", property); string = node->array; ASSERT (strlen(string) + 1 == node->sizeof_array); return string; }
int hw_find_boolean_property (struct hw *me, const char *property) { const struct hw_property *node; unsigned_cell boolean; node = hw_find_property (me, property); if (node == NULL) hw_abort (me, "property \"%s\" not found", property); if (node->type != boolean_property) hw_abort (me, "property \"%s\" of wrong type (boolean)", property); ASSERT (sizeof (boolean) == node->sizeof_array); memcpy (&boolean, node->array, sizeof (boolean)); return boolean; }
signed_cell hw_find_integer_property (struct hw *me, const char *property) { const struct hw_property *node; signed_cell integer; node = hw_find_property (me, property); if (node == NULL) hw_abort (me, "property \"%s\" not found", property); if (node->type != integer_property) hw_abort (me, "property \"%s\" of wrong type (integer)", property); ASSERT (sizeof (integer) == node->sizeof_array); memcpy (&integer, node->array, sizeof (integer)); return BE2H_cell (integer); }
void hw_find_ihandle_runtime_property (struct hw *me, const char *property, ihandle_runtime_property_spec *ihandle) { struct hw_property_data *entry = find_property_data (me, property); if (entry == NULL) hw_abort (me, "property \"%s\" not found", property); if (entry->property->type != ihandle_property || entry->property->disposition != permenant_object) hw_abort (me, "property \"%s\" of wrong type", property); ASSERT (entry->init_array != NULL); /* the full path */ ihandle->full_path = entry->init_array; }
static const char * full_name_of_hw (struct hw *leaf, char *buf, unsigned sizeof_buf) { /* get a buffer */ char full_name[1024]; if (buf == (char*)0) { buf = full_name; sizeof_buf = sizeof (full_name); } /* use head recursion to construct the path */ if (hw_parent (leaf) == NULL) /* root */ { if (sizeof_buf < 1) hw_abort (leaf, "buffer overflow"); *buf = '\0'; } else /* sub node */ { char unit[1024]; full_name_of_hw (hw_parent (leaf), buf, sizeof_buf); if (hw_unit_encode (hw_parent (leaf), hw_unit_address (leaf), unit + 1, sizeof (unit) - 1) > 0) unit[0] = '@'; else unit[0] = '\0'; if (strlen (buf) + strlen ("/") + strlen (hw_name (leaf)) + strlen (unit) >= sizeof_buf) hw_abort (leaf, "buffer overflow"); strcat (buf, "/"); strcat (buf, hw_name (leaf)); strcat (buf, unit); } /* return it usefully */ if (buf == full_name) buf = hw_strdup (leaf, full_name); return buf; }
static unsigned mn103tim_io_write_buffer (struct hw *me, const void *source, int space, unsigned_word base, unsigned nr_bytes) { struct mn103tim *timers = hw_data (me); enum timer_register_types timer_reg; HW_TRACE ((me, "write to 0x%08lx length %d with 0x%x", (long) base, (int) nr_bytes, *(unsigned32 *)source)); timer_reg = decode_addr (me, timers, base); /* It can be either a mode register, a base register, a binary counter, */ /* or a special timer 6 register. Check in that order. */ if ( timer_reg <= LAST_MODE_REG ) { if ( timer_reg == 6 ) { write_tm6md(me, timers, base, source, nr_bytes); } else { write_mode_reg(me, timers, timer_reg-FIRST_MODE_REG, source, nr_bytes); } } else if ( timer_reg <= LAST_BASE_REG ) { write_base_reg(me, timers, timer_reg-FIRST_BASE_REG, source, nr_bytes); } else if ( timer_reg <= LAST_COUNTER ) { hw_abort(me, "cannot write to counter"); } else if ( timer_reg <= LAST_TIMER_REG ) { write_special_timer6_reg(me, timers, timer_reg, source, nr_bytes); } else { hw_abort(me, "invalid reg type"); } return nr_bytes; }
void hw_finish (struct hw *me) { if (hw_finished_p (me)) hw_abort (me, "Attempt to finish finished device"); /* Fill in the (hopefully) defined address/size cells values */ if (hw_find_property (me, "#address-cells") != NULL) me->nr_address_cells_of_hw_unit = hw_find_integer_property (me, "#address-cells"); else me->nr_address_cells_of_hw_unit = 2; if (hw_find_property (me, "#size-cells") != NULL) me->nr_size_cells_of_hw_unit = hw_find_integer_property (me, "#size-cells"); else me->nr_size_cells_of_hw_unit = 1; /* Fill in the (hopefully) defined trace variable */ if (hw_find_property (me, "trace?") != NULL) me->trace_of_hw_p = hw_find_boolean_property (me, "trace?"); /* allow global variable to define default tracing */ else if (! hw_trace_p (me) && hw_find_property (hw_root (me), "global-trace?") != NULL && hw_find_boolean_property (hw_root (me), "global-trace?")) me->trace_of_hw_p = 1; /* Allow the real device to override any methods */ me->base_of_hw->descriptor->to_finish (me); me->base_of_hw->finished_p = 1; }
void hw_add_duplicate_property (struct hw *me, const char *property, const struct hw_property *original) { struct hw_property_data *master; TRACE (trace_devices, ("hw_add_duplicate_property(me=0x%lx, property=%s, ...)\n", (long)me, property)); if (original->disposition != permenant_object) hw_abort (me, "Can only duplicate permenant objects"); /* find the original's master */ master = original->owner->properties_of_hw; while (master->property != original) { master = master->next; ASSERT(master != NULL); } /* now duplicate it */ hw_add_property (me, property, original->type, master->init_array, master->sizeof_init_array, original->array, original->sizeof_array, original, permenant_object); }
static int decode_addr (struct hw *me, struct mn103iop *io_port, unsigned_word address) { unsigned_word offset; offset = address - io_port->block[0].base; switch (offset) { case 0x00: return P0OUT; case 0x01: return P1OUT; case 0x04: return P2OUT; case 0x05: return P3OUT; case 0x20: return P0MD; case 0x21: return P1MD; case 0x24: return P2MD; case 0x25: return P3MD; case 0x44: return P2SS; case 0x48: return P4SS; case 0x60: return P0DIR; case 0x61: return P1DIR; case 0x64: return P2DIR; case 0x65: return P3DIR; case 0x80: return P0IN; case 0x81: return P1IN; case 0x84: return P2IN; case 0x85: return P3IN; default: { hw_abort (me, "bad address"); return -1; } } }
static unsigned mn103int_io_write_buffer (struct hw *me, const void *source, int space, unsigned_word base, unsigned nr_bytes) { struct mn103int *controller = hw_data (me); const unsigned8 *buf = source; unsigned byte; /* HW_TRACE ((me, "write 0x%08lx %d", (long) base, (int) nr_bytes)); */ for (byte = 0; byte < nr_bytes; byte++) { unsigned_word address = base + byte; unsigned_word offset; switch (decode_addr (me, controller, address, &offset)) { case ICR_BLOCK: write_icr (me, controller, offset, buf[byte]); break; case IAGR_BLOCK: /* not allowed */ break; case EXTMD_BLOCK: write_extmd (me, controller, offset, buf[byte]); break; default: hw_abort (me, "bad switch"); } } return nr_bytes; }
static unsigned mn103int_io_read_buffer (struct hw *me, void *dest, int space, unsigned_word base, unsigned nr_bytes) { struct mn103int *controller = hw_data (me); unsigned8 *buf = dest; unsigned byte; /* HW_TRACE ((me, "read 0x%08lx %d", (long) base, (int) nr_bytes)); */ for (byte = 0; byte < nr_bytes; byte++) { unsigned_word address = base + byte; unsigned_word offset; switch (decode_addr (me, controller, address, &offset)) { case ICR_BLOCK: buf[byte] = read_icr (me, controller, offset); break; case IAGR_BLOCK: buf[byte] = read_iagr (me, controller, offset); break; case EXTMD_BLOCK: buf[byte] = read_extmd (me, controller, offset); break; default: hw_abort (me, "bad switch"); } } return nr_bytes; }
static void tx3904sio_port_event (struct hw *me, int my_port, struct hw *source, int source_port, int level) { struct tx3904sio *controller = hw_data (me); switch (my_port) { case RESET_PORT: { HW_TRACE ((me, "reset")); tx3904sio_fifo_reset(me, & controller->rx_fifo); tx3904sio_fifo_reset(me, & controller->tx_fifo); controller->slsr = controller->sdicr = controller->sdisr = controller->sfcr = controller->sbgr = 0; controller->slcr = 0x40000000; /* set TWUB */ controller->sbgr = 0x03ff0000; /* set BCLK=3, BRD=FF */ /* Don't interfere with I/O poller. */ break; } default: hw_abort (me, "Event on unknown port %d", my_port); break; } }
void create_hw_event_data (struct hw *me) { if (me->events_of_hw != NULL) hw_abort (me, "stray events"); /* NOP */ }