root/usr/src/uts/sun4u/sunfire/sys/ac.h
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef _SYS_AC_H
#define _SYS_AC_H

#pragma ident   "%Z%%M% %I%     %E% SMI"

#ifdef  __cplusplus
extern "C" {
#endif

/* useful debugging stuff */
#define AC_ATTACH_DEBUG         0x1
#define AC_REGISTERS_DEBUG      0x2

/*
 * OBP supplies us with two register sets for the AC nodes. They are:
 *
 *      0               miscellaneous regs
 *      1               Cache tags
 *
 * We do not use the cache tags for anything in the kernel, so we
 * do not map them in.
 */

/* Macros for physical acccess, fhc.h has to be present */
#define AC_OFFSET               0x00001000000ull
#define AC_CENTRAL              0x80000000
#define AC_ARB_FAST             0x00002000
#define AC_BCSR(board)          (FHC_BOARD_BASE(2*(board)) + FHC_OFFSET + \
                                AC_OFFSET + AC_OFF_BCSR)

/* Register set 0 Offsets */
#define AC_OFF_BRCS             0x10
#define AC_OFF_BCSR             0x20
#define AC_OFF_ESR              0x30
#define AC_OFF_EMR              0x40
#define AC_OFF_MEMCTL           0x60
#define AC_OFF_MEMDEC0          0x70
#define AC_OFF_MEMDEC1          0x80
#define AC_OFF_UPA0             0x2000
#define AC_OFF_UPA1             0x4000
#define AC_OFF_CNTR             0x6000
#define AC_OFF_MCCR             0x6020

/* Use predefined strings to name the kstats from this driver. */
#define AC_KSTAT_NAME           "address_controller"
#define MEMCTL_KSTAT_NAMED      "acmemctl"
#define MEMDECODE0_KSTAT_NAMED  "acmemdecode0"
#define MEMDECODE1_KSTAT_NAMED  "acmemdecode1"
#define CNTR_KSTAT_NAMED        "accounter"
#define MCCR_KSTAT_NAMED        "acmccr"
#define BANK_0_KSTAT_NAMED      "acbank0"
#define BANK_1_KSTAT_NAMED      "acbank1"

/* used for the picN kstats */
#define AC_NUM_PICS     2
#define AC_COUNTER_TO_PIC0(CNTR)        ((CNTR) & 0xFFFFFFFFULL)
#define AC_COUNTER_TO_PIC1(CNTR)        ((CNTR) >> 32)

/* used to clear/set the pcr */
#define AC_CLEAR_PCR(PCR)               ((PCR) & ~(0x3F3F))
#define AC_SET_HOT_PLUG(PCR)            ((PCR) | (0x3F3F))

/* used for programming the pic */
#define AC_SET_PIC_BUS_PAUSE(BRD)       (0x80000000LL - 0x9ac4 - ((BRD) << 3))

/* defines for AC Board Configuration and Status Register */
#define NO_CACHE        0
#define CACHE_512K      2
#define CACHE_1M        3
#define CACHE_2M        4
#define CACHE_4M        5
#define CACHE_8M        6
#define CACHE_16M       7

#define ARB_MASTER      0x8000
#define ARB_INIT        0x4000
#define ARB_FAST        0x2000
#define FTC_CPAR        0x0200

#define AC_CSR_REFEN            (1ULL << 27)

/* defines for Memory decode registers */
#define AC_MEM_VALID            0x8000000000000000ULL

/* size of a memory SIMM group */
#define RASIZE0(memctl)         (8 << ((((memctl) >> 8) & 0x7) << 1))
#define RASIZE1(memctl)         (8 << ((((memctl) >> 11) & 0x7) << 1))
#define RATBL0(memctl)          (((memctl) >> 8) & 0x7)
#define RATBL1(memctl)          (((memctl) >> 11) & 0x7)

/*
 * Interleave factor of a memory SIMM group.
 * Possible values are 1, 2, 4, 8, and 16. 1 means not interleaved.
 * Larger groups can be interleaved with smaller groups. Groups
 * on the same board can be interleaved as well.
 */
#define INTLV0(memctl)          (1 << ((memctl) & 0x7))
#define INTLV1(memctl)          (1 << (((memctl) >> 3) & 0x7))
#define INTVAL0(memctl)         ((memctl) & 0x7)
#define INTVAL1(memctl)         (((memctl) >> 3) & 0x7)

/*
 * Physical base mask of a memory SIMM group. Note that this is
 * not the real physical base, and is just used to match up the
 * interleaving of groups. The mask bits (UK) are used to mask
 * out the match (UM) field so that the bases can be compared.
 */
#define GRP_UK(memdec)  (((memdec) >> 39) & 0xFFF)
#define GRP_UM(memdec)  (((memdec) >> 12) & 0x7FFF)
#define GRP_BASE(memdec) (GRP_UM(memdec) & ~(GRP_UK(memdec)))
#define GRP_LK(memdec)  (((memdec) >> 6) & 0xf)
#define GRP_LM(memdec)  ((memdec) & 0xf)
#define GRP_LBASE(memdec) (GRP_LM(memdec) & ~(GRP_LK(memdec)))
#define GRP_REALBASE(m) ((GRP_BASE(m) << 26) | (GRP_LBASE(m) << 6))
#define GRP_UK2SPAN(memdec) ((GRP_UK(memdec) + 1) << 26)
#define GRP_SPANMB(memdec) (GRP_UK2SPAN(memdec) >> 20)

/*
 * memory states and conditions for sunfire memory system
 */
enum ac_bank_id { Bank0 = 0, Bank1 = 1 };
enum ac_bank_status { StUnknown = 0, StNoMem, StBad, StActive, StSpare };
enum ac_bank_condition { ConUnknown = 0, ConOK, ConFailing, ConFailed,
                        ConTest, ConBad };

/*
 * AC memory bank ioctl interface.
 */

/* 'G' (for gigabytes!) does not appear to be used elsewhere in the kernel */
#define AC_IOC          ('G'<<8)

/*
 * For all AC_MEM_ ioctls the arg pointer points to a sysc_cfga_cmd_t
 * except for AC_MEM_ADMIN_VER. The private pointer then points to a
 * structure of the appropriate type, if required.
 */
#define AC_MEM_ADMIN_VER        (AC_IOC|0)      /* arg is &ac_mem_version_t */
#define AC_MEM_CONFIGURE        (AC_IOC|1)      /* private == NULL */
#define AC_MEM_UNCONFIGURE      (AC_IOC|2)      /* private == NULL */
#define AC_MEM_STAT             (AC_IOC|3)      /* ac_stat_t */
#define AC_MEM_TEST_START       (AC_IOC|4)      /* ac_mem_test_start_t */
#define AC_MEM_TEST_STOP        (AC_IOC|5)      /* ac_mem_test_stop_t */
#define AC_MEM_TEST_READ        (AC_IOC|6)      /* ac_mem_test_read_t */
#define AC_MEM_TEST_WRITE       (AC_IOC|7)      /* ac_mem_test_write_t */
#define AC_MEM_EXERCISE         (AC_IOC|128)    /* various */

#define AC_OUTPUT_LEN           MAXPATHLEN              /* output str len */

typedef enum {
        AC_ERR_DEFAULT = 0,     /* generic errors */
        AC_ERR_INTRANS,         /* hardware in transition */
        AC_ERR_UTHREAD,         /* can't stop user thread */
        AC_ERR_KTHREAD,         /* can't stop kernel thread */
        AC_ERR_SUSPEND,         /* can't suspend a device */
        AC_ERR_RESUME,          /* can't resume a device */
        AC_ERR_POWER,           /* not enough power for slot */
        AC_ERR_COOLING,         /* not enough cooling for slot */
        AC_ERR_PRECHARGE,       /* not enough precharge for slot */
        AC_ERR_HOTPLUG,         /* Hot Plug Unavailable */
        AC_ERR_HW_COMPAT,       /* incompatible hardware found during dr */
        AC_ERR_NON_DR_PROM,     /* prom not support Dynamic Reconfiguration */
        AC_ERR_CORE_RESOURCE,   /* core resource cannot be removed */
        AC_ERR_PROM,            /* error encountered in OBP/POST */
        AC_ERR_DR_INIT,         /* error encountered in sysc_dr_init op */
        AC_ERR_NDI_ATTACH,      /* error encountered in NDI attach operations */
        AC_ERR_NDI_DETACH,      /* error encountered in NDI detach operations */
        AC_ERR_RSTATE,          /* wrong receptacle state */
        AC_ERR_OSTATE,          /* wrong occupant state */
        AC_ERR_COND,            /* invalid condition */
        AC_ERR_BD,              /* invalid board id */
        AC_ERR_BD_TYPE,         /* invalid board type */
        AC_ERR_BD_STATE,        /* invalid board state */
        AC_ERR_MEM_PERM,        /* no write permission */
        AC_ERR_MEM_BK,          /* invalid memory bank */
        AC_ERR_MEM_TEST,        /* invalid memory test id */
        AC_ERR_MEM_TEST_PAR,    /* invalid memory test parameter(s) */
        AC_ERR_KPM_CANCELLED,   /* kphysm_del_cancel (for complete) */
        AC_ERR_KPM_REFUSED,     /* kphysm_pre_del failed (for complete) */
        AC_ERR_KPM_SPAN,        /* memory already in use (add) */
        AC_ERR_KPM_DUP,         /* memory span duplicate (delete) */
        AC_ERR_KPM_FAULT,       /* memory access test failed (add) */
        AC_ERR_KPM_RESOURCE,    /* some resource was not available */
        AC_ERR_KPM_NOTSUP,      /* operation not supported */
        AC_ERR_KPM_NOHANDLES,   /* cannot allocate any more handles */
        AC_ERR_KPM_NONRELOC,    /* non-relocatable pages in span */
        AC_ERR_KPM_HANDLE,      /* bad handle supplied */
        AC_ERR_KPM_BUSY,        /* memory in span is being deleted */
        AC_ERR_KPM_NOTVIABLE,   /* vM viability test failed */
        AC_ERR_KPM_SEQUENCE,    /* function called out of sequence */
        AC_ERR_KPM_NOWORK,      /* no pages to delete */
        AC_ERR_KPM_NOTFINISHED, /* thread not finished */
        AC_ERR_KPM_NOTRUNNING,  /* thread not running */
        AC_ERR_VMEM,            /* insufficient virtual memory */
        AC_ERR_INTR,            /* delete interrupt by user */
        AC_ERR_TIMEOUT,         /* delete timed out */
        AC_ERR_MEM_DEINTLV      /* could not de-interleave memory */
} ac_err_t;

/*
 * Config admin command structure for AC_MEM ioctls.
 */
typedef struct ac_cfga_cmd {
        uint_t          force:1;        /* force this state transition */
        uint_t          test:1;         /* Need to test hardware */
        int             arg;            /* generic data for test */
        ac_err_t        errtype;        /* error code returned */
        char            *outputstr;     /* output returned from ioctl */
        void            *private;       /* command private data */
} ac_cfga_cmd_t;

typedef struct ac_cfga_cmd32 {
        uint_t          force:1;        /* force this state transition */
        uint_t          test:1;         /* Need to test hardware */
        int             arg;            /* generic data for test */
        ac_err_t        errtype;        /* error code returned */
        caddr32_t       outputstr;      /* output returned from ioctl */
        caddr32_t       private;        /* command private data */
} ac_cfga_cmd32_t;

typedef uint_t ac_mem_version_t;                /* platform interface rev */
#define AC_MEM_ADMIN_VERSION    1

typedef uint_t mem_test_handle_t;

typedef struct {
        uint64_t                module_id;
        uint64_t                afsr;
        uint64_t                afar;
        uint64_t                udbh_error_reg;
        uint64_t                udbl_error_reg;
} sunfire_processor_error_regs_t;

/*
 * page_size gives the requires size for the read or write buffer.
 * A read can be restricted to one or more line_size units starting
 * at a multiple of line_size units from the start of the page.
 * afar_base is the physical base of the bank being tested so
 * that the afar value can be translated to an offset into the bank.
 */
typedef struct {
        mem_test_handle_t       handle;
        pid_t                   tester_pid;     /* PID of test starter */
        sysc_cfga_cond_t        prev_condition;
        u_longlong_t            bank_size;      /* bytes */
        uint_t                  page_size;      /* bytes */
        uint_t                  line_size;      /* bytes */
        u_longlong_t            afar_base;
} ac_mem_test_start_t;

typedef struct {
        mem_test_handle_t       handle;
        sysc_cfga_cond_t        condition;
} ac_mem_test_stop_t;

/*
 * line_offset is in the range 0 - (page_size/line_size)-1
 * line_count is in the range 1 - (page_size/line_size)
 */
typedef struct {
        u_longlong_t            page_num;
        uint_t                  line_offset;
        uint_t                  line_count;
} ac_test_addr_t;

/*
 * Data will be transferred in/out of the buffer at:
 *              (page_buf + (line_offset*line_size))
 */
typedef struct {
        mem_test_handle_t       handle;
        void                    *page_buf;
        ac_test_addr_t          address;
        sunfire_processor_error_regs_t  *error_buf;
} ac_mem_test_read_t;

typedef struct {
        mem_test_handle_t       handle;
        void                    *page_buf;
        ac_test_addr_t          address;
} ac_mem_test_write_t;

#ifdef _SYSCALL32

/* Kernel's view of ILP32 structure version. */

typedef struct {
        mem_test_handle_t       handle;
        caddr32_t               page_buf;               /* void * */
        ac_test_addr_t          address;
        caddr32_t               error_buf; /* sunfire_processor_error_regs_t */
} ac_mem_test_read32_t;

typedef struct {
        mem_test_handle_t       handle;
        caddr32_t               page_buf;               /* void * */
        ac_test_addr_t          address;
} ac_mem_test_write32_t;

#endif /* _SYSCALL32 */

/* structure returned from AC_MEM_STAT ioctl */
typedef struct {
        sysc_cfga_rstate_t      rstate;
        sysc_cfga_ostate_t      ostate;
        sysc_cfga_cond_t        condition;
        time_t                  status_time;
        uint_t                  board;
        uint_t                  real_size;
        uint_t                  use_size;
        uint_t                  busy;           /* add/delete in progress */
        uint_t                  page_size;      /* bytes */
        uint64_t                phys_pages;
        uint64_t                managed;
        uint64_t                nonrelocatable;
        /* to supply address, group, info */
        uint64_t                ac_memctl;
        uint64_t                ac_decode0;
        uint64_t                ac_decode1;
} ac_stat_t;

#ifdef _SYSCALL32

/* Kernel's view of ILP32 structure version. */

typedef struct {
        sysc_cfga_rstate_t      rstate;
        sysc_cfga_ostate_t      ostate;
        sysc_cfga_cond_t        condition;
        time32_t                status_time;
        uint_t                  board;
        uint_t                  real_size;
        uint_t                  use_size;
        uint_t                  busy;           /* add/delete in progress */
        uint_t                  page_size;      /* bytes */
        uint64_t                phys_pages;
        uint64_t                managed;
        uint64_t                nonrelocatable;
        /* to supply address, group, info */
        uint64_t                ac_memctl;
        uint64_t                ac_decode0;
        uint64_t                ac_decode1;
} ac_stat32_t;

#endif /* _SYSCALL32 */

/* Command values in cmd_cfga.arg for the AC_MEM_EXERCISE ioctl. */
#define AC_MEMX_RELOCATE_ALL    0

/* Stats structure for AC_MEMX_RELOCATE_ALL (cmd_cfga.private != NULL). */
struct ac_memx_relocate_stats {
        uint_t          base;
        uint_t          npgs;
        uint_t          nopaget;
        uint_t          nolock;
        uint_t          isfree;
        uint_t          reloc;
        uint_t          noreloc;
};

/* End of ioctl interface. */

#if defined(_KERNEL)

typedef struct {
        ac_cfga_cmd_t   cmd_cfga;
        char            *errbuf;        /* internal error buffer */
        struct ac_soft_state *softsp;
        uint_t          bank;           /* Decoded bank number. */
} ac_cfga_pkt_t;

#define AC_ERR_SET(pkt, err)    (pkt)->cmd_cfga.errtype = (err)

#define MEM_BOARD_VISIBLE(BD) \
                ((BD)->sc.rstate == SYSC_CFGA_RSTATE_CONNECTED && \
                (BD)->sc.ostate == SYSC_CFGA_OSTATE_CONFIGURED)

#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif

#define AC_BANK0_STATUS         "bank-0-status"
#define AC_BANK1_STATUS         "bank-1-status"
#define AC_BANK_NOMEM           "nomem"
#define AC_BANK_OK              "ok"
#define AC_BANK_SPARE           "spare"
#define AC_BANK_FAILED          "failed"

/*
 * Test for a valid size setting. The size must be set as
 * a contiguous number of bits starting at the least significant bit.
 * Adding one to such a number causes a carry to be propagated to
 * the first zero bit, eg 00111 -> 01000. Thus for a correctly
 * formed value, the AND of the two numbers is 0.
 */
#define GRP_SIZE_IS_SET(memdec) ((GRP_UK(memdec) & (GRP_UK(memdec) + 1)) == 0)

/* set the decode register bits according to the desired bank layout */
#define SETUP_DECODE(addr, mb, intlv, group) \
        ((((addr) >> 26) & 0x7fffULL) << 12) |                  /* UM */ \
        ((((mb) >> 6) - 1ULL) << 39) |                          /* UK */ \
        ((group) & 0xfULL) |                                    /* LM */ \
        ((0xfULL << (intlv) & 0xfULL) << 6)                     /* LK */

/*
 * Driver minor number macros.
 */
#define AC_GETINSTANCE(M)       ((M) >> 1)
#define AC_GETBANK(M)           ((M) & 1)
#define AC_PUTINSTANCE(I)       ((I) << 1)

/*
 * Attachment point names.
 */
#define NAME_BANK0      "bank0"
#define NAME_BANK1      "bank1"

/*
 * Memory Database
 * This information is generally accessed through the bd_list so we will
 * just protect it by that for now.
 */
struct ac_mem_info {
        int busy;                               /* A bank is in transition */
        time_t status_change;                   /* Time of last change */

        sysc_cfga_rstate_t rstate;
        sysc_cfga_ostate_t ostate;
        sysc_cfga_cond_t condition;
        uint_t real_size;                       /* Real size in MB of bank */
        uint_t use_size;                        /* In use size in MB */
};

/* Structures used in the driver to manage the hardware */
struct ac_soft_state {
        dev_info_t *dip;        /* dev info of myself */
        dev_info_t *pdip;       /* dev info of my parent */
        int board;              /* Board number for this AC */

        /* fields protected by bd_list lock */
        struct ac_mem_info bank[2];     /* memory bank information */

        /* Mapped addresses of registers */
        void *ac_base;          /* Base address of Address Controller */
        volatile uint32_t *ac_id;               /* ID register */
        volatile uint64_t *ac_memctl;           /* Memory Control */
        volatile uint64_t *ac_memdecode0;       /* Memory Decode 0 */
        volatile uint64_t *ac_memdecode1;       /* Memory Decode 1 */
        volatile uint64_t *ac_counter;          /* AC counter register */
        volatile uint32_t *ac_mccr;             /* AC Counter control */
        kstat_t *ac_ksp;
        kstat_t *ac_counters_ksp;               /* performance counter kstat */
};

extern void     ac_blkcopy(caddr_t, caddr_t, uint_t, uint_t);
extern void     ac_mapin(uint64_t, caddr_t);
extern void     ac_unmap(caddr_t);

/* kstat structure used by ac to pass data to user programs. */
struct ac_kstat {
        struct kstat_named ac_memctl;           /* AC Memory control */
        struct kstat_named ac_memdecode0;       /* AC Memory Decode Bank 0 */
        struct kstat_named ac_memdecode1;       /* AC Memory Decode Bank 1 */
        struct kstat_named ac_mccr;             /* AC Mem Counter Control */
        struct kstat_named ac_counter;          /* AC Counter */
        struct kstat_named ac_bank0_status;
        struct kstat_named ac_bank1_status;
};

#endif  /* _KERNEL */

#ifdef  __cplusplus
}
#endif

#endif  /* _SYS_AC_H */