pq3cfi.c revision 1.2
1/* $NetBSD: pq3cfi.c,v 1.2 2011/07/17 23:08:56 dyoung Exp $ */ 2 3/* 4 * NOR CFI driver support for booke 5 */ 6 7#include "opt_flash.h" 8#include "locators.h" 9 10#include <sys/cdefs.h> 11__KERNEL_RCSID(0, "$NetBSD: pq3cfi.c,v 1.2 2011/07/17 23:08:56 dyoung Exp $"); 12 13#include <sys/param.h> 14#include <sys/systm.h> 15#include <sys/cdefs.h> 16#include <sys/device.h> 17#include <sys/endian.h> 18 19#include <sys/bus.h> 20 21#include <powerpc/booke/cpuvar.h> 22 23#include <dev/nor/nor.h> 24#include <dev/nor/cfi.h> 25 26 27static int pq3cfi_match(device_t, cfdata_t, void *); 28static void pq3cfi_attach(device_t, device_t, void *); 29static int pq3cfi_detach(device_t, int); 30 31struct pq3cfi_softc { 32 device_t sc_dev; 33 device_t sc_nordev; 34 struct cfi sc_cfi; 35 bus_addr_t sc_addr; 36 bus_size_t sc_size; 37 struct nor_interface sc_nor_if; 38}; 39 40CFATTACH_DECL_NEW(pq3cfi, sizeof(struct pq3cfi_softc), pq3cfi_match, 41 pq3cfi_attach, pq3cfi_detach, NULL); 42 43/* 44 * pq3cfi_addr - return bus address for the CFI NOR flash 45 * 46 * if the chip select not specified, use address from attach args 47 * otherwise use address from the chip select. 48 */ 49static inline bus_addr_t 50pq3cfi_addr(struct generic_attach_args *ga) 51{ 52 bus_addr_t addr_aa = ga->ga_addr; 53 54 if (ga->ga_cs != OBIOCF_CS_DEFAULT) { 55#ifdef NOTYET 56 bus_addr_t addr_cs = get_addr_from_cs(ga->ga_cs); 57 if (addr_aa != addr_cs) 58 aprint_warn("%s: configured addr %#x, CS%d addr %#x\n", 59 __func__, addr_aa, ga->ga_cs, addr_cs); 60 return addr_cs; 61#endif 62 } 63 return addr_aa; 64} 65 66static int 67pq3cfi_match(device_t parent, cfdata_t match, void *aux) 68{ 69 struct generic_attach_args *ga = aux; 70 bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE; 71 bus_addr_t addr; 72 struct cfi cfi; 73 int rv; 74 75 KASSERT(ga->ga_bst != NULL); 76 77 addr = pq3cfi_addr(ga); 78 if (addr == OBIOCF_ADDR_DEFAULT) { 79 aprint_error("%s: no base address\n", __func__); 80 return 0; 81 } 82 83 cfi.cfi_bst = ga->ga_bst; 84 int error = bus_space_map(cfi.cfi_bst, addr, tmpsize, 0, &cfi.cfi_bsh); 85 if (error != 0) { 86 aprint_error("%s: cannot map %d at offset %#x, error %d\n", __func__, tmpsize, addr, error); 87 return false; 88 } 89 90 if (! cfi_probe(&cfi)) { 91 aprint_debug("%s: probe addr %#x, CFI not found\n", 92 __func__, addr); 93 rv = 0; 94 } else { 95 rv = 1; 96 } 97 98 bus_space_unmap(cfi.cfi_bst, cfi.cfi_bsh, tmpsize); 99 100 return rv; 101} 102 103static void 104pq3cfi_attach(device_t parent, device_t self, void *aux) 105{ 106 struct pq3cfi_softc *sc = device_private(self); 107 struct generic_attach_args *ga = aux; 108 struct cfi_query_data * const qryp = &sc->sc_cfi.cfi_qry_data; 109 const bus_size_t tmpsize = CFI_QRY_MIN_MAP_SIZE; 110 bool found; 111 int error; 112 113 aprint_normal("\n"); 114 115 sc->sc_dev = self; 116 sc->sc_cfi.cfi_bst = ga->ga_bst; 117 sc->sc_addr = pq3cfi_addr(ga); 118 119 /* map enough to identify, remap later when size is known */ 120 error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, tmpsize, 121 0, &sc->sc_cfi.cfi_bsh); 122 if (error != 0) { 123 aprint_error_dev(self, "could not map error %d\n", error); 124 return; 125 } 126 127 found = cfi_identify(&sc->sc_cfi); 128 129 bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, tmpsize); 130 131 if (! found) { 132 /* should not happen, we already probed OK in match */ 133 aprint_error_dev(self, "could not map error %d\n", error); 134 return; 135 } 136 137 sc->sc_size = 1 << qryp->device_size; 138 139 sc->sc_nor_if = nor_interface_cfi; 140 sc->sc_nor_if.private = &sc->sc_cfi; 141 sc->sc_nor_if.access_width = (1 << sc->sc_cfi.cfi_portwidth); 142 143 cfi_print(self, &sc->sc_cfi); 144 145 error = bus_space_map(sc->sc_cfi.cfi_bst, sc->sc_addr, sc->sc_size, 146 0, &sc->sc_cfi.cfi_bsh); 147 if (error != 0) { 148 aprint_error_dev(self, "could not map error %d\n", error); 149 return; 150 } 151 152 if (! pmf_device_register1(self, NULL, NULL, NULL)) 153 aprint_error_dev(self, "couldn't establish power handler\n"); 154 155 sc->sc_nordev = nor_attach_mi(&sc->sc_nor_if, self); 156 157} 158 159static int 160pq3cfi_detach(device_t self, int flags) 161{ 162 struct pq3cfi_softc *sc = device_private(self); 163 int rv = 0; 164 165 pmf_device_deregister(self); 166 167 if (sc->sc_nordev != NULL) 168 rv = config_detach(sc->sc_nordev, flags); 169 170 bus_space_unmap(sc->sc_cfi.cfi_bst, sc->sc_cfi.cfi_bsh, sc->sc_size); 171 172 return rv; 173} 174