1224857Snwhitehorn/*-
2224857Snwhitehorn * Copyright (C) 2010 Nathan Whitehorn
3224857Snwhitehorn * Copyright (C) 2011 glevand <geoffrey.levand@mail.ru>
4224857Snwhitehorn * All rights reserved.
5224857Snwhitehorn *
6224857Snwhitehorn * Redistribution and use in source and binary forms, with or without
7224857Snwhitehorn * modification, are permitted provided that the following conditions
8224857Snwhitehorn * are met:
9224857Snwhitehorn * 1. Redistributions of source code must retain the above copyright
10224857Snwhitehorn *    notice, this list of conditions and the following disclaimer,
11224857Snwhitehorn *    without modification, immediately at the beginning of the file.
12224857Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
13224857Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
14224857Snwhitehorn *    documentation and/or other materials provided with the distribution.
15224857Snwhitehorn *
16224857Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17224857Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18224857Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19224857Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20224857Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21224857Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22224857Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23224857Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24224857Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25224857Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26224857Snwhitehorn */
27224857Snwhitehorn
28224857Snwhitehorn#include <sys/cdefs.h>
29224857Snwhitehorn__FBSDID("$FreeBSD: releng/11.0/sys/powerpc/ps3/ps3cdrom.c 295880 2016-02-22 09:02:20Z skra $");
30224857Snwhitehorn
31224857Snwhitehorn#include <sys/param.h>
32224857Snwhitehorn#include <sys/module.h>
33224857Snwhitehorn#include <sys/systm.h>
34224857Snwhitehorn#include <sys/kernel.h>
35224857Snwhitehorn#include <sys/ata.h>
36224857Snwhitehorn#include <sys/bus.h>
37224857Snwhitehorn#include <sys/conf.h>
38224857Snwhitehorn#include <sys/kthread.h>
39224857Snwhitehorn#include <sys/lock.h>
40224857Snwhitehorn#include <sys/malloc.h>
41224857Snwhitehorn#include <sys/mutex.h>
42224857Snwhitehorn
43224857Snwhitehorn#include <vm/vm.h>
44224857Snwhitehorn#include <vm/pmap.h>
45224857Snwhitehorn
46224857Snwhitehorn#include <machine/pio.h>
47224857Snwhitehorn#include <machine/bus.h>
48224857Snwhitehorn#include <machine/platform.h>
49224857Snwhitehorn#include <machine/resource.h>
50224857Snwhitehorn#include <sys/bus.h>
51224857Snwhitehorn#include <sys/rman.h>
52224857Snwhitehorn
53224857Snwhitehorn#include <cam/cam.h>
54224857Snwhitehorn#include <cam/cam_ccb.h>
55224857Snwhitehorn#include <cam/cam_sim.h>
56224857Snwhitehorn#include <cam/cam_xpt_sim.h>
57224857Snwhitehorn#include <cam/cam_debug.h>
58224857Snwhitehorn#include <cam/scsi/scsi_all.h>
59224857Snwhitehorn
60224857Snwhitehorn#include "ps3bus.h"
61224857Snwhitehorn#include "ps3-hvcall.h"
62224857Snwhitehorn
63224857Snwhitehorn#define PS3CDROM_LOCK_INIT(_sc)		\
64224857Snwhitehorn	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), "ps3cdrom", \
65224857Snwhitehorn	    MTX_DEF)
66224857Snwhitehorn#define PS3CDROM_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
67224857Snwhitehorn#define PS3CDROM_LOCK(_sc)		mtx_lock(&(_sc)->sc_mtx)
68224857Snwhitehorn#define	PS3CDROM_UNLOCK(_sc)		mtx_unlock(&(_sc)->sc_mtx)
69224857Snwhitehorn#define PS3CDROM_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
70224857Snwhitehorn#define PS3CDROM_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
71224857Snwhitehorn
72224857Snwhitehorn#define PS3CDROM_MAX_XFERS		3
73224857Snwhitehorn
74224857Snwhitehorn#define	LV1_STORAGE_SEND_ATAPI_COMMAND	0x01
75224857Snwhitehorn
76224857Snwhitehornstruct ps3cdrom_softc;
77224857Snwhitehorn
78224857Snwhitehornstruct ps3cdrom_xfer {
79224857Snwhitehorn	TAILQ_ENTRY(ps3cdrom_xfer) x_queue;
80224857Snwhitehorn	struct ps3cdrom_softc *x_sc;
81224857Snwhitehorn	union ccb *x_ccb;
82224857Snwhitehorn	bus_dmamap_t x_dmamap;
83224857Snwhitehorn	uint64_t x_tag;
84224857Snwhitehorn};
85224857Snwhitehorn
86224857SnwhitehornTAILQ_HEAD(ps3cdrom_xferq, ps3cdrom_xfer);
87224857Snwhitehorn
88224857Snwhitehornstruct ps3cdrom_softc {
89224857Snwhitehorn	device_t sc_dev;
90224857Snwhitehorn
91224857Snwhitehorn	struct mtx sc_mtx;
92224857Snwhitehorn
93224857Snwhitehorn	uint64_t sc_blksize;
94224857Snwhitehorn	uint64_t sc_nblocks;
95224857Snwhitehorn
96224857Snwhitehorn	int sc_irqid;
97224857Snwhitehorn	struct resource	*sc_irq;
98224857Snwhitehorn	void *sc_irqctx;
99224857Snwhitehorn
100224857Snwhitehorn	bus_dma_tag_t sc_dmatag;
101224857Snwhitehorn
102224857Snwhitehorn	struct cam_sim *sc_sim;
103224857Snwhitehorn	struct cam_path *sc_path;
104224857Snwhitehorn
105224857Snwhitehorn	struct ps3cdrom_xfer sc_xfer[PS3CDROM_MAX_XFERS];
106224857Snwhitehorn	struct ps3cdrom_xferq sc_active_xferq;
107224857Snwhitehorn	struct ps3cdrom_xferq sc_free_xferq;
108224857Snwhitehorn};
109224857Snwhitehorn
110224857Snwhitehornenum lv1_ata_proto {
111224857Snwhitehorn	NON_DATA_PROTO		= 0x00,
112224857Snwhitehorn	PIO_DATA_IN_PROTO	= 0x01,
113224857Snwhitehorn	PIO_DATA_OUT_PROTO	= 0x02,
114224857Snwhitehorn	DMA_PROTO		= 0x03
115224857Snwhitehorn};
116224857Snwhitehorn
117224857Snwhitehornenum lv1_ata_in_out {
118224857Snwhitehorn	DIR_WRITE		= 0x00,
119224857Snwhitehorn	DIR_READ		= 0x01
120224857Snwhitehorn};
121224857Snwhitehorn
122224857Snwhitehornstruct lv1_atapi_cmd {
123224857Snwhitehorn	uint8_t pkt[32];
124224857Snwhitehorn	uint32_t pktlen;
125224857Snwhitehorn	uint32_t nblocks;
126224857Snwhitehorn	uint32_t blksize;
127224857Snwhitehorn	uint32_t proto;		/* enum lv1_ata_proto */
128224857Snwhitehorn	uint32_t in_out;	/* enum lv1_ata_in_out */
129224857Snwhitehorn	uint64_t buf;
130224857Snwhitehorn	uint32_t arglen;
131224857Snwhitehorn};
132224857Snwhitehorn
133224857Snwhitehornstatic void ps3cdrom_action(struct cam_sim *sim, union ccb *ccb);
134224857Snwhitehornstatic void ps3cdrom_poll(struct cam_sim *sim);
135224857Snwhitehornstatic void ps3cdrom_async(void *callback_arg, u_int32_t code,
136224857Snwhitehorn    struct cam_path* path, void *arg);
137224857Snwhitehorn
138224857Snwhitehornstatic void ps3cdrom_intr(void *arg);
139224857Snwhitehorn
140224857Snwhitehornstatic void ps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs,
141224857Snwhitehorn    int error);
142224857Snwhitehorn
143224857Snwhitehornstatic int ps3cdrom_decode_lv1_status(uint64_t status,
144224857Snwhitehorn	u_int8_t *sense_key, u_int8_t *asc, u_int8_t *ascq);
145224857Snwhitehorn
146224857Snwhitehornstatic int
147224857Snwhitehornps3cdrom_probe(device_t dev)
148224857Snwhitehorn{
149224857Snwhitehorn	if (ps3bus_get_bustype(dev) != PS3_BUSTYPE_STORAGE ||
150224857Snwhitehorn	    ps3bus_get_devtype(dev) != PS3_DEVTYPE_CDROM)
151224857Snwhitehorn		return (ENXIO);
152224857Snwhitehorn
153224857Snwhitehorn	device_set_desc(dev, "Playstation 3 CDROM");
154224857Snwhitehorn
155224857Snwhitehorn	return (BUS_PROBE_SPECIFIC);
156224857Snwhitehorn}
157224857Snwhitehorn
158224857Snwhitehornstatic int
159224857Snwhitehornps3cdrom_attach(device_t dev)
160224857Snwhitehorn{
161224857Snwhitehorn	struct ps3cdrom_softc *sc = device_get_softc(dev);
162224857Snwhitehorn	struct cam_devq *devq;
163224857Snwhitehorn	struct ps3cdrom_xfer *xp;
164224857Snwhitehorn	struct ccb_setasync csa;
165224857Snwhitehorn	int i, err;
166224857Snwhitehorn
167224857Snwhitehorn	sc->sc_dev = dev;
168224857Snwhitehorn
169224857Snwhitehorn	PS3CDROM_LOCK_INIT(sc);
170224857Snwhitehorn
171224857Snwhitehorn	/* Setup interrupt handler */
172224857Snwhitehorn
173224857Snwhitehorn	sc->sc_irqid = 0;
174224857Snwhitehorn	sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irqid,
175224857Snwhitehorn	    RF_ACTIVE);
176224857Snwhitehorn	if (!sc->sc_irq) {
177224857Snwhitehorn		device_printf(dev, "Could not allocate IRQ\n");
178224857Snwhitehorn		err = ENXIO;
179224857Snwhitehorn		goto fail_destroy_lock;
180224857Snwhitehorn	}
181224857Snwhitehorn
182224857Snwhitehorn	err = bus_setup_intr(dev, sc->sc_irq,
183224857Snwhitehorn	    INTR_TYPE_CAM | INTR_MPSAFE | INTR_ENTROPY,
184224857Snwhitehorn	    NULL, ps3cdrom_intr, sc, &sc->sc_irqctx);
185224857Snwhitehorn	if (err) {
186224857Snwhitehorn		device_printf(dev, "Could not setup IRQ\n");
187224857Snwhitehorn		err = ENXIO;
188224857Snwhitehorn		goto fail_release_intr;
189224857Snwhitehorn	}
190224857Snwhitehorn
191224857Snwhitehorn	/* Setup DMA */
192224857Snwhitehorn
193224857Snwhitehorn	err = bus_dma_tag_create(bus_get_dma_tag(dev), 4096, 0,
194224857Snwhitehorn	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
195224857Snwhitehorn	    BUS_SPACE_UNRESTRICTED, 1, PAGE_SIZE, 0,
196224857Snwhitehorn	    busdma_lock_mutex, &sc->sc_mtx, &sc->sc_dmatag);
197224857Snwhitehorn	if (err) {
198224857Snwhitehorn		device_printf(dev, "Could not create DMA tag\n");
199224857Snwhitehorn		err = ENXIO;
200224857Snwhitehorn		goto fail_teardown_intr;
201224857Snwhitehorn	}
202224857Snwhitehorn
203224857Snwhitehorn	/* Setup transfer queues */
204224857Snwhitehorn
205224857Snwhitehorn	TAILQ_INIT(&sc->sc_active_xferq);
206224857Snwhitehorn	TAILQ_INIT(&sc->sc_free_xferq);
207224857Snwhitehorn
208224857Snwhitehorn	for (i = 0; i < PS3CDROM_MAX_XFERS; i++) {
209224857Snwhitehorn		xp = &sc->sc_xfer[i];
210224857Snwhitehorn		xp->x_sc = sc;
211224857Snwhitehorn
212224857Snwhitehorn		err = bus_dmamap_create(sc->sc_dmatag, BUS_DMA_COHERENT,
213224857Snwhitehorn		    &xp->x_dmamap);
214224857Snwhitehorn		if (err) {
215224857Snwhitehorn			device_printf(dev, "Could not create DMA map (%d)\n",
216224857Snwhitehorn			    err);
217224857Snwhitehorn			goto fail_destroy_dmamap;
218224857Snwhitehorn		}
219224857Snwhitehorn
220224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
221224857Snwhitehorn	}
222224857Snwhitehorn
223224857Snwhitehorn	/* Setup CAM */
224224857Snwhitehorn
225224857Snwhitehorn	devq = cam_simq_alloc(PS3CDROM_MAX_XFERS - 1);
226224857Snwhitehorn	if (!devq) {
227224857Snwhitehorn		device_printf(dev, "Could not allocate SIM queue\n");
228224857Snwhitehorn		err = ENOMEM;
229224857Snwhitehorn		goto fail_destroy_dmatag;
230224857Snwhitehorn	}
231224857Snwhitehorn
232224857Snwhitehorn	sc->sc_sim = cam_sim_alloc(ps3cdrom_action, ps3cdrom_poll, "ps3cdrom",
233224857Snwhitehorn	    sc, device_get_unit(dev), &sc->sc_mtx, PS3CDROM_MAX_XFERS - 1, 0,
234224857Snwhitehorn	    devq);
235224857Snwhitehorn	if (!sc->sc_sim) {
236224857Snwhitehorn		device_printf(dev, "Could not allocate SIM\n");
237224857Snwhitehorn		cam_simq_free(devq);
238224857Snwhitehorn		err = ENOMEM;
239224857Snwhitehorn		goto fail_destroy_dmatag;
240224857Snwhitehorn	}
241224857Snwhitehorn
242224857Snwhitehorn	/* Setup XPT */
243224857Snwhitehorn
244224857Snwhitehorn	PS3CDROM_LOCK(sc);
245224857Snwhitehorn
246224857Snwhitehorn	err = xpt_bus_register(sc->sc_sim, dev, 0);
247224857Snwhitehorn	if (err != CAM_SUCCESS) {
248224857Snwhitehorn		device_printf(dev, "Could not register XPT bus\n");
249224857Snwhitehorn		err = ENXIO;
250224857Snwhitehorn		PS3CDROM_UNLOCK(sc);
251224857Snwhitehorn		goto fail_free_sim;
252224857Snwhitehorn	}
253224857Snwhitehorn
254224857Snwhitehorn	err = xpt_create_path(&sc->sc_path, NULL, cam_sim_path(sc->sc_sim),
255224857Snwhitehorn	    CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
256224857Snwhitehorn	if (err != CAM_REQ_CMP) {
257224857Snwhitehorn		device_printf(dev, "Could not create XPT path\n");
258224857Snwhitehorn		err = ENOMEM;
259224857Snwhitehorn		PS3CDROM_UNLOCK(sc);
260224857Snwhitehorn		goto fail_unregister_xpt_bus;
261224857Snwhitehorn	}
262224857Snwhitehorn
263224857Snwhitehorn	xpt_setup_ccb(&csa.ccb_h, sc->sc_path, 5);
264224857Snwhitehorn	csa.ccb_h.func_code = XPT_SASYNC_CB;
265224857Snwhitehorn	csa.event_enable = AC_LOST_DEVICE;
266224857Snwhitehorn	csa.callback = ps3cdrom_async;
267224857Snwhitehorn	csa.callback_arg = sc->sc_sim;
268224857Snwhitehorn	xpt_action((union ccb *) &csa);
269224857Snwhitehorn
270224857Snwhitehorn	CAM_DEBUG(sc->sc_path, CAM_DEBUG_TRACE,
271224857Snwhitehorn	    ("registered SIM for ps3cdrom%d\n", device_get_unit(dev)));
272224857Snwhitehorn
273224857Snwhitehorn	PS3CDROM_UNLOCK(sc);
274224857Snwhitehorn
275224857Snwhitehorn	return (BUS_PROBE_SPECIFIC);
276224857Snwhitehorn
277224857Snwhitehornfail_unregister_xpt_bus:
278224857Snwhitehorn
279224857Snwhitehorn	xpt_bus_deregister(cam_sim_path(sc->sc_sim));
280224857Snwhitehorn
281224857Snwhitehornfail_free_sim:
282224857Snwhitehorn
283224857Snwhitehorn	cam_sim_free(sc->sc_sim, TRUE);
284224857Snwhitehorn
285224857Snwhitehornfail_destroy_dmamap:
286224857Snwhitehorn
287224857Snwhitehorn	while ((xp = TAILQ_FIRST(&sc->sc_free_xferq))) {
288224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
289224857Snwhitehorn		bus_dmamap_destroy(sc->sc_dmatag, xp->x_dmamap);
290224857Snwhitehorn	}
291224857Snwhitehorn
292224857Snwhitehornfail_destroy_dmatag:
293224857Snwhitehorn
294224857Snwhitehorn	bus_dma_tag_destroy(sc->sc_dmatag);
295224857Snwhitehorn
296224857Snwhitehornfail_teardown_intr:
297224857Snwhitehorn
298224857Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
299224857Snwhitehorn
300224857Snwhitehornfail_release_intr:
301224857Snwhitehorn
302224857Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
303224857Snwhitehorn
304224857Snwhitehornfail_destroy_lock:
305224857Snwhitehorn
306224857Snwhitehorn	PS3CDROM_LOCK_DESTROY(sc);
307224857Snwhitehorn
308224857Snwhitehorn	return (err);
309224857Snwhitehorn}
310224857Snwhitehorn
311224857Snwhitehornstatic int
312224857Snwhitehornps3cdrom_detach(device_t dev)
313224857Snwhitehorn{
314224857Snwhitehorn	struct ps3cdrom_softc *sc = device_get_softc(dev);
315224857Snwhitehorn	int i;
316224857Snwhitehorn
317224857Snwhitehorn	xpt_async(AC_LOST_DEVICE, sc->sc_path, NULL);
318224857Snwhitehorn	xpt_free_path(sc->sc_path);
319224857Snwhitehorn	xpt_bus_deregister(cam_sim_path(sc->sc_sim));
320224857Snwhitehorn	cam_sim_free(sc->sc_sim, TRUE);
321224857Snwhitehorn
322224857Snwhitehorn	for (i = 0; i < PS3CDROM_MAX_XFERS; i++)
323224857Snwhitehorn		bus_dmamap_destroy(sc->sc_dmatag, sc->sc_xfer[i].x_dmamap);
324224857Snwhitehorn
325224857Snwhitehorn	bus_dma_tag_destroy(sc->sc_dmatag);
326224857Snwhitehorn
327224857Snwhitehorn	bus_teardown_intr(dev, sc->sc_irq, sc->sc_irqctx);
328224857Snwhitehorn	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irqid, sc->sc_irq);
329224857Snwhitehorn
330224857Snwhitehorn	PS3CDROM_LOCK_DESTROY(sc);
331224857Snwhitehorn
332224857Snwhitehorn	return (0);
333224857Snwhitehorn}
334224857Snwhitehorn
335224857Snwhitehornstatic void
336224857Snwhitehornps3cdrom_action(struct cam_sim *sim, union ccb *ccb)
337224857Snwhitehorn{
338224857Snwhitehorn	struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *)cam_sim_softc(sim);
339224857Snwhitehorn	device_t dev = sc->sc_dev;
340224857Snwhitehorn	struct ps3cdrom_xfer *xp;
341224857Snwhitehorn	int err;
342224857Snwhitehorn
343224857Snwhitehorn	PS3CDROM_ASSERT_LOCKED(sc);
344224857Snwhitehorn
345224857Snwhitehorn	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
346224857Snwhitehorn	   ("function code 0x%02x\n", ccb->ccb_h.func_code));
347224857Snwhitehorn
348224857Snwhitehorn	switch (ccb->ccb_h.func_code) {
349224857Snwhitehorn	case XPT_SCSI_IO:
350224857Snwhitehorn		if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_INPROG)
351224857Snwhitehorn			break;
352224857Snwhitehorn
353224857Snwhitehorn		if(ccb->ccb_h.target_id > 0) {
354224857Snwhitehorn			ccb->ccb_h.status = CAM_TID_INVALID;
355224857Snwhitehorn			break;
356224857Snwhitehorn		}
357224857Snwhitehorn
358224857Snwhitehorn		if(ccb->ccb_h.target_lun > 0) {
359224857Snwhitehorn			ccb->ccb_h.status = CAM_LUN_INVALID;
360224857Snwhitehorn			break;
361224857Snwhitehorn		}
362224857Snwhitehorn
363224857Snwhitehorn		xp = TAILQ_FIRST(&sc->sc_free_xferq);
364224857Snwhitehorn
365224857Snwhitehorn		KASSERT(xp != NULL, ("no free transfers"));
366224857Snwhitehorn
367224857Snwhitehorn		xp->x_ccb = ccb;
368224857Snwhitehorn
369224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_free_xferq, xp, x_queue);
370224857Snwhitehorn
371246713Skib		err = bus_dmamap_load_ccb(sc->sc_dmatag, xp->x_dmamap,
372246713Skib		    ccb, ps3cdrom_transfer, xp, 0);
373224857Snwhitehorn		if (err && err != EINPROGRESS) {
374224857Snwhitehorn			device_printf(dev, "Could not load DMA map (%d)\n",
375224857Snwhitehorn			    err);
376224857Snwhitehorn
377224857Snwhitehorn			xp->x_ccb = NULL;
378224857Snwhitehorn			TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
379224857Snwhitehorn			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
380224857Snwhitehorn			break;
381224857Snwhitehorn		}
382224857Snwhitehorn		return;
383224857Snwhitehorn	case XPT_SET_TRAN_SETTINGS:
384224857Snwhitehorn		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
385224857Snwhitehorn		break;
386224857Snwhitehorn	case XPT_GET_TRAN_SETTINGS:
387224857Snwhitehorn	{
388224857Snwhitehorn		struct ccb_trans_settings *cts = &ccb->cts;
389224857Snwhitehorn
390224857Snwhitehorn		cts->protocol = PROTO_SCSI;
391224857Snwhitehorn		cts->protocol_version = SCSI_REV_2;
392224857Snwhitehorn		cts->transport = XPORT_SPI;
393224857Snwhitehorn		cts->transport_version = 2;
394224857Snwhitehorn		cts->proto_specific.valid = 0;
395224857Snwhitehorn		cts->xport_specific.valid = 0;
396224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
397224857Snwhitehorn		break;
398224857Snwhitehorn	}
399224857Snwhitehorn	case XPT_RESET_BUS:
400224857Snwhitehorn	case XPT_RESET_DEV:
401224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_CMP;
402224857Snwhitehorn		break;
403224857Snwhitehorn	case XPT_CALC_GEOMETRY:
404224857Snwhitehorn		cam_calc_geometry(&ccb->ccg, 1);
405224857Snwhitehorn		break;
406224857Snwhitehorn	case XPT_PATH_INQ:
407224857Snwhitehorn	{
408224857Snwhitehorn		struct ccb_pathinq *cpi = &ccb->cpi;
409224857Snwhitehorn
410224857Snwhitehorn		cpi->version_num = 1;
411224857Snwhitehorn		cpi->hba_inquiry = 0;
412224857Snwhitehorn		cpi->target_sprt = 0;
413224857Snwhitehorn		cpi->hba_inquiry = PI_SDTR_ABLE;
414224857Snwhitehorn		cpi->hba_misc = PIM_NOBUSRESET | PIM_SEQSCAN | PIM_NO_6_BYTE;
415224857Snwhitehorn		cpi->hba_eng_cnt = 0;
416224857Snwhitehorn		bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags));
417224857Snwhitehorn		cpi->max_target = 0;
418224857Snwhitehorn		cpi->max_lun = 0;
419224857Snwhitehorn		cpi->initiator_id = 7;
420224857Snwhitehorn		cpi->bus_id = cam_sim_bus(sim);
421224857Snwhitehorn		cpi->unit_number = cam_sim_unit(sim);
422224857Snwhitehorn		cpi->base_transfer_speed = 150000;
423224857Snwhitehorn		strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
424224857Snwhitehorn		strncpy(cpi->hba_vid, "Sony", HBA_IDLEN);
425224857Snwhitehorn		strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
426224857Snwhitehorn		cpi->transport = XPORT_SPI;
427224857Snwhitehorn		cpi->transport_version = 2;
428224857Snwhitehorn		cpi->protocol = PROTO_SCSI;
429224857Snwhitehorn		cpi->protocol_version = SCSI_REV_2;
430224857Snwhitehorn		cpi->maxio = PAGE_SIZE;
431224857Snwhitehorn		cpi->ccb_h.status = CAM_REQ_CMP;
432224857Snwhitehorn		break;
433224857Snwhitehorn	}
434224857Snwhitehorn	default:
435224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
436224857Snwhitehorn		    ("unsupported function code 0x%02x\n",
437224857Snwhitehorn		    ccb->ccb_h.func_code));
438224857Snwhitehorn		ccb->ccb_h.status = CAM_REQ_INVALID;
439224857Snwhitehorn		break;
440224857Snwhitehorn	}
441224857Snwhitehorn
442224857Snwhitehorn	xpt_done(ccb);
443224857Snwhitehorn}
444224857Snwhitehorn
445224857Snwhitehornstatic void
446224857Snwhitehornps3cdrom_poll(struct cam_sim *sim)
447224857Snwhitehorn{
448224857Snwhitehorn	ps3cdrom_intr(cam_sim_softc(sim));
449224857Snwhitehorn}
450224857Snwhitehorn
451224857Snwhitehornstatic void
452224857Snwhitehornps3cdrom_async(void *callback_arg, u_int32_t code,
453224857Snwhitehorn	struct cam_path* path, void *arg)
454224857Snwhitehorn{
455224857Snwhitehorn	switch (code) {
456224857Snwhitehorn	case AC_LOST_DEVICE:
457224857Snwhitehorn		xpt_print_path(path);
458224857Snwhitehorn		break;
459224857Snwhitehorn	default:
460224857Snwhitehorn		break;
461224857Snwhitehorn	}
462224857Snwhitehorn}
463224857Snwhitehorn
464224857Snwhitehornstatic void
465224857Snwhitehornps3cdrom_intr(void *arg)
466224857Snwhitehorn{
467224857Snwhitehorn	struct ps3cdrom_softc *sc = (struct ps3cdrom_softc *) arg;
468224857Snwhitehorn	device_t dev = sc->sc_dev;
469224857Snwhitehorn	uint64_t devid = ps3bus_get_device(dev);
470224857Snwhitehorn	struct ps3cdrom_xfer *xp;
471224857Snwhitehorn	union ccb *ccb;
472224857Snwhitehorn	u_int8_t *cdb, sense_key, asc, ascq;
473224857Snwhitehorn	uint64_t tag, status;
474224857Snwhitehorn
475224857Snwhitehorn	if (lv1_storage_get_async_status(devid, &tag, &status) != 0)
476224857Snwhitehorn		return;
477224857Snwhitehorn
478224857Snwhitehorn	PS3CDROM_LOCK(sc);
479224857Snwhitehorn
480224857Snwhitehorn	/* Find transfer with the returned tag */
481224857Snwhitehorn
482224857Snwhitehorn	TAILQ_FOREACH(xp, &sc->sc_active_xferq, x_queue) {
483224857Snwhitehorn		if (xp->x_tag == tag)
484224857Snwhitehorn			break;
485224857Snwhitehorn	}
486224857Snwhitehorn
487224857Snwhitehorn	if (xp) {
488224857Snwhitehorn		ccb = xp->x_ccb;
489224857Snwhitehorn		cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
490224857Snwhitehorn			    ccb->csio.cdb_io.cdb_ptr :
491224857Snwhitehorn			    ccb->csio.cdb_io.cdb_bytes;
492224857Snwhitehorn
493224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
494224857Snwhitehorn		   ("ATAPI command 0x%02x tag 0x%016lx completed (0x%016lx)\n",
495224857Snwhitehorn		    cdb[0], tag, status));
496224857Snwhitehorn
497224857Snwhitehorn		if (!status) {
498224857Snwhitehorn			ccb->csio.scsi_status = SCSI_STATUS_OK;
499224857Snwhitehorn			ccb->csio.resid = 0;
500224857Snwhitehorn			ccb->ccb_h.status = CAM_REQ_CMP;
501224857Snwhitehorn		} else {
502224857Snwhitehorn			ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
503224857Snwhitehorn			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
504224857Snwhitehorn
505224857Snwhitehorn			if (!ps3cdrom_decode_lv1_status(status, &sense_key,
506224857Snwhitehorn			    &asc, &ascq)) {
507224857Snwhitehorn
508224857Snwhitehorn				CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
509224857Snwhitehorn				   ("sense key 0x%02x asc 0x%02x ascq 0x%02x\n",
510224857Snwhitehorn				    sense_key, asc, ascq));
511224857Snwhitehorn
512225950Sken				scsi_set_sense_data(&ccb->csio.sense_data,
513225950Sken				    /*sense_format*/ SSD_TYPE_NONE,
514225950Sken				    /*current_error*/ 1,
515225950Sken				    sense_key,
516225950Sken				    asc,
517225950Sken				    ascq,
518225950Sken				    SSD_ELEM_NONE);
519225950Sken				ccb->csio.sense_len = SSD_FULL_SIZE;
520224857Snwhitehorn				ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR |
521224857Snwhitehorn				    CAM_AUTOSNS_VALID;
522224857Snwhitehorn			}
523224857Snwhitehorn
524224857Snwhitehorn			if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)
525224857Snwhitehorn				ccb->csio.resid = ccb->csio.dxfer_len;
526224857Snwhitehorn		}
527224857Snwhitehorn
528224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_IN)
529224857Snwhitehorn			bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
530224857Snwhitehorn			    BUS_DMASYNC_POSTREAD);
531224857Snwhitehorn
532224857Snwhitehorn		bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
533224857Snwhitehorn
534224857Snwhitehorn		xp->x_ccb = NULL;
535224857Snwhitehorn		TAILQ_REMOVE(&sc->sc_active_xferq, xp, x_queue);
536224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
537224857Snwhitehorn
538224857Snwhitehorn		xpt_done(ccb);
539224857Snwhitehorn	} else {
540224857Snwhitehorn		device_printf(dev,
541224857Snwhitehorn		    "Could not find transfer with tag 0x%016lx\n",  tag);
542224857Snwhitehorn	}
543224857Snwhitehorn
544224857Snwhitehorn	PS3CDROM_UNLOCK(sc);
545224857Snwhitehorn}
546224857Snwhitehorn
547224857Snwhitehornstatic void
548224857Snwhitehornps3cdrom_transfer(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
549224857Snwhitehorn{
550224857Snwhitehorn	struct ps3cdrom_xfer *xp = (struct ps3cdrom_xfer *) arg;
551224857Snwhitehorn	struct ps3cdrom_softc *sc = xp->x_sc;
552224857Snwhitehorn	device_t dev = sc->sc_dev;
553224857Snwhitehorn	uint64_t devid = ps3bus_get_device(dev);
554224857Snwhitehorn	union ccb *ccb = xp->x_ccb;
555224857Snwhitehorn	u_int8_t *cdb;
556224857Snwhitehorn	uint64_t start_sector, block_count;
557224857Snwhitehorn	int err;
558224857Snwhitehorn
559255943Snwhitehorn	KASSERT(nsegs == 1 || nsegs == 0,
560255943Snwhitehorn	    ("ps3cdrom_transfer: invalid number of DMA segments %d", nsegs));
561255943Snwhitehorn	KASSERT(error == 0, ("ps3cdrom_transfer: DMA error %d", error));
562224857Snwhitehorn
563224857Snwhitehorn	PS3CDROM_ASSERT_LOCKED(sc);
564224857Snwhitehorn
565224857Snwhitehorn	if (error) {
566224857Snwhitehorn		device_printf(dev, "Could not load DMA map (%d)\n",  error);
567224857Snwhitehorn
568224857Snwhitehorn		xp->x_ccb = NULL;
569224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
570224857Snwhitehorn		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
571224857Snwhitehorn		xpt_done(ccb);
572224857Snwhitehorn		return;
573224857Snwhitehorn	}
574224857Snwhitehorn
575224857Snwhitehorn	cdb = (ccb->ccb_h.flags & CAM_CDB_POINTER) ?
576224857Snwhitehorn		    ccb->csio.cdb_io.cdb_ptr :
577224857Snwhitehorn		    ccb->csio.cdb_io.cdb_bytes;
578224857Snwhitehorn
579224857Snwhitehorn	CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
580224857Snwhitehorn	   ("ATAPI command 0x%02x cdb_len %d dxfer_len %d\n ", cdb[0],
581224857Snwhitehorn	    ccb->csio.cdb_len, ccb->csio.dxfer_len));
582224857Snwhitehorn
583224857Snwhitehorn	switch (cdb[0]) {
584224857Snwhitehorn	case READ_10:
585255943Snwhitehorn		KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to read"));
586224857Snwhitehorn		start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
587224857Snwhitehorn		    (cdb[4] << 8) | cdb[5];
588224857Snwhitehorn		block_count = (cdb[7] << 8) | cdb[8];
589224857Snwhitehorn
590224857Snwhitehorn		err = lv1_storage_read(devid, 0 /* region id */,
591224857Snwhitehorn		    start_sector, block_count, 0 /* flags */, segs[0].ds_addr,
592224857Snwhitehorn		    &xp->x_tag);
593224857Snwhitehorn		bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
594224857Snwhitehorn		    BUS_DMASYNC_POSTREAD);
595224857Snwhitehorn		break;
596224857Snwhitehorn	case WRITE_10:
597255943Snwhitehorn		KASSERT(nsegs == 1, ("ps3cdrom_transfer: no data to write"));
598224857Snwhitehorn		start_sector = (cdb[2] << 24) | (cdb[3] << 16) |
599224857Snwhitehorn		    (cdb[4] << 8) | cdb[5];
600224857Snwhitehorn		block_count = (cdb[7] << 8) | cdb[8];
601224857Snwhitehorn
602224857Snwhitehorn		bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
603224857Snwhitehorn		    BUS_DMASYNC_PREWRITE);
604224857Snwhitehorn		err = lv1_storage_write(devid, 0 /* region id */,
605224857Snwhitehorn		    start_sector, block_count, 0 /* flags */,
606224857Snwhitehorn		    segs[0].ds_addr, &xp->x_tag);
607224857Snwhitehorn		break;
608224857Snwhitehorn	default:
609224857Snwhitehorn		{
610224857Snwhitehorn		struct lv1_atapi_cmd atapi_cmd;
611224857Snwhitehorn
612224857Snwhitehorn		bzero(&atapi_cmd, sizeof(atapi_cmd));
613224857Snwhitehorn		atapi_cmd.pktlen = 12;
614224857Snwhitehorn		bcopy(cdb, atapi_cmd.pkt, ccb->csio.cdb_len);
615224857Snwhitehorn
616224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_IN) {
617224857Snwhitehorn			atapi_cmd.in_out = DIR_READ;
618224857Snwhitehorn			atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
619224857Snwhitehorn			    DMA_PROTO : PIO_DATA_IN_PROTO;
620224857Snwhitehorn		} else if (ccb->ccb_h.flags & CAM_DIR_OUT) {
621224857Snwhitehorn			atapi_cmd.in_out = DIR_WRITE;
622224857Snwhitehorn			atapi_cmd.proto = (ccb->csio.dxfer_len >= 2048) ?
623224857Snwhitehorn			    DMA_PROTO : PIO_DATA_OUT_PROTO;
624224857Snwhitehorn		} else {
625224857Snwhitehorn			atapi_cmd.proto = NON_DATA_PROTO;
626224857Snwhitehorn		}
627224857Snwhitehorn
628255943Snwhitehorn		atapi_cmd.nblocks = atapi_cmd.arglen =
629255943Snwhitehorn		    (nsegs == 0) ? 0 : segs[0].ds_len;
630224857Snwhitehorn		atapi_cmd.blksize = 1;
631255943Snwhitehorn		atapi_cmd.buf = (nsegs == 0) ? 0 : segs[0].ds_addr;
632224857Snwhitehorn
633224857Snwhitehorn		if (ccb->ccb_h.flags & CAM_DIR_OUT)
634224857Snwhitehorn			bus_dmamap_sync(sc->sc_dmatag, xp->x_dmamap,
635224857Snwhitehorn			    BUS_DMASYNC_PREWRITE);
636224857Snwhitehorn
637224857Snwhitehorn		err = lv1_storage_send_device_command(devid,
638224857Snwhitehorn		    LV1_STORAGE_SEND_ATAPI_COMMAND, vtophys(&atapi_cmd),
639224857Snwhitehorn		    sizeof(atapi_cmd), atapi_cmd.buf, atapi_cmd.arglen,
640224857Snwhitehorn		    &xp->x_tag);
641224857Snwhitehorn
642224857Snwhitehorn		break;
643224857Snwhitehorn		}
644224857Snwhitehorn	}
645224857Snwhitehorn
646224857Snwhitehorn	if (err) {
647224857Snwhitehorn		device_printf(dev, "ATAPI command 0x%02x failed (%d)\n",
648224857Snwhitehorn		    cdb[0], err);
649224857Snwhitehorn
650224857Snwhitehorn		bus_dmamap_unload(sc->sc_dmatag, xp->x_dmamap);
651224857Snwhitehorn
652224857Snwhitehorn		xp->x_ccb = NULL;
653224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_free_xferq, xp, x_queue);
654224857Snwhitehorn
655225950Sken		bzero(&ccb->csio.sense_data, sizeof(ccb->csio.sense_data));
656225950Sken		/* Invalid field in parameter list */
657225950Sken		scsi_set_sense_data(&ccb->csio.sense_data,
658225950Sken				    /*sense_format*/ SSD_TYPE_NONE,
659225950Sken				    /*current_error*/ 1,
660225950Sken				    /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
661225950Sken				    /*asc*/ 0x26,
662225950Sken				    /*ascq*/ 0x00,
663225950Sken				    SSD_ELEM_NONE);
664225950Sken
665225950Sken		ccb->csio.sense_len = SSD_FULL_SIZE;
666225950Sken		ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
667224857Snwhitehorn		ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID;
668224857Snwhitehorn		xpt_done(ccb);
669224857Snwhitehorn	} else {
670224857Snwhitehorn		CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
671224857Snwhitehorn		   ("ATAPI command 0x%02x tag 0x%016lx submitted\n ", cdb[0],
672224857Snwhitehorn		   xp->x_tag));
673224857Snwhitehorn
674224857Snwhitehorn		TAILQ_INSERT_TAIL(&sc->sc_active_xferq, xp, x_queue);
675224857Snwhitehorn		ccb->ccb_h.status |= CAM_SIM_QUEUED;
676224857Snwhitehorn	}
677224857Snwhitehorn}
678224857Snwhitehorn
679224857Snwhitehornstatic int
680224857Snwhitehornps3cdrom_decode_lv1_status(uint64_t status, u_int8_t *sense_key, u_int8_t *asc,
681224857Snwhitehorn    u_int8_t *ascq)
682224857Snwhitehorn{
683224857Snwhitehorn	if (((status >> 24) & 0xff) != SCSI_STATUS_CHECK_COND)
684224857Snwhitehorn		return -1;
685224857Snwhitehorn
686224857Snwhitehorn	*sense_key = (status >> 16) & 0xff;
687224857Snwhitehorn	*asc = (status >> 8) & 0xff;
688224857Snwhitehorn	*ascq = status & 0xff;
689224857Snwhitehorn
690224857Snwhitehorn	return (0);
691224857Snwhitehorn}
692224857Snwhitehorn
693224857Snwhitehornstatic device_method_t ps3cdrom_methods[] = {
694224857Snwhitehorn	DEVMETHOD(device_probe,		ps3cdrom_probe),
695224857Snwhitehorn	DEVMETHOD(device_attach,	ps3cdrom_attach),
696224857Snwhitehorn	DEVMETHOD(device_detach,	ps3cdrom_detach),
697224857Snwhitehorn	{0, 0},
698224857Snwhitehorn};
699224857Snwhitehorn
700224857Snwhitehornstatic driver_t ps3cdrom_driver = {
701224857Snwhitehorn	"ps3cdrom",
702224857Snwhitehorn	ps3cdrom_methods,
703224857Snwhitehorn	sizeof(struct ps3cdrom_softc),
704224857Snwhitehorn};
705224857Snwhitehorn
706224857Snwhitehornstatic devclass_t ps3cdrom_devclass;
707224857Snwhitehorn
708224857SnwhitehornDRIVER_MODULE(ps3cdrom, ps3bus, ps3cdrom_driver, ps3cdrom_devclass, 0, 0);
709224857SnwhitehornMODULE_DEPEND(ps3cdrom, cam, 1, 1, 1);
710