pq3cfi.c revision 1.4
1/* $NetBSD: pq3cfi.c,v 1.4 2011/08/06 05:48:01 cliff Exp $ */ 2/*- 3 * Copyright (c) 2011 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Cliff Neighbors. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/* 32 * NOR CFI driver support for booke 33 */ 34 35#include "opt_flash.h" 36#include "locators.h" 37 38#include <sys/cdefs.h> 39__KERNEL_RCSID(0, "$NetBSD: pq3cfi.c,v 1.4 2011/08/06 05:48:01 cliff Exp $"); 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/cdefs.h> 44#include <sys/device.h> 45#include <sys/endian.h> 46 47#include <sys/bus.h> 48 49#include <powerpc/booke/cpuvar.h> 50 51#include <dev/nor/nor.h> 52#include <dev/nor/cfi.h> 53 54 55static int pq3cfi_match(device_t, cfdata_t, void *); 56static void pq3cfi_attach(device_t, device_t, void *); 57static int pq3cfi_detach(device_t, int); 58 59struct pq3cfi_softc { 60 device_t sc_dev; 61 device_t sc_nordev; 62 struct cfi sc_cfi; 63 bus_addr_t sc_addr; 64 bus_size_t sc_size; 65 struct nor_interface sc_nor_if; 66}; 67 68CFATTACH_DECL_NEW(pq3cfi, sizeof(struct pq3cfi_softc), pq3cfi_match, 69 pq3cfi_attach, pq3cfi_detach, NULL); 70 71static int 72pq3cfi_match(device_t parent, cfdata_t match, void *aux) 73{ 74 struct generic_attach_args *ga = aux; 75 bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE; 76 bus_addr_t addr; 77 struct cfi cfi; 78 int rv; 79 80 KASSERT(ga->ga_bst != NULL); 81 82 addr = ga->ga_addr; 83 if (addr == OBIOCF_ADDR_DEFAULT) { 84 aprint_error("%s: no base address\n", __func__); 85 return 0; 86 } 87 88 cfi.cfi_bst = ga->ga_bst; 89 int error = bus_space_map(cfi.cfi_bst, addr, tmpsize, 0, &cfi.cfi_bsh); 90 if (error != 0) { 91 aprint_error("%s: cannot map %d at offset %#x, error %d\n", __func__, tmpsize, addr, error); 92 return false; 93 } 94 95 if (! cfi_probe(&cfi)) { 96 aprint_debug("%s: probe addr %#x, CFI not found\n", 97 __func__, addr); 98 rv = 0; 99 } else { 100 rv = 1; 101 } 102 103 bus_space_unmap(cfi.cfi_bst, cfi.cfi_bsh, tmpsize); 104 105 return rv; 106} 107 108static void 109pq3cfi_attach(device_t parent, device_t self, void *aux) 110{ 111 struct pq3cfi_softc *sc = device_private(self); 112 struct generic_attach_args *ga = aux; 113 struct cfi_query_data * const qryp = &sc->sc_cfi.cfi_qry_data; 114 const bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE; 115 bool found; 116 int error; 117 118 aprint_normal("\n"); 119 120 sc->sc_dev = self; 121 sc->sc_cfi.cfi_bst = ga->ga_bst; 122 sc->sc_addr = ga->ga_addr; 123 124 /* map enough to identify, remap later when size is known */ 125 error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, tmpsize, 126 0, &sc->sc_cfi.cfi_bsh); 127 if (error != 0) { 128 aprint_error_dev(self, "could not map error %d\n", error); 129 return; 130 } 131 132 found = cfi_identify(&sc->sc_cfi); 133 134 bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, tmpsize); 135 136 if (! found) { 137 /* should not happen, we already probed OK in match */ 138 aprint_error_dev(self, "could not map error %d\n", error); 139 return; 140 } 141 142 sc->sc_size = 1 << qryp->device_size; 143 144 sc->sc_nor_if = nor_interface_cfi; 145 sc->sc_nor_if.private = &sc->sc_cfi; 146 sc->sc_nor_if.access_width = (1 << sc->sc_cfi.cfi_portwidth); 147 148 cfi_print(self, &sc->sc_cfi); 149 150 error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, sc->sc_size, 151 0, &sc->sc_cfi.cfi_bsh); 152 if (error != 0) { 153 aprint_error_dev(self, "could not map error %d\n", error); 154 return; 155 } 156 157 if (! pmf_device_register1(self, NULL, NULL, NULL)) 158 aprint_error_dev(self, "couldn't establish power handler\n"); 159 160 sc->sc_nordev = nor_attach_mi(&sc->sc_nor_if, self); 161 162} 163 164static int 165pq3cfi_detach(device_t self, int flags) 166{ 167 struct pq3cfi_softc *sc = device_private(self); 168 int rv = 0; 169 170 pmf_device_deregister(self); 171 172 if (sc->sc_nordev != NULL) 173 rv = config_detach(sc->sc_nordev, flags); 174 175 bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, sc->sc_size); 176 177 return rv; 178} 179