18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * omap_cf.c -- OMAP 16xx CompactFlash controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2005 David Brownell 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/errno.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <pcmcia/ss.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <mach/hardware.h> 208c2ecf20Sopenharmony_ci#include <asm/io.h> 218c2ecf20Sopenharmony_ci#include <linux/sizes.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <mach/mux.h> 248c2ecf20Sopenharmony_ci#include <mach/tc.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* NOTE: don't expect this to support many I/O cards. The 16xx chips have 288c2ecf20Sopenharmony_ci * hard-wired timings to support Compact Flash memory cards; they won't work 298c2ecf20Sopenharmony_ci * with various other devices (like WLAN adapters) without some external 308c2ecf20Sopenharmony_ci * logic to help out. 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * NOTE: CF controller docs disagree with address space docs as to where 338c2ecf20Sopenharmony_ci * CF_BASE really lives; this is a doc erratum. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci#define CF_BASE 0xfffe2800 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* status; read after IRQ */ 388c2ecf20Sopenharmony_ci#define CF_STATUS (CF_BASE + 0x00) 398c2ecf20Sopenharmony_ci# define CF_STATUS_BAD_READ (1 << 2) 408c2ecf20Sopenharmony_ci# define CF_STATUS_BAD_WRITE (1 << 1) 418c2ecf20Sopenharmony_ci# define CF_STATUS_CARD_DETECT (1 << 0) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* which chipselect (CS0..CS3) is used for CF (active low) */ 448c2ecf20Sopenharmony_ci#define CF_CFG (CF_BASE + 0x02) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* card reset */ 478c2ecf20Sopenharmony_ci#define CF_CONTROL (CF_BASE + 0x04) 488c2ecf20Sopenharmony_ci# define CF_CONTROL_RESET (1 << 0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define omap_cf_present() (!(omap_readw(CF_STATUS) & CF_STATUS_CARD_DETECT)) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const char driver_name[] = "omap_cf"; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct omap_cf_socket { 578c2ecf20Sopenharmony_ci struct pcmcia_socket socket; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci struct timer_list timer; 608c2ecf20Sopenharmony_ci unsigned present:1; 618c2ecf20Sopenharmony_ci unsigned active:1; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci struct platform_device *pdev; 648c2ecf20Sopenharmony_ci unsigned long phys_cf; 658c2ecf20Sopenharmony_ci u_int irq; 668c2ecf20Sopenharmony_ci struct resource iomem; 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define POLL_INTERVAL (2 * HZ) 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int omap_cf_ss_init(struct pcmcia_socket *s) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* the timer is primarily to kick this socket's pccardd */ 798c2ecf20Sopenharmony_cistatic void omap_cf_timer(struct timer_list *t) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct omap_cf_socket *cf = from_timer(cf, t, timer); 828c2ecf20Sopenharmony_ci unsigned present = omap_cf_present(); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (present != cf->present) { 858c2ecf20Sopenharmony_ci cf->present = present; 868c2ecf20Sopenharmony_ci pr_debug("%s: card %s\n", driver_name, 878c2ecf20Sopenharmony_ci present ? "present" : "gone"); 888c2ecf20Sopenharmony_ci pcmcia_parse_events(&cf->socket, SS_DETECT); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (cf->active) 928c2ecf20Sopenharmony_ci mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* This irq handler prevents "irqNNN: nobody cared" messages as drivers 968c2ecf20Sopenharmony_ci * claim the card's IRQ. It may also detect some card insertions, but 978c2ecf20Sopenharmony_ci * not removals; it can't always eliminate timer irqs. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_cistatic irqreturn_t omap_cf_irq(int irq, void *_cf) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct omap_cf_socket *cf = (struct omap_cf_socket *)_cf; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci omap_cf_timer(&cf->timer); 1048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (!sp) 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* NOTE CF is always 3VCARD */ 1138c2ecf20Sopenharmony_ci if (omap_cf_present()) { 1148c2ecf20Sopenharmony_ci struct omap_cf_socket *cf; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; 1178c2ecf20Sopenharmony_ci cf = container_of(s, struct omap_cf_socket, socket); 1188c2ecf20Sopenharmony_ci s->pcmcia_irq = 0; 1198c2ecf20Sopenharmony_ci s->pci_irq = cf->irq; 1208c2ecf20Sopenharmony_ci } else 1218c2ecf20Sopenharmony_ci *sp = 0; 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int 1268c2ecf20Sopenharmony_ciomap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci u16 control; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* REVISIT some non-OSK boards may support power switching */ 1318c2ecf20Sopenharmony_ci switch (s->Vcc) { 1328c2ecf20Sopenharmony_ci case 0: 1338c2ecf20Sopenharmony_ci case 33: 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci default: 1368c2ecf20Sopenharmony_ci return -EINVAL; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci control = omap_readw(CF_CONTROL); 1408c2ecf20Sopenharmony_ci if (s->flags & SS_RESET) 1418c2ecf20Sopenharmony_ci omap_writew(CF_CONTROL_RESET, CF_CONTROL); 1428c2ecf20Sopenharmony_ci else 1438c2ecf20Sopenharmony_ci omap_writew(0, CF_CONTROL); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", 1468c2ecf20Sopenharmony_ci driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int omap_cf_ss_suspend(struct pcmcia_socket *s) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci pr_debug("%s: %s\n", driver_name, __func__); 1548c2ecf20Sopenharmony_ci return omap_cf_set_socket(s, &dead_socket); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci/* regions are 2K each: mem, attrib, io (and reserved-for-ide) */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int 1608c2ecf20Sopenharmony_ciomap_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct omap_cf_socket *cf; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci cf = container_of(s, struct omap_cf_socket, socket); 1658c2ecf20Sopenharmony_ci io->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; 1668c2ecf20Sopenharmony_ci io->start = cf->phys_cf + SZ_4K; 1678c2ecf20Sopenharmony_ci io->stop = io->start + SZ_2K - 1; 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int 1728c2ecf20Sopenharmony_ciomap_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct omap_cf_socket *cf; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (map->card_start) 1778c2ecf20Sopenharmony_ci return -EINVAL; 1788c2ecf20Sopenharmony_ci cf = container_of(s, struct omap_cf_socket, socket); 1798c2ecf20Sopenharmony_ci map->static_start = cf->phys_cf; 1808c2ecf20Sopenharmony_ci map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; 1818c2ecf20Sopenharmony_ci if (map->flags & MAP_ATTRIB) 1828c2ecf20Sopenharmony_ci map->static_start += SZ_2K; 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct pccard_operations omap_cf_ops = { 1878c2ecf20Sopenharmony_ci .init = omap_cf_ss_init, 1888c2ecf20Sopenharmony_ci .suspend = omap_cf_ss_suspend, 1898c2ecf20Sopenharmony_ci .get_status = omap_cf_get_status, 1908c2ecf20Sopenharmony_ci .set_socket = omap_cf_set_socket, 1918c2ecf20Sopenharmony_ci .set_io_map = omap_cf_set_io_map, 1928c2ecf20Sopenharmony_ci .set_mem_map = omap_cf_set_mem_map, 1938c2ecf20Sopenharmony_ci}; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/*--------------------------------------------------------------------------*/ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* 1988c2ecf20Sopenharmony_ci * NOTE: right now the only board-specific platform_data is 1998c2ecf20Sopenharmony_ci * "what chipselect is used". Boards could want more. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic int __init omap_cf_probe(struct platform_device *pdev) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci unsigned seg; 2058c2ecf20Sopenharmony_ci struct omap_cf_socket *cf; 2068c2ecf20Sopenharmony_ci int irq; 2078c2ecf20Sopenharmony_ci int status; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci seg = (int) pdev->dev.platform_data; 2108c2ecf20Sopenharmony_ci if (seg == 0 || seg > 3) 2118c2ecf20Sopenharmony_ci return -ENODEV; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* either CFLASH.IREQ (INT_1610_CF) or some GPIO */ 2148c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 2158c2ecf20Sopenharmony_ci if (irq < 0) 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci cf = kzalloc(sizeof *cf, GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!cf) 2208c2ecf20Sopenharmony_ci return -ENOMEM; 2218c2ecf20Sopenharmony_ci timer_setup(&cf->timer, omap_cf_timer, 0); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci cf->pdev = pdev; 2248c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, cf); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* this primarily just shuts up irq handling noise */ 2278c2ecf20Sopenharmony_ci status = request_irq(irq, omap_cf_irq, IRQF_SHARED, 2288c2ecf20Sopenharmony_ci driver_name, cf); 2298c2ecf20Sopenharmony_ci if (status < 0) 2308c2ecf20Sopenharmony_ci goto fail0; 2318c2ecf20Sopenharmony_ci cf->irq = irq; 2328c2ecf20Sopenharmony_ci cf->socket.pci_irq = irq; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci switch (seg) { 2358c2ecf20Sopenharmony_ci /* NOTE: CS0 could be configured too ... */ 2368c2ecf20Sopenharmony_ci case 1: 2378c2ecf20Sopenharmony_ci cf->phys_cf = OMAP_CS1_PHYS; 2388c2ecf20Sopenharmony_ci break; 2398c2ecf20Sopenharmony_ci case 2: 2408c2ecf20Sopenharmony_ci cf->phys_cf = OMAP_CS2_PHYS; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case 3: 2438c2ecf20Sopenharmony_ci cf->phys_cf = omap_cs3_phys(); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci default: 2468c2ecf20Sopenharmony_ci goto fail1; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci cf->iomem.start = cf->phys_cf; 2498c2ecf20Sopenharmony_ci cf->iomem.end = cf->iomem.end + SZ_8K - 1; 2508c2ecf20Sopenharmony_ci cf->iomem.flags = IORESOURCE_MEM; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* pcmcia layer only remaps "real" memory */ 2538c2ecf20Sopenharmony_ci cf->socket.io_offset = (unsigned long) 2548c2ecf20Sopenharmony_ci ioremap(cf->phys_cf + SZ_4K, SZ_2K); 2558c2ecf20Sopenharmony_ci if (!cf->socket.io_offset) 2568c2ecf20Sopenharmony_ci goto fail1; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!request_mem_region(cf->phys_cf, SZ_8K, driver_name)) 2598c2ecf20Sopenharmony_ci goto fail1; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* NOTE: CF conflicts with MMC1 */ 2628c2ecf20Sopenharmony_ci omap_cfg_reg(W11_1610_CF_CD1); 2638c2ecf20Sopenharmony_ci omap_cfg_reg(P11_1610_CF_CD2); 2648c2ecf20Sopenharmony_ci omap_cfg_reg(R11_1610_CF_IOIS16); 2658c2ecf20Sopenharmony_ci omap_cfg_reg(V10_1610_CF_IREQ); 2668c2ecf20Sopenharmony_ci omap_cfg_reg(W10_1610_CF_RESET); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci omap_writew(~(1 << seg), CF_CFG); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci pr_info("%s: cs%d on irq %d\n", driver_name, seg, irq); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* NOTE: better EMIFS setup might support more cards; but the 2738c2ecf20Sopenharmony_ci * TRM only shows how to affect regular flash signals, not their 2748c2ecf20Sopenharmony_ci * CF/PCMCIA variants... 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_ci pr_debug("%s: cs%d, previous ccs %08x acs %08x\n", driver_name, 2778c2ecf20Sopenharmony_ci seg, omap_readl(EMIFS_CCS(seg)), omap_readl(EMIFS_ACS(seg))); 2788c2ecf20Sopenharmony_ci omap_writel(0x0004a1b3, EMIFS_CCS(seg)); /* synch mode 4 etc */ 2798c2ecf20Sopenharmony_ci omap_writel(0x00000000, EMIFS_ACS(seg)); /* OE hold/setup */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* CF uses armxor_ck, which is "always" available */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci pr_debug("%s: sts %04x cfg %04x control %04x %s\n", driver_name, 2848c2ecf20Sopenharmony_ci omap_readw(CF_STATUS), omap_readw(CF_CFG), 2858c2ecf20Sopenharmony_ci omap_readw(CF_CONTROL), 2868c2ecf20Sopenharmony_ci omap_cf_present() ? "present" : "(not present)"); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci cf->socket.owner = THIS_MODULE; 2898c2ecf20Sopenharmony_ci cf->socket.dev.parent = &pdev->dev; 2908c2ecf20Sopenharmony_ci cf->socket.ops = &omap_cf_ops; 2918c2ecf20Sopenharmony_ci cf->socket.resource_ops = &pccard_static_ops; 2928c2ecf20Sopenharmony_ci cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP 2938c2ecf20Sopenharmony_ci | SS_CAP_MEM_ALIGN; 2948c2ecf20Sopenharmony_ci cf->socket.map_size = SZ_2K; 2958c2ecf20Sopenharmony_ci cf->socket.io[0].res = &cf->iomem; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci status = pcmcia_register_socket(&cf->socket); 2988c2ecf20Sopenharmony_ci if (status < 0) 2998c2ecf20Sopenharmony_ci goto fail2; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci cf->active = 1; 3028c2ecf20Sopenharmony_ci mod_timer(&cf->timer, jiffies + POLL_INTERVAL); 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cifail2: 3068c2ecf20Sopenharmony_ci release_mem_region(cf->phys_cf, SZ_8K); 3078c2ecf20Sopenharmony_cifail1: 3088c2ecf20Sopenharmony_ci if (cf->socket.io_offset) 3098c2ecf20Sopenharmony_ci iounmap((void __iomem *) cf->socket.io_offset); 3108c2ecf20Sopenharmony_ci free_irq(irq, cf); 3118c2ecf20Sopenharmony_cifail0: 3128c2ecf20Sopenharmony_ci kfree(cf); 3138c2ecf20Sopenharmony_ci return status; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int __exit omap_cf_remove(struct platform_device *pdev) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci struct omap_cf_socket *cf = platform_get_drvdata(pdev); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci cf->active = 0; 3218c2ecf20Sopenharmony_ci pcmcia_unregister_socket(&cf->socket); 3228c2ecf20Sopenharmony_ci del_timer_sync(&cf->timer); 3238c2ecf20Sopenharmony_ci iounmap((void __iomem *) cf->socket.io_offset); 3248c2ecf20Sopenharmony_ci release_mem_region(cf->phys_cf, SZ_8K); 3258c2ecf20Sopenharmony_ci free_irq(cf->irq, cf); 3268c2ecf20Sopenharmony_ci kfree(cf); 3278c2ecf20Sopenharmony_ci return 0; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic struct platform_driver omap_cf_driver = { 3318c2ecf20Sopenharmony_ci .driver = { 3328c2ecf20Sopenharmony_ci .name = driver_name, 3338c2ecf20Sopenharmony_ci }, 3348c2ecf20Sopenharmony_ci .remove = __exit_p(omap_cf_remove), 3358c2ecf20Sopenharmony_ci}; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int __init omap_cf_init(void) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci if (cpu_is_omap16xx()) 3408c2ecf20Sopenharmony_ci return platform_driver_probe(&omap_cf_driver, omap_cf_probe); 3418c2ecf20Sopenharmony_ci return -ENODEV; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void __exit omap_cf_exit(void) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci if (cpu_is_omap16xx()) 3478c2ecf20Sopenharmony_ci platform_driver_unregister(&omap_cf_driver); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cimodule_init(omap_cf_init); 3518c2ecf20Sopenharmony_cimodule_exit(omap_cf_exit); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP CF Driver"); 3548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3558c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:omap_cf"); 356