/* Low-level parallel port routines for StrongARM SA1100 * * Author: Zhu Qun Ying * * Copyright (C) 2000 * * distributed under terms of GPL * use GPIO pins for parallel interface * * Take Note: * nStrobe, aAutoFd, nSelection, Busy signals are inverted. * * That means when you set the control: * frob_control(PARPORT_STROBE, PARPORT_STROBE) will set it to low, * frob_control(PARPORT_STROBE, 0) will set it to high * * The inversions are here to be compatible with courrent ieee1284 * software implementaiton of parport HAL. Unless you want to write * your own ieee1284 signaling control, you will need to stick to these * inversions. * * 27 Apr. 2000 Change the read/write control and read status to reflect * bits inversion. * 17 Apr. 2000 Initial version */ /* For my board, the pin layout is as follows * * GPIO3: control buffer direction control (1 forward always) * GPIO2: data buffer direction control (1 forward, 0 reverse) * GPIO1: Output Enable * * Data pins: Input/Ouput * * GPIO7-4: D0-D4 I/O * GPIO11-8: D5-D8 I/O * * Control pins: * * GPIO15: nAck I * GPIO14: Busy I * GPIO13: PError I * GPIO12: Select I * GPIO18: nFault I * * GPIO0: nStrobe O * GPIO19: nAutoFd O * GPIO17: FP_init O * GPIO16: nSelectn O * */ #include #include #include #include #include #include #include #include // IO address for ARM #undef DEBUG #ifdef DEBUG #define dprintk printk #else #define dprintk(stuff...) // gcc extention #endif // for control #define ARM_STROBE GPIO_GPIO0 #define ARM_AUTOFD GPIO_GPIO19 #define ARM_INIT GPIO_GPIO17 #define ARM_SELECTN GPIO_GPIO16 #define ARM_STROBE_N 0 #define ARM_AUTOFD_N 19 #define ARM_INIT_N 17 #define ARM_SELECTN_N 16 // for status #define ARM_PAPEROUT GPIO_GPIO13 #define ARM_SELECT GPIO_GPIO12 #define ARM_ACK GPIO_GPIO15 #define ARM_BUSY GPIO_GPIO14 #define ARM_ERROR GPIO_GPIO18 #define ARM_PAPEROUT_N 13 #define ARM_SELECT_N 12 #define ARM_ACK_N 15 #define ARM_BUSY_N 14 #define ARM_ERROR_N 18 #define ARM_DATA_LINES 0xFF0 // direction and oe control #define ARM_DIRECTION GPIO_GPIO2 #define ARM_PARPORT_OE GPIO_GPIO1 #define ARM_DIRECTION_N 2 #define ARM_PARPORT_OE_N 1 #define SUCCESS 0 static struct parport *this_port = NULL; // change direction, but how about the signaling?? static void parport_arm_data_forward(struct parport *p) { dprintk(__FUNCTION__ "()\n"); // disable OE GPCR = ARM_PARPORT_OE; // switch data pin to output GPDR = (GPDR & ~ARM_DATA_LINES) | ARM_DATA_LINES; // set data pins to 1 GPSR = ARM_DIRECTION; // enable output GPSR = ARM_PARPORT_OE; } static void parport_arm_data_reverse(struct parport *p) { dprintk(__FUNCTION__ "()\n"); GPCR = ARM_PARPORT_OE; // switch data pins to input GPDR = GPDR & ~ARM_DATA_LINES; // set data pins to zero // enable input GPCR = ARM_DIRECTION; // enable buffer GPSR = ARM_PARPORT_OE; } static void parport_arm_write_data(struct parport *p, unsigned char data) { int i; // lower 4 bits for (i = 0; i < 4; ++i) { if ((data >> i) & 0x1) // a 1 GPSR = 0x1 << (7 - i); // implementation dependent, take care else // a 0 GPCR = 0x1 << (7 - i); } // upper 4 bits for (i = 4; i < 8; ++i) { if ((data >> i) & 0x1) // a 1 GPSR = 0x1 << (15 - i); else // a 0 GPCR = 0x1 << (15 - i); } } static unsigned char parport_arm_read_data(struct parport *p) { unsigned char data = 0; u_int32_t register_data; dprintk(__FUNCTION__ "()\n"); // read back data register_data = GPLR; // lower 4 bits data |= (u_char) ((register_data & GPIO_GPIO7) >> 7); // bit 0 data |= (u_char) ((register_data & GPIO_GPIO6) >> 5); // bit 1 data |= (u_char) ((register_data & GPIO_GPIO5) >> 3); // bit 2 data |= (u_char) ((register_data & GPIO_GPIO4) >> 1); // bit 3 // upper 4 bits data |= (u_char) ((register_data & GPIO_GPIO11) >> 7); // bit 4 data |= (u_char) ((register_data & GPIO_GPIO10) >> 5); // bit 5 data |= (u_char) ((register_data & GPIO_GPIO9) >> 3); // bit 6 data |= (u_char) ((register_data & GPIO_GPIO8) >> 1); // bit 7 return data; } static void parport_arm_write_control(struct parport *p, unsigned char control) { // nStrobe, nAutoFD, nSelectIn are inverted, 27 April. if (control & PARPORT_CONTROL_STROBE) GPCR = 0x1 << ARM_STROBE_N; else GPSR = 0x1 << ARM_STROBE_N; if (control & PARPORT_CONTROL_AUTOFD) GPCR = 0x1 << ARM_AUTOFD_N; else GPSR = 0x1 << ARM_AUTOFD_N; if (control & PARPORT_CONTROL_INIT) GPSR = 0x1 << ARM_INIT_N; else GPCR = 0x1 << ARM_INIT_N; if (control & PARPORT_CONTROL_SELECT) GPCR = 0x1 << ARM_SELECTN_N; else GPSR = 0x1 << ARM_SELECTN_N; } static unsigned char parport_arm_read_control(struct parport *p) { u_int32_t reg_data = GPLR; unsigned char ctrl_data = 0; ctrl_data |= (unsigned char) ((!((reg_data & ARM_STROBE) >> ARM_STROBE_N)) << 0); ctrl_data |= (unsigned char) ((!((reg_data & ARM_AUTOFD) >> ARM_AUTOFD_N)) << 1); ctrl_data |= (unsigned char) (((reg_data & ARM_INIT) >> ARM_INIT_N) << 2); ctrl_data |= (unsigned char) ((!((reg_data & ARM_SELECTN) >> ARM_SELECTN_N)) << 3); return ctrl_data; } static unsigned char parport_arm_frob_control(struct parport *p, unsigned char mask, unsigned char val) { unsigned char old = parport_arm_read_control(p); parport_arm_write_control(p, (old & ~mask) ^ (val & mask)); return old; } static unsigned char parport_arm_read_status(struct parport *p) { unsigned char status = 0; u_int32_t reg_data = GPLR; // Busy signal is inverted status |= (unsigned char) (((reg_data & ARM_ERROR) >> ARM_ERROR_N) << 3); status |= (unsigned char) (((reg_data & ARM_SELECT) >> ARM_SELECT_N) << 4); status |= (unsigned char) (((reg_data & ARM_PAPEROUT) >> ARM_PAPEROUT_N) << 5); status |= (unsigned char) (((reg_data & ARM_ACK) >> ARM_ACK_N) << 6); status |= (unsigned char) ((!((reg_data & ARM_BUSY) >> ARM_BUSY_N)) << 7); return status; } static void parport_arm_enable_irq(struct parport *p) { // not implemented, no irq } static void parport_arm_disable_irq(struct parport *p) { // not implemented, no irq } static void parport_arm_init_state(struct pardevice *dev, struct parport_state *s) { s->u.strongarm.data = 0; s->u.strongarm.data_dir = 1; // output s->u.strongarm.ctrl = 0x0; } static void parport_arm_save_state(struct parport *p, struct parport_state *s) { s->u.strongarm.data = 0; s->u.strongarm.data_dir = (u_char) ((GPDR & ARM_DIRECTION) >> ARM_DIRECTION_N); s->u.strongarm.ctrl = parport_arm_read_control(p); } static void parport_arm_restore_state(struct parport *p, struct parport_state *s) { GPDR = (GPDR & ~ARM_DIRECTION) | (s->u.strongarm.data_dir << ARM_DIRECTION_N); // 1 is output, 0 input if (s->u.strongarm.data_dir) parport_arm_write_data(p, s->u.strongarm.data); parport_arm_write_control(p, s->u.strongarm.ctrl); } static void parport_arm_inc_use_count(void) { MOD_INC_USE_COUNT; } static void parport_arm_dec_use_count(void) { MOD_DEC_USE_COUNT; } struct parport_operations parport_arm_ops = { parport_arm_write_data, parport_arm_read_data, parport_arm_write_control, parport_arm_read_control, parport_arm_frob_control, parport_arm_read_status, parport_arm_enable_irq, parport_arm_disable_irq, parport_arm_data_forward, parport_arm_data_reverse, parport_arm_init_state, parport_arm_save_state, parport_arm_restore_state, parport_arm_inc_use_count, parport_arm_dec_use_count, parport_ieee1284_epp_write_data, parport_ieee1284_epp_read_data, parport_ieee1284_epp_write_addr, parport_ieee1284_epp_read_addr, parport_ieee1284_ecp_write_data, parport_ieee1284_ecp_read_data, parport_ieee1284_ecp_write_addr, parport_ieee1284_write_compat, parport_ieee1284_read_nibble, parport_ieee1284_read_byte }; MODULE_AUTHOR("Zhu Qun Ying qyzhu@krdl.org.sg"); MODULE_DESCRIPTION("parallel port for SrongARM SA1100\n"); // module code int init_module(void) { struct parport *p; unsigned long base_address = 0x378; // pc style address. // set IO pins direction GPDR = ARM_STROBE | ARM_AUTOFD | ARM_INIT | ARM_SELECTN | ARM_DATA_LINES | ARM_PARPORT_OE | ARM_DIRECTION; // set forward direction and OE GPSR = ARM_DIRECTION | ARM_PARPORT_OE; p = parport_register_port(base_address, PARPORT_IRQ_NONE, // no IRQ PARPORT_DMA_NONE, // NO DMA &parport_arm_ops); if (!p) return 1; this_port = p; parport_proc_register(p); parport_announce_port(p); dprintk("parport_arm module loaded\n"); return SUCCESS; } void cleanup_module(void) { dprintk("parport_arm module removed\n"); parport_proc_unregister(this_port); parport_unregister_port(this_port); }