18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* linux/drivers/parport/parport_ax88796.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * (c) 2005,2006 Simtec Electronics 58c2ecf20Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/parport.h> 118c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/io.h> 178c2ecf20Sopenharmony_ci#include <asm/irq.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define AX_SPR_BUSY (1<<7) 208c2ecf20Sopenharmony_ci#define AX_SPR_ACK (1<<6) 218c2ecf20Sopenharmony_ci#define AX_SPR_PE (1<<5) 228c2ecf20Sopenharmony_ci#define AX_SPR_SLCT (1<<4) 238c2ecf20Sopenharmony_ci#define AX_SPR_ERR (1<<3) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define AX_CPR_nDOE (1<<5) 268c2ecf20Sopenharmony_ci#define AX_CPR_SLCTIN (1<<3) 278c2ecf20Sopenharmony_ci#define AX_CPR_nINIT (1<<2) 288c2ecf20Sopenharmony_ci#define AX_CPR_ATFD (1<<1) 298c2ecf20Sopenharmony_ci#define AX_CPR_STRB (1<<0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct ax_drvdata { 328c2ecf20Sopenharmony_ci struct parport *parport; 338c2ecf20Sopenharmony_ci struct parport_state suspend; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci struct device *dev; 368c2ecf20Sopenharmony_ci struct resource *io; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci unsigned char irq_enabled; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci void __iomem *base; 418c2ecf20Sopenharmony_ci void __iomem *spp_data; 428c2ecf20Sopenharmony_ci void __iomem *spp_spr; 438c2ecf20Sopenharmony_ci void __iomem *spp_cpr; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic inline struct ax_drvdata *pp_to_drv(struct parport *p) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return p->private_data; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic unsigned char 528c2ecf20Sopenharmony_ciparport_ax88796_read_data(struct parport *p) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return readb(dd->spp_data); 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void 608c2ecf20Sopenharmony_ciparport_ax88796_write_data(struct parport *p, unsigned char data) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci writeb(data, dd->spp_data); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic unsigned char 688c2ecf20Sopenharmony_ciparport_ax88796_read_control(struct parport *p) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 718c2ecf20Sopenharmony_ci unsigned int cpr = readb(dd->spp_cpr); 728c2ecf20Sopenharmony_ci unsigned int ret = 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!(cpr & AX_CPR_STRB)) 758c2ecf20Sopenharmony_ci ret |= PARPORT_CONTROL_STROBE; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!(cpr & AX_CPR_ATFD)) 788c2ecf20Sopenharmony_ci ret |= PARPORT_CONTROL_AUTOFD; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (cpr & AX_CPR_nINIT) 818c2ecf20Sopenharmony_ci ret |= PARPORT_CONTROL_INIT; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!(cpr & AX_CPR_SLCTIN)) 848c2ecf20Sopenharmony_ci ret |= PARPORT_CONTROL_SELECT; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return ret; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void 908c2ecf20Sopenharmony_ciparport_ax88796_write_control(struct parport *p, unsigned char control) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 938c2ecf20Sopenharmony_ci unsigned int cpr = readb(dd->spp_cpr); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci cpr &= AX_CPR_nDOE; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (!(control & PARPORT_CONTROL_STROBE)) 988c2ecf20Sopenharmony_ci cpr |= AX_CPR_STRB; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!(control & PARPORT_CONTROL_AUTOFD)) 1018c2ecf20Sopenharmony_ci cpr |= AX_CPR_ATFD; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (control & PARPORT_CONTROL_INIT) 1048c2ecf20Sopenharmony_ci cpr |= AX_CPR_nINIT; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!(control & PARPORT_CONTROL_SELECT)) 1078c2ecf20Sopenharmony_ci cpr |= AX_CPR_SLCTIN; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci dev_dbg(dd->dev, "write_control: ctrl=%02x, cpr=%02x\n", control, cpr); 1108c2ecf20Sopenharmony_ci writeb(cpr, dd->spp_cpr); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (parport_ax88796_read_control(p) != control) { 1138c2ecf20Sopenharmony_ci dev_err(dd->dev, "write_control: read != set (%02x, %02x)\n", 1148c2ecf20Sopenharmony_ci parport_ax88796_read_control(p), control); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic unsigned char 1198c2ecf20Sopenharmony_ciparport_ax88796_read_status(struct parport *p) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1228c2ecf20Sopenharmony_ci unsigned int status = readb(dd->spp_spr); 1238c2ecf20Sopenharmony_ci unsigned int ret = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (status & AX_SPR_BUSY) 1268c2ecf20Sopenharmony_ci ret |= PARPORT_STATUS_BUSY; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (status & AX_SPR_ACK) 1298c2ecf20Sopenharmony_ci ret |= PARPORT_STATUS_ACK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (status & AX_SPR_ERR) 1328c2ecf20Sopenharmony_ci ret |= PARPORT_STATUS_ERROR; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (status & AX_SPR_SLCT) 1358c2ecf20Sopenharmony_ci ret |= PARPORT_STATUS_SELECT; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (status & AX_SPR_PE) 1388c2ecf20Sopenharmony_ci ret |= PARPORT_STATUS_PAPEROUT; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return ret; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic unsigned char 1448c2ecf20Sopenharmony_ciparport_ax88796_frob_control(struct parport *p, unsigned char mask, 1458c2ecf20Sopenharmony_ci unsigned char val) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1488c2ecf20Sopenharmony_ci unsigned char old = parport_ax88796_read_control(p); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci dev_dbg(dd->dev, "frob: mask=%02x, val=%02x, old=%02x\n", 1518c2ecf20Sopenharmony_ci mask, val, old); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci parport_ax88796_write_control(p, (old & ~mask) | val); 1548c2ecf20Sopenharmony_ci return old; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void 1588c2ecf20Sopenharmony_ciparport_ax88796_enable_irq(struct parport *p) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1618c2ecf20Sopenharmony_ci unsigned long flags; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci local_irq_save(flags); 1648c2ecf20Sopenharmony_ci if (!dd->irq_enabled) { 1658c2ecf20Sopenharmony_ci enable_irq(p->irq); 1668c2ecf20Sopenharmony_ci dd->irq_enabled = 1; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci local_irq_restore(flags); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void 1728c2ecf20Sopenharmony_ciparport_ax88796_disable_irq(struct parport *p) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1758c2ecf20Sopenharmony_ci unsigned long flags; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci local_irq_save(flags); 1788c2ecf20Sopenharmony_ci if (dd->irq_enabled) { 1798c2ecf20Sopenharmony_ci disable_irq(p->irq); 1808c2ecf20Sopenharmony_ci dd->irq_enabled = 0; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci local_irq_restore(flags); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void 1868c2ecf20Sopenharmony_ciparport_ax88796_data_forward(struct parport *p) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1898c2ecf20Sopenharmony_ci void __iomem *cpr = dd->spp_cpr; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci writeb((readb(cpr) & ~AX_CPR_nDOE), cpr); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void 1958c2ecf20Sopenharmony_ciparport_ax88796_data_reverse(struct parport *p) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 1988c2ecf20Sopenharmony_ci void __iomem *cpr = dd->spp_cpr; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci writeb(readb(cpr) | AX_CPR_nDOE, cpr); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void 2048c2ecf20Sopenharmony_ciparport_ax88796_init_state(struct pardevice *d, struct parport_state *s) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(d->port); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci memset(s, 0, sizeof(struct parport_state)); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci dev_dbg(dd->dev, "init_state: %p: state=%p\n", d, s); 2118c2ecf20Sopenharmony_ci s->u.ax88796.cpr = readb(dd->spp_cpr); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void 2158c2ecf20Sopenharmony_ciparport_ax88796_save_state(struct parport *p, struct parport_state *s) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci dev_dbg(dd->dev, "save_state: %p: state=%p\n", p, s); 2208c2ecf20Sopenharmony_ci s->u.ax88796.cpr = readb(dd->spp_cpr); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void 2248c2ecf20Sopenharmony_ciparport_ax88796_restore_state(struct parport *p, struct parport_state *s) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dev_dbg(dd->dev, "restore_state: %p: state=%p\n", p, s); 2298c2ecf20Sopenharmony_ci writeb(s->u.ax88796.cpr, dd->spp_cpr); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic struct parport_operations parport_ax88796_ops = { 2338c2ecf20Sopenharmony_ci .write_data = parport_ax88796_write_data, 2348c2ecf20Sopenharmony_ci .read_data = parport_ax88796_read_data, 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci .write_control = parport_ax88796_write_control, 2378c2ecf20Sopenharmony_ci .read_control = parport_ax88796_read_control, 2388c2ecf20Sopenharmony_ci .frob_control = parport_ax88796_frob_control, 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci .read_status = parport_ax88796_read_status, 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci .enable_irq = parport_ax88796_enable_irq, 2438c2ecf20Sopenharmony_ci .disable_irq = parport_ax88796_disable_irq, 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci .data_forward = parport_ax88796_data_forward, 2468c2ecf20Sopenharmony_ci .data_reverse = parport_ax88796_data_reverse, 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci .init_state = parport_ax88796_init_state, 2498c2ecf20Sopenharmony_ci .save_state = parport_ax88796_save_state, 2508c2ecf20Sopenharmony_ci .restore_state = parport_ax88796_restore_state, 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci .epp_write_data = parport_ieee1284_epp_write_data, 2538c2ecf20Sopenharmony_ci .epp_read_data = parport_ieee1284_epp_read_data, 2548c2ecf20Sopenharmony_ci .epp_write_addr = parport_ieee1284_epp_write_addr, 2558c2ecf20Sopenharmony_ci .epp_read_addr = parport_ieee1284_epp_read_addr, 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci .ecp_write_data = parport_ieee1284_ecp_write_data, 2588c2ecf20Sopenharmony_ci .ecp_read_data = parport_ieee1284_ecp_read_data, 2598c2ecf20Sopenharmony_ci .ecp_write_addr = parport_ieee1284_ecp_write_addr, 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci .compat_write_data = parport_ieee1284_write_compat, 2628c2ecf20Sopenharmony_ci .nibble_read_data = parport_ieee1284_read_nibble, 2638c2ecf20Sopenharmony_ci .byte_read_data = parport_ieee1284_read_byte, 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int parport_ax88796_probe(struct platform_device *pdev) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct device *_dev = &pdev->dev; 2718c2ecf20Sopenharmony_ci struct ax_drvdata *dd; 2728c2ecf20Sopenharmony_ci struct parport *pp; 2738c2ecf20Sopenharmony_ci struct resource *res; 2748c2ecf20Sopenharmony_ci unsigned long size; 2758c2ecf20Sopenharmony_ci int spacing; 2768c2ecf20Sopenharmony_ci int irq; 2778c2ecf20Sopenharmony_ci int ret; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci dd = kzalloc(sizeof(*dd), GFP_KERNEL); 2808c2ecf20Sopenharmony_ci if (!dd) 2818c2ecf20Sopenharmony_ci return -ENOMEM; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2848c2ecf20Sopenharmony_ci if (res == NULL) { 2858c2ecf20Sopenharmony_ci dev_err(_dev, "no MEM specified\n"); 2868c2ecf20Sopenharmony_ci ret = -ENXIO; 2878c2ecf20Sopenharmony_ci goto exit_mem; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci size = resource_size(res); 2918c2ecf20Sopenharmony_ci spacing = size / 3; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci dd->io = request_mem_region(res->start, size, pdev->name); 2948c2ecf20Sopenharmony_ci if (dd->io == NULL) { 2958c2ecf20Sopenharmony_ci dev_err(_dev, "cannot reserve memory\n"); 2968c2ecf20Sopenharmony_ci ret = -ENXIO; 2978c2ecf20Sopenharmony_ci goto exit_mem; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci dd->base = ioremap(res->start, size); 3018c2ecf20Sopenharmony_ci if (dd->base == NULL) { 3028c2ecf20Sopenharmony_ci dev_err(_dev, "cannot ioremap region\n"); 3038c2ecf20Sopenharmony_ci ret = -ENXIO; 3048c2ecf20Sopenharmony_ci goto exit_res; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3088c2ecf20Sopenharmony_ci if (irq <= 0) 3098c2ecf20Sopenharmony_ci irq = PARPORT_IRQ_NONE; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pp = parport_register_port((unsigned long)dd->base, irq, 3128c2ecf20Sopenharmony_ci PARPORT_DMA_NONE, 3138c2ecf20Sopenharmony_ci &parport_ax88796_ops); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (pp == NULL) { 3168c2ecf20Sopenharmony_ci dev_err(_dev, "failed to register parallel port\n"); 3178c2ecf20Sopenharmony_ci ret = -ENOMEM; 3188c2ecf20Sopenharmony_ci goto exit_unmap; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci pp->private_data = dd; 3228c2ecf20Sopenharmony_ci dd->parport = pp; 3238c2ecf20Sopenharmony_ci dd->dev = _dev; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci dd->spp_data = dd->base; 3268c2ecf20Sopenharmony_ci dd->spp_spr = dd->base + (spacing * 1); 3278c2ecf20Sopenharmony_ci dd->spp_cpr = dd->base + (spacing * 2); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* initialise the port controls */ 3308c2ecf20Sopenharmony_ci writeb(AX_CPR_STRB, dd->spp_cpr); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (irq >= 0) { 3338c2ecf20Sopenharmony_ci /* request irq */ 3348c2ecf20Sopenharmony_ci ret = request_irq(irq, parport_irq_handler, 3358c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, pdev->name, pp); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (ret < 0) 3388c2ecf20Sopenharmony_ci goto exit_port; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci dd->irq_enabled = 1; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pp); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dev_info(_dev, "attached parallel port driver\n"); 3468c2ecf20Sopenharmony_ci parport_announce_port(pp); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci exit_port: 3518c2ecf20Sopenharmony_ci parport_remove_port(pp); 3528c2ecf20Sopenharmony_ci exit_unmap: 3538c2ecf20Sopenharmony_ci iounmap(dd->base); 3548c2ecf20Sopenharmony_ci exit_res: 3558c2ecf20Sopenharmony_ci release_mem_region(dd->io->start, size); 3568c2ecf20Sopenharmony_ci exit_mem: 3578c2ecf20Sopenharmony_ci kfree(dd); 3588c2ecf20Sopenharmony_ci return ret; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int parport_ax88796_remove(struct platform_device *pdev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct parport *p = platform_get_drvdata(pdev); 3648c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci free_irq(p->irq, p); 3678c2ecf20Sopenharmony_ci parport_remove_port(p); 3688c2ecf20Sopenharmony_ci iounmap(dd->base); 3698c2ecf20Sopenharmony_ci release_mem_region(dd->io->start, resource_size(dd->io)); 3708c2ecf20Sopenharmony_ci kfree(dd); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int parport_ax88796_suspend(struct platform_device *dev, 3788c2ecf20Sopenharmony_ci pm_message_t state) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci struct parport *p = platform_get_drvdata(dev); 3818c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci parport_ax88796_save_state(p, &dd->suspend); 3848c2ecf20Sopenharmony_ci writeb(AX_CPR_nDOE | AX_CPR_STRB, dd->spp_cpr); 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int parport_ax88796_resume(struct platform_device *dev) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct parport *p = platform_get_drvdata(dev); 3918c2ecf20Sopenharmony_ci struct ax_drvdata *dd = pp_to_drv(p); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci parport_ax88796_restore_state(p, &dd->suspend); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci#else 3988c2ecf20Sopenharmony_ci#define parport_ax88796_suspend NULL 3998c2ecf20Sopenharmony_ci#define parport_ax88796_resume NULL 4008c2ecf20Sopenharmony_ci#endif 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ax88796-pp"); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic struct platform_driver axdrv = { 4058c2ecf20Sopenharmony_ci .driver = { 4068c2ecf20Sopenharmony_ci .name = "ax88796-pp", 4078c2ecf20Sopenharmony_ci }, 4088c2ecf20Sopenharmony_ci .probe = parport_ax88796_probe, 4098c2ecf20Sopenharmony_ci .remove = parport_ax88796_remove, 4108c2ecf20Sopenharmony_ci .suspend = parport_ax88796_suspend, 4118c2ecf20Sopenharmony_ci .resume = parport_ax88796_resume, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cimodule_platform_driver(axdrv); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 4178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AX88796 Parport parallel port driver"); 4188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 419